-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #821 from Chia-Network/secp
Add secp256k1 and secp256r1 types
- Loading branch information
Showing
16 changed files
with
737 additions
and
1 deletion.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
[package] | ||
name = "chia-secp" | ||
version = "0.16.0" | ||
edition = "2021" | ||
license = "Apache-2.0" | ||
description = "Secp256k1 and secp256r1 types for Chia" | ||
authors = ["Brandon Haggstrom <[email protected]>"] | ||
homepage = "https://github.com/Chia-Network/chia_rs" | ||
repository = "https://github.com/Chia-Network/chia_rs" | ||
|
||
[lints] | ||
workspace = true | ||
|
||
[features] | ||
arbitrary = ["dep:arbitrary"] | ||
|
||
[dependencies] | ||
arbitrary = { workspace = true, optional = true } | ||
k256 = { workspace = true } | ||
p256 = { workspace = true } | ||
hex = { workspace = true } | ||
chia-sha2= { workspace = true } | ||
|
||
[dev-dependencies] | ||
rand = { workspace = true } | ||
rand_chacha = { workspace = true } | ||
anyhow = { workspace = true } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
mod secp256k1; | ||
mod secp256r1; | ||
|
||
pub use secp256k1::*; | ||
pub use secp256r1::*; | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use rand::{Rng, SeedableRng}; | ||
use rand_chacha::ChaCha8Rng; | ||
|
||
use super::*; | ||
|
||
#[test] | ||
fn test_secp256k1_key() -> anyhow::Result<()> { | ||
let mut rng = ChaCha8Rng::seed_from_u64(1337); | ||
|
||
let sk = K1SecretKey::from_bytes(&rng.gen())?; | ||
assert_eq!( | ||
hex::encode(sk.to_bytes()), | ||
"ae491886341a539a1ccfaffcc9c78650ad1adc6270620c882b8d29bf6b9bc4cd" | ||
); | ||
assert_eq!(format!("{sk:?}"), "K1SecretKey(...)"); | ||
|
||
let pk = sk.public_key(); | ||
assert_eq!( | ||
hex::encode(pk.to_bytes()), | ||
"02827cdbbed87e45683d448be2ea15fb72ba3732247bda18474868cf5456123fb4" | ||
); | ||
assert_eq!( | ||
format!("{pk:?}"), | ||
"K1PublicKey(02827cdbbed87e45683d448be2ea15fb72ba3732247bda18474868cf5456123fb4)" | ||
); | ||
assert_eq!( | ||
format!("{pk}"), | ||
"02827cdbbed87e45683d448be2ea15fb72ba3732247bda18474868cf5456123fb4" | ||
); | ||
|
||
let message_hash: [u8; 32] = rng.gen(); | ||
let sig = sk.sign_prehashed(&message_hash)?; | ||
assert_eq!( | ||
hex::encode(sig.to_bytes()), | ||
"6f07897d1d28b8698af5dec5ca06907b1304b227dc9f740b8c4065cf04d5e8653ae66aa17063e7120ee7f22fae54373b35230e259244b90400b65cf00d86c591" | ||
); | ||
assert_eq!( | ||
format!("{sig:?}"), | ||
"K1Signature(6f07897d1d28b8698af5dec5ca06907b1304b227dc9f740b8c4065cf04d5e8653ae66aa17063e7120ee7f22fae54373b35230e259244b90400b65cf00d86c591)" | ||
); | ||
assert_eq!( | ||
format!("{sig}"), | ||
"6f07897d1d28b8698af5dec5ca06907b1304b227dc9f740b8c4065cf04d5e8653ae66aa17063e7120ee7f22fae54373b35230e259244b90400b65cf00d86c591" | ||
); | ||
|
||
assert!(pk.verify_prehashed(&message_hash, &sig)); | ||
|
||
Ok(()) | ||
} | ||
|
||
#[test] | ||
fn test_secp256r1_key() -> anyhow::Result<()> { | ||
let mut rng = ChaCha8Rng::seed_from_u64(1337); | ||
|
||
let sk = R1SecretKey::from_bytes(&rng.gen())?; | ||
assert_eq!( | ||
hex::encode(sk.to_bytes()), | ||
"ae491886341a539a1ccfaffcc9c78650ad1adc6270620c882b8d29bf6b9bc4cd" | ||
); | ||
assert_eq!(format!("{sk:?}"), "R1SecretKey(...)"); | ||
|
||
let pk = sk.public_key(); | ||
assert_eq!( | ||
hex::encode(pk.to_bytes()), | ||
"037dc85102f5eb7867b9580fea8b242c774173e1a47db320c798242d3a7a7579e4" | ||
); | ||
assert_eq!( | ||
format!("{pk:?}"), | ||
"R1PublicKey(037dc85102f5eb7867b9580fea8b242c774173e1a47db320c798242d3a7a7579e4)" | ||
); | ||
assert_eq!( | ||
format!("{pk}"), | ||
"037dc85102f5eb7867b9580fea8b242c774173e1a47db320c798242d3a7a7579e4" | ||
); | ||
|
||
let message_hash: [u8; 32] = rng.gen(); | ||
let sig = sk.sign_prehashed(&message_hash)?; | ||
assert_eq!( | ||
hex::encode(sig.to_bytes()), | ||
"550e83da8cf9b2d407ed093ae213869ebd7ceaea603920f87d535690e52b40537915d8fe3d5a96c87e700c56dc638c32f7a2954f2ba409367d1a132000cc2228" | ||
); | ||
assert_eq!( | ||
format!("{sig:?}"), | ||
"R1Signature(550e83da8cf9b2d407ed093ae213869ebd7ceaea603920f87d535690e52b40537915d8fe3d5a96c87e700c56dc638c32f7a2954f2ba409367d1a132000cc2228)" | ||
); | ||
assert_eq!( | ||
format!("{sig}"), | ||
"550e83da8cf9b2d407ed093ae213869ebd7ceaea603920f87d535690e52b40537915d8fe3d5a96c87e700c56dc638c32f7a2954f2ba409367d1a132000cc2228" | ||
); | ||
|
||
assert!(pk.verify_prehashed(&message_hash, &sig)); | ||
|
||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
mod public_key; | ||
mod secret_key; | ||
mod signature; | ||
|
||
pub use public_key::*; | ||
pub use secret_key::*; | ||
pub use signature::*; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
use std::fmt; | ||
use std::hash::{Hash, Hasher}; | ||
|
||
use chia_sha2::Sha256; | ||
use k256::ecdsa::signature::hazmat::PrehashVerifier; | ||
use k256::ecdsa::{Error, VerifyingKey}; | ||
|
||
use super::K1Signature; | ||
|
||
#[derive(Clone, Copy, PartialEq, Eq)] | ||
pub struct K1PublicKey(pub(crate) VerifyingKey); | ||
|
||
impl Hash for K1PublicKey { | ||
fn hash<H: Hasher>(&self, state: &mut H) { | ||
self.to_bytes().hash(state); | ||
} | ||
} | ||
|
||
impl fmt::Debug for K1PublicKey { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
write!(f, "K1PublicKey({self})") | ||
} | ||
} | ||
|
||
impl fmt::Display for K1PublicKey { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
write!(f, "{}", hex::encode(self.to_bytes())) | ||
} | ||
} | ||
|
||
#[cfg(feature = "arbitrary")] | ||
impl<'a> arbitrary::Arbitrary<'a> for K1PublicKey { | ||
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> { | ||
Self::from_bytes(&u.arbitrary()?).map_err(|_| arbitrary::Error::IncorrectFormat) | ||
} | ||
} | ||
|
||
impl K1PublicKey { | ||
pub const SIZE: usize = 33; | ||
|
||
pub fn to_bytes(&self) -> [u8; Self::SIZE] { | ||
self.0.to_encoded_point(true).as_ref().try_into().unwrap() | ||
} | ||
|
||
pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> Result<Self, Error> { | ||
Ok(Self(VerifyingKey::from_sec1_bytes(bytes)?)) | ||
} | ||
|
||
pub fn verify_prehashed(&self, message_hash: &[u8; 32], signature: &K1Signature) -> bool { | ||
self.0.verify_prehash(message_hash, &signature.0).is_ok() | ||
} | ||
|
||
pub fn fingerprint(&self) -> u32 { | ||
let mut hasher = Sha256::new(); | ||
hasher.update(self.to_bytes()); | ||
let hash = hasher.finalize(); | ||
u32::from_be_bytes(hash[0..4].try_into().unwrap()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
use std::{ | ||
fmt, | ||
hash::{Hash, Hasher}, | ||
}; | ||
|
||
use k256::ecdsa::{Error, SigningKey}; | ||
|
||
use super::{K1PublicKey, K1Signature}; | ||
|
||
#[derive(Clone, PartialEq, Eq)] | ||
pub struct K1SecretKey(SigningKey); | ||
|
||
impl Hash for K1SecretKey { | ||
fn hash<H: Hasher>(&self, state: &mut H) { | ||
self.to_bytes().hash(state); | ||
} | ||
} | ||
|
||
impl fmt::Debug for K1SecretKey { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
write!(f, "K1SecretKey(...)") | ||
} | ||
} | ||
|
||
#[cfg(feature = "arbitrary")] | ||
impl<'a> arbitrary::Arbitrary<'a> for K1SecretKey { | ||
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> { | ||
Self::from_bytes(&u.arbitrary()?).map_err(|_| arbitrary::Error::IncorrectFormat) | ||
} | ||
} | ||
|
||
impl K1SecretKey { | ||
pub fn to_bytes(&self) -> [u8; 32] { | ||
self.0.to_bytes().into() | ||
} | ||
|
||
pub fn from_bytes(bytes: &[u8; 32]) -> Result<Self, Error> { | ||
Ok(Self(SigningKey::from_bytes(bytes.into())?)) | ||
} | ||
|
||
pub fn public_key(&self) -> K1PublicKey { | ||
K1PublicKey(*self.0.verifying_key()) | ||
} | ||
|
||
pub fn sign_prehashed(&self, message_hash: &[u8; 32]) -> Result<K1Signature, Error> { | ||
Ok(K1Signature( | ||
self.0.sign_prehash_recoverable(message_hash)?.0, | ||
)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
use std::{ | ||
fmt, | ||
hash::{Hash, Hasher}, | ||
}; | ||
|
||
use k256::ecdsa::{Error, Signature}; | ||
|
||
#[derive(Clone, Copy, PartialEq, Eq)] | ||
pub struct K1Signature(pub(crate) Signature); | ||
|
||
impl Hash for K1Signature { | ||
fn hash<H: Hasher>(&self, state: &mut H) { | ||
self.to_bytes().hash(state); | ||
} | ||
} | ||
|
||
impl fmt::Debug for K1Signature { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
write!(f, "K1Signature({self})") | ||
} | ||
} | ||
|
||
impl fmt::Display for K1Signature { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
write!(f, "{}", hex::encode(self.to_bytes())) | ||
} | ||
} | ||
|
||
#[cfg(feature = "arbitrary")] | ||
impl<'a> arbitrary::Arbitrary<'a> for K1Signature { | ||
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> { | ||
Self::from_bytes(&u.arbitrary()?).map_err(|_| arbitrary::Error::IncorrectFormat) | ||
} | ||
} | ||
|
||
impl K1Signature { | ||
pub const SIZE: usize = 64; | ||
|
||
pub fn to_bytes(&self) -> [u8; Self::SIZE] { | ||
self.0.to_bytes().into() | ||
} | ||
|
||
pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> Result<Self, Error> { | ||
Ok(Self(Signature::from_slice(bytes)?)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
mod public_key; | ||
mod secret_key; | ||
mod signature; | ||
|
||
pub use public_key::*; | ||
pub use secret_key::*; | ||
pub use signature::*; |
Oops, something went wrong.