use base64::DecodeError;
use ff::Field;
use group::{Curve, GroupEncoding, UncompressedEncoding};
use midnight_curves::secp256k1::{
Fp as secp256k1Base, Fq as secp256k1Scalar, Secp256k1, Secp256k1Affine,
};
use rand::RngCore;
#[derive(Clone, Debug)]
pub struct Ecdsa;
pub type PublicKey = Secp256k1;
pub type SecretKey = secp256k1Scalar;
#[derive(Clone, Copy, Debug)]
pub struct ECDSASig {
r: [u8; 32],
s: secp256k1Scalar,
}
impl ECDSASig {
pub fn get_r(&self) -> [u8; 32] {
self.r
}
pub fn get_s(&self) -> secp256k1Scalar {
self.s
}
pub fn from_bytes_le(bytes: &[u8]) -> Self {
assert_eq!(bytes.len(), 64);
let r: [u8; 32] = bytes[..32].try_into().unwrap();
let s_bytes: [u8; 32] = bytes[..32].try_into().unwrap();
let s = secp256k1Scalar::from_bytes(&s_bytes).expect("Valid Secp256k1 scalar in signature");
ECDSASig { r, s }
}
pub fn from_bytes_be(bytes: &[u8]) -> Self {
assert_eq!(bytes.len(), 64);
let mut r = [0u8; 32];
r.copy_from_slice(&bytes[..32]);
r.reverse();
let mut s_bytes = [0u8; 32];
s_bytes.copy_from_slice(&bytes[32..]);
s_bytes.reverse();
let s = secp256k1Scalar::from_bytes(&s_bytes).expect("Valid Secp256k1 scalar in signature");
ECDSASig { r, s }
}
}
impl Ecdsa {
pub fn keygen<R: RngCore>(rng: &mut R) -> (PublicKey, SecretKey) {
let sk = secp256k1Scalar::random(rng);
let pk = Secp256k1::generator() * sk;
(pk, sk)
}
pub fn sign<R: RngCore>(sk: &SecretKey, msg_hash: &secp256k1Scalar, rng: &mut R) -> ECDSASig {
let k = secp256k1Scalar::random(rng);
let k_point: Secp256k1 = Secp256k1::generator() * k;
let r_as_base = k_point.to_affine().x;
let r = r_as_base.to_bytes();
let r_as_scalar = secp256k1Scalar::from_bytes(&r).unwrap();
let s = k.invert().unwrap() * (msg_hash + r_as_scalar * sk);
ECDSASig { r, s }
}
pub fn verify(pk: &PublicKey, msg_hash: &secp256k1Scalar, signature: &ECDSASig) -> bool {
let g = Secp256k1::generator();
let r_as_scalar = secp256k1Scalar::from_bytes(&signature.r).unwrap();
let r_as_base = secp256k1Base::from_bytes(&signature.r).unwrap();
let s_inv = signature.s.invert().unwrap();
let k_point = g * (s_inv * msg_hash) + pk * (s_inv * r_as_scalar);
k_point.to_affine().x == r_as_base
}
}
pub trait FromBase64: Sized {
fn from_base64(base64_bytes: &[u8]) -> Result<Self, DecodeError>;
}
impl FromBase64 for ECDSASig {
fn from_base64(base64_bytes: &[u8]) -> Result<Self, DecodeError> {
let bytes = base64::decode_config(base64_bytes, base64::URL_SAFE_NO_PAD)?;
Ok(ECDSASig::from_bytes_be(&bytes))
}
}
impl FromBase64 for PublicKey {
fn from_base64(base64_bytes: &[u8]) -> Result<Self, DecodeError> {
let input_len = base64_bytes.len();
match input_len {
44 => {
let mut bytes = base64::decode_config(base64_bytes, base64::STANDARD_NO_PAD)?;
assert_eq!(bytes.len(), 33);
bytes[0] &= 0x01;
bytes[1..].reverse();
let repr = bytes.as_slice().into();
let ret =
Secp256k1Affine::from_bytes(&repr).expect("Valid compressed Secp256k1 point.");
Ok(ret.into())
}
86 => from_jwk(&base64_bytes[..43], &base64_bytes[43..]),
_ => Err(DecodeError::InvalidLength),
}
}
}
fn from_jwk(x: &[u8], y: &[u8]) -> Result<PublicKey, DecodeError> {
let mut x_bytes = base64::decode_config(x, base64::URL_SAFE)?;
let mut y_bytes = base64::decode_config(y, base64::URL_SAFE)?;
assert_eq!(x_bytes.len(), 32);
assert_eq!(y_bytes.len(), 32);
x_bytes.reverse();
y_bytes.reverse();
let bytes: [u8; 64] = [x_bytes, y_bytes].concat().try_into().expect("64 bytes");
let ret = Secp256k1Affine::from_uncompressed(&bytes.into()).expect("Invalid point");
Ok(ret.into())
}