1use ibig::UBig;
2use iris_ztd::{
3 crypto::cheetah::{
4 ch_add, ch_neg, ch_scal_big, trunc_g_order, CheetahPoint, F6lt, A_GEN, G_ORDER,
5 },
6 tip5::hash::hash_varlen,
7 Belt, Digest, Hashable, Noun, NounDecode, NounEncode,
8};
9use iris_ztd_derive::{NounDecode, NounEncode};
10extern crate alloc;
11
12#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, NounEncode, NounDecode)]
13pub struct PublicKey(pub CheetahPoint);
14
15impl PublicKey {
16 pub fn verify(&self, m: &Digest, sig: &Signature) -> bool {
17 if sig.c == UBig::from(0u64)
18 || sig.c >= *G_ORDER
19 || sig.s == UBig::from(0u64)
20 || sig.s >= *G_ORDER
21 {
22 return false;
23 }
24
25 let sg = match ch_scal_big(&sig.s, &A_GEN) {
28 Ok(pt) => pt,
29 Err(_) => return false,
30 };
31 let c_pk = match ch_scal_big(&sig.c, &self.0) {
32 Ok(pt) => pt,
33 Err(_) => return false,
34 };
35 let scalar = match ch_add(&sg, &ch_neg(&c_pk)) {
36 Ok(pt) => pt,
37 Err(_) => return false,
38 };
39 let chal = {
40 let mut transcript: Vec<Belt> = Vec::new();
41 transcript.extend_from_slice(&scalar.x.0);
42 transcript.extend_from_slice(&scalar.y.0);
43 transcript.extend_from_slice(&self.0.x.0);
44 transcript.extend_from_slice(&self.0.y.0);
45 transcript.extend_from_slice(&m.0);
46 trunc_g_order(&hash_varlen(&mut transcript))
47 };
48
49 chal == sig.c
50 }
51
52 pub fn to_be_bytes(&self) -> [u8; 97] {
53 let mut data = [0u8; 97];
54 data[0] = 0x01; let mut offset = 1;
56 for belt in self.0.y.0.iter().rev() {
58 data[offset..offset + 8].copy_from_slice(&belt.0.to_be_bytes());
59 offset += 8;
60 }
61 for belt in self.0.x.0.iter().rev() {
63 data[offset..offset + 8].copy_from_slice(&belt.0.to_be_bytes());
64 offset += 8;
65 }
66 data
67 }
68
69 pub fn from_be_bytes(bytes: &[u8]) -> PublicKey {
70 let mut x = [Belt(0); 6];
71 let mut y = [Belt(0); 6];
72
73 for i in 0..6 {
75 let offset = 1 + i * 8;
76 let mut buf = [0u8; 8];
77 buf.copy_from_slice(&bytes[offset..offset + 8]);
78 y[5 - i] = Belt(u64::from_be_bytes(buf));
79 }
80
81 for i in 0..6 {
83 let offset = 49 + i * 8;
84 let mut buf = [0u8; 8];
85 buf.copy_from_slice(&bytes[offset..offset + 8]);
86 x[5 - i] = Belt(u64::from_be_bytes(buf));
87 }
88
89 PublicKey(CheetahPoint {
90 x: F6lt(x),
91 y: F6lt(y),
92 inf: false,
93 })
94 }
95
96 pub(crate) fn to_slip10_bytes(&self) -> Vec<u8> {
98 let mut data = Vec::new();
99 for belt in self.0.y.0.iter().rev().chain(self.0.x.0.iter().rev()) {
100 data.extend_from_slice(&belt.0.to_be_bytes());
101 }
102 data
103 }
104}
105
106impl Hashable for PublicKey {
107 fn hash(&self) -> Digest {
108 self.to_noun().hash()
109 }
110}
111
112#[derive(Debug, Clone)]
113pub struct Signature {
114 pub c: UBig, pub s: UBig, }
117
118impl NounEncode for Signature {
119 fn to_noun(&self) -> Noun {
120 (
121 Belt::from_bytes(&self.c.to_le_bytes()).as_slice(),
122 Belt::from_bytes(&self.s.to_le_bytes()).as_slice(),
123 )
124 .to_noun()
125 }
126}
127
128impl NounDecode for Signature {
129 fn from_noun(noun: &Noun) -> Option<Self> {
130 let (c, s): (Vec<Belt>, Vec<Belt>) = NounDecode::from_noun(noun)?;
131
132 let c = Belt::to_bytes(&c);
133 let s = Belt::to_bytes(&s);
134
135 Some(Signature {
136 c: UBig::from_le_bytes(&c),
137 s: UBig::from_le_bytes(&s),
138 })
139 }
140}
141
142impl Hashable for Signature {
143 fn hash(&self) -> Digest {
144 self.to_noun().hash()
145 }
146}
147
148#[derive(Debug, Clone)]
149pub struct PrivateKey(pub UBig);
150
151impl PrivateKey {
152 pub fn public_key(&self) -> PublicKey {
153 PublicKey(ch_scal_big(&self.0, &A_GEN).unwrap())
154 }
155
156 pub fn sign(&self, m: &Digest) -> Signature {
157 let pubkey = self.public_key().0;
158 let nonce = {
159 let mut transcript = Vec::new();
160 transcript.extend_from_slice(&pubkey.x.0);
161 transcript.extend_from_slice(&pubkey.y.0);
162 transcript.extend_from_slice(&m.0);
163 self.0.to_le_bytes().chunks(4).for_each(|chunk| {
164 let mut buf = [0u8; 4];
165 buf[..chunk.len()].copy_from_slice(chunk);
166 transcript.push(Belt(u32::from_le_bytes(buf) as u64));
167 });
168 trunc_g_order(&hash_varlen(&mut transcript))
169 };
170 let chal = {
171 let scalar = ch_scal_big(&nonce, &A_GEN).unwrap();
173 let mut transcript = Vec::new();
174 transcript.extend_from_slice(&scalar.x.0);
175 transcript.extend_from_slice(&scalar.y.0);
176 transcript.extend_from_slice(&pubkey.x.0);
177 transcript.extend_from_slice(&pubkey.y.0);
178 transcript.extend_from_slice(&m.0);
179 trunc_g_order(&hash_varlen(&mut transcript))
180 };
181 let sig = (&nonce + &chal * &self.0) % &*G_ORDER;
182 Signature { c: chal, s: sig }
183 }
184
185 pub fn to_be_bytes(&self) -> [u8; 32] {
186 let bytes = self.0.to_be_bytes();
187 let mut arr = [0u8; 32];
188 arr[32 - bytes.len()..].copy_from_slice(&bytes);
189 arr
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196
197 #[test]
198 fn test_sign_and_verify() {
199 let priv_key = PrivateKey(UBig::from(123u64));
200 let digest = Digest([Belt(1), Belt(2), Belt(3), Belt(4), Belt(5)]);
201 let signature = priv_key.sign(&digest);
202 let pubkey = priv_key.public_key();
203 assert!(
204 pubkey.verify(&digest, &signature),
205 "Signature verification failed!"
206 );
207
208 let mut wrong_digest = digest;
210 wrong_digest.0[0] = Belt(0);
211 assert!(
212 !pubkey.verify(&wrong_digest, &signature),
213 "Should reject wrong digest"
214 );
215 let mut wrong_sig = signature.clone();
216 wrong_sig.s += UBig::from(1u64);
217 assert!(
218 !pubkey.verify(&digest, &wrong_sig),
219 "Should reject wrong signature"
220 );
221 let mut wrong_pubkey = pubkey.clone();
222 wrong_pubkey.0.x.0[0].0 += 1;
223 assert!(
224 !wrong_pubkey.verify(&digest, &signature),
225 "Should reject wrong public key"
226 );
227 }
228
229 #[test]
230 fn test_vector() {
231 let digest = Digest([Belt(8), Belt(9), Belt(10), Belt(11), Belt(12)]);
233 let pubkey = PublicKey(CheetahPoint {
234 x: F6lt([
235 Belt(2754611494552410273),
236 Belt(8599518745794843693),
237 Belt(10526511002404673680),
238 Belt(4830863958577994148),
239 Belt(375185138577093320),
240 Belt(12938930721685970739),
241 ]),
242 y: F6lt([
243 Belt(3062714866612034253),
244 Belt(15671931273416742386),
245 Belt(4071440668668521568),
246 Belt(7738250649524482367),
247 Belt(5259065445844042557),
248 Belt(8456011930642078370),
249 ]),
250 inf: false,
251 });
252 let c_hex = "6f3cd43cd8709f4368aed04cd84292ab1c380cb645aaa7d010669d70375cbe88";
253 let s_hex = "5197ab182e307a350b5cf3606d6e99a6f35b0d382c8330dde6e51fb6ef8ebb8c";
254 let signature = Signature {
255 c: UBig::from_str_radix(c_hex, 16).unwrap(),
256 s: UBig::from_str_radix(s_hex, 16).unwrap(),
257 };
258 assert!(pubkey.verify(&digest, &signature));
259 }
260}