thru_base/
tn_signature.rs1use curve25519_dalek::{
2 edwards::{CompressedEdwardsY, EdwardsPoint},
3 scalar::Scalar,
4};
5use ed25519_dalek::{hazmat::ExpandedSecretKey, SigningKey, VerifyingKey};
6use sha2::{Digest, Sha512};
7use std::{convert::TryInto, fmt};
8
9const DOMAIN_BLOCK_SIZE: usize = 128;
10
11#[derive(Clone, Copy, Debug)]
12pub enum SignatureDomain {
13 Transaction,
14 BlockHeader,
15 Block,
16 Gossip,
17}
18
19impl SignatureDomain {
20 fn tag(self) -> u64 {
21 match self {
22 SignatureDomain::Transaction => 1,
23 SignatureDomain::BlockHeader => 2,
24 SignatureDomain::Block => 3,
25 SignatureDomain::Gossip => 4,
26 }
27 }
28}
29
30#[derive(Debug)]
31pub enum TnSignatureError {
32 InvalidSignature,
33 InvalidPublicKey,
34 InvalidScalar,
35}
36
37impl fmt::Display for TnSignatureError {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 match self {
40 TnSignatureError::InvalidSignature => write!(f, "invalid signature"),
41 TnSignatureError::InvalidPublicKey => write!(f, "invalid public key"),
42 TnSignatureError::InvalidScalar => write!(f, "invalid scalar"),
43 }
44 }
45}
46
47impl std::error::Error for TnSignatureError {}
48
49fn domain_block(domain: SignatureDomain) -> [u8; DOMAIN_BLOCK_SIZE] {
50 let mut block = [0u8; DOMAIN_BLOCK_SIZE];
51 block[..8].copy_from_slice(&domain.tag().to_be_bytes());
52 block
53}
54
55pub fn sign(
56 domain: SignatureDomain,
57 msg: &[u8],
58 public_key: &[u8; 32],
59 private_key: &[u8; 32],
60) -> Result<[u8; 64], TnSignatureError> {
61 let signing_key = SigningKey::from_bytes(private_key);
62 let expanded: ExpandedSecretKey = signing_key.as_bytes().into();
63 let block = domain_block(domain);
64
65 let mut h_r = Sha512::new();
66 h_r.update(&block);
67 h_r.update(&expanded.hash_prefix);
68 h_r.update(msg);
69 let r = Scalar::from_bytes_mod_order_wide(&h_r.finalize().into());
70 let r_point = EdwardsPoint::mul_base(&r).compress();
71
72 let mut h_k = Sha512::new();
73 h_k.update(&block);
74 h_k.update(r_point.as_bytes());
75 h_k.update(public_key);
76 h_k.update(msg);
77 let k = Scalar::from_bytes_mod_order_wide(&h_k.finalize().into());
78
79 let s = k * expanded.scalar + r;
80
81 let mut sig = [0u8; 64];
82 sig[..32].copy_from_slice(r_point.as_bytes());
83 sig[32..].copy_from_slice(&s.to_bytes());
84 Ok(sig)
85}
86
87pub fn verify(
88 domain: SignatureDomain,
89 msg: &[u8],
90 sig: &[u8; 64],
91 public_key: &[u8; 32],
92) -> Result<(), TnSignatureError> {
93 let r_bytes: [u8; 32] = sig[..32].try_into().map_err(|_| TnSignatureError::InvalidSignature)?;
94 let s_bytes: [u8; 32] = sig[32..]
95 .try_into()
96 .map_err(|_| TnSignatureError::InvalidSignature)?;
97
98 let r_point = CompressedEdwardsY(r_bytes)
99 .decompress()
100 .ok_or(TnSignatureError::InvalidSignature)?;
101 if r_point.is_small_order() {
102 return Err(TnSignatureError::InvalidSignature);
103 }
104
105 let s_scalar = Option::<Scalar>::from(Scalar::from_canonical_bytes(s_bytes))
106 .ok_or(TnSignatureError::InvalidScalar)?;
107
108 let verifying_key =
109 VerifyingKey::from_bytes(public_key).map_err(|_| TnSignatureError::InvalidPublicKey)?;
110 let a_point: EdwardsPoint = verifying_key.into();
111 if a_point.is_small_order() {
112 return Err(TnSignatureError::InvalidPublicKey);
113 }
114
115 let block = domain_block(domain);
116 let mut h_k = Sha512::new();
117 h_k.update(&block);
118 h_k.update(&r_bytes);
119 h_k.update(public_key);
120 h_k.update(msg);
121 let k = Scalar::from_bytes_mod_order_wide(&h_k.finalize().into());
122
123 let minus_a = -a_point;
124 let r_cmp =
125 EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &minus_a, &s_scalar).compress();
126
127 if r_cmp.as_bytes() == &r_bytes {
128 return Ok(());
129 }
130 Err(TnSignatureError::InvalidSignature)
131}
132
133pub fn sign_transaction(
134 msg: &[u8],
135 public_key: &[u8; 32],
136 private_key: &[u8; 32],
137) -> Result<[u8; 64], TnSignatureError> {
138 sign(SignatureDomain::Transaction, msg, public_key, private_key)
139}
140
141pub fn verify_transaction(
142 msg: &[u8],
143 sig: &[u8; 64],
144 public_key: &[u8; 32],
145) -> Result<(), TnSignatureError> {
146 verify(SignatureDomain::Transaction, msg, sig, public_key)
147}