This repository has been archived by the owner on Mar 26, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
libsecp256k1.nim
160 lines (130 loc) · 5.44 KB
/
libsecp256k1.nim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# 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 ../datatypes, ../private/conversion_bytes
import secp256k1, nimcrypto
const SECP256K1_CONTEXT_ALL = SECP256K1_CONTEXT_VERIFY or SECP256K1_CONTEXT_SIGN
let ctx = secp256k1_context_create(SECP256K1_CONTEXT_ALL)
{.experimental.}
proc `=destroy`(ctx: ptr secp256k1_context) =
if not ctx.isNil:
ctx.secp256k1_context_destroy
type
Serialized_PubKey = array[65, byte]
proc asPtrPubKey(key: PublicKey): ptr secp256k1_pubkey =
cast[ptr secp256k1_pubkey](unsafeAddr key)
proc asPtrCuchar(key: PrivateKey): ptr cuchar =
cast[ptr cuchar](unsafeAddr key)
proc asPtrCuchar(key: Serialized_PubKey): ptr cuchar =
cast[ptr cuchar](unsafeAddr key)
proc asPtrCuchar(msg_hash: MDigest[256]): ptr cuchar =
cast[ptr cuchar](unsafeAddr msg_hash)
proc asPtrRecoverableSignature(sig: Signature): ptr secp256k1_ecdsa_recoverable_signature =
cast[ptr secp256k1_ecdsa_recoverable_signature](unsafeAddr sig)
proc private_key_to_public_key*(key: PrivateKey): PublicKey {.noInit.}=
## Generates a public key from the private key
let success:bool = bool secp256k1_ec_pubkey_create(
ctx,
result.asPtrPubKey,
key.asPtrCuchar
)
if not success:
raise newException(ValueError, "Private key is invalid")
proc serialize*(s: Signature, output: var openarray[byte], fromIdx: int = 0) =
## Serialize an ECDSA signature in compact format, 65 bytes long
## (64 bytes + recovery id). The output is written starting from `fromIdx`.
assert(output.len - fromIdx >= 65)
var v: cint
discard secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx,
cast[ptr cuchar](addr output[fromIdx]), addr v, s.asPtrRecoverableSignature)
output[fromIdx + 64] = byte(v)
proc parseSignature*(data: openarray[byte], fromIdx: int = 0): Signature =
## Parse a compact ECDSA signature. Bytes [fromIdx .. fromIdx + 63] of `data`
## should contain the signature, byte [fromIdx + 64] should contain the recovery id.
assert(data.len - fromIdx >= 65)
if secp256k1_ecdsa_recoverable_signature_parse_compact(ctx,
result.asPtrRecoverableSignature,
cast[ptr cuchar](unsafeAddr data[fromIdx]),
cint(data[fromIdx + 64])) != 1:
raise newException(ValueError, "Signature data is invalid")
proc serialize*(key: PublicKey, output: var openarray[byte], addPrefix = false) =
## Exports a publicKey to `output` buffer so that it can be
var
tmp{.noInit.}: Serialized_PubKey
tmp_len: csize = 65
# Proc always return 1
discard secp256k1_ec_pubkey_serialize(
ctx,
tmp.asPtrCuchar,
addr tmp_len,
key.asPtrPubKey,
SECP256K1_EC_UNCOMPRESSED
)
assert tmp_len == 65 # header 0x04 (uncompressed) + 128 hex char
if addPrefix:
assert(output.len >= 65)
copyMem(addr output[0], addr tmp[0], 65)
else:
assert(output.len >= 64)
copyMem(addr output[0], addr tmp[1], 64) # Skip the 0x04 prefix
proc toString*(key: PublicKey): string =
var data: array[64, byte]
key.serialize(data)
result = data.toHex
proc toStringWithPrefix*(key: PublicKey): string =
var data: array[65, byte]
key.serialize(data, true)
result = data.toHex
proc serialize*(key: PublicKey): string {.deprecated.} = key.toStringWithPrefix()
proc parsePublicKeyWithPrefix(data: openarray[byte], result: var PublicKey) =
## Parse a variable-length public key into the PublicKey object
if secp256k1_ec_pubkey_parse(ctx, result.asPtrPubKey, cast[ptr cuchar](unsafeAddr data[0]), data.len.csize) != 1:
raise newException(Exception, "Could not parse public key")
proc parsePublicKey*(data: openarray[byte]): PublicKey =
## Parse a variable-length public key into the PublicKey object
case data.len
of 65:
parsePublicKeyWithPrefix(data, result)
of 64:
var tmpData: Serialized_PubKey
copyMem(addr tmpData[1], unsafeAddr data[0], 64)
tmpData[0] = 0x04
parsePublicKeyWithPrefix(tmpData, result)
else: # TODO: Support other lengths
raise newException(Exception, "Wrong public key length")
proc ecdsa_sign*(key: PrivateKey, msg_hash: MDigest[256]): Signature {.noInit.}=
## Sign a message with a recoverable signature
## Input:
## - A message encoded with keccak_256
## Output:
## - A recoverable signature
let success: bool = bool secp256k1_ecdsa_sign_recoverable(
ctx,
result.asPtrRecoverableSignature,
msg_hash.asPtrCuchar,
key.asPtrCuchar,
nil, # Nonce function, default is RFC6979 (HMAC-SHA256)
nil # Arbitrary data for the nonce function
)
if not success:
raise newException(ValueError, "The nonce generation function failed, or the private key was invalid.")
proc ecdsa_recover*(msg_hash: MDigest[256], sig: Signature): PublicKey =
## Recover the Public Key from the message hash and the signature
let success: bool = bool secp256k1_ecdsa_recover(
ctx,
result.asPtrPubKey,
sig.asPtrRecoverableSignature,
msg_hash.asPtrCuchar
)
if not success:
raise newException(ValueError, "Failed to recover public key. Is the signature correct?")
proc `$`*(key: PublicKey): string {.inline.} = key.toString()
proc `$`*(s: Signature): string =
var data: array[65, byte]
s.serialize(data)
data.toHex()