1use crate::ec::basis::{
15 difference_point, difference_point_with_hint, ec_curve_to_basis_2f_to_hint,
16};
17use crate::ec::pairing::{fp2_dlog_2e_pub, weil};
18use crate::ec::point::{ec_dbl_iter_basis, xadd};
19use crate::ec::{EcBasis, EcPoint};
20use crate::fp::{Fp2, FpBackend};
21use crate::params::{Level1, SecurityLevel};
22use crate::precomp::LevelPrecomp;
23use crate::theta::HD_EXTRA_TORSION;
24use hybrid_array::typenum::Unsigned;
25use hybrid_array::Array;
26
27use crate::hash::hash_to_challenge;
28use crate::types::{
29 decode_digits, encode_digits, fmt_fp2, fmt_hex, fmt_scalar, PublicKey, Scalar, Signature,
30};
31use crate::verify::{
32 basis_from_hint, check_canonical_basis_change_matrix, compute_challenge_curve,
33 compute_commitment_curve_verify, matrix_scalar_application_even_basis, mp_compare, mp_is_even,
34 protocols_verify, two_response_isogeny_verify_inner,
35};
36use crate::Error;
37
38#[derive(Clone, Copy, Debug, PartialEq, Eq)]
43pub enum SignatureFormat {
44 Expanded,
45 Standard,
46 Compressed,
47}
48
49#[derive(Clone)]
75pub struct ExpandedSignature<L: SecurityLevel = Level1> {
76 pub(crate) e_aux_a: Fp2<L>,
78 pub(crate) backtracking: u8,
82 pub(crate) two_resp_length: u8,
84 pub(crate) chall_coeff: Scalar<L>,
86 pub(crate) p_chl_x: Fp2<L>,
88 pub(crate) q_chl_x: Fp2<L>,
90 pub(crate) kernel_is_q: bool,
93 pub(crate) pmq_sign_hint: bool,
96 pub(crate) hint_aux: u8,
98 pub(crate) hint_chall: u8,
100}
101
102impl<L: FpBackend> core::fmt::Debug for ExpandedSignature<L> {
103 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
104 f.write_str("ExpandedSignature { e_aux_a: ")?;
105 fmt_fp2(f, &self.e_aux_a)?;
106 write!(
107 f,
108 ", bt: {}, trl: {}, chall: ",
109 self.backtracking, self.two_resp_length
110 )?;
111 fmt_scalar(f, &self.chall_coeff)?;
112 f.write_str(", p_chl_x: ")?;
113 fmt_fp2(f, &self.p_chl_x)?;
114 f.write_str(", q_chl_x: ")?;
115 fmt_fp2(f, &self.q_chl_x)?;
116 write!(
117 f,
118 ", kernel_is_q: {}, pmq_sign_hint: {}, hint_aux: 0x{:02x}, hint_chall: 0x{:02x} }}",
119 self.kernel_is_q, self.pmq_sign_hint, self.hint_aux, self.hint_chall
120 )
121 }
122}
123
124impl<L: FpBackend> ExpandedSignature<L> {
125 pub const WIRE_BYTES: usize = <L as SecurityLevel>::Fp2EncodedBytes::USIZE * 3
133 + <L as SecurityLevel>::LAMBDA as usize / 8
134 + 4;
135
136 fn chall_coeff_bytes() -> usize {
137 L::LAMBDA as usize / 8
138 }
139
140 pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
142 if bytes.len() != Self::WIRE_BYTES {
143 return Err(Error::InvalidLength);
144 }
145
146 let fp2_len = <L as SecurityLevel>::Fp2EncodedBytes::USIZE;
147 let mut pos = 0;
148
149 let e_aux_a = Fp2::<L>::decode(&bytes[pos..pos + fp2_len]).ok_or(Error::MalformedInput)?;
150 pos += fp2_len;
151
152 let bt_byte = bytes[pos];
153 pos += 1;
154 let kernel_is_q = (bt_byte & 0x80) != 0;
155 let pmq_sign_hint = (bt_byte & 0x40) != 0;
156 let backtracking = bt_byte & 0x3F;
157
158 let two_resp_length = bytes[pos];
159 pos += 1;
160
161 let chall_bytes = Self::chall_coeff_bytes();
162 let mut chall_coeff = Scalar::<L>::default();
163 decode_digits(
164 chall_coeff.digits.as_mut_slice(),
165 &bytes[pos..],
166 chall_bytes,
167 );
168 pos += chall_bytes;
169
170 let p_chl_x = Fp2::<L>::decode(&bytes[pos..pos + fp2_len]).ok_or(Error::MalformedInput)?;
171 pos += fp2_len;
172
173 let q_chl_x = Fp2::<L>::decode(&bytes[pos..pos + fp2_len]).ok_or(Error::MalformedInput)?;
174 pos += fp2_len;
175
176 let hint_aux = bytes[pos];
177 pos += 1;
178 let hint_chall = bytes[pos];
179 pos += 1;
180 debug_assert_eq!(pos, Self::WIRE_BYTES);
181
182 Ok(Self {
183 e_aux_a,
184 backtracking,
185 two_resp_length,
186 chall_coeff,
187 p_chl_x,
188 q_chl_x,
189 kernel_is_q,
190 pmq_sign_hint,
191 hint_aux,
192 hint_chall,
193 })
194 }
195
196 pub fn to_bytes(&self) -> Array<u8, L::ExpandedSigLen> {
198 let mut buf = Array::<u8, L::ExpandedSigLen>::default();
199 let fp2_len = <L as SecurityLevel>::Fp2EncodedBytes::USIZE;
200 let mut pos = 0;
201
202 let enc = self.e_aux_a.encode();
203 buf[pos..pos + fp2_len].copy_from_slice(&enc);
204 pos += fp2_len;
205
206 buf[pos] = self.backtracking
207 | if self.kernel_is_q { 0x80 } else { 0 }
208 | if self.pmq_sign_hint { 0x40 } else { 0 };
209 pos += 1;
210 buf[pos] = self.two_resp_length;
211 pos += 1;
212
213 let chall_bytes = Self::chall_coeff_bytes();
214 encode_digits(
215 &mut buf[pos..],
216 self.chall_coeff.digits.as_slice(),
217 chall_bytes,
218 );
219 pos += chall_bytes;
220
221 let enc = self.p_chl_x.encode();
222 buf[pos..pos + fp2_len].copy_from_slice(&enc);
223 pos += fp2_len;
224
225 let enc = self.q_chl_x.encode();
226 buf[pos..pos + fp2_len].copy_from_slice(&enc);
227 pos += fp2_len;
228
229 buf[pos] = self.hint_aux;
230 pos += 1;
231 buf[pos] = self.hint_chall;
232 pos += 1;
233 debug_assert_eq!(pos, Self::WIRE_BYTES);
234
235 buf
236 }
237}
238
239impl<L: FpBackend> TryFrom<&[u8]> for ExpandedSignature<L> {
240 type Error = Error;
241
242 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
243 Self::from_bytes(bytes)
244 }
245}
246
247impl<L: FpBackend> From<ExpandedSignature<L>> for Array<u8, L::ExpandedSigLen> {
248 fn from(sig: ExpandedSignature<L>) -> Self {
249 sig.to_bytes()
250 }
251}
252
253impl<L: FpBackend> signature::SignatureEncoding for ExpandedSignature<L>
254where
255 Array<u8, L::ExpandedSigLen>: Send + Sync,
256{
257 type Repr = Array<u8, L::ExpandedSigLen>;
258}
259
260impl<L: FpBackend + LevelPrecomp> signature::Verifier<ExpandedSignature<L>> for PublicKey<L> {
261 fn verify(&self, msg: &[u8], sig: &ExpandedSignature<L>) -> Result<(), signature::Error> {
262 verify_expanded(self, msg, sig).map_err(|_| signature::Error::new())
263 }
264}
265
266impl<L: FpBackend> core::fmt::Display for ExpandedSignature<L> {
267 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
268 fmt_hex(f, &self.to_bytes())
269 }
270}
271
272fn basis_to_hint<L: FpBackend + LevelPrecomp>(
274 curve: &mut crate::ec::EcCurve<L>,
275 f: u32,
276) -> Result<(EcBasis<L>, u8), Error> {
277 ec_curve_to_basis_2f_to_hint(
278 curve,
279 f,
280 L::basis_e0_px_bytes(),
281 L::basis_e0_qx_bytes(),
282 L::p_cofactor_for_2f(),
283 L::p_cofactor_for_2f_bitlength() as usize,
284 L::torsion_even_power(),
285 )
286 .map_err(|()| Error::MalformedInput)
287}
288
289fn mp_mul_mod(out: &mut [u64], a: &[u64], b: &[u64], mod_bits: usize) {
291 let n = out.len();
292 out.fill(0);
293 for i in 0..a.len().min(n) {
294 let mut carry: u64 = 0;
295 for j in 0..b.len().min(n - i) {
296 let prod = (a[i] as u128) * (b[j] as u128) + (out[i + j] as u128) + (carry as u128);
297 out[i + j] = prod as u64;
298 carry = (prod >> 64) as u64;
299 }
300 }
301 mp_mod_2exp(out, mod_bits);
302}
303
304fn mp_shiftr(a: &mut [u64], shift: usize) {
306 let n = a.len();
307 let word_shift = shift / 64;
308 let bit_shift = shift % 64;
309
310 if word_shift >= n {
311 a.fill(0);
312 return;
313 }
314
315 if bit_shift == 0 {
316 for i in 0..n - word_shift {
317 a[i] = a[i + word_shift];
318 }
319 } else {
320 for i in 0..n - word_shift - 1 {
321 a[i] = (a[i + word_shift] >> bit_shift) | (a[i + word_shift + 1] << (64 - bit_shift));
322 }
323 a[n - word_shift - 1] = a[n - 1] >> bit_shift;
324 }
325 a[n - word_shift..n].fill(0);
326}
327
328fn mp_mod_2exp(a: &mut [u64], e: usize) {
330 let q = e / 64;
331 let r = e % 64;
332 if q < a.len() {
333 if r != 0 {
334 a[q] &= (1u64 << r) - 1;
335 } else {
336 a[q] = 0;
337 }
338 a[q + 1..].fill(0);
339 }
340}
341
342fn hensel_inv_mod_2e(out: &mut [u64], a: &[u64], e: usize) {
345 let n = out.len();
346 out.fill(0);
347 out[0] = 1;
348
349 let mut ax = [0u64; 8];
350 let mut factor = [0u64; 8];
351 let mut x_copy = [0u64; 8];
352
353 let mut prec = 1usize;
354 while prec < e {
355 let next = (prec * 2).min(e);
356
357 ax[..n].fill(0);
358 for i in 0..n {
359 let mut carry: u64 = 0;
360 for j in 0..n.min(n - i) {
361 if i + j >= n {
362 break;
363 }
364 let prod =
365 (a[i] as u128) * (out[j] as u128) + (ax[i + j] as u128) + (carry as u128);
366 ax[i + j] = prod as u64;
367 carry = (prod >> 64) as u64;
368 }
369 }
370 mp_mod_2exp(&mut ax[..n], next);
371
372 let mut carry = 3u128;
374 for i in 0..n {
375 let val = (!ax[i]) as u128 + carry;
376 factor[i] = val as u64;
377 carry = val >> 64;
378 }
379 mp_mod_2exp(&mut factor[..n], next);
380
381 x_copy[..n].copy_from_slice(&out[..n]);
382 out.fill(0);
383 for i in 0..n {
384 let mut carry_mul: u64 = 0;
385 for j in 0..n.min(n - i) {
386 if i + j >= n {
387 break;
388 }
389 let prod = (x_copy[i] as u128) * (factor[j] as u128)
390 + (out[i + j] as u128)
391 + (carry_mul as u128);
392 out[i + j] = prod as u64;
393 carry_mul = (prod >> 64) as u64;
394 }
395 }
396 mp_mod_2exp(out, next);
397
398 prec = next;
399 }
400}
401
402fn set_high_bits(digits: &mut [u64], hint: u8, start_bit: usize, n_bits: usize) {
404 for b in 0..n_bits {
405 if hint & (1u8 << b) != 0 {
406 let pos = start_bit + b;
407 let limb = pos / 64;
408 let bit = pos % 64;
409 if limb < digits.len() {
410 digits[limb] |= 1u64 << bit;
411 }
412 }
413 }
414}
415
416#[derive(Clone)]
453pub struct CompressedSignature<L: SecurityLevel = Level1> {
454 pub(crate) e_aux_a: Fp2<L>,
456 pub(crate) backtracking: u8,
458 pub(crate) two_resp_length: u8,
460 pub(crate) mat_00: Scalar<L>,
462 pub(crate) mat_01: Scalar<L>,
464 pub(crate) mat_var: Scalar<L>,
466 pub(crate) chall_coeff: Scalar<L>,
468 pub(crate) det_hint: u8,
471}
472
473impl<L: FpBackend> core::fmt::Debug for CompressedSignature<L> {
474 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
475 f.write_str("CompressedSignature { e_aux_a: ")?;
476 fmt_fp2(f, &self.e_aux_a)?;
477 write!(
478 f,
479 ", bt: {}, trl: {}, mat_00: ",
480 self.backtracking, self.two_resp_length
481 )?;
482 fmt_scalar(f, &self.mat_00)?;
483 f.write_str(", mat_01: ")?;
484 fmt_scalar(f, &self.mat_01)?;
485 f.write_str(", mat_var: ")?;
486 fmt_scalar(f, &self.mat_var)?;
487 f.write_str(", chall: ")?;
488 fmt_scalar(f, &self.chall_coeff)?;
489 write!(f, ", det_hint: 0x{:02x} }}", self.det_hint)
490 }
491}
492
493impl<L: SecurityLevel> CompressedSignature<L> {
494 fn pack_meta(&self) -> u8 {
499 (self.backtracking & 0x03) | ((self.det_hint & 0x03) << 2) | (self.two_resp_length << 4)
500 }
501
502 fn unpack_meta(packed: u8) -> (u8, u8, u8) {
504 let backtracking = packed & 0x03;
505 let det_hint = (packed >> 2) & 0x03;
506 let two_resp_length = (packed >> 4) & 0x0F;
507 (backtracking, det_hint, two_resp_length)
508 }
509}
510
511impl<L: FpBackend> CompressedSignature<L> {
512 pub const WIRE_BYTES: usize = <L as SecurityLevel>::Fp2EncodedBytes::USIZE
521 + 3 * ((<L as SecurityLevel>::E_RSP as usize + 9) / 8)
522 + <L as SecurityLevel>::LAMBDA as usize / 8
523 + 1;
524
525 fn matrix_entry_bytes() -> usize {
526 (L::E_RSP as usize + 9) / 8
527 }
528
529 fn chall_coeff_bytes() -> usize {
530 L::LAMBDA as usize / 8
531 }
532
533 pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
535 if bytes.len() != Self::WIRE_BYTES {
536 return Err(Error::InvalidLength);
537 }
538
539 let fp2_len = <L as SecurityLevel>::Fp2EncodedBytes::USIZE;
540 let mat_bytes = Self::matrix_entry_bytes();
541 let chall_bytes = Self::chall_coeff_bytes();
542 let mut pos = 0;
543
544 let e_aux_a = Fp2::<L>::decode(&bytes[pos..pos + fp2_len]).ok_or(Error::MalformedInput)?;
545 pos += fp2_len;
546
547 let (backtracking, det_hint, two_resp_length) = Self::unpack_meta(bytes[pos]);
548 pos += 1;
549
550 let mut mat_00 = Scalar::<L>::default();
551 decode_digits(mat_00.digits.as_mut_slice(), &bytes[pos..], mat_bytes);
552 pos += mat_bytes;
553
554 let mut mat_01 = Scalar::<L>::default();
555 decode_digits(mat_01.digits.as_mut_slice(), &bytes[pos..], mat_bytes);
556 pos += mat_bytes;
557
558 let mut mat_var = Scalar::<L>::default();
559 decode_digits(mat_var.digits.as_mut_slice(), &bytes[pos..], mat_bytes);
560 pos += mat_bytes;
561
562 let mut chall_coeff = Scalar::<L>::default();
563 decode_digits(
564 chall_coeff.digits.as_mut_slice(),
565 &bytes[pos..],
566 chall_bytes,
567 );
568 pos += chall_bytes;
569
570 debug_assert_eq!(pos, Self::WIRE_BYTES);
571
572 Ok(Self {
573 e_aux_a,
574 backtracking,
575 two_resp_length,
576 mat_00,
577 mat_01,
578 mat_var,
579 chall_coeff,
580 det_hint,
581 })
582 }
583
584 pub fn to_bytes(&self) -> Array<u8, L::CompressedSigLen> {
586 let mut buf = Array::<u8, L::CompressedSigLen>::default();
587 let fp2_len = <L as SecurityLevel>::Fp2EncodedBytes::USIZE;
588 let mat_bytes = Self::matrix_entry_bytes();
589 let chall_bytes = Self::chall_coeff_bytes();
590 let mut pos = 0;
591
592 let enc = self.e_aux_a.encode();
593 buf[pos..pos + fp2_len].copy_from_slice(&enc);
594 pos += fp2_len;
595
596 buf[pos] = self.pack_meta();
597 pos += 1;
598
599 encode_digits(&mut buf[pos..], self.mat_00.digits.as_slice(), mat_bytes);
600 pos += mat_bytes;
601 encode_digits(&mut buf[pos..], self.mat_01.digits.as_slice(), mat_bytes);
602 pos += mat_bytes;
603 encode_digits(&mut buf[pos..], self.mat_var.digits.as_slice(), mat_bytes);
604 pos += mat_bytes;
605
606 encode_digits(
607 &mut buf[pos..],
608 self.chall_coeff.digits.as_slice(),
609 chall_bytes,
610 );
611 pos += chall_bytes;
612
613 debug_assert_eq!(pos, Self::WIRE_BYTES);
614
615 buf
616 }
617
618 pub fn decompress(&self, pk: &PublicKey<L>) -> Result<Signature<L>, Error>
623 where
624 L: LevelPrecomp,
625 {
626 let pow_dim2 = L::E_RSP as i32 - self.two_resp_length as i32 - self.backtracking as i32;
627 if pow_dim2 <= 1 {
629 return Err(Error::InvalidSignature);
630 }
631 let pow_dim2 = pow_dim2 as usize;
632 let trl = self.two_resp_length as usize;
633 let det_precision = pow_dim2 + trl;
634 let f = det_precision + HD_EXTRA_TORSION as usize;
635 let g = pow_dim2 + HD_EXTRA_TORSION as usize;
636 let nw = L::MpLimbs::USIZE;
637
638 let m00_odd = self.mat_00.digits[0] & 1 != 0;
639
640 let pivot = if m00_odd {
641 &self.mat_00
642 } else {
643 if self.mat_01.digits[0] & 1 == 0 {
644 return Err(Error::InvalidSignature);
645 }
646 &self.mat_01
647 };
648
649 let mut e_chall =
653 compute_challenge_curve(&self.chall_coeff, self.backtracking, &pk.curve, pk.hint_pk)
654 .ok_or(Error::InvalidSignature)?;
655 let (b_chall, hint_chall) = basis_to_hint(&mut e_chall, L::F_CHR)?;
656 let b_chall = ec_dbl_iter_basis(&b_chall, L::F_CHR as usize - f, &mut e_chall);
657 let ppq_chall = xadd(&b_chall.p, &b_chall.q, &b_chall.pmq);
658 let omega_f = weil::<L>(f as u32, &b_chall.p, &b_chall.q, &ppq_chall, &mut e_chall);
659
660 let mut e_aux =
661 crate::ec::EcCurve::<L>::from_a(&self.e_aux_a).ok_or(Error::InvalidSignature)?;
662 let (b_aux, hint_aux) = basis_to_hint(&mut e_aux, L::F_CHR)?;
663 let b_aux = ec_dbl_iter_basis(&b_aux, L::F_CHR as usize - g, &mut e_aux);
664 let ppq_aux = xadd(&b_aux.p, &b_aux.q, &b_aux.pmq);
665 let omega_aux = weil::<L>(g as u32, &b_aux.p, &b_aux.q, &ppq_aux, &mut e_aux);
666
667 let omega_f_inv = omega_f.inv();
668 let omega_aux_inv = omega_aux.inv();
669 let mut det_digits = [0u64; 8];
670 fp2_dlog_2e_pub::<L>(
671 &mut det_digits[..nw],
672 &omega_aux_inv,
673 &omega_f_inv,
674 f as u32,
675 )
676 .ok_or(Error::InvalidSignature)?;
677 mp_mod_2exp(&mut det_digits[..nw], det_precision);
678
679 let mut inv_pivot = Scalar::<L>::default();
682 hensel_inv_mod_2e(
683 inv_pivot.digits.as_mut_slice(),
684 pivot.digits.as_slice(),
685 det_precision,
686 );
687
688 let (mat_10, mat_11) = if m00_odd {
689 let mut product = Scalar::<L>::default();
690 mp_mul_mod(
691 product.digits.as_mut_slice(),
692 self.mat_01.digits.as_slice(),
693 self.mat_var.digits.as_slice(),
694 det_precision,
695 );
696 let mut numerator = Scalar::<L>::default();
697 let mut carry: u64 = 0;
698 for (i, &det_limb) in det_digits.iter().enumerate().take(nw) {
699 let sum = (det_limb as u128) + (product.digits[i] as u128) + (carry as u128);
700 numerator.digits[i] = sum as u64;
701 carry = (sum >> 64) as u64;
702 }
703 mp_mod_2exp(numerator.digits.as_mut_slice(), det_precision);
704
705 let mut recovered = Scalar::<L>::default();
706 mp_mul_mod(
707 recovered.digits.as_mut_slice(),
708 numerator.digits.as_slice(),
709 inv_pivot.digits.as_slice(),
710 det_precision,
711 );
712 set_high_bits(
713 recovered.digits.as_mut_slice(),
714 self.det_hint,
715 det_precision,
716 HD_EXTRA_TORSION as usize,
717 );
718 (self.mat_var.clone(), recovered)
719 } else {
720 let mut product = Scalar::<L>::default();
721 mp_mul_mod(
722 product.digits.as_mut_slice(),
723 self.mat_00.digits.as_slice(),
724 self.mat_var.digits.as_slice(),
725 det_precision,
726 );
727 let mut numerator = Scalar::<L>::default();
728 let mut borrow: u64 = 0;
729 for (i, &det_limb) in det_digits.iter().enumerate().take(nw) {
730 let (diff, b1) = product.digits[i].overflowing_sub(det_limb);
731 let (diff2, b2) = diff.overflowing_sub(borrow);
732 numerator.digits[i] = diff2;
733 borrow = (b1 as u64) + (b2 as u64);
734 }
735 mp_mod_2exp(numerator.digits.as_mut_slice(), det_precision);
736
737 let mut recovered = Scalar::<L>::default();
738 mp_mul_mod(
739 recovered.digits.as_mut_slice(),
740 numerator.digits.as_slice(),
741 inv_pivot.digits.as_slice(),
742 det_precision,
743 );
744 set_high_bits(
745 recovered.digits.as_mut_slice(),
746 self.det_hint,
747 det_precision,
748 HD_EXTRA_TORSION as usize,
749 );
750 (recovered, self.mat_var.clone())
751 };
752
753 Ok(Signature {
754 e_aux_a: self.e_aux_a.clone(),
755 backtracking: self.backtracking,
756 two_resp_length: self.two_resp_length,
757 mat: [[self.mat_00.clone(), self.mat_01.clone()], [mat_10, mat_11]],
758 chall_coeff: self.chall_coeff.clone(),
759 hint_aux,
760 hint_chall,
761 })
762 }
763}
764
765impl<L: FpBackend> TryFrom<&[u8]> for CompressedSignature<L> {
766 type Error = Error;
767
768 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
769 Self::from_bytes(bytes)
770 }
771}
772
773impl<L: FpBackend> From<CompressedSignature<L>> for Array<u8, L::CompressedSigLen> {
774 fn from(sig: CompressedSignature<L>) -> Self {
775 sig.to_bytes()
776 }
777}
778
779impl<L: FpBackend> signature::SignatureEncoding for CompressedSignature<L>
780where
781 Array<u8, L::CompressedSigLen>: Send + Sync,
782{
783 type Repr = Array<u8, L::CompressedSigLen>;
784}
785
786impl<L: FpBackend + LevelPrecomp> signature::Verifier<CompressedSignature<L>> for PublicKey<L> {
787 fn verify(&self, msg: &[u8], sig: &CompressedSignature<L>) -> Result<(), signature::Error> {
788 verify_compressed(self, msg, sig).map_err(|_| signature::Error::new())
789 }
790}
791
792impl<L: FpBackend + LevelPrecomp> signature::Verifier<AnySignature<L>> for PublicKey<L> {
793 fn verify(&self, msg: &[u8], sig: &AnySignature<L>) -> Result<(), signature::Error> {
794 match sig {
795 AnySignature::Standard(s) => crate::verify::protocols_verify(self, msg, s),
796 AnySignature::Expanded(s) => verify_expanded(self, msg, s),
797 AnySignature::Compressed(s) => verify_compressed(self, msg, s),
798 }
799 .map_err(|_| signature::Error::new())
800 }
801}
802
803impl<L: FpBackend> core::fmt::Display for CompressedSignature<L> {
804 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
805 fmt_hex(f, &self.to_bytes())
806 }
807}
808
809#[derive(Clone)]
816pub enum AnySignature<L: SecurityLevel = Level1> {
817 Expanded(ExpandedSignature<L>),
818 Standard(Signature<L>),
819 Compressed(CompressedSignature<L>),
820}
821
822impl<L: FpBackend> core::fmt::Debug for AnySignature<L> {
823 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
824 match self {
825 AnySignature::Expanded(s) => core::fmt::Debug::fmt(s, f),
826 AnySignature::Standard(s) => core::fmt::Debug::fmt(s, f),
827 AnySignature::Compressed(s) => core::fmt::Debug::fmt(s, f),
828 }
829 }
830}
831
832impl<L: FpBackend> AnySignature<L> {
833 pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
835 let len = bytes.len();
836 if len == ExpandedSignature::<L>::WIRE_BYTES {
837 Ok(AnySignature::Expanded(ExpandedSignature::from_bytes(
838 bytes,
839 )?))
840 } else if len == L::SigLen::USIZE {
841 let sig = Signature::<L>::from_bytes(bytes)?;
842 Ok(AnySignature::Standard(sig))
843 } else if len == CompressedSignature::<L>::WIRE_BYTES {
844 Ok(AnySignature::Compressed(CompressedSignature::from_bytes(
845 bytes,
846 )?))
847 } else {
848 Err(Error::MalformedInput)
849 }
850 }
851
852 pub fn format(&self) -> SignatureFormat {
854 match self {
855 AnySignature::Expanded(_) => SignatureFormat::Expanded,
856 AnySignature::Standard(_) => SignatureFormat::Standard,
857 AnySignature::Compressed(_) => SignatureFormat::Compressed,
858 }
859 }
860}
861
862impl<L: FpBackend> core::fmt::Display for AnySignature<L> {
863 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
864 match self {
865 AnySignature::Expanded(s) => core::fmt::Display::fmt(s, f),
866 AnySignature::Standard(s) => core::fmt::Display::fmt(s, f),
867 AnySignature::Compressed(s) => core::fmt::Display::fmt(s, f),
868 }
869 }
870}
871
872impl<L: FpBackend + LevelPrecomp> Signature<L> {
873 pub fn expand(&self, pk: &PublicKey<L>) -> Result<ExpandedSignature<L>, Error> {
882 let pow_dim2_deg_resp =
883 L::E_RSP as i32 - self.two_resp_length as i32 - self.backtracking as i32;
884 if pow_dim2_deg_resp <= 1 {
886 return Err(Error::InvalidSignature);
887 }
888
889 check_canonical_basis_change_matrix(self).ok_or(Error::InvalidSignature)?;
890
891 let mut e_chall =
892 compute_challenge_curve(&self.chall_coeff, self.backtracking, &pk.curve, pk.hint_pk)
893 .ok_or(Error::InvalidSignature)?;
894
895 let mut b_chall_can = basis_from_hint(&mut e_chall, L::F_CHR, self.hint_chall)
896 .ok_or(Error::InvalidSignature)?;
897
898 let dbl_chall = L::F_CHR as usize
899 - pow_dim2_deg_resp as usize
900 - HD_EXTRA_TORSION as usize
901 - self.two_resp_length as usize;
902 b_chall_can = crate::ec::point::ec_dbl_iter_basis(&b_chall_can, dbl_chall, &mut e_chall);
903
904 let f =
905 pow_dim2_deg_resp as usize + HD_EXTRA_TORSION as usize + self.two_resp_length as usize;
906 matrix_scalar_application_even_basis(&mut b_chall_can, &e_chall, &self.mat, f)
907 .ok_or(Error::InvalidSignature)?;
908
909 let kernel_is_q = self.two_resp_length > 0
910 && mp_is_even::<L>(&self.mat[0][0])
911 && mp_is_even::<L>(&self.mat[1][0]);
912
913 let p_aff = b_chall_can.p.x.mul(&b_chall_can.p.z.inv());
914 let q_aff = b_chall_can.q.x.mul(&b_chall_can.q.z.inv());
915
916 let p_pt = EcPoint::new(p_aff.clone(), Fp2::one());
919 let q_pt = EcPoint::new(q_aff.clone(), Fp2::one());
920 let candidate = difference_point(&p_pt, &q_pt, &e_chall);
921 let known_pmq = &b_chall_can.pmq;
922 let cross1 = candidate.x.mul(&known_pmq.z);
923 let cross2 = candidate.z.mul(&known_pmq.x);
924 let pmq_sign_hint = !bool::from(cross1.ct_equal(&cross2));
925
926 Ok(ExpandedSignature {
927 e_aux_a: self.e_aux_a.clone(),
928 backtracking: self.backtracking,
929 two_resp_length: self.two_resp_length,
930 chall_coeff: self.chall_coeff.clone(),
931 p_chl_x: p_aff,
932 q_chl_x: q_aff,
933 kernel_is_q,
934 pmq_sign_hint,
935 hint_aux: self.hint_aux,
936 hint_chall: self.hint_chall,
937 })
938 }
939
940 pub fn compress(&self) -> CompressedSignature<L> {
946 let pow_dim2 =
947 L::E_RSP as usize - self.two_resp_length as usize - self.backtracking as usize;
948 let det_precision = pow_dim2 + self.two_resp_length as usize;
949
950 let m00_odd = self.mat[0][0].digits[0] & 1 != 0;
951 let (kept, dropped) = if m00_odd {
952 (&self.mat[1][0], &self.mat[1][1])
953 } else {
954 (&self.mat[1][1], &self.mat[1][0])
955 };
956
957 let mut dropped_shifted = dropped.clone();
958 mp_shiftr(dropped_shifted.digits.as_mut_slice(), det_precision);
959 let det_hint = dropped_shifted.digits[0] as u8 & 0x03;
960
961 CompressedSignature {
962 e_aux_a: self.e_aux_a.clone(),
963 backtracking: self.backtracking,
964 two_resp_length: self.two_resp_length,
965 mat_00: self.mat[0][0].clone(),
966 mat_01: self.mat[0][1].clone(),
967 mat_var: kept.clone(),
968 chall_coeff: self.chall_coeff.clone(),
969 det_hint,
970 }
971 }
972}
973
974pub(crate) fn verify_expanded<L: FpBackend + LevelPrecomp>(
980 pk: &PublicKey<L>,
981 msg: &[u8],
982 sig: &ExpandedSignature<L>,
983) -> Result<(), Error> {
984 let pow_dim2_deg_resp = L::E_RSP as i32 - sig.two_resp_length as i32 - sig.backtracking as i32;
985
986 if pow_dim2_deg_resp <= 1 {
988 return Err(Error::InvalidSignature);
989 }
990
991 if !crate::ec::EcCurve::<L>::verify_a(&pk.curve.a) {
992 return Err(Error::InvalidSignature);
993 }
994
995 let mut e_aux = crate::ec::EcCurve::<L>::from_a(&sig.e_aux_a).ok_or(Error::InvalidSignature)?;
996
997 if !crate::verify::verify_canonical_hint::<L>(&mut e_aux, sig.hint_aux) {
998 return Err(Error::InvalidSignature);
999 }
1000
1001 let mut e_chall =
1002 compute_challenge_curve(&sig.chall_coeff, sig.backtracking, &pk.curve, pk.hint_pk)
1003 .ok_or(Error::InvalidSignature)?;
1004
1005 let (_, expected_hint_chall) = basis_to_hint(&mut e_chall, L::F_CHR)?;
1009 if sig.hint_chall != expected_hint_chall {
1010 return Err(Error::InvalidSignature);
1011 }
1012
1013 let p_pt = EcPoint::new(sig.p_chl_x.clone(), Fp2::one());
1017 let q_pt = EcPoint::new(sig.q_chl_x.clone(), Fp2::one());
1018 let pmq_pt = difference_point_with_hint(&p_pt, &q_pt, &e_chall, sig.pmq_sign_hint)
1019 .ok_or(Error::InvalidSignature)?;
1020 let mut b_chall_can = EcBasis {
1021 p: p_pt,
1022 q: q_pt,
1023 pmq: pmq_pt,
1024 };
1025
1026 let b_aux_can =
1028 basis_from_hint(&mut e_aux, L::F_CHR, sig.hint_aux).ok_or(Error::InvalidSignature)?;
1029 let dbl_aux = L::F_CHR as usize - pow_dim2_deg_resp as usize - HD_EXTRA_TORSION as usize;
1030 let b_aux_can = crate::ec::point::ec_dbl_iter_basis(&b_aux_can, dbl_aux, &mut e_aux);
1031
1032 if sig.two_resp_length == 0 && sig.kernel_is_q {
1034 return Err(Error::InvalidSignature);
1035 }
1036
1037 if sig.two_resp_length > 0 {
1038 two_response_isogeny_verify_inner(
1039 &mut e_chall,
1040 &mut b_chall_can,
1041 sig.kernel_is_q,
1042 sig.two_resp_length,
1043 pow_dim2_deg_resp,
1044 )
1045 .ok_or(Error::InvalidSignature)?;
1046 }
1047
1048 let e_com = compute_commitment_curve_verify(
1050 &b_chall_can,
1051 &b_aux_can,
1052 &e_chall,
1053 &e_aux,
1054 pow_dim2_deg_resp,
1055 )
1056 .ok_or(Error::InvalidSignature)?;
1057
1058 let chk_chall = hash_to_challenge(pk, &e_com, msg)?;
1060 if mp_compare::<L>(&sig.chall_coeff, &chk_chall) != 0 {
1061 return Err(Error::InvalidSignature);
1062 }
1063
1064 Ok(())
1065}
1066
1067pub(crate) fn verify_compressed<L: FpBackend + LevelPrecomp>(
1069 pk: &PublicKey<L>,
1070 msg: &[u8],
1071 sig: &CompressedSignature<L>,
1072) -> Result<(), Error> {
1073 let standard = sig.decompress(pk)?;
1074 protocols_verify(pk, msg, &standard)
1075}
1076
1077#[cfg(test)]
1078mod tests {
1079 extern crate std;
1080
1081 use super::*;
1082 use crate::params::{Level1, Level3, Level5};
1083
1084 #[test]
1085 fn standard_sizes() {
1086 assert_eq!(<Level1 as SecurityLevel>::SigLen::USIZE, 148);
1087 assert_eq!(<Level3 as SecurityLevel>::SigLen::USIZE, 224);
1088 assert_eq!(<Level5 as SecurityLevel>::SigLen::USIZE, 292);
1089 }
1090
1091 #[test]
1092 fn expanded_sizes() {
1093 assert_eq!(ExpandedSignature::<Level1>::WIRE_BYTES, 212);
1095 assert_eq!(ExpandedSignature::<Level3>::WIRE_BYTES, 316);
1096 assert_eq!(ExpandedSignature::<Level5>::WIRE_BYTES, 420);
1097 }
1098
1099 #[test]
1100 fn compressed_sizes() {
1101 assert_eq!(CompressedSignature::<Level1>::WIRE_BYTES, 129);
1102 assert_eq!(CompressedSignature::<Level3>::WIRE_BYTES, 196);
1103 assert_eq!(CompressedSignature::<Level5>::WIRE_BYTES, 257);
1104 }
1105
1106 #[test]
1107 #[allow(clippy::assertions_on_constants)]
1108 fn size_ordering() {
1109 assert!(
1110 CompressedSignature::<Level1>::WIRE_BYTES < <Level1 as SecurityLevel>::SigLen::USIZE
1111 );
1112 assert!(<Level1 as SecurityLevel>::SigLen::USIZE < ExpandedSignature::<Level1>::WIRE_BYTES);
1113 }
1114
1115 #[test]
1116 fn standard_signature_backward_compatible() {
1117 let sig = Signature::<Level1>::default();
1118 let bytes = sig.to_bytes();
1119 let decoded = Signature::<Level1>::from_bytes(&bytes);
1120 assert!(decoded.is_ok());
1121 }
1122
1123 #[test]
1124 fn any_signature_standard_roundtrip() {
1125 let sig = Signature::<Level1>::default();
1126 let bytes = sig.to_bytes();
1127 let decoded = AnySignature::<Level1>::from_bytes(&bytes);
1128 assert!(decoded.is_ok());
1129 match decoded.unwrap() {
1130 AnySignature::Standard(_) => {}
1131 _ => panic!("expected Standard variant"),
1132 }
1133 }
1134
1135 #[test]
1136 fn any_signature_rejects_wrong_length() {
1137 let bad = [0u8; 200];
1138 assert!(AnySignature::<Level1>::from_bytes(&bad).is_err());
1139 }
1140
1141 #[test]
1142 fn any_signature_rejects_empty() {
1143 assert!(AnySignature::<Level1>::from_bytes(&[]).is_err());
1144 }
1145
1146 #[test]
1147 fn any_signature_format_accessor() {
1148 let sig = Signature::<Level1>::default();
1149 let any = AnySignature::Standard(sig);
1150 assert_eq!(any.format(), SignatureFormat::Standard);
1151 }
1152
1153 #[test]
1154 fn expanded_from_bytes_too_short() {
1155 let data = [0u8; 100];
1156 assert!(matches!(
1157 ExpandedSignature::<Level1>::from_bytes(&data),
1158 Err(Error::InvalidLength)
1159 ));
1160 }
1161
1162 #[test]
1163 fn expanded_serialization_roundtrip() {
1164 let sig = ExpandedSignature::<Level1> {
1165 e_aux_a: Fp2::zero(),
1166 backtracking: 3,
1167 two_resp_length: 1,
1168 chall_coeff: Scalar::default(),
1169 p_chl_x: Fp2::zero(),
1170 q_chl_x: Fp2::zero(),
1171 kernel_is_q: true,
1172 pmq_sign_hint: true,
1173 hint_aux: 0xAB,
1174 hint_chall: 0xCD,
1175 };
1176 let buf = sig.to_bytes();
1177 let decoded = ExpandedSignature::<Level1>::from_bytes(
1178 &buf[..ExpandedSignature::<Level1>::WIRE_BYTES],
1179 )
1180 .expect("roundtrip decode failed");
1181 assert_eq!(decoded.backtracking, 3);
1182 assert_eq!(decoded.two_resp_length, 1);
1183 assert!(decoded.kernel_is_q);
1184 assert!(decoded.pmq_sign_hint);
1185 assert_eq!(decoded.hint_aux, 0xAB);
1186 assert_eq!(decoded.hint_chall, 0xCD);
1187 }
1188
1189 #[test]
1190 fn expanded_kernel_flag_packing() {
1191 let sig = ExpandedSignature::<Level1> {
1192 e_aux_a: Fp2::zero(),
1193 backtracking: 5,
1194 two_resp_length: 0,
1195 chall_coeff: Scalar::default(),
1196 p_chl_x: Fp2::zero(),
1197 q_chl_x: Fp2::zero(),
1198 kernel_is_q: false,
1199 pmq_sign_hint: false,
1200 hint_aux: 0,
1201 hint_chall: 0,
1202 };
1203 let buf = sig.to_bytes();
1204 let decoded = ExpandedSignature::<Level1>::from_bytes(
1205 &buf[..ExpandedSignature::<Level1>::WIRE_BYTES],
1206 )
1207 .unwrap();
1208 assert_eq!(decoded.backtracking, 5);
1209 assert!(!decoded.kernel_is_q);
1210 }
1211
1212 #[test]
1213 fn compressed_from_bytes_too_short() {
1214 let data = [0u8; 100];
1215 assert!(matches!(
1216 CompressedSignature::<Level1>::from_bytes(&data),
1217 Err(Error::InvalidLength)
1218 ));
1219 }
1220
1221 #[test]
1222 fn compressed_serialization_roundtrip() {
1223 let sig = CompressedSignature::<Level1> {
1224 e_aux_a: Fp2::zero(),
1225 backtracking: 2,
1226 two_resp_length: 1,
1227 mat_00: Scalar::default(),
1228 mat_01: Scalar::default(),
1229 mat_var: Scalar::default(),
1230 chall_coeff: Scalar::default(),
1231 det_hint: 0x03,
1232 };
1233 let buf = sig.to_bytes();
1234 let decoded = CompressedSignature::<Level1>::from_bytes(
1235 &buf[..CompressedSignature::<Level1>::WIRE_BYTES],
1236 )
1237 .expect("roundtrip decode failed");
1238 assert_eq!(decoded.backtracking, 2);
1239 assert_eq!(decoded.two_resp_length, 1);
1240 assert_eq!(decoded.det_hint, 0x03);
1241 }
1242
1243 #[test]
1244 fn expand_does_not_panic_on_default_inputs() {
1245 let sig = Signature::<Level1>::default();
1246 let pk = PublicKey::<Level1>::default();
1247 let _ = sig.expand(&pk);
1248 }
1249
1250 #[test]
1251 fn compress_default_signature() {
1252 let sig = Signature::<Level1>::default();
1253 let compressed = sig.compress();
1254 assert_eq!(compressed.backtracking, 0);
1255 assert_eq!(compressed.two_resp_length, 0);
1256 }
1257
1258 #[test]
1259 fn hensel_inverse_basic() {
1260 let mut out = [0u64; 4];
1261 let a = [3u64, 0, 0, 0];
1262 hensel_inv_mod_2e(&mut out, &a, 64);
1263 let product = (3u128) * (out[0] as u128);
1265 assert_eq!(product as u64, 1);
1266 }
1267
1268 #[test]
1269 fn hensel_inverse_multiword() {
1270 let mut out = [0u64; 4];
1271 let a = [7u64, 0, 0, 0];
1272 hensel_inv_mod_2e(&mut out, &a, 125);
1273 let mut check = [0u64; 4];
1275 mp_mul_mod(&mut check, &a, &out, 125);
1276 assert_eq!(check[0], 1);
1277 assert_eq!(check[1], 0);
1278 }
1279}