2022-02-15 20:28:14 +01:00
|
|
|
module ed25519
|
|
|
|
|
|
|
|
import crypto.rand
|
|
|
|
import crypto.sha512
|
|
|
|
import crypto.internal.subtle
|
|
|
|
import crypto.ed25519.internal.edwards25519
|
|
|
|
|
|
|
|
// public_key_size is the sizeof public keys in bytes
|
|
|
|
pub const public_key_size = 32
|
|
|
|
|
|
|
|
// private_key_size is the sizeof private keys in bytes
|
|
|
|
pub const private_key_size = 64
|
|
|
|
|
|
|
|
// signature_size is the size of signatures generated and verified by this modules, in bytes.
|
|
|
|
pub const signature_size = 64
|
|
|
|
|
|
|
|
// seed_size is the size of private key seeds in bytes
|
|
|
|
pub const seed_size = 32
|
|
|
|
|
|
|
|
// `PublicKey` is Ed25519 public keys.
|
2022-04-15 14:35:35 +02:00
|
|
|
pub type PublicKey = []u8
|
2022-02-15 20:28:14 +01:00
|
|
|
|
2022-02-16 21:56:14 +01:00
|
|
|
// equal reports whether p and x have the same value.
|
2022-04-15 14:35:35 +02:00
|
|
|
pub fn (p PublicKey) equal(x []u8) bool {
|
2022-02-15 20:28:14 +01:00
|
|
|
return subtle.constant_time_compare(p, PublicKey(x)) == 1
|
|
|
|
}
|
|
|
|
|
2022-02-16 21:56:14 +01:00
|
|
|
// PrivateKey is Ed25519 private keys
|
2022-04-15 14:35:35 +02:00
|
|
|
pub type PrivateKey = []u8
|
2022-02-15 20:28:14 +01:00
|
|
|
|
2022-02-16 21:56:14 +01:00
|
|
|
// seed returns the private key seed corresponding to priv.
|
|
|
|
// RFC 8032's private keys correspond to seeds in this module.
|
2022-04-15 14:35:35 +02:00
|
|
|
pub fn (priv PrivateKey) seed() []u8 {
|
|
|
|
mut seed := []u8{len: ed25519.seed_size}
|
2022-03-09 19:26:00 +01:00
|
|
|
copy(mut seed, priv[..32])
|
2022-02-15 20:28:14 +01:00
|
|
|
return seed
|
|
|
|
}
|
|
|
|
|
2022-04-15 14:35:35 +02:00
|
|
|
// public_key returns the []u8 corresponding to priv.
|
2022-02-16 21:56:14 +01:00
|
|
|
pub fn (priv PrivateKey) public_key() PublicKey {
|
2022-02-15 20:28:14 +01:00
|
|
|
assert priv.len == ed25519.private_key_size
|
2022-04-15 14:35:35 +02:00
|
|
|
mut publickey := []u8{len: ed25519.public_key_size}
|
2022-03-09 19:26:00 +01:00
|
|
|
copy(mut publickey, priv[32..])
|
2022-02-15 20:28:14 +01:00
|
|
|
return PublicKey(publickey)
|
|
|
|
}
|
|
|
|
|
|
|
|
// currentyly x not `crypto.PrivateKey`
|
2022-04-15 14:35:35 +02:00
|
|
|
pub fn (priv PrivateKey) equal(x []u8) bool {
|
2022-02-15 20:28:14 +01:00
|
|
|
return subtle.constant_time_compare(priv, PrivateKey(x)) == 1
|
|
|
|
}
|
|
|
|
|
2022-02-16 21:56:14 +01:00
|
|
|
// sign signs the given message with priv.
|
2022-04-15 14:35:35 +02:00
|
|
|
pub fn (priv PrivateKey) sign(message []u8) ?[]u8 {
|
2022-02-15 20:28:14 +01:00
|
|
|
/*
|
|
|
|
if opts.HashFunc() != crypto.Hash(0) {
|
|
|
|
return nil, errors.New("ed25519: cannot sign hashed message")
|
|
|
|
}*/
|
|
|
|
|
|
|
|
return sign(priv, message)
|
|
|
|
}
|
|
|
|
|
2022-02-16 21:56:14 +01:00
|
|
|
// sign`signs the message with privatekey and returns a signature
|
2022-04-15 14:35:35 +02:00
|
|
|
pub fn sign(privatekey PrivateKey, message []u8) ?[]u8 {
|
|
|
|
mut signature := []u8{len: ed25519.signature_size}
|
2022-03-09 19:26:00 +01:00
|
|
|
sign_generic(mut signature, privatekey, message) ?
|
2022-02-15 20:28:14 +01:00
|
|
|
return signature
|
|
|
|
}
|
|
|
|
|
2022-04-15 14:35:35 +02:00
|
|
|
fn sign_generic(mut signature []u8, privatekey []u8, message []u8) ? {
|
2022-02-15 20:28:14 +01:00
|
|
|
if privatekey.len != ed25519.private_key_size {
|
|
|
|
panic('ed25519: bad private key length: $privatekey.len')
|
|
|
|
}
|
|
|
|
seed, publickey := privatekey[..ed25519.seed_size], privatekey[ed25519.seed_size..]
|
|
|
|
|
|
|
|
mut h := sha512.sum512(seed)
|
|
|
|
mut s := edwards25519.new_scalar()
|
|
|
|
s.set_bytes_with_clamping(h[..32]) ?
|
|
|
|
mut prefix := h[32..]
|
|
|
|
|
|
|
|
mut mh := sha512.new()
|
|
|
|
mh.write(prefix) ?
|
|
|
|
mh.write(message) ?
|
|
|
|
|
2022-04-15 14:35:35 +02:00
|
|
|
mut msg_digest := []u8{cap: sha512.size}
|
2022-02-15 20:28:14 +01:00
|
|
|
msg_digest = mh.sum(msg_digest)
|
|
|
|
|
|
|
|
mut r := edwards25519.new_scalar()
|
|
|
|
r.set_uniform_bytes(msg_digest) ?
|
|
|
|
|
|
|
|
mut rr := edwards25519.Point{}
|
|
|
|
rr.scalar_base_mult(mut r)
|
|
|
|
|
|
|
|
mut kh := sha512.new()
|
|
|
|
kh.write(rr.bytes()) ?
|
|
|
|
kh.write(publickey) ?
|
|
|
|
kh.write(message) ?
|
|
|
|
|
2022-04-15 14:35:35 +02:00
|
|
|
mut hram_digest := []u8{cap: sha512.size}
|
2022-02-15 20:28:14 +01:00
|
|
|
hram_digest = kh.sum(hram_digest)
|
|
|
|
mut k := edwards25519.new_scalar()
|
|
|
|
k.set_uniform_bytes(hram_digest) ?
|
|
|
|
|
|
|
|
mut ss := edwards25519.new_scalar()
|
|
|
|
ss.multiply_add(k, s, r)
|
|
|
|
|
2022-03-09 19:26:00 +01:00
|
|
|
copy(mut signature[..32], rr.bytes())
|
|
|
|
copy(mut signature[32..], ss.bytes())
|
2022-02-15 20:28:14 +01:00
|
|
|
}
|
|
|
|
|
2022-02-16 21:56:14 +01:00
|
|
|
// verify reports whether sig is a valid signature of message by publickey.
|
2022-04-15 14:35:35 +02:00
|
|
|
pub fn verify(publickey PublicKey, message []u8, sig []u8) ?bool {
|
2022-02-15 20:28:14 +01:00
|
|
|
if publickey.len != ed25519.public_key_size {
|
|
|
|
return error('ed25519: bad public key length: $publickey.len')
|
|
|
|
}
|
|
|
|
|
|
|
|
if sig.len != ed25519.signature_size || sig[63] & 224 != 0 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
mut aa := edwards25519.Point{}
|
|
|
|
aa.set_bytes(publickey) ?
|
|
|
|
|
|
|
|
mut kh := sha512.new()
|
|
|
|
kh.write(sig[..32]) ?
|
|
|
|
kh.write(publickey) ?
|
|
|
|
kh.write(message) ?
|
|
|
|
|
2022-04-15 14:35:35 +02:00
|
|
|
mut hram_digest := []u8{cap: sha512.size}
|
2022-02-15 20:28:14 +01:00
|
|
|
hram_digest = kh.sum(hram_digest)
|
|
|
|
|
|
|
|
mut k := edwards25519.new_scalar()
|
|
|
|
k.set_uniform_bytes(hram_digest) ?
|
|
|
|
|
|
|
|
mut ss := edwards25519.new_scalar()
|
|
|
|
ss.set_canonical_bytes(sig[32..]) ?
|
|
|
|
|
|
|
|
// [S]B = R + [k]A --> [k](-A) + [S]B = R
|
|
|
|
mut minus_a := edwards25519.Point{}
|
|
|
|
minus_a.negate(aa)
|
|
|
|
mut rr := edwards25519.Point{}
|
|
|
|
rr.vartime_double_scalar_base_mult(k, minus_a, ss)
|
|
|
|
|
|
|
|
return subtle.constant_time_compare(sig[..32], rr.bytes()) == 1
|
|
|
|
}
|
|
|
|
|
2022-02-16 21:56:14 +01:00
|
|
|
// generate_key generates a public/private key pair entropy using `crypto.rand`.
|
2022-02-15 20:28:14 +01:00
|
|
|
pub fn generate_key() ?(PublicKey, PrivateKey) {
|
|
|
|
mut seed := rand.bytes(ed25519.seed_size) ?
|
|
|
|
|
|
|
|
privatekey := new_key_from_seed(seed)
|
2022-04-15 14:35:35 +02:00
|
|
|
mut publickey := []u8{len: ed25519.public_key_size}
|
2022-03-09 19:26:00 +01:00
|
|
|
copy(mut publickey, privatekey[32..])
|
2022-02-15 20:28:14 +01:00
|
|
|
|
|
|
|
return publickey, privatekey
|
|
|
|
}
|
|
|
|
|
2022-02-16 21:56:14 +01:00
|
|
|
// new_key_from_seed calculates a private key from a seed. private keys of RFC 8032
|
2022-02-15 20:28:14 +01:00
|
|
|
// correspond to seeds in this module
|
2022-04-15 14:35:35 +02:00
|
|
|
pub fn new_key_from_seed(seed []u8) PrivateKey {
|
2022-02-15 20:28:14 +01:00
|
|
|
// Outline the function body so that the returned key can be stack-allocated.
|
2022-04-15 14:35:35 +02:00
|
|
|
mut privatekey := []u8{len: ed25519.private_key_size}
|
2022-03-09 19:26:00 +01:00
|
|
|
new_key_from_seed_generic(mut privatekey, seed)
|
2022-02-15 20:28:14 +01:00
|
|
|
return PrivateKey(privatekey)
|
|
|
|
}
|
|
|
|
|
2022-04-15 14:35:35 +02:00
|
|
|
fn new_key_from_seed_generic(mut privatekey []u8, seed []u8) {
|
2022-02-15 20:28:14 +01:00
|
|
|
if seed.len != ed25519.seed_size {
|
|
|
|
panic('ed25519: bad seed length: $seed.len')
|
|
|
|
}
|
|
|
|
|
|
|
|
mut h := sha512.sum512(seed)
|
|
|
|
mut s := edwards25519.new_scalar()
|
2022-04-12 12:38:40 +02:00
|
|
|
s.set_bytes_with_clamping(h[..32]) or { panic(err) }
|
2022-02-15 20:28:14 +01:00
|
|
|
mut aa := edwards25519.Point{}
|
|
|
|
aa.scalar_base_mult(mut s)
|
|
|
|
|
|
|
|
mut publickey := aa.bytes()
|
|
|
|
|
2022-03-09 19:26:00 +01:00
|
|
|
copy(mut privatekey, seed)
|
|
|
|
copy(mut privatekey[32..], publickey)
|
2022-02-15 20:28:14 +01:00
|
|
|
}
|