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
use pea_core::*;
use secp256k1::{
ecdsa::{RecoverableSignature, RecoveryId},
Message, PublicKey, SecretKey, SECP256K1,
};
use sha2::{Digest, Sha256};
use std::error::Error;
#[cfg(feature = "vrf")]
use vrf::{
openssl::{CipherSuite, ECVRF},
VRF,
};
#[derive(Debug)]
pub struct Key {
pub secret_key: SecretKey,
}
impl Key {
pub fn generate() -> Key {
Key {
secret_key: SecretKey::new(&mut rand::thread_rng()),
}
}
pub fn from_slice(secret_key_bytes: &SecretKeyBytes) -> Key {
Key {
secret_key: SecretKey::from_slice(secret_key_bytes).expect("32 bytes, within curve order"),
}
}
pub fn secret_key_bytes(&self) -> SecretKeyBytes {
self.secret_key.secret_bytes()
}
pub fn public_key(&self) -> PublicKey {
self.secret_key.public_key(SECP256K1)
}
pub fn public_key_bytes(&self) -> PublicKeyBytes {
self.public_key().serialize()
}
pub fn address_bytes(&self) -> AddressBytes {
Key::address(&self.public_key_bytes())
}
pub fn address(public_key_bytes: &PublicKeyBytes) -> AddressBytes {
let mut hasher = Sha256::new();
hasher.update(public_key_bytes);
let hash = hasher.finalize();
let mut address = [0; 20];
address.copy_from_slice(&hash[..20]);
address
}
pub fn sign(&self, hash: &Hash) -> Result<SignatureBytes, Box<dyn Error>> {
let message = Message::from_slice(hash)?;
Ok(loop {
let signature = SECP256K1.sign_ecdsa_recoverable_with_noncedata(&message, &self.secret_key, &rand::random());
let (recovery_id, signature_bytes) = signature.serialize_compact();
if recovery_id.to_i32() == RECOVERY_ID {
break signature_bytes;
}
})
}
pub fn recover(hash: &Hash, signature_bytes: &SignatureBytes) -> Result<PublicKeyBytes, Box<dyn Error>> {
let message = Message::from_slice(hash)?;
let signature = RecoverableSignature::from_compact(signature_bytes, RecoveryId::from_i32(RECOVERY_ID).unwrap())?;
let public_key_bytes: PublicKeyBytes = SECP256K1.recover_ecdsa(&message, &signature)?.serialize();
Ok(public_key_bytes)
}
#[cfg(feature = "vrf")]
pub fn vrf_prove(&self, alpha: &[u8]) -> Option<Pi> {
let mut vrf = ECVRF::from_suite(CipherSuite::SECP256K1_SHA256_TAI).unwrap();
let pi = vrf.prove(&self.secret_key_bytes(), alpha);
if let Err(_) = pi {
return None;
}
Some(pi.unwrap().try_into().unwrap())
}
#[cfg(feature = "vrf")]
pub fn vrf_proof_to_hash(pi: &[u8]) -> Option<Beta> {
let mut vrf = ECVRF::from_suite(CipherSuite::SECP256K1_SHA256_TAI).unwrap();
let beta = vrf.proof_to_hash(pi);
if let Err(_) = beta {
return None;
}
Some(beta.unwrap().try_into().unwrap())
}
#[cfg(feature = "vrf")]
pub fn vrf_verify(y: &[u8], pi: &[u8], alpha: &[u8]) -> Option<Beta> {
let mut vrf = ECVRF::from_suite(CipherSuite::SECP256K1_SHA256_TAI).unwrap();
let beta = vrf.verify(y, pi, alpha);
if let Err(_) = beta {
return None;
}
Some(beta.unwrap().try_into().unwrap())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_address() {
assert_eq!(
Key::address(&[0; 33]),
[127, 156, 158, 49, 172, 130, 86, 202, 47, 37, 133, 131, 223, 38, 45, 188, 125, 111, 104, 242]
);
}
#[test]
fn test_sign_verify() {
let key = Key::generate();
let hash = [0; 32];
let signature_bytes = key.sign(&hash).unwrap();
assert_eq!(key.public_key_bytes(), Key::recover(&hash, &signature_bytes).unwrap());
}
#[test]
#[cfg(feature = "vrf")]
fn test_vrf_public_key() {
let mut vrf = ECVRF::from_suite(CipherSuite::SECP256K1_SHA256_TAI).unwrap();
let key = Key::generate();
let public_key = vrf.derive_public_key(&key.secret_key_bytes()).unwrap();
assert_eq!(key.public_key_bytes().to_vec(), public_key);
}
#[test]
#[cfg(feature = "vrf")]
fn test_vrf_prove_verify() {
let key = Key::generate();
let alpha: [u8; 32] = rand::random();
let pi = key.vrf_prove(&alpha).unwrap();
let beta = Key::vrf_verify(&key.public_key_bytes(), &pi, &alpha);
assert!(beta.unwrap() == Key::vrf_proof_to_hash(&pi).unwrap());
}
}