mirror of
https://github.com/status-im/nim-eth.git
synced 2025-02-17 16:37:15 +00:00
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:
parent
5dc0a533b0
commit
02d661503f
430
eth/keys.nim
430
eth/keys.nim
@ -6,105 +6,95 @@
|
|||||||
# - MIT license (LICENSE-MIT)
|
# - 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
|
type
|
||||||
EthKeysStatus* = enum
|
PrivateKey* = distinct SkSecretKey
|
||||||
Success, ## Operation was successful
|
|
||||||
Error ## Operation failed
|
|
||||||
|
|
||||||
EthKeysException* = object of CatchableError
|
PublicKey* = distinct SkPublicKey
|
||||||
## Exception generated by this module
|
## 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):
|
SharedSecretFull* = SkEcdhRawSecret
|
||||||
include eth/keys/libsecp256k1
|
SharedSecret* = object
|
||||||
|
data*: array[KeyLength, byte]
|
||||||
|
|
||||||
proc ekErrorMsg*(): string {.inline.} =
|
KeyPair* = object
|
||||||
## Return current error message.
|
seckey*: PrivateKey
|
||||||
result = libsecp256k1ErrorMsg()
|
pubkey*: PublicKey
|
||||||
|
|
||||||
proc isZeroKey*(seckey: PrivateKey): bool =
|
proc toPublicKey*(seckey: PrivateKey): SkResult[PublicKey] =
|
||||||
## Check if private key `seckey` contains only 0 bytes.
|
SkSecretKey(seckey).toPublicKey().mapConvert(PublicKey)
|
||||||
result = true
|
|
||||||
for i in seckey.data:
|
|
||||||
if i != byte(0):
|
|
||||||
result = false
|
|
||||||
|
|
||||||
proc isZeroKey*(pubkey: PublicKey): bool =
|
proc fromRaw*(T: type PrivateKey, data: openArray[byte]): SkResult[PrivateKey] =
|
||||||
## Check if public key `pubkey` contains only 0 bytes.
|
SkSecretKey.fromRaw(data).mapConvert(PrivateKey)
|
||||||
result = true
|
|
||||||
for i in pubkey.data:
|
|
||||||
if i != byte(0):
|
|
||||||
result = false
|
|
||||||
|
|
||||||
proc signMessage*(seckey: PrivateKey,
|
proc fromHex*(T: type PrivateKey, data: string): SkResult[PrivateKey] =
|
||||||
data: openarray[byte]): Signature {.inline.} =
|
SkSecretKey.fromHex(data).mapConvert(PrivateKey)
|
||||||
## 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 signMessage*(seckey: PrivateKey, data: string): Signature {.inline.} =
|
proc fromRaw*(T: type PublicKey, data: openArray[byte]): SkResult[T] =
|
||||||
## Sign message of arbitrary length `data` using private key `seckey`.
|
if data.len() == SkRawCompressedPubKeySize:
|
||||||
signMessage(seckey, cast[seq[byte]](data))
|
return SkPublicKey.fromRaw(data).mapConvert(PublicKey)
|
||||||
|
|
||||||
proc signMessage*(seckey: PrivateKey,
|
if len(data) < SkRawPublicKeySize - 1:
|
||||||
hash: MDigest[256]): Signature {.inline.} =
|
return err(&"keys: raw eth public key should be {SkRawPublicKeySize - 1} bytes")
|
||||||
## Sign 256bit `hash` using private key `seckey`.
|
|
||||||
result = signMessage(seckey, hash.data)
|
|
||||||
|
|
||||||
proc verifyMessage*(data: openarray[byte], message: openarray[byte]): bool =
|
var d: array[SkRawPublicKeySize, byte]
|
||||||
## Verify binary data blob `data` has properly signed message `message`.
|
d[0] = 0x04'u8
|
||||||
var pubkey: PublicKey
|
copyMem(addr d[1], unsafeAddr data[0], 64)
|
||||||
if recoverSignatureKey(data, message, pubkey) == EthKeysStatus.Success:
|
|
||||||
result = true
|
|
||||||
else:
|
|
||||||
result = false
|
|
||||||
|
|
||||||
proc verifyMessage*(data: openarray[byte],
|
SkPublicKey.fromRaw(d).mapConvert(PublicKey)
|
||||||
hash: MDigest[256]): bool {.inline.} =
|
|
||||||
## Verify binary data blob `data` has properly signed hash `hash`.
|
|
||||||
result = verifyMessage(data, hash.data)
|
|
||||||
|
|
||||||
proc recoverKeyFromMessage*(data: openarray[byte],
|
proc fromHex*(T: type PublicKey, data: string): SkResult[PublicKey] =
|
||||||
hash: MDigest[256]): PublicKey {.inline.} =
|
try:
|
||||||
## Recover public key from signed binary blob `data` using 256bit hash `hash`.
|
# TODO strip string?
|
||||||
if recoverSignatureKey(data, hash.data, result) != EthKeysStatus.Success:
|
T.fromRaw(hexToSeqByte(data))
|
||||||
raise newException(EthKeysException, ekErrorMsg())
|
except CatchableError:
|
||||||
|
err("keys: cannot parse eth public key")
|
||||||
|
|
||||||
proc recoverKeyFromMessage*(data: openarray[byte],
|
proc random*(t: type KeyPair): SkResult[KeyPair] =
|
||||||
message: string): PublicKey {.inline.} =
|
let tmp = ?SkKeypair.random()
|
||||||
## Recover public key from signed binary blob `data` using `message`.
|
ok(KeyPair(seckey: PrivateKey(tmp.seckey), pubkey: PublicKey(tmp.pubkey)))
|
||||||
var hash = keccak256.digest(message)
|
|
||||||
if recoverSignatureKey(data, hash.data, result) != EthKeysStatus.Success:
|
|
||||||
raise newException(EthKeysException, ekErrorMsg())
|
|
||||||
|
|
||||||
proc recoverKeyFromSignature*(signature: Signature,
|
proc toRaw*(pubkey: PublicKey): array[64, byte] =
|
||||||
message: string): PublicKey {.inline.} =
|
let tmp = SkPublicKey(pubkey).toRaw()
|
||||||
## Recover public key from signature `signature` using `message`.
|
copyMem(addr result[0], unsafeAddr tmp[1], 64)
|
||||||
var hash = keccak256.digest(message)
|
|
||||||
if recoverSignatureKey(signature, hash.data, result) != EthKeysStatus.Success:
|
|
||||||
raise newException(EthKeysException, ekErrorMsg())
|
|
||||||
|
|
||||||
proc recoverKeyFromSignature*(signature: Signature,
|
proc toRawCompressed*(pubkey: PublicKey): array[33, byte] {.borrow.}
|
||||||
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 toAddress*(pubkey: PublicKey, with0x = true): string =
|
proc toAddress*(pubkey: PublicKey, with0x = true): string =
|
||||||
## Convert public key to hexadecimal string address.
|
## 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 = 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 =
|
proc toChecksumAddress*(pubkey: PublicKey, with0x = true): string =
|
||||||
## Convert public key to checksumable mixed-case address (EIP-55).
|
## Convert public key to checksumable mixed-case address (EIP-55).
|
||||||
result = if with0x: "0x" else: ""
|
result = if with0x: "0x" else: ""
|
||||||
var hash1 = keccak256.digest(pubkey.getRaw())
|
var hash1 = keccak256.digest(pubkey.toRaw())
|
||||||
var hhash1 = toHex(toOpenArray(hash1.data, 12, len(hash1.data) - 1), true)
|
var hhash1 = toHex(toOpenArray(hash1.data, 12, len(hash1.data) - 1))
|
||||||
var hash2 = keccak256.digest(hhash1)
|
var hash2 = keccak256.digest(hhash1)
|
||||||
var hhash2 = toHex(hash2.data, true)
|
var hhash2 = toHex(hash2.data)
|
||||||
for i in 0..<len(hhash1):
|
for i in 0..<len(hhash1):
|
||||||
if hhash2[i] >= '0' and hhash2[i] <= '7':
|
if hhash2[i] >= '0' and hhash2[i] <= '7':
|
||||||
result.add(hhash1[i])
|
result.add(hhash1[i])
|
||||||
@ -132,7 +122,7 @@ proc validateChecksumAddress*(a: string): bool =
|
|||||||
else:
|
else:
|
||||||
return false
|
return false
|
||||||
var hash = keccak256.digest(address)
|
var hash = keccak256.digest(address)
|
||||||
var hexhash = toHex(hash.data, true)
|
var hexhash = toHex(hash.data)
|
||||||
for i in 0..<len(address):
|
for i in 0..<len(address):
|
||||||
if hexhash[i] >= '0' and hexhash[i] <= '7':
|
if hexhash[i] >= '0' and hexhash[i] <= '7':
|
||||||
check.add(address[i])
|
check.add(address[i])
|
||||||
@ -144,19 +134,293 @@ proc validateChecksumAddress*(a: string): bool =
|
|||||||
check.add(ch)
|
check.add(ch)
|
||||||
result = (check == a)
|
result = (check == a)
|
||||||
|
|
||||||
proc toCanonicalAddress*(pubkey: PublicKey): array[20, byte] =
|
func toCanonicalAddress*(pubkey: PublicKey): array[20, byte] =
|
||||||
## Convert public key to canonical address.
|
## 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)
|
copyMem(addr result[0], addr hash.data[12], 20)
|
||||||
|
|
||||||
proc `$`*(pubkey: PublicKey): string =
|
func `$`*(pubkey: PublicKey): string =
|
||||||
## Convert public key to hexadecimal string representation.
|
## 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.
|
## 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
|
## 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
|
||||||
|
@ -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
415
eth/keys/secp.nim
Normal 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)
|
@ -341,6 +341,7 @@ when isMainModule:
|
|||||||
discovery.open()
|
discovery.open()
|
||||||
|
|
||||||
proc test() {.async.} =
|
proc test() {.async.} =
|
||||||
|
{.gcsafe.}:
|
||||||
await discovery.bootstrap()
|
await discovery.bootstrap()
|
||||||
|
|
||||||
waitFor test()
|
waitFor test()
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
import uri, strutils, net
|
import uri, strutils, net
|
||||||
import eth/keys
|
import eth/keys
|
||||||
|
|
||||||
|
export keys
|
||||||
|
|
||||||
type
|
type
|
||||||
ENodeStatus* = enum
|
ENodeStatus* = enum
|
||||||
## ENode status codes
|
## ENode status codes
|
||||||
|
@ -70,12 +70,6 @@ suite "ECC/ECDSA/ECDHE tests suite":
|
|||||||
check:
|
check:
|
||||||
$signature == $expectSignature
|
$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":
|
test "test_recover_from_signature_obj":
|
||||||
var s = initPrivateKey(pkbytes)
|
var s = initPrivateKey(pkbytes)
|
||||||
var mhash = keccak256.digest(message)
|
var mhash = keccak256.digest(message)
|
||||||
@ -162,7 +156,7 @@ suite "ECC/ECDSA/ECDHE tests suite":
|
|||||||
let expect = fromHex(stripSpaces(sharedSecrets[i]))
|
let expect = fromHex(stripSpaces(sharedSecrets[i]))
|
||||||
check:
|
check:
|
||||||
ecdhAgree(s, p, secret) == EthKeysStatus.Success
|
ecdhAgree(s, p, secret) == EthKeysStatus.Success
|
||||||
compare(expect, secret.data) == true
|
expect == secret.data
|
||||||
|
|
||||||
test "ECDHE/cpp-ethereum crypto.cpp#L394":
|
test "ECDHE/cpp-ethereum crypto.cpp#L394":
|
||||||
# ECDHE test vectors
|
# ECDHE test vectors
|
||||||
@ -175,7 +169,7 @@ suite "ECC/ECDSA/ECDHE tests suite":
|
|||||||
let expect = fromHex(stripSpaces(expectm))
|
let expect = fromHex(stripSpaces(expectm))
|
||||||
check:
|
check:
|
||||||
ecdhAgree(s, p, secret) == EthKeysStatus.Success
|
ecdhAgree(s, p, secret) == EthKeysStatus.Success
|
||||||
compare(expect, secret.data) == true
|
expect == secret.data
|
||||||
|
|
||||||
test "ECDHE/cpp-ethereum rlpx.cpp#L425":
|
test "ECDHE/cpp-ethereum rlpx.cpp#L425":
|
||||||
# ECDHE test vectors
|
# ECDHE test vectors
|
||||||
|
@ -71,7 +71,7 @@ suite "ENode":
|
|||||||
IncorrectIP,
|
IncorrectIP,
|
||||||
IncorrectIP,
|
IncorrectIP,
|
||||||
IncorrectIP,
|
IncorrectIP,
|
||||||
Success,
|
ENodeStatus.Success,
|
||||||
IncorrectUri,
|
IncorrectUri,
|
||||||
IncorrectPort,
|
IncorrectPort,
|
||||||
IncorrectPort,
|
IncorrectPort,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user