From e8b33c64fac2e69e82fab7d7b8fadb2e2262a49a Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Fri, 17 Apr 2020 12:51:13 +0200 Subject: [PATCH] secp: use upstream secp convenience api (#141) * secp: use upstream secp convenience api --- libp2p.nimble | 1 - libp2p/crypto/secp.nim | 271 +++++++++++----------------------------- tests/testsecp256k1.nim | 9 +- 3 files changed, 77 insertions(+), 204 deletions(-) diff --git a/libp2p.nimble b/libp2p.nimble index 134b30f..8f5aced 100644 --- a/libp2p.nimble +++ b/libp2p.nimble @@ -8,7 +8,6 @@ license = "MIT" skipDirs = @["tests", "examples", "Nim"] requires "nim >= 1.2.0", - "secp256k1", "nimcrypto >= 0.4.1", "chronos >= 2.3.8", "bearssl >= 0.1.4", diff --git a/libp2p/crypto/secp.nim b/libp2p/crypto/secp.nim index 1dbd5cc..d1a5475 100644 --- a/libp2p/crypto/secp.nim +++ b/libp2p/crypto/secp.nim @@ -6,8 +6,7 @@ ## at your option. ## This file may not be copied, modified, or distributed except according to ## those terms. -import secp256k1, nimcrypto/sysrand, nimcrypto/utils, nimcrypto/hash, - nimcrypto/sha2 +import secp256k1 as s, stew/byteutils, nimcrypto/hash, nimcrypto/sha2 export sha2 const @@ -19,177 +18,104 @@ const ## Size of public key in octets (bytes) type - SkPublicKey* = secp256k1_pubkey - ## Representation of public key. - - SkPrivateKey* = object - ## Representation of secret key. - data*: array[SkRawPrivateKeySize, byte] - - SkKeyPair* = object - ## Representation of private/public keys pair. - seckey*: SkPrivateKey - pubkey*: SkPublicKey - - SkSignature* = secp256k1_ecdsa_signature - ## Representation of signature. - - SkContext* = ref object - ## Representation of Secp256k1 context object. - context: ptr secp256k1_context - error: string + SkPublicKey* = distinct s.SkPublicKey + SkPrivateKey* = distinct s.SkSecretKey + SkKeyPair* = distinct s.SkKeyPair + SkSignature* = distinct s.SkSignature Secp256k1Error* = object of CatchableError ## Exceptions generated by `libsecp256k1` -## -## Private procedures interface -## +template toException(v: cstring): (ref Secp256k1Error) = + (ref Secp256k1Error)(msg: $v) -var secpContext {.threadvar.}: SkContext - ## Thread local variable which holds current context +template pubkey*(v: SkKeyPair): SkPublicKey = SkPublicKey(s.SkKeyPair(v).pubkey) +template seckey*(v: SkKeyPair): SkPrivateKey = SkPrivateKey(s.SkKeyPair(v).seckey) -proc illegalCallback(message: cstring, data: pointer) {.cdecl, raises: [].} = - let ctx = cast[SkContext](data) - ctx.error = $message - -proc errorCallback(message: cstring, data: pointer) {.cdecl, raises: [].} = - let ctx = cast[SkContext](data) - ctx.error = $message - -proc shutdownLibsecp256k1(ctx: SkContext) = - # TODO: use destructor when finalizer are deprecated for destructors - if not(isNil(ctx.context)): - secp256k1_context_destroy(ctx.context) - -proc newSkContext(): SkContext = - ## Create new Secp256k1 context object. - new(result, shutdownLibsecp256k1) - let flags = cuint(SECP256K1_CONTEXT_VERIFY or SECP256K1_CONTEXT_SIGN) - result.context = secp256k1_context_create(flags) - secp256k1_context_set_illegal_callback(result.context, illegalCallback, - cast[pointer](result)) - secp256k1_context_set_error_callback(result.context, errorCallback, - cast[pointer](result)) - result.error = "" - -proc getContext(): SkContext = - ## Get current `EccContext` - if isNil(secpContext): - secpContext = newSkContext() - result = secpContext - -template raiseSecp256k1Error() = - ## Raises `libsecp256k1` error as exception - let mctx = getContext() - if len(mctx.error) > 0: - let msg = mctx.error - mctx.error.setLen(0) - raise newException(Secp256k1Error, msg) - else: - raise newException(Secp256k1Error, "") - -proc init*(key: var SkPrivateKey, data: openarray[byte]): bool = +proc init*(key: var SkPrivateKey, data: openarray[byte]): bool {.raises: [Defect].} = ## Initialize Secp256k1 `private key` ``key`` from raw binary ## representation ``data``. ## ## Procedure returns ``true`` on success. - let ctx = getContext() - if len(data) >= SkRawPrivateKeySize: - let res = secp256k1_ec_seckey_verify(ctx.context, - cast[ptr cuchar](unsafeAddr data[0])) - result = (res == 1) and (len(ctx.error) == 0) - if result: - copyMem(addr key.data[0], unsafeAddr data[0], SkRawPrivateKeySize) + if (let v = SkSecretKey.fromRaw(data); v.isOk): + key = SkPrivateKey(v[]) + return true -proc init*(key: var SkPrivateKey, data: string): bool {.inline.} = +proc init*(key: var SkPrivateKey, data: string): bool {.raises: [Defect].} = ## Initialize Secp256k1 `private key` ``key`` from hexadecimal string ## representation ``data``. ## ## Procedure returns ``true`` on success. - var buffer: seq[byte] try: - buffer = utils.fromHex(stripSpaces(data)) - except: - return false - result = init(key, buffer) + key = SkPrivateKey(SkSecretKey.fromHex(data).tryGet()) + return true + except Secp256k1Error: + discard -proc init*(key: var SkPublicKey, data: openarray[byte]): bool = +proc init*(key: var SkPublicKey, data: openarray[byte]): bool {.raises: [Defect].} = ## Initialize Secp256k1 `public key` ``key`` from raw binary ## representation ``data``. ## ## Procedure returns ``true`` on success. - let ctx = getContext() - var length = 0 - if len(data) > 0: - if data[0] == 0x02'u8 or data[0] == 0x03'u8: - length = min(len(data), 33) - elif data[0] == 0x04'u8 or data[0] == 0x06'u8 or data[0] == 0x07'u8: - length = min(len(data), 65) - else: - return false - let res = secp256k1_ec_pubkey_parse(ctx.context, addr key, - cast[ptr cuchar](unsafeAddr data[0]), - csize_t(length)) - result = (res == 1) and (len(ctx.error) == 0) + try: + key = SkPublicKey(s.SkPublicKey.fromRaw(data).tryGet()) + return true + except Secp256k1Error: + discard -proc init*(key: var SkPublicKey, data: string): bool = +proc init*(key: var SkPublicKey, data: string): bool {.raises: [Defect].} = ## Initialize Secp256k1 `public key` ``key`` from hexadecimal string ## representation ``data``. ## ## Procedure returns ``true`` on success. - var buffer: seq[byte] try: - buffer = utils.fromHex(stripSpaces(data)) - except: - return false - result = init(key, buffer) + key = SkPublicKey(s.SkPublicKey.fromHex(data).tryGet()) + return true + except Secp256k1Error: + discard -proc init*(sig: var SkSignature, data: openarray[byte]): bool = +proc init*(sig: var SkSignature, data: openarray[byte]): bool {.raises: [Defect].} = ## Initialize Secp256k1 `signature` ``sig`` from raw binary ## representation ``data``. ## ## Procedure returns ``true`` on success. - let ctx = getContext() - let length = len(data) - if length >= 0: - let res = secp256k1_ecdsa_signature_parse_der(ctx.context, addr sig, - cast[ptr cuchar](unsafeAddr data[0]), - csize_t(length)) - result = (res == 1) and (len(ctx.error) == 0) + try: + sig = SkSignature(s.SkSignature.fromDer(data).tryGet()) + return true + except Secp256k1Error: + discard -proc init*(sig: var SkSignature, data: string): bool = +proc init*(sig: var SkSignature, data: string): bool {.raises: [Defect].} = ## Initialize Secp256k1 `signature` ``sig`` from hexadecimal string ## representation ``data``. ## ## Procedure returns ``true`` on success. + # TODO DER vs raw here is fishy var buffer: seq[byte] try: - buffer = utils.fromHex(stripSpaces(data)) - except: + buffer = hexToSeqByte(data) + except ValueError: return false result = init(sig, buffer) proc init*(t: typedesc[SkPrivateKey], - data: openarray[byte]): SkPrivateKey {.inline.} = + data: openarray[byte]): SkPrivateKey {.raises: [Defect, Secp256k1Error].} = ## Initialize Secp256k1 `private key` from raw binary ## representation ``data``. ## ## Procedure returns `private key` on success. - if not init(result, data): - raise newException(Secp256k1Error, "Incorrect binary form") + SkPrivateKey(s.SkSecretKey.fromRaw(data).tryGet()) proc init*(t: typedesc[SkPrivateKey], - data: string): SkPrivateKey {.inline.} = + data: string): SkPrivateKey {.raises: [Defect, Secp256k1Error].} = ## Initialize Secp256k1 `private key` from hexadecimal string ## representation ``data``. ## ## Procedure returns `private key` on success. - if not init(result, data): - raise newException(Secp256k1Error, "Incorrect binary form") + s.SkSecretKey.fromHex(data).tryGet() proc init*(t: typedesc[SkPublicKey], - data: openarray[byte]): SkPublicKey {.inline.} = + data: openarray[byte]): SkPublicKey {.raises: [Defect, Secp256k1Error].} = ## Initialize Secp256k1 `public key` from raw binary ## representation ``data``. ## @@ -198,7 +124,7 @@ proc init*(t: typedesc[SkPublicKey], raise newException(Secp256k1Error, "Incorrect binary form") proc init*(t: typedesc[SkPublicKey], - data: string): SkPublicKey {.inline.} = + data: string): SkPublicKey {.raises: [Defect, Secp256k1Error].} = ## Initialize Secp256k1 `public key` from hexadecimal string ## representation ``data``. ## @@ -207,7 +133,7 @@ proc init*(t: typedesc[SkPublicKey], raise newException(Secp256k1Error, "Incorrect binary form") proc init*(t: typedesc[SkSignature], - data: openarray[byte]): SkSignature {.inline.} = + data: openarray[byte]): SkSignature {.raises: [Defect, Secp256k1Error].} = ## Initialize Secp256k1 `signature` from raw binary ## representation ``data``. ## @@ -216,7 +142,7 @@ proc init*(t: typedesc[SkSignature], raise newException(Secp256k1Error, "Incorrect binary form") proc init*(t: typedesc[SkSignature], - data: string): SkSignature {.inline.} = + data: string): SkSignature {.raises: [Defect, Secp256k1Error].} = ## Initialize Secp256k1 `signature` from hexadecimal string ## representation ``data``. ## @@ -224,28 +150,17 @@ proc init*(t: typedesc[SkSignature], if not init(result, data): raise newException(Secp256k1Error, "Incorrect binary form") -proc getKey*(key: SkPrivateKey): SkPublicKey = +proc getKey*(key: SkPrivateKey): SkPublicKey {.raises: [Defect, Secp256k1Error].} = ## Calculate and return Secp256k1 `public key` from `private key` ``key``. - let ctx = getContext() - let res = secp256k1_ec_pubkey_create(ctx.context, addr result, - cast[ptr cuchar](unsafeAddr key)) - if (res != 1) or (len(ctx.error) != 0): - raiseSecp256k1Error() + SkPublicKey(s.SkSecretKey(key).toPublicKey().tryGet()) proc random*(t: typedesc[SkPrivateKey]): SkPrivateKey = ## Generates new random private key. - let ctx = getContext() - while true: - if randomBytes(result.data) == SkRawPrivateKeySize: - let res = secp256k1_ec_seckey_verify(ctx.context, - cast[ptr cuchar](addr result.data[0])) - if (res == 1) and (len(ctx.error) == 0): - break + SkPrivateKey(s.SkSecretKey.random().tryGet()) proc random*(t: typedesc[SkKeyPair]): SkKeyPair {.inline.} = ## Generates new random key pair. - result.seckey = SkPrivateKey.random() - result.pubkey = result.seckey.getKey() + SkKeyPair(s.SkKeyPair.random().tryGet()) proc toBytes*(key: SkPrivateKey, data: var openarray[byte]): int = ## Serialize Secp256k1 `private key` ``key`` to raw binary form and store it @@ -255,7 +170,7 @@ proc toBytes*(key: SkPrivateKey, data: var openarray[byte]): int = ## Secp256k1 private key. result = SkRawPrivateKeySize if len(data) >= SkRawPrivateKeySize: - copyMem(addr data[0], unsafeAddr key.data[0], SkRawPrivateKeySize) + data[0..= SkRawPublicKeySize: - discard secp256k1_ec_pubkey_serialize(ctx.context, - cast[ptr cuchar](addr data[0]), - addr length, unsafeAddr key, - SECP256K1_EC_COMPRESSED) + data[0..= result: - copyMem(addr data[0], addr buffer[0], result) + s.SkSignature(sig).toDer(data) proc getBytes*(key: SkPrivateKey): seq[byte] {.inline.} = ## Serialize Secp256k1 `private key` and return it. - result = @(key.data) + result = @(s.SkSecretKey(key).toRaw()) proc getBytes*(key: SkPublicKey): seq[byte] {.inline.} = ## Serialize Secp256k1 `public key` and return it. - result = newSeq[byte](SkRawPublicKeySize) - discard toBytes(key, result) + result = @(s.SkPublicKey(key).toRawCompressed()) proc getBytes*(sig: SkSignature): seq[byte] {.inline.} = ## Serialize Secp256k1 `signature` and return it. @@ -306,74 +206,47 @@ proc getBytes*(sig: SkSignature): seq[byte] {.inline.} = proc `==`*(ska, skb: SkPrivateKey): bool = ## Compare Secp256k1 `private key` objects for equality. - result = (ska.data == skb.data) + result = (s.SkSecretKey(ska).toRaw == s.SkSecretKey(skb).toRaw) proc `==`*(pka, pkb: SkPublicKey): bool = ## Compare Secp256k1 `public key` objects for equality. - var - akey: array[SkRawPublicKeySize, byte] - bkey: array[SkRawPublicKeySize, byte] - discard pka.toBytes(akey) - discard pkb.toBytes(bkey) - result = (akey == bkey) + result = (s.SkPublicKey(pka).toRaw == s.SkPublicKey(pkb).toRaw) proc `==`*(sia, sib: SkSignature): bool = ## Compare Secp256k1 `signature` objects for equality. - var - asig: array[SkRawSignatureSize, byte] - bsig: array[SkRawSignatureSize, byte] - discard sia.toBytes(asig) - discard sib.toBytes(bsig) - result = (asig == bsig) + result = (s.SkSignature(sia).toRaw == s.SkSignature(sib).toRaw) -proc `$`*(key: SkPrivateKey): string = toHex(key.data) +proc `$`*(key: SkPrivateKey): string = s.SkSecretKey(key).toHex() ## Return string representation of Secp256k1 `private key`. proc `$`*(key: SkPublicKey): string = ## Return string representation of Secp256k1 `private key`.s - var spub: array[SkRawPublicKeySize, byte] - discard key.toBytes(spub) - result = toHex(spub) + result = toHex(s.SkPublicKey(key).toRawCompressed()) proc `$`*(sig: SkSignature): string = ## Return string representation of Secp256k1 `signature`.s - result = toHex(sig.data) + result = toHex(s.SkSignature(sig).toDer()) proc sign*[T: byte|char](key: SkPrivateKey, msg: openarray[T]): SkSignature = ## Sign message `msg` using private key `key` and return signature object. - let ctx = getContext() - var hash = sha256.digest(msg) - let res = secp256k1_ecdsa_sign(ctx.context, addr result, - cast[ptr cuchar](addr hash.data[0]), - cast[ptr cuchar](unsafeAddr key), - nil, nil) - if (res != 1) or (len(ctx.error) != 0): - raiseSecp256k1Error() + let h = sha256.digest(msg) + SkSignature(sign(s.SkSecretKey(key), h).tryGet()) proc verify*[T: byte|char](sig: SkSignature, msg: openarray[T], key: SkPublicKey): bool = - let ctx = getContext() - var hash = sha256.digest(msg) - let res = secp256k1_ecdsa_verify(ctx.context, unsafeAddr sig, - cast[ptr cuchar](addr hash.data[0]), - unsafeAddr key) - if (res == 1) and (len(ctx.error) == 0): - result = true + let h = sha256.digest(msg) + verify(s.SkSignature(sig), h, s.SkPublicKey(key)) -proc clear*(key: var SkPrivateKey) {.inline.} = +proc clear*(key: var SkPrivateKey) {.borrow.} ## Wipe and clear memory of Secp256k1 `private key`. - burnMem(key.data) -proc clear*(key: var SkPublicKey) {.inline.} = +proc clear*(key: var SkPublicKey) {.borrow.} ## Wipe and clear memory of Secp256k1 `public key`. - burnMem(addr key, SkRawPrivateKeySize * 2) -proc clear*(sig: var SkSignature) {.inline.} = +proc clear*(sig: var SkSignature) {.borrow.} ## Wipe and clear memory of Secp256k1 `signature`. # Internal memory representation size of signature object is 64 bytes. - burnMem(addr sig, SkRawPrivateKeySize * 2) -proc clear*(pair: var SkKeyPair) {.inline.} = - ## Wipe and clear memory of Secp256k1 `key pair`. - pair.seckey.clear() - pair.pubkey.clear() +proc clear*(pair: var SkKeyPair) {.borrow.} + +proc verify*(seckey: SkPrivateKey): bool {.borrow.} diff --git a/tests/testsecp256k1.nim b/tests/testsecp256k1.nim index 26e7608..6af8d47 100644 --- a/tests/testsecp256k1.nim +++ b/tests/testsecp256k1.nim @@ -35,8 +35,8 @@ suite "Secp256k1 testing suite": rkey1.clear() rkey2.clear() check: - isFullZero(rkey1.data) == true - isFullZero(rkey2.data) == true + not rkey1.verify() + not rkey2.verify() test "Public key serialize/deserialize test": for i in 0..