LCOV - code coverage report
Current view: top level - src/sapling - noteencryption.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 74 99 74.7 %
Date: 2025-02-23 09:33:43 Functions: 8 10 80.0 %

          Line data    Source code
       1             : // Copyright (c) 2016-2020 The ZCash developers
       2             : // Copyright (c) 2021 The PIVX Core developers
       3             : // Distributed under the MIT software license, see the accompanying
       4             : // file COPYING or https://www.opensource.org/licenses/mit-license.php.
       5             : 
       6             : #include "sapling/noteencryption.h"
       7             : 
       8             : #include "sapling/prf.h"
       9             : #include "sapling/sapling_util.h"
      10             : 
      11             : #include <librustzcash.h>
      12             : #include <sodium.h>
      13             : 
      14             : #include <stdexcept>
      15             : 
      16             : #define NOTEENCRYPTION_CIPHER_KEYSIZE 32
      17             : 
      18           0 : void clamp_curve25519(unsigned char key[crypto_scalarmult_SCALARBYTES])
      19             : {
      20           0 :     key[0] &= 248;
      21           0 :     key[31] &= 127;
      22           0 :     key[31] |= 64;
      23           0 : }
      24             : 
      25        1439 : void PRF_ock(
      26             :     unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE],
      27             :     const uint256 &ovk,
      28             :     const uint256 &cv,
      29             :     const uint256 &cm,
      30             :     const uint256 &epk
      31             : )
      32             : {
      33        1439 :     unsigned char block[128] = {};
      34        1439 :     memcpy(block+0, ovk.begin(), 32);
      35        1439 :     memcpy(block+32, cv.begin(), 32);
      36        1439 :     memcpy(block+64, cm.begin(), 32);
      37        1439 :     memcpy(block+96, epk.begin(), 32);
      38             : 
      39        1439 :     unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES] = {};
      40        1439 :     memcpy(personalization, "Zcash_Derive_ock", 16);
      41             : 
      42        1439 :     if (crypto_generichash_blake2b_salt_personal(K, NOTEENCRYPTION_CIPHER_KEYSIZE,
      43             :                                                  block, 128,
      44             :                                                  nullptr, 0, // No key.
      45             :                                                  nullptr,    // No salt.
      46             :                                                  personalization
      47             :                                                 ) != 0)
      48             :     {
      49           0 :         throw std::logic_error("hash function failure");
      50             :     }
      51        1439 : }
      52             : 
      53       10335 : void KDF_Sapling(
      54             :     unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE],
      55             :     const uint256 &dhsecret,
      56             :     const uint256 &epk
      57             : )
      58             : {
      59       10335 :     unsigned char block[64] = {};
      60       10335 :     memcpy(block+0, dhsecret.begin(), 32);
      61       10335 :     memcpy(block+32, epk.begin(), 32);
      62             : 
      63       10335 :     unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES] = {};
      64       10335 :     memcpy(personalization, "Zcash_SaplingKDF", 16);
      65             : 
      66       10335 :     if (crypto_generichash_blake2b_salt_personal(K, NOTEENCRYPTION_CIPHER_KEYSIZE,
      67             :                                                  block, 64,
      68             :                                                  nullptr, 0, // No key.
      69             :                                                  nullptr,    // No salt.
      70             :                                                  personalization
      71             :                                                 ) != 0)
      72             :     {
      73           0 :         throw std::logic_error("hash function failure");
      74             :     }
      75       10335 : }
      76             : 
      77           0 : void KDF(unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE],
      78             :     const uint256 &dhsecret,
      79             :     const uint256 &epk,
      80             :     const uint256 &pk_enc,
      81             :     const uint256 &hSig,
      82             :     unsigned char nonce
      83             :    )
      84             : {
      85           0 :     if (nonce == 0xff) {
      86           0 :         throw std::logic_error("no additional nonce space for KDF");
      87             :     }
      88             : 
      89           0 :     unsigned char block[128] = {};
      90           0 :     memcpy(block+0, hSig.begin(), 32);
      91           0 :     memcpy(block+32, dhsecret.begin(), 32);
      92           0 :     memcpy(block+64, epk.begin(), 32);
      93           0 :     memcpy(block+96, pk_enc.begin(), 32);
      94             : 
      95           0 :     unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES] = {};
      96           0 :     memcpy(personalization, "ZcashKDF", 8);
      97           0 :     memcpy(personalization+8, &nonce, 1);
      98             : 
      99           0 :     if (crypto_generichash_blake2b_salt_personal(K, NOTEENCRYPTION_CIPHER_KEYSIZE,
     100             :                                                  block, 128,
     101             :                                                  nullptr, 0, // No key.
     102             :                                                  nullptr,    // No salt.
     103             :                                                  personalization
     104             :                                                 ) != 0)
     105             :     {
     106           0 :         throw std::logic_error("hash function failure");
     107             :     }
     108           0 : }
     109             : 
     110             : namespace libzcash {
     111             : 
     112        1328 : Optional<SaplingNoteEncryption> SaplingNoteEncryption::FromDiversifier(diversifier_t d) {
     113        1328 :     uint256 epk;
     114        1328 :     uint256 esk;
     115             : 
     116             :     // Pick random esk
     117        1328 :     librustzcash_sapling_generate_r(esk.begin());
     118             : 
     119             :     // Compute epk given the diversifier
     120        1328 :     if (!librustzcash_sapling_ka_derivepublic(d.begin(), esk.begin(), epk.begin())) {
     121           1 :         return nullopt;
     122             :     }
     123             : 
     124        1327 :     return SaplingNoteEncryption(epk, esk);
     125             : }
     126             : 
     127        1328 : Optional<SaplingEncCiphertext> SaplingNoteEncryption::encrypt_to_recipient(
     128             :     const uint256 &pk_d,
     129             :     const SaplingEncPlaintext &message
     130             : )
     131             : {
     132        1328 :     if (already_encrypted_enc) {
     133           1 :         throw std::logic_error("already encrypted to the recipient using this key");
     134             :     }
     135             : 
     136        1327 :     uint256 dhsecret;
     137             : 
     138        1327 :     if (!librustzcash_sapling_ka_agree(pk_d.begin(), esk.begin(), dhsecret.begin())) {
     139           0 :         return nullopt;
     140             :     }
     141             : 
     142             :     // Construct the symmetric key
     143        1327 :     unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE];
     144        1327 :     KDF_Sapling(K, dhsecret, epk);
     145             : 
     146             :     // The nonce is zero because we never reuse keys
     147        1327 :     unsigned char cipher_nonce[crypto_aead_chacha20poly1305_IETF_NPUBBYTES] = {};
     148             : 
     149        1327 :     SaplingEncCiphertext ciphertext;
     150             : 
     151        1327 :     crypto_aead_chacha20poly1305_ietf_encrypt(
     152             :         ciphertext.begin(), nullptr,
     153             :         message.begin(), ZC_SAPLING_ENCPLAINTEXT_SIZE,
     154             :         nullptr, 0, // no "additional data"
     155             :         nullptr, cipher_nonce, K
     156             :     );
     157             : 
     158        1327 :     already_encrypted_enc = true;
     159             : 
     160        1327 :     return ciphertext;
     161             : }
     162             : 
     163        8911 : Optional<SaplingEncPlaintext> AttemptSaplingEncDecryption(
     164             :     const SaplingEncCiphertext &ciphertext,
     165             :     const uint256 &ivk,
     166             :     const uint256 &epk
     167             : )
     168             : {
     169        8911 :     uint256 dhsecret;
     170             : 
     171        8911 :     if (!librustzcash_sapling_ka_agree(epk.begin(), ivk.begin(), dhsecret.begin())) {
     172           0 :         return nullopt;
     173             :     }
     174             : 
     175             :     // Construct the symmetric key
     176        8911 :     unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE];
     177        8911 :     KDF_Sapling(K, dhsecret, epk);
     178             : 
     179             :     // The nonce is zero because we never reuse keys
     180        8911 :     unsigned char cipher_nonce[crypto_aead_chacha20poly1305_IETF_NPUBBYTES] = {};
     181             : 
     182        8911 :     SaplingEncPlaintext plaintext;
     183             : 
     184        8911 :     if (crypto_aead_chacha20poly1305_ietf_decrypt(
     185             :         plaintext.begin(), nullptr,
     186             :         nullptr,
     187             :         ciphertext.begin(), ZC_SAPLING_ENCCIPHERTEXT_SIZE,
     188             :         nullptr,
     189             :         0,
     190             :         cipher_nonce, K) != 0)
     191             :     {
     192        4765 :         return nullopt;
     193             :     }
     194             : 
     195        4146 :     return plaintext;
     196             : }
     197             : 
     198          97 : Optional<SaplingEncPlaintext> AttemptSaplingEncDecryption (
     199             :     const SaplingEncCiphertext &ciphertext,
     200             :     const uint256 &epk,
     201             :     const uint256 &esk,
     202             :     const uint256 &pk_d
     203             : )
     204             : {
     205          97 :     uint256 dhsecret;
     206             : 
     207          97 :     if (!librustzcash_sapling_ka_agree(pk_d.begin(), esk.begin(), dhsecret.begin())) {
     208           0 :         return nullopt;
     209             :     }
     210             : 
     211             :     // Construct the symmetric key
     212          97 :     unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE];
     213          97 :     KDF_Sapling(K, dhsecret, epk);
     214             : 
     215             :     // The nonce is zero because we never reuse keys
     216          97 :     unsigned char cipher_nonce[crypto_aead_chacha20poly1305_IETF_NPUBBYTES] = {};
     217             : 
     218          97 :     SaplingEncPlaintext plaintext;
     219             : 
     220          97 :     if (crypto_aead_chacha20poly1305_ietf_decrypt(
     221             :         plaintext.begin(), nullptr,
     222             :         nullptr,
     223             :         ciphertext.begin(), ZC_SAPLING_ENCCIPHERTEXT_SIZE,
     224             :         nullptr,
     225             :         0,
     226             :         cipher_nonce, K) != 0)
     227             :     {
     228           0 :         return nullopt;
     229             :     }
     230             : 
     231          97 :     return plaintext;
     232             : }
     233             : 
     234             : 
     235        1328 : SaplingOutCiphertext SaplingNoteEncryption::encrypt_to_ourselves(
     236             :     const uint256 &ovk,
     237             :     const uint256 &cv,
     238             :     const uint256 &cm,
     239             :     const SaplingOutPlaintext &message
     240             : )
     241             : {
     242        1328 :     if (already_encrypted_out) {
     243           1 :         throw std::logic_error("already encrypted to the recipient using this key");
     244             :     }
     245             : 
     246             :     // Construct the symmetric key
     247        1327 :     unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE];
     248        1327 :     PRF_ock(K, ovk, cv, cm, epk);
     249             : 
     250             :     // The nonce is zero because we never reuse keys
     251        1327 :     unsigned char cipher_nonce[crypto_aead_chacha20poly1305_IETF_NPUBBYTES] = {};
     252             : 
     253        1327 :     SaplingOutCiphertext ciphertext;
     254             : 
     255        1327 :     crypto_aead_chacha20poly1305_ietf_encrypt(
     256             :         ciphertext.begin(), nullptr,
     257             :         message.begin(), ZC_SAPLING_OUTPLAINTEXT_SIZE,
     258             :         nullptr, 0, // no "additional data"
     259             :         nullptr, cipher_nonce, K
     260             :     );
     261             : 
     262        1327 :     already_encrypted_out = true;
     263             : 
     264        1327 :     return ciphertext;
     265             : }
     266             : 
     267         112 : Optional<SaplingOutPlaintext> AttemptSaplingOutDecryption(
     268             :     const SaplingOutCiphertext &ciphertext,
     269             :     const uint256 &ovk,
     270             :     const uint256 &cv,
     271             :     const uint256 &cm,
     272             :     const uint256 &epk
     273             : )
     274             : {
     275             :     // Construct the symmetric key
     276         112 :     unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE];
     277         112 :     PRF_ock(K, ovk, cv, cm, epk);
     278             : 
     279             :     // The nonce is zero because we never reuse keys
     280         112 :     unsigned char cipher_nonce[crypto_aead_chacha20poly1305_IETF_NPUBBYTES] = {};
     281             : 
     282         112 :     SaplingOutPlaintext plaintext;
     283             : 
     284         112 :     if (crypto_aead_chacha20poly1305_ietf_decrypt(
     285             :         plaintext.begin(), nullptr,
     286             :         nullptr,
     287             :         ciphertext.begin(), ZC_SAPLING_OUTCIPHERTEXT_SIZE,
     288             :         nullptr,
     289             :         0,
     290             :         cipher_nonce, K) != 0)
     291             :     {
     292          13 :         return nullopt;
     293             :     }
     294             : 
     295          99 :     return plaintext;
     296             : }
     297             : 
     298             : }

Generated by: LCOV version 1.14