Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ECDSA signatures over NIST-P256 to aptos-crypto #9594

Merged
merged 51 commits into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
69f3353
added p256 dep
mstraka100 Jul 18, 2023
e3ad021
cargo check passes
mstraka100 Jul 18, 2023
f85cecf
progress
mstraka100 Jul 19, 2023
69257da
progress
mstraka100 Jul 19, 2023
e836fb1
fewer errors
mstraka100 Jul 20, 2023
c3bc8fe
update zeroize subdependency
mstraka100 Jul 20, 2023
2db972e
edit
mstraka100 Jul 21, 2023
3de0714
compiles
mstraka100 Aug 3, 2023
3b94993
edits
mstraka100 Aug 3, 2023
76141ac
started unit tests
mstraka100 Aug 8, 2023
a6c0216
tests pass
mstraka100 Aug 8, 2023
d34e8c3
edits
mstraka100 Aug 9, 2023
e6d9500
merge with main
mstraka100 Aug 9, 2023
87f27f2
tests pass
mstraka100 Aug 9, 2023
5dbc2c4
edits
mstraka100 Aug 9, 2023
ebf8bec
edits
mstraka100 Aug 10, 2023
b760539
smaller Cargo.lock
mstraka100 Aug 10, 2023
9ab4170
small edits
mstraka100 Aug 11, 2023
82ec02c
Merge branch 'main' into michael/P256
mstraka100 Aug 11, 2023
e9e1846
Update workflows (#9650)
gedigi Aug 14, 2023
195e5d9
merge with main
mstraka100 Sep 26, 2023
a397f50
fix malleability test
mstraka100 Sep 28, 2023
718a162
edit
mstraka100 Sep 28, 2023
47f391a
edits
mstraka100 Sep 28, 2023
f5430fd
change p256 to ecdsa_p256
mstraka100 Sep 28, 2023
b1c8a9d
add comment for encoding
mstraka100 Sep 28, 2023
b268823
move Sealed
mstraka100 Sep 28, 2023
f42969f
improve making signatures cannoical, fix mod.rs example
mstraka100 Sep 28, 2023
60d85bf
merge with main
mstraka100 Sep 29, 2023
f2a6a45
small edits
mstraka100 Oct 2, 2023
6764842
merge with main
mstraka100 Oct 17, 2023
0236ee0
comment edit
mstraka100 Oct 17, 2023
bddb158
serialization comments
mstraka100 Oct 17, 2023
8d41c09
Merge branch 'main' into michael/P256
mstraka100 Oct 17, 2023
18f23f6
linter
mstraka100 Oct 17, 2023
4f2a7d6
Merge branch 'michael/P256' of github.com:aptos-labs/aptos-core into …
mstraka100 Oct 17, 2023
8875827
edits
mstraka100 Oct 17, 2023
f93f0f4
fix random private key
mstraka100 Oct 18, 2023
06196fa
Merge branch 'main' into michael/P256
mstraka100 Oct 18, 2023
26dafbf
endianness comments
mstraka100 Oct 18, 2023
f0e9bab
Merge branch 'michael/P256' of github.com:aptos-labs/aptos-core into …
mstraka100 Oct 18, 2023
f89a60e
change endianness of private key generation
mstraka100 Oct 18, 2023
689783f
comment removed
mstraka100 Oct 18, 2023
a1506e2
added unit test for private key endianness
mstraka100 Oct 18, 2023
0f53a2d
linter
mstraka100 Oct 18, 2023
c0c4db1
Merge branch 'main' into michael/P256
mstraka100 Oct 18, 2023
50fca74
sort dependencies
mstraka100 Oct 18, 2023
23fe9eb
Merge branch 'michael/P256' of github.com:aptos-labs/aptos-core into …
mstraka100 Oct 18, 2023
e1f751d
Merge branch 'main' into michael/P256
mstraka100 Oct 18, 2023
589c1f4
Merge branch 'main' into michael/P256
mstraka100 Oct 18, 2023
9bc6596
Merge branch 'main' into michael/P256
mstraka100 Oct 18, 2023
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
255 changes: 219 additions & 36 deletions Cargo.lock

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,10 @@ num-traits = "0.2.15"
once_cell = "1.10.0"
ouroboros = "0.15.6"
owo-colors = "3.5.0"
# TODO: Do we actually use serde?
mstraka100 marked this conversation as resolved.
Show resolved Hide resolved
p256 = { version = "0.13.2" } #, features = ["serde"] }
mstraka100 marked this conversation as resolved.
Show resolved Hide resolved
signature = "2.1.0"
sec1 = "0.7.0"
parking_lot = "0.12.0"
paste = "1.0.7"
pbjson = "0.5.1"
Expand Down Expand Up @@ -648,7 +652,7 @@ walkdir = "2.3.3"
warp = { version = "0.3.5", features = ["tls"] }
warp-reverse-proxy = "1.0.0"
which = "4.2.5"
x25519-dalek = "1.2.0"
x25519-dalek = { git = "https://github.com/aptos-labs/x25519-dalek" }
mstraka100 marked this conversation as resolved.
Show resolved Hide resolved
mstraka100 marked this conversation as resolved.
Show resolved Hide resolved

# MOVE DEPENDENCIES
move-abigen = { path = "third_party/move/move-prover/move-abigen" }
Expand Down
2 changes: 2 additions & 0 deletions crates/aptos-crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ static_assertions = { workspace = true }
thiserror = { workspace = true }
tiny-keccak = { workspace = true }
x25519-dalek = { workspace = true }
p256 = { workspace = true }
signature = { workspace = true }

[dev-dependencies]
ark-bls12-381 = { workspace = true }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Seeds for failure cases proptest has generated in the past. It is
# automatically read and these particular cases re-run before any
# novel cases are generated.
#
# It is recommended to check this file in to source control so that
# everyone who runs the test benefits from these saved cases.
cc 539e60874bc331d8b4664a1cc6cc906579f5fb3547428adbc6cbeea2a28e31ea # shrinks to keypair = 2023e235b5da887e01fab303e0cfffc959952c02d787bb2dfca7163fd05e0205ca41046a6b82adba722f31d51689afba24566a94ec37fc32d0ff520a5e6287fe75cd8271ea1489a22ee93b8568526fa93589ece0301fb9312cde1ef2778feaeb5c9168
cc 84b533dd1f5cd9430aa1d8428646fbb73e1ee314be8317faa448117c680c1606 # shrinks to keypair = 2071a0cbcf6abcc57ac335c7b93ba478ed6b3eec8fb2c03761ef5f872696b38d334104cd791851816b4d18c8da299442a924fd8a7b86a5093b026d0052820560ece21df8149f42fed9460a04aeedc55630f4fcc08744fee4eaf9a3b6025ffc2b5cd73a
cc 1f0606f82d939fa43037a1ae1e104a6af5fcb17495a60acb727bc4d434c7ebc5 # shrinks to message = TestAptosCrypto("𐛚"), keypair = 20fca559cbb362e02384a4b09da66e5704131fcae30f0d4c4f7280bd7b6dc0f1ae4104ffa0b95e042e600449c5512db882b8698038149b3774254b2f554a289a99fe913c4a16bde282e3c27303597a9f63f7a6741efba82535cc42f65e7377224e71a9
cc aa831748c8a3c9291a059cf7f146c8953a6d40ca772d417bc048a56d113ef947 # shrinks to x = 2114806089028681516, keypair = 200c125a0ee13b47d0248b551ad5a60bfd282d7c3189f9c411bc403765cad0c9ef4104dd6ad49f1f4162d22aba9c60f06ed68318f6538247b7fdfa99f05bd91ebf98daad39422a5d438ede4f0425acf20066a7aa53a2f171e885cf5365047ec4dbd09b
cc 482c9720ccab5338952c69ef05921d0a515c4cc1b412940383104cb580160fcf # shrinks to message = TestAptosCrypto(""), keypairs = [208b5526f3f5713933435a7cf9845720731661a848a100e35c90e0b39bf2b5951841040fd99726d76fae26119a5944808f344c2a78827b191b4762a8da4c4af78a1e2c39dfbb0cd47daf67781fd4844b706ffafc661fc9754a626b5820db299b6d7bce, 20afea7aa686b48be0949edc4eeffdaaac21d809a6b4bc2b04ea3ef289baf2b0d441041640a5468342d07b90246e2e3dc7b0c42c040f72e43dfc23e56531e3605190e1744632e74189d926f753801ae7df2951f4980e1bb8991c2cc9bdde48a79bcb3c, 205362b2d8ee3e3a45610b6d6827be860cef542fa6cfd04c5a3179742acb44201e4104a7c5b9892202dedbbc0c62da87ddf87bc353f6d8fc504c32aaab0475906165864edc3172aae096255787c8cd771a7b0a9a50cf00ffe6983271cdd7f55e209283, 20dc03d77ece8f3c465eee9251debed8caacf085c1d9db798f180dbf3ad2cb466741045b10ee95d307da8855743c537f9220caa1b9c46fa38dd1cea04bb376e034e3a4a68d227dbd81529cb794cf518ab45e64bb03bf4ad17fba5b5895ad463f91432b, 20bf72e06870b080ecb993f582b890e1622e0d74e675f5e861b5fa4678f3d520624104d6524bfb7ef7d0537b8430a923e6ce0d0c601725b611a9c845a4144e522a45079da32127ad93903c2243219458f61d4faa84165e936e9a1e9e3eb197a5f8cbc3, 200117adf8da18d89d85920057dfe74b8fc984aae97f77b98f721473c3ebbcc63e4104d24e193cd8507b68295dd68df185c4ebac4b976f43dd9af8bd31bc2290211bd9ab455c8ba1d9d0760ff4d896e90399ff175bb731d355084e01901805caae397b, 204771b8a594584e6bc2d74fdd0889c50074efdcb9db77ec3868736cda0244ab984104683ec98cd47981a6c9fd4dff14d834aff1e6d670f0de4c622ba6d2a733f1970b296fac0d03ab9afc1e11d6667259199705897befd74b4307d32fe7615c234582, 201a01076284a879796d503e73e8865b0aea6cb3d41d3189b121ab84933a86166b41045b59bc55ab9f39404c59692b0f37bbf856c26959c1b9d3265ae2d585eb9aebae950f39d0ae1a0811cc8bc04782d53ee3cb12ab4511576b58a12a689a3780e303, 20829bcb79633ec7a761110499bb6f1b100880e489a27bb2e2e10a3771dbbc35294104cfa6f78c73a5026508819797d2e725c045eef7ee8217b95dbca078e46bc624024fb8287c5e23a8da0e1bc7c42d54323f69c1fd98e80310287f98642af2917dbe, 2088336ef311e8adc58a77bf41dbe24a1cd3a6a125302aa6f7ef4a489e12dc6622410450cf7c8ee6778b6e92ab0e520f1c4cb6ff7df86772e8c1c9f33066193caed9bc305bac4fa8e3b6c187975608d3ccdbaaa95dc1baa732e89e848e2b049dd3e6e3]
cc 19487dcfe0f0e04e72c2065fb69c14ebd14adb8ea6ff9d70e1985ea325538fa1 # shrinks to msg = [65, 247, 56, 10, 230, 149, 73, 158, 144, 90, 166, 195, 70, 177, 6, 252, 86, 41, 56, 120, 40, 240, 224, 40, 127, 4, 219, 180, 253, 43, 93, 10, 217, 145, 155, 191, 132, 87, 87, 14, 169, 89, 206, 34, 39, 231, 149, 131, 204, 89, 79, 195, 9, 42, 62, 54, 63, 153, 183, 218, 198, 40, 131, 184, 228, 171, 68, 78, 46, 247, 38, 61, 109, 181, 46, 231, 174, 225, 187, 185, 71, 11, 197, 236, 239], keypair = 208f6aa70033d75374c9a898a184676eea8aa68060bdd01b889f2e046ee96d8cbb4104fe66b01f689f42071baf915ae70e75f75349b78fa320abe6c8109a88f678f04de9f5570372371d88dba0d377b37f663f2338aa63f21ca865d7e9ba56d71e66c6
cc c0d5a00a2bfb7c18d4188babea8347983ad0b3027e5e4fd3c512ca1bab1bf417 # shrinks to message = TestAptosCrypto("&\"��9&𑥕\u{1d1ac}�₡"), keypair = 20f2ec360701a1027c67f237a8ced9e21d6ae058096435ee0032d5433c9997fca1410406928fcae4da8fbc63dcea3d60f4951fda1a9e17bc59ffde55e0d65de32684d75aeed4177885916e48713179a656e50a5b107372b53d10d25c885bbac1c270be
cc 8b646e2a1ab153933cafb58c017e87f0151c4f7c8438ca74ff34b0b3192f33aa # shrinks to message = TestAptosCrypto("Iೲೀ𑤷ລ\\<\u{1ac1}𖮈¥𞺧?xÁ𛅕T.$s:ዃ/?Ὅ\\<*יּ𐭜𐠈"), keypair = 20e83972f72633e4acce41281cd87c99267966dd0bae421dde66f5e1e8ffdfc78d4104af06935d9a454ed97a0471a2338a2273619cb094774a696bc9faeabcbd067f46cdf3ea6fd7e31375a779c54053c3b111ed5ebd3ae719b3611141b5c97b8033d5
1 change: 1 addition & 0 deletions crates/aptos-crypto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub mod test_utils;
pub mod traits;
pub mod validatable;
pub mod x25519;
pub mod p256;

#[cfg(test)]
mod unit_tests;
Expand Down
63 changes: 63 additions & 0 deletions crates/aptos-crypto/src/p256/mod.rs
mstraka100 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright © Aptos Foundation
// SPDX-License-Identifier: Apache-2.0
//! This module provides an API for the ECDSA signature scheme over the NIST-P256 curve as defined in [NIST SP 800-186](https://csrc.nist.gov/publications/detail/sp/800-186/final).
//!
//! Signature verification also checks and rejects non-canonical signatures. Signing is guaranteed
//! to output the canonical signature which passes this module's verification.
//!
//! # Examples
//!
//! ```
//! use aptos_crypto_derive::{CryptoHasher, BCSCryptoHash};
//! use aptos_crypto::{
//! p256::*,
//! traits::{Signature, SigningKey, Uniform},
//! test_utils::KeyPair
//! };
//! use rand::{rngs::StdRng, SeedableRng};
//! use rand_core::OsRng;
//! use serde::{Serialize, Deserialize};
//!
//! #[derive(Serialize, Deserialize, CryptoHasher, BCSCryptoHash)]
//! pub struct TestCryptoDocTest(String);
//! let message = TestCryptoDocTest("Test message".to_string());
//!
//! let mut rng = OsRng;
//! let kp = KeyPair::<P256PrivateKey, P256PublicKey>::generate(&mut rng);
//!
//! let signature = kp.private_key.sign(&message).unwrap();
//! assert!(signature.verify(&message, &kp.public_key).is_ok());
//! ```

/// The length in bytes of the P256PrivateKey
pub const P256_PRIVATE_KEY_LENGTH: usize = 32;
/// The length in bytes of the P256PublicKey
pub const P256_PUBLIC_KEY_LENGTH: usize = 65;
/// The length in bytes of the P256Signature
pub const P256_SIGNATURE_LENGTH: usize = 64;

/// The order of P256 as defined in [NIST SP 800-186](https://csrc.nist.gov/publications/detail/sp/800-186/final), minus one.
const ORDER_MINUS_ONE: [u8; 32] = [
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84, 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x50,
];

/// The value (q-1)/2, where q is the order of P256 as defined in [NIST SP 800-186](https://csrc.nist.gov/publications/detail/sp/800-186/final).
/// Computed with the following SageMath code:
///
/// # Curve order
/// qq = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551
/// q_half = (qq-1)/2
pub const ORDER_HALF: [u8; 32] = [
0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDE, 0x73, 0x7D, 0x56, 0xD3, 0x8B, 0xCF, 0x42, 0x79, 0xDC, 0xE5, 0x61, 0x7E, 0x31, 0x92, 0xA8,
];

pub mod p256_keys;
pub mod p256_sigs;

#[cfg(any(test, feature = "fuzzing"))]
pub use p256_keys::keypair_strategy;
pub use p256_keys::{
P256PrivateKey, P256PrivateKey as PrivateKey, P256PublicKey,
P256PublicKey as PublicKey,
};
pub use p256_sigs::{P256Signature, P256Signature as Signature};
270 changes: 270 additions & 0 deletions crates/aptos-crypto/src/p256/p256_keys.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
// Copyright © Aptos Foundation
// SPDX-License-Identifier: Apache-2.0

//! This file implements traits for P256 private keys and public keys.

#[cfg(any(test, feature = "fuzzing"))]
use crate::test_utils::{self, KeyPair};
use crate::{
p256::{P256Signature, P256_PRIVATE_KEY_LENGTH, P256_PUBLIC_KEY_LENGTH},
hash::CryptoHash,
traits::*,
};
use p256::ecdsa::signature::Signer;
use p256;
use aptos_crypto_derive::{DeserializeKey, SerializeKey, SilentDebug, SilentDisplay};
use core::convert::TryFrom;
#[cfg(any(test, feature = "fuzzing"))]
use proptest::prelude::*;
use serde::Serialize;
use std::fmt;

/// A P256 private key
#[derive(DeserializeKey, SerializeKey, SilentDebug, SilentDisplay)]
pub struct P256PrivateKey(pub(crate) p256::ecdsa::SigningKey);

impl private::Sealed for P256PrivateKey {}

#[cfg(feature = "assert-private-keys-not-cloneable")]
static_assertions::assert_not_impl_any!(P256PrivateKey: Clone);

#[cfg(any(test, feature = "cloneable-private-keys"))]
impl Clone for P256PrivateKey {
fn clone(&self) -> Self {
let serialized: &[u8] = &(self.to_bytes());
P256PrivateKey::try_from(serialized).unwrap()
}
}

/// A P256 public key
#[derive(DeserializeKey, Clone, SerializeKey)]
pub struct P256PublicKey(pub(crate) p256::ecdsa::VerifyingKey);

impl private::Sealed for P256PublicKey {}
mstraka100 marked this conversation as resolved.
Show resolved Hide resolved

impl P256PrivateKey {
/// The length of the P256PrivateKey
pub const LENGTH: usize = P256_PRIVATE_KEY_LENGTH;

/// Serialize a P256PrivateKey.
pub fn to_bytes(&self) -> [u8; P256_PRIVATE_KEY_LENGTH] {
self.0.to_bytes().into()
}

/// Deserialize an P256PrivateKey without any validation checks apart from expected key size.
fn from_bytes_unchecked(
mstraka100 marked this conversation as resolved.
Show resolved Hide resolved
bytes: &[u8],
) -> std::result::Result<P256PrivateKey, CryptoMaterialError> {
match p256::ecdsa::SigningKey::from_slice(bytes) {
Ok(p256_secret_key) => Ok(P256PrivateKey(p256_secret_key)),
Err(_) => Err(CryptoMaterialError::DeserializationError),
}
}

/// Private function aimed at minimizing code duplication between sign
/// methods of the SigningKey implementation. This should remain private.
fn sign_arbitrary_message(&self, message: &[u8]) -> P256Signature {
let secret_key: &p256::ecdsa::SigningKey = &self.0;
mstraka100 marked this conversation as resolved.
Show resolved Hide resolved
let sig = P256Signature(secret_key.sign(message.as_ref()));
let canonical_sig = P256Signature::make_canonical(&sig);

canonical_sig
}
}

impl P256PublicKey {
/// Serialize a P256PublicKey.
pub fn to_bytes(&self) -> [u8; P256_PUBLIC_KEY_LENGTH] {
// The RustCrypto P256 `to_sec1_bytes` call here should never return an array of the wrong length and cause a panic
alinush marked this conversation as resolved.
Show resolved Hide resolved
(*self.0.to_sec1_bytes()).try_into().unwrap()
}

/// Deserialize a P256PublicKey, checking expected key size
/// and that it is a valid curve point.
pub(crate) fn from_bytes_unchecked(
bytes: &[u8],
) -> std::result::Result<P256PublicKey, CryptoMaterialError> {
match p256::ecdsa::VerifyingKey::from_sec1_bytes(bytes) {
Ok(p256_public_key) => Ok(P256PublicKey(p256_public_key)),
Err(_) => Err(CryptoMaterialError::DeserializationError),
}
}
}

///////////////////////
// PrivateKey Traits //
///////////////////////

impl PrivateKey for P256PrivateKey {
type PublicKeyMaterial = P256PublicKey;
}

impl SigningKey for P256PrivateKey {
type SignatureMaterial = P256Signature;
type VerifyingKeyMaterial = P256PublicKey;

fn sign<T: CryptoHash + Serialize>(
&self,
message: &T,
) -> Result<P256Signature, CryptoMaterialError> {
Ok(P256PrivateKey::sign_arbitrary_message(
self,
signing_message(message)?.as_ref(),
))
}

#[cfg(any(test, feature = "fuzzing"))]
fn sign_arbitrary_message(&self, message: &[u8]) -> P256Signature {
P256PrivateKey::sign_arbitrary_message(self, message)
}
}

impl Uniform for P256PrivateKey {
fn generate<R>(rng: &mut R) -> Self
where
R: ::rand::RngCore + ::rand::CryptoRng + ::rand_core::CryptoRng + ::rand_core::RngCore,
{
let mut bytes: [u8; P256_PRIVATE_KEY_LENGTH] = Default::default();
mstraka100 marked this conversation as resolved.
Show resolved Hide resolved
rng.fill_bytes(&mut bytes);
P256PrivateKey(p256::ecdsa::SigningKey::from_slice(&bytes[..]).unwrap())
}
}

impl PartialEq<Self> for P256PrivateKey {
fn eq(&self, other: &Self) -> bool {
self.to_bytes() == other.to_bytes()
}
}

impl Eq for P256PrivateKey {}

// We could have a distinct kind of validation for the PrivateKey: e.g., checking the derived
// PublicKey is valid?
impl TryFrom<&[u8]> for P256PrivateKey {
type Error = CryptoMaterialError;

/// Deserialize a P256PrivateKey. This method will check for private key validity: i.e.,
/// correct key length.
fn try_from(bytes: &[u8]) -> std::result::Result<P256PrivateKey, CryptoMaterialError> {
// Note that the only requirement is that the size of the key is 32 bytes, something that
// is already checked during deserialization of p256::ecdsa::SigningKey
P256PrivateKey::from_bytes_unchecked(bytes)
}
}

impl Length for P256PrivateKey {
fn length(&self) -> usize {
Self::LENGTH
}
}

impl ValidCryptoMaterial for P256PrivateKey {
fn to_bytes(&self) -> Vec<u8> {
self.to_bytes().to_vec()
}
}

impl Genesis for P256PrivateKey {
fn genesis() -> Self {
let mut buf = [0u8; P256_PRIVATE_KEY_LENGTH];
buf[P256_PRIVATE_KEY_LENGTH - 1] = 1;
Self::try_from(buf.as_ref()).unwrap()
}
}

//////////////////////
// PublicKey Traits //
//////////////////////

// Implementing From<&PrivateKey<...>> allows to derive a public key in a more elegant fashion
impl From<&P256PrivateKey> for P256PublicKey {
fn from(private_key: &P256PrivateKey) -> Self {
let secret = &private_key.0;
let public: p256::ecdsa::VerifyingKey = secret.into();
P256PublicKey(public)
}
}

// We deduce PublicKey from this
impl PublicKey for P256PublicKey {
type PrivateKeyMaterial = P256PrivateKey;
}

impl std::hash::Hash for P256PublicKey {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
let encoded_pubkey = self.to_bytes();
state.write(&encoded_pubkey);
}
}

// Those are required by the implementation of hash above
impl PartialEq for P256PublicKey {
fn eq(&self, other: &P256PublicKey) -> bool {
self.to_bytes() == other.to_bytes()
}
}

impl Eq for P256PublicKey {}

// We deduce VerifyingKey from pointing to the signature material
// we get the ability to do `pubkey.validate(msg, signature)`
impl VerifyingKey for P256PublicKey {
type SignatureMaterial = P256Signature;
type SigningKeyMaterial = P256PrivateKey;
}

impl fmt::Display for P256PublicKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", hex::encode(self.0.to_sec1_bytes()))
}
}

impl fmt::Debug for P256PublicKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "P256PublicKey({})", self)
}
}

impl TryFrom<&[u8]> for P256PublicKey {
type Error = CryptoMaterialError;

/// Deserialize a P256PublicKey.
fn try_from(bytes: &[u8]) -> std::result::Result<P256PublicKey, CryptoMaterialError> {
P256PublicKey::from_bytes_unchecked(bytes)
}
}

impl Length for P256PublicKey {
fn length(&self) -> usize {
P256_PUBLIC_KEY_LENGTH
}
}

impl ValidCryptoMaterial for P256PublicKey {
fn to_bytes(&self) -> Vec<u8> {
self.0.to_sec1_bytes().to_vec()
mstraka100 marked this conversation as resolved.
Show resolved Hide resolved
}
}

/////////////
// Fuzzing //
/////////////

/// Produces a uniformly random P256 keypair from a seed
#[cfg(any(test, feature = "fuzzing"))]
pub fn keypair_strategy() -> impl Strategy<Value = KeyPair<P256PrivateKey, P256PublicKey>> {
test_utils::uniform_keypair_strategy::<P256PrivateKey, P256PublicKey>()
}

/// Produces a uniformly random P256 public key
#[cfg(any(test, feature = "fuzzing"))]
impl proptest::arbitrary::Arbitrary for P256PublicKey {
type Parameters = ();
type Strategy = BoxedStrategy<Self>;

fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
crate::test_utils::uniform_keypair_strategy::<P256PrivateKey, P256PublicKey>()
.prop_map(|v| v.public_key)
.boxed()
}
}
Loading