diff --git a/Cargo.lock b/Cargo.lock index 6db7f0e90fc..822c2d4094d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2493,7 +2493,7 @@ dependencies = [ [[package]] name = "libp2p-identity" -version = "0.1.1" +version = "0.1.2" dependencies = [ "asn1_der", "base64 0.21.0", diff --git a/core/src/signed_envelope.rs b/core/src/signed_envelope.rs index 0eafda4c487..a9d7ecd5208 100644 --- a/core/src/signed_envelope.rs +++ b/core/src/signed_envelope.rs @@ -76,7 +76,7 @@ impl SignedEnvelope { use quick_protobuf::MessageWrite; let envelope = proto::Envelope { - public_key: self.key.to_protobuf_encoding(), + public_key: self.key.encode_protobuf(), payload_type: self.payload_type, payload: self.payload, signature: self.signature, @@ -101,7 +101,7 @@ impl SignedEnvelope { proto::Envelope::from_reader(&mut reader, bytes).map_err(DecodeError::from)?; Ok(Self { - key: PublicKey::from_protobuf_encoding(&envelope.public_key)?, + key: PublicKey::try_decode_protobuf(&envelope.public_key)?, payload_type: envelope.payload_type.to_vec(), payload: envelope.payload.to_vec(), signature: envelope.signature.to_vec(), diff --git a/identity/CHANGELOG.md b/identity/CHANGELOG.md index 961b5c6921d..5ab5cfc2528 100644 --- a/identity/CHANGELOG.md +++ b/identity/CHANGELOG.md @@ -1,3 +1,10 @@ +## 0.1.2 - unreleased + +- Follow Rust naming conventions for conversion methods. + See [PR 3775]. + +[PR 3775]: https://github.com/libp2p/rust-libp2p/pull/3775 + ## 0.1.1 - Add `From` impl for specific keypairs. diff --git a/identity/Cargo.toml b/identity/Cargo.toml index d0352036c14..a87010c53ed 100644 --- a/identity/Cargo.toml +++ b/identity/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libp2p-identity" -version = "0.1.1" +version = "0.1.2" edition = "2021" description = "Data structures and algorithms for identifying peers in libp2p." rust-version = "1.60.0" diff --git a/identity/src/ecdsa.rs b/identity/src/ecdsa.rs index 27063a2c57f..2210e4d128b 100644 --- a/identity/src/ecdsa.rs +++ b/identity/src/ecdsa.rs @@ -33,7 +33,7 @@ use p256::{ }; use void::Void; -/// An ECDSA keypair. +/// An ECDSA keypair generated using `secp256r1` curve. #[derive(Clone)] pub struct Keypair { secret: SecretKey, @@ -85,7 +85,7 @@ impl From for SecretKey { } } -/// An ECDSA secret key. +/// An ECDSA secret key generated using `secp256r1` curve. #[derive(Clone)] pub struct SecretKey(SigningKey); @@ -102,14 +102,23 @@ impl SecretKey { signature.as_bytes().to_owned() } - /// Encode a secret key into a byte buffer. + /// Convert a secret key into a byte buffer containing raw scalar of the key. pub fn to_bytes(&self) -> Vec { self.0.to_bytes().to_vec() } - /// Decode a secret key from a byte buffer. + /// Decode a secret key from a byte buffer containing raw scalar of the key. + #[deprecated( + since = "0.2.0", + note = "This method name does not follow Rust naming conventions, use `SecretKey::try_from_bytes` instead" + )] pub fn from_bytes(buf: &[u8]) -> Result { - SigningKey::from_bytes(buf) + Self::try_from_bytes(buf) + } + + /// Try to parse a secret key from a byte buffer containing raw scalar of the key. + pub fn try_from_bytes(buf: impl AsRef<[u8]>) -> Result { + SigningKey::from_bytes(buf.as_ref()) .map_err(|err| DecodingError::failed_to_parse("ecdsa p256 secret key", err)) .map(SecretKey) } @@ -135,8 +144,17 @@ impl PublicKey { self.0.verify(msg, &sig).is_ok() } - /// Decode a public key from a byte buffer without compression. + /// Decode a public key from a byte buffer containing raw components of a key with or without compression. + #[deprecated( + since = "0.2.0", + note = "This method name does not follow Rust naming conventions, use `PublicKey::try_from_bytes` instead." + )] pub fn from_bytes(k: &[u8]) -> Result { + Self::try_from_bytes(k) + } + + /// Try to parse a public key from a byte buffer containing raw components of a key with or without compression. + pub fn try_from_bytes(k: &[u8]) -> Result { let enc_pt = EncodedPoint::from_bytes(k) .map_err(|e| DecodingError::failed_to_parse("ecdsa p256 encoded point", e))?; @@ -145,7 +163,7 @@ impl PublicKey { .map(PublicKey) } - /// Encode a public key into a byte buffer without compression. + /// Convert a public key into a byte buffer containing raw components of the key without compression. pub fn to_bytes(&self) -> Vec { self.0.to_encoded_point(false).as_bytes().to_owned() } @@ -157,11 +175,20 @@ impl PublicKey { } /// Decode a public key into a DER encoded byte buffer as defined by SEC1 standard. + #[deprecated( + since = "0.2.0", + note = "This method name does not follow Rust naming conventions, use `PublicKey::try_decode_der` instead." + )] pub fn decode_der(k: &[u8]) -> Result { + Self::try_decode_der(k) + } + + /// Try to decode a public key from a DER encoded byte buffer as defined by SEC1 standard. + pub fn try_decode_der(k: &[u8]) -> Result { let buf = Self::del_asn1_header(k).ok_or_else(|| { DecodingError::failed_to_parse::("ASN.1-encoded ecdsa p256 public key", None) })?; - Self::from_bytes(buf) + Self::try_from_bytes(buf) } // ecPublicKey (ANSI X9.62 public key type) OID: 1.2.840.10045.2.1 diff --git a/identity/src/ed25519.rs b/identity/src/ed25519.rs index b565599d277..90782de378a 100644 --- a/identity/src/ed25519.rs +++ b/identity/src/ed25519.rs @@ -41,15 +41,35 @@ impl Keypair { /// Encode the keypair into a byte array by concatenating the bytes /// of the secret scalar and the compressed public point, /// an informal standard for encoding Ed25519 keypairs. + #[deprecated(since = "0.2.0", note = "Renamed to `Keypair::to_bytes`")] pub fn encode(&self) -> [u8; 64] { + self.to_bytes() + } + + /// Convert the keypair into a byte array by concatenating the bytes + /// of the secret scalar and the compressed public point, + /// an informal standard for encoding Ed25519 keypairs. + pub fn to_bytes(&self) -> [u8; 64] { self.0.to_bytes() } /// Decode a keypair from the [binary format](https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.5) - /// produced by [`Keypair::encode`], zeroing the input on success. + /// produced by [`Keypair::to_bytes`], zeroing the input on success. /// /// Note that this binary format is the same as `ed25519_dalek`'s and `ed25519_zebra`'s. + #[deprecated( + since = "0.2.0", + note = "This method name does not follow Rust naming conventions, use `Keypair::try_from_bytes` instead." + )] pub fn decode(kp: &mut [u8]) -> Result { + Self::try_from_bytes(kp) + } + + /// Try to parse a keypair from the [binary format](https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.5) + /// produced by [`Keypair::to_bytes`], zeroing the input on success. + /// + /// Note that this binary format is the same as `ed25519_dalek`'s and `ed25519_zebra`'s. + pub fn try_from_bytes(kp: &mut [u8]) -> Result { ed25519::Keypair::from_bytes(kp) .map(|k| { kp.zeroize(); @@ -70,7 +90,7 @@ impl Keypair { /// Get the secret key of this keypair. pub fn secret(&self) -> SecretKey { - SecretKey::from_bytes(&mut self.0.secret.to_bytes()) + SecretKey::try_from_bytes(&mut self.0.secret.to_bytes()) .expect("ed25519::SecretKey::from_bytes(to_bytes(k)) != k") } } @@ -86,7 +106,7 @@ impl fmt::Debug for Keypair { impl Clone for Keypair { fn clone(&self) -> Keypair { let mut sk_bytes = self.0.secret.to_bytes(); - let secret = SecretKey::from_bytes(&mut sk_bytes) + let secret = SecretKey::try_from_bytes(&mut sk_bytes) .expect("ed25519::SecretKey::from_bytes(to_bytes(k)) != k") .0; @@ -164,12 +184,31 @@ impl PublicKey { /// Encode the public key into a byte array in compressed form, i.e. /// where one coordinate is represented by a single bit. + #[deprecated( + since = "0.2.0", + note = "Renamed to `PublicKey::to_bytes` to reflect actual behaviour." + )] pub fn encode(&self) -> [u8; 32] { + self.to_bytes() + } + + /// Convert the public key to a byte array in compressed form, i.e. + /// where one coordinate is represented by a single bit. + pub fn to_bytes(&self) -> [u8; 32] { self.0.to_bytes() } - /// Decode a public key from a byte array as produced by `encode`. + /// Decode a public key from a byte array as produced by `to_bytes`. + #[deprecated( + since = "0.2.0", + note = "This method name does not follow Rust naming conventions, use `PublicKey::try_from_bytes` instead." + )] pub fn decode(k: &[u8]) -> Result { + Self::try_from_bytes(k) + } + + /// Try to parse a public key from a byte array containing the actual key as produced by `to_bytes`. + pub fn try_from_bytes(k: &[u8]) -> Result { ed25519::PublicKey::from_bytes(k) .map_err(|e| DecodingError::failed_to_parse("Ed25519 public key", e)) .map(PublicKey) @@ -189,7 +228,8 @@ impl AsRef<[u8]> for SecretKey { impl Clone for SecretKey { fn clone(&self) -> SecretKey { let mut sk_bytes = self.0.to_bytes(); - Self::from_bytes(&mut sk_bytes).expect("ed25519::SecretKey::from_bytes(to_bytes(k)) != k") + Self::try_from_bytes(&mut sk_bytes) + .expect("ed25519::SecretKey::from_bytes(to_bytes(k)) != k") } } @@ -214,7 +254,20 @@ impl SecretKey { /// Create an Ed25519 secret key from a byte slice, zeroing the input on success. /// If the bytes do not constitute a valid Ed25519 secret key, an error is /// returned. + #[deprecated( + since = "0.2.0", + note = "This method name does not follow Rust naming conventions, use `SecretKey::try_from_bytes` instead." + )] + #[allow(unused_mut)] pub fn from_bytes(mut sk_bytes: impl AsMut<[u8]>) -> Result { + Self::try_from_bytes(sk_bytes) + } + + /// Try to parse an Ed25519 secret key from a byte slice + /// containing the actual key, zeroing the input on success. + /// If the bytes do not constitute a valid Ed25519 secret key, an error is + /// returned. + pub fn try_from_bytes(mut sk_bytes: impl AsMut<[u8]>) -> Result { let sk_bytes = sk_bytes.as_mut(); let secret = ed25519::SecretKey::from_bytes(&*sk_bytes) .map_err(|e| DecodingError::failed_to_parse("Ed25519 secret key", e))?; @@ -236,8 +289,8 @@ mod tests { fn ed25519_keypair_encode_decode() { fn prop() -> bool { let kp1 = Keypair::generate(); - let mut kp1_enc = kp1.encode(); - let kp2 = Keypair::decode(&mut kp1_enc).unwrap(); + let mut kp1_enc = kp1.to_bytes(); + let kp2 = Keypair::try_from_bytes(&mut kp1_enc).unwrap(); eq_keypairs(&kp1, &kp2) && kp1_enc.iter().all(|b| *b == 0) } QuickCheck::new().tests(10).quickcheck(prop as fn() -> _); @@ -248,7 +301,7 @@ mod tests { fn prop() -> bool { let kp1 = Keypair::generate(); let mut sk = kp1.0.secret.to_bytes(); - let kp2 = Keypair::from(SecretKey::from_bytes(&mut sk).unwrap()); + let kp2 = Keypair::from(SecretKey::try_from_bytes(&mut sk).unwrap()); eq_keypairs(&kp1, &kp2) && sk == [0u8; 32] } QuickCheck::new().tests(10).quickcheck(prop as fn() -> _); diff --git a/identity/src/error.rs b/identity/src/error.rs index 2a46571d858..79ef21d5c44 100644 --- a/identity/src/error.rs +++ b/identity/src/error.rs @@ -23,6 +23,8 @@ use std::error::Error; use std::fmt; +use crate::KeyType; + /// An error during decoding of key material. #[derive(Debug)] pub struct DecodingError { @@ -156,3 +158,26 @@ impl Error for SigningError { self.source.as_ref().map(|s| &**s as &dyn Error) } } + +/// Error produced when failing to convert [`Keypair`](crate::Keypair) to a more concrete keypair. +#[derive(Debug)] +pub struct OtherVariantError { + actual: KeyType, +} + +impl OtherVariantError { + pub(crate) fn new(actual: KeyType) -> OtherVariantError { + OtherVariantError { actual } + } +} + +impl fmt::Display for OtherVariantError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&format!( + "Cannot convert to the given type, the actual key type inside is {}", + self.actual + )) + } +} + +impl Error for OtherVariantError {} diff --git a/identity/src/keypair.rs b/identity/src/keypair.rs index 0e4cce683b1..a080fcfb690 100644 --- a/identity/src/keypair.rs +++ b/identity/src/keypair.rs @@ -18,8 +18,9 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +use crate::error::OtherVariantError; use crate::error::{DecodingError, SigningError}; -use crate::proto; +use crate::{proto, KeyType}; use quick_protobuf::{BytesReader, Writer}; use std::convert::TryFrom; @@ -59,28 +60,28 @@ pub enum Keypair { #[cfg(feature = "ed25519")] #[deprecated( since = "0.1.0", - note = "This enum will be made opaque in the future, use `Keypair::into_ed25519` instead." + note = "This enum will be made opaque in the future, use `Keypair::try_into_ed25519` instead." )] Ed25519(ed25519::Keypair), /// An RSA keypair. #[cfg(all(feature = "rsa", not(target_arch = "wasm32")))] #[deprecated( since = "0.1.0", - note = "This enum will be made opaque in the future, use `Keypair::into_rsa` instead." + note = "This enum will be made opaque in the future, use `Keypair::try_into_rsa` instead." )] Rsa(rsa::Keypair), /// A Secp256k1 keypair. #[cfg(feature = "secp256k1")] #[deprecated( since = "0.1.0", - note = "This enum will be made opaque in the future, use `Keypair::into_secp256k1` instead." + note = "This enum will be made opaque in the future, use `Keypair::try_into_secp256k1` instead." )] Secp256k1(secp256k1::Keypair), /// An ECDSA keypair. #[cfg(feature = "ecdsa")] #[deprecated( since = "0.1.0", - note = "This enum will be made opaque in the future, use `Keypair::into_ecdsa` instead." + note = "This enum will be made opaque in the future, use `Keypair::try_into_ecdsa` instead." )] Ecdsa(ecdsa::Keypair), } @@ -108,43 +109,59 @@ impl Keypair { } #[cfg(feature = "ed25519")] + #[deprecated( + since = "0.2.0", + note = "This method name does not follow Rust naming conventions, use `Keypair::try_into_ed25519` instead." + )] pub fn into_ed25519(self) -> Option { - #[allow(deprecated)] - #[allow(unreachable_patterns)] - match self { - Keypair::Ed25519(inner) => Some(inner), - _ => None, - } + self.try_into().ok() + } + + #[cfg(feature = "ed25519")] + pub fn try_into_ed25519(self) -> Result { + self.try_into() } #[cfg(feature = "secp256k1")] + #[deprecated( + since = "0.2.0", + note = "This method name does not follow Rust naming conventions, use `Keypair::try_into_secp256k1` instead." + )] pub fn into_secp256k1(self) -> Option { - #[allow(deprecated)] - #[allow(unreachable_patterns)] - match self { - Keypair::Secp256k1(inner) => Some(inner), - _ => None, - } + self.try_into().ok() + } + + #[cfg(feature = "secp256k1")] + pub fn try_into_secp256k1(self) -> Result { + self.try_into() } #[cfg(all(feature = "rsa", not(target_arch = "wasm32")))] + #[deprecated( + since = "0.2.0", + note = "This method name does not follow Rust naming conventions, use `Keypair::try_into_rsa` instead." + )] pub fn into_rsa(self) -> Option { - #[allow(deprecated)] - #[allow(unreachable_patterns)] - match self { - Keypair::Rsa(inner) => Some(inner), - _ => None, - } + self.try_into().ok() + } + + #[cfg(all(feature = "rsa", not(target_arch = "wasm32")))] + pub fn try_into_rsa(self) -> Result { + self.try_into() } #[cfg(feature = "ecdsa")] + #[deprecated( + since = "0.2.0", + note = "This method name does not follow Rust naming conventions, use `Keypair::try_into_ecdsa` instead." + )] pub fn into_ecdsa(self) -> Option { - #[allow(deprecated)] - #[allow(unreachable_patterns)] - match self { - Keypair::Ecdsa(inner) => Some(inner), - _ => None, - } + self.try_into().ok() + } + + #[cfg(feature = "ecdsa")] + pub fn try_into_ecdsa(self) -> Result { + self.try_into() } /// Decode an keypair from a DER-encoded secret key in PKCS#8 PrivateKeyInfo @@ -300,6 +317,78 @@ impl From for Keypair { } } +#[cfg(feature = "ed25519")] +impl TryInto for Keypair { + type Error = OtherVariantError; + + fn try_into(self) -> Result { + #[allow(deprecated)] + match self { + Keypair::Ed25519(inner) => Ok(inner), + #[cfg(all(feature = "rsa", not(target_arch = "wasm32")))] + Keypair::Rsa(_) => Err(OtherVariantError::new(KeyType::RSA)), + #[cfg(feature = "secp256k1")] + Keypair::Secp256k1(_) => Err(OtherVariantError::new(KeyType::Secp256k1)), + #[cfg(feature = "ecdsa")] + Keypair::Ecdsa(_) => Err(OtherVariantError::new(KeyType::Ecdsa)), + } + } +} + +#[cfg(feature = "ecdsa")] +impl TryInto for Keypair { + type Error = OtherVariantError; + + fn try_into(self) -> Result { + #[allow(deprecated)] + match self { + Keypair::Ecdsa(inner) => Ok(inner), + #[cfg(feature = "ed25519")] + Keypair::Ed25519(_) => Err(OtherVariantError::new(KeyType::Ed25519)), + #[cfg(all(feature = "rsa", not(target_arch = "wasm32")))] + Keypair::Rsa(_) => Err(OtherVariantError::new(KeyType::RSA)), + #[cfg(feature = "secp256k1")] + Keypair::Secp256k1(_) => Err(OtherVariantError::new(KeyType::Secp256k1)), + } + } +} + +#[cfg(feature = "secp256k1")] +impl TryInto for Keypair { + type Error = OtherVariantError; + + fn try_into(self) -> Result { + #[allow(deprecated)] + match self { + Keypair::Secp256k1(inner) => Ok(inner), + #[cfg(feature = "ed25519")] + Keypair::Ed25519(_) => Err(OtherVariantError::new(KeyType::Ed25519)), + #[cfg(all(feature = "rsa", not(target_arch = "wasm32")))] + Keypair::Rsa(_) => Err(OtherVariantError::new(KeyType::RSA)), + #[cfg(feature = "ecdsa")] + Keypair::Ecdsa(_) => Err(OtherVariantError::new(KeyType::Ecdsa)), + } + } +} + +#[cfg(all(feature = "rsa", not(target_arch = "wasm32")))] +impl TryInto for Keypair { + type Error = OtherVariantError; + + fn try_into(self) -> Result { + #[allow(deprecated)] + match self { + Keypair::Rsa(inner) => Ok(inner), + #[cfg(feature = "ed25519")] + Keypair::Ed25519(_) => Err(OtherVariantError::new(KeyType::Ed25519)), + #[cfg(feature = "secp256k1")] + Keypair::Secp256k1(_) => Err(OtherVariantError::new(KeyType::Secp256k1)), + #[cfg(feature = "ecdsa")] + Keypair::Ecdsa(_) => Err(OtherVariantError::new(KeyType::Ecdsa)), + } + } +} + /// The public key of a node's identity keypair. #[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum PublicKey { @@ -356,48 +445,71 @@ impl PublicKey { } #[cfg(feature = "ed25519")] + #[deprecated( + since = "0.2.0", + note = "This method name does not follow Rust naming conventions, use `PublicKey::try_into_ed25519` instead." + )] pub fn into_ed25519(self) -> Option { - #[allow(deprecated)] - #[allow(unreachable_patterns)] - match self { - PublicKey::Ed25519(inner) => Some(inner), - _ => None, - } + self.try_into().ok() + } + + #[cfg(feature = "ed25519")] + pub fn try_into_ed25519(self) -> Result { + self.try_into() } #[cfg(feature = "secp256k1")] + #[deprecated( + since = "0.2.0", + note = "This method name does not follow Rust naming conventions, use `PublicKey::try_into_secp256k1` instead." + )] pub fn into_secp256k1(self) -> Option { - #[allow(deprecated)] - #[allow(unreachable_patterns)] - match self { - PublicKey::Secp256k1(inner) => Some(inner), - _ => None, - } + self.try_into().ok() + } + + #[cfg(feature = "secp256k1")] + pub fn try_into_secp256k1(self) -> Result { + self.try_into() } #[cfg(all(feature = "rsa", not(target_arch = "wasm32")))] + #[deprecated( + since = "0.2.0", + note = "This method name does not follow Rust naming conventions, use `PublicKey::try_into_rsa` instead." + )] pub fn into_rsa(self) -> Option { - #[allow(deprecated)] - #[allow(unreachable_patterns)] - match self { - PublicKey::Rsa(inner) => Some(inner), - _ => None, - } + self.try_into().ok() + } + + #[cfg(all(feature = "rsa", not(target_arch = "wasm32")))] + pub fn try_into_rsa(self) -> Result { + self.try_into() } #[cfg(feature = "ecdsa")] + #[deprecated( + since = "0.2.0", + note = "This method name does not follow Rust naming conventions, use `PublicKey::try_into_ecdsa` instead." + )] pub fn into_ecdsa(self) -> Option { - #[allow(deprecated)] - #[allow(unreachable_patterns)] - match self { - PublicKey::Ecdsa(inner) => Some(inner), - _ => None, - } + self.try_into().ok() + } + + #[cfg(feature = "ecdsa")] + pub fn try_into_ecdsa(self) -> Result { + self.try_into() } /// Encode the public key into a protobuf structure for storage or /// exchange with other nodes. + #[deprecated(since = "0.2.0", note = "Renamed to `PublicKey::encode_protobuf`.")] pub fn to_protobuf_encoding(&self) -> Vec { + Self::encode_protobuf(self) + } + + /// Encode the public key into a protobuf structure for storage or + /// exchange with other nodes. + pub fn encode_protobuf(&self) -> Vec { use quick_protobuf::MessageWrite; let public_key = proto::PublicKey::from(self); @@ -413,7 +525,17 @@ impl PublicKey { /// Decode a public key from a protobuf structure, e.g. read from storage /// or received from another node. + #[deprecated( + since = "0.2.0", + note = "This method name does not follow Rust naming conventions, use `PublicKey::try_decode_protobuf` instead." + )] pub fn from_protobuf_encoding(bytes: &[u8]) -> Result { + Self::try_decode_protobuf(bytes) + } + + /// Decode a public key from a protobuf structure, e.g. read from storage + /// or received from another node. + pub fn try_decode_protobuf(bytes: &[u8]) -> Result { use quick_protobuf::MessageRead; let mut reader = BytesReader::from_bytes(bytes); @@ -475,6 +597,78 @@ impl TryFrom for PublicKey { } } +#[cfg(feature = "ed25519")] +impl TryInto for PublicKey { + type Error = OtherVariantError; + + fn try_into(self) -> Result { + #[allow(deprecated)] + match self { + PublicKey::Ed25519(inner) => Ok(inner), + #[cfg(all(feature = "rsa", not(target_arch = "wasm32")))] + PublicKey::Rsa(_) => Err(OtherVariantError::new(KeyType::RSA)), + #[cfg(feature = "secp256k1")] + PublicKey::Secp256k1(_) => Err(OtherVariantError::new(KeyType::Secp256k1)), + #[cfg(feature = "ecdsa")] + PublicKey::Ecdsa(_) => Err(OtherVariantError::new(KeyType::Ecdsa)), + } + } +} + +#[cfg(feature = "ecdsa")] +impl TryInto for PublicKey { + type Error = OtherVariantError; + + fn try_into(self) -> Result { + #[allow(deprecated)] + match self { + PublicKey::Ecdsa(inner) => Ok(inner), + #[cfg(feature = "ed25519")] + PublicKey::Ed25519(_) => Err(OtherVariantError::new(KeyType::Ed25519)), + #[cfg(all(feature = "rsa", not(target_arch = "wasm32")))] + PublicKey::Rsa(_) => Err(OtherVariantError::new(KeyType::RSA)), + #[cfg(feature = "secp256k1")] + PublicKey::Secp256k1(_) => Err(OtherVariantError::new(KeyType::Secp256k1)), + } + } +} + +#[cfg(feature = "secp256k1")] +impl TryInto for PublicKey { + type Error = OtherVariantError; + + fn try_into(self) -> Result { + #[allow(deprecated)] + match self { + PublicKey::Secp256k1(inner) => Ok(inner), + #[cfg(feature = "ed25519")] + PublicKey::Ed25519(_) => Err(OtherVariantError::new(KeyType::Ed25519)), + #[cfg(all(feature = "rsa", not(target_arch = "wasm32")))] + PublicKey::Rsa(_) => Err(OtherVariantError::new(KeyType::RSA)), + #[cfg(feature = "ecdsa")] + PublicKey::Ecdsa(_) => Err(OtherVariantError::new(KeyType::Ecdsa)), + } + } +} + +#[cfg(all(feature = "rsa", not(target_arch = "wasm32")))] +impl TryInto for PublicKey { + type Error = OtherVariantError; + + fn try_into(self) -> Result { + #[allow(deprecated)] + match self { + PublicKey::Rsa(inner) => Ok(inner), + #[cfg(feature = "ed25519")] + PublicKey::Ed25519(_) => Err(OtherVariantError::new(KeyType::Ed25519)), + #[cfg(feature = "secp256k1")] + PublicKey::Secp256k1(_) => Err(OtherVariantError::new(KeyType::Secp256k1)), + #[cfg(feature = "ecdsa")] + PublicKey::Ecdsa(_) => Err(OtherVariantError::new(KeyType::Ecdsa)), + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/identity/src/lib.rs b/identity/src/lib.rs index 078a2de2106..87b02cd5a2e 100644 --- a/identity/src/lib.rs +++ b/identity/src/lib.rs @@ -121,7 +121,27 @@ impl From<&PublicKey> for proto::PublicKey { } } -pub use error::{DecodingError, SigningError}; +pub use error::{DecodingError, OtherVariantError, SigningError}; pub use keypair::{Keypair, PublicKey}; #[cfg(feature = "peerid")] pub use peer_id::{ParseError, PeerId}; + +#[derive(Debug, PartialEq, Eq)] +/// The type of key a `KeyPair` is holding. +pub enum KeyType { + Ed25519, + RSA, + Secp256k1, + Ecdsa, +} + +impl std::fmt::Display for KeyType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + KeyType::Ed25519 => f.write_str("Ed25519"), + KeyType::RSA => f.write_str("RSA"), + KeyType::Secp256k1 => f.write_str("Secp256k1"), + KeyType::Ecdsa => f.write_str("Ecdsa"), + } + } +} diff --git a/identity/src/peer_id.rs b/identity/src/peer_id.rs index 1a96f4311e9..788e0b79666 100644 --- a/identity/src/peer_id.rs +++ b/identity/src/peer_id.rs @@ -66,7 +66,7 @@ impl fmt::Display for PeerId { impl PeerId { /// Builds a `PeerId` from a public key. pub fn from_public_key(key: &crate::keypair::PublicKey) -> PeerId { - let key_enc = key.to_protobuf_encoding(); + let key_enc = key.encode_protobuf(); let multihash = if key_enc.len() <= MAX_INLINE_KEY_LENGTH { Multihash::wrap(MULTIHASH_IDENTITY_CODE, &key_enc) @@ -141,7 +141,7 @@ impl PeerId { let alg = Code::try_from(self.multihash.code()) .expect("Internal multihash is always a valid `Code`"); - let enc = public_key.to_protobuf_encoding(); + let enc = public_key.encode_protobuf(); Some(alg.digest(&enc) == self.multihash) } } diff --git a/identity/src/rsa.rs b/identity/src/rsa.rs index 12f5d75e7dd..b598b093b7e 100644 --- a/identity/src/rsa.rs +++ b/identity/src/rsa.rs @@ -46,7 +46,16 @@ impl Keypair { /// format (i.e. unencrypted) as defined in [RFC5208]. /// /// [RFC5208]: https://tools.ietf.org/html/rfc5208#section-5 + #[deprecated(since = "0.2.0", note = "Renamed to `Keypair::try_decode_pkcs8`.")] pub fn from_pkcs8(der: &mut [u8]) -> Result { + Self::try_decode_pkcs8(der) + } + + /// Decode an RSA keypair from a DER-encoded private key in PKCS#8 PrivateKeyInfo + /// format (i.e. unencrypted) as defined in [RFC5208]. + /// + /// [RFC5208]: https://tools.ietf.org/html/rfc5208#section-5 + pub fn try_decode_pkcs8(der: &mut [u8]) -> Result { let kp = RsaKeyPair::from_pkcs8(der) .map_err(|e| DecodingError::failed_to_parse("RSA PKCS#8 PrivateKeyInfo", e))?; der.zeroize(); @@ -109,7 +118,17 @@ impl PublicKey { /// Decode an RSA public key from a DER-encoded X.509 SubjectPublicKeyInfo /// structure. See also `encode_x509`. + #[deprecated( + since = "0.2.0", + note = "This method name does not follow Rust naming conventions, use `PublicKey::try_decode_x509` instead." + )] pub fn decode_x509(pk: &[u8]) -> Result { + Self::try_decode_x509(pk) + } + + /// Decode an RSA public key from a DER-encoded X.509 SubjectPublicKeyInfo + /// structure. See also `encode_x509`. + pub fn try_decode_x509(pk: &[u8]) -> Result { Asn1SubjectPublicKeyInfo::decode(pk) .map_err(|e| DecodingError::failed_to_parse("RSA X.509", e)) .map(|spki| spki.subjectPublicKey.0) @@ -317,22 +336,22 @@ mod tests { impl Arbitrary for SomeKeypair { fn arbitrary(g: &mut Gen) -> SomeKeypair { let mut key = g.choose(&[KEY1, KEY2, KEY3]).unwrap().to_vec(); - SomeKeypair(Keypair::from_pkcs8(&mut key).unwrap()) + SomeKeypair(Keypair::try_decode_pkcs8(&mut key).unwrap()) } } #[test] fn rsa_from_pkcs8() { - assert!(Keypair::from_pkcs8(&mut KEY1.to_vec()).is_ok()); - assert!(Keypair::from_pkcs8(&mut KEY2.to_vec()).is_ok()); - assert!(Keypair::from_pkcs8(&mut KEY3.to_vec()).is_ok()); + assert!(Keypair::try_decode_pkcs8(&mut KEY1.to_vec()).is_ok()); + assert!(Keypair::try_decode_pkcs8(&mut KEY2.to_vec()).is_ok()); + assert!(Keypair::try_decode_pkcs8(&mut KEY3.to_vec()).is_ok()); } #[test] fn rsa_x509_encode_decode() { fn prop(SomeKeypair(kp): SomeKeypair) -> Result { let pk = kp.public(); - PublicKey::decode_x509(&pk.encode_x509()) + PublicKey::try_decode_x509(&pk.encode_x509()) .map_err(|e| e.to_string()) .map(|pk2| pk2 == pk) } diff --git a/identity/src/secp256k1.rs b/identity/src/secp256k1.rs index 119c3ef64e9..413e2566227 100644 --- a/identity/src/secp256k1.rs +++ b/identity/src/secp256k1.rs @@ -97,7 +97,21 @@ impl SecretKey { /// error is returned. /// /// Note that the expected binary format is the same as `libsecp256k1`'s. + #[deprecated( + since = "0.2.0", + note = "This method name does not follow Rust naming conventions, use `SecretKey::try_from_bytes` instead." + )] + #[allow(unused_mut)] pub fn from_bytes(mut sk: impl AsMut<[u8]>) -> Result { + Self::try_from_bytes(sk) + } + + /// Create a secret key from a byte slice, zeroing the slice on success. + /// If the bytes do not constitute a valid Secp256k1 secret key, an + /// error is returned. + /// + /// Note that the expected binary format is the same as `libsecp256k1`'s. + pub fn try_from_bytes(mut sk: impl AsMut<[u8]>) -> Result { let sk_bytes = sk.as_mut(); let secret = libsecp256k1::SecretKey::parse_slice(&*sk_bytes) .map_err(|e| DecodingError::failed_to_parse("parse secp256k1 secret key", e))?; @@ -118,7 +132,7 @@ impl SecretKey { .and_then(Vec::load) .map_err(|e| DecodingError::failed_to_parse("secp256k1 SecretKey bytes", e))?; - let sk = SecretKey::from_bytes(&mut sk_bytes)?; + let sk = SecretKey::try_from_bytes(&mut sk_bytes)?; sk_bytes.zeroize(); der_obj.zeroize(); Ok(sk) @@ -157,7 +171,7 @@ pub struct PublicKey(libsecp256k1::PublicKey); impl fmt::Debug for PublicKey { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("PublicKey(compressed): ")?; - for byte in &self.encode() { + for byte in &self.to_bytes() { write!(f, "{byte:x}")?; } Ok(()) @@ -166,25 +180,25 @@ impl fmt::Debug for PublicKey { impl cmp::PartialEq for PublicKey { fn eq(&self, other: &Self) -> bool { - self.encode().eq(&other.encode()) + self.to_bytes().eq(&other.to_bytes()) } } impl hash::Hash for PublicKey { fn hash(&self, state: &mut H) { - self.encode().hash(state); + self.to_bytes().hash(state); } } impl cmp::PartialOrd for PublicKey { fn partial_cmp(&self, other: &Self) -> Option { - self.encode().partial_cmp(&other.encode()) + self.to_bytes().partial_cmp(&other.to_bytes()) } } impl cmp::Ord for PublicKey { fn cmp(&self, other: &Self) -> cmp::Ordering { - self.encode().cmp(&other.encode()) + self.to_bytes().cmp(&other.to_bytes()) } } @@ -203,18 +217,44 @@ impl PublicKey { /// Encode the public key in compressed form, i.e. with one coordinate /// represented by a single bit. + #[deprecated(since = "0.2.0", note = "Renamed to `PublicKey::to_bytes`.")] pub fn encode(&self) -> [u8; 33] { + self.to_bytes() + } + + /// 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() } /// Encode the public key in uncompressed form. + #[deprecated( + since = "0.2.0", + note = "Renamed to `PublicKey::to_bytes_uncompressed`." + )] pub fn encode_uncompressed(&self) -> [u8; 65] { + self.to_bytes_uncompressed() + } + + /// Convert the public key to a byte buffer in uncompressed form. + pub fn to_bytes_uncompressed(&self) -> [u8; 65] { self.0.serialize() } /// Decode a public key from a byte slice in the the format produced /// by `encode`. + #[deprecated( + since = "0.2.0", + note = "This method name does not follow Rust naming conventions, use `PublicKey::try_from_bytes` instead." + )] pub fn decode(k: &[u8]) -> Result { + Self::try_from_bytes(k) + } + + /// Decode a public key from a byte slice in the the format produced + /// by `encode`. + pub fn try_from_bytes(k: &[u8]) -> Result { libsecp256k1::PublicKey::parse_slice(k, Some(libsecp256k1::PublicKeyFormat::Compressed)) .map_err(|e| DecodingError::failed_to_parse("secp256k1 public key", e)) .map(PublicKey) @@ -230,7 +270,7 @@ mod tests { let sk1 = SecretKey::generate(); let mut sk_bytes = [0; 32]; sk_bytes.copy_from_slice(&sk1.0.serialize()[..]); - let sk2 = SecretKey::from_bytes(&mut sk_bytes).unwrap(); + let sk2 = SecretKey::try_from_bytes(&mut sk_bytes).unwrap(); assert_eq!(sk1.0.serialize(), sk2.0.serialize()); assert_eq!(sk_bytes, [0; 32]); } diff --git a/protocols/gossipsub/Cargo.toml b/protocols/gossipsub/Cargo.toml index 4f25af93f6b..53d9d4be702 100644 --- a/protocols/gossipsub/Cargo.toml +++ b/protocols/gossipsub/Cargo.toml @@ -13,7 +13,7 @@ categories = ["network-programming", "asynchronous"] [dependencies] libp2p-swarm = { version = "0.42.1", path = "../../swarm" } libp2p-core = { version = "0.39.0", path = "../../core" } -libp2p-identity = { version = "0.1.0", path = "../../identity" } +libp2p-identity = { version = "0.1.2", path = "../../identity" } bytes = "1.4" byteorder = "1.3.4" fnv = "1.0.7" diff --git a/protocols/gossipsub/src/behaviour.rs b/protocols/gossipsub/src/behaviour.rs index 256390adffd..fb58e55f24a 100644 --- a/protocols/gossipsub/src/behaviour.rs +++ b/protocols/gossipsub/src/behaviour.rs @@ -203,7 +203,7 @@ impl From for PublishConfig { match authenticity { MessageAuthenticity::Signed(keypair) => { let public_key = keypair.public(); - let key_enc = public_key.to_protobuf_encoding(); + let key_enc = public_key.encode_protobuf(); let key = if key_enc.len() <= 42 { // The public key can be inlined in [`rpc_proto::proto::::Message::from`], so we don't include it // specifically in the [`rpc_proto::proto::Message::key`] field. diff --git a/protocols/gossipsub/src/protocol.rs b/protocols/gossipsub/src/protocol.rs index f7b04269c92..98e05567929 100644 --- a/protocols/gossipsub/src/protocol.rs +++ b/protocols/gossipsub/src/protocol.rs @@ -234,13 +234,9 @@ impl GossipsubCodec { // If there is a key value in the protobuf, use that key otherwise the key must be // obtained from the inlined source peer_id. - let public_key = match message - .key - .as_deref() - .map(PublicKey::from_protobuf_encoding) - { + let public_key = match message.key.as_deref().map(PublicKey::try_decode_protobuf) { Some(Ok(key)) => key, - _ => match PublicKey::from_protobuf_encoding(&source.to_bytes()[2..]) { + _ => match PublicKey::try_decode_protobuf(&source.to_bytes()[2..]) { Ok(v) => v, Err(_) => { warn!("Signature verification failed: No valid public key supplied"); diff --git a/protocols/identify/Cargo.toml b/protocols/identify/Cargo.toml index 88885c1a2bc..bfe75da8133 100644 --- a/protocols/identify/Cargo.toml +++ b/protocols/identify/Cargo.toml @@ -16,7 +16,7 @@ futures = "0.3.28" futures-timer = "3.0.2" libp2p-core = { version = "0.39.0", path = "../../core" } libp2p-swarm = { version = "0.42.1", path = "../../swarm" } -libp2p-identity = { version = "0.1.0", path = "../../identity" } +libp2p-identity = { version = "0.1.2", path = "../../identity" } log = "0.4.1" lru = "0.10.0" quick-protobuf-codec = { version = "0.1", path = "../../misc/quick-protobuf-codec" } diff --git a/protocols/identify/src/protocol.rs b/protocols/identify/src/protocol.rs index 51e530291dc..1a10b591278 100644 --- a/protocols/identify/src/protocol.rs +++ b/protocols/identify/src/protocol.rs @@ -169,7 +169,7 @@ where .map(|addr| addr.to_vec()) .collect(); - let pubkey_bytes = info.public_key.to_protobuf_encoding(); + let pubkey_bytes = info.public_key.encode_protobuf(); let message = proto::Identify { agentVersion: Some(info.agent_version), @@ -235,7 +235,7 @@ impl TryFrom for Info { addrs }; - let public_key = PublicKey::from_protobuf_encoding(&msg.publicKey.unwrap_or_default())?; + let public_key = PublicKey::try_decode_protobuf(&msg.publicKey.unwrap_or_default())?; let observed_addr = match parse_multiaddr(msg.observedAddr.unwrap_or_default()) { Ok(a) => a, @@ -386,7 +386,7 @@ mod tests { publicKey: Some( identity::Keypair::generate_ed25519() .public() - .to_protobuf_encoding(), + .encode_protobuf(), ), }; diff --git a/transports/noise/Cargo.toml b/transports/noise/Cargo.toml index 4e745c28adc..28a8ceeb0a8 100644 --- a/transports/noise/Cargo.toml +++ b/transports/noise/Cargo.toml @@ -13,7 +13,7 @@ bytes = "1" curve25519-dalek = "3.0.0" futures = "0.3.28" libp2p-core = { version = "0.39.0", path = "../../core" } -libp2p-identity = { version = "0.1.0", path = "../../identity", features = ["ed25519"] } +libp2p-identity = { version = "0.1.2", path = "../../identity", features = ["ed25519"] } log = "0.4" quick-protobuf = "0.8" once_cell = "1.17.1" diff --git a/transports/noise/src/io/handshake.rs b/transports/noise/src/io/handshake.rs index 672f24ef7ce..e9428f8441c 100644 --- a/transports/noise/src/io/handshake.rs +++ b/transports/noise/src/io/handshake.rs @@ -214,7 +214,7 @@ where let pb = pb_result?; if !pb.identity_key.is_empty() { - let pk = identity::PublicKey::from_protobuf_encoding(&pb.identity_key)?; + let pk = identity::PublicKey::try_decode_protobuf(&pb.identity_key)?; if let Some(ref k) = state.id_remote_pubkey { if k != &pk { return Err(NoiseError::UnexpectedKey); @@ -236,7 +236,7 @@ where T: AsyncWrite + Unpin, { let mut pb = proto::NoiseHandshakePayload { - identity_key: state.identity.public.to_protobuf_encoding(), + identity_key: state.identity.public.encode_protobuf(), ..Default::default() }; diff --git a/transports/plaintext/Cargo.toml b/transports/plaintext/Cargo.toml index 81e0f1b98d0..fea37124ed1 100644 --- a/transports/plaintext/Cargo.toml +++ b/transports/plaintext/Cargo.toml @@ -15,7 +15,7 @@ asynchronous-codec = "0.6" bytes = "1" futures = "0.3.28" libp2p-core = { version = "0.39.0", path = "../../core" } -libp2p-identity = { version = "0.1.0", path = "../../identity" } +libp2p-identity = { version = "0.1.2", path = "../../identity" } log = "0.4.8" quick-protobuf = "0.8" unsigned-varint = { version = "0.7", features = ["asynchronous_codec"] } diff --git a/transports/plaintext/src/handshake.rs b/transports/plaintext/src/handshake.rs index 3f70f515a09..fb156190c57 100644 --- a/transports/plaintext/src/handshake.rs +++ b/transports/plaintext/src/handshake.rs @@ -54,7 +54,7 @@ impl HandshakeContext { fn new(config: PlainText2Config) -> Self { let exchange = Exchange { id: Some(config.local_public_key.to_peer_id().to_bytes()), - pubkey: Some(config.local_public_key.to_protobuf_encoding()), + pubkey: Some(config.local_public_key.encode_protobuf()), }; let mut buf = Vec::with_capacity(exchange.get_size()); let mut writer = Writer::new(&mut buf); @@ -77,7 +77,7 @@ impl HandshakeContext { let mut reader = BytesReader::from_bytes(&exchange_bytes); let prop = Exchange::from_reader(&mut reader, &exchange_bytes)?; - let public_key = PublicKey::from_protobuf_encoding(&prop.pubkey.unwrap_or_default())?; + let public_key = PublicKey::try_decode_protobuf(&prop.pubkey.unwrap_or_default())?; let peer_id = PeerId::from_bytes(&prop.id.unwrap_or_default())?; // Check the validity of the remote's `Exchange`. diff --git a/transports/tls/Cargo.toml b/transports/tls/Cargo.toml index 26d9679a814..3658f92fc94 100644 --- a/transports/tls/Cargo.toml +++ b/transports/tls/Cargo.toml @@ -12,7 +12,7 @@ exclude = ["src/test_assets"] futures = { version = "0.3.28", default-features = false } futures-rustls = "0.22.2" libp2p-core = { version = "0.39.0", path = "../../core" } -libp2p-identity = { version = "0.1.0", path = "../../identity" } +libp2p-identity = { version = "0.1.2", path = "../../identity" } rcgen = "0.10.0" ring = "0.16.20" thiserror = "1.0.40" diff --git a/transports/tls/src/certificate.rs b/transports/tls/src/certificate.rs index 6321fa26400..8531ade72fa 100644 --- a/transports/tls/src/certificate.rs +++ b/transports/tls/src/certificate.rs @@ -159,7 +159,7 @@ fn parse_unverified(der_input: &[u8]) -> Result { // required KeyType Type = 1; // required bytes Data = 2; // } - let public_key = identity::PublicKey::from_protobuf_encoding(&public_key) + let public_key = identity::PublicKey::try_decode_protobuf(&public_key) .map_err(|_| webpki::Error::UnknownIssuer)?; let ext = P2pExtension { public_key, @@ -215,7 +215,7 @@ fn make_libp2p_extension( // signature OCTET STRING // } let extension_content = { - let serialized_pubkey = identity_keypair.public().to_protobuf_encoding(); + let serialized_pubkey = identity_keypair.public().encode_protobuf(); yasna::encode_der(&(serialized_pubkey, signature)) };