Skip to content

Commit ff921c7

Browse files
manh9203alv-around
authored andcommitted
[feat] Add constructors for ECDSA verifying key (#1348) (#1384)
* add constructor for public key * set capacity for res vector
1 parent fd80906 commit ff921c7

File tree

2 files changed

+131
-13
lines changed

2 files changed

+131
-13
lines changed

extensions/ecc/guest/src/ecdsa.rs

Lines changed: 104 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
use alloc::vec::Vec;
12
use core::ops::{Add, AddAssign, Mul};
23

34
use ecdsa::{self, hazmat::bits2field, Error, RecoveryId, Result};
4-
use elliptic_curve::PrimeCurve;
5+
use elliptic_curve::{sec1::Tag, PrimeCurve};
56
use openvm_algebra_guest::{DivUnsafe, IntMod, Reduce};
67

78
use crate::{
@@ -25,15 +26,112 @@ pub struct PublicKey<C: IntrinsicCurve> {
2526
point: <C as IntrinsicCurve>::Point,
2627
}
2728

28-
impl<C: IntrinsicCurve> PublicKey<C> {
29-
pub fn into_inner(self) -> <C as IntrinsicCurve>::Point {
30-
self.point
29+
impl<C: IntrinsicCurve> PublicKey<C>
30+
where
31+
C::Point: WeierstrassPoint + Group + FromCompressed<Coordinate<C>>,
32+
Coordinate<C>: IntMod,
33+
for<'a> &'a Coordinate<C>: Mul<&'a Coordinate<C>, Output = Coordinate<C>>,
34+
{
35+
pub fn new(point: <C as IntrinsicCurve>::Point) -> Self {
36+
Self { point }
37+
}
38+
39+
pub fn from_sec1_bytes(bytes: &[u8]) -> Result<Self> {
40+
if bytes.is_empty() {
41+
return Err(Error::new());
42+
}
43+
44+
// Validate tag
45+
let tag = Tag::from_u8(bytes[0]).unwrap();
46+
47+
// Validate length
48+
let expected_len = tag.message_len(Coordinate::<C>::NUM_LIMBS);
49+
if bytes.len() != expected_len {
50+
return Err(Error::new());
51+
}
52+
53+
match tag {
54+
Tag::Identity => {
55+
let point = <<C as IntrinsicCurve>::Point as WeierstrassPoint>::IDENTITY;
56+
Ok(Self { point })
57+
}
58+
59+
Tag::CompressedEvenY | Tag::CompressedOddY => {
60+
let x = Coordinate::<C>::from_be_bytes(&bytes[1..]);
61+
let rec_id = bytes[0] & 1;
62+
let point = FromCompressed::decompress(x, &rec_id);
63+
Ok(Self { point })
64+
}
65+
66+
Tag::Uncompressed => {
67+
let (x_bytes, y_bytes) = bytes[1..].split_at(Coordinate::<C>::NUM_LIMBS);
68+
let x = Coordinate::<C>::from_be_bytes(x_bytes);
69+
let y = Coordinate::<C>::from_be_bytes(y_bytes);
70+
let point = <C as IntrinsicCurve>::Point::from_xy(x, y).unwrap();
71+
Ok(Self { point })
72+
}
73+
74+
_ => Err(Error::new()),
75+
}
76+
}
77+
78+
pub fn to_sec1_bytes(&self, compress: bool) -> Vec<u8> {
79+
if self.point.is_identity() {
80+
return vec![0x00];
81+
}
82+
83+
let (x, y) = self.point.clone().into_coords();
84+
85+
if compress {
86+
let mut bytes = Vec::<u8>::with_capacity(1 + Coordinate::<C>::NUM_LIMBS);
87+
let tag = if y.as_le_bytes()[0] & 1 == 1 {
88+
Tag::CompressedOddY
89+
} else {
90+
Tag::CompressedEvenY
91+
};
92+
bytes.push(tag.into());
93+
bytes.extend_from_slice(x.to_be_bytes().as_ref());
94+
bytes
95+
} else {
96+
let mut bytes = Vec::<u8>::with_capacity(1 + Coordinate::<C>::NUM_LIMBS * 2);
97+
bytes.push(Tag::Uncompressed.into());
98+
bytes.extend_from_slice(x.to_be_bytes().as_ref());
99+
bytes.extend_from_slice(y.to_be_bytes().as_ref());
100+
bytes
101+
}
102+
}
103+
104+
pub fn as_affine(&self) -> &<C as IntrinsicCurve>::Point {
105+
&self.point
31106
}
32107
}
33108

34-
impl<C: IntrinsicCurve> VerifyingKey<C> {
109+
impl<C: IntrinsicCurve> VerifyingKey<C>
110+
where
111+
C::Point: WeierstrassPoint + Group + FromCompressed<Coordinate<C>>,
112+
Coordinate<C>: IntMod,
113+
for<'a> &'a Coordinate<C>: Mul<&'a Coordinate<C>, Output = Coordinate<C>>,
114+
{
115+
pub fn new(public_key: PublicKey<C>) -> Self {
116+
Self { inner: public_key }
117+
}
118+
119+
pub fn from_sec1_bytes(bytes: &[u8]) -> Result<Self> {
120+
let public_key = PublicKey::<C>::from_sec1_bytes(bytes)?;
121+
Ok(Self::new(public_key))
122+
}
123+
124+
pub fn from_affine(point: <C as IntrinsicCurve>::Point) -> Result<Self> {
125+
let public_key = PublicKey::<C>::new(point);
126+
Ok(Self::new(public_key))
127+
}
128+
129+
pub fn to_sec1_bytes(&self, compress: bool) -> Vec<u8> {
130+
self.inner.to_sec1_bytes(compress)
131+
}
132+
35133
pub fn as_affine(&self) -> &<C as IntrinsicCurve>::Point {
36-
&self.inner.point
134+
self.inner.as_affine()
37135
}
38136
}
39137

extensions/ecc/tests/programs/examples/ecdsa.rs

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,34 @@ pub fn main() {
4545
"0200866db99873b09fc2fb1e3ba549b156e96d1a567e3284f5f0e859a83320cb8b"
4646
))
4747
.unwrap();
48-
// sec1 encoding, the first byte is for compression flag
49-
let expected_key = expected_key.to_encoded_point(false);
50-
let public_key = recovered_key.as_affine();
51-
let mut buffer = [0u8; 64];
52-
buffer[..32].copy_from_slice(&public_key.x().to_be_bytes());
53-
buffer[32..].copy_from_slice(&public_key.y().to_be_bytes());
54-
assert_eq!(&buffer, &expected_key.as_bytes()[1..]);
5548

49+
// Test sec1 encoding and decoding
50+
let expected_key_uncompressed = expected_key.to_encoded_point(false);
51+
let public_key_uncompressed = recovered_key.to_sec1_bytes(false);
52+
assert_eq!(
53+
&public_key_uncompressed,
54+
&expected_key_uncompressed.as_bytes()
55+
);
56+
57+
let expected_key_compressed = expected_key.to_encoded_point(true);
58+
let public_key_compressed = recovered_key.to_sec1_bytes(true);
59+
assert_eq!(&public_key_compressed, &expected_key_compressed.as_bytes());
60+
61+
let public_key_uncompressed_decoded =
62+
VerifyingKey::<Secp256k1>::from_sec1_bytes(&public_key_uncompressed).unwrap();
63+
let public_key_compressed_decoded =
64+
VerifyingKey::<Secp256k1>::from_sec1_bytes(&public_key_compressed).unwrap();
65+
66+
assert_eq!(
67+
public_key_uncompressed_decoded.as_affine(),
68+
recovered_key.as_affine()
69+
);
70+
assert_eq!(
71+
public_key_compressed_decoded.as_affine(),
72+
recovered_key.as_affine()
73+
);
74+
75+
// Test verification
5676
recovered_key
5777
.verify_prehashed(&prehash, &signature)
5878
.unwrap();

0 commit comments

Comments
 (0)