Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
890b151
switch to k256
drHuangMHT Feb 27, 2025
1904fe4
Merge branch 'master' into identity-k256
dariusc93 Mar 2, 2025
e273c8b
Merge branch 'master' into identity-k256
dariusc93 Mar 7, 2025
0910715
Merge branch 'master' into identity-k256
jxs Mar 14, 2025
e25cfa8
fix verify
drHuangMHT Mar 17, 2025
18b53f0
changelog and manifest
drHuangMHT Mar 17, 2025
020d37c
Merge branch 'master' into identity-k256
drHuangMHT Mar 17, 2025
7b69d77
workspace version bump
drHuangMHT Mar 17, 2025
2bc4768
remove sec1 and sha2 as direct dependency
drHuangMHT Mar 18, 2025
91e624c
optimize copy and clone
drHuangMHT Mar 18, 2025
f72a827
derive Copy for Publickey, forward trait impl
drHuangMHT Mar 21, 2025
4e1512d
Merge branch 'master' into identity-k256
drHuangMHT Mar 21, 2025
90e0328
revert trait impl changes
drHuangMHT Mar 28, 2025
0483ec9
Merge branch 'master' into identity-k256
drHuangMHT Apr 1, 2025
97aaae6
replace sign_digest_recoverable with Signer::sign
drHuangMHT Apr 10, 2025
20e5539
Merge branch 'master' into identity-k256
drHuangMHT Apr 10, 2025
11e5308
manifest formatting and bring back sha2
drHuangMHT Apr 10, 2025
a116df6
remove default feature
drHuangMHT Apr 10, 2025
152cebf
correctly document verify_hash behaviour
drHuangMHT Apr 23, 2025
3b5cce8
remove generic_array from direct dependencies
drHuangMHT Apr 23, 2025
6a32f28
Merge branch 'master' into identity-k256
drHuangMHT Apr 23, 2025
38d652c
Merge branch 'master' into identity-k256
jxs Apr 28, 2025
491dcef
Merge branch 'master' into identity-k256
mergify[bot] Apr 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion identity/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ asn1_der = { version = "0.7.6", optional = true }
bs58 = { version = "0.5.1", optional = true }
ed25519-dalek = { version = "2.1", optional = true }
hkdf = { version = "0.12.4", optional = true }
k256 = { version = "0.13.4", optional = true, features = ["ecdsa", "arithmetic", "sha256"] }
libsecp256k1 = { version = "0.7.0", optional = true }
tracing = { workspace = true }
multihash = { version = "0.19.1", optional = true }
Expand All @@ -32,12 +33,13 @@ zeroize = { version = "1.8", optional = true }
ring = { workspace = true, features = ["alloc", "std"], optional = true }

[features]
secp256k1 = ["dep:libsecp256k1", "dep:asn1_der", "dep:sha2", "dep:hkdf", "dep:zeroize"]
secp256k1 = ["dep:k256", "dep:libsecp256k1","dep:sec1", "dep:asn1_der", "dep:sha2", "dep:hkdf", "dep:zeroize"]
ecdsa = ["dep:p256", "dep:zeroize", "dep:sec1", "dep:sha2", "dep:hkdf"]
rsa = ["dep:ring", "dep:asn1_der", "dep:rand", "dep:zeroize"]
ed25519 = ["dep:ed25519-dalek", "dep:zeroize", "dep:sha2", "dep:hkdf"]
peerid = ["dep:multihash", "dep:bs58", "dep:thiserror", "dep:sha2", "dep:hkdf"]
rand = ["dep:rand", "ed25519-dalek?/rand_core"]
default = ["rand", "secp256k1"]

[dev-dependencies]
quickcheck = { workspace = true }
Expand Down
77 changes: 52 additions & 25 deletions identity/src/secp256k1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
use core::{cmp, fmt, hash};

use asn1_der::typed::{DerDecodable, Sequence};
use libsecp256k1::{Message, Signature};
use k256::ecdsa::Signature;
use sha2::{Digest as ShaDigestTrait, Sha256};
use zeroize::Zeroize;

Expand Down Expand Up @@ -65,7 +65,7 @@
/// Promote a Secp256k1 secret key into a keypair.
impl From<SecretKey> for Keypair {
fn from(secret: SecretKey) -> Keypair {
let public = PublicKey(libsecp256k1::PublicKey::from_secret_key(&secret.0));
let public = PublicKey(secret.0.verifying_key().clone());

Check failure on line 68 in identity/src/secp256k1.rs

View workflow job for this annotation

GitHub Actions / clippy (beta)

using `clone` on type `VerifyingKey<Secp256k1>` which implements the `Copy` trait

Check failure on line 68 in identity/src/secp256k1.rs

View workflow job for this annotation

GitHub Actions / clippy (1.83.0)

using `clone` on type `VerifyingKey<Secp256k1>` which implements the `Copy` trait
Keypair { secret, public }
}
}
Expand All @@ -79,7 +79,7 @@

/// A Secp256k1 secret key.
#[derive(Clone)]
pub struct SecretKey(libsecp256k1::SecretKey);
pub struct SecretKey(k256::ecdsa::SigningKey);

impl fmt::Debug for SecretKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Expand All @@ -91,7 +91,7 @@
/// Generate a new random Secp256k1 secret key.
#[cfg(feature = "rand")]
pub fn generate() -> SecretKey {
SecretKey(libsecp256k1::SecretKey::random(&mut rand::thread_rng()))
SecretKey(k256::ecdsa::SigningKey::random(&mut rand::thread_rng()))
}

/// Create a secret key from a byte slice, zeroing the slice on success.
Expand All @@ -101,7 +101,7 @@
/// Note that the expected binary format is the same as `libsecp256k1`'s.
pub fn try_from_bytes(mut sk: impl AsMut<[u8]>) -> Result<SecretKey, DecodingError> {
let sk_bytes = sk.as_mut();
let secret = libsecp256k1::SecretKey::parse_slice(&*sk_bytes)
let secret = k256::ecdsa::SigningKey::from_slice(&sk_bytes)

Check failure on line 104 in identity/src/secp256k1.rs

View workflow job for this annotation

GitHub Actions / clippy (beta)

this expression creates a reference which is immediately dereferenced by the compiler

Check failure on line 104 in identity/src/secp256k1.rs

View workflow job for this annotation

GitHub Actions / clippy (1.83.0)

this expression creates a reference which is immediately dereferenced by the compiler
.map_err(|e| DecodingError::failed_to_parse("parse secp256k1 secret key", e))?;
sk_bytes.zeroize();
Ok(SecretKey(secret))
Expand Down Expand Up @@ -131,30 +131,30 @@
///
/// [RFC3278]: https://tools.ietf.org/html/rfc3278#section-8.2
pub fn sign(&self, msg: &[u8]) -> Vec<u8> {
let generic_array = Sha256::digest(msg);
let digest = Sha256::new_with_prefix(msg);

// FIXME: Once `generic-array` hits 1.0, we should be able to just use `Into` here.
let mut array = [0u8; 32];
array.copy_from_slice(generic_array.as_slice());

let message = Message::parse(&array);

libsecp256k1::sign(&message, &self.0)
self.0
.sign_digest_recoverable(digest)
.unwrap()
.0
.serialize_der()
.as_ref()
.into()
.to_der()
.to_bytes()
.to_vec()
}

/// Returns the raw bytes of the secret key.
pub fn to_bytes(&self) -> [u8; 32] {
self.0.serialize()
let encoded_point = self.0.to_bytes().to_vec();
debug_assert!(encoded_point.len() == 32);
let mut array: [u8; 32] = [0u8; 32];
array.copy_from_slice(&encoded_point);
array
}
}

/// A Secp256k1 public key.
#[derive(Eq, Clone)]
pub struct PublicKey(libsecp256k1::PublicKey);
pub struct PublicKey(k256::ecdsa::VerifyingKey);

impl fmt::Debug for PublicKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Expand Down Expand Up @@ -198,26 +198,37 @@

/// Verify the Secp256k1 DER-encoded signature on a raw 256-bit message using the public key.
pub fn verify_hash(&self, msg: &[u8], sig: &[u8]) -> bool {
Message::parse_slice(msg)
.and_then(|m| Signature::parse_der(sig).map(|s| libsecp256k1::verify(&m, &s, &self.0)))
// Cannot verify raw hash because `verify_digest` needs a fixed-size input
use k256::ecdsa::hazmat::VerifyPrimitive;
let digest = Sha256::new_with_prefix(msg);
Signature::from_der(sig)
.map(|s| self.0.as_affine().verify_digest(digest, &s).is_ok())
.unwrap_or(false)
}

/// Convert the public key to a byte buffer in compressed form, i.e. with one coordinate
/// represented by a single bit.
pub fn to_bytes(&self) -> [u8; 33] {
self.0.serialize_compressed()
let encoded_point = self.0.to_encoded_point(true);
debug_assert!(encoded_point.as_bytes().len() == 33);
let mut array: [u8; 33] = [0u8; 33];
array.copy_from_slice(encoded_point.as_bytes());
array
}

/// Convert the public key to a byte buffer in uncompressed form.
pub fn to_bytes_uncompressed(&self) -> [u8; 65] {
self.0.serialize()
let encoded_point = self.0.to_encoded_point(false);
debug_assert!(encoded_point.as_bytes().len() == 65);
let mut array: [u8; 65] = [0u8; 65];
array.copy_from_slice(encoded_point.as_bytes());
array
}

/// Decode a public key from a byte slice in the format produced
/// by `encode`.
pub fn try_from_bytes(k: &[u8]) -> Result<PublicKey, DecodingError> {
libsecp256k1::PublicKey::parse_slice(k, Some(libsecp256k1::PublicKeyFormat::Compressed))
k256::ecdsa::VerifyingKey::from_sec1_bytes(k)
.map_err(|e| DecodingError::failed_to_parse("secp256k1 public key", e))
.map(PublicKey)
}
Expand All @@ -232,9 +243,25 @@
fn secp256k1_secret_from_bytes() {
let sk1 = SecretKey::generate();
let mut sk_bytes = [0; 32];
sk_bytes.copy_from_slice(&sk1.0.serialize()[..]);
sk_bytes.copy_from_slice(&sk1.to_bytes()[..]);
let sk2 = SecretKey::try_from_bytes(&mut sk_bytes).unwrap();
assert_eq!(sk1.0.serialize(), sk2.0.serialize());
assert_eq!(sk1.to_bytes(), sk2.to_bytes());
assert_eq!(sk_bytes, [0; 32]);
}

#[test]
fn compatibility() {
// Showcases compatibility between the two library.
// Will not be included when merging.
let libsecp256k1_key = libsecp256k1::SecretKey::random(&mut rand::thread_rng());
let k256_key = SecretKey::try_from_bytes(libsecp256k1_key.serialize()).expect("keys to be interchangable on both lib");
assert_eq!(libsecp256k1_key.serialize(), k256_key.to_bytes());

let k256_pubkey = Keypair::from(k256_key.clone()).public().clone();
let libsecp256k1_pubkey = libsecp256k1::PublicKey::from_secret_key(&libsecp256k1_key);
PublicKey::try_from_bytes(&libsecp256k1_pubkey.serialize()).expect("parsing to succeed");
PublicKey::try_from_bytes(&libsecp256k1_pubkey.serialize_compressed()).expect("parsing to succeed");
assert_eq!(libsecp256k1_pubkey.serialize_compressed(), k256_pubkey.to_bytes());
assert_eq!(libsecp256k1_pubkey.serialize(), k256_pubkey.to_bytes_uncompressed());
}
}
Loading