midnight_circuits/utils/
ecdsa.rs1use base64::DecodeError;
16use ff::Field;
17use group::{Curve, GroupEncoding};
18use midnight_curves::k256::{Fp as K256Base, Fq as K256Scalar, K256Affine, K256};
19use rand::RngCore;
20
21use crate::CircuitField;
22
23#[derive(Clone, Debug)]
24pub struct Ecdsa;
26
27pub type PublicKey = K256;
29
30pub type SecretKey = K256Scalar;
32
33#[derive(Clone, Copy, Debug)]
34
35pub struct ECDSASig {
37 r: [u8; 32],
39 s: K256Scalar,
40}
41
42impl ECDSASig {
43 pub fn get_r(&self) -> [u8; 32] {
45 self.r
46 }
47
48 pub fn get_s(&self) -> K256Scalar {
50 self.s
51 }
52
53 pub fn from_bytes_be(bytes: &[u8]) -> Self {
57 assert_eq!(bytes.len(), 64);
58
59 let mut r = [0u8; 32];
60 r.copy_from_slice(&bytes[..32]);
61 r.reverse();
62
63 let s =
64 K256Scalar::from_bytes_be(&bytes[32..]).expect("Valid secp256k1 scalar in signature");
65 ECDSASig { r, s }
66 }
67}
68
69impl Ecdsa {
70 pub fn keygen<R: RngCore>(rng: &mut R) -> (PublicKey, SecretKey) {
72 let sk = K256Scalar::random(rng);
73 let pk = K256::generator() * sk;
74 (pk, sk)
75 }
76
77 pub fn sign<R: RngCore>(sk: &SecretKey, msg_hash: &K256Scalar, rng: &mut R) -> ECDSASig {
79 let k = K256Scalar::random(rng);
80 let k_point: K256 = K256::generator() * k;
81
82 let r_as_base = k_point.to_affine().x();
83 let r = r_as_base.to_bytes_le();
84 let r_as_scalar = K256Scalar::from_bytes_le(&r).unwrap();
85
86 let s = k.invert().unwrap() * (msg_hash + r_as_scalar * sk);
87 ECDSASig { r, s }
88 }
89
90 pub fn verify(pk: &PublicKey, msg_hash: &K256Scalar, signature: &ECDSASig) -> bool {
92 let g = K256::generator();
93 let r_as_scalar = K256Scalar::from_bytes_le(&signature.r).unwrap();
94 let r_as_base = K256Base::from_bytes_le(&signature.r).unwrap();
95
96 let s_inv = signature.s.invert().unwrap();
97 let k_point = g * (s_inv * msg_hash) + *pk * (s_inv * r_as_scalar);
98
99 k_point.to_affine().x() == r_as_base
100 }
101}
102
103pub trait FromBase64: Sized {
106 fn from_base64(base64_bytes: &[u8]) -> Result<Self, DecodeError>;
108}
109
110impl FromBase64 for ECDSASig {
111 fn from_base64(base64_bytes: &[u8]) -> Result<Self, DecodeError> {
113 let bytes = base64::decode_config(base64_bytes, base64::URL_SAFE_NO_PAD)?;
114 Ok(ECDSASig::from_bytes_be(&bytes))
115 }
116}
117
118impl FromBase64 for PublicKey {
119 fn from_base64(base64_bytes: &[u8]) -> Result<Self, DecodeError> {
121 let input_len = base64_bytes.len();
122
123 match input_len {
124 44 => {
126 let bytes = base64::decode_config(base64_bytes, base64::STANDARD_NO_PAD)?;
127 assert_eq!(bytes.len(), 33);
128 let repr: [u8; 33] = bytes.try_into().expect("33 bytes");
129
130 let ret = K256Affine::from_bytes(&repr.into())
131 .expect("Valid compressed secp256k1 point.");
132 Ok(ret.into())
133 }
134
135 86 => from_jwk(&base64_bytes[..43], &base64_bytes[43..]),
137 _ => Err(DecodeError::InvalidLength),
138 }
139 }
140}
141
142fn from_jwk(x: &[u8], y: &[u8]) -> Result<PublicKey, DecodeError> {
145 let x_bytes = base64::decode_config(x, base64::URL_SAFE)?;
146 let y_bytes = base64::decode_config(y, base64::URL_SAFE)?;
147 assert_eq!(x_bytes.len(), 32);
148 assert_eq!(y_bytes.len(), 32);
149
150 let x_fp = K256Base::from_bytes_be(&x_bytes).expect("Valid x coordinate");
152 let y_fp = K256Base::from_bytes_be(&y_bytes).expect("Valid y coordinate");
153
154 let ret = K256Affine::from_xy(x_fp, y_fp).expect("Valid point on curve");
155 Ok(ret.into())
156}