Skip to main content

thru_base/
tn_signature.rs

1use 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}