2022-07-16 21:45:41 +00:00
|
|
|
use sha3::{Digest, Keccak256};
|
|
|
|
|
|
2024-03-24 09:00:54 -04:00
|
|
|
use group::ff::PrimeField;
|
2022-07-16 21:45:41 +00:00
|
|
|
use k256::{
|
2023-03-28 04:38:01 -04:00
|
|
|
elliptic_curve::{
|
2024-03-24 09:00:54 -04:00
|
|
|
bigint::ArrayEncoding, ops::Reduce, point::AffineCoordinates, sec1::ToEncodedPoint,
|
2023-03-28 04:38:01 -04:00
|
|
|
},
|
2024-03-24 09:00:54 -04:00
|
|
|
ProjectivePoint, Scalar, U256,
|
2022-07-16 21:45:41 +00:00
|
|
|
};
|
|
|
|
|
|
2024-03-24 09:00:54 -04:00
|
|
|
use frost::{
|
|
|
|
|
algorithm::{Hram, SchnorrSignature},
|
|
|
|
|
curve::Secp256k1,
|
|
|
|
|
};
|
2022-07-16 21:45:41 +00:00
|
|
|
|
2024-03-24 09:00:54 -04:00
|
|
|
pub(crate) fn keccak256(data: &[u8]) -> [u8; 32] {
|
2023-11-06 17:26:32 -05:00
|
|
|
Keccak256::digest(data).into()
|
2022-07-16 21:45:41 +00:00
|
|
|
}
|
|
|
|
|
|
2024-03-24 09:00:54 -04:00
|
|
|
pub(crate) fn address(point: &ProjectivePoint) -> [u8; 20] {
|
2022-07-16 21:45:41 +00:00
|
|
|
let encoded_point = point.to_encoded_point(false);
|
2024-03-24 09:00:54 -04:00
|
|
|
// Last 20 bytes of the hash of the concatenated x and y coordinates
|
|
|
|
|
// We obtain the concatenated x and y coordinates via the uncompressed encoding of the point
|
|
|
|
|
keccak256(&encoded_point.as_ref()[1 .. 65])[12 ..].try_into().unwrap()
|
2022-07-16 21:45:41 +00:00
|
|
|
}
|
|
|
|
|
|
2024-03-24 09:00:54 -04:00
|
|
|
#[allow(non_snake_case)]
|
|
|
|
|
pub struct PublicKey {
|
|
|
|
|
pub A: ProjectivePoint,
|
|
|
|
|
pub px: Scalar,
|
|
|
|
|
pub parity: u8,
|
|
|
|
|
}
|
2022-07-16 21:45:41 +00:00
|
|
|
|
2024-03-24 09:00:54 -04:00
|
|
|
impl PublicKey {
|
2022-07-16 21:45:41 +00:00
|
|
|
#[allow(non_snake_case)]
|
2024-03-24 09:00:54 -04:00
|
|
|
pub fn new(A: ProjectivePoint) -> Option<PublicKey> {
|
|
|
|
|
let affine = A.to_affine();
|
|
|
|
|
let parity = u8::from(bool::from(affine.y_is_odd())) + 27;
|
|
|
|
|
if parity != 27 {
|
|
|
|
|
None?;
|
|
|
|
|
}
|
2022-07-16 21:45:41 +00:00
|
|
|
|
2024-03-24 09:00:54 -04:00
|
|
|
let x_coord = affine.x();
|
|
|
|
|
let x_coord_scalar = <Scalar as Reduce<U256>>::reduce_bytes(&x_coord);
|
|
|
|
|
// Return None if a reduction would occur
|
|
|
|
|
if x_coord_scalar.to_repr() != x_coord {
|
|
|
|
|
None?;
|
2022-07-16 21:45:41 +00:00
|
|
|
}
|
2022-07-22 02:34:36 -04:00
|
|
|
|
2024-03-24 09:00:54 -04:00
|
|
|
Some(PublicKey { A, px: x_coord_scalar, parity })
|
|
|
|
|
}
|
2022-07-16 21:45:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Default)]
|
|
|
|
|
pub struct EthereumHram {}
|
|
|
|
|
impl Hram<Secp256k1> for EthereumHram {
|
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
|
fn hram(R: &ProjectivePoint, A: &ProjectivePoint, m: &[u8]) -> Scalar {
|
|
|
|
|
let a_encoded_point = A.to_encoded_point(true);
|
|
|
|
|
let mut a_encoded = a_encoded_point.as_ref().to_owned();
|
|
|
|
|
a_encoded[0] += 25; // Ethereum uses 27/28 for point parity
|
2024-03-24 09:00:54 -04:00
|
|
|
assert!((a_encoded[0] == 27) || (a_encoded[0] == 28));
|
2022-07-16 21:45:41 +00:00
|
|
|
let mut data = address(R).to_vec();
|
|
|
|
|
data.append(&mut a_encoded);
|
2024-03-24 09:00:54 -04:00
|
|
|
data.extend(m);
|
2023-03-28 04:38:01 -04:00
|
|
|
Scalar::reduce(U256::from_be_slice(&keccak256(&data)))
|
2022-07-16 21:45:41 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-24 09:00:54 -04:00
|
|
|
pub struct Signature {
|
|
|
|
|
pub(crate) c: Scalar,
|
|
|
|
|
pub(crate) s: Scalar,
|
2022-07-16 21:45:41 +00:00
|
|
|
}
|
2024-03-24 09:00:54 -04:00
|
|
|
impl Signature {
|
|
|
|
|
pub fn new(
|
|
|
|
|
public_key: &PublicKey,
|
|
|
|
|
chain_id: U256,
|
|
|
|
|
m: &[u8],
|
|
|
|
|
signature: SchnorrSignature<Secp256k1>,
|
|
|
|
|
) -> Option<Signature> {
|
|
|
|
|
let c = EthereumHram::hram(
|
|
|
|
|
&signature.R,
|
|
|
|
|
&public_key.A,
|
|
|
|
|
&[chain_id.to_be_byte_array().as_slice(), &keccak256(m)].concat(),
|
|
|
|
|
);
|
|
|
|
|
if !signature.verify(public_key.A, c) {
|
|
|
|
|
None?;
|
|
|
|
|
}
|
|
|
|
|
Some(Signature { c, s: signature.s })
|
2022-07-16 21:45:41 +00:00
|
|
|
}
|
|
|
|
|
}
|