mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-06-26 00:01:33 +00:00
feat: introduce libp2p-identity
crate
This patch combines the `libp2p_core::identity` and `libp2p_core::peer_id` modules into a new crate: `libp2p-identity`. Resolves https://github.com/libp2p/rust-libp2p/issues/3349. Pull-Request: #3350.
This commit is contained in:
@ -2,8 +2,6 @@ syntax = "proto3";
|
||||
|
||||
package envelope_proto;
|
||||
|
||||
import "keys.proto";
|
||||
|
||||
// Envelope encloses a signed payload produced by a peer, along with the public
|
||||
// key of the keypair it was signed with so that it can be statelessly validated
|
||||
// by the receiver.
|
||||
@ -14,7 +12,7 @@ import "keys.proto";
|
||||
message Envelope {
|
||||
// public_key is the public key of the keypair the enclosed payload was
|
||||
// signed with.
|
||||
keys_proto.PublicKey public_key = 1;
|
||||
bytes public_key = 1;
|
||||
|
||||
// payload_type encodes the type of payload, so that it can be deserialized
|
||||
// deterministically.
|
||||
|
@ -16,7 +16,7 @@ use super::*;
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Debug, Default, PartialEq, Clone)]
|
||||
pub struct Envelope {
|
||||
pub public_key: Option<keys_proto::PublicKey>,
|
||||
pub public_key: Vec<u8>,
|
||||
pub payload_type: Vec<u8>,
|
||||
pub payload: Vec<u8>,
|
||||
pub signature: Vec<u8>,
|
||||
@ -27,7 +27,7 @@ impl<'a> MessageRead<'a> for Envelope {
|
||||
let mut msg = Self::default();
|
||||
while !r.is_eof() {
|
||||
match r.next_tag(bytes) {
|
||||
Ok(10) => msg.public_key = Some(r.read_message::<keys_proto::PublicKey>(bytes)?),
|
||||
Ok(10) => msg.public_key = r.read_bytes(bytes)?.to_owned(),
|
||||
Ok(18) => msg.payload_type = r.read_bytes(bytes)?.to_owned(),
|
||||
Ok(26) => msg.payload = r.read_bytes(bytes)?.to_owned(),
|
||||
Ok(42) => msg.signature = r.read_bytes(bytes)?.to_owned(),
|
||||
@ -42,14 +42,14 @@ impl<'a> MessageRead<'a> for Envelope {
|
||||
impl MessageWrite for Envelope {
|
||||
fn get_size(&self) -> usize {
|
||||
0
|
||||
+ self.public_key.as_ref().map_or(0, |m| 1 + sizeof_len((m).get_size()))
|
||||
+ if self.public_key.is_empty() { 0 } else { 1 + sizeof_len((&self.public_key).len()) }
|
||||
+ if self.payload_type.is_empty() { 0 } else { 1 + sizeof_len((&self.payload_type).len()) }
|
||||
+ if self.payload.is_empty() { 0 } else { 1 + sizeof_len((&self.payload).len()) }
|
||||
+ if self.signature.is_empty() { 0 } else { 1 + sizeof_len((&self.signature).len()) }
|
||||
}
|
||||
|
||||
fn write_message<W: WriterBackend>(&self, w: &mut Writer<W>) -> Result<()> {
|
||||
if let Some(ref s) = self.public_key { w.write_with_tag(10, |w| w.write_message(s))?; }
|
||||
if !self.public_key.is_empty() { w.write_with_tag(10, |w| w.write_bytes(&**&self.public_key))?; }
|
||||
if !self.payload_type.is_empty() { w.write_with_tag(18, |w| w.write_bytes(&**&self.payload_type))?; }
|
||||
if !self.payload.is_empty() { w.write_with_tag(26, |w| w.write_bytes(&**&self.payload))?; }
|
||||
if !self.signature.is_empty() { w.write_with_tag(42, |w| w.write_bytes(&**&self.signature))?; }
|
||||
|
@ -1,20 +0,0 @@
|
||||
syntax = "proto2";
|
||||
|
||||
package keys_proto;
|
||||
|
||||
enum KeyType {
|
||||
RSA = 0;
|
||||
Ed25519 = 1;
|
||||
Secp256k1 = 2;
|
||||
ECDSA = 3;
|
||||
}
|
||||
|
||||
message PublicKey {
|
||||
required KeyType Type = 1;
|
||||
required bytes Data = 2;
|
||||
}
|
||||
|
||||
message PrivateKey {
|
||||
required KeyType Type = 1;
|
||||
required bytes Data = 2;
|
||||
}
|
@ -1,125 +0,0 @@
|
||||
// Automatically generated rust module for 'keys.proto' file
|
||||
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(unknown_lints)]
|
||||
#![allow(clippy::all)]
|
||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||
|
||||
|
||||
use quick_protobuf::{MessageInfo, MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result};
|
||||
use quick_protobuf::sizeofs::*;
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum KeyType {
|
||||
RSA = 0,
|
||||
Ed25519 = 1,
|
||||
Secp256k1 = 2,
|
||||
ECDSA = 3,
|
||||
}
|
||||
|
||||
impl Default for KeyType {
|
||||
fn default() -> Self {
|
||||
KeyType::RSA
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for KeyType {
|
||||
fn from(i: i32) -> Self {
|
||||
match i {
|
||||
0 => KeyType::RSA,
|
||||
1 => KeyType::Ed25519,
|
||||
2 => KeyType::Secp256k1,
|
||||
3 => KeyType::ECDSA,
|
||||
_ => Self::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for KeyType {
|
||||
fn from(s: &'a str) -> Self {
|
||||
match s {
|
||||
"RSA" => KeyType::RSA,
|
||||
"Ed25519" => KeyType::Ed25519,
|
||||
"Secp256k1" => KeyType::Secp256k1,
|
||||
"ECDSA" => KeyType::ECDSA,
|
||||
_ => Self::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Debug, Default, PartialEq, Clone)]
|
||||
pub struct PublicKey {
|
||||
pub Type: keys_proto::KeyType,
|
||||
pub Data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl<'a> MessageRead<'a> for PublicKey {
|
||||
fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result<Self> {
|
||||
let mut msg = Self::default();
|
||||
while !r.is_eof() {
|
||||
match r.next_tag(bytes) {
|
||||
Ok(8) => msg.Type = r.read_enum(bytes)?,
|
||||
Ok(18) => msg.Data = r.read_bytes(bytes)?.to_owned(),
|
||||
Ok(t) => { r.read_unknown(bytes, t)?; }
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
Ok(msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageWrite for PublicKey {
|
||||
fn get_size(&self) -> usize {
|
||||
0
|
||||
+ 1 + sizeof_varint(*(&self.Type) as u64)
|
||||
+ 1 + sizeof_len((&self.Data).len())
|
||||
}
|
||||
|
||||
fn write_message<W: WriterBackend>(&self, w: &mut Writer<W>) -> Result<()> {
|
||||
w.write_with_tag(8, |w| w.write_enum(*&self.Type as i32))?;
|
||||
w.write_with_tag(18, |w| w.write_bytes(&**&self.Data))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Debug, Default, PartialEq, Clone)]
|
||||
pub struct PrivateKey {
|
||||
pub Type: keys_proto::KeyType,
|
||||
pub Data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl<'a> MessageRead<'a> for PrivateKey {
|
||||
fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result<Self> {
|
||||
let mut msg = Self::default();
|
||||
while !r.is_eof() {
|
||||
match r.next_tag(bytes) {
|
||||
Ok(8) => msg.Type = r.read_enum(bytes)?,
|
||||
Ok(18) => msg.Data = r.read_bytes(bytes)?.to_owned(),
|
||||
Ok(t) => { r.read_unknown(bytes, t)?; }
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
Ok(msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageWrite for PrivateKey {
|
||||
fn get_size(&self) -> usize {
|
||||
0
|
||||
+ 1 + sizeof_varint(*(&self.Type) as u64)
|
||||
+ 1 + sizeof_len((&self.Data).len())
|
||||
}
|
||||
|
||||
fn write_message<W: WriterBackend>(&self, w: &mut Writer<W>) -> Result<()> {
|
||||
w.write_with_tag(8, |w| w.write_enum(*&self.Type as i32))?;
|
||||
w.write_with_tag(18, |w| w.write_bytes(&**&self.Data))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
// Automatically generated mod.rs
|
||||
pub mod envelope_proto;
|
||||
pub mod keys_proto;
|
||||
pub mod peer_record_proto;
|
||||
|
@ -1,385 +0,0 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
//! A node's network identity keys.
|
||||
//!
|
||||
//! Such identity keys can be randomly generated on every startup,
|
||||
//! but using already existing, fixed keys is usually required.
|
||||
//! Though libp2p uses other crates (e.g. `ed25519_dalek`) internally,
|
||||
//! such details are not exposed as part of libp2p's public interface
|
||||
//! to keep them easily upgradable or replaceable (e.g. to `ed25519_zebra`).
|
||||
//! Consequently, keys of external ed25519 or secp256k1 crates cannot be
|
||||
//! directly converted into libp2p network identities.
|
||||
//! Instead, loading fixed keys must use the standard, thus more portable
|
||||
//! binary representation of the specific key type
|
||||
//! (e.g. [ed25519 binary format](https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.5)).
|
||||
//! All key types have functions to enable conversion to/from their binary representations.
|
||||
|
||||
#[cfg(feature = "ecdsa")]
|
||||
pub mod ecdsa;
|
||||
pub mod ed25519;
|
||||
#[cfg(all(feature = "rsa", not(target_arch = "wasm32")))]
|
||||
pub mod rsa;
|
||||
#[cfg(feature = "secp256k1")]
|
||||
pub mod secp256k1;
|
||||
|
||||
pub mod error;
|
||||
|
||||
use self::error::*;
|
||||
use crate::{proto, PeerId};
|
||||
use quick_protobuf::{BytesReader, Writer};
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
|
||||
/// Identity keypair of a node.
|
||||
///
|
||||
/// # Example: Generating RSA keys with OpenSSL
|
||||
///
|
||||
/// ```text
|
||||
/// openssl genrsa -out private.pem 2048
|
||||
/// openssl pkcs8 -in private.pem -inform PEM -topk8 -out private.pk8 -outform DER -nocrypt
|
||||
/// rm private.pem # optional
|
||||
/// ```
|
||||
///
|
||||
/// Loading the keys:
|
||||
///
|
||||
/// ```text
|
||||
/// let mut bytes = std::fs::read("private.pk8").unwrap();
|
||||
/// let keypair = Keypair::rsa_from_pkcs8(&mut bytes);
|
||||
/// ```
|
||||
///
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum Keypair {
|
||||
/// An Ed25519 keypair.
|
||||
Ed25519(ed25519::Keypair),
|
||||
/// An RSA keypair.
|
||||
#[cfg(all(feature = "rsa", not(target_arch = "wasm32")))]
|
||||
Rsa(rsa::Keypair),
|
||||
/// A Secp256k1 keypair.
|
||||
#[cfg(feature = "secp256k1")]
|
||||
Secp256k1(secp256k1::Keypair),
|
||||
/// An ECDSA keypair.
|
||||
#[cfg(feature = "ecdsa")]
|
||||
Ecdsa(ecdsa::Keypair),
|
||||
}
|
||||
|
||||
impl Keypair {
|
||||
/// Generate a new Ed25519 keypair.
|
||||
pub fn generate_ed25519() -> Keypair {
|
||||
Keypair::Ed25519(ed25519::Keypair::generate())
|
||||
}
|
||||
|
||||
/// Generate a new Secp256k1 keypair.
|
||||
#[cfg(feature = "secp256k1")]
|
||||
pub fn generate_secp256k1() -> Keypair {
|
||||
Keypair::Secp256k1(secp256k1::Keypair::generate())
|
||||
}
|
||||
|
||||
/// Generate a new ECDSA keypair.
|
||||
#[cfg(feature = "ecdsa")]
|
||||
pub fn generate_ecdsa() -> Keypair {
|
||||
Keypair::Ecdsa(ecdsa::Keypair::generate())
|
||||
}
|
||||
|
||||
/// Decode an keypair from a DER-encoded secret key in PKCS#8 PrivateKeyInfo
|
||||
/// format (i.e. unencrypted) as defined in [RFC5208].
|
||||
///
|
||||
/// [RFC5208]: https://tools.ietf.org/html/rfc5208#section-5
|
||||
#[cfg(all(feature = "rsa", not(target_arch = "wasm32")))]
|
||||
pub fn rsa_from_pkcs8(pkcs8_der: &mut [u8]) -> Result<Keypair, DecodingError> {
|
||||
rsa::Keypair::from_pkcs8(pkcs8_der).map(Keypair::Rsa)
|
||||
}
|
||||
|
||||
/// Decode a keypair from a DER-encoded Secp256k1 secret key in an ECPrivateKey
|
||||
/// structure as defined in [RFC5915].
|
||||
///
|
||||
/// [RFC5915]: https://tools.ietf.org/html/rfc5915
|
||||
#[cfg(feature = "secp256k1")]
|
||||
pub fn secp256k1_from_der(der: &mut [u8]) -> Result<Keypair, DecodingError> {
|
||||
secp256k1::SecretKey::from_der(der)
|
||||
.map(|sk| Keypair::Secp256k1(secp256k1::Keypair::from(sk)))
|
||||
}
|
||||
|
||||
/// Sign a message using the private key of this keypair, producing
|
||||
/// a signature that can be verified using the corresponding public key.
|
||||
pub fn sign(&self, msg: &[u8]) -> Result<Vec<u8>, SigningError> {
|
||||
use Keypair::*;
|
||||
match self {
|
||||
Ed25519(ref pair) => Ok(pair.sign(msg)),
|
||||
#[cfg(all(feature = "rsa", not(target_arch = "wasm32")))]
|
||||
Rsa(ref pair) => pair.sign(msg),
|
||||
#[cfg(feature = "secp256k1")]
|
||||
Secp256k1(ref pair) => pair.secret().sign(msg),
|
||||
#[cfg(feature = "ecdsa")]
|
||||
Ecdsa(ref pair) => Ok(pair.secret().sign(msg)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the public key of this keypair.
|
||||
pub fn public(&self) -> PublicKey {
|
||||
use Keypair::*;
|
||||
match self {
|
||||
Ed25519(pair) => PublicKey::Ed25519(pair.public()),
|
||||
#[cfg(all(feature = "rsa", not(target_arch = "wasm32")))]
|
||||
Rsa(pair) => PublicKey::Rsa(pair.public()),
|
||||
#[cfg(feature = "secp256k1")]
|
||||
Secp256k1(pair) => PublicKey::Secp256k1(pair.public().clone()),
|
||||
#[cfg(feature = "ecdsa")]
|
||||
Ecdsa(pair) => PublicKey::Ecdsa(pair.public().clone()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode a private key as protobuf structure.
|
||||
pub fn to_protobuf_encoding(&self) -> Result<Vec<u8>, DecodingError> {
|
||||
use quick_protobuf::MessageWrite;
|
||||
|
||||
let pk = match self {
|
||||
Self::Ed25519(data) => proto::PrivateKey {
|
||||
Type: proto::KeyType::Ed25519,
|
||||
Data: data.encode().to_vec(),
|
||||
},
|
||||
#[cfg(all(feature = "rsa", not(target_arch = "wasm32")))]
|
||||
Self::Rsa(_) => return Err(DecodingError::encoding_unsupported("RSA")),
|
||||
#[cfg(feature = "secp256k1")]
|
||||
Self::Secp256k1(_) => return Err(DecodingError::encoding_unsupported("secp256k1")),
|
||||
#[cfg(feature = "ecdsa")]
|
||||
Self::Ecdsa(_) => return Err(DecodingError::encoding_unsupported("ECDSA")),
|
||||
};
|
||||
|
||||
let mut buf = Vec::with_capacity(pk.get_size());
|
||||
let mut writer = Writer::new(&mut buf);
|
||||
pk.write_message(&mut writer).expect("Encoding to succeed");
|
||||
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
/// 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 quick_protobuf::MessageRead;
|
||||
|
||||
let mut reader = BytesReader::from_bytes(bytes);
|
||||
let mut private_key = proto::PrivateKey::from_reader(&mut reader, bytes)
|
||||
.map_err(|e| DecodingError::bad_protobuf("private key bytes", e))
|
||||
.map(zeroize::Zeroizing::new)?;
|
||||
|
||||
match private_key.Type {
|
||||
proto::KeyType::Ed25519 => {
|
||||
ed25519::Keypair::decode(&mut private_key.Data).map(Keypair::Ed25519)
|
||||
}
|
||||
proto::KeyType::RSA => Err(DecodingError::decoding_unsupported("RSA")),
|
||||
proto::KeyType::Secp256k1 => Err(DecodingError::decoding_unsupported("secp256k1")),
|
||||
proto::KeyType::ECDSA => Err(DecodingError::decoding_unsupported("ECDSA")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl zeroize::Zeroize for proto::PrivateKey {
|
||||
fn zeroize(&mut self) {
|
||||
// KeyType cannot be zeroized.
|
||||
self.Type = proto::KeyType::default();
|
||||
self.Data.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
/// The public key of a node's identity keypair.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub enum PublicKey {
|
||||
/// A public Ed25519 key.
|
||||
Ed25519(ed25519::PublicKey),
|
||||
#[cfg(all(feature = "rsa", not(target_arch = "wasm32")))]
|
||||
/// A public RSA key.
|
||||
Rsa(rsa::PublicKey),
|
||||
#[cfg(feature = "secp256k1")]
|
||||
/// A public Secp256k1 key.
|
||||
Secp256k1(secp256k1::PublicKey),
|
||||
/// A public ECDSA key.
|
||||
#[cfg(feature = "ecdsa")]
|
||||
Ecdsa(ecdsa::PublicKey),
|
||||
}
|
||||
|
||||
impl PublicKey {
|
||||
/// Verify a signature for a message using this public key, i.e. check
|
||||
/// that the signature has been produced by the corresponding
|
||||
/// private key (authenticity), and that the message has not been
|
||||
/// tampered with (integrity).
|
||||
#[must_use]
|
||||
pub fn verify(&self, msg: &[u8], sig: &[u8]) -> bool {
|
||||
use PublicKey::*;
|
||||
match self {
|
||||
Ed25519(pk) => pk.verify(msg, sig),
|
||||
#[cfg(all(feature = "rsa", not(target_arch = "wasm32")))]
|
||||
Rsa(pk) => pk.verify(msg, sig),
|
||||
#[cfg(feature = "secp256k1")]
|
||||
Secp256k1(pk) => pk.verify(msg, sig),
|
||||
#[cfg(feature = "ecdsa")]
|
||||
Ecdsa(pk) => pk.verify(msg, sig),
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode the public key into a protobuf structure for storage or
|
||||
/// exchange with other nodes.
|
||||
pub fn to_protobuf_encoding(&self) -> Vec<u8> {
|
||||
use quick_protobuf::MessageWrite;
|
||||
|
||||
let public_key = proto::PublicKey::from(self);
|
||||
|
||||
let mut buf = Vec::with_capacity(public_key.get_size());
|
||||
let mut writer = Writer::new(&mut buf);
|
||||
public_key
|
||||
.write_message(&mut writer)
|
||||
.expect("Encoding to succeed");
|
||||
|
||||
buf
|
||||
}
|
||||
|
||||
/// 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 quick_protobuf::MessageRead;
|
||||
|
||||
let mut reader = BytesReader::from_bytes(bytes);
|
||||
|
||||
let pubkey = proto::PublicKey::from_reader(&mut reader, bytes)
|
||||
.map_err(|e| DecodingError::bad_protobuf("public key bytes", e))?;
|
||||
|
||||
pubkey.try_into()
|
||||
}
|
||||
|
||||
/// Convert the `PublicKey` into the corresponding `PeerId`.
|
||||
pub fn to_peer_id(&self) -> PeerId {
|
||||
self.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PublicKey> for proto::PublicKey {
|
||||
fn from(key: &PublicKey) -> Self {
|
||||
match key {
|
||||
PublicKey::Ed25519(key) => proto::PublicKey {
|
||||
Type: proto::KeyType::Ed25519,
|
||||
Data: key.encode().to_vec(),
|
||||
},
|
||||
#[cfg(all(feature = "rsa", not(target_arch = "wasm32")))]
|
||||
PublicKey::Rsa(key) => proto::PublicKey {
|
||||
Type: proto::KeyType::RSA,
|
||||
Data: key.encode_x509(),
|
||||
},
|
||||
#[cfg(feature = "secp256k1")]
|
||||
PublicKey::Secp256k1(key) => proto::PublicKey {
|
||||
Type: proto::KeyType::Secp256k1,
|
||||
Data: key.encode().to_vec(),
|
||||
},
|
||||
#[cfg(feature = "ecdsa")]
|
||||
PublicKey::Ecdsa(key) => proto::PublicKey {
|
||||
Type: proto::KeyType::ECDSA,
|
||||
Data: key.encode_der(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<proto::PublicKey> for PublicKey {
|
||||
type Error = DecodingError;
|
||||
|
||||
fn try_from(pubkey: proto::PublicKey) -> Result<Self, Self::Error> {
|
||||
match pubkey.Type {
|
||||
proto::KeyType::Ed25519 => {
|
||||
ed25519::PublicKey::decode(&pubkey.Data).map(PublicKey::Ed25519)
|
||||
}
|
||||
#[cfg(all(feature = "rsa", not(target_arch = "wasm32")))]
|
||||
proto::KeyType::RSA => rsa::PublicKey::decode_x509(&pubkey.Data).map(PublicKey::Rsa),
|
||||
#[cfg(any(not(feature = "rsa"), target_arch = "wasm32"))]
|
||||
proto::KeyType::RSA => {
|
||||
log::debug!("support for RSA was disabled at compile-time");
|
||||
Err(DecodingError::missing_feature("rsa"))
|
||||
}
|
||||
#[cfg(feature = "secp256k1")]
|
||||
proto::KeyType::Secp256k1 => {
|
||||
secp256k1::PublicKey::decode(&pubkey.Data).map(PublicKey::Secp256k1)
|
||||
}
|
||||
#[cfg(not(feature = "secp256k1"))]
|
||||
proto::KeyType::Secp256k1 => {
|
||||
log::debug!("support for secp256k1 was disabled at compile-time");
|
||||
Err(DecodingError::missing_feature("secp256k1"))
|
||||
}
|
||||
#[cfg(feature = "ecdsa")]
|
||||
proto::KeyType::ECDSA => {
|
||||
ecdsa::PublicKey::decode_der(&pubkey.Data).map(PublicKey::Ecdsa)
|
||||
}
|
||||
#[cfg(not(feature = "ecdsa"))]
|
||||
proto::KeyType::ECDSA => {
|
||||
log::debug!("support for ECDSA was disabled at compile-time");
|
||||
Err(DecodingError::missing_feature("ecdsa"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use base64::prelude::*;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
fn keypair_protobuf_roundtrip() {
|
||||
let expected_keypair = Keypair::generate_ed25519();
|
||||
let expected_peer_id = expected_keypair.public().to_peer_id();
|
||||
|
||||
let encoded = expected_keypair.to_protobuf_encoding().unwrap();
|
||||
|
||||
let keypair = Keypair::from_protobuf_encoding(&encoded).unwrap();
|
||||
let peer_id = keypair.public().to_peer_id();
|
||||
|
||||
assert_eq!(expected_peer_id, peer_id);
|
||||
}
|
||||
|
||||
#[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_STANDARD.decode(base_64_encoded).unwrap();
|
||||
|
||||
let keypair = Keypair::from_protobuf_encoding(&encoded).unwrap();
|
||||
let peer_id = keypair.public().to_peer_id();
|
||||
|
||||
assert_eq!(expected_peer_id, peer_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn public_key_implements_hash() {
|
||||
use std::hash::Hash;
|
||||
|
||||
fn assert_implements_hash<T: Hash>() {}
|
||||
|
||||
assert_implements_hash::<PublicKey>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn public_key_implements_ord() {
|
||||
use std::cmp::Ord;
|
||||
|
||||
fn assert_implements_ord<T: Ord>() {}
|
||||
|
||||
assert_implements_ord::<PublicKey>();
|
||||
}
|
||||
}
|
@ -1,261 +0,0 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
//! ECDSA keys with secp256r1 curve support.
|
||||
|
||||
use super::error::DecodingError;
|
||||
use core::cmp;
|
||||
use core::fmt;
|
||||
use core::hash;
|
||||
use p256::{
|
||||
ecdsa::{
|
||||
signature::{Signer, Verifier},
|
||||
Signature, SigningKey, VerifyingKey,
|
||||
},
|
||||
EncodedPoint,
|
||||
};
|
||||
use void::Void;
|
||||
|
||||
/// An ECDSA keypair.
|
||||
#[derive(Clone)]
|
||||
pub struct Keypair {
|
||||
secret: SecretKey,
|
||||
public: PublicKey,
|
||||
}
|
||||
|
||||
impl Keypair {
|
||||
/// Generate a new random ECDSA keypair.
|
||||
pub fn generate() -> Keypair {
|
||||
Keypair::from(SecretKey::generate())
|
||||
}
|
||||
|
||||
/// Sign a message using the private key of this keypair.
|
||||
pub fn sign(&self, msg: &[u8]) -> Vec<u8> {
|
||||
self.secret.sign(msg)
|
||||
}
|
||||
|
||||
/// Get the public key of this keypair.
|
||||
pub fn public(&self) -> &PublicKey {
|
||||
&self.public
|
||||
}
|
||||
|
||||
/// Get the secret key of this keypair.
|
||||
pub fn secret(&self) -> &SecretKey {
|
||||
&self.secret
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Keypair {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Keypair")
|
||||
.field("public", &self.public())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Promote an ECDSA secret key into a keypair.
|
||||
impl From<SecretKey> for Keypair {
|
||||
fn from(secret: SecretKey) -> Keypair {
|
||||
let public = PublicKey(VerifyingKey::from(&secret.0));
|
||||
Keypair { secret, public }
|
||||
}
|
||||
}
|
||||
|
||||
/// Demote an ECDSA keypair to a secret key.
|
||||
impl From<Keypair> for SecretKey {
|
||||
fn from(kp: Keypair) -> SecretKey {
|
||||
kp.secret
|
||||
}
|
||||
}
|
||||
|
||||
/// An ECDSA secret key.
|
||||
#[derive(Clone)]
|
||||
pub struct SecretKey(SigningKey);
|
||||
|
||||
impl SecretKey {
|
||||
/// Generate a new random ECDSA secret key.
|
||||
pub fn generate() -> SecretKey {
|
||||
SecretKey(SigningKey::random(&mut rand::thread_rng()))
|
||||
}
|
||||
|
||||
/// Sign a message with this secret key, producing a DER-encoded ECDSA signature.
|
||||
pub fn sign(&self, msg: &[u8]) -> Vec<u8> {
|
||||
let signature: p256::ecdsa::DerSignature = self.0.sign(msg);
|
||||
|
||||
signature.as_bytes().to_owned()
|
||||
}
|
||||
|
||||
/// Encode a secret key into a byte buffer.
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
self.0.to_bytes().to_vec()
|
||||
}
|
||||
|
||||
/// Decode a secret key from a byte buffer.
|
||||
pub fn from_bytes(buf: &[u8]) -> Result<Self, DecodingError> {
|
||||
SigningKey::from_bytes(buf)
|
||||
.map_err(|err| DecodingError::failed_to_parse("ecdsa p256 secret key", err))
|
||||
.map(SecretKey)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SecretKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "SecretKey")
|
||||
}
|
||||
}
|
||||
|
||||
/// An ECDSA public key.
|
||||
#[derive(Clone, Eq, PartialOrd, Ord)]
|
||||
pub struct PublicKey(VerifyingKey);
|
||||
|
||||
impl PublicKey {
|
||||
/// Verify an ECDSA signature on a message using the public key.
|
||||
pub fn verify(&self, msg: &[u8], sig: &[u8]) -> bool {
|
||||
let sig = match Signature::from_der(sig) {
|
||||
Ok(sig) => sig,
|
||||
Err(_) => return false,
|
||||
};
|
||||
self.0.verify(msg, &sig).is_ok()
|
||||
}
|
||||
|
||||
/// Decode a public key from a byte buffer without compression.
|
||||
pub fn from_bytes(k: &[u8]) -> Result<PublicKey, DecodingError> {
|
||||
let enc_pt = EncodedPoint::from_bytes(k)
|
||||
.map_err(|e| DecodingError::failed_to_parse("ecdsa p256 encoded point", e))?;
|
||||
|
||||
VerifyingKey::from_encoded_point(&enc_pt)
|
||||
.map_err(|err| DecodingError::failed_to_parse("ecdsa p256 public key", err))
|
||||
.map(PublicKey)
|
||||
}
|
||||
|
||||
/// Encode a public key into a byte buffer without compression.
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
self.0.to_encoded_point(false).as_bytes().to_owned()
|
||||
}
|
||||
|
||||
/// Encode a public key into a DER encoded byte buffer as defined by SEC1 standard.
|
||||
pub fn encode_der(&self) -> Vec<u8> {
|
||||
let buf = self.to_bytes();
|
||||
Self::add_asn1_header(&buf)
|
||||
}
|
||||
|
||||
/// Decode a public key into a DER encoded byte buffer as defined by SEC1 standard.
|
||||
pub fn decode_der(k: &[u8]) -> Result<PublicKey, DecodingError> {
|
||||
let buf = Self::del_asn1_header(k).ok_or_else(|| {
|
||||
DecodingError::failed_to_parse::<Void, _>("ASN.1-encoded ecdsa p256 public key", None)
|
||||
})?;
|
||||
Self::from_bytes(buf)
|
||||
}
|
||||
|
||||
// ecPublicKey (ANSI X9.62 public key type) OID: 1.2.840.10045.2.1
|
||||
const EC_PUBLIC_KEY_OID: [u8; 9] = [0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01];
|
||||
// secp256r1 OID: 1.2.840.10045.3.1.7
|
||||
const SECP_256_R1_OID: [u8; 10] = [0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07];
|
||||
|
||||
// Add ASN1 header.
|
||||
fn add_asn1_header(key_buf: &[u8]) -> Vec<u8> {
|
||||
// ASN.1 struct type and length.
|
||||
let mut asn1_buf = vec![
|
||||
0x30,
|
||||
0x00,
|
||||
0x30,
|
||||
(Self::EC_PUBLIC_KEY_OID.len() + Self::SECP_256_R1_OID.len()) as u8,
|
||||
];
|
||||
// Append OIDs.
|
||||
asn1_buf.extend_from_slice(&Self::EC_PUBLIC_KEY_OID);
|
||||
asn1_buf.extend_from_slice(&Self::SECP_256_R1_OID);
|
||||
// Append key bitstring type and length.
|
||||
asn1_buf.extend_from_slice(&[0x03, (key_buf.len() + 1) as u8, 0x00]);
|
||||
// Append key bitstring value.
|
||||
asn1_buf.extend_from_slice(key_buf);
|
||||
// Update overall length field.
|
||||
asn1_buf[1] = (asn1_buf.len() - 2) as u8;
|
||||
|
||||
asn1_buf
|
||||
}
|
||||
|
||||
// Check and remove ASN.1 header.
|
||||
fn del_asn1_header(asn1_buf: &[u8]) -> Option<&[u8]> {
|
||||
let oids_len = Self::EC_PUBLIC_KEY_OID.len() + Self::SECP_256_R1_OID.len();
|
||||
let asn1_head = asn1_buf.get(..4)?;
|
||||
let oids_buf = asn1_buf.get(4..4 + oids_len)?;
|
||||
let bitstr_head = asn1_buf.get(4 + oids_len..4 + oids_len + 3)?;
|
||||
|
||||
// Sanity check
|
||||
if asn1_head[0] != 0x30
|
||||
|| asn1_head[2] != 0x30
|
||||
|| asn1_head[3] as usize != oids_len
|
||||
|| oids_buf[..Self::EC_PUBLIC_KEY_OID.len()] != Self::EC_PUBLIC_KEY_OID
|
||||
|| oids_buf[Self::EC_PUBLIC_KEY_OID.len()..] != Self::SECP_256_R1_OID
|
||||
|| bitstr_head[0] != 0x03
|
||||
|| bitstr_head[2] != 0x00
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
let key_len = bitstr_head[1].checked_sub(1)? as usize;
|
||||
let key_buf = asn1_buf.get(4 + oids_len + 3..4 + oids_len + 3 + key_len)?;
|
||||
Some(key_buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for PublicKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("PublicKey(asn.1 uncompressed): ")?;
|
||||
for byte in &self.encode_der() {
|
||||
write!(f, "{byte:x}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl cmp::PartialEq for PublicKey {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.to_bytes().eq(&other.to_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl hash::Hash for PublicKey {
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||
self.to_bytes().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn sign_verify() {
|
||||
let pair = Keypair::generate();
|
||||
let pk = pair.public();
|
||||
|
||||
let msg = "hello world".as_bytes();
|
||||
let sig = pair.sign(msg);
|
||||
assert!(pk.verify(msg, &sig));
|
||||
|
||||
let mut invalid_sig = sig.clone();
|
||||
invalid_sig[3..6].copy_from_slice(&[10, 23, 42]);
|
||||
assert!(!pk.verify(msg, &invalid_sig));
|
||||
|
||||
let invalid_msg = "h3ll0 w0rld".as_bytes();
|
||||
assert!(!pk.verify(invalid_msg, &sig));
|
||||
}
|
||||
}
|
@ -1,271 +0,0 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
//! Ed25519 keys.
|
||||
|
||||
use super::error::DecodingError;
|
||||
use core::cmp;
|
||||
use core::fmt;
|
||||
use core::hash;
|
||||
use ed25519_dalek::{self as ed25519, Signer as _, Verifier as _};
|
||||
use rand::RngCore;
|
||||
use std::convert::TryFrom;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
/// An Ed25519 keypair.
|
||||
pub struct Keypair(ed25519::Keypair);
|
||||
|
||||
impl Keypair {
|
||||
/// Generate a new random Ed25519 keypair.
|
||||
pub fn generate() -> Keypair {
|
||||
Keypair::from(SecretKey::generate())
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn encode(&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.
|
||||
///
|
||||
/// Note that this binary format is the same as `ed25519_dalek`'s and `ed25519_zebra`'s.
|
||||
pub fn decode(kp: &mut [u8]) -> Result<Keypair, DecodingError> {
|
||||
ed25519::Keypair::from_bytes(kp)
|
||||
.map(|k| {
|
||||
kp.zeroize();
|
||||
Keypair(k)
|
||||
})
|
||||
.map_err(|e| DecodingError::failed_to_parse("Ed25519 keypair", e))
|
||||
}
|
||||
|
||||
/// Sign a message using the private key of this keypair.
|
||||
pub fn sign(&self, msg: &[u8]) -> Vec<u8> {
|
||||
self.0.sign(msg).to_bytes().to_vec()
|
||||
}
|
||||
|
||||
/// Get the public key of this keypair.
|
||||
pub fn public(&self) -> PublicKey {
|
||||
PublicKey(self.0.public)
|
||||
}
|
||||
|
||||
/// Get the secret key of this keypair.
|
||||
pub fn secret(&self) -> SecretKey {
|
||||
SecretKey::from_bytes(&mut self.0.secret.to_bytes())
|
||||
.expect("ed25519::SecretKey::from_bytes(to_bytes(k)) != k")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Keypair {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Keypair")
|
||||
.field("public", &self.0.public)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
.expect("ed25519::SecretKey::from_bytes(to_bytes(k)) != k")
|
||||
.0;
|
||||
let public = ed25519::PublicKey::from_bytes(&self.0.public.to_bytes())
|
||||
.expect("ed25519::PublicKey::from_bytes(to_bytes(k)) != k");
|
||||
Keypair(ed25519::Keypair { secret, public })
|
||||
}
|
||||
}
|
||||
|
||||
/// Demote an Ed25519 keypair to a secret key.
|
||||
impl From<Keypair> for SecretKey {
|
||||
fn from(kp: Keypair) -> SecretKey {
|
||||
SecretKey(kp.0.secret)
|
||||
}
|
||||
}
|
||||
|
||||
/// Promote an Ed25519 secret key into a keypair.
|
||||
impl From<SecretKey> for Keypair {
|
||||
fn from(sk: SecretKey) -> Keypair {
|
||||
let secret: ed25519::ExpandedSecretKey = (&sk.0).into();
|
||||
let public = ed25519::PublicKey::from(&secret);
|
||||
Keypair(ed25519::Keypair {
|
||||
secret: sk.0,
|
||||
public,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// An Ed25519 public key.
|
||||
#[derive(Eq, Clone)]
|
||||
pub struct PublicKey(ed25519::PublicKey);
|
||||
|
||||
impl fmt::Debug for PublicKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("PublicKey(compressed): ")?;
|
||||
for byte in self.0.as_bytes() {
|
||||
write!(f, "{byte:x}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl cmp::PartialEq for PublicKey {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0.as_bytes().eq(other.0.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl hash::Hash for PublicKey {
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||
self.0.as_bytes().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl cmp::PartialOrd for PublicKey {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||
self.0.as_bytes().partial_cmp(other.0.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl cmp::Ord for PublicKey {
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
self.0.as_bytes().cmp(other.0.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl PublicKey {
|
||||
/// Verify the Ed25519 signature on a message using the public key.
|
||||
pub fn verify(&self, msg: &[u8], sig: &[u8]) -> bool {
|
||||
ed25519::Signature::try_from(sig)
|
||||
.and_then(|s| self.0.verify(msg, &s))
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
/// Encode the public key into a byte array in compressed form, i.e.
|
||||
/// where one coordinate is represented by a single bit.
|
||||
pub fn encode(&self) -> [u8; 32] {
|
||||
self.0.to_bytes()
|
||||
}
|
||||
|
||||
/// Decode a public key from a byte array as produced by `encode`.
|
||||
pub fn decode(k: &[u8]) -> Result<PublicKey, DecodingError> {
|
||||
ed25519::PublicKey::from_bytes(k)
|
||||
.map_err(|e| DecodingError::failed_to_parse("Ed25519 public key", e))
|
||||
.map(PublicKey)
|
||||
}
|
||||
}
|
||||
|
||||
/// An Ed25519 secret key.
|
||||
pub struct SecretKey(ed25519::SecretKey);
|
||||
|
||||
/// View the bytes of the secret key.
|
||||
impl AsRef<[u8]> for SecretKey {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.0.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SecretKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "SecretKey")
|
||||
}
|
||||
}
|
||||
|
||||
impl SecretKey {
|
||||
/// Generate a new Ed25519 secret key.
|
||||
pub fn generate() -> SecretKey {
|
||||
let mut bytes = [0u8; 32];
|
||||
rand::thread_rng().fill_bytes(&mut bytes);
|
||||
SecretKey(
|
||||
ed25519::SecretKey::from_bytes(&bytes).expect(
|
||||
"this returns `Err` only if the length is wrong; the length is correct; qed",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn from_bytes(mut sk_bytes: impl AsMut<[u8]>) -> Result<SecretKey, DecodingError> {
|
||||
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))?;
|
||||
sk_bytes.zeroize();
|
||||
Ok(SecretKey(secret))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use quickcheck::*;
|
||||
|
||||
fn eq_keypairs(kp1: &Keypair, kp2: &Keypair) -> bool {
|
||||
kp1.public() == kp2.public() && kp1.0.secret.as_bytes() == kp2.0.secret.as_bytes()
|
||||
}
|
||||
|
||||
#[test]
|
||||
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();
|
||||
eq_keypairs(&kp1, &kp2) && kp1_enc.iter().all(|b| *b == 0)
|
||||
}
|
||||
QuickCheck::new().tests(10).quickcheck(prop as fn() -> _);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ed25519_keypair_from_secret() {
|
||||
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());
|
||||
eq_keypairs(&kp1, &kp2) && sk == [0u8; 32]
|
||||
}
|
||||
QuickCheck::new().tests(10).quickcheck(prop as fn() -> _);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ed25519_signature() {
|
||||
let kp = Keypair::generate();
|
||||
let pk = kp.public();
|
||||
|
||||
let msg = "hello world".as_bytes();
|
||||
let sig = kp.sign(msg);
|
||||
assert!(pk.verify(msg, &sig));
|
||||
|
||||
let mut invalid_sig = sig.clone();
|
||||
invalid_sig[3..6].copy_from_slice(&[10, 23, 42]);
|
||||
assert!(!pk.verify(msg, &invalid_sig));
|
||||
|
||||
let invalid_msg = "h3ll0 w0rld".as_bytes();
|
||||
assert!(!pk.verify(invalid_msg, &sig));
|
||||
}
|
||||
}
|
@ -1,139 +0,0 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
//! Errors during identity key operations.
|
||||
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
/// An error during decoding of key material.
|
||||
#[derive(Debug)]
|
||||
pub struct DecodingError {
|
||||
msg: String,
|
||||
source: Option<Box<dyn Error + Send + Sync>>,
|
||||
}
|
||||
|
||||
impl DecodingError {
|
||||
#[cfg(not(all(
|
||||
feature = "ecdsa",
|
||||
feature = "rsa",
|
||||
feature = "secp256k1",
|
||||
not(target_arch = "wasm32")
|
||||
)))]
|
||||
pub(crate) fn missing_feature(feature_name: &'static str) -> Self {
|
||||
Self {
|
||||
msg: format!("cargo feature `{feature_name}` is not enabled"),
|
||||
source: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn failed_to_parse<E, S>(what: &'static str, source: S) -> Self
|
||||
where
|
||||
E: Error + Send + Sync + 'static,
|
||||
S: Into<Option<E>>,
|
||||
{
|
||||
Self {
|
||||
msg: format!("failed to parse {what}"),
|
||||
source: match source.into() {
|
||||
None => None,
|
||||
Some(e) => Some(Box::new(e)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn bad_protobuf(
|
||||
what: &'static str,
|
||||
source: impl Error + Send + Sync + 'static,
|
||||
) -> Self {
|
||||
Self {
|
||||
msg: format!("failed to decode {what} from protobuf"),
|
||||
source: Some(Box::new(source)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn decoding_unsupported(key_type: &'static str) -> Self {
|
||||
Self {
|
||||
msg: format!("decoding {key_type} key from Protobuf is unsupported"),
|
||||
source: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
all(feature = "rsa", not(target_arch = "wasm32")),
|
||||
feature = "secp256k1",
|
||||
feature = "ecdsa"
|
||||
))]
|
||||
pub(crate) fn encoding_unsupported(key_type: &'static str) -> Self {
|
||||
Self {
|
||||
msg: format!("encoding {key_type} key to Protobuf is unsupported"),
|
||||
source: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for DecodingError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Key decoding error: {}", self.msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for DecodingError {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
self.source.as_ref().map(|s| &**s as &dyn Error)
|
||||
}
|
||||
}
|
||||
|
||||
/// An error during signing of a message.
|
||||
#[derive(Debug)]
|
||||
pub struct SigningError {
|
||||
msg: String,
|
||||
source: Option<Box<dyn Error + Send + Sync>>,
|
||||
}
|
||||
|
||||
/// An error during encoding of key material.
|
||||
impl SigningError {
|
||||
#[cfg(any(feature = "secp256k1", feature = "rsa"))]
|
||||
pub(crate) fn new<S: ToString>(msg: S) -> Self {
|
||||
Self {
|
||||
msg: msg.to_string(),
|
||||
source: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rsa")]
|
||||
pub(crate) fn source(self, source: impl Error + Send + Sync + 'static) -> Self {
|
||||
Self {
|
||||
source: Some(Box::new(source)),
|
||||
..self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SigningError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Key signing error: {}", self.msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for SigningError {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
self.source.as_ref().map(|s| &**s as &dyn Error)
|
||||
}
|
||||
}
|
@ -1,351 +0,0 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
//! RSA keys.
|
||||
|
||||
use super::error::*;
|
||||
use asn1_der::typed::{DerDecodable, DerEncodable, DerTypeView, Sequence};
|
||||
use asn1_der::{Asn1DerError, Asn1DerErrorVariant, DerObject, Sink, VecBacking};
|
||||
use ring::rand::SystemRandom;
|
||||
use ring::signature::KeyPair;
|
||||
use ring::signature::{self, RsaKeyPair, RSA_PKCS1_2048_8192_SHA256, RSA_PKCS1_SHA256};
|
||||
use std::{fmt, sync::Arc};
|
||||
use zeroize::Zeroize;
|
||||
|
||||
/// An RSA keypair.
|
||||
#[derive(Clone)]
|
||||
pub struct Keypair(Arc<RsaKeyPair>);
|
||||
|
||||
impl std::fmt::Debug for Keypair {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
f.debug_struct("Keypair")
|
||||
.field("public", self.0.public_key())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Keypair {
|
||||
/// 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 from_pkcs8(der: &mut [u8]) -> Result<Keypair, DecodingError> {
|
||||
let kp = RsaKeyPair::from_pkcs8(der)
|
||||
.map_err(|e| DecodingError::failed_to_parse("RSA PKCS#8 PrivateKeyInfo", e))?;
|
||||
der.zeroize();
|
||||
Ok(Keypair(Arc::new(kp)))
|
||||
}
|
||||
|
||||
/// Get the public key from the keypair.
|
||||
pub fn public(&self) -> PublicKey {
|
||||
PublicKey(self.0.public_key().as_ref().to_vec())
|
||||
}
|
||||
|
||||
/// Sign a message with this keypair.
|
||||
pub fn sign(&self, data: &[u8]) -> Result<Vec<u8>, SigningError> {
|
||||
let mut signature = vec![0; self.0.public_modulus_len()];
|
||||
let rng = SystemRandom::new();
|
||||
match self.0.sign(&RSA_PKCS1_SHA256, &rng, data, &mut signature) {
|
||||
Ok(()) => Ok(signature),
|
||||
Err(e) => Err(SigningError::new("RSA").source(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An RSA public key.
|
||||
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct PublicKey(Vec<u8>);
|
||||
|
||||
impl PublicKey {
|
||||
/// Verify an RSA signature on a message using the public key.
|
||||
pub fn verify(&self, msg: &[u8], sig: &[u8]) -> bool {
|
||||
let key = signature::UnparsedPublicKey::new(&RSA_PKCS1_2048_8192_SHA256, &self.0);
|
||||
key.verify(msg, sig).is_ok()
|
||||
}
|
||||
|
||||
/// Encode the RSA public key in DER as a PKCS#1 RSAPublicKey structure,
|
||||
/// as defined in [RFC3447].
|
||||
///
|
||||
/// [RFC3447]: https://tools.ietf.org/html/rfc3447#appendix-A.1.1
|
||||
pub fn encode_pkcs1(&self) -> Vec<u8> {
|
||||
// This is the encoding currently used in-memory, so it is trivial.
|
||||
self.0.clone()
|
||||
}
|
||||
|
||||
/// Encode the RSA public key in DER as a X.509 SubjectPublicKeyInfo structure,
|
||||
/// as defined in [RFC5280].
|
||||
///
|
||||
/// [RFC5280]: https://tools.ietf.org/html/rfc5280#section-4.1
|
||||
pub fn encode_x509(&self) -> Vec<u8> {
|
||||
let spki = Asn1SubjectPublicKeyInfo {
|
||||
algorithmIdentifier: Asn1RsaEncryption {
|
||||
algorithm: Asn1OidRsaEncryption,
|
||||
parameters: (),
|
||||
},
|
||||
subjectPublicKey: Asn1SubjectPublicKey(self.clone()),
|
||||
};
|
||||
let mut buf = Vec::new();
|
||||
spki.encode(&mut buf)
|
||||
.map(|_| buf)
|
||||
.expect("RSA X.509 public key encoding failed.")
|
||||
}
|
||||
|
||||
/// Decode an RSA public key from a DER-encoded X.509 SubjectPublicKeyInfo
|
||||
/// structure. See also `encode_x509`.
|
||||
pub fn decode_x509(pk: &[u8]) -> Result<PublicKey, DecodingError> {
|
||||
Asn1SubjectPublicKeyInfo::decode(pk)
|
||||
.map_err(|e| DecodingError::failed_to_parse("RSA X.509", e))
|
||||
.map(|spki| spki.subjectPublicKey.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for PublicKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("PublicKey(PKCS1): ")?;
|
||||
for byte in &self.0 {
|
||||
write!(f, "{byte:x}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// DER encoding / decoding of public keys
|
||||
//
|
||||
// Primer: http://luca.ntop.org/Teaching/Appunti/asn1.html
|
||||
// Playground: https://lapo.it/asn1js/
|
||||
|
||||
/// A raw ASN1 OID.
|
||||
#[derive(Copy, Clone)]
|
||||
struct Asn1RawOid<'a> {
|
||||
object: DerObject<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Asn1RawOid<'a> {
|
||||
/// The underlying OID as byte literal.
|
||||
pub fn oid(&self) -> &[u8] {
|
||||
self.object.value()
|
||||
}
|
||||
|
||||
/// Writes an OID raw `value` as DER-object to `sink`.
|
||||
pub fn write<S: Sink>(value: &[u8], sink: &mut S) -> Result<(), Asn1DerError> {
|
||||
DerObject::write(Self::TAG, value.len(), &mut value.iter(), sink)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DerTypeView<'a> for Asn1RawOid<'a> {
|
||||
const TAG: u8 = 6;
|
||||
|
||||
fn object(&self) -> DerObject<'a> {
|
||||
self.object
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DerEncodable for Asn1RawOid<'a> {
|
||||
fn encode<S: Sink>(&self, sink: &mut S) -> Result<(), Asn1DerError> {
|
||||
self.object.encode(sink)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DerDecodable<'a> for Asn1RawOid<'a> {
|
||||
fn load(object: DerObject<'a>) -> Result<Self, Asn1DerError> {
|
||||
if object.tag() != Self::TAG {
|
||||
return Err(Asn1DerError::new(Asn1DerErrorVariant::InvalidData(
|
||||
"DER object tag is not the object identifier tag.",
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(Self { object })
|
||||
}
|
||||
}
|
||||
|
||||
/// The ASN.1 OID for "rsaEncryption".
|
||||
#[derive(Clone)]
|
||||
struct Asn1OidRsaEncryption;
|
||||
|
||||
impl Asn1OidRsaEncryption {
|
||||
/// The DER encoding of the object identifier (OID) 'rsaEncryption' for
|
||||
/// RSA public keys defined for X.509 in [RFC-3279] and used in
|
||||
/// SubjectPublicKeyInfo structures defined in [RFC-5280].
|
||||
///
|
||||
/// [RFC-3279]: https://tools.ietf.org/html/rfc3279#section-2.3.1
|
||||
/// [RFC-5280]: https://tools.ietf.org/html/rfc5280#section-4.1
|
||||
const OID: [u8; 9] = [0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01];
|
||||
}
|
||||
|
||||
impl DerEncodable for Asn1OidRsaEncryption {
|
||||
fn encode<S: Sink>(&self, sink: &mut S) -> Result<(), Asn1DerError> {
|
||||
Asn1RawOid::write(&Self::OID, sink)
|
||||
}
|
||||
}
|
||||
|
||||
impl DerDecodable<'_> for Asn1OidRsaEncryption {
|
||||
fn load(object: DerObject<'_>) -> Result<Self, Asn1DerError> {
|
||||
match Asn1RawOid::load(object)?.oid() {
|
||||
oid if oid == Self::OID => Ok(Self),
|
||||
_ => Err(Asn1DerError::new(Asn1DerErrorVariant::InvalidData(
|
||||
"DER object is not the 'rsaEncryption' identifier.",
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The ASN.1 AlgorithmIdentifier for "rsaEncryption".
|
||||
struct Asn1RsaEncryption {
|
||||
algorithm: Asn1OidRsaEncryption,
|
||||
parameters: (),
|
||||
}
|
||||
|
||||
impl DerEncodable for Asn1RsaEncryption {
|
||||
fn encode<S: Sink>(&self, sink: &mut S) -> Result<(), Asn1DerError> {
|
||||
let mut algorithm_buf = Vec::new();
|
||||
let algorithm = self.algorithm.der_object(VecBacking(&mut algorithm_buf))?;
|
||||
|
||||
let mut parameters_buf = Vec::new();
|
||||
let parameters = self
|
||||
.parameters
|
||||
.der_object(VecBacking(&mut parameters_buf))?;
|
||||
|
||||
Sequence::write(&[algorithm, parameters], sink)
|
||||
}
|
||||
}
|
||||
|
||||
impl DerDecodable<'_> for Asn1RsaEncryption {
|
||||
fn load(object: DerObject<'_>) -> Result<Self, Asn1DerError> {
|
||||
let seq: Sequence = Sequence::load(object)?;
|
||||
|
||||
Ok(Self {
|
||||
algorithm: seq.get_as(0)?,
|
||||
parameters: seq.get_as(1)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The ASN.1 SubjectPublicKey inside a SubjectPublicKeyInfo,
|
||||
/// i.e. encoded as a DER BIT STRING.
|
||||
struct Asn1SubjectPublicKey(PublicKey);
|
||||
|
||||
impl DerEncodable for Asn1SubjectPublicKey {
|
||||
fn encode<S: Sink>(&self, sink: &mut S) -> Result<(), Asn1DerError> {
|
||||
let pk_der = &(self.0).0;
|
||||
let mut bit_string = Vec::with_capacity(pk_der.len() + 1);
|
||||
// The number of bits in pk_der is trivially always a multiple of 8,
|
||||
// so there are always 0 "unused bits" signaled by the first byte.
|
||||
bit_string.push(0u8);
|
||||
bit_string.extend(pk_der);
|
||||
DerObject::write(3, bit_string.len(), &mut bit_string.iter(), sink)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl DerDecodable<'_> for Asn1SubjectPublicKey {
|
||||
fn load(object: DerObject<'_>) -> Result<Self, Asn1DerError> {
|
||||
if object.tag() != 3 {
|
||||
return Err(Asn1DerError::new(Asn1DerErrorVariant::InvalidData(
|
||||
"DER object tag is not the bit string tag.",
|
||||
)));
|
||||
}
|
||||
|
||||
let pk_der: Vec<u8> = object.value().iter().skip(1).cloned().collect();
|
||||
// We don't parse pk_der further as an ASN.1 RsaPublicKey, since
|
||||
// we only need the DER encoding for `verify`.
|
||||
Ok(Self(PublicKey(pk_der)))
|
||||
}
|
||||
}
|
||||
|
||||
/// ASN.1 SubjectPublicKeyInfo
|
||||
#[allow(non_snake_case)]
|
||||
struct Asn1SubjectPublicKeyInfo {
|
||||
algorithmIdentifier: Asn1RsaEncryption,
|
||||
subjectPublicKey: Asn1SubjectPublicKey,
|
||||
}
|
||||
|
||||
impl DerEncodable for Asn1SubjectPublicKeyInfo {
|
||||
fn encode<S: Sink>(&self, sink: &mut S) -> Result<(), Asn1DerError> {
|
||||
let mut identifier_buf = Vec::new();
|
||||
let identifier = self
|
||||
.algorithmIdentifier
|
||||
.der_object(VecBacking(&mut identifier_buf))?;
|
||||
|
||||
let mut key_buf = Vec::new();
|
||||
let key = self.subjectPublicKey.der_object(VecBacking(&mut key_buf))?;
|
||||
|
||||
Sequence::write(&[identifier, key], sink)
|
||||
}
|
||||
}
|
||||
|
||||
impl DerDecodable<'_> for Asn1SubjectPublicKeyInfo {
|
||||
fn load(object: DerObject<'_>) -> Result<Self, Asn1DerError> {
|
||||
let seq: Sequence = Sequence::load(object)?;
|
||||
|
||||
Ok(Self {
|
||||
algorithmIdentifier: seq.get_as(0)?,
|
||||
subjectPublicKey: seq.get_as(1)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use quickcheck::*;
|
||||
|
||||
const KEY1: &[u8] = include_bytes!("test/rsa-2048.pk8");
|
||||
const KEY2: &[u8] = include_bytes!("test/rsa-3072.pk8");
|
||||
const KEY3: &[u8] = include_bytes!("test/rsa-4096.pk8");
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct SomeKeypair(Keypair);
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
#[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());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rsa_x509_encode_decode() {
|
||||
fn prop(SomeKeypair(kp): SomeKeypair) -> Result<bool, String> {
|
||||
let pk = kp.public();
|
||||
PublicKey::decode_x509(&pk.encode_x509())
|
||||
.map_err(|e| e.to_string())
|
||||
.map(|pk2| pk2 == pk)
|
||||
}
|
||||
QuickCheck::new().tests(10).quickcheck(prop as fn(_) -> _);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rsa_sign_verify() {
|
||||
fn prop(SomeKeypair(kp): SomeKeypair, msg: Vec<u8>) -> Result<bool, SigningError> {
|
||||
kp.sign(&msg).map(|s| kp.public().verify(&msg, &s))
|
||||
}
|
||||
QuickCheck::new()
|
||||
.tests(10)
|
||||
.quickcheck(prop as fn(_, _) -> _);
|
||||
}
|
||||
}
|
@ -1,237 +0,0 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
//! Secp256k1 keys.
|
||||
|
||||
use super::error::{DecodingError, SigningError};
|
||||
use asn1_der::typed::{DerDecodable, Sequence};
|
||||
use core::cmp;
|
||||
use core::fmt;
|
||||
use core::hash;
|
||||
use libsecp256k1::{Message, Signature};
|
||||
use sha2::{Digest as ShaDigestTrait, Sha256};
|
||||
use zeroize::Zeroize;
|
||||
|
||||
/// A Secp256k1 keypair.
|
||||
#[derive(Clone)]
|
||||
pub struct Keypair {
|
||||
secret: SecretKey,
|
||||
public: PublicKey,
|
||||
}
|
||||
|
||||
impl Keypair {
|
||||
/// Generate a new sec256k1 `Keypair`.
|
||||
pub fn generate() -> Keypair {
|
||||
Keypair::from(SecretKey::generate())
|
||||
}
|
||||
|
||||
/// Get the public key of this keypair.
|
||||
pub fn public(&self) -> &PublicKey {
|
||||
&self.public
|
||||
}
|
||||
|
||||
/// Get the secret key of this keypair.
|
||||
pub fn secret(&self) -> &SecretKey {
|
||||
&self.secret
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Keypair {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Keypair")
|
||||
.field("public", &self.public)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// 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));
|
||||
Keypair { secret, public }
|
||||
}
|
||||
}
|
||||
|
||||
/// Demote a Secp256k1 keypair into a secret key.
|
||||
impl From<Keypair> for SecretKey {
|
||||
fn from(kp: Keypair) -> SecretKey {
|
||||
kp.secret
|
||||
}
|
||||
}
|
||||
|
||||
/// A Secp256k1 secret key.
|
||||
#[derive(Clone)]
|
||||
pub struct SecretKey(libsecp256k1::SecretKey);
|
||||
|
||||
impl fmt::Debug for SecretKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "SecretKey")
|
||||
}
|
||||
}
|
||||
|
||||
impl SecretKey {
|
||||
/// Generate a new random Secp256k1 secret key.
|
||||
pub fn generate() -> SecretKey {
|
||||
SecretKey(libsecp256k1::SecretKey::random(&mut rand::thread_rng()))
|
||||
}
|
||||
|
||||
/// 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 from_bytes(mut sk: impl AsMut<[u8]>) -> Result<SecretKey, DecodingError> {
|
||||
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))?;
|
||||
sk_bytes.zeroize();
|
||||
Ok(SecretKey(secret))
|
||||
}
|
||||
|
||||
/// Decode a DER-encoded Secp256k1 secret key in an ECPrivateKey
|
||||
/// structure as defined in [RFC5915], zeroing the input slice on success.
|
||||
///
|
||||
/// [RFC5915]: https://tools.ietf.org/html/rfc5915
|
||||
pub fn from_der(mut der: impl AsMut<[u8]>) -> Result<SecretKey, DecodingError> {
|
||||
// TODO: Stricter parsing.
|
||||
let der_obj = der.as_mut();
|
||||
|
||||
let mut sk_bytes = Sequence::decode(der_obj)
|
||||
.and_then(|seq| seq.get(1))
|
||||
.and_then(Vec::load)
|
||||
.map_err(|e| DecodingError::failed_to_parse("secp256k1 SecretKey bytes", e))?;
|
||||
|
||||
let sk = SecretKey::from_bytes(&mut sk_bytes)?;
|
||||
sk_bytes.zeroize();
|
||||
der_obj.zeroize();
|
||||
Ok(sk)
|
||||
}
|
||||
|
||||
/// Sign a message with this secret key, producing a DER-encoded
|
||||
/// ECDSA signature, as defined in [RFC3278].
|
||||
///
|
||||
/// [RFC3278]: https://tools.ietf.org/html/rfc3278#section-8.2
|
||||
pub fn sign(&self, msg: &[u8]) -> Result<Vec<u8>, SigningError> {
|
||||
self.sign_hash(Sha256::digest(msg).as_ref())
|
||||
}
|
||||
|
||||
/// Returns the raw bytes of the secret key.
|
||||
pub fn to_bytes(&self) -> [u8; 32] {
|
||||
self.0.serialize()
|
||||
}
|
||||
|
||||
/// Sign a raw message of length 256 bits with this secret key, produces a DER-encoded
|
||||
/// ECDSA signature.
|
||||
pub fn sign_hash(&self, msg: &[u8]) -> Result<Vec<u8>, SigningError> {
|
||||
let m = Message::parse_slice(msg)
|
||||
.map_err(|_| SigningError::new("failed to parse secp256k1 digest"))?;
|
||||
Ok(libsecp256k1::sign(&m, &self.0)
|
||||
.0
|
||||
.serialize_der()
|
||||
.as_ref()
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// A Secp256k1 public key.
|
||||
#[derive(Eq, Clone)]
|
||||
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() {
|
||||
write!(f, "{byte:x}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl cmp::PartialEq for PublicKey {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.encode().eq(&other.encode())
|
||||
}
|
||||
}
|
||||
|
||||
impl hash::Hash for PublicKey {
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||
self.encode().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl cmp::PartialOrd for PublicKey {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||
self.encode().partial_cmp(&other.encode())
|
||||
}
|
||||
}
|
||||
|
||||
impl cmp::Ord for PublicKey {
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
self.encode().cmp(&other.encode())
|
||||
}
|
||||
}
|
||||
|
||||
impl PublicKey {
|
||||
/// Verify the Secp256k1 signature on a message using the public key.
|
||||
pub fn verify(&self, msg: &[u8], sig: &[u8]) -> bool {
|
||||
self.verify_hash(Sha256::digest(msg).as_ref(), sig)
|
||||
}
|
||||
|
||||
/// 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)))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Encode the public key in compressed form, i.e. with one coordinate
|
||||
/// represented by a single bit.
|
||||
pub fn encode(&self) -> [u8; 33] {
|
||||
self.0.serialize_compressed()
|
||||
}
|
||||
|
||||
/// Encode the public key in uncompressed form.
|
||||
pub fn encode_uncompressed(&self) -> [u8; 65] {
|
||||
self.0.serialize()
|
||||
}
|
||||
|
||||
/// Decode a public key from a byte slice in the the format produced
|
||||
/// by `encode`.
|
||||
pub fn decode(k: &[u8]) -> Result<PublicKey, DecodingError> {
|
||||
libsecp256k1::PublicKey::parse_slice(k, Some(libsecp256k1::PublicKeyFormat::Compressed))
|
||||
.map_err(|e| DecodingError::failed_to_parse("secp256k1 public key", e))
|
||||
.map(PublicKey)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn secp256k1_secret_from_bytes() {
|
||||
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();
|
||||
assert_eq!(sk1.0.serialize(), sk2.0.serialize());
|
||||
assert_eq!(sk_bytes, [0; 32]);
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -40,8 +40,7 @@
|
||||
mod proto {
|
||||
include!("generated/mod.rs");
|
||||
pub use self::{
|
||||
envelope_proto::*, keys_proto::*, peer_record_proto::mod_PeerRecord::*,
|
||||
peer_record_proto::PeerRecord,
|
||||
envelope_proto::*, peer_record_proto::mod_PeerRecord::*, peer_record_proto::PeerRecord,
|
||||
};
|
||||
}
|
||||
|
||||
@ -51,25 +50,78 @@ use std::fmt;
|
||||
use std::fmt::Formatter;
|
||||
pub type Negotiated<T> = multistream_select::Negotiated<T>;
|
||||
|
||||
mod peer_id;
|
||||
#[deprecated(since = "0.39.0", note = "Depend on `libp2p-identity` instead.")]
|
||||
pub mod identity {
|
||||
pub use libp2p_identity::Keypair;
|
||||
pub use libp2p_identity::PublicKey;
|
||||
|
||||
pub mod ed25519 {
|
||||
pub use libp2p_identity::ed25519::Keypair;
|
||||
pub use libp2p_identity::ed25519::PublicKey;
|
||||
pub use libp2p_identity::ed25519::SecretKey;
|
||||
}
|
||||
|
||||
#[cfg(feature = "ecdsa")]
|
||||
#[deprecated(
|
||||
since = "0.39.0",
|
||||
note = "The `ecdsa` feature-flag is deprecated and will be removed in favor of `libp2p-identity`."
|
||||
)]
|
||||
pub mod ecdsa {
|
||||
pub use libp2p_identity::ecdsa::Keypair;
|
||||
pub use libp2p_identity::ecdsa::PublicKey;
|
||||
pub use libp2p_identity::ecdsa::SecretKey;
|
||||
}
|
||||
|
||||
#[cfg(feature = "secp256k1")]
|
||||
#[deprecated(
|
||||
since = "0.39.0",
|
||||
note = "The `secp256k1` feature-flag is deprecated and will be removed in favor of `libp2p-identity`."
|
||||
)]
|
||||
pub mod secp256k1 {
|
||||
pub use libp2p_identity::secp256k1::Keypair;
|
||||
pub use libp2p_identity::secp256k1::PublicKey;
|
||||
pub use libp2p_identity::secp256k1::SecretKey;
|
||||
}
|
||||
|
||||
#[cfg(feature = "rsa")]
|
||||
#[deprecated(
|
||||
since = "0.39.0",
|
||||
note = "The `rsa` feature-flag is deprecated and will be removed in favor of `libp2p-identity`."
|
||||
)]
|
||||
pub mod rsa {
|
||||
pub use libp2p_identity::rsa::Keypair;
|
||||
pub use libp2p_identity::rsa::PublicKey;
|
||||
}
|
||||
|
||||
pub mod error {
|
||||
pub use libp2p_identity::DecodingError;
|
||||
pub use libp2p_identity::SigningError;
|
||||
}
|
||||
}
|
||||
|
||||
mod translation;
|
||||
|
||||
pub mod connection;
|
||||
pub mod either;
|
||||
pub mod identity;
|
||||
pub mod muxing;
|
||||
pub mod peer_record;
|
||||
pub mod signed_envelope;
|
||||
pub mod transport;
|
||||
pub mod upgrade;
|
||||
|
||||
#[deprecated(since = "0.39.0", note = "Depend on `libp2p-identity` instead.")]
|
||||
pub type PublicKey = libp2p_identity::PublicKey;
|
||||
|
||||
#[deprecated(since = "0.39.0", note = "Depend on `libp2p-identity` instead.")]
|
||||
pub type PeerId = libp2p_identity::PeerId;
|
||||
|
||||
#[deprecated(since = "0.39.0", note = "Depend on `libp2p-identity` instead.")]
|
||||
pub type ParseError = libp2p_identity::ParseError;
|
||||
|
||||
pub use connection::{ConnectedPoint, Endpoint};
|
||||
pub use identity::PublicKey;
|
||||
pub use multiaddr::Multiaddr;
|
||||
pub use multihash;
|
||||
pub use muxing::StreamMuxer;
|
||||
pub use peer_id::ParseError;
|
||||
pub use peer_id::PeerId;
|
||||
pub use peer_record::PeerRecord;
|
||||
pub use signed_envelope::SignedEnvelope;
|
||||
pub use translation::address_translation;
|
||||
|
@ -1,313 +0,0 @@
|
||||
// Copyright 2018 Parity Technologies (UK) Ltd.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
use crate::PublicKey;
|
||||
use multiaddr::{Multiaddr, Protocol};
|
||||
use multihash::{Code, Error, Multihash, MultihashDigest};
|
||||
use rand::Rng;
|
||||
use std::{convert::TryFrom, fmt, str::FromStr};
|
||||
use thiserror::Error;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Public keys with byte-lengths smaller than `MAX_INLINE_KEY_LENGTH` will be
|
||||
/// automatically used as the peer id using an identity multihash.
|
||||
const MAX_INLINE_KEY_LENGTH: usize = 42;
|
||||
|
||||
/// Identifier of a peer of the network.
|
||||
///
|
||||
/// The data is a CIDv0 compatible multihash of the protobuf encoded public key of the peer
|
||||
/// as specified in [specs/peer-ids](https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md).
|
||||
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct PeerId {
|
||||
multihash: Multihash,
|
||||
}
|
||||
|
||||
impl fmt::Debug for PeerId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple("PeerId").field(&self.to_base58()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PeerId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.to_base58().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl PeerId {
|
||||
/// Builds a `PeerId` from a public key.
|
||||
pub fn from_public_key(key: &PublicKey) -> PeerId {
|
||||
let key_enc = key.to_protobuf_encoding();
|
||||
|
||||
let hash_algorithm = if key_enc.len() <= MAX_INLINE_KEY_LENGTH {
|
||||
Code::Identity
|
||||
} else {
|
||||
Code::Sha2_256
|
||||
};
|
||||
|
||||
let multihash = hash_algorithm.digest(&key_enc);
|
||||
|
||||
PeerId { multihash }
|
||||
}
|
||||
|
||||
/// Parses a `PeerId` from bytes.
|
||||
pub fn from_bytes(data: &[u8]) -> Result<PeerId, Error> {
|
||||
PeerId::from_multihash(Multihash::from_bytes(data)?)
|
||||
.map_err(|mh| Error::UnsupportedCode(mh.code()))
|
||||
}
|
||||
|
||||
/// Tries to turn a `Multihash` into a `PeerId`.
|
||||
///
|
||||
/// If the multihash does not use a valid hashing algorithm for peer IDs,
|
||||
/// or the hash value does not satisfy the constraints for a hashed
|
||||
/// peer ID, it is returned as an `Err`.
|
||||
pub fn from_multihash(multihash: Multihash) -> Result<PeerId, Multihash> {
|
||||
match Code::try_from(multihash.code()) {
|
||||
Ok(Code::Sha2_256) => Ok(PeerId { multihash }),
|
||||
Ok(Code::Identity) if multihash.digest().len() <= MAX_INLINE_KEY_LENGTH => {
|
||||
Ok(PeerId { multihash })
|
||||
}
|
||||
_ => Err(multihash),
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to extract a [`PeerId`] from the given [`Multiaddr`].
|
||||
///
|
||||
/// In case the given [`Multiaddr`] ends with `/p2p/<peer-id>`, this function
|
||||
/// will return the encapsulated [`PeerId`], otherwise it will return `None`.
|
||||
pub fn try_from_multiaddr(address: &Multiaddr) -> Option<PeerId> {
|
||||
address.iter().last().and_then(|p| match p {
|
||||
Protocol::P2p(hash) => PeerId::from_multihash(hash).ok(),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Generates a random peer ID from a cryptographically secure PRNG.
|
||||
///
|
||||
/// This is useful for randomly walking on a DHT, or for testing purposes.
|
||||
pub fn random() -> PeerId {
|
||||
let peer_id = rand::thread_rng().gen::<[u8; 32]>();
|
||||
PeerId {
|
||||
multihash: Multihash::wrap(Code::Identity.into(), &peer_id)
|
||||
.expect("The digest size is never too large"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a raw bytes representation of this `PeerId`.
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
self.multihash.to_bytes()
|
||||
}
|
||||
|
||||
/// Returns a base-58 encoded string of this `PeerId`.
|
||||
pub fn to_base58(&self) -> String {
|
||||
bs58::encode(self.to_bytes()).into_string()
|
||||
}
|
||||
|
||||
/// Checks whether the public key passed as parameter matches the public key of this `PeerId`.
|
||||
///
|
||||
/// Returns `None` if this `PeerId`s hash algorithm is not supported when encoding the
|
||||
/// given public key, otherwise `Some` boolean as the result of an equality check.
|
||||
pub fn is_public_key(&self, public_key: &PublicKey) -> Option<bool> {
|
||||
let alg = Code::try_from(self.multihash.code())
|
||||
.expect("Internal multihash is always a valid `Code`");
|
||||
let enc = public_key.to_protobuf_encoding();
|
||||
Some(alg.digest(&enc) == self.multihash)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PublicKey> for PeerId {
|
||||
fn from(key: PublicKey) -> PeerId {
|
||||
PeerId::from_public_key(&key)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PublicKey> for PeerId {
|
||||
fn from(key: &PublicKey) -> PeerId {
|
||||
PeerId::from_public_key(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<u8>> for PeerId {
|
||||
type Error = Vec<u8>;
|
||||
|
||||
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
PeerId::from_bytes(&value).map_err(|_| value)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Multihash> for PeerId {
|
||||
type Error = Multihash;
|
||||
|
||||
fn try_from(value: Multihash) -> Result<Self, Self::Error> {
|
||||
PeerId::from_multihash(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Multihash> for PeerId {
|
||||
fn as_ref(&self) -> &Multihash {
|
||||
&self.multihash
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PeerId> for Multihash {
|
||||
fn from(peer_id: PeerId) -> Self {
|
||||
peer_id.multihash
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PeerId> for Vec<u8> {
|
||||
fn from(peer_id: PeerId) -> Self {
|
||||
peer_id.to_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for PeerId {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
if serializer.is_human_readable() {
|
||||
serializer.serialize_str(&self.to_base58())
|
||||
} else {
|
||||
serializer.serialize_bytes(&self.to_bytes()[..])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de> Deserialize<'de> for PeerId {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
use serde::de::*;
|
||||
|
||||
struct PeerIdVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for PeerIdVisitor {
|
||||
type Value = PeerId;
|
||||
|
||||
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "valid peer id")
|
||||
}
|
||||
|
||||
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
PeerId::from_bytes(v).map_err(|_| Error::invalid_value(Unexpected::Bytes(v), &self))
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
PeerId::from_str(v).map_err(|_| Error::invalid_value(Unexpected::Str(v), &self))
|
||||
}
|
||||
}
|
||||
|
||||
if deserializer.is_human_readable() {
|
||||
deserializer.deserialize_str(PeerIdVisitor)
|
||||
} else {
|
||||
deserializer.deserialize_bytes(PeerIdVisitor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ParseError {
|
||||
#[error("base-58 decode error: {0}")]
|
||||
B58(#[from] bs58::decode::Error),
|
||||
#[error("decoding multihash failed")]
|
||||
MultiHash,
|
||||
}
|
||||
|
||||
impl FromStr for PeerId {
|
||||
type Err = ParseError;
|
||||
|
||||
#[inline]
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let bytes = bs58::decode(s).into_vec()?;
|
||||
PeerId::from_bytes(&bytes).map_err(|_| ParseError::MultiHash)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{identity, PeerId};
|
||||
|
||||
#[test]
|
||||
fn peer_id_is_public_key() {
|
||||
let key = identity::Keypair::generate_ed25519().public();
|
||||
let peer_id = key.to_peer_id();
|
||||
assert_eq!(peer_id.is_public_key(&key), Some(true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn peer_id_into_bytes_then_from_bytes() {
|
||||
let peer_id = identity::Keypair::generate_ed25519().public().to_peer_id();
|
||||
let second = PeerId::from_bytes(&peer_id.to_bytes()).unwrap();
|
||||
assert_eq!(peer_id, second);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn peer_id_to_base58_then_back() {
|
||||
let peer_id = identity::Keypair::generate_ed25519().public().to_peer_id();
|
||||
let second: PeerId = peer_id.to_base58().parse().unwrap();
|
||||
assert_eq!(peer_id, second);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn random_peer_id_is_valid() {
|
||||
for _ in 0..5000 {
|
||||
let peer_id = PeerId::random();
|
||||
assert_eq!(peer_id, PeerId::from_bytes(&peer_id.to_bytes()).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_peer_id_from_multi_address() {
|
||||
let address = "/memory/1234/p2p/12D3KooWGQmdpzHXCqLno4mMxWXKNFQHASBeF99gTm2JR8Vu5Bdc"
|
||||
.to_string()
|
||||
.parse()
|
||||
.unwrap();
|
||||
|
||||
let peer_id = PeerId::try_from_multiaddr(&address).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
peer_id,
|
||||
"12D3KooWGQmdpzHXCqLno4mMxWXKNFQHASBeF99gTm2JR8Vu5Bdc"
|
||||
.parse()
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_panic_on_extract_peer_id_from_multi_address_if_not_present() {
|
||||
let address = "/memory/1234".to_string().parse().unwrap();
|
||||
|
||||
let maybe_empty = PeerId::try_from_multiaddr(&address);
|
||||
|
||||
assert!(maybe_empty.is_none());
|
||||
}
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
use crate::identity::error::SigningError;
|
||||
use crate::identity::Keypair;
|
||||
use crate::proto;
|
||||
use crate::signed_envelope::SignedEnvelope;
|
||||
use crate::{signed_envelope, DecodeError, Multiaddr, PeerId};
|
||||
use crate::{proto, signed_envelope, DecodeError, Multiaddr};
|
||||
use instant::SystemTime;
|
||||
use libp2p_identity::Keypair;
|
||||
use libp2p_identity::PeerId;
|
||||
use libp2p_identity::SigningError;
|
||||
use quick_protobuf::{BytesReader, Writer};
|
||||
use std::convert::TryInto;
|
||||
|
||||
|
@ -1,8 +1,7 @@
|
||||
use crate::identity::error::SigningError;
|
||||
use crate::identity::Keypair;
|
||||
use crate::{identity, proto, DecodeError, PublicKey};
|
||||
use crate::{proto, DecodeError};
|
||||
use libp2p_identity::SigningError;
|
||||
use libp2p_identity::{Keypair, PublicKey};
|
||||
use quick_protobuf::{BytesReader, Writer};
|
||||
use std::convert::TryInto;
|
||||
use std::fmt;
|
||||
use unsigned_varint::encode::usize_buffer;
|
||||
|
||||
@ -77,7 +76,7 @@ impl SignedEnvelope {
|
||||
use quick_protobuf::MessageWrite;
|
||||
|
||||
let envelope = proto::Envelope {
|
||||
public_key: Some((&self.key).into()),
|
||||
public_key: self.key.to_protobuf_encoding(),
|
||||
payload_type: self.payload_type,
|
||||
payload: self.payload,
|
||||
signature: self.signature,
|
||||
@ -102,10 +101,7 @@ impl SignedEnvelope {
|
||||
proto::Envelope::from_reader(&mut reader, bytes).map_err(DecodeError::from)?;
|
||||
|
||||
Ok(Self {
|
||||
key: envelope
|
||||
.public_key
|
||||
.ok_or(DecodingError::MissingPublicKey)?
|
||||
.try_into()?,
|
||||
key: PublicKey::from_protobuf_encoding(&envelope.public_key)?,
|
||||
payload_type: envelope.payload_type.to_vec(),
|
||||
payload: envelope.payload.to_vec(),
|
||||
signature: envelope.signature.to_vec(),
|
||||
@ -152,7 +148,7 @@ pub enum DecodingError {
|
||||
InvalidEnvelope(#[from] DecodeError),
|
||||
/// The public key in the envelope could not be converted to our internal public key type.
|
||||
#[error("Failed to convert public key")]
|
||||
InvalidPublicKey(#[from] identity::error::DecodingError),
|
||||
InvalidPublicKey(#[from] libp2p_identity::DecodingError),
|
||||
/// The public key in the envelope could not be converted to our internal public key type.
|
||||
#[error("Public key is missing from protobuf struct")]
|
||||
MissingPublicKey,
|
||||
|
@ -33,9 +33,10 @@ use crate::{
|
||||
self, apply_inbound, apply_outbound, InboundUpgrade, InboundUpgradeApply, OutboundUpgrade,
|
||||
OutboundUpgradeApply, UpgradeError,
|
||||
},
|
||||
Negotiated, PeerId,
|
||||
Negotiated,
|
||||
};
|
||||
use futures::{prelude::*, ready};
|
||||
use libp2p_identity::PeerId;
|
||||
use multiaddr::Multiaddr;
|
||||
use std::{
|
||||
error::Error,
|
||||
|
Reference in New Issue
Block a user