Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
First rough implementation of ECDSA support
  • Loading branch information
davxy committed Nov 16, 2021
commit 7b0fec43bef43397c95d6a8a062cfd293423dac1
3 changes: 2 additions & 1 deletion core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,9 @@ rand07 = { package = "rand", version = "0.7" }
prost-build = "0.9"

[features]
default = ["secp256k1"]
default = ["secp256k1", "ecdsa"]
secp256k1 = ["libsecp256k1"]
ecdsa = []

[[bench]]
name = "peer_id"
Expand Down
42 changes: 42 additions & 0 deletions core/src/identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
//! (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(not(target_arch = "wasm32"))]
pub mod rsa;
Expand Down Expand Up @@ -71,6 +73,9 @@ pub enum Keypair {
/// A Secp256k1 keypair.
#[cfg(feature = "secp256k1")]
Secp256k1(secp256k1::Keypair),
/// An ECDSA keypair.
#[cfg(feature = "ecdsa")]
Ecdsa(ecdsa::Keypair),
}

impl Keypair {
Expand All @@ -85,6 +90,11 @@ impl Keypair {
Keypair::Secp256k1(secp256k1::Keypair::generate())
}

#[cfg(feature = "ecdsa")]
pub fn generate_ecdsa(curve_id: ecdsa::CurveId) -> Keypair {
Keypair::Ecdsa(ecdsa::Keypair::generate(curve_id))
}

/// Decode an keypair from a DER-encoded secret key in PKCS#8 PrivateKeyInfo
/// format (i.e. unencrypted) as defined in [RFC5208].
///
Expand Down Expand Up @@ -114,6 +124,8 @@ impl Keypair {
Rsa(ref pair) => pair.sign(msg),
#[cfg(feature = "secp256k1")]
Secp256k1(ref pair) => pair.secret().sign(msg),
#[cfg(feature = "ecdsa")]
Ecdsa(ref pair) => Ok(pair.sign(msg)),
}
}

Expand All @@ -126,6 +138,8 @@ impl Keypair {
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()),
}
}

Expand All @@ -150,6 +164,12 @@ impl Keypair {
"Encoding Secp256k1 key into Protobuf is unsupported",
))
}
#[cfg(feature = "ecdsa")]
Self::Ecdsa(_) => {
return Err(DecodingError::new(
"Encoding ECDSA key into Protobuf is unsupported",
))
}
};

Ok(pk.encode_to_vec())
Expand Down Expand Up @@ -177,6 +197,9 @@ impl Keypair {
keys_proto::KeyType::Secp256k1 => Err(DecodingError::new(
"Decoding Secp256k1 key from Protobuf is unsupported.",
)),
keys_proto::KeyType::Ecdsa => Err(DecodingError::new(
"Decoding ECDSA key from Protobuf is unsupported.",
)),
}
}
}
Expand All @@ -199,6 +222,9 @@ pub enum PublicKey {
#[cfg(feature = "secp256k1")]
/// A public Secp256k1 key.
Secp256k1(secp256k1::PublicKey),
/// A public ECDSA key.
#[cfg(feature = "ecdsa")]
Ecdsa(ecdsa::PublicKey),
}

impl PublicKey {
Expand All @@ -215,6 +241,8 @@ impl PublicKey {
Rsa(pk) => pk.verify(msg, sig),
#[cfg(feature = "secp256k1")]
Secp256k1(pk) => pk.verify(msg, sig),
#[cfg(feature = "ecdsa")]
Ecdsa(pk) => pk.verify(msg, sig),
}
}

Expand Down Expand Up @@ -266,6 +294,11 @@ impl From<&PublicKey> for keys_proto::PublicKey {
r#type: keys_proto::KeyType::Secp256k1 as i32,
data: key.encode().to_vec(),
},
#[cfg(feature = "ecdsa")]
PublicKey::Ecdsa(key) => keys_proto::PublicKey {
r#type: keys_proto::KeyType::Ecdsa as i32,
data: key.encode_asn1(),
},
}
}
}
Expand Down Expand Up @@ -299,6 +332,15 @@ impl TryFrom<keys_proto::PublicKey> for PublicKey {
log::debug!("support for secp256k1 was disabled at compile-time");
Err(DecodingError::new("Unsupported"))
}
#[cfg(feature = "ecdsa")]
keys_proto::KeyType::Ecdsa => {
ecdsa::PublicKey::decode(&pubkey.data).map(PublicKey::Ecdsa)
}
#[cfg(not(feature = "ecdsa"))]
keys_proto::KeyType::Ecdsa => {
log::debug!("support for ECDSA was disabled at compile-time");
Err(DecodingError::new("Unsupported"))
}
}
}
}
Expand Down
186 changes: 186 additions & 0 deletions core/src/identity/ecdsa.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
// 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.

use super::error::{DecodingError, SigningError};
use core::fmt;
use ring::{
rand::SystemRandom,
signature::{
self, EcdsaKeyPair as EcdsaKeyPairImpl, EcdsaSigningAlgorithm, EcdsaVerificationAlgorithm,
KeyPair as RingKeyPair, UnparsedPublicKey as PublicKeyImpl,
},
};
use std::sync::Arc;

#[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub enum CurveId {
Secp256r1,
}

/// An ECDSA keypair.
#[derive(Clone)]
pub struct Keypair {
curve_id: CurveId,
pair: Arc<EcdsaKeyPairImpl>,
}

impl Keypair {
/// Generate a new random ECDSA p256 keypair.
pub fn generate(curve_id: CurveId) -> Keypair {
let rng = SystemRandom::new();
let alg = get_sign_alg(curve_id);
let pkcs8 = EcdsaKeyPairImpl::generate_pkcs8(alg, &rng).unwrap();
let imp = EcdsaKeyPairImpl::from_pkcs8(alg, pkcs8.as_ref()).unwrap();
Keypair {
curve_id,
pair: Arc::new(imp),
}
}

/// Sign a message using the private key of this keypair.
pub fn sign(&self, msg: &[u8]) -> Vec<u8> {
let rng = SystemRandom::new();
self.pair.sign(&rng, msg).unwrap().as_ref().to_vec()
}

// Get the public key of this keypair.
pub fn public(&self) -> PublicKey {
PublicKey {
curve_id: self.curve_id,
key: self.pair.public_key().as_ref().to_owned(),
}
}
}

impl fmt::Debug for Keypair {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Keypair")
.field("public", &self.public())
.finish()
}
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PublicKey {
curve_id: CurveId,
key: Vec<u8>,
}

impl PublicKey {
/// Verify the ecdsa signature on a message using the public key.
pub fn verify(&self, msg: &[u8], sig: &[u8]) -> bool {
let alg = get_verify_alg(self.curve_id);
let pk = PublicKeyImpl::new(alg, &self.key);
pk.verify(msg, sig).is_ok()
}

// Encode a public key in ASN.1 format byte buffer.
pub fn encode_asn1(&self) -> Vec<u8> {
let bytes = self.key.clone();
add_asn1_header(self.curve_id, bytes)
}

// Decode a public key from an ASN.1 byte buffer.
pub fn decode(k: &[u8]) -> Result<PublicKey, DecodingError> {
let curve_id = CurveId::Secp256r1;
let key = del_asn1_header(k.to_owned())?;
Ok(PublicKey { curve_id, key })
// .map_err(|_| DecodingError::new("failed to parse secp256k1 public key"))
// .map(PublicKey)
}
}

fn get_sign_alg(curve_id: CurveId) -> &'static EcdsaSigningAlgorithm {
match curve_id {
CurveId::Secp256r1 => &signature::ECDSA_P256_SHA256_FIXED_SIGNING,
}
}

fn get_verify_alg(curve_id: CurveId) -> &'static EcdsaVerificationAlgorithm {
match curve_id {
CurveId::Secp256r1 => &signature::ECDSA_P256_SHA256_FIXED,
}
}

fn del_asn1_header(asn1_bytes: Vec<u8>) -> Result<Vec<u8>, DecodingError> {
let key_len = 65;
let key = &asn1_bytes[asn1_bytes.len() - key_len..];
Ok(key.to_owned())
}

// SECG named elliptic curve)
fn get_curve_oid(curve_id: CurveId) -> Vec<u8> {
match curve_id {
// secp256r1 OID: 1.2.840.10045.3.1.7
CurveId::Secp256r1 => vec![0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07],
// secp384r1 OID: 1.3.132.0.34
//CurveId::Secp384r1 => vec![0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22],
}
}

// ASN1 header.
#[rustfmt::skip]
fn add_asn1_header(curve_id: CurveId, mut key_bytes: Vec<u8>) -> Vec<u8> {
let mut res = vec![
// ASN.1 struct type and length.
0x30, 0x00,
// ASN.1 struct type and length.
0x30, 0x00,
];

// ecPublicKey (ANSI X9.62 public key type) OID: 1.2.840.10045.2.1
let mut ec_oid = vec![ 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01 ];
// curve OID
let mut curve_oid = get_curve_oid(curve_id);

let oids_len = ec_oid.len() + curve_oid.len();
res.append(&mut ec_oid);
res.append(&mut curve_oid);

// Update oids length field
res[3] = oids_len as u8;

// Append key bitstring type and length.
let mut bitstring_type_len = vec![
0x03, (key_bytes.len() + 1) as u8, 0x00,
];
res.append(&mut bitstring_type_len);
// Append key bitstring.
res.append(&mut key_bytes);
// Update overall length field.
res[1] = (res.len() - 2) as u8;

res
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn ecdsa_generate() {
let pair = Keypair::generate(CurveId::Secp256r1);

let public = pair.public();
println!("{:?}", public);
}
}
1 change: 1 addition & 0 deletions core/src/keys.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ enum KeyType {
RSA = 0;
Ed25519 = 1;
Secp256k1 = 2;
ECDSA = 3;
}

message PublicKey {
Expand Down