-
-
Notifications
You must be signed in to change notification settings - Fork 51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ECDSA over secp256k1 signatures and verification #490
Changes from 1 commit
f43a97c
6434eba
e8540b6
41d502d
2760af4
9f31c38
5862d43
4fd34e2
a4ca057
22bd57b
4e17514
46d8f92
7164f07
890b185
50de116
931044d
fe8a8aa
bec1536
34442fa
06f7a5f
ad16403
e9174e6
4d72a6b
a8ecd59
828189c
722fa37
64130a1
e9387e8
0d24f6a
5229551
1d05da4
c0b3806
87bc887
8000567
04ce1c8
0c5195f
89688ba
5011fe3
fa5a5eb
2f6a897
2693aec
1b44a8f
c2c39af
fa9e0ab
4cac2ec
30285ff
901597e
6c09fe1
0bbc839
20057e0
9642ca6
2fd2bb2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,351 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
../hashes/h_sha256, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It should be hash agnostic, with it's actual instantiation using a predefined hash. See the function signature of BLS signatures constantine/constantine/signatures/bls_signatures.nim Lines 51 to 88 in 13d5bb7
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
../named/algebras, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
../math/io/[io_bigints, io_fields, io_ec], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
../math/elliptic/[ec_shortweierstrass_affine, ec_shortweierstrass_jacobian, ec_scalar_mul], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
../math/arithmetic, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
../platforms/abstractions, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
../serialization/codecs, # for fromHex and (in the future) base64 encoding | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
../mac/mac_hmac # for deterministic nonce generation via RFC 6979 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
../named/zoo_generators, # for generator | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
../csprngs/sysrand | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import std / macros # for `update` convenience helper | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## Decides the type of sampler we use for the nonce. By default | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## a simple uniform random sampler. Alternatively a deterministic | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## sampler based on message hash and private key. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
NonceSampler* = enum | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
nsRandom, ## pure uniform random sampling | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
nsRfc6979 ## deterministic according to RFC 6979 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# For easier readibility, define the curve and generator | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# as globals in this file | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const C = Secp256k1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const G = Secp256k1.getGenerator("G1") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The signature scheme itself should be generic in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup, fair enough. I wrote it by defining the curve like this so that the code itself is already an (almost) valid generic / static proc. Didn't know if we want to make it fully generic immediately or start with secp256k1 only. Will address it. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
proc hashMessage(message: string): array[32, byte] = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Hash a given message | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var h {.noinit.}: sha256 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
h.init() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
h.update(message) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
h.finish(result) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This duplicates constantine/constantine/hashes.nim Lines 17 to 54 in 13d5bb7
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
proc toBytes(x: Fr[C] | Fp[C]): array[32, byte] = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
I assume this is temporary for debugging? Returning big arrays tends to generate bad code:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, that either slipped my mind or I just wasn't aware of it! Good to know. Will change it to an in-place variant. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let bi = x.toBig() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
discard result.marshal(bi, bigEndian) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
proc toDER(r, s: Fr[C]): seq[byte] = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## Converts the given signature `(r, s)` into a signature in | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## ASN.1 DER encoding. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## Note that the implementation is not written for efficiency | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## and should be viewed as a convenience tool for the time being. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Convert signature to DER format | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result = @[byte(0x30)] # sequence marker | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Convert r and s to big-endian bytes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var rBytes = @(r.toBytes()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var sBytes = @(s.toBytes()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Add padding if needed (if high bit is set) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (rBytes[0] and 0x80) != 0: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
rBytes = @[byte(0)] & rBytes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (sBytes[0] and 0x80) != 0: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sBytes = @[byte(0)] & sBytes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Add integer markers and lengths | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let rEncoded = @[byte(0x02), byte(rBytes.len)] & rBytes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let sEncoded = @[byte(0x02), byte(sBytes.len)] & sBytes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Total length | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let totalLen = rEncoded.len + sEncoded.len | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result.add(byte(totalLen)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Add r and s encodings | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result.add(rEncoded) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result.add(sEncoded) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func fromDigest(dst: var Fr[C], src: array[32, byte]): bool {.discardable.} = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## Convert a SHA256 digest to an element in the scalar field `Fr[Secp256k1]`. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## The proc returns a boolean indicating whether the data in `src` is | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## smaller than the field modulus. It is discardable, because in some | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## use cases this is fine (e.g. constructing a field element from a hash), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## but invalid in the nonce generation following RFC6979. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var scalar {.noInit.}: BigInt[256] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
scalar.unmarshal(src, bigEndian) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# `true` if smaller than modulus | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result = bool(scalar < Fr[C].getModulus()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
dst.fromBig(scalar) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
proc randomFieldElement[FF](): FF = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## random element in ~Fp[T]/Fr[T]~ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let m = FF.getModulus() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var b: matchingBigInt(FF.Name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
while b.isZero().bool or (b > m).bool: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## XXX: raise / what else to do if `sysrand` call fails? | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
doAssert b.limbs.sysrand() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In case this were to fail, do we want a different failure mode? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For now it's fine not to address this, AFAIK most protocols use RFC6979 to avoid adding the RNG to the attack surface. If the RNG fails to produce data, I'm not even sure what it means at the OS level, no more entropy maybe? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure either to be honest, but given that we do return a |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result.fromBig(b) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
proc arrayWith[N: static int](val: byte): array[N, byte] = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Same remark on returning big values |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
for i in 0 ..< N: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result[i] = val | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
macro update[T](hmac: var HMAC[T], args: varargs[untyped]): untyped = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## Mini helper to allow HMAC to act on multiple arguments in succession | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result = newStmtList() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
for arg in args: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result.add quote do: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
`hmac`.update(`arg`) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
template round(hmac, input, output: typed, args: varargs[untyped]): untyped = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## Perform a full 'round' of HMAC. Pre-shared secret is `input`, the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## result will be stored in `output`. All `args` are fed into the HMAC | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## in the order they are given. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
hmac.init(input) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
hmac.update(args) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
hmac.finish(output) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
proc nonceRfc6979(msgHash, privateKey: Fr[C]): Fr[C] = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## Generate deterministic nonce according to RFC 6979. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## Spec: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## https://datatracker.ietf.org/doc/html/rfc6979#section-3.2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Step a: `h1 = H(m)` hash message (already done, input is hash), convert to array of bytes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let msgHashBytes = msgHash.toBytes() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Piece of step d: Conversion of the private key to a byte array. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# No need for `bits2octets`, because the private key is already a valid | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# scalar in the field `Fr[C]` and thus < p-1 (`bits2octets` converts | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# `r` bytes to a BigInt, reduces modulo prime order `p` and converts to | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# a byte array). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let privKeyBytes = privateKey.toBytes() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Initial values | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Step b: `V = 0x01 0x01 0x01 ... 0x01` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var v = arrayWith[32](byte 0x01) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Step c: `K = 0x00 0x00 0x00 ... 0x00` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var k = arrayWith[32](byte 0x00) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Create HMAC contexts | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var hmac {.noinit.}: HMAC[sha256] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Step d: `K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1))` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
hmac.round(k, k, v, [byte 0x00], privKeyBytes, msgHashBytes) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Step e: `V = HMAC_K(V)` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
hmac.round(k, v, v) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Step f: `K = HMAC_K(V || 0x01 || int2octets(x) || bits2octets(h1))` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
hmac.round(k, k, v, [byte 0x01], privKeyBytes, msgHashBytes) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Step g: `V = HMAC_K(V)` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
hmac.round(k, v, v) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Step h: Loop until valid nonce found | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
while true: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Step h.1 (init T to zero) and h.2: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# `V = HMAC_K(V)` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# `T = T || V` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# We do not need to accumulate a `T`, because we use SHA256 as a hash | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# function (256 bits) and Secp256k1 as a curve (also 256 big int). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
hmac.round(k, v, v) # v becomes T | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Step h.3: `k = bits2int(T)` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var candidate: Fr[C] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# `fromDigest` returns `false` if the array is larger than the field modulus, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# important for uniform sampling in valid range `[1, q-1]`! | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let smaller = candidate.fromDigest(v) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if not bool(candidate.isZero()) and smaller: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return candidate | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Step h.3 failure state: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# `K = HMAC_K(V || 0x00)` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# `V = HMAC_K(V)` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Try again if invalid | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
hmac.round(k, k, v, [byte 0x00]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
hmac.round(k, v, v) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
proc generateNonce(kind: NonceSampler, msgHash, privateKey: Fr[C]): Fr[C] = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
case kind | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
of nsRandom: randomFieldElement[Fr[C]]() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
of nsRfc6979: nonceRfc6979(msgHash, privateKey) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
proc signMessage*(message: string, privateKey: Fr[C], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
nonceSampler: NonceSampler = nsRandom): tuple[r, s: Fr[C]] = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can reuse the API constantine/constantine/signatures/bls_signatures.nim Lines 51 to 55 in 13d5bb7
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## Sign a given `message` using the `privateKey`. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## By default we use a purely random nonce (uniform random number), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## but passing `nonceSampler = nsRfc6979` uses RFC 6979 to compute | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## a deterministic nonce (and thus deterministic signature) given | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## the message and private key as base. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# 1. hash the message in big endian order | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let h = hashMessage(message) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var message_hash: Fr[C] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
message_hash.fromDigest(h) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# loop until we found a valid (non zero) signature | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
while true: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Generate random nonce | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var k = generateNonce(nonceSampler, message_hash, privateKey) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Calculate r (x-coordinate of kG) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# `r = k·G (mod n)` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let r_point = k * G | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# get x coordinate of the point `r` *in affine coordinates* | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let rx = r_point.getAffine().x # element of Fp | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## XXX: smarter way for this? | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let r = Fr[C].fromBig(rx.toBig()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need to convert the coordinate in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the best generic conversion. In theory for secp256k1, you could take |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if bool(r.isZero()): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
continue # try again | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Calculate s | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# `s = (k⁻¹ · (h + r · p)) (mod n)` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# with `h`: message hash as `Fr[C]` (if we didn't use SHA256 w/ 32 byte output | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# we'd need to truncate to N bits for N being bits in modulo `n`) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
k.inv() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var s = (k * (message_hash + r * privateKey)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might be faster to do k*privateKey and then a multiscalarmul([k, k*privkey], [message_hash, r]). Note that the out-of-place functions are for convenience when debugging/testing/developping but not for production code (#413) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I understand. Still hasn't fully sunk in (intuitively) to avoid out-of-place procs! My bad. 😁 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# get inversion of `s` for 'lower-s normalization' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var sneg = s # inversion of `s` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sneg.neg() # q - s | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# conditionally assign result based on BigInt comparison | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let mask = s.toBig() > sneg.toBig() # if true, `s` is in upper half, need `sneg` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ccopy(s, sneg, mask) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if bool(s == zero): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
continue # try again | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return (r: r, s: s) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
proc verifySignature*( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
message: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
signature: tuple[r, s: Fr[C]], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
publicKey: EC_ShortW_Aff[Fp[C], G1] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
): bool = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## Verify a given `signature` for a `message` using the given `publicKey`. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# 1. Hash the message (same as in signing) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let h = hashMessage(message) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var e: Fr[C] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
e.fromDigest(h) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# 2. Compute w = s⁻¹ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var w = signature.s | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
w.inv() # w = s⁻¹ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# 3. Compute u₁ = ew and u₂ = rw | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let u1 = e * w | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let u2 = signature.r * w | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# 4. Compute u₁G + u₂Q | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let point1 = u1 * G | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let point2 = u2 * publicKey | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let R = point1 + point2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# 5. Get x coordinate and convert to Fr (like in signing) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let x = R.getAffine().x | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let r_computed = Fr[C].fromBig(x.toBig()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# 6. Verify r_computed equals provided r | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result = bool(r_computed == signature.r) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
proc getPrivateKey*(): Fr[C] = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## Generate a new private key using a cryptographic random number generator. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result = randomFieldElement[Fr[C]]() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
proc toPemPrivateKey(privateKey: Fr[C]): seq[byte] = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Start with SEQUENCE | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result = @[byte(0x30)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Version (always 1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let version = @[byte(0x02), byte(1), byte(1)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Private key as octet string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let privKeyBytes = privateKey.toBytes() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let privKeyEncoded = @[byte(0x04), byte(privKeyBytes.len)] & @privKeyBytes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Parameters (secp256k1 OID: 1.3.132.0.10) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let parameters = @[byte(0xA0), byte(7), byte(6), byte(5), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
byte(0x2B), byte(0x81), byte(0x04), byte(0x00), byte(0x0A)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Combine all parts | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let contents = version & privKeyEncoded & parameters | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result.add(byte(contents.len)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result.add(contents) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
proc toPemPublicKey(publicKey: EC_ShortW_Aff[Fp[C], G1]): seq[byte] = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Start with SEQUENCE | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result = @[byte(0x30)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Algorithm identifier | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let algoId = @[ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
byte(0x30), byte(0x10), # SEQUENCE | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
byte(0x06), byte(0x07), # OID for EC | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
byte(0x2A), byte(0x86), byte(0x48), # 1.2.840.10045.2.1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
byte(0xCE), byte(0x3D), byte(0x02), byte(0x01), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
byte(0x06), byte(0x05), # OID for secp256k1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
byte(0x2B), byte(0x81), byte(0x04), byte(0x00), byte(0x0A) # 1.3.132.0.10 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Public key as bit string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let pubKeyBytes = @[ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
byte(0x00), # DER BIT STRING: number of unused bits (always 0 for keys) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
byte(0x04) # SEC1: uncompressed point format marker | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
] & @(publicKey.x.toBytes()) & @(publicKey.y.toBytes()) # x & y coordinates | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let pubKeyEncoded = @[byte(0x03), byte(pubKeyBytes.len)] & pubKeyBytes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Combine all parts | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let contents = algoId & pubKeyEncoded | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result.add(byte(contents.len)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result.add(contents) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## NOTE: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## The below procs / code is currently "unsuited" for Constantine in the sense that | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## it currently still contains stdlib dependencies. Most of those are trivial, with the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## exception of a base64 encoder. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## Having a ANS1.DER encoder (and maybe decoder in the future) for SEC1 private and | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## public keys would be nice to have in CTT, I think (at least for the curves that | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## we support for the related operations; secp256k1 at the moment). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## XXX: Might also need to replace this by header / tail approach to avoid | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## stdlib `%`! | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import std / [strutils, base64, math] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const PrivateKeyTmpl = """-----BEGIN EC PRIVATE KEY----- | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
$# | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
-----END EC PRIVATE KEY----- | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const PublicKeyTmpl = """-----BEGIN PUBLIC KEY----- | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
$# | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
-----END PUBLIC KEY----- | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
proc wrap(s: string, maxLineWidth = 64): string = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## Wrap the given string at `maxLineWidth` over multiple lines | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let lines = s.len.ceilDiv maxLineWidth | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result = newStringOfCap(s.len + lines) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
for i in 0 ..< lines: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let frm = i * maxLineWidth | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let to = min(s.len, (i+1) * maxLineWidth) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result.add s[frm ..< to] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if i < lines-1: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result.add "\n" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
proc toPemFile*(publicKey: EC_ShortW_Aff[Fp[C], G1]): string = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## Convert a given private key to data in PEM format following SEC1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# 1. Convert public key to ASN.1 DER | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let derB = publicKey.toPemPublicKey() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# 2. Encode bytes in base64 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let der64 = derB.encode().wrap() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# 3. Wrap in begin/end public key template | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result = PublicKeyTmpl % [der64] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
proc toPemFile*(privateKey: Fr[C]): string = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## XXX: For now using `std/base64` but will need to write base64 encoder | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## & add tests for CTT base64 decoder! | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
## Convert a given private key to data in PEM format following SEC1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# 1. Convert private key to ASN.1 DER encoding | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let derB = toPemPrivateKey(privateKey) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# 2. Encode bytes in base64 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let der64 = derB.encode().wrap() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# 3. Wrap in begin/end private key template | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
result = PrivateKeyTmpl % [der64] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ECDSA should be in the
constantine/signatures
folder