Skip to content
Merged
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
Prev Previous commit
Next Next commit
Fix ECDSA ASN.1 PublicKey serialization
Furthermore SecretKey can now be serialized/deserialized to/from a byte
vector.
  • Loading branch information
davxy committed Nov 29, 2021
commit a821bfbe9fe190d5fd293d92b02c97607e2c9f9c
102 changes: 93 additions & 9 deletions core/src/identity/ecdsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@

use super::error::DecodingError;
use core::fmt;
use p256::ecdsa::{
signature::{Signer, Verifier},
Signature, SigningKey, VerifyingKey,
use p256::{
ecdsa::{
signature::{Signer, Verifier},
Signature, SigningKey, VerifyingKey,
},
EncodedPoint,
};

/// An ECDSA keypair.
Expand Down Expand Up @@ -93,6 +96,18 @@ impl SecretKey {
pub fn sign(&self, msg: &[u8]) -> Vec<u8> {
self.0.sign(msg).to_der().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::new("failed to parse ecdsa p256 secret key").source(err))
.map(SecretKey)
}
}

impl fmt::Debug for SecretKey {
Expand All @@ -115,16 +130,85 @@ impl PublicKey {
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(|_| {
DecodingError::new("failed to parse ecdsa p256 public key, bad point encoding")
})?;

VerifyingKey::from_encoded_point(&enc_pt)
.map_err(|err| DecodingError::new("failed to parse ecdsa p256 public key").source(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> {
self.0.to_encoded_point(false).as_bytes().to_owned()
let buf = self.to_bytes();
Self::add_asn1_header(&buf)
}

// Decode a public key from a DER encoded byte buffer as defined for SEC1 standard.
/// Decode a public key into a DER encoded byte buffer as defined by SEC1 standard.
pub fn decode_der(k: &[u8]) -> Result<PublicKey, DecodingError> {
VerifyingKey::from_sec1_bytes(k)
.map_err(|_| DecodingError::new("failed to parse ecdsa p256 public key"))
.map(PublicKey)
let buf = Self::del_asn1_header(k).ok_or(DecodingError::new(
"failed to parse asn.1 encoded ecdsa p256 public key",
))?;
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 as usize)?;
Some(key_buf)
}
}

Expand All @@ -143,7 +227,7 @@ mod tests {
use super::*;

#[test]
fn ecdsa_signature() {
fn sign_verify() {
let pair = Keypair::generate();
let pk = pair.public();

Expand Down