refactor secp interface (#203)

* avoid mix of exceptions and return codes
* introduce result
* use deprecated compatibility API to avoid big-bang refactoring
* loosely based on nim-libp2p secp as well as the rust-secp256k1 wrapper
* oh, boy does our secp256k1 wrapper need updating - we're _far_
behind...
This commit is contained in:
Jacek Sieka 2020-04-02 14:40:29 +02:00 committed by GitHub
parent 5dc0a533b0
commit 02d661503f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 769 additions and 507 deletions

View File

@ -6,105 +6,95 @@
# - MIT license (LICENSE-MIT)
#
import nimcrypto/hash, nimcrypto/keccak
# This module contains adaptations of the general secp interface to help make
# working with keys and signatures as they appear in Ethereum in particular:
#
# * Public keys as serialized in uncompressed format without the initial byte
# * Shared secrets are serialized in raw format without the intial byte
import
nimcrypto/hash, nimcrypto/keccak, ./keys/secp,
stew/[byteutils, objects, result], strformat
export secp, result
const
KeyLength* = SkEcdhRawSecretSize - 1
## Shared secret key length without format marker
RawPublicKeySize* = SkRawPublicKeySize - 1
## Size of uncompressed public key without format marker (0x04)
RawSignatureSize* = SkRawRecoverableSignatureSize
type
EthKeysStatus* = enum
Success, ## Operation was successful
Error ## Operation failed
PrivateKey* = distinct SkSecretKey
EthKeysException* = object of CatchableError
## Exception generated by this module
PublicKey* = distinct SkPublicKey
## Public key that's serialized to raw format without 0x04 marker
Signature* = distinct SkRecoverableSignature
## Ethereum uses recoverable signatures allowing some space savings
SignatureNR* = distinct SkSignature
## ...but ENR uses non-recoverable signatures!
when not defined(native):
include eth/keys/libsecp256k1
SharedSecretFull* = SkEcdhRawSecret
SharedSecret* = object
data*: array[KeyLength, byte]
proc ekErrorMsg*(): string {.inline.} =
## Return current error message.
result = libsecp256k1ErrorMsg()
KeyPair* = object
seckey*: PrivateKey
pubkey*: PublicKey
proc isZeroKey*(seckey: PrivateKey): bool =
## Check if private key `seckey` contains only 0 bytes.
result = true
for i in seckey.data:
if i != byte(0):
result = false
proc toPublicKey*(seckey: PrivateKey): SkResult[PublicKey] =
SkSecretKey(seckey).toPublicKey().mapConvert(PublicKey)
proc isZeroKey*(pubkey: PublicKey): bool =
## Check if public key `pubkey` contains only 0 bytes.
result = true
for i in pubkey.data:
if i != byte(0):
result = false
proc fromRaw*(T: type PrivateKey, data: openArray[byte]): SkResult[PrivateKey] =
SkSecretKey.fromRaw(data).mapConvert(PrivateKey)
proc signMessage*(seckey: PrivateKey,
data: openarray[byte]): Signature {.inline.} =
## Sign message of arbitrary length `data` using private key `seckey`.
let hash = keccak256.digest(data)
if signRawMessage(hash.data, seckey, result) != EthKeysStatus.Success:
raise newException(EthKeysException, ekErrorMsg())
proc fromHex*(T: type PrivateKey, data: string): SkResult[PrivateKey] =
SkSecretKey.fromHex(data).mapConvert(PrivateKey)
proc signMessage*(seckey: PrivateKey, data: string): Signature {.inline.} =
## Sign message of arbitrary length `data` using private key `seckey`.
signMessage(seckey, cast[seq[byte]](data))
proc fromRaw*(T: type PublicKey, data: openArray[byte]): SkResult[T] =
if data.len() == SkRawCompressedPubKeySize:
return SkPublicKey.fromRaw(data).mapConvert(PublicKey)
proc signMessage*(seckey: PrivateKey,
hash: MDigest[256]): Signature {.inline.} =
## Sign 256bit `hash` using private key `seckey`.
result = signMessage(seckey, hash.data)
if len(data) < SkRawPublicKeySize - 1:
return err(&"keys: raw eth public key should be {SkRawPublicKeySize - 1} bytes")
proc verifyMessage*(data: openarray[byte], message: openarray[byte]): bool =
## Verify binary data blob `data` has properly signed message `message`.
var pubkey: PublicKey
if recoverSignatureKey(data, message, pubkey) == EthKeysStatus.Success:
result = true
else:
result = false
var d: array[SkRawPublicKeySize, byte]
d[0] = 0x04'u8
copyMem(addr d[1], unsafeAddr data[0], 64)
proc verifyMessage*(data: openarray[byte],
hash: MDigest[256]): bool {.inline.} =
## Verify binary data blob `data` has properly signed hash `hash`.
result = verifyMessage(data, hash.data)
SkPublicKey.fromRaw(d).mapConvert(PublicKey)
proc recoverKeyFromMessage*(data: openarray[byte],
hash: MDigest[256]): PublicKey {.inline.} =
## Recover public key from signed binary blob `data` using 256bit hash `hash`.
if recoverSignatureKey(data, hash.data, result) != EthKeysStatus.Success:
raise newException(EthKeysException, ekErrorMsg())
proc fromHex*(T: type PublicKey, data: string): SkResult[PublicKey] =
try:
# TODO strip string?
T.fromRaw(hexToSeqByte(data))
except CatchableError:
err("keys: cannot parse eth public key")
proc recoverKeyFromMessage*(data: openarray[byte],
message: string): PublicKey {.inline.} =
## Recover public key from signed binary blob `data` using `message`.
var hash = keccak256.digest(message)
if recoverSignatureKey(data, hash.data, result) != EthKeysStatus.Success:
raise newException(EthKeysException, ekErrorMsg())
proc random*(t: type KeyPair): SkResult[KeyPair] =
let tmp = ?SkKeypair.random()
ok(KeyPair(seckey: PrivateKey(tmp.seckey), pubkey: PublicKey(tmp.pubkey)))
proc recoverKeyFromSignature*(signature: Signature,
message: string): PublicKey {.inline.} =
## Recover public key from signature `signature` using `message`.
var hash = keccak256.digest(message)
if recoverSignatureKey(signature, hash.data, result) != EthKeysStatus.Success:
raise newException(EthKeysException, ekErrorMsg())
proc toRaw*(pubkey: PublicKey): array[64, byte] =
let tmp = SkPublicKey(pubkey).toRaw()
copyMem(addr result[0], unsafeAddr tmp[1], 64)
proc recoverKeyFromSignature*(signature: Signature,
hash: MDigest[256]): PublicKey {.inline.} =
## Recover public key from signature `signature` using `message`.
if recoverSignatureKey(signature, hash.data, result) != EthKeysStatus.Success:
raise newException(EthKeysException, ekErrorMsg())
proc toRawCompressed*(pubkey: PublicKey): array[33, byte] {.borrow.}
proc toAddress*(pubkey: PublicKey, with0x = true): string =
## Convert public key to hexadecimal string address.
var hash = keccak256.digest(pubkey.getRaw())
var hash = keccak256.digest(pubkey.toRaw())
result = if with0x: "0x" else: ""
result.add(toHex(toOpenArray(hash.data, 12, len(hash.data) - 1), true))
result.add(toHex(toOpenArray(hash.data, 12, len(hash.data) - 1)))
proc toChecksumAddress*(pubkey: PublicKey, with0x = true): string =
## Convert public key to checksumable mixed-case address (EIP-55).
result = if with0x: "0x" else: ""
var hash1 = keccak256.digest(pubkey.getRaw())
var hhash1 = toHex(toOpenArray(hash1.data, 12, len(hash1.data) - 1), true)
var hash1 = keccak256.digest(pubkey.toRaw())
var hhash1 = toHex(toOpenArray(hash1.data, 12, len(hash1.data) - 1))
var hash2 = keccak256.digest(hhash1)
var hhash2 = toHex(hash2.data, true)
var hhash2 = toHex(hash2.data)
for i in 0..<len(hhash1):
if hhash2[i] >= '0' and hhash2[i] <= '7':
result.add(hhash1[i])
@ -132,7 +122,7 @@ proc validateChecksumAddress*(a: string): bool =
else:
return false
var hash = keccak256.digest(address)
var hexhash = toHex(hash.data, true)
var hexhash = toHex(hash.data)
for i in 0..<len(address):
if hexhash[i] >= '0' and hexhash[i] <= '7':
check.add(address[i])
@ -144,19 +134,293 @@ proc validateChecksumAddress*(a: string): bool =
check.add(ch)
result = (check == a)
proc toCanonicalAddress*(pubkey: PublicKey): array[20, byte] =
func toCanonicalAddress*(pubkey: PublicKey): array[20, byte] =
## Convert public key to canonical address.
var hash = keccak256.digest(pubkey.getRaw())
var hash = keccak256.digest(pubkey.toRaw())
copyMem(addr result[0], addr hash.data[12], 20)
proc `$`*(pubkey: PublicKey): string =
func `$`*(pubkey: PublicKey): string =
## Convert public key to hexadecimal string representation.
result = toHex(pubkey.getRaw(), true)
toHex(pubkey.toRaw())
proc `$`*(sig: Signature): string =
func `$`*(sig: Signature): string =
## Convert signature to hexadecimal string representation.
result = toHex(sig.getRaw(), true)
toHex(SkRecoverableSignature(sig).toRaw())
proc `$`*(seckey: PrivateKey): string =
func `$`*(seckey: PrivateKey): string =
## Convert private key to hexadecimal string representation
result = toHex(seckey.data, true)
toHex(SkSecretKey(seckey).toRaw())
proc `==`*(lhs, rhs: PublicKey): bool {.borrow.}
proc random*(T: type PrivateKey): SkResult[PrivateKey] =
SkSecretKey.random().mapConvert(PrivateKey)
proc toRaw*(key: PrivateKey): array[SkRawSecretKeySize, byte] {.borrow.}
# Backwards compat - the functions in here are deprecated and should be moved
# reimplemented using functions that return Result instead!
from nimcrypto/utils import stripSpaces
type
EthKeysException* {.deprecated.} = object of CatchableError
Secp256k1Exception* {.deprecated.} = object of CatchableError
EthKeysStatus* {.deprecated.} = enum
Success
Error
template data*(pubkey: PublicKey): auto =
SkPublicKey(pubkey).data
template data*(seckey: PrivateKey): auto =
SkSecretKey(seckey).data
template data*(sig: Signature): auto =
SkRecoverableSignature(sig).data
proc isZeroKey*(seckey: PrivateKey): bool {.deprecated.} =
## Check if private key `seckey` contains only 0 bytes.
# TODO this is a weird check - better would be to check if the key is valid!
result = true
for i in seckey.data: # constant time, loop all bytes always
if i != byte(0):
result = false
proc isZeroKey*(pubkey: PublicKey): bool {.deprecated.} =
## Check if public key `pubkey` contains only 0 bytes.
# TODO this is a weird check - better would be to check if the key is valid!
result = true
for i in pubkey.data: # constant time, loop all bytes always
if i != byte(0):
result = false
proc newPrivateKey*(): PrivateKey {.deprecated: "random".} =
let key = PrivateKey.random()
if key.isErr:
raise newException(Secp256k1Exception, $key.error)
key[]
proc newKeyPair*(): KeyPair {.deprecated: "random".} =
let kp = KeyPair.random()
if kp.isErr:
raise newException(Secp256k1Exception, $kp.error)
kp[]
proc getPublicKey*(seckey: PrivateKey): PublicKey {.deprecated: "toPublicKey".} =
let key = seckey.toPublicKey()
if key.isErr:
raise newException(Secp256k1Exception, "invalid private key")
PublicKey(key[])
proc ecdhAgree*(
seckey: PrivateKey, pubkey: PublicKey,
s: var SharedSecret): EthKeysStatus {.deprecated.} =
let v = ecdhRaw(
SkSecretKey(seckey), SkPublicKey(pubkey)).map proc(v: auto): SharedSecret =
copyMem(addr result.data[0], unsafeAddr(v.data[1]), sizeof(result))
if v.isOk():
s = v[]
return Success
return Error
proc getRaw*(
pubkey: PublicKey): array[RawPublicKeySize, byte] {.deprecated: "toRaw".} =
pubkey.toRaw()
proc getRawCompressed*(
pubkey: PublicKey): array[SkRawCompressedPubKeySize, byte] {.
deprecated: "toRawCompressed".} =
pubkey.toRawCompressed()
proc recoverPublicKey*(
data: openArray[byte], pubkey: var PublicKey): EthKeysStatus {.
deprecated: "fromRaw".} =
let v = PublicKey.fromRaw(data)
if v.isOk():
pubkey = v[]
return Success
return Error
proc signRawMessage*(data: openarray[byte], seckey: PrivateKey,
signature: var Signature): EthKeysStatus {.deprecated.} =
if len(data) != SkMessageSize:
return Error
let sig = signRecoverable(
SkSecretKey(seckey), SkMessage(data: toArray(32, data.toOpenArray(0, 31))))
if sig.isOk():
signature = Signature(sig[])
return Success
return Error
proc signRawMessage*(data: openarray[byte], seckey: PrivateKey,
signature: var SignatureNR): EthKeysStatus {.deprecated.} =
## Sign message `data` of `KeyLength` size using private key `seckey` and
## store result into `signature`.
let length = len(data)
if length != KeyLength:
return(EthKeysStatus.Error)
let sig = sign(
SkSecretKey(seckey), SkMessage(data: toArray(32, data.toOpenArray(0, 31))))
if sig.isOk():
signature = SignatureNR(sig[])
return Success
return Error
proc signMessage*(seckey: PrivateKey,
data: openarray[byte]): Signature {.deprecated.} =
let hash = keccak256.digest(data)
if signRawMessage(hash.data, seckey, result) != EthKeysStatus.Success:
raise newException(EthKeysException, "signature failed")
proc getRaw*(
s: SignatureNR): array[SkRawSignatureSize, byte] {.deprecated: "toRaw".} =
## Converts signature `s` to serialized form.
SkSignature(s).toRaw()
proc getRaw*(
s: Signature): array[SkRawRecoverableSignatureSize, byte] {.
deprecated: "toRaw".} =
## Converts signature `s` to serialized form.
SkRecoverableSignature(s).toRaw()
proc recoverSignatureKey*(signature: Signature,
msg: openarray[byte],
pubkey: var PublicKey): EthKeysStatus {.deprecated.} =
if len(msg) < SkMessageSize:
return Error
let pk = recover(
SkRecoverableSignature(signature),
SkMessage(data: toArray(32, msg.toOpenArray(0, 31))))
if pk.isErr(): return Error
pubkey = PublicKey(pk[])
return Success
proc recoverSignatureKey*(data: openarray[byte],
msg: openarray[byte],
pubkey: var PublicKey): EthKeysStatus {.deprecated.} =
let signature = SkRecoverableSignature.fromRaw(data)
if signature.isErr(): return Error
if len(msg) < SkMessageSize:
return Error
let pk = recover(
SkRecoverableSignature(signature[]),
SkMessage(data: toArray(32, msg.toOpenArray(0, 31))))
if pk.isErr(): return Error
pubkey = PublicKey(pk[])
return Success
proc initPrivateKey*(
data: openArray[byte]): PrivateKey {.deprecated: "PrivateKey.fromRaw".} =
let res = PrivateKey.fromRaw(data)
if res.isOk():
return res[]
raise (ref EthKeysException)(msg: $res.error)
proc initPrivateKey*(
data: string): PrivateKey {.deprecated: "PrivateKey.fromHex".} =
let res = PrivateKey.fromHex(stripSpaces(data))
if res.isOk():
return res[]
raise (ref EthKeysException)(msg: $res.error)
proc initPublicKey*(
hexstr: string): PublicKey {.deprecated: "PublicKey.fromHex".} =
let pk = PublicKey.fromHex(stripSpaces(hexstr))
if pk.isOk(): return pk[]
raise newException(EthKeysException, $pk.error)
proc initPublicKey*(data: openarray[byte]): PublicKey {.deprecated.} =
let pk = PublicKey.fromRaw(data)
if pk.isOk(): return pk[]
raise newException(EthKeysException, $pk.error)
proc signMessage*(seckey: PrivateKey, data: string): Signature {.deprecated.} =
signMessage(seckey, cast[seq[byte]](data))
proc toKeyPair*(key: PrivateKey): KeyPair {.deprecated.} =
KeyPair(seckey: key, pubkey: key.getPublicKey())
proc initSignature*(data: openArray[byte]): Signature {.deprecated.} =
let sig = SkRecoverableSignature.fromRaw(data)
if sig.isOk(): return Signature(sig[])
raise newException(EthKeysException, $sig.error)
proc initSignature*(hexstr: string): Signature {.deprecated.} =
let sig = SkRecoverableSignature.fromHex(stripSpaces(hexstr))
if sig.isOk(): return Signature(sig[])
raise newException(EthKeysException, $sig.error)
proc recoverSignature*(data: openarray[byte],
signature: var Signature): EthKeysStatus {.deprecated.} =
## Deprecated, use `parseCompact` instead
if data.len < RawSignatureSize:
return(EthKeysStatus.Error)
let sig = SkRecoverableSignature.fromRaw(data)
if sig.isErr():
return Error
signature = Signature(sig[])
return Success
proc recoverKeyFromSignature*(signature: Signature,
hash: MDigest[256]): PublicKey {.deprecated.} =
## Recover public key from signature `signature` using `message`.
let key = recover(SkRecoverableSignature(signature), hash)
if key.isOk():
return PublicKey(key[])
raise newException(EthKeysException, $key.error)
proc recoverKeyFromSignature*(
signature: Signature,
message: openArray[byte]): PublicKey {.deprecated.} =
let hash = keccak256.digest(message)
recoverKeyFromSignature(signature, hash)
proc recoverKeyFromSignature*(
signature: Signature, data: string): PublicKey {.deprecated.} =
recoverKeyFromSignature(signature, cast[seq[byte]](data))
proc parseCompact*(
signature: var SignatureNR,
data: openarray[byte]): EthKeysStatus {.deprecated.} =
let sig = SkSignature.fromRaw(data)
if sig.isErr():
return Error
signature = SignatureNR(sig[])
return Success
proc verifySignatureRaw*(
signature: SignatureNR, message: openarray[byte],
publicKey: PublicKey): EthKeysStatus {.deprecated.} =
## Verify `signature` using original `message` (32 bytes) and `publicKey`.
if verify(
SkSignature(signature),
SkMessage(data: toArray(32, message.toOpenArray(0, 31))),
SkPublicKey(publicKey)):
return Success
return Error
proc ecdhAgree*(
seckey: PrivateKey, pubkey: PublicKey,
s: var SharedSecretFull): EthKeysStatus {.deprecated.} =
let v = ecdhRaw(SkSecretKey(seckey), SkPublicKey(pubkey))
if v.isOk():
s = SharedSecretFull(v[])
return Success
return Error

View File

@ -1,414 +0,0 @@
#
# Nim Ethereum Keys (nim-eth-keys)
# Copyright (c) 2018 Status Research & Development GmbH
# Licensed under either of
# - Apache License, version 2.0, (LICENSE-APACHEv2)
# - MIT license (LICENSE-MIT)
#
## This is libsecp256k1 backend.
import secp256k1, nimcrypto/sysrand, nimcrypto/utils
const
KeyLength* = 256 div 8
CompressedPubKeyLength* = 33
RawSignatureNRSize* = KeyLength * 2 # Non-recoverable signature
RawSignatureSize* = RawSignatureNRSize + 1 # Recoverable
RawPublicKeySize* = KeyLength * 2
InvalidPrivateKey = "Invalid private key!"
InvalidPublicKey = "Invalid public key!"
InvalidSignature = "Invalid signature!"
VerificationFailed = "Signature verification has been failed!"
MessageSizeError = "Size of message to sign must be KeyLength bytes!"
type
PublicKey* = secp256k1_pubkey
## Representation of public key
PrivateKey* = object
## Representation of secret key
data*: array[KeyLength, byte]
SharedSecret* = object
## 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
pubkey*: PublicKey
Signature* = secp256k1_ecdsa_recoverable_signature
## Representation of signature
SignatureNR* = secp256k1_ecdsa_signature
## Representation of non-recoverable signature
Secp256k1Exception* = object of CatchableError
## Exceptions generated by `libsecp256k1`
EthKeysContext = ref object
context: ptr secp256k1_context
error: string
var ekContext {.threadvar.}: EthKeysContext
## Thread local variable which holds current context
##
## Private procedures interface
##
proc illegalCallback(message: cstring; data: pointer) {.cdecl.} =
let ctx = cast[EthKeysContext](data)
ctx.error = $message
proc errorCallback(message: cstring, data: pointer) {.cdecl.} =
let ctx = cast[EthKeysContext](data)
ctx.error = $message
proc shutdownLibsecp256k1(ekContext: EthKeysContext) =
# TODO: use destructor when finalizer are deprecated for destructors
if not isNil(ekContext.context):
secp256k1_context_destroy(ekContext.context)
proc newEthKeysContext(): EthKeysContext =
## Create new `EthKeysContext`.
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 getSecpContext(): ptr secp256k1_context =
## Get current `secp256k1_context`
if isNil(ekContext):
ekContext = newEthKeysContext()
result = ekContext.context
proc getContext(): EthKeysContext =
## Get current `EccContext`
if isNil(ekContext):
ekContext = newEthKeysContext()
result = ekContext
template raiseSecp256k1Error() =
## Raises `libsecp256k1` error as exception
let mctx = getContext()
if len(mctx.error) > 0:
var msg = mctx.error
mctx.error.setLen(0)
raise newException(Secp256k1Exception, msg)
proc libsecp256k1ErrorMsg(): string =
let mctx = getContext()
result = mctx.error
proc setErrorMsg(m: string) =
let mctx = getContext()
mctx.error = m
##
## Public procedures interface
##
proc newPrivateKey*(): PrivateKey =
## Generates new private key.
let ctx = getSecpContext()
while true:
if randomBytes(result.data) == KeyLength:
if secp256k1_ec_seckey_verify(ctx, cast[ptr cuchar](addr result)) == 1:
break
proc getPublicKey*(seckey: PrivateKey): PublicKey =
## Return public key for private key `seckey`.
let ctx = getSecpContext()
if secp256k1_ec_pubkey_create(ctx, addr result,
cast[ptr cuchar](unsafeAddr seckey)) != 1:
raiseSecp256k1Error()
proc toKeyPair*(key: PrivateKey): KeyPair =
KeyPair(seckey: key, pubkey: key.getPublicKey())
proc newKeyPair*(): KeyPair =
## Generates new private and public key.
result.seckey = newPrivateKey()
result.pubkey = result.seckey.getPublicKey()
proc initPrivateKey*(hexstr: string): PrivateKey =
## Create new private key from hexadecimal string representation.
let ctx = getSecpContext()
var o = fromHex(stripSpaces(hexstr))
if len(o) < KeyLength:
raise newException(EthKeysException, InvalidPrivateKey)
copyMem(addr result, addr o[0], KeyLength)
if secp256k1_ec_seckey_verify(ctx, cast[ptr cuchar](addr result)) != 1:
raise newException(EthKeysException, InvalidPrivateKey)
proc initPrivateKey*(data: openarray[byte]): PrivateKey =
## Create new private key from binary data blob.
let ctx = getSecpContext()
if len(data) < KeyLength:
raise newException(EthKeysException, InvalidPrivateKey)
copyMem(addr result, unsafeAddr data[0], KeyLength)
if secp256k1_ec_seckey_verify(ctx, cast[ptr cuchar](addr result)) != 1:
raise newException(EthKeysException, InvalidPrivateKey)
proc recoverPublicKey*(data: openarray[byte],
pubkey: var PublicKey): EthKeysStatus =
## Unserialize public key from `data`.
let ctx = getSecpContext()
let length = len(data)
if length >= RawPublicKeySize:
var rawkey: array[RawPublicKeySize + 1, byte]
rawkey[0] = 0x04'u8 # mark key with UNCOMPRESSED flag
copyMem(addr rawkey[1], unsafeAddr data[0], RawPublicKeySize)
if secp256k1_ec_pubkey_parse(ctx, addr pubkey,
cast[ptr cuchar](addr rawkey),
RawPublicKeySize + 1) != 1:
return(EthKeysStatus.Error)
elif length == CompressedPubKeyLength:
# Compressed format
if secp256k1_ec_pubkey_parse(ctx, addr pubkey,
cast[ptr cuchar](unsafeAddr data),
length) != 1:
return(EthKeysStatus.Error)
else:
setErrorMsg(InvalidPublicKey)
return(EthKeysStatus.Error)
result = EthKeysStatus.Success
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:
setErrorMsg(InvalidSignature)
return(EthKeysStatus.Error)
var recid = cint(data[KeyLength * 2])
if secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, addr signature,
cast[ptr cuchar](unsafeAddr data[0]),
recid) != 1:
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))
if recoverPublicKey(o, result) != EthKeysStatus.Success:
raise newException(EthKeysException, InvalidPublicKey)
proc initPublicKey*(data: openarray[byte]): PublicKey =
## Create new public key from binary data blob.
if recoverPublicKey(data, result) != EthKeysStatus.Success:
raise newException(EthKeysException, InvalidPublicKey)
proc initSignature*(hexstr: string): Signature =
## Create new signature from hexadecimal string representation.
var o = fromHex(stripSpaces(hexstr))
if recoverSignature(o, result) != EthKeysStatus.Success:
raise newException(EthKeysException, libsecp256k1ErrorMsg())
proc initSignature*(data: openarray[byte]): Signature =
## Create new signature from 'data'.
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.
var res: array[KeyLength + 1, byte]
let ctx = getSecpContext()
if secp256k1_ecdh_raw(ctx, cast[ptr cuchar](addr res),
unsafeAddr pubkey,
cast[ptr cuchar](unsafeAddr seckey)) != 1:
return(EthKeysStatus.Error)
copyMem(addr secret, addr res[1], KeyLength)
return(EthKeysStatus.Success)
proc toRaw*(pubkey: PublicKey, data: var openarray[byte], compressed = false) =
## Converts public key `pubkey` to serialized form and store it in `data`.
if compressed:
var length = len(data)
doAssert(length >= CompressedPubKeyLength)
let ctx = getSecpContext()
if secp256k1_ec_pubkey_serialize(ctx, cast[ptr cuchar](addr data[0]),
addr length, unsafeAddr pubkey,
SECP256K1_EC_COMPRESSED) != 1:
raiseSecp256k1Error()
else:
var key: array[RawPublicKeySize + 1, byte]
doAssert(len(data) >= RawPublicKeySize)
var length = csize(sizeof(key))
let ctx = getSecpContext()
if secp256k1_ec_pubkey_serialize(ctx, cast[ptr cuchar](addr key),
addr length, unsafeAddr pubkey,
SECP256K1_EC_UNCOMPRESSED) != 1:
raiseSecp256k1Error()
doAssert(length == RawPublicKeySize + 1)
doAssert(key[0] == 0x04'u8)
copyMem(addr data[0], addr key[1], RawPublicKeySize)
proc getRaw*(pubkey: PublicKey): array[RawPublicKeySize, byte] {.noinit, inline.} =
## Converts public key `pubkey` to serialized form.
pubkey.toRaw(result)
proc `==`*(lhs, rhs: PublicKey): bool =
lhs.getRaw == rhs.getRaw
proc getRawCompressed*(pubkey: PublicKey): array[CompressedPubKeyLength, byte] {.noinit, inline.} =
## Converts public key `pubkey` to serialized form.
pubkey.toRaw(result, true)
proc toRaw*(s: Signature, data: var openarray[byte]) =
## Converts signature `s` to serialized form and store it in `data`.
let ctx = getSecpContext()
var recid = cint(0)
doAssert(len(data) >= RawSignatureSize)
if secp256k1_ecdsa_recoverable_signature_serialize_compact(
ctx, cast[ptr cuchar](addr data[0]), addr recid, unsafeAddr s) != 1:
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 =
## Perform check on digitally signed `data` using original message `msg` and
## recover public key to `pubkey` on success.
let ctx = getSecpContext()
let length = len(data)
if len(msg) < KeyLength:
setErrorMsg(MessageSizeError)
return(EthKeysStatus.Error)
if length < RawSignatureSize:
setErrorMsg(InvalidSignature)
return(EthKeysStatus.Error)
var recid = cint(data[KeyLength * 2])
var s: secp256k1_ecdsa_recoverable_signature
if secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, addr s,
cast[ptr cuchar](unsafeAddr data[0]),
recid) != 1:
return(EthKeysStatus.Error)
if secp256k1_ecdsa_recover(ctx, addr pubkey, addr s,
cast[ptr cuchar](msg)) != 1:
setErrorMsg(VerificationFailed)
return(EthKeysStatus.Error)
result = EthKeysStatus.Success
proc recoverSignatureKey*(signature: Signature,
msg: openarray[byte],
pubkey: var PublicKey): EthKeysStatus =
## Perform check of `signature` using original message `msg` and
## recover public key to `pubkey` on success.
let ctx = getSecpContext()
if len(msg) < KeyLength:
setErrorMsg(MessageSizeError)
return(EthKeysStatus.Error)
if secp256k1_ecdsa_recover(ctx, addr pubkey, unsafeAddr signature,
cast[ptr cuchar](msg)) != 1:
setErrorMsg(VerificationFailed)
return(EthKeysStatus.Error)
result = EthKeysStatus.Success
proc signRawMessage*(data: openarray[byte], seckey: PrivateKey,
signature: var Signature): 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_recoverable(ctx, addr signature,
cast[ptr cuchar](unsafeAddr data[0]),
cast[ptr cuchar](unsafeAddr seckey),
nil, nil) != 1:
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)

415
eth/keys/secp.nim Normal file
View File

@ -0,0 +1,415 @@
## Copyright (c) 2018-2020 Status Research & Development GmbH
## Licensed under either of
## * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
## * MIT license ([LICENSE-MIT](LICENSE-MIT))
## at your option.
## This file may not be copied, modified, or distributed except according to
## those terms.
##
import
strformat,
secp256k1,
stew/[byteutils, objects, result],
nimcrypto/[hash, sysrand]
from nimcrypto/utils import burnMem
export result
{.push raises: [Defect].}
# Implementation notes
#
# The goal of this wrapper is to create a thin later on top of the API presented
# in libsecp256k1, exploiting some of its regulatities to make it slightly more
# convenient to use from Nim
#
# * We hide raw pointer accesses and lengths behind nim types
# * We guarantee certain parameter properties, like not null and proper length,
# on the Nim side - in turn, we can rely on certain errors never happening in
# libsecp256k1, so we can skip checking for them
# * Functions like "fromRaw/toRaw" are balanced and will always rountrip
# * Functions like `fromRaw` are not called `init` because they may fail
# * Exception-free
const
SkRawSecretKeySize* = 32 # 256 div 8
## Size of private key in octets (bytes)
SkRawSignatureSize* = 64
## Compact serialized non-recoverable signature
SkDerSignatureMaxSize* = 72
## Max bytes in DER encoding
SkRawRecoverableSignatureSize* = 65
## Size of recoverable signature in octets (bytes)
SkRawPublicKeySize* = 65
## Size of uncompressed public key in octets (bytes)
SkRawCompressedPubKeySize* = 33
## Size of compressed public key in octets (bytes)
SkMessageSize* = 32
## Size of message that can be signed
SkEdchSecretSize* = 32
## ECDH-agreed key size
SkEcdhRawSecretSize* = 33
## ECDH-agreed raw key size
type
SkPublicKey* = secp256k1_pubkey
## Representation of public key.
SkSecretKey* = object
## Representation of secret key.
data*: array[SkRawSecretKeySize, byte]
SkKeyPair* = object
## Representation of private/public keys pair.
seckey*: SkSecretKey
pubkey*: SkPublicKey
SkSignature* = secp256k1_ecdsa_signature
## Representation of non-recoverable signature.
SkRecoverableSignature* = secp256k1_ecdsa_recoverable_signature
## Representation of recoverable signature.
SkContext* = ref object
## Representation of Secp256k1 context object.
context: ptr secp256k1_context
SkMessage* = MDigest[SkMessageSize * 8]
## Message that can be signed or verified
SkEcdhSecret* = object
## Representation of ECDH shared secret
data*: array[SkEdchSecretSize, byte]
SkEcdhRawSecret* = object
## Representation of ECDH shared secret, with leading `y` byte
# (`y` is 0x02 when pubkey.y is even or 0x03 when odd)
data*: array[SkEcdhRawSecretSize, byte]
SkResult*[T] = result.Result[T, cstring]
##
## Private procedures interface
##
var secpContext {.threadvar.}: SkContext
## Thread local variable which holds current context
proc illegalCallback(message: cstring, data: pointer) {.cdecl.} =
# This should never happen because we check all parameters before passing
# them to secp
echo message
echo getStackTrace()
quit 1
proc errorCallback(message: cstring, data: pointer) {.cdecl.} =
# Internal panic - should never happen
echo message
echo getStackTrace()
quit 1
template ptr0(v: array|openArray): ptr cuchar =
cast[ptr cuchar](unsafeAddr v[0])
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))
func getContext(): ptr secp256k1_context =
## Get current `EccContext`
{.noSideEffect.}: # TODO what problems will this cause?
if isNil(secpContext):
secpContext = newSkContext()
secpContext.context
proc random*(T: type SkSecretKey): SkResult[T] =
## Generates new random private key.
let ctx = getContext()
var sk: T
while randomBytes(sk.data) == SkRawSecretKeySize:
if secp256k1_ec_seckey_verify(ctx, sk.data.ptr0) == 1:
return ok(sk)
return err("secp: cannot get random bytes for key")
proc fromRaw*(T: type SkSecretKey, data: openArray[byte]): SkResult[T] =
## Load a valid private key, as created by `toRaw`
if len(data) < SkRawSecretKeySize:
return err(static(&"secp: raw private key should be {SkRawSecretKeySize} bytes"))
if secp256k1_ec_seckey_verify(getContext(), data.ptr0) != 1:
return err("secp: invalid private key")
ok(T(data: toArray(32, data.toOpenArray(0, SkRawSecretKeySize - 1))))
proc fromHex*(T: type SkSecretKey, data: string): SkResult[SkSecretKey] =
## Initialize Secp256k1 `private key` ``key`` from hexadecimal string
## representation ``data``.
try:
# TODO strip string?
T.fromRaw(hexToSeqByte(data))
except CatchableError:
err("secp: cannot parse private key")
proc toRaw*(seckey: SkSecretKey): array[SkRawSecretKeySize, byte] =
## Serialize Secp256k1 `private key` ``key`` to raw binary form
seckey.data
proc toPublicKey*(key: SkSecretKey): SkResult[SkPublicKey] =
## Calculate and return Secp256k1 `public key` from `private key` ``key``.
var pubkey: SkPublicKey
if secp256k1_ec_pubkey_create(getContext(), addr pubkey, key.data.ptr0) != 1:
return err("secp: cannot create pubkey, private key invalid?")
ok(pubkey)
proc fromRaw*(T: type SkPublicKey, data: openArray[byte]): SkResult[T] =
## Initialize Secp256k1 `public key` ``key`` from raw binary
## representation ``data``, which may be compressed, uncompressed or hybrid
if len(data) < 1:
return err(static(
&"secp: public key must be {SkRawCompressedPubKeySize} or {SkRawPublicKeySize} bytes"))
var length: int
if data[0] == 0x02'u8 or data[0] == 0x03'u8:
length = min(len(data), SkRawCompressedPubKeySize)
elif data[0] == 0x04'u8 or data[0] == 0x06'u8 or data[0] == 0x07'u8:
length = min(len(data), SkRawPublicKeySize)
else:
return err("secp: public key format not recognised")
var key: SkPublicKey
if secp256k1_ec_pubkey_parse(
getContext(), addr key, data.ptr0, length) != 1:
return err("secp: cannot parse public key")
ok(key)
proc fromHex*(T: type SkPublicKey, data: string): SkResult[T] =
## Initialize Secp256k1 `public key` ``key`` from hexadecimal string
## representation ``data``.
try:
# TODO strip string?
T.fromRaw(hexToSeqByte(data))
except CatchableError:
err("secp: cannot parse public key")
proc toRaw*(pubkey: SkPublicKey): array[SkRawPublicKeySize, byte] =
## Serialize Secp256k1 `public key` ``key`` to raw uncompressed form
var length = csize(len(result))
# Can't fail, per documentation
discard secp256k1_ec_pubkey_serialize(
getContext(), result.ptr0, addr length, unsafeAddr pubkey,
SECP256K1_EC_UNCOMPRESSED)
proc toRawCompressed*(key: SkPublicKey): array[SkRawCompressedPubKeySize, byte] =
## Serialize Secp256k1 `public key` ``key`` to raw compressed form
var length = csize(len(result))
# Can't fail, per documentation
discard secp256k1_ec_pubkey_serialize(
getContext(), result.ptr0, addr length, unsafeAddr key,
SECP256K1_EC_COMPRESSED)
proc fromRaw*(T: type SkSignature, data: openArray[byte]): SkResult[T] =
## Load compact signature from data
if data.len() < SkRawSignatureSize:
return err(static(&"secp: signature must be {SkRawSignatureSize} bytes"))
var sig: SkSignature
if secp256k1_ecdsa_signature_parse_compact(
getContext(), addr sig, data.ptr0) != 1:
return err("secp: cannot parse signaure")
ok(sig)
proc fromDer*(T: type SkSignature, data: openarray[byte]): SkResult[T] =
## Initialize Secp256k1 `signature` ``sig`` from DER
## representation ``data``.
if len(data) < 1:
return err("secp: DER signature too short")
var sig: T
if secp256k1_ecdsa_signature_parse_der(
getContext().context, addr sig, data.ptr0, csize(len(data))) != 1:
return err("secp: cannot parse DER signature")
ok(sig)
proc fromHex*(T: type SkSignature, data: string): SkResult[T] =
## Initialize Secp256k1 `signature` ``sig`` from hexadecimal string
## representation ``data``.
try:
# TODO strip string?
T.fromRaw(hexToSeqByte(data))
except CatchableError:
err("secp: cannot parse signature")
proc toRaw*(sig: SkSignature): array[SkRawSignatureSize, byte] =
## Serialize signature to compact binary form
# Can't fail, per documentation
discard secp256k1_ecdsa_signature_serialize_compact(
getContext(), result.ptr0, unsafeAddr sig)
proc toDer*(sig: SkSignature, data: var openarray[byte]): int =
## Serialize Secp256k1 `signature` ``sig`` to raw binary form and store it
## to ``data``.
##
## Procedure returns number of bytes (octets) needed to store
## Secp256k1 signature.
let ctx = getContext()
var buffer: array[SkDerSignatureMaxSize, byte]
var plength = csize(len(buffer))
discard secp256k1_ecdsa_signature_serialize_der(
ctx, buffer.ptr0, addr plength, unsafeAddr sig)
result = plength
if len(data) >= plength:
copyMem(addr data[0], addr buffer[0], plength)
proc toDer*(sig: SkSignature): seq[byte] =
## Serialize Secp256k1 `signature` and return it.
result = newSeq[byte](72)
let length = toDer(sig, result)
result.setLen(length)
proc fromRaw*(T: type SkRecoverableSignature, data: openArray[byte]): SkResult[T] =
if data.len() < SkRawRecoverableSignatureSize:
return err(
static(&"secp: recoverable signature must be {SkRawRecoverableSignatureSize} bytes"))
let recid = cint(data[64])
var sig: SkRecoverableSignature
if secp256k1_ecdsa_recoverable_signature_parse_compact(
getContext(), addr sig, data.ptr0, recid) != 1:
return err("secp: invalid recoverable signature")
ok(sig)
proc fromHex*(T: type SkRecoverableSignature, data: string): SkResult[T] =
## Initialize Secp256k1 `signature` ``sig`` from hexadecimal string
## representation ``data``.
try:
# TODO strip string?
T.fromRaw(hexToSeqByte(data))
except CatchableError:
err("secp: cannot parse recoverable signature")
proc toRaw*(sig: SkRecoverableSignature): array[SkRawRecoverableSignatureSize, byte] =
## Converts recoverable signature to compact binary form
var recid = cint(0)
# Can't fail, per documentation
discard secp256k1_ecdsa_recoverable_signature_serialize_compact(
getContext(), result.ptr0, addr recid, unsafeAddr sig)
result[64] = byte(recid)
proc random*(T: type SkKeyPair): SkResult[T] =
## Generates new random key pair.
let seckey = ? SkSecretKey.random()
ok(T(
seckey: seckey,
pubkey: seckey.toPublicKey().expect("random key should always be valid")
))
proc `==`*(lhs, rhs: SkPublicKey): bool =
## Compare Secp256k1 `public key` objects for equality.
lhs.toRaw() == rhs.toRaw()
proc `==`*(lhs, rhs: SkSignature): bool =
## Compare Secp256k1 `signature` objects for equality.
lhs.toRaw() == rhs.toRaw()
proc `==`*(lhs, rhs: SkRecoverableSignature): bool =
## Compare Secp256k1 `recoverable signature` objects for equality.
lhs.toRaw() == rhs.toRaw()
proc sign*(key: SkSecretKey, msg: SkMessage): SkResult[SkSignature] =
## Sign message `msg` using private key `key` and return signature object.
var sig: SkSignature
if secp256k1_ecdsa_sign(
getContext(), addr sig, msg.data.ptr0, key.data.ptr0, nil, nil) != 1:
return err("secp: cannot create signature, key invalid?")
ok(sig)
proc signRecoverable*(key: SkSecretKey, msg: SkMessage): SkResult[SkRecoverableSignature] =
## Sign message `msg` using private key `key` and return signature object.
var sig: SkRecoverableSignature
if secp256k1_ecdsa_sign_recoverable(
getContext(), addr sig, msg.data.ptr0, key.data.ptr0, nil, nil) != 1:
return err("secp: cannot create recoverable signature, key invalid?")
ok(sig)
proc verify*(sig: SkSignature, msg: SkMessage, key: SkPublicKey): bool =
secp256k1_ecdsa_verify(
getContext(), unsafeAddr sig, msg.data.ptr0, unsafeAddr key) == 1
proc recover*(sig: SkRecoverableSignature, msg: SkMessage): SkResult[SkPublicKey] =
var pubkey: SkPublicKey
if secp256k1_ecdsa_recover(
getContext(), addr pubkey, unsafeAddr sig, msg.data.ptr0) != 1:
return err("secp: cannot recover public key from signature")
ok(pubkey)
proc ecdh*(seckey: SkSecretKey, pubkey: SkPublicKey): SkResult[SkEcdhSecret] =
## Calculate ECDH shared secret.
var secret: SkEcdhSecret
if secp256k1_ecdh(
getContext(), secret.data.ptr0, unsafeAddr pubkey, seckey.data.ptr0) != 1:
return err("secp: cannot compute ECDH secret")
ok(secret)
proc ecdhRaw*(seckey: SkSecretKey, pubkey: SkPublicKey): SkResult[SkEcdhRawSecret] =
## Calculate ECDH shared secret.
var secret: SkEcdhRawSecret
if secp256k1_ecdh_raw(
getContext(), secret.data.ptr0, unsafeAddr pubkey, seckey.data.ptr0) != 1:
return err("Cannot compute raw ECDH secret")
ok(secret)
proc clear*(v: var SkSecretKey) {.inline.} =
## Wipe and clear memory of Secp256k1 `private key`.
burnMem(v.data)
proc clear*(v: var SkPublicKey) {.inline.} =
## Wipe and clear memory of Secp256k1 `public key`.
burnMem(v.data)
proc clear*(v: var SkSignature) {.inline.} =
## Wipe and clear memory of Secp256k1 `signature`.
burnMem(v.data)
proc clear*(v: var SkRecoverableSignature) {.inline.} =
## Wipe and clear memory of Secp256k1 `signature`.
burnMem(v.data)
proc clear*(v: var SkKeyPair) {.inline.} =
## Wipe and clear memory of Secp256k1 `key pair`.
v.seckey.clear()
v.pubkey.clear()
proc clear*(v: var SkEcdhSecret) =
burnMem(v.data)
proc clear*(v: var SkEcdhRawSecret) =
burnMem(v.data)

View File

@ -341,6 +341,7 @@ when isMainModule:
discovery.open()
proc test() {.async.} =
await discovery.bootstrap()
{.gcsafe.}:
await discovery.bootstrap()
waitFor test()

View File

@ -11,6 +11,8 @@
import uri, strutils, net
import eth/keys
export keys
type
ENodeStatus* = enum
## ENode status codes

View File

@ -70,12 +70,6 @@ suite "ECC/ECDSA/ECDHE tests suite":
check:
$signature == $expectSignature
test "test_signing_from_private_key_obj":
var s = initPrivateKey(pkbytes)
var signature = s.signMessage(message)
var mhash = keccak256.digest(message)
check verifyMessage(signature.data, mhash) == true
test "test_recover_from_signature_obj":
var s = initPrivateKey(pkbytes)
var mhash = keccak256.digest(message)
@ -162,7 +156,7 @@ suite "ECC/ECDSA/ECDHE tests suite":
let expect = fromHex(stripSpaces(sharedSecrets[i]))
check:
ecdhAgree(s, p, secret) == EthKeysStatus.Success
compare(expect, secret.data) == true
expect == secret.data
test "ECDHE/cpp-ethereum crypto.cpp#L394":
# ECDHE test vectors
@ -175,7 +169,7 @@ suite "ECC/ECDSA/ECDHE tests suite":
let expect = fromHex(stripSpaces(expectm))
check:
ecdhAgree(s, p, secret) == EthKeysStatus.Success
compare(expect, secret.data) == true
expect == secret.data
test "ECDHE/cpp-ethereum rlpx.cpp#L425":
# ECDHE test vectors

View File

@ -71,7 +71,7 @@ suite "ENode":
IncorrectIP,
IncorrectIP,
IncorrectIP,
Success,
ENodeStatus.Success,
IncorrectUri,
IncorrectPort,
IncorrectPort,