Skip to content

Commit

Permalink
add hash256 for hashing two times, fix signing
Browse files Browse the repository at this point in the history
  • Loading branch information
hmzdot committed Mar 8, 2023
1 parent d7b9582 commit 7c0c777
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 48 deletions.
20 changes: 8 additions & 12 deletions src/elliptic_curve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub mod private_key;
mod tests {
use std::str::FromStr;

use crate::{finite_fields::macros::felt, elliptic_curve::{secp256k1::Secp256k1Felt, signature::Signature}};
use crate::{finite_fields::macros::felt, elliptic_curve::{secp256k1::Secp256k1Felt, signature::Signature}, helpers::bytes::ToBytesBigEndian};
use num_bigint::BigUint;
use primitive_types::U256;

Expand Down Expand Up @@ -170,27 +170,23 @@ mod tests {
let point = Secp256k1Point::new(point_x, point_y);

let good_z = Secp256k1Felt::from_bytes(
b"ec208baa0fc1c19f708a9ca96fdeff3ac3f230bb4a7ba4aede4942ad003c0f60",
)
.unwrap();
&"ec208baa0fc1c19f708a9ca96fdeff3ac3f230bb4a7ba4aede4942ad003c0f60".to_bytes_be(),
);

let signature = Signature::new(
Secp256k1Felt::from_bytes(
b"ac8d1c87e51d0d441be8b3dd5b05c8795b48875dffe00b7ffcfac23010d3a395",
)
.unwrap(),
&"ac8d1c87e51d0d441be8b3dd5b05c8795b48875dffe00b7ffcfac23010d3a395".to_bytes_be(),
),
Secp256k1Felt::from_bytes(
b"068342ceff8935ededd102dd876ffd6ba72d6a427a3edb13d26eb0781cb423c4",
&"068342ceff8935ededd102dd876ffd6ba72d6a427a3edb13d26eb0781cb423c4".to_bytes_be(),
)
.unwrap(),
);

assert_eq!(point.verify(&good_z, &signature), true);

let bad_z = Secp256k1Felt::from_bytes(
b"bad000aa0fc1c19f708a9ca96fdeff3ac3f230bb4a7ba4aede4942ad003c0f60",
)
.unwrap();
&"bad000aa0fc1c19f708a9ca96fdeff3ac3f230bb4a7ba4aede4942ad003c0f60".to_bytes_be(),
);

assert_eq!(point.verify(&bad_z, &signature), false);
}
Expand Down
56 changes: 37 additions & 19 deletions src/elliptic_curve/private_key.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
use super::{secp256k1::{Secp256k1Felt, Secp256k1Point}, signature::{Signature, self}};
use crate::helpers::hash::hash256;

use super::{
secp256k1::{Secp256k1Felt, Secp256k1Point},
signature::{self, Signature},
};
use hmac::{Hmac, Mac};
use num_bigint::BigUint;
use sha2::Sha256;
Expand All @@ -20,37 +25,44 @@ impl PrivateKey {
}

/// Signs a field element using the private key
///
///
/// # Panics
///
///
/// This method will panic if the field element is not a valid field element
pub fn sign(&self, z: Secp256k1Felt) -> Signature {
#[allow(clippy::many_single_char_names)]
pub fn sign(&self, z: &Secp256k1Felt) -> Signature {
let k = self.deterministic_k(z.inner().clone());
println!("k: {}", k);

let r = Secp256k1Felt::from((Secp256k1Point::g() * &k).x().clone().unwrap());
// r = (k * G).x
let g_x = (Secp256k1Point::g() * &k).x().clone().unwrap();
let r = Secp256k1Felt::from(g_x);

// s = (z + r * secret) / k
let k = Secp256k1Felt::new(k);
let mut s = &z + &r * &self.secret;
let mut s = (z + &r * &self.secret) / &k;

// if s > n / 2 then s = n - s
let n = Secp256k1Point::order();
let n_half = &n / BigUint::from(2u32);

if s.inner() > &n_half {
s = Secp256k1Felt::new(n - s.inner());
}

Signature::new(r,s)
Signature::new(r, s)
}

/// Signs a byte slice using the private key
/// This is a convenience method that hashes the message before signing
///
/// This is a convenience method that hashes the message before signing
///
/// # Panics
///
///
/// This method will panic if the hash of the message is not a valid field element
pub fn sign_slice(&self, message: &[u8]) -> Signature {
let z = Secp256k1Felt::from_bytes(sha256::digest(message).as_bytes()).unwrap();
self.sign(z)
let hash = hash256(message);
let z = Secp256k1Felt::from_bytes(hash.as_slice());

self.sign(&z)
}

/// Checks if message is signed by this private key
Expand All @@ -60,17 +72,17 @@ impl PrivateKey {

/// Convenience method to verify a slice after hashing it
pub fn verify_slice(&self, message: &[u8], sig: &Signature) -> bool {
let z = Secp256k1Felt::from_bytes(sha256::digest(message).as_bytes()).unwrap();
let message_digest = hash256(message);
let z = Secp256k1Felt::from_bytes(&message_digest);
self.verify(&z, sig)
}

/// Creates a unique, deterministic k value
///
///
/// This is important because if the same k value is used twice, the private key can
/// be recovered using both signatures.
///
///
/// The specification for determining k is defined in RFC 6779 (<https://tools.ietf.org/html/rfc6979>)
/// TODO: Fix the function
fn deterministic_k(&self, z: BigUint) -> BigUint {
let k = [0u8; 32];
let v = [1u8; 32];
Expand All @@ -80,8 +92,14 @@ impl PrivateKey {
z -= Secp256k1Point::order();
}

let z_bytes = z.to_bytes_be();
let secret_bytes = self.secret.inner().to_bytes_be();
let z = z.to_bytes_be();
let mut z_bytes = vec![0; 32 - z.len()];
z_bytes.extend_from_slice(z.as_slice());

// Pad secret to 32 bytes, from the left
let secret = self.secret.inner().to_bytes_be();
let mut secret_bytes = vec![0; 32 - secret.len()];
secret_bytes.extend_from_slice(secret.as_slice());

// k := hmac_k (v || 0x00 || secret_bytes || z_bytes)
let mut hmac = Hmac256::new_from_slice(&k).unwrap();
Expand Down
16 changes: 10 additions & 6 deletions src/elliptic_curve/secp256k1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,9 @@ impl Secp256k1Felt {
Self(Felt::new(inner, Self::prime()))
}

pub fn from_bytes(value: &[u8]) -> Result<Self> {
match BigUint::parse_bytes(value, 16) {
Some(value) => Ok(Self::new(value)),
None => Err(eyre!("Invalid byte sequence")),
}
pub fn from_bytes(value: &[u8]) -> Self {
let inner = BigUint::from_bytes_be(value);
Self::new(inner)
}

pub fn inner(&self) -> &BigUint {
Expand Down Expand Up @@ -66,7 +64,13 @@ impl Add<Secp256k1Felt> for Secp256k1Felt {
type Output = Secp256k1Felt;

fn add(self, rhs: Secp256k1Felt) -> Self::Output {
Self::from(self.0 + rhs.0)
let one = BigUint::from(1u32);
let n = Self::order();

let sum = self.inner() + rhs.inner();
let sum_mod = sum.modpow(&one, &n);

Self::new(sum_mod)
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/elliptic_curve/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ impl Signature {
}

/// Verifies the signature, given the message, signature and the public key
#[allow(clippy::many_single_char_names)]
pub fn verify(
&self,
z: &Secp256k1Felt,
public_key: &Secp256k1Point,
) -> bool {
println!("{} {} {} {}", z, self.r(), self.s(), public_key);
let g = Secp256k1Point::g();

let u = z / self.s();
Expand All @@ -61,7 +61,7 @@ impl Signature {
z: &[u8],
public_key: &Secp256k1Point,
) -> bool {
let z = Secp256k1Felt::from_bytes(z).unwrap();
let z = Secp256k1Felt::from_bytes(z);
self.verify(&z, public_key)
}
}
Expand Down
27 changes: 27 additions & 0 deletions src/helpers/bytes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
pub trait ToBytesBigEndian {
fn to_bytes_be(&self) -> Vec<u8>;
}

impl ToBytesBigEndian for String {
fn to_bytes_be(&self) -> Vec<u8> {
self.as_bytes()
.chunks(2) // Split into pairs of bytes
.map(|chunk| {
let hex_str = std::str::from_utf8(chunk).unwrap();
u8::from_str_radix(hex_str, 16).unwrap()
})
.collect()
}
}

impl ToBytesBigEndian for &str {
fn to_bytes_be(&self) -> Vec<u8> {
self.as_bytes()
.chunks(2) // Split into pairs of bytes
.map(|chunk| {
let hex_str = std::str::from_utf8(chunk).unwrap();
u8::from_str_radix(hex_str, 16).unwrap()
})
.collect()
}
}
7 changes: 7 additions & 0 deletions src/helpers/hash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use super::bytes::ToBytesBigEndian;

/// Two rounds of SHA256.
pub fn hash256(data: &[u8]) -> Vec<u8> {
let first_round = sha256::digest(data).to_bytes_be();
sha256::digest(first_round.as_slice()).to_bytes_be()
}
2 changes: 2 additions & 0 deletions src/helpers/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod bytes;
pub mod hash;
17 changes: 8 additions & 9 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,25 @@
clippy::module_name_repetitions
)]

use crate::elliptic_curve::private_key::PrivateKey;
use crate::{elliptic_curve::private_key::PrivateKey};
use elliptic_curve::secp256k1::Secp256k1Felt;

pub mod elliptic_curve;
pub mod finite_fields;
mod helpers;

fn main() {
let secret = Secp256k1Felt::from_bytes(
b"ec208baa0fc1c19f708a9ca96fdeff3ac3f230bb4a7ba4aede4942ad003c0f60",
)
.unwrap();
let secret = Secp256k1Felt::from_bytes("my secret".as_bytes());

let wallet = PrivateKey::new(secret);
let signature = wallet.sign_slice(b"Programming Bitcoin!");

println!("Signature: {}", signature);

let is_legit = wallet.verify_slice(b"Programming Bitcoin", &signature);
println!("Shouldn't be legit: {}", is_legit);
// let is_legit = wallet.verify_slice(b"Programming Bitcoin", &signature);
// println!("Shouldn't be legit: {}", is_legit);

let now = std::time::Instant::now();
let is_legit = wallet.verify_slice(b"Programming Bitcoin!", &signature);
println!("Should be legit: {}", is_legit);

println!("Time: {:?}", now.elapsed());
}

0 comments on commit 7c0c777

Please sign in to comment.