Implement note encryption
This commit is contained in:
parent
27692c26ea
commit
e823108984
|
@ -27,6 +27,7 @@ var ZC_NOTECIPHERTEXT_SIZE = (
|
|||
module.exports = {
|
||||
ZC_MEMO_SIZE: ZC_MEMO_SIZE,
|
||||
ZC_NOTECIPHERTEXT_SIZE: ZC_NOTECIPHERTEXT_SIZE,
|
||||
ZC_NOTEPLAINTEXT_SIZE: ZC_NOTEPLAINTEXT_SIZE,
|
||||
ZC_NUM_JS_INPUTS: ZC_NUM_JS_INPUTS,
|
||||
ZC_NUM_JS_OUTPUTS: ZC_NUM_JS_OUTPUTS
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
'use strict'
|
||||
|
||||
var blake2b = require('./blake2b')
|
||||
var libsodium = require('libsodium-sumo')
|
||||
|
||||
function KDF (dhsecret, epk, pkEnc, hSig, nonce) {
|
||||
if (nonce === 0xff) {
|
||||
throw new Error('no additional nonce space for KDF')
|
||||
}
|
||||
|
||||
var block = new Uint8Array(128)
|
||||
hSig.copy(block, 0)
|
||||
dhsecret.copy(block, 32)
|
||||
epk.copy(block, 64)
|
||||
pkEnc.copy(block, 96)
|
||||
|
||||
var personalization = new Uint8Array(libsodium._crypto_generichash_blake2b_personalbytes()).fill(0)
|
||||
Buffer.from('ZcashKDF').copy(personalization, 0)
|
||||
personalization[8] = nonce
|
||||
|
||||
return Buffer.from(blake2b.crypto_generichash_blake2b_salt_personal(
|
||||
32,
|
||||
block,
|
||||
undefined, // No key.
|
||||
undefined, // No salt.
|
||||
personalization))
|
||||
}
|
||||
|
||||
module.exports = KDF
|
|
@ -0,0 +1,38 @@
|
|||
'use strict'
|
||||
|
||||
var sodium = require('libsodium-wrappers-sumo')
|
||||
var typeforce = require('typeforce')
|
||||
var types = require('./types')
|
||||
var zutil = require('./util')
|
||||
|
||||
var KDF = require('./kdf')
|
||||
|
||||
function ZCNoteDecryption (skEnc) {
|
||||
typeforce(types.Buffer256bit, skEnc)
|
||||
|
||||
this.sk_enc = skEnc
|
||||
this.pk_enc = zutil.generate_pubkey(skEnc)
|
||||
}
|
||||
|
||||
ZCNoteDecryption.prototype.decrypt = function (ciphertext, epk, hSig, nonce) {
|
||||
typeforce(types.tuple(
|
||||
types.Buffer,
|
||||
types.Buffer256bit,
|
||||
types.Buffer256bit,
|
||||
types.Number
|
||||
), arguments)
|
||||
|
||||
var dhsecret = Buffer.from(sodium.crypto_scalarmult(this.sk_enc, epk))
|
||||
|
||||
// Construct the symmetric key
|
||||
var K = KDF(dhsecret, epk, this.pk_enc, hSig, nonce)
|
||||
|
||||
// The nonce is zero because we never reuse keys
|
||||
var cipherNonce = new Uint8Array(sodium.crypto_aead_chacha20poly1305_ietf_NPUBBYTES)
|
||||
sodium.memzero(cipherNonce)
|
||||
|
||||
return Buffer.from(sodium.crypto_aead_chacha20poly1305_ietf_decrypt(
|
||||
null, ciphertext, null, cipherNonce, K))
|
||||
}
|
||||
|
||||
module.exports = ZCNoteDecryption
|
|
@ -0,0 +1,41 @@
|
|||
'use strict'
|
||||
|
||||
var sodium = require('libsodium-wrappers-sumo')
|
||||
var typeforce = require('typeforce')
|
||||
var types = require('./types')
|
||||
var zutil = require('./util')
|
||||
|
||||
var KDF = require('./kdf')
|
||||
|
||||
function ZCNoteEncryption (hSig) {
|
||||
typeforce(types.Buffer256bit, hSig)
|
||||
|
||||
this.nonce = 0
|
||||
this.hSig = hSig
|
||||
this.esk = zutil.random_uint256()
|
||||
this.epk = zutil.generate_pubkey(this.esk)
|
||||
}
|
||||
|
||||
ZCNoteEncryption.prototype.encrypt = function (pkEnc, message) {
|
||||
typeforce(types.tuple(
|
||||
types.Buffer256bit,
|
||||
types.Buffer
|
||||
), arguments)
|
||||
|
||||
var dhsecret = Buffer.from(sodium.crypto_scalarmult(this.esk, pkEnc))
|
||||
|
||||
// Construct the symmetric key
|
||||
var K = KDF(dhsecret, this.epk, pkEnc, this.hSig, this.nonce)
|
||||
|
||||
// Increment the number of encryptions we've performed
|
||||
this.nonce++
|
||||
|
||||
// The nonce is zero because we never reuse keys
|
||||
var cipherNonce = new Uint8Array(sodium.crypto_aead_chacha20poly1305_ietf_NPUBBYTES)
|
||||
sodium.memzero(cipherNonce)
|
||||
|
||||
return Buffer.from(sodium.crypto_aead_chacha20poly1305_ietf_encrypt(
|
||||
message, null, null, cipherNonce, K))
|
||||
}
|
||||
|
||||
module.exports = ZCNoteEncryption
|
|
@ -83,4 +83,8 @@ NotePlaintext.prototype.note = function (aPk) {
|
|||
return new Note(aPk, this.value, this.rho, this.r)
|
||||
}
|
||||
|
||||
NotePlaintext.prototype.encrypt = function (encryptor, pkEnc) {
|
||||
return encryptor.encrypt(pkEnc, this.toBuffer())
|
||||
}
|
||||
|
||||
module.exports = NotePlaintext
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
/* global describe, it */
|
||||
'use strict'
|
||||
|
||||
var assert = require('assert')
|
||||
|
||||
var ZCNoteDecryption = require('../src/note_decryption')
|
||||
var ZCNoteEncryption = require('../src/note_encryption')
|
||||
|
||||
var util = require('../src/util')
|
||||
var zconst = require('../src/const')
|
||||
|
||||
describe('Note Encryption', function () {
|
||||
var skEnc = util.generate_privkey([].reverse.call(Buffer.from('21035d60bc1983e37950ce4803418a8fb33ea68d5b937ca382ecbae7564d6a07', 'hex')))
|
||||
var pkEnc = util.generate_pubkey(skEnc)
|
||||
|
||||
var hSig = util.random_uint256()
|
||||
var b = new ZCNoteEncryption(hSig)
|
||||
var message = Buffer.alloc(zconst.ZC_NOTEPLAINTEXT_SIZE)
|
||||
for (let i = 0; i < zconst.ZC_NOTEPLAINTEXT_SIZE; ++i) {
|
||||
message[i] = i
|
||||
}
|
||||
|
||||
for (let i = 0; i < 255; ++i) {
|
||||
var ciphertext
|
||||
var decrypter = new ZCNoteDecryption(skEnc)
|
||||
|
||||
it('correctly encrypts and decrypts nonce ' + i, function () {
|
||||
ciphertext = b.encrypt(pkEnc, message)
|
||||
var plaintext = decrypter.decrypt(ciphertext, b.epk, hSig, i)
|
||||
assert.strictEqual(plaintext.toString('hex'), message.toString('hex'))
|
||||
})
|
||||
|
||||
it('fails to decrypt ' + i + ' with wrong nonce', function () {
|
||||
assert.throws(function () {
|
||||
decrypter.decrypt(ciphertext, b.epk, hSig, (i === 0) ? 1 : (i - 1))
|
||||
})
|
||||
})
|
||||
|
||||
it('fails to decrypt ' + i + ' with wrong oneTimePubKey', function () {
|
||||
var c = new ZCNoteEncryption(hSig)
|
||||
assert.throws(function () {
|
||||
decrypter.decrypt(ciphertext, c.epk, hSig, i)
|
||||
})
|
||||
})
|
||||
|
||||
it('fails to decrypt ' + i + ' with wrong seed', function () {
|
||||
assert.throws(function () {
|
||||
decrypter.decrypt(ciphertext, b.epk, [].reverse.call(Buffer.from('11035d60bc1983e37950ce4803418a8fb33ea68d5b937ca382ecbae7564d6a77', 'hex')), i)
|
||||
})
|
||||
})
|
||||
|
||||
it('fails to decrypt ' + i + ' with corrupted ciphertext', function () {
|
||||
ciphertext[10] ^= 0xff
|
||||
assert.throws(function () {
|
||||
decrypter.decrypt(ciphertext, b.epk, hSig, i)
|
||||
})
|
||||
ciphertext[10] ^= 0xff
|
||||
})
|
||||
|
||||
it('fails to decrypt ' + i + ' with wrong private key', function () {
|
||||
var skEnc2 = util.generate_privkey(util.random_uint252())
|
||||
var decrypter2 = new ZCNoteDecryption(skEnc2)
|
||||
assert.throws(function () {
|
||||
decrypter2.decrypt(ciphertext, b.epk, hSig, i)
|
||||
})
|
||||
})
|
||||
|
||||
it('fails to decrypt ' + i + ' with wrong public key (test of KDF)', function () {
|
||||
var decrypter2 = new ZCNoteDecryption(skEnc)
|
||||
decrypter2.pk_enc = util.generate_pubkey(util.random_uint256())
|
||||
assert.throws(function () {
|
||||
decrypter2.decrypt(ciphertext, b.epk, hSig, i)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
it('runs out of nonce space with nonce 255', function () {
|
||||
assert.throws(function () {
|
||||
b.encrypt(pkEnc, message)
|
||||
}, new RegExp('no additional nonce space for KDF'))
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue