From a24e4221bdeeffc9260e6e08636bc7f8f231e168 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Mon, 7 Jun 2021 10:17:46 +0200 Subject: [PATCH] core/src/identity: Implement Keypair::from_protobuf_encoding for ed25519 (#2090) Implement `Keypair` decoding from Protobuf according to [peer id specification]. For now support ed25519 keys only. Future commits might add RSA secp256k1 and ecdsa. [peer id specification]: https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md#keys --- core/Cargo.toml | 1 + core/src/identity.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/core/Cargo.toml b/core/Cargo.toml index e4c0a3fd..dbc75844 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -40,6 +40,7 @@ ring = { version = "0.16.9", features = ["alloc", "std"], default-features = fal [dev-dependencies] async-std = { version = "1.6.2", features = ["attributes"] } +base64 = "0.13.0" criterion = "0.3" libp2p-mplex = { path = "../muxers/mplex" } libp2p-noise = { path = "../transports/noise" } diff --git a/core/src/identity.rs b/core/src/identity.rs index 2ea92a67..c0f3cb7d 100644 --- a/core/src/identity.rs +++ b/core/src/identity.rs @@ -115,6 +115,37 @@ impl Keypair { Secp256k1(pair) => PublicKey::Secp256k1(pair.public().clone()), } } + + /// Decode a private key from a protobuf structure and parse it as a [`Keypair`]. + pub fn from_protobuf_encoding(bytes: &[u8]) -> Result { + use prost::Message; + + let mut private_key = keys_proto::PrivateKey::decode(bytes) + .map_err(|e| DecodingError::new("Protobuf").source(e)) + .map(zeroize::Zeroizing::new)?; + + let key_type = keys_proto::KeyType::from_i32(private_key.r#type) + .ok_or_else(|| DecodingError::new(format!("unknown key type: {}", private_key.r#type)))?; + + match key_type { + keys_proto::KeyType::Ed25519 => { + ed25519::Keypair::decode(&mut private_key.data).map(Keypair::Ed25519) + }, + keys_proto::KeyType::Rsa => { + Err(DecodingError::new("Decoding RSA key from Protobuf is unsupported.")) + }, + keys_proto::KeyType::Secp256k1 => { + Err(DecodingError::new("Decoding Secp256k1 key from Protobuf is unsupported.")) + }, + } + } +} + +impl zeroize::Zeroize for keys_proto::PrivateKey { + fn zeroize(&mut self) { + self.r#type.zeroize(); + self.data.zeroize(); + } } /// The public key of a node's identity keypair. @@ -219,3 +250,22 @@ impl PublicKey { } } +#[cfg(test)] +mod tests { + use super::*; + use std::str::FromStr; + + #[test] + fn keypair_from_protobuf_encoding() { + // E.g. retrieved from an IPFS config file. + let base_64_encoded = "CAESQL6vdKQuznQosTrW7FWI9At+XX7EBf0BnZLhb6w+N+XSQSdfInl6c7U4NuxXJlhKcRBlBw9d0tj2dfBIVf6mcPA="; + let expected_peer_id = PeerId::from_str("12D3KooWEChVMMMzV8acJ53mJHrw1pQ27UAGkCxWXLJutbeUMvVu").unwrap(); + + let encoded = base64::decode(base_64_encoded).unwrap(); + + let keypair = Keypair::from_protobuf_encoding(&encoded).unwrap(); + let peer_id = keypair.public().into_peer_id(); + + assert_eq!(expected_peer_id, peer_id); + } +}