From 14686714c04e3da3342495c97aa0b6dd38b31339 Mon Sep 17 00:00:00 2001 From: cheatfate Date: Fri, 1 Mar 2019 08:34:52 +0200 Subject: [PATCH] Add raw init functions for ecnist.nim Add ECDHE helpers for ecnist.nim Add test vectors for ECDHE. --- libp2p/crypto/ecnist.nim | 146 ++++++++++++++++++++++++++++++++++++++- tests/testecnist.nim | 107 ++++++++++++++++++++++++++++ 2 files changed, 252 insertions(+), 1 deletion(-) diff --git a/libp2p/crypto/ecnist.nim b/libp2p/crypto/ecnist.nim index 37a8b15..4c1c787 100644 --- a/libp2p/crypto/ecnist.nim +++ b/libp2p/crypto/ecnist.nim @@ -549,7 +549,7 @@ proc init*(sig: var EcSignature, data: openarray[byte]): Asn1Status = proc init*[T: EcPKI](sospk: var T, data: string): Asn1Status {.inline.} = ## Initialize EC `private key`, `public key` or `signature` ``sospk`` from - ## hexadecimal string representation ``data``. + ## ASN.1 DER hexadecimal string representation ``data``. ## ## Procedure returns ``Asn1Status``. result = sospk.init(fromHex(data)) @@ -583,6 +583,114 @@ proc init*[T: EcPKI](t: typedesc[T], data: string): T {.inline.} = ## string representation ``data`` and return constructed object. result = t.init(fromHex(data)) +proc initRaw*(key: var EcPrivateKey, data: openarray[byte]): bool = + ## Initialize EC `private key` or `scalar` ``key`` from raw binary + ## representation ``data``. + ## + ## Length of ``data`` array must be ``SecKey256Length``, ``SecKey384Length`` + ## or ``SecKey521Length``. + ## + ## Procedure returns ``true`` on success, ``false`` otherwise. + var curve: cint + if len(data) == SecKey256Length: + curve = cast[cint](Secp256r1) + result = true + elif len(data) == SecKey384Length: + curve = cast[cint](Secp384r1) + result = true + elif len(data) == SecKey521Length: + curve = cast[cint](Secp521r1) + result = true + if result: + result = false + if checkScalar(data, curve) == 1'u32: + let length = len(data) + key = new EcPrivateKey + key.buffer = newSeq[byte](length) + copyMem(addr key.buffer[0], unsafeAddr data[0], length) + key.key.x = cast[ptr cuchar](addr key.buffer[0]) + key.key.xlen = length + key.key.curve = curve + result = true + +proc initRaw*(pubkey: var EcPublicKey, data: openarray[byte]): bool = + ## Initialize EC public key ``pubkey`` from raw binary representation + ## ``data``. + ## + ## Length of ``data`` array must be ``PubKey256Length``, ``PubKey384Length`` + ## or ``PubKey521Length``. + ## + ## Procedure returns ``true`` on success, ``false`` otherwise. + var curve: cint + if len(data) > 0: + if data[0] == 0x04'u8: + if len(data) == PubKey256Length: + curve = cast[cint](Secp256r1) + result = true + elif len(data) == PubKey384Length: + curve = cast[cint](Secp384r1) + result = true + elif len(data) == PubKey521Length: + curve = cast[cint](Secp521r1) + result = true + if result: + result = false + if checkPublic(data, curve) != 0: + let length = len(data) + pubkey = new EcPublicKey + pubkey.buffer = newSeq[byte](length) + copyMem(addr pubkey.buffer[0], unsafeAddr data[0], length) + pubkey.key.q = cast[ptr cuchar](addr pubkey.buffer[0]) + pubkey.key.qlen = length + pubkey.key.curve = curve + result = true + +proc initRaw*(sig: var EcSignature, data: openarray[byte]): bool = + ## Initialize EC signature ``sig`` from raw binary representation ``data``. + ## + ## Length of ``data`` array must be ``Sig256Length``, ``Sig384Length`` + ## or ``Sig521Length``. + ## + ## Procedure returns ``true`` on success, ``false`` otherwise. + var curve: cint + let length = len(data) + if (length == Sig256Length) or (length == Sig384Length) or + (length == Sig521Length): + result = true + if result: + sig = new EcSignature + sig.buffer = @data + +proc initRaw*[T: EcPKI](sospk: var T, data: string): bool {.inline.} = + ## Initialize EC `private key`, `public key` or `signature` ``sospk`` from + ## raw hexadecimal string representation ``data``. + ## + ## Procedure returns ``true`` on success, ``false`` otherwise. + result = sospk.initRaw(fromHex(data)) + +proc initRaw*(t: typedesc[EcPrivateKey], data: openarray[byte]): EcPrivateKey = + ## Initialize EC private key from raw binary representation ``data`` and + ## return constructed object. + if not result.initRaw(data): + raise newException(EcKeyIncorrectError, "Incorrect private key") + +proc initRaw*(t: typedesc[EcPublicKey], data: openarray[byte]): EcPublicKey = + ## Initialize EC public key from raw binary representation ``data`` and + ## return constructed object. + if not result.initRaw(data): + raise newException(EcKeyIncorrectError, "Incorrect public key") + +proc initRaw*(t: typedesc[EcSignature], data: openarray[byte]): EcSignature = + ## Initialize EC signature from raw binary representation ``data`` and + ## return constructed object. + if not result.initRaw(data): + raise newException(EcKeyIncorrectError, "Incorrect signature") + +proc initRaw*[T: EcPKI](t: typedesc[T], data: string): T {.inline.} = + ## Initialize EC `private key`, `public key` or `signature` from raw + ## hexadecimal string representation ``data`` and return constructed object. + result = t.initRaw(fromHex(data)) + proc scalarMul*(pub: EcPublicKey, sec: EcPrivateKey): EcPublicKey = ## Return scalar multiplication of ``pub`` and ``sec``. ## @@ -604,6 +712,42 @@ proc scalarMul*(pub: EcPublicKey, sec: EcPrivateKey): EcPublicKey = if res != 0: result = key +proc toSecret*(pubkey: EcPublicKey, seckey: EcPrivateKey, + data: var openarray[byte]): int = + ## Calculate ECDHE shared secret using Go's elliptic/curve approach, using + ## remote public key ``pubkey`` and local private key ``seckey`` and store + ## shared secret to ``data``. + ## + ## Returns number of bytes (octets) needed to store shared secret, or ``0`` + ## on error. + ## + ## ``data`` array length must be at least 32 bytes for `secp256r1`, 48 bytes + ## for `secp384r1` and 66 bytes for `secp521r1`. + var mult = scalarMul(pubkey, seckey) + var length = 0 + if not isNil(mult): + if seckey.key.curve == BR_EC_SECP256R1: + result = 32 + elif seckey.key.curve == BR_EC_SECP384R1: + result = 48 + elif seckey.key.curve == BR_EC_SECP521R1: + result = 66 + if len(data) >= result: + var qplus1 = cast[pointer](cast[uint](mult.key.q) + 1'u) + copyMem(addr data[0], qplus1, result) + +proc getSecret*(pubkey: EcPublicKey, seckey: EcPrivateKey): seq[byte] = + ## Calculate ECDHE shared secret using Go's elliptic curve approach, using + ## remote public key ``pubkey`` and local private key ``seckey`` and return + ## shared secret. + ## + ## If error happens length of result array will be ``0``. + var data: array[66, byte] + let res = toSecret(pubkey, seckey, data) + if res > 0: + result = newSeq[byte](res) + copyMem(addr result[0], addr data[0], res) + proc sign*[T: byte|char](seckey: EcPrivateKey, message: openarray[T]): EcSignature = ## Get ECDSA signature of data ``message`` using private key ``seckey``. diff --git a/tests/testecnist.nim b/tests/testecnist.nim index f803d4d..9edbf33 100644 --- a/tests/testecnist.nim +++ b/tests/testecnist.nim @@ -229,6 +229,68 @@ const C05CB9C3A2DA""" ] + # ECDHE test vectors was made using Go's elliptic/curve + + ECDHEPrivateKeys = [ + "978edaa0671f5a37ec5372b62689e9328af71b6fb4ac3be24d195ca082b0f2fa", + "a849c80cb4f507ab24569473c2b94a84608088e7cb448816ba60d91ade3c8470", + "a23bf3eb7c515aab57974845d6b8c05c108bc7c5c68b2aa18bc9a05c4668993e", + "1b35c3c3efea9ad4e78f47063cf17bc14ddb89bd9a8980b744a19b92076ae88d", + "24c97a005fc6ce93c96f072d37bf8aeab0b97f2135845655c16df24e09cceafa", + "fa52527eb5cd88d751807d05332164a66c9ae5bc4a4c37e7f21f1f64daad19d9", + """fab46775d806e9135f5db2ee65bc32b530b2db7a3a85f25d69ffe4f39a7f3c01 + 98d863ae1ed71ed3e5b9bbf882020c25""", + """8494fb7f48c31617414fb444d3e0f28225a4ee04aee8e518cb6fece1c5603141 + 525bc96ef570a8361ed1abe74b467daa""", + """760a6543f29654b9b4e7a67a328d6f61895f29df53c031dd457342ba103fe452 + 776791b52a4fd156e5cd0ec005a59ad0""", + """15c44a82d4ab43cef6d8a33c01d41bfeaab21ee1aba6ecb7eff8db31c681337b + 3b11e785df0c7cf2f838be395591810b""", + """16a646076ec966b59655343875980adb7aa25dbf8480b1fad73c0a4699f9262c + bab67a8768754064afd68e1a12e405e6""", + """bfe6d3a1e67f2e75d6204aff913b62163d6bc1ca2a281c0c6a95fbc989adddf8 + 836ada035aa400ba8b47480bcc7b95c3""", + """01920b49ce0ecf2a672ca2843d300140dde242af772ec89e4ad57dadf1610bb0 + 912f613a3e29193a04691a5b4e8ae6130d8b610642c88f99cdd2b5b9e9269c26 + 1fa5""", + """015a7286a1ecc521e49ffed6b9740b2e7ebdb3762cb6be5f4c173c26686105c5 + 73a06d59814c89b67c642e2fc85de46e4242565e64ec82d8ada5ac3e1ab1be4e + 091f""", + """011a8d01ac83d3f7b1ac8ef06afb75ec7c1b176cf01405686cb2ead34f8bc278 + 0972d2e348d49bdc0cb3fbf6414f7815aa4ea83eeed71a6a2fde7070bc074735 + 86c3""", + """014c09dcf8cc8d7fe59c5b2d9edf5c6a0a42700f540f1670e46be35e7edc8ed4 + 01d32513cc9167a386623abd458cef9bd9facd0aa9b1d671b02c19bd71938b00 + d957""", + """0172a4ee2f85ba7ea47e0fee1410344211df58415fbac933c793d138d75c4dd1 + 664cfaf58e13c040f11191438dbf7394e36d7c3b2025d31af19fc6485b979a77 + 2f39""", + """011b3b6ac868ae156c66b140d92092167193e88f04909be61b3592c7006296e3 + cc1dab37955fea9e5ab24047c2ef717e402a88bd616ef27c68cf6976e68b6c69 + b326""" + ] + + ECDHESecrets = [ + "0e6c0eb060c5e33b7125ab72fa70b21b472b64e137ac11238da14838dfb1603c", + "1b42c4831dca82787ff4f411b3774165e788ead225bd34291d6582f714849984", + "568ff01d85d325e31ee0eafb1be4ce7695aa996c5d8d8a804052f819b4c6baae", + """595e63e5b14747b2c87f7042122327fbf9409ccecad2d794f706e84150ea8d0d + 67d0b2cdf3a7b2491db83188f64bc5c2""", + """ad60579d3d6ab4d512d3e493d8d432fd0c5f91de61cf9375141c7521db792273 + 88482177eb2c96e789830ff51ffe1955""", + """6a04cfb7cb3514b8326bac52ff09d24e320d2662b9a7964a58c1fe32c25167e5 + d96dc59193b0d9a7463652a3e7096daf""", + """0037b9b156c934a594ce991ec904c28b257de50930076b9702186c0f0c4affae + 02d3c5ff1c896339dbcb9f9d11a86a4c27d705a22e4a5297cb389cbb1bf55e47 + c07c""", + """00f7916c1119c46d4c499f5c73cae3279466b104f87ef4c5092f38148b2dad84 + c18ecf7ce439ec59799c086557453484b454722d23135291d9d4ffe6e1719f16 + 3d25""", + """018c54bd7cb4aab23b07760c48e563a1828ff521442e5388eb62f916c33a8db2 + ec8bc6a5c8c41b3ebb09f0cf66bbae602d355161c97597b088060bb8456a4458 + 35ab""" + ] + suite "EC NIST-P256/384/521 test suite": test "[secp256r1] Private key serialize/deserialize test": @@ -279,6 +341,21 @@ suite "EC NIST-P256/384/521 test suite": isNil(shared2) == false shared1 == shared2 + test "[secp256r1] ECDHE test vectors": + for i in 0..<3: + var key1 = fromHex(stripSpaces(ECDHEPrivateKeys[i * 2])) + var key2 = fromHex(stripSpaces(ECDHEPrivateKeys[i * 2 + 1])) + var seckey1 = EcPrivateKey.initRaw(key1) + var seckey2 = EcPrivateKey.initRaw(key2) + var pubkey1 = seckey1.getKey() + var pubkey2 = seckey2.getKey() + var secret1 = getSecret(pubkey2, seckey1) + var secret2 = getSecret(pubkey1, seckey2) + var expsecret = fromHex(stripSpaces(ECDHESecrets[i])) + check: + secret1 == expsecret + secret2 == expsecret + test "[secp256r1] ECDSA test vectors": for i in 0..<2: var sk = EcPrivateKey.init(stripSpaces(SignatureSecKeys[i])) @@ -371,6 +448,21 @@ suite "EC NIST-P256/384/521 test suite": isNil(shared2) == false shared1 == shared2 + test "[secp384r1] ECDHE test vectors": + for i in 3..<6: + var key1 = fromHex(stripSpaces(ECDHEPrivateKeys[i * 2])) + var key2 = fromHex(stripSpaces(ECDHEPrivateKeys[i * 2 + 1])) + var seckey1 = EcPrivateKey.initRaw(key1) + var seckey2 = EcPrivateKey.initRaw(key2) + var pubkey1 = seckey1.getKey() + var pubkey2 = seckey2.getKey() + var secret1 = getSecret(pubkey2, seckey1) + var secret2 = getSecret(pubkey1, seckey2) + var expsecret = fromHex(stripSpaces(ECDHESecrets[i])) + check: + secret1 == expsecret + secret2 == expsecret + test "[secp384r1] ECDSA test vectors": for i in 2..<4: var sk = EcPrivateKey.init(stripSpaces(SignatureSecKeys[i])) @@ -463,6 +555,21 @@ suite "EC NIST-P256/384/521 test suite": isNil(shared2) == false shared1 == shared2 + test "[secp384r1] ECDHE test vectors": + for i in 6..<9: + var key1 = fromHex(stripSpaces(ECDHEPrivateKeys[i * 2])) + var key2 = fromHex(stripSpaces(ECDHEPrivateKeys[i * 2 + 1])) + var seckey1 = EcPrivateKey.initRaw(key1) + var seckey2 = EcPrivateKey.initRaw(key2) + var pubkey1 = seckey1.getKey() + var pubkey2 = seckey2.getKey() + var secret1 = getSecret(pubkey2, seckey1) + var secret2 = getSecret(pubkey1, seckey2) + var expsecret = fromHex(stripSpaces(ECDHESecrets[i])) + check: + secret1 == expsecret + secret2 == expsecret + test "[secp521r1] ECDSA test vectors": for i in 4..<6: var sk = EcPrivateKey.init(stripSpaces(SignatureSecKeys[i]))