From 9bf2e1786bd8d310f3cfae65e97547a645829109 Mon Sep 17 00:00:00 2001 From: jangko Date: Thu, 20 Apr 2023 11:54:54 +0700 Subject: [PATCH] remove ecdhRaw usage and replace it with ecdh+custom hash function --- eth/keys.nim | 39 +++++++++++++++++++------ eth/p2p/auth.nim | 10 +++---- eth/p2p/discoveryv5/encoding.nim | 2 +- eth/p2p/ecies.nim | 10 +++---- tests/keys/test_keys.nim | 10 +++---- tests/p2p/test_discoveryv5_encoding.nim | 2 +- 6 files changed, 47 insertions(+), 26 deletions(-) diff --git a/eth/keys.nim b/eth/keys.nim index 31642ae..d351bbf 100644 --- a/eth/keys.nim +++ b/eth/keys.nim @@ -17,7 +17,7 @@ import std/strformat, secp256k1, bearssl/hash as bhash, bearssl/rand, - stew/[byteutils, objects, results], + stew/[byteutils, objects, results, ptrops], ./common/eth_hash from nimcrypto/utils import burnMem @@ -25,7 +25,9 @@ from nimcrypto/utils import burnMem export secp256k1, results, rand const - KeyLength* = SkEcdhRawSecretSize - 1 + FullKeyLength* = 33 + + KeyLength* = FullKeyLength - 1 ## Shared secret key length without format marker RawPublicKeySize* = SkRawPublicKeySize - 1 ## Size of uncompressed public key without format marker (0x04) @@ -43,7 +45,9 @@ type SignatureNR* = distinct SkSignature ## ...but ENR uses non-recoverable signatures! - SharedSecretFull* = SkEcdhRawSecret + SharedSecretFull* = object + data*: array[FullKeyLength, byte] + SharedSecret* = object data*: array[KeyLength, byte] @@ -230,11 +234,28 @@ func verify*(sig: SignatureNR, msg: openArray[byte], key: PublicKey): bool = let hash = keccakHash(msg) verify(sig, SkMessage(hash.data), key) -func ecdhRaw*(seckey: PrivateKey, pubkey: PublicKey): SharedSecret = - let tmp = ecdhRaw(SkSecretKey(seckey), SkPublicKey(pubkey)) +proc ecdhSecretHash(output: ptr byte, x32, y32: ptr byte, data: pointer): cint + {.cdecl, raises: [].} = + copyMem(output, x32, 32) + return 1 - # Remove first byte! - copyMem(addr result.data[0], unsafeAddr(tmp.data[1]), sizeof(result)) +func ecdhSecret*(seckey: PrivateKey, pubkey: PublicKey): SharedSecret = + # This function only fail if the hash function return zero. + # Because our hash function always success, we can turn the error into defect + let res = ecdh[KeyLength](SkSecretKey(seckey), SkPublicKey(pubkey), ecdhSecretHash, nil) + doAssert res.isOk, $res.error + SharedSecret(data: res.get) -func ecdhRawFull*(seckey: PrivateKey, pubkey: PublicKey): SharedSecretFull = - SharedSecretFull(ecdhRaw(SkSecretKey(seckey), SkPublicKey(pubkey))) +proc ecdhSecretFullHash(output: ptr byte, x32, y32: ptr byte, data: pointer): cint + {.cdecl, raises: [].} = + # output[0] = 0x02 | (y32[31] & 1) + output[] = 0x02 or (y32.offset(31)[] and 0x01) + copyMem(output.offset(1), x32, 32) + return 1 + +func ecdhSecretFull*(seckey: PrivateKey, pubkey: PublicKey): SharedSecretFull = + # This function only fail if the hash function return zero. + # Because our hash function always success, we can turn the error into defect + let res = ecdh[FullKeyLength](SkSecretKey(seckey), SkPublicKey(pubkey), ecdhSecretFullHash, nil) + doAssert res.isOk, $res.error + SharedSecretFull(data: res.get) diff --git a/eth/p2p/auth.nim b/eth/p2p/auth.nim index 3ce53d1..8ee7a68 100644 --- a/eth/p2p/auth.nim +++ b/eth/p2p/auth.nim @@ -150,7 +150,7 @@ proc authMessagePreEIP8(h: var Handshake, outlen = 0 let header = cast[ptr AuthMessageV4](addr buffer[0]) - var secret = ecdhRaw(h.host.seckey, pubkey) + var secret = ecdhSecret(h.host.seckey, pubkey) secret.data = secret.data xor h.initiatorNonce let signature = sign(h.ephemeral.seckey, SkMessage(secret.data)) @@ -190,7 +190,7 @@ proc authMessageEIP8(h: var Handshake, doAssert(EIP8 in h.flags) outlen = 0 - var secret = ecdhRaw(h.host.seckey, pubkey) + var secret = ecdhSecret(h.host.seckey, pubkey) secret.data = secret.data xor h.initiatorNonce let signature = sign(h.ephemeral.seckey, SkMessage(secret.data)) @@ -364,7 +364,7 @@ proc decodeAuthMessageV4(h: var Handshake, m: openArray[byte]): AuthResult[void] pubkey = ? PublicKey.fromRaw(header.pubkey).mapErrTo(InvalidPubKey) signature = ? Signature.fromRaw(header.signature).mapErrTo(SignatureError) - var secret = ecdhRaw(h.host.seckey, pubkey) + var secret = ecdhSecret(h.host.seckey, pubkey) secret.data = secret.data xor header.nonce var recovered = recover(signature, SkMessage(secret.data)) @@ -415,7 +415,7 @@ proc decodeAuthMessageEIP8(h: var Handshake, m: openArray[byte]): AuthResult[voi pubkey = ? PublicKey.fromRaw(pubkeyBr).mapErrTo(InvalidPubKey) nonce = toArray(KeyLength, nonceBr) - var secret = ecdhRaw(h.host.seckey, pubkey) + var secret = ecdhSecret(h.host.seckey, pubkey) secret.data = secret.data xor nonce let recovered = recover(signature, SkMessage(secret.data)) @@ -524,7 +524,7 @@ proc getSecrets*( secret: ConnectionSecret # ecdhe-secret = ecdh.agree(ephemeral-privkey, remote-ephemeral-pubk) - var shsec = ecdhRaw(h.ephemeral.seckey, h.remoteEPubkey) + var shsec = ecdhSecret(h.ephemeral.seckey, h.remoteEPubkey) # shared-secret = keccak(ecdhe-secret || keccak(nonce || initiator-nonce)) ctx0.init() diff --git a/eth/p2p/discoveryv5/encoding.nim b/eth/p2p/discoveryv5/encoding.nim index f414249..77268a5 100644 --- a/eth/p2p/discoveryv5/encoding.nim +++ b/eth/p2p/discoveryv5/encoding.nim @@ -138,7 +138,7 @@ proc verifyIdSignature*(sig: SignatureNR, challengeData, ephKey: openArray[byte] proc deriveKeys*(n1, n2: NodeId, priv: PrivateKey, pub: PublicKey, challengeData: openArray[byte]): HandshakeSecrets = - let eph = ecdhRawFull(priv, pub) + let eph = ecdhSecretFull(priv, pub) var info = newSeqOfCap[byte](keyAgreementPrefix.len + 32 * 2) for i, c in keyAgreementPrefix: info.add(byte(c)) diff --git a/eth/p2p/ecies.nim b/eth/p2p/ecies.nim index 39e3c45..4c5b0df 100644 --- a/eth/p2p/ecies.nim +++ b/eth/p2p/ecies.nim @@ -114,8 +114,8 @@ proc eciesEncrypt*(rng: var HmacDrbgContext, input: openArray[byte], var ephemeral = KeyPair.random(rng) - secret = ecdhRaw(ephemeral.seckey, pubkey) - material = kdf(secret.data) + secret = ecdhSecret(ephemeral.seckey, pubkey) + material = kdf(secret.data) clear(secret) @@ -183,10 +183,10 @@ proc eciesDecrypt*(input: openArray[byte], var pubkey = ? PublicKey.fromRaw(header.pubkey).mapErrTo(IncorrectKey) - secret = ecdhRaw(seckey, pubkey) + secret = ecdhSecret(seckey, pubkey) + material = kdf(secret.data) - var material = kdf(secret.data) - burnMem(secret) + clear(secret) copyMem(addr encKey[0], addr material[0], aes128.sizeKey) var macKey = diff --git a/tests/keys/test_keys.nim b/tests/keys/test_keys.nim index df46bb4..6dfacb1 100644 --- a/tests/keys/test_keys.nim +++ b/tests/keys/test_keys.nim @@ -160,7 +160,7 @@ suite "ECC/ECDSA/ECDHE tests suite": var s = PrivateKey.fromHex(privateKeys[i])[] var p = PublicKey.fromHex(stripSpaces(publicKeys[i]))[] let expect = fromHex(stripSpaces(sharedSecrets[i])) - let secret = ecdhRaw(s, p) + let secret = ecdhSecret(s, p) check: expect == secret.data @@ -172,7 +172,7 @@ suite "ECC/ECDSA/ECDHE tests suite": var s = PrivateKey.fromRaw(keccak256.digest("ecdhAgree").data)[] var p = s.toPublicKey() let expect = fromHex(stripSpaces(expectm)) - let secret = ecdhRaw(s, p) + let secret = ecdhSecret(s, p) check: expect == secret.data @@ -189,7 +189,7 @@ suite "ECC/ECDSA/ECDHE tests suite": var s = PrivateKey.fromHex(stripSpaces(s0))[] var p = PublicKey.fromHex(stripSpaces(p0))[] let expect = fromHex(stripSpaces(e0)) - let secret = ecdhRaw(s, p) + let secret = ecdhSecret(s, p) check: compare(expect, secret.data) == true @@ -242,8 +242,8 @@ suite "ECC/ECDSA/ECDHE tests suite": var alicePublic = aliceSecret.toPublicKey() var bobSecret = PrivateKey.random(rng[]) var bobPublic = bobSecret.toPublicKey() - var secret1 = ecdhRaw(aliceSecret, bobPublic) - var secret2 = ecdhRaw(bobSecret, alicePublic) + var secret1 = ecdhSecret(aliceSecret, bobPublic) + var secret2 = ecdhSecret(bobSecret, alicePublic) check: secret1 == secret2 diff --git a/tests/p2p/test_discoveryv5_encoding.nim b/tests/p2p/test_discoveryv5_encoding.nim index 0c0ba19..0c02099 100644 --- a/tests/p2p/test_discoveryv5_encoding.nim +++ b/tests/p2p/test_discoveryv5_encoding.nim @@ -180,7 +180,7 @@ suite "Discovery v5.1 Cryptographic Primitives Test Vectors": let pub = PublicKey.fromHex(publicKey)[] priv = PrivateKey.fromHex(secretKey)[] - eph = ecdhRawFull(priv, pub) + eph = ecdhSecretFull(priv, pub) check: eph.data == hexToSeqByte(sharedSecret)