mirror of https://github.com/status-im/nim-eth.git
Moved eth-keys to eth
This commit is contained in:
parent
1d6434883e
commit
4c89f9e270
17
eth.nimble
17
eth.nimble
|
@ -4,12 +4,21 @@ description = "Ethereum Common library"
|
|||
license = "MIT"
|
||||
skipDirs = @["tests"]
|
||||
|
||||
requires "nim > 0.18.0",
|
||||
requires "nim > 0.19.0",
|
||||
"nimcrypto",
|
||||
"ranges",
|
||||
"stint",
|
||||
"byteutils"
|
||||
"byteutils",
|
||||
"secp256k1"
|
||||
|
||||
proc test(filename: string) =
|
||||
echo "Running: ", filename
|
||||
exec "nim c -r " & filename
|
||||
|
||||
import strutils, os
|
||||
|
||||
task test, "run tests":
|
||||
cd "tests"
|
||||
exec "nim c -r test_common"
|
||||
for i in walkDirRec("tests"):
|
||||
let fn = splitPath(i).tail
|
||||
if fn.startsWith("test_") and fn.endsWith(".nim"):
|
||||
test(i)
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
#
|
||||
# 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)
|
||||
#
|
||||
|
||||
import nimcrypto/hash, nimcrypto/keccak
|
||||
|
||||
type
|
||||
EthKeysStatus* = enum
|
||||
Success, ## Operation was successful
|
||||
Error ## Operation failed
|
||||
|
||||
EthKeysException* = object of Exception
|
||||
## Exception generated by this module
|
||||
|
||||
when not defined(native):
|
||||
include eth/keys/libsecp256k1
|
||||
|
||||
proc ekErrorMsg*(): string {.inline.} =
|
||||
## Return current error message.
|
||||
result = libsecp256k1ErrorMsg()
|
||||
|
||||
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 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 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 signMessage*(seckey: PrivateKey, data: string): Signature {.inline.} =
|
||||
## Sign message of arbitrary length `data` using private key `seckey`.
|
||||
signMessage(seckey, cast[seq[byte]](data))
|
||||
|
||||
proc signMessage*(seckey: PrivateKey,
|
||||
hash: MDigest[256]): Signature {.inline.} =
|
||||
## Sign 256bit `hash` using private key `seckey`.
|
||||
result = signMessage(seckey, hash.data)
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
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 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 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 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 toAddress*(pubkey: PublicKey, with0x = true): string =
|
||||
## Convert public key to hexadecimal string address.
|
||||
var hash = keccak256.digest(pubkey.getRaw())
|
||||
result = if with0x: "0x" else: ""
|
||||
result.add(toHex(toOpenArray(hash.data, 12, len(hash.data) - 1), true))
|
||||
|
||||
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 hash2 = keccak256.digest(hhash1)
|
||||
var hhash2 = toHex(hash2.data, true)
|
||||
for i in 0..<len(hhash1):
|
||||
if hhash2[i] >= '0' and hhash2[i] <= '7':
|
||||
result.add(hhash1[i])
|
||||
else:
|
||||
if hhash1[i] >= '0' and hhash1[i] <= '9':
|
||||
result.add(hhash1[i])
|
||||
else:
|
||||
let ch = chr(ord(hhash1[i]) - ord('a') + ord('A'))
|
||||
result.add(ch)
|
||||
|
||||
proc validateChecksumAddress*(a: string): bool =
|
||||
## Validate checksumable mixed-case address (EIP-55).
|
||||
var address = ""
|
||||
var check = "0x"
|
||||
if len(a) != 42:
|
||||
return false
|
||||
if a[0] != '0' and a[1] != 'x':
|
||||
return false
|
||||
for i in 2..41:
|
||||
let ch = a[i]
|
||||
if ch in {'0'..'9'} or ch in {'a'..'f'}:
|
||||
address &= ch
|
||||
elif ch in {'A'..'F'}:
|
||||
address &= chr(ord(ch) - ord('A') + ord('a'))
|
||||
else:
|
||||
return false
|
||||
var hash = keccak256.digest(address)
|
||||
var hexhash = toHex(hash.data, true)
|
||||
for i in 0..<len(address):
|
||||
if hexhash[i] >= '0' and hexhash[i] <= '7':
|
||||
check.add(address[i])
|
||||
else:
|
||||
if address[i] >= '0' and address[i] <= '9':
|
||||
check.add(address[i])
|
||||
else:
|
||||
let ch = chr(ord(address[i]) - ord('a') + ord('A'))
|
||||
check.add(ch)
|
||||
result = (check == a)
|
||||
|
||||
proc toCanonicalAddress*(pubkey: PublicKey): array[20, byte] =
|
||||
## Convert public key to canonical address.
|
||||
var hash = keccak256.digest(pubkey.getRaw())
|
||||
copyMem(addr result[0], addr hash.data[12], 20)
|
||||
|
||||
proc `$`*(pubkey: PublicKey): string =
|
||||
## Convert public key to hexadecimal string representation.
|
||||
result = toHex(pubkey.getRaw(), true)
|
||||
|
||||
proc `$`*(sig: Signature): string =
|
||||
## Convert signature to hexadecimal string representation.
|
||||
result = toHex(sig.getRaw(), true)
|
||||
|
||||
proc `$`*(seckey: PrivateKey): string =
|
||||
## Convert private key to hexadecimal string representation
|
||||
result = toHex(seckey.data, true)
|
|
@ -0,0 +1,321 @@
|
|||
#
|
||||
# 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
|
||||
RawSignatureSize* = KeyLength * 2 + 1
|
||||
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]
|
||||
|
||||
KeyPair* = object
|
||||
## Representation of private/public keys pair
|
||||
seckey*: PrivateKey
|
||||
pubkey*: PublicKey
|
||||
|
||||
Signature* = secp256k1_ecdsa_recoverable_signature
|
||||
## Representation of signature
|
||||
|
||||
Secp256k1Exception* = object of Exception
|
||||
## 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 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:
|
||||
setErrorMsg(InvalidPublicKey)
|
||||
return(EthKeysStatus.Error)
|
||||
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)
|
||||
result = EthKeysStatus.Success
|
||||
|
||||
proc recoverSignature*(data: openarray[byte],
|
||||
signature: var Signature): 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 initPublicKey*(hexstr: string): PublicKey =
|
||||
## Create new public key from hexadecimal string representation.
|
||||
var o = fromHex(stripSpaces(hexstr))
|
||||
if len(o) < RawPublicKeySize:
|
||||
raise newException(EthKeysException, InvalidPublicKey)
|
||||
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 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 getRaw*(pubkey: PublicKey): array[RawPublicKeySize, byte] {.noinit.} =
|
||||
## Converts public key `pubkey` to serialized form.
|
||||
var key: array[RawPublicKeySize + 1, byte]
|
||||
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()
|
||||
assert(length == RawPublicKeySize + 1)
|
||||
assert(key[0] == 0x04'u8)
|
||||
copyMem(addr result[0], addr key[1], RawPublicKeySize)
|
||||
|
||||
proc toRaw*(pubkey: PublicKey, data: var openarray[byte]) =
|
||||
## Converts public key `pubkey` to serialized form and store it in `data`.
|
||||
var key: array[RawPublicKeySize + 1, byte]
|
||||
assert(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()
|
||||
assert(length == RawPublicKeySize + 1)
|
||||
assert(key[0] == 0x04'u8)
|
||||
copyMem(addr data[0], addr key[1], RawPublicKeySize)
|
||||
|
||||
proc getRaw*(s: Signature): array[RawSignatureSize, byte] {.noinit.} =
|
||||
## Converts signature `s` to serialized form.
|
||||
let ctx = getSecpContext()
|
||||
var recid = cint(0)
|
||||
if secp256k1_ecdsa_recoverable_signature_serialize_compact(
|
||||
ctx, cast[ptr cuchar](unsafeAddr result), addr recid, unsafeAddr s) != 1:
|
||||
raiseSecp256k1Error()
|
||||
result[64] = uint8(recid)
|
||||
|
||||
proc toRaw*(s: Signature, data: var openarray[byte]) =
|
||||
## Converts signature `s` to serialized form and store it in `data`.
|
||||
let ctx = getSecpContext()
|
||||
var recid = cint(0)
|
||||
assert(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 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)
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
# Nim Eth-keys
|
||||
# Copyright (c) 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
# This is a sample of signatures generated with a known-good implementation of the ECDSA
|
||||
# algorithm, which we use to test our ECC backends. If necessary, it can be generated from scratch
|
||||
# with the following code:
|
||||
#
|
||||
# """python
|
||||
# from devp2p import crypto
|
||||
# from eth_utils import encode_hex
|
||||
# msg = b'message'
|
||||
# msghash = crypto.sha3(b'message')
|
||||
# for secret in ['alice', 'bob', 'eve']:
|
||||
# print("'{}': dict(".format(secret))
|
||||
# privkey = crypto.mk_privkey(secret)
|
||||
# pubkey = crypto.privtopub(privkey)
|
||||
# print(" privkey='{}',".format(encode_hex(privkey)))
|
||||
# print(" pubkey='{}',".format(encode_hex(crypto.privtopub(privkey))))
|
||||
# ecc = crypto.ECCx(raw_privkey=privkey)
|
||||
# sig = ecc.sign(msghash)
|
||||
# print(" sig='{}',".format(encode_hex(sig)))
|
||||
# print(" raw_sig='{}')".format(crypto._decode_sig(sig)))
|
||||
# assert crypto.ecdsa_recover(msghash, sig) == pubkey
|
||||
# """
|
||||
|
||||
import nimcrypto
|
||||
|
||||
type
|
||||
testKeySig* = object
|
||||
privkey*: string
|
||||
pubkey*: string
|
||||
raw_sig*: tuple[v: int, r, s: string]
|
||||
serialized_sig*: string
|
||||
|
||||
let
|
||||
MSG* = "message"
|
||||
MSGHASH* = keccak256.digest(MSG)
|
||||
|
||||
# Conversion done through https://www.mobilefish.com/services/big_number/big_number.php
|
||||
|
||||
let
|
||||
alice* = testKeySig(
|
||||
privkey: "9c0257114eb9399a2985f8e75dad7600c5d89fe3824ffa99ec1c3eb8bf3b0501",
|
||||
pubkey: "5eed5fa3a67696c334762bb4823e585e2ee579aba3558d9955296d6c04541b426078dbd48d74af1fd0c72aa1a05147cf17be6b60bdbed6ba19b08ec28445b0ca",
|
||||
raw_sig: (
|
||||
v: 1,
|
||||
r: "B20E2EA5D3CBAA83C1E0372F110CF12535648613B479B64C1A8C1A20C5021F38", # Decimal "80536744857756143861726945576089915884233437828013729338039544043241440681784",
|
||||
s: "0434D07EC5795E3F789794351658E80B7FAF47A46328F41E019D7B853745CDFD" # Decimal "1902566422691403459035240420865094128779958320521066670269403689808757640701"
|
||||
),
|
||||
serialized_sig: "b20e2ea5d3cbaa83c1e0372f110cf12535648613b479b64c1a8c1a20c5021f380434d07ec5795e3f789794351658e80b7faf47a46328f41e019d7b853745cdfd01"
|
||||
)
|
||||
|
||||
bob* = testKeySig(
|
||||
privkey: "38e47a7b719dce63662aeaf43440326f551b8a7ee198cee35cb5d517f2d296a2",
|
||||
pubkey: "347746ccb908e583927285fa4bd202f08e2f82f09c920233d89c47c79e48f937d049130e3d1c14cf7b21afefc057f71da73dec8e8ff74ff47dc6a574ccd5d570",
|
||||
raw_sig: (
|
||||
v: 1,
|
||||
r: "5C48EA4F0F2257FA23BD25E6FCB0B75BBE2FF9BBDA0167118DAB2BB6E31BA76E", # Decimal "41741612198399299636429810387160790514780876799439767175315078161978521003886",
|
||||
s: "691DBDAF2A231FC9958CD8EDD99507121F8184042E075CF10F98BA88ABFF1F36" # Decimal "47545396818609319588074484786899049290652725314938191835667190243225814114102"
|
||||
),
|
||||
serialized_sig: "5c48ea4f0f2257fa23bd25e6fcb0b75bbe2ff9bbda0167118dab2bb6e31ba76e691dbdaf2a231fc9958cd8edd99507121f8184042e075cf10f98ba88abff1f3601"
|
||||
)
|
||||
|
||||
eve* = testKeySig(
|
||||
privkey: "876be0999ed9b7fc26f1b270903ef7b0c35291f89407903270fea611c85f515c",
|
||||
pubkey: "c06641f0d04f64dba13eac9e52999f2d10a1ff0ca68975716b6583dee0318d91e7c2aed363ed22edeba2215b03f6237184833fd7d4ad65f75c2c1d5ea0abecc0",
|
||||
raw_sig: (
|
||||
v: 0,
|
||||
r: "BABEEFC5082D3CA2E0BC80532AB38F9CFB196FB9977401B2F6A98061F15ED603", # Decimal "84467545608142925331782333363288012579669270632210954476013542647119929595395",
|
||||
s: "603D0AF084BF906B2CDF6CDDE8B2E1C3E51A41AF5E9ADEC7F3643B3F1AA2AADF" # Decimal "43529886636775750164425297556346136250671451061152161143648812009114516499167"
|
||||
),
|
||||
serialized_sig: "babeefc5082d3ca2e0bc80532ab38f9cfb196fb9977401b2f6a98061f15ed603603d0af084bf906b2cdf6cdde8b2e1c3e51a41af5e9adec7f3643b3f1aa2aadf00"
|
||||
)
|
|
@ -0,0 +1,66 @@
|
|||
# Nim Eth-keys
|
||||
# Copyright (c) 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import eth/keys, #../src/private/conversion_bytes,
|
||||
./config
|
||||
|
||||
import unittest
|
||||
|
||||
suite "Test key and signature data structure":
|
||||
|
||||
test "Signing from private key object (ported from official eth-keys)":
|
||||
for person in [alice, bob, eve]:
|
||||
let
|
||||
pk = initPrivateKey(person.privkey)
|
||||
signature = pk.sign_msg(MSG)
|
||||
|
||||
check: verify_msg(pk.public_key, MSG, signature)
|
||||
|
||||
test "Hash signing from private key object (ported from official eth-keys)":
|
||||
for person in [alice, bob, eve]:
|
||||
let
|
||||
pk = initPrivateKey(person.privkey)
|
||||
signature = pk.sign_msg(MSGHASH)
|
||||
|
||||
check: verify_msg(pk.public_key, MSGHASH, signature)
|
||||
|
||||
test "Recover public key from message":
|
||||
for person in [alice, bob, eve]:
|
||||
let
|
||||
pk = initPrivateKey(person.privkey)
|
||||
signature = pk.sign_msg(MSG)
|
||||
|
||||
recovered_pubkey = recover_pubkey_from_msg(MSG, signature)
|
||||
|
||||
check: pk.public_key == recovered_pubkey
|
||||
|
||||
test "Recover public key from message hash":
|
||||
for person in [alice, bob, eve]:
|
||||
let
|
||||
pk = initPrivateKey(person.privkey)
|
||||
signature = pk.sign_msg(MSGHASH)
|
||||
|
||||
recovered_pubkey = recover_pubkey_from_msg(MSGHASH, signature)
|
||||
|
||||
check: pk.public_key == recovered_pubkey
|
||||
|
||||
test "Signature serialization and deserialization":
|
||||
for person in [alice, bob, eve]:
|
||||
let
|
||||
pk = initPrivateKey(person.privkey)
|
||||
signature = pk.sign_msg(MSG)
|
||||
deserializedSignature = parseSignature(hexToSeqByteBE(person.serialized_sig))
|
||||
|
||||
var serialized_sig: array[65, byte]
|
||||
signature.serialize(serialized_sig)
|
||||
|
||||
check:
|
||||
signature == deserializedSignature
|
||||
serialized_sig.toHex() == person.serialized_sig
|
||||
$signature == person.serialized_sig
|
|
@ -0,0 +1,22 @@
|
|||
# Nim Eth-keys
|
||||
# Copyright (c) 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ../src/eth_keys,
|
||||
./config
|
||||
|
||||
import unittest
|
||||
|
||||
suite "Testing private -> public key conversion":
|
||||
test "Known private to known public keys (test data from Ethereum eth-keys)":
|
||||
for person in [alice, bob, eve]:
|
||||
let privkey = initPrivateKey(person.privkey)
|
||||
|
||||
let computed_pubkey = $privkey.public_key
|
||||
|
||||
check: computed_pubkey == person.pubkey
|
|
@ -0,0 +1,267 @@
|
|||
#
|
||||
# Ethereum P2P
|
||||
# (c) Copyright 2018
|
||||
# Status Research & Development GmbH
|
||||
#
|
||||
# See the file "LICENSE", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
import unittest, strutils
|
||||
import eth/keys
|
||||
import nimcrypto/hash, nimcrypto/keccak, nimcrypto/utils
|
||||
|
||||
proc compare(x: openarray[byte], y: openarray[byte]): bool =
|
||||
result = len(x) == len(y)
|
||||
if result:
|
||||
for i in 0..(len(x) - 1):
|
||||
if x[i] != y[i]:
|
||||
result = false
|
||||
break
|
||||
const
|
||||
pkbytes = "58d23b55bc9cdce1f18c2500f40ff4ab7245df9a89505e9b1fa4851f623d241d"
|
||||
message = "message"
|
||||
address = "dc544d1aa88ff8bbd2f2aec754b1f1e99e1812fd"
|
||||
|
||||
alice = [
|
||||
"9c0257114eb9399a2985f8e75dad7600c5d89fe3824ffa99ec1c3eb8bf3b0501",
|
||||
"""5eed5fa3a67696c334762bb4823e585e2ee579aba3558d9955296d6c04541b42
|
||||
6078dbd48d74af1fd0c72aa1a05147cf17be6b60bdbed6ba19b08ec28445b0ca""",
|
||||
"""b20e2ea5d3cbaa83c1e0372f110cf12535648613b479b64c1a8c1a20c5021f38
|
||||
0434d07ec5795e3f789794351658e80b7faf47a46328f41e019d7b853745cdfd01"""
|
||||
]
|
||||
bob = [
|
||||
"38e47a7b719dce63662aeaf43440326f551b8a7ee198cee35cb5d517f2d296a2",
|
||||
"""347746ccb908e583927285fa4bd202f08e2f82f09c920233d89c47c79e48f937
|
||||
d049130e3d1c14cf7b21afefc057f71da73dec8e8ff74ff47dc6a574ccd5d570""",
|
||||
"""5c48ea4f0f2257fa23bd25e6fcb0b75bbe2ff9bbda0167118dab2bb6e31ba76e
|
||||
691dbdaf2a231fc9958cd8edd99507121f8184042e075cf10f98ba88abff1f3601"""
|
||||
]
|
||||
eve = [
|
||||
"876be0999ed9b7fc26f1b270903ef7b0c35291f89407903270fea611c85f515c",
|
||||
"""c06641f0d04f64dba13eac9e52999f2d10a1ff0ca68975716b6583dee0318d91
|
||||
e7c2aed363ed22edeba2215b03f6237184833fd7d4ad65f75c2c1d5ea0abecc0""",
|
||||
"""babeefc5082d3ca2e0bc80532ab38f9cfb196fb9977401b2f6a98061f15ed603
|
||||
603d0af084bf906b2cdf6cdde8b2e1c3e51a41af5e9adec7f3643b3f1aa2aadf00"""
|
||||
]
|
||||
|
||||
suite "ECC/ECDSA/ECDHE tests suite":
|
||||
test "Known private to known public keys (test data from Ethereum eth-keys)":
|
||||
for person in [alice, bob, eve]:
|
||||
let privkey = initPrivateKey(person[0])
|
||||
var pubkeyHex = $privkey.getPublicKey()
|
||||
check:
|
||||
pubkeyHex == stripSpaces(person[1])
|
||||
|
||||
test "Recover public key from message":
|
||||
for person in [alice, bob, eve]:
|
||||
let privkey = initPrivateKey(person[0])
|
||||
let signature = privkey.signMessage(message)
|
||||
let recoveredKey = signature.recoverKeyFromSignature(message)
|
||||
check:
|
||||
$privkey.getPublicKey() == $recoveredKey
|
||||
|
||||
test "Signature serialization and deserialization":
|
||||
for person in [alice, bob, eve]:
|
||||
let privkey = initPrivateKey(person[0])
|
||||
let signature = privkey.signMessage(message)
|
||||
let expectSignature = initSignature(person[2])
|
||||
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)
|
||||
var signature = s.signMessage(message)
|
||||
var p = recoverKeyFromSignature(signature, mhash)
|
||||
check:
|
||||
s.getPublicKey() == p
|
||||
|
||||
test "test_to_address_from_public_key":
|
||||
var s = initPrivateKey(pkbytes)
|
||||
var chk = s.getPublicKey().toAddress()
|
||||
var expect = "0x" & address
|
||||
check chk == expect
|
||||
|
||||
test "test_to_canonical_address_from_public_key":
|
||||
var s = initPrivateKey(pkbytes)
|
||||
var chk = s.getPublicKey().toCanonicalAddress()
|
||||
var expect = fromHex(stripSpaces(address))
|
||||
check compare(chk, expect) == true
|
||||
|
||||
test "test_to_checksum_address_from_public_key":
|
||||
var s = initPrivateKey(pkbytes)
|
||||
var chk = s.getPublicKey().toChecksumAddress()
|
||||
var expect = "0x" & address
|
||||
check:
|
||||
chk.toLowerAscii() == expect
|
||||
|
||||
test "EIP-55 checksum addresses test cases":
|
||||
var checks = [
|
||||
"0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
|
||||
"0x52908400098527886E0F7030069857D2E4169EE7",
|
||||
"0x8617E340B3D01FA5F11F306F4090FD50E238070D",
|
||||
"0xde709f2102306220921060314715629080e2fb77",
|
||||
"0x27b1fdb04752bbc536007a920d24acb045561c26",
|
||||
"0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
|
||||
"0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
|
||||
"0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB",
|
||||
"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb"
|
||||
]
|
||||
var badchecks = [
|
||||
"",
|
||||
"0xXB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
|
||||
"0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d35X",
|
||||
"0XfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
|
||||
"XXfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
|
||||
"0xfB6916095"
|
||||
]
|
||||
for item in checks:
|
||||
check validateChecksumAddress(item) == true
|
||||
for item in badchecks:
|
||||
check validateChecksumAddress(item) == false
|
||||
|
||||
test "EIP-55 100 addresses":
|
||||
for i in 1..100:
|
||||
var kp = newKeyPair()
|
||||
var chaddress = kp.pubkey.toChecksumAddress()
|
||||
var noaddress = kp.pubkey.toAddress()
|
||||
if noaddress != chaddress:
|
||||
check validateChecksumAddress(noaddress) == false
|
||||
check validateChecksumAddress(chaddress) == true
|
||||
|
||||
test "ECDHE/py-evm test_ecies.py#L19":
|
||||
# ECDHE test vectors
|
||||
# Copied from
|
||||
# https://github.com/ethereum/py-evm/blob/master/tests/p2p/test_ecies.py#L19
|
||||
const privateKeys = [
|
||||
"332143e9629eedff7d142d741f896258f5a1bfab54dab2121d3ec5000093d74b",
|
||||
"7ebbc6a8358bc76dd73ebc557056702c8cfc34e5cfcd90eb83af0347575fd2ad"
|
||||
]
|
||||
const publicKeys = [
|
||||
"""f0d2b97981bd0d415a843b5dfe8ab77a30300daab3658c578f2340308a2da1a07
|
||||
f0821367332598b6aa4e180a41e92f4ebbae3518da847f0b1c0bbfe20bcf4e1""",
|
||||
"""83ede0f19c3c98649265956a4193677b14c338a22de2086a08d84e4446fe37e4e
|
||||
233478259ec90dbeef52f4f6c890f8c38660ec7b61b9d439b8a6d1c323dc025"""
|
||||
]
|
||||
const sharedSecrets = [
|
||||
"ee1418607c2fcfb57fda40380e885a707f49000a5dda056d828b7d9bd1f29a08",
|
||||
"167ccc13ac5e8a26b131c3446030c60fbfac6aa8e31149d0869f93626a4cdf62"
|
||||
]
|
||||
var secret: SharedSecret
|
||||
for i in 0..1:
|
||||
var s = privateKeys[i].initPrivateKey()
|
||||
var p = publicKeys[i].initPublicKey()
|
||||
let expect = fromHex(stripSpaces(sharedSecrets[i]))
|
||||
check:
|
||||
ecdhAgree(s, p, secret) == EthKeysStatus.Success
|
||||
compare(expect, secret.data) == true
|
||||
|
||||
test "ECDHE/cpp-ethereum crypto.cpp#L394":
|
||||
# ECDHE test vectors
|
||||
# Copied from https://github.com/ethereum/cpp-ethereum/blob/develop/test/unittests/libdevcrypto/crypto.cpp#L394
|
||||
var expectm = """
|
||||
8ac7e464348b85d9fdfc0a81f2fdc0bbbb8ee5fb3840de6ed60ad9372e718977"""
|
||||
var secret: SharedSecret
|
||||
var s = initPrivateKey(keccak256.digest("ecdhAgree").data)
|
||||
var p = s.getPublicKey()
|
||||
let expect = fromHex(stripSpaces(expectm))
|
||||
check:
|
||||
ecdhAgree(s, p, secret) == EthKeysStatus.Success
|
||||
compare(expect, secret.data) == true
|
||||
|
||||
test "ECDHE/cpp-ethereum rlpx.cpp#L425":
|
||||
# ECDHE test vectors
|
||||
# Copied from https://github.com/ethereum/cpp-ethereum/blob/2409d7ec7d34d5ff5770463b87eb87f758e621fe/test/unittests/libp2p/rlpx.cpp#L425
|
||||
var s0 = """
|
||||
332143e9629eedff7d142d741f896258f5a1bfab54dab2121d3ec5000093d74b"""
|
||||
var p0 = """
|
||||
f0d2b97981bd0d415a843b5dfe8ab77a30300daab3658c578f2340308a2da1a0
|
||||
7f0821367332598b6aa4e180a41e92f4ebbae3518da847f0b1c0bbfe20bcf4e1"""
|
||||
var e0 = """
|
||||
ee1418607c2fcfb57fda40380e885a707f49000a5dda056d828b7d9bd1f29a08"""
|
||||
var secret: SharedSecret
|
||||
var s = initPrivateKey(s0)
|
||||
var p = initPublicKey(p0)
|
||||
let expect = fromHex(stripSpaces(e0))
|
||||
check:
|
||||
ecdhAgree(s, p, secret) == Success
|
||||
compare(expect, secret.data) == true
|
||||
|
||||
test "ECDSA/cpp-ethereum crypto.cpp#L132":
|
||||
# ECDSA test vectors
|
||||
# Copied from https://github.com/ethereum/cpp-ethereum/blob/develop/test/unittests/libdevcrypto/crypto.cpp#L132
|
||||
var signature = """
|
||||
b826808a8c41e00b7c5d71f211f005a84a7b97949d5e765831e1da4e34c9b8295d
|
||||
2a622eee50f25af78241c1cb7cfff11bcf2a13fe65dee1e3b86fd79a4e3ed000"""
|
||||
var pubkey = """
|
||||
e40930c838d6cca526795596e368d16083f0672f4ab61788277abfa23c3740e1cc
|
||||
84453b0b24f49086feba0bd978bb4446bae8dff1e79fcc1e9cf482ec2d07c3"""
|
||||
var check1 = fromHex(stripSpaces(signature))
|
||||
var check2 = fromHex(stripSpaces(pubkey))
|
||||
var sig: Signature
|
||||
var key: PublicKey
|
||||
var s = initPrivateKey(keccak256.digest("sec").data)
|
||||
var m = keccak256.digest("msg").data
|
||||
check signRawMessage(m, s, sig) == Success
|
||||
var sersig = sig.getRaw()
|
||||
check recoverSignatureKey(sersig, m, key) == Success
|
||||
var serkey = key.getRaw()
|
||||
check:
|
||||
compare(sersig, check1) == true
|
||||
compare(serkey, check2) == true
|
||||
|
||||
test "ECDSA/100 signatures":
|
||||
# signature test
|
||||
var rkey: PublicKey
|
||||
var sig: Signature
|
||||
for i in 1..100:
|
||||
var m = newPrivateKey().data
|
||||
var s = newPrivateKey()
|
||||
var key = s.getPublicKey()
|
||||
check signRawMessage(m, s, sig) == Success
|
||||
var sersig = sig.getRaw()
|
||||
check:
|
||||
recoverSignatureKey(sersig, m, rkey) == Success
|
||||
key == rkey
|
||||
|
||||
test "KEYS/100 create/recovery keys":
|
||||
# key create/recovery test
|
||||
var rkey: PublicKey
|
||||
for i in 1..100:
|
||||
var s = newPrivateKey()
|
||||
var key = s.getPublicKey()
|
||||
check:
|
||||
recoverPublicKey(key.getRaw(), rkey) == Success
|
||||
key == rkey
|
||||
|
||||
test "ECDHE/100 shared secrets":
|
||||
# ECDHE shared secret test
|
||||
var secret1, secret2: SharedSecret
|
||||
for i in 1..100:
|
||||
var aliceSecret = newPrivateKey()
|
||||
var alicePublic = aliceSecret.getPublicKey()
|
||||
var bobSecret = newPrivateKey()
|
||||
var bobPublic = bobSecret.getPublicKey()
|
||||
check:
|
||||
ecdhAgree(aliceSecret, bobPublic, secret1) == Success
|
||||
ecdhAgree(bobSecret, alicePublic, secret2) == Success
|
||||
secret1 == secret2
|
||||
|
||||
test "isZeroKey() checks":
|
||||
var seckey1: PrivateKey
|
||||
var pubkey1: PublicKey
|
||||
var seckey2 = newPrivateKey()
|
||||
var pubkey2 = seckey2.getPublicKey()
|
||||
|
||||
check:
|
||||
seckey1.isZeroKey() == true
|
||||
pubkey1.isZeroKey() == true
|
||||
seckey2.isZeroKey() == false
|
||||
pubkey2.isZeroKey() == false
|
Loading…
Reference in New Issue