diff --git a/eth/keys/libsecp256k1.nim b/eth/keys/libsecp256k1.nim index cd68b3a..aa62ab8 100644 --- a/eth/keys/libsecp256k1.nim +++ b/eth/keys/libsecp256k1.nim @@ -13,7 +13,8 @@ import secp256k1, nimcrypto/sysrand, nimcrypto/utils const KeyLength* = 256 div 8 CompressedPubKeyLength* = 33 - RawSignatureSize* = KeyLength * 2 + 1 + RawSignatureNRSize* = KeyLength * 2 # Non-recoverable signature + RawSignatureSize* = RawSignatureNRSize + 1 # Recoverable RawPublicKeySize* = KeyLength * 2 InvalidPrivateKey = "Invalid private key!" InvalidPublicKey = "Invalid public key!" @@ -33,6 +34,11 @@ type ## Representation of ECDH shared secret data*: array[KeyLength, byte] + SharedSecretFull* = object + ## Representation of ECDH shared secret, with leading `y` byte + # (`y` is 0x02 when pubkey.y is even or 0x03 when odd) + data*: array[1 + KeyLength, byte] + KeyPair* = object ## Representation of private/public keys pair seckey*: PrivateKey @@ -41,6 +47,9 @@ type Signature* = secp256k1_ecdsa_recoverable_signature ## Representation of signature + SignatureNR* = secp256k1_ecdsa_signature + ## Representation of non-recoverable signature + Secp256k1Exception* = object of Exception ## Exceptions generated by `libsecp256k1` @@ -175,12 +184,23 @@ proc recoverPublicKey*(data: openarray[byte], result = EthKeysStatus.Success -proc recoverSignature*(data: openarray[byte], - signature: var Signature): EthKeysStatus = +proc parseCompact*(signature: var SignatureNR, data: openarray[byte]): EthKeysStatus = + let ctx = getSecpContext() + let length = len(data) + if length == RawSignatureNRSize: + if secp256k1_ecdsa_signature_parse_compact(ctx, addr signature, + cast[ptr cuchar](unsafeAddr data[0])) != 1: + return(EthKeysStatus.Error) + else: + setErrorMsg(InvalidSignature) + return(EthKeysStatus.Error) + result = EthKeysStatus.Success + +proc parseCompact*(signature: var Signature, data: openarray[byte]): EthKeysStatus = ## Unserialize signature from `data`. let ctx = getSecpContext() let length = len(data) - if length < RawSignatureSize: + if length != RawSignatureSize: setErrorMsg(InvalidSignature) return(EthKeysStatus.Error) var recid = cint(data[KeyLength * 2]) @@ -190,6 +210,14 @@ proc recoverSignature*(data: openarray[byte], return(EthKeysStatus.Error) result = EthKeysStatus.Success +proc recoverSignature*(data: openarray[byte], + signature: var Signature): EthKeysStatus {.deprecated.} = + ## Deprecated, use `parseCompact` instead + if data.len < RawSignatureSize: + setErrorMsg(InvalidSignature) + return(EthKeysStatus.Error) + signature.parseCompact(data.toOpenArray(0, RawSignatureSize - 1)) + proc initPublicKey*(hexstr: string): PublicKey = ## Create new public key from hexadecimal string representation. var o = fromHex(stripSpaces(hexstr)) @@ -212,6 +240,16 @@ proc initSignature*(data: openarray[byte]): Signature = if recoverSignature(data, result) != EthKeysStatus.Success: raise newException(EthKeysException, libsecp256k1ErrorMsg()) +proc ecdhAgree*(seckey: PrivateKey, pubkey: PublicKey, + secret: var SharedSecretFull): EthKeysStatus = + ## Calculate ECDH shared secret. + let ctx = getSecpContext() + if secp256k1_ecdh_raw(ctx, cast[ptr cuchar](addr secret.data), + unsafeAddr pubkey, + cast[ptr cuchar](unsafeAddr seckey)) != 1: + return(EthKeysStatus.Error) + return(EthKeysStatus.Success) + proc ecdhAgree*(seckey: PrivateKey, pubkey: PublicKey, secret: var SharedSecret): EthKeysStatus = ## Calculate ECDH shared secret. @@ -255,15 +293,6 @@ proc getRawCompressed*(pubkey: PublicKey): array[CompressedPubKeyLength, byte] { ## Converts public key `pubkey` to serialized form. pubkey.toRaw(result, true) -proc getRaw*(s: Signature): array[RawSignatureSize, byte] {.noinit.} = - ## Converts signature `s` to serialized form. - let ctx = getSecpContext() - var recid = cint(0) - if secp256k1_ecdsa_recoverable_signature_serialize_compact( - ctx, cast[ptr cuchar](unsafeAddr result), addr recid, unsafeAddr s) != 1: - raiseSecp256k1Error() - result[64] = uint8(recid) - proc toRaw*(s: Signature, data: var openarray[byte]) = ## Converts signature `s` to serialized form and store it in `data`. let ctx = getSecpContext() @@ -274,6 +303,22 @@ proc toRaw*(s: Signature, data: var openarray[byte]) = raiseSecp256k1Error() data[64] = uint8(recid) +proc getRaw*(s: Signature): array[RawSignatureSize, byte] {.noinit.} = + ## Converts signature `s` to serialized form. + s.toRaw(result) + +proc toRaw*(s: SignatureNR, data: var openarray[byte]) = + ## Converts signature `s` to serialized form and store it in `data`. + doAssert(len(data) == RawSignatureNRSize) + let ctx = getSecpContext() + if secp256k1_ecdsa_signature_serialize_compact(ctx, cast[ptr cuchar](addr data[0]), + unsafeAddr s) != 1: + raiseSecp256k1Error() + +proc getRaw*(s: SignatureNR): array[RawSignatureNRSize, byte] {.noinit.} = + ## Converts signature `s` to serialized form. + s.toRaw(result) + proc recoverSignatureKey*(data: openarray[byte], msg: openarray[byte], pubkey: var PublicKey): EthKeysStatus = @@ -330,3 +375,34 @@ proc signRawMessage*(data: openarray[byte], seckey: PrivateKey, return(EthKeysStatus.Error) return(EthKeysStatus.Success) +proc signRawMessage*(data: openarray[byte], seckey: PrivateKey, + signature: var SignatureNR): EthKeysStatus = + ## Sign message `data` of `KeyLength` size using private key `seckey` and + ## store result into `signature`. + let ctx = getSecpContext() + let length = len(data) + if length != KeyLength: + setErrorMsg(MessageSizeError) + return(EthKeysStatus.Error) + if secp256k1_ecdsa_sign(ctx, addr signature, + cast[ptr cuchar](unsafeAddr data[0]), + cast[ptr cuchar](unsafeAddr seckey), + nil, nil) != 1: + return(EthKeysStatus.Error) + return(EthKeysStatus.Success) + +proc verifySignatureRaw*(signature: SignatureNR, message: openarray[byte], + publicKey: PublicKey): EthKeysStatus = + ## Verify `signature` using original `message` (32 bytes) and `publicKey`. + let ctx = getSecpContext() + if len(message) != KeyLength: + setErrorMsg(MessageSizeError) + return(EthKeysStatus.Error) + + if secp256k1_ecdsa_verify(ctx, unsafeAddr signature, + cast[ptr cuchar](unsafeAddr message[0]), + unsafeAddr publicKey) != 1: + setErrorMsg(VerificationFailed) + return(EthKeysStatus.Error) + + return(EthKeysStatus.Success)