add crypto_tweak api
This commit is contained in:
parent
c428394bb6
commit
64089d1fca
@ -370,5 +370,8 @@ module.exports = {
|
|||||||
crypto_sign_BYTES,
|
crypto_sign_BYTES,
|
||||||
crypto_sign_PUBLICKEYBYTES,
|
crypto_sign_PUBLICKEYBYTES,
|
||||||
crypto_sign_SECRETKEYBYTES,
|
crypto_sign_SECRETKEYBYTES,
|
||||||
crypto_sign_SEEDBYTES
|
crypto_sign_SEEDBYTES,
|
||||||
|
crypto_sign_ed25519_PUBLICKEYBYTES,
|
||||||
|
crypto_sign_ed25519_SECRETKEYBYTES,
|
||||||
|
crypto_sign_ed25519_SEEDBYTES
|
||||||
}
|
}
|
||||||
|
216
crypto_tweak.js
Normal file
216
crypto_tweak.js
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
const b4a = require('b4a')
|
||||||
|
|
||||||
|
const {
|
||||||
|
crypto_scalarmult_ed25519,
|
||||||
|
crypto_scalarmult_ed25519_noclamp,
|
||||||
|
crypto_scalarmult_ed25519_base,
|
||||||
|
crypto_scalarmult_ed25519_base_noclamp,
|
||||||
|
crypto_scalarmult_ed25519_SCALARBYTES
|
||||||
|
} = require('./crypto_scalarmult_ed25519')
|
||||||
|
|
||||||
|
const {
|
||||||
|
crypto_sign_keypair_seed,
|
||||||
|
crypto_sign_ed25519_PUBLICKEYBYTES
|
||||||
|
} = require('./crypto_sign_ed25519')
|
||||||
|
|
||||||
|
const {
|
||||||
|
crypto_hash_sha512_state,
|
||||||
|
crypto_hash_sha512_update,
|
||||||
|
crypto_hash_sha512_final,
|
||||||
|
crypto_hash
|
||||||
|
} = require('./crypto_hash')
|
||||||
|
|
||||||
|
const {
|
||||||
|
crypto_core_ed25519_is_valid_point,
|
||||||
|
crypto_core_ed25519_scalar_reduce,
|
||||||
|
crypto_core_ed25519_scalar_add,
|
||||||
|
crypto_core_ed25519_scalar_mul,
|
||||||
|
crypto_core_ed25519_add
|
||||||
|
} = require('./crypto_core')
|
||||||
|
|
||||||
|
const { curve25519_h: COFACTOR } = require('./fe25519_25')
|
||||||
|
|
||||||
|
const { sodium_memzero } = require('./utils')
|
||||||
|
|
||||||
|
/*
|
||||||
|
*EXPERIMENTAL API*
|
||||||
|
|
||||||
|
This module is an experimental implementation of a key tweaking protocol
|
||||||
|
over ed25519 keys. The signature algorithm has been reimplemented from
|
||||||
|
libsodium, but the nonce generation algorithm is *non-standard*.
|
||||||
|
|
||||||
|
Use at your own risk
|
||||||
|
*/
|
||||||
|
|
||||||
|
const crypto_tweak_ed25519_BYTES = crypto_sign_ed25519_PUBLICKEYBYTES
|
||||||
|
const crypto_tweak_ed25519_SCALARBYTES = crypto_scalarmult_ed25519_SCALARBYTES
|
||||||
|
|
||||||
|
function _crypto_tweak_nonce (nonce, n, m, mlen) {
|
||||||
|
// dom2(x, y) with x = 0 (not prehashed) and y = "crypto_tweak_ed25519"
|
||||||
|
const prefix = b4a.alloc(54)
|
||||||
|
|
||||||
|
prefix.write('SigEd25519 no Ed25519 collisions\x00\x14crypto_tweak_ed25519')
|
||||||
|
|
||||||
|
const hs = crypto_hash_sha512_state()
|
||||||
|
|
||||||
|
crypto_hash_sha512_update(hs, prefix)
|
||||||
|
crypto_hash_sha512_update(hs, n, 32)
|
||||||
|
crypto_hash_sha512_update(hs, m, m.byteLength)
|
||||||
|
crypto_hash_sha512_final(hs, nonce)
|
||||||
|
}
|
||||||
|
|
||||||
|
function _crypto_sign_ed25519_clamp (k) {
|
||||||
|
k[0] &= 248
|
||||||
|
k[31] &= 127
|
||||||
|
k[31] |= 64
|
||||||
|
}
|
||||||
|
|
||||||
|
function _crypto_tweak_ed25519(q, n, ns) {
|
||||||
|
sodium_memzero(q)
|
||||||
|
|
||||||
|
crypto_hash(n, ns)
|
||||||
|
n[31] &= 127 // clear highest bit
|
||||||
|
|
||||||
|
crypto_scalarmult_ed25519_base_noclamp(q, n)
|
||||||
|
|
||||||
|
// hash tweak until we get a valid tweaked q
|
||||||
|
while (crypto_core_ed25519_is_valid_point(q) != 1) {
|
||||||
|
crypto_hash(n, n, 32)
|
||||||
|
n[31] &= 127 // clear highest bit
|
||||||
|
|
||||||
|
crypto_scalarmult_ed25519_base_noclamp(q, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function crypto_tweak_ed25519_base (n, q, ns) {
|
||||||
|
sodium_memzero(q)
|
||||||
|
|
||||||
|
const n64 = b4a.alloc(64)
|
||||||
|
_crypto_tweak_ed25519(q, n64, ns)
|
||||||
|
|
||||||
|
n.set(n64.subarray(0, 32))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: check pk is correct if we pass it
|
||||||
|
function crypto_tweak_ed25519_sign_detached (sig, m, n, pk = null) {
|
||||||
|
const hs = crypto_hash_sha512_state()
|
||||||
|
|
||||||
|
const nonce = b4a.alloc(64)
|
||||||
|
const R = b4a.alloc(32)
|
||||||
|
const hram = b4a.alloc(64)
|
||||||
|
const _pk = b4a.alloc(32)
|
||||||
|
|
||||||
|
// check if pk was passed
|
||||||
|
if (pk === null) {
|
||||||
|
pk = _pk
|
||||||
|
|
||||||
|
// derive pk from scalar
|
||||||
|
crypto_scalarmult_ed25519_base_noclamp(pk, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
_crypto_tweak_nonce(nonce, n, m)
|
||||||
|
crypto_core_ed25519_scalar_reduce(nonce, nonce)
|
||||||
|
|
||||||
|
// R = G ^ nonce : curve point from nonce
|
||||||
|
crypto_scalarmult_ed25519_base_noclamp(R, nonce)
|
||||||
|
|
||||||
|
// generate challenge as h(ram) = hash(R, pk, message)
|
||||||
|
crypto_hash_sha512_update(hs, R, 32)
|
||||||
|
crypto_hash_sha512_update(hs, pk, 32)
|
||||||
|
crypto_hash_sha512_update(hs, m, m.byteLength)
|
||||||
|
|
||||||
|
crypto_hash_sha512_final(hs, hram)
|
||||||
|
|
||||||
|
crypto_core_ed25519_scalar_reduce(hram, hram)
|
||||||
|
|
||||||
|
// sig = nonce + n * h(ram)
|
||||||
|
crypto_core_ed25519_scalar_mul(sig.subarray(0, 32), hram.subarray(0, 32), n)
|
||||||
|
crypto_core_ed25519_scalar_add(sig.subarray(32, 64), nonce.subarray(0, 32), sig)
|
||||||
|
|
||||||
|
sig.set(R)
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// get scalar from secret key
|
||||||
|
function crypto_tweak_ed25519_sk_to_scalar (n, sk) {
|
||||||
|
const n64 = b4a.alloc(64)
|
||||||
|
|
||||||
|
// get sk scalar from seed, cf. crypto_sign_keypair_seed
|
||||||
|
crypto_hash(n64, sk, 32)
|
||||||
|
_crypto_sign_ed25519_clamp(n64)
|
||||||
|
|
||||||
|
n.set(n64.subarray(0, 32))
|
||||||
|
}
|
||||||
|
|
||||||
|
// tweak a secret key
|
||||||
|
function crypto_tweak_ed25519_scalar (scalar_out, scalar, ns) {
|
||||||
|
const q = b4a.alloc(32)
|
||||||
|
const n = b4a.alloc(64)
|
||||||
|
|
||||||
|
const n32 = n.subarray(0, 32)
|
||||||
|
|
||||||
|
_crypto_tweak_ed25519(q, n, ns)
|
||||||
|
crypto_tweak_ed25519_scalar_add(scalar_out, n32, scalar)
|
||||||
|
}
|
||||||
|
|
||||||
|
// tweak a public key
|
||||||
|
function crypto_tweak_ed25519_pk (tpk, pk, ns) {
|
||||||
|
const n = b4a.alloc(64)
|
||||||
|
const q = b4a.alloc(32)
|
||||||
|
|
||||||
|
_crypto_tweak_ed25519(q, n, ns)
|
||||||
|
return crypto_core_ed25519_add(tpk, q, pk)
|
||||||
|
}
|
||||||
|
|
||||||
|
function crypto_tweak_ed25519_keypair (pk, scalar_out, scalar, ns) {
|
||||||
|
const n64 = b4a.alloc(64)
|
||||||
|
|
||||||
|
crypto_hash(n64, ns)
|
||||||
|
n64[31] &= 127 // clear highest bit
|
||||||
|
|
||||||
|
crypto_tweak_ed25519_scalar_add(scalar_out, scalar, n64)
|
||||||
|
crypto_scalarmult_ed25519_base_noclamp(pk, scalar_out)
|
||||||
|
|
||||||
|
// hash tweak until we get a valid tweaked point
|
||||||
|
while (crypto_core_ed25519_is_valid_point(pk) != 1) {
|
||||||
|
crypto_hash(n64, n64, 32)
|
||||||
|
n64[31] &= 127 // clear highest bit
|
||||||
|
|
||||||
|
crypto_tweak_ed25519_scalar_add(scalar_out, scalar, n64)
|
||||||
|
crypto_scalarmult_ed25519_base_noclamp(pk, scalar_out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add tweak to scalar
|
||||||
|
function crypto_tweak_ed25519_scalar_add (scalar_out, scalar, n) {
|
||||||
|
crypto_core_ed25519_scalar_add(scalar_out, scalar, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// add tweak point to public key
|
||||||
|
function crypto_tweak_ed25519_pk_add (tpk, pk, q) {
|
||||||
|
crypto_core_ed25519_add(tpk, pk, q)
|
||||||
|
}
|
||||||
|
|
||||||
|
// add tweak to scalar
|
||||||
|
function crypto_tweak_ed25519_scalar_mul (scalar_out, scalar, n) {
|
||||||
|
crypto_core_ed25519_scalar_mul(scalar_out, scalar, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// add tweak point to public key
|
||||||
|
function crypto_tweak_ed25519_publickey_mul (tpk, pk, n) {
|
||||||
|
crypto_scalarmult_ed25519_noclamp(tpk, n, pk)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
crypto_tweak_ed25519_base,
|
||||||
|
crypto_tweak_ed25519_sign_detached,
|
||||||
|
crypto_tweak_ed25519_sk_to_scalar,
|
||||||
|
crypto_tweak_ed25519_scalar,
|
||||||
|
crypto_tweak_ed25519_pk,
|
||||||
|
crypto_tweak_ed25519_keypair,
|
||||||
|
crypto_tweak_ed25519_scalar_add,
|
||||||
|
crypto_tweak_ed25519_pk_add,
|
||||||
|
crypto_tweak_ed25519_BYTES,
|
||||||
|
crypto_tweak_ed25519_SCALARBYTES
|
||||||
|
}
|
113
test/crypto_tweak_ed25519.js
Normal file
113
test/crypto_tweak_ed25519.js
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
const test = require('brittle')
|
||||||
|
const sodium = require('..')
|
||||||
|
const fixtures = require('./fixtures/crypto_tweak_ed25519_sign.js')
|
||||||
|
|
||||||
|
test('crypto_tweak', function (t) {
|
||||||
|
const pk = Buffer.alloc(sodium.crypto_sign_PUBLICKEYBYTES)
|
||||||
|
const sk = Buffer.alloc(sodium.crypto_sign_SECRETKEYBYTES)
|
||||||
|
|
||||||
|
sodium.crypto_sign_keypair(pk, sk)
|
||||||
|
|
||||||
|
const tpk = Buffer.alloc(sodium.crypto_tweak_ed25519_BYTES)
|
||||||
|
const tsk = Buffer.alloc(sodium.crypto_tweak_ed25519_SCALARBYTES)
|
||||||
|
|
||||||
|
const tkpk = Buffer.alloc(sodium.crypto_tweak_ed25519_BYTES)
|
||||||
|
const tksk = Buffer.alloc(sodium.crypto_tweak_ed25519_SCALARBYTES)
|
||||||
|
|
||||||
|
const point = Buffer.alloc(sodium.crypto_tweak_ed25519_BYTES)
|
||||||
|
const tweak = Buffer.alloc(sodium.crypto_tweak_ed25519_SCALARBYTES)
|
||||||
|
|
||||||
|
const ns = Buffer.alloc(32)
|
||||||
|
sodium.crypto_generichash(ns, Buffer.from('namespace'))
|
||||||
|
|
||||||
|
t.exception.all(function () {
|
||||||
|
sodium.crypto_tweak_ed25519_base()
|
||||||
|
}, 'should validate input')
|
||||||
|
|
||||||
|
t.exception.all(function () {
|
||||||
|
sodium.crypto_tweak_ed25519_base(Buffer.alloc(0), Buffer.alloc(0), ns)
|
||||||
|
}, 'should validate input length')
|
||||||
|
|
||||||
|
sodium.crypto_tweak_ed25519_base(tweak, point, ns)
|
||||||
|
|
||||||
|
const _sk = sk.subarray(0, 32)
|
||||||
|
sodium.crypto_tweak_ed25519_sk_to_scalar(_sk, sk)
|
||||||
|
|
||||||
|
sodium.crypto_tweak_ed25519_pk(tpk, pk, ns)
|
||||||
|
sodium.crypto_tweak_ed25519_scalar(tsk, _sk, ns)
|
||||||
|
|
||||||
|
sodium.crypto_tweak_ed25519_keypair(tkpk, tksk, _sk, ns)
|
||||||
|
|
||||||
|
sodium.crypto_tweak_ed25519_pk_add(pk, pk, point)
|
||||||
|
sodium.crypto_tweak_ed25519_scalar_add(_sk, _sk, tweak)
|
||||||
|
|
||||||
|
t.alike(pk, tpk, 'tweak public key')
|
||||||
|
t.alike(_sk, tsk, 'tweak secret key')
|
||||||
|
t.alike(pk, tkpk, 'tweak keypair public key')
|
||||||
|
t.alike(_sk, tksk, 'tweak keypair secret key')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('crypto_tweak_sign', function (t) {
|
||||||
|
const pk = Buffer.alloc(sodium.crypto_sign_PUBLICKEYBYTES)
|
||||||
|
const sk = Buffer.alloc(sodium.crypto_sign_SECRETKEYBYTES)
|
||||||
|
|
||||||
|
sodium.crypto_sign_keypair(pk, sk)
|
||||||
|
|
||||||
|
const tpk = Buffer.alloc(sodium.crypto_tweak_ed25519_BYTES)
|
||||||
|
const tsk = Buffer.alloc(sodium.crypto_tweak_ed25519_SCALARBYTES)
|
||||||
|
|
||||||
|
const point = Buffer.alloc(sodium.crypto_tweak_ed25519_BYTES)
|
||||||
|
const tweak = Buffer.alloc(sodium.crypto_tweak_ed25519_SCALARBYTES)
|
||||||
|
|
||||||
|
const ns = Buffer.alloc(32)
|
||||||
|
sodium.crypto_generichash(ns, Buffer.from('namespace'))
|
||||||
|
|
||||||
|
sodium.crypto_tweak_ed25519_base(tweak, point, ns)
|
||||||
|
|
||||||
|
sodium.crypto_tweak_ed25519_pk(tpk, pk, ns)
|
||||||
|
sodium.crypto_tweak_ed25519_sk_to_scalar(tsk, sk)
|
||||||
|
sodium.crypto_tweak_ed25519_scalar(tsk, tsk, ns)
|
||||||
|
|
||||||
|
sodium.crypto_tweak_ed25519_pk_add(pk, pk, point)
|
||||||
|
|
||||||
|
const _sk = sk.subarray(0, 32)
|
||||||
|
sodium.crypto_tweak_ed25519_sk_to_scalar(_sk, sk)
|
||||||
|
sodium.crypto_tweak_ed25519_scalar_add(_sk, _sk, tweak)
|
||||||
|
|
||||||
|
const m = Buffer.from('test message')
|
||||||
|
const sig = Buffer.alloc(sodium.crypto_sign_BYTES)
|
||||||
|
|
||||||
|
sodium.crypto_tweak_ed25519_sign_detached(sig, m, _sk)
|
||||||
|
t.ok(sodium.crypto_sign_verify_detached(sig, m, pk))
|
||||||
|
t.ok(sodium.crypto_sign_verify_detached(sig, m, tpk))
|
||||||
|
|
||||||
|
sodium.crypto_tweak_ed25519_sign_detached(sig, m, tsk)
|
||||||
|
t.ok(sodium.crypto_sign_verify_detached(sig, m, pk))
|
||||||
|
t.ok(sodium.crypto_sign_verify_detached(sig, m, tpk))
|
||||||
|
})
|
||||||
|
|
||||||
|
test('crypto_tweak sign fixtures', t => {
|
||||||
|
for (const f of fixtures) {
|
||||||
|
const [sk, n, m, sig, tweak, tpk, tn] = f.map(Buffer.from)
|
||||||
|
|
||||||
|
const signature = Buffer.alloc(64)
|
||||||
|
const scalar = Buffer.alloc(32)
|
||||||
|
const pk = Buffer.alloc(32)
|
||||||
|
|
||||||
|
sodium.crypto_tweak_ed25519_sk_to_scalar(scalar, sk)
|
||||||
|
t.alike(scalar, n)
|
||||||
|
|
||||||
|
sodium.crypto_tweak_ed25519_sign_detached(signature, m, n)
|
||||||
|
t.alike(signature, sig)
|
||||||
|
|
||||||
|
sodium.randombytes_buf(signature)
|
||||||
|
sodium.crypto_sign_ed25519_sk_to_pk(pk, sk)
|
||||||
|
|
||||||
|
sodium.crypto_tweak_ed25519_sign_detached(signature, m, n, pk)
|
||||||
|
t.alike(signature, sig)
|
||||||
|
|
||||||
|
sodium.crypto_tweak_ed25519_keypair(pk, scalar, n, tweak)
|
||||||
|
t.alike(pk, tpk)
|
||||||
|
t.alike(scalar, tn)
|
||||||
|
}
|
||||||
|
})
|
Loading…
Reference in New Issue
Block a user