-
Notifications
You must be signed in to change notification settings - Fork 962
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
Migrate from protoc to quick-protobuf #3066
Changes from all commits
47cfd97
d8bece6
9762456
4b26d2b
4eb3f1a
6eed862
b714785
3365856
cf4b705
41d8bab
152d9fd
0a0c42a
df4d1c1
afe818e
ec86e1f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,6 +44,7 @@ pub mod error; | |
|
||
use self::error::*; | ||
use crate::{keys_proto, PeerId}; | ||
use std::borrow::Cow; | ||
use std::convert::{TryFrom, TryInto}; | ||
|
||
/// Identity keypair of a node. | ||
|
@@ -147,12 +148,10 @@ impl Keypair { | |
|
||
/// Encode a private key as protobuf structure. | ||
pub fn to_protobuf_encoding(&self) -> Result<Vec<u8>, DecodingError> { | ||
use prost::Message; | ||
|
||
let pk = match self { | ||
Self::Ed25519(data) => keys_proto::PrivateKey { | ||
r#type: keys_proto::KeyType::Ed25519.into(), | ||
data: data.encode().into(), | ||
Type: keys_proto::KeyType::Ed25519, | ||
Data: Cow::from(data.encode().to_vec()), | ||
}, | ||
#[cfg(all(feature = "rsa", not(target_arch = "wasm32")))] | ||
Self::Rsa(_) => return Err(DecodingError::encoding_unsupported("RSA")), | ||
|
@@ -162,35 +161,31 @@ impl Keypair { | |
Self::Ecdsa(_) => return Err(DecodingError::encoding_unsupported("ECDSA")), | ||
}; | ||
|
||
Ok(pk.encode_to_vec()) | ||
Ok(quick_protobuf::serialize_into_vec(&pk).expect("Encoding to succeed.")) | ||
} | ||
|
||
/// Decode a private key from a protobuf structure and parse it as a [`Keypair`]. | ||
pub fn from_protobuf_encoding(bytes: &[u8]) -> Result<Keypair, DecodingError> { | ||
use prost::Message; | ||
|
||
let mut private_key = keys_proto::PrivateKey::decode(bytes) | ||
.map_err(|e| DecodingError::bad_protobuf("private key bytes", e)) | ||
.map(zeroize::Zeroizing::new)?; | ||
|
||
let key_type = keys_proto::KeyType::from_i32(private_key.r#type) | ||
.ok_or_else(|| DecodingError::unknown_key_type(private_key.r#type))?; | ||
let mut private_key = | ||
quick_protobuf::deserialize_from_slice::<keys_proto::PrivateKey>(bytes) | ||
.map_err(|e| DecodingError::bad_protobuf("private key bytes", e)) | ||
.map(zeroize::Zeroizing::new)?; | ||
|
||
match key_type { | ||
match private_key.Type { | ||
keys_proto::KeyType::Ed25519 => { | ||
ed25519::Keypair::decode(&mut private_key.data).map(Keypair::Ed25519) | ||
ed25519::Keypair::decode(private_key.Data.to_mut()).map(Keypair::Ed25519) | ||
} | ||
keys_proto::KeyType::Rsa => Err(DecodingError::decoding_unsupported("RSA")), | ||
keys_proto::KeyType::RSA => Err(DecodingError::decoding_unsupported("RSA")), | ||
keys_proto::KeyType::Secp256k1 => Err(DecodingError::decoding_unsupported("secp256k1")), | ||
keys_proto::KeyType::Ecdsa => Err(DecodingError::decoding_unsupported("ECDSA")), | ||
keys_proto::KeyType::ECDSA => Err(DecodingError::decoding_unsupported("ECDSA")), | ||
} | ||
} | ||
} | ||
|
||
impl zeroize::Zeroize for keys_proto::PrivateKey { | ||
impl zeroize::Zeroize for keys_proto::PrivateKey<'_> { | ||
fn zeroize(&mut self) { | ||
self.r#type.zeroize(); | ||
self.data.zeroize(); | ||
self.Type = keys_proto::KeyType::RSA; | ||
self.Data.to_mut().zeroize(); | ||
} | ||
} | ||
|
||
|
@@ -232,23 +227,15 @@ impl PublicKey { | |
/// Encode the public key into a protobuf structure for storage or | ||
/// exchange with other nodes. | ||
pub fn to_protobuf_encoding(&self) -> Vec<u8> { | ||
use prost::Message; | ||
|
||
let public_key = keys_proto::PublicKey::from(self); | ||
|
||
let mut buf = Vec::with_capacity(public_key.encoded_len()); | ||
public_key | ||
.encode(&mut buf) | ||
.expect("Vec<u8> provides capacity as needed"); | ||
buf | ||
quick_protobuf::serialize_into_vec(&public_key).expect("Encoding to succeed.") | ||
} | ||
|
||
/// Decode a public key from a protobuf structure, e.g. read from storage | ||
/// or received from another node. | ||
pub fn from_protobuf_encoding(bytes: &[u8]) -> Result<PublicKey, DecodingError> { | ||
use prost::Message; | ||
|
||
let pubkey = keys_proto::PublicKey::decode(bytes) | ||
let pubkey: keys_proto::PublicKey = quick_protobuf::deserialize_from_slice(bytes) | ||
.map_err(|e| DecodingError::bad_protobuf("public key bytes", e))?; | ||
|
||
pubkey.try_into() | ||
|
@@ -260,67 +247,64 @@ impl PublicKey { | |
} | ||
} | ||
|
||
impl From<&PublicKey> for keys_proto::PublicKey { | ||
impl From<&PublicKey> for keys_proto::PublicKey<'_> { | ||
fn from(key: &PublicKey) -> Self { | ||
match key { | ||
PublicKey::Ed25519(key) => keys_proto::PublicKey { | ||
r#type: keys_proto::KeyType::Ed25519 as i32, | ||
data: key.encode().to_vec(), | ||
Type: keys_proto::KeyType::Ed25519, | ||
Data: Cow::from(key.encode().to_vec()), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the usage of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seem like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should be able to add |
||
}, | ||
#[cfg(all(feature = "rsa", not(target_arch = "wasm32")))] | ||
PublicKey::Rsa(key) => keys_proto::PublicKey { | ||
r#type: keys_proto::KeyType::Rsa as i32, | ||
data: key.encode_x509(), | ||
Type: keys_proto::KeyType::RSA, | ||
Data: Cow::from(key.encode_x509()), | ||
}, | ||
#[cfg(feature = "secp256k1")] | ||
PublicKey::Secp256k1(key) => keys_proto::PublicKey { | ||
r#type: keys_proto::KeyType::Secp256k1 as i32, | ||
data: key.encode().to_vec(), | ||
Type: keys_proto::KeyType::Secp256k1, | ||
Data: Cow::from(key.encode().to_vec()), | ||
}, | ||
#[cfg(feature = "ecdsa")] | ||
PublicKey::Ecdsa(key) => keys_proto::PublicKey { | ||
r#type: keys_proto::KeyType::Ecdsa as i32, | ||
data: key.encode_der(), | ||
Type: keys_proto::KeyType::ECDSA, | ||
Data: Cow::from(key.encode_der()), | ||
}, | ||
} | ||
} | ||
} | ||
|
||
impl TryFrom<keys_proto::PublicKey> for PublicKey { | ||
impl TryFrom<keys_proto::PublicKey<'_>> for PublicKey { | ||
type Error = DecodingError; | ||
|
||
fn try_from(pubkey: keys_proto::PublicKey) -> Result<Self, Self::Error> { | ||
let key_type = keys_proto::KeyType::from_i32(pubkey.r#type) | ||
.ok_or_else(|| DecodingError::unknown_key_type(pubkey.r#type))?; | ||
|
||
match key_type { | ||
match pubkey.Type { | ||
keys_proto::KeyType::Ed25519 => { | ||
ed25519::PublicKey::decode(&pubkey.data).map(PublicKey::Ed25519) | ||
ed25519::PublicKey::decode(&pubkey.Data).map(PublicKey::Ed25519) | ||
} | ||
#[cfg(all(feature = "rsa", not(target_arch = "wasm32")))] | ||
keys_proto::KeyType::Rsa => { | ||
rsa::PublicKey::decode_x509(&pubkey.data).map(PublicKey::Rsa) | ||
keys_proto::KeyType::RSA => { | ||
rsa::PublicKey::decode_x509(&pubkey.Data).map(PublicKey::Rsa) | ||
} | ||
#[cfg(any(not(feature = "rsa"), target_arch = "wasm32"))] | ||
keys_proto::KeyType::Rsa => { | ||
keys_proto::KeyType::RSA => { | ||
log::debug!("support for RSA was disabled at compile-time"); | ||
Err(DecodingError::missing_feature("rsa")) | ||
} | ||
#[cfg(feature = "secp256k1")] | ||
keys_proto::KeyType::Secp256k1 => { | ||
secp256k1::PublicKey::decode(&pubkey.data).map(PublicKey::Secp256k1) | ||
secp256k1::PublicKey::decode(&pubkey.Data).map(PublicKey::Secp256k1) | ||
} | ||
#[cfg(not(feature = "secp256k1"))] | ||
keys_proto::KeyType::Secp256k1 => { | ||
log::debug!("support for secp256k1 was disabled at compile-time"); | ||
Err(DecodingError::missing_feature("secp256k1")) | ||
} | ||
#[cfg(feature = "ecdsa")] | ||
keys_proto::KeyType::Ecdsa => { | ||
ecdsa::PublicKey::decode_der(&pubkey.data).map(PublicKey::Ecdsa) | ||
keys_proto::KeyType::ECDSA => { | ||
ecdsa::PublicKey::decode_der(&pubkey.Data).map(PublicKey::Ecdsa) | ||
} | ||
#[cfg(not(feature = "ecdsa"))] | ||
keys_proto::KeyType::Ecdsa => { | ||
keys_proto::KeyType::ECDSA => { | ||
log::debug!("support for ECDSA was disabled at compile-time"); | ||
Err(DecodingError::missing_feature("ecdsa")) | ||
} | ||
|
@@ -349,9 +333,9 @@ mod tests { | |
#[test] | ||
fn keypair_from_protobuf_encoding() { | ||
// E.g. retrieved from an IPFS config file. | ||
let base_64_encoded = "CAESQL6vdKQuznQosTrW7FWI9At+XX7EBf0BnZLhb6w+N+XSQSdfInl6c7U4NuxXJlhKcRBlBw9d0tj2dfBIVf6mcPA="; | ||
let base_64_encoded = "RAgBEkC+r3SkLs50KLE61uxViPQLfl1+xAX9AZ2S4W+sPjfl0kEnXyJ5enO1ODbsVyZYSnEQZQcPXdLY9nXwSFX+pnDw"; | ||
let expected_peer_id = | ||
PeerId::from_str("12D3KooWEChVMMMzV8acJ53mJHrw1pQ27UAGkCxWXLJutbeUMvVu").unwrap(); | ||
Comment on lines
-352
to
-354
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why does this test change? We should not need to change this test. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This changes because of this #3066 (comment). I've been thinking about this and feel uneasy about it. Should we instead use this pattern tafia/quick-protobuf#202 (comment)? That way we're consistent with the official encoding. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think so yes. We need to be compatible here so this test should not need changing! |
||
PeerId::from_str("16VfNUn8nhtbaQVrQwwBekWYnWNJ4uJRv8pD6T1n1h3CFJr1MMgZM").unwrap(); | ||
|
||
let encoded = base64::decode(base_64_encoded).unwrap(); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -68,13 +68,6 @@ impl DecodingError { | |
} | ||
} | ||
|
||
pub(crate) fn unknown_key_type(key_type: i32) -> Self { | ||
Self { | ||
msg: format!("unknown key-type {key_type}"), | ||
source: None, | ||
} | ||
} | ||
Comment on lines
-71
to
-76
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎉 |
||
|
||
pub(crate) fn decoding_unsupported(key_type: &'static str) -> Self { | ||
Self { | ||
msg: format!("decoding {key_type} key from Protobuf is unsupported"), | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this is infallible by looking at the code. Unfortunately, I have not found a concrete answer in the docs or issues.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe so too. One error I could find is https://docs.rs/quick-protobuf/latest/src/quick_protobuf/writer.rs.html#379 which can't happen if the
Vec
is allocated correctly.The other error path comes from lines like https://github.com/tafia/quick-protobuf/blob/d977371e05170a016f03a80512b2a925468e3a1a/quick-protobuf/examples/pb_rs/data_types.rs#L199 which end up being the same error case as above so I think we are good.
I believe this function could actually be made infallible in
quick-protobuf
but I am not sure.