Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core: Update to asn1_der v0.7 #2000

Merged
merged 8 commits into from
Mar 22, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
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
4 changes: 3 additions & 1 deletion core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ keywords = ["peer-to-peer", "libp2p", "networking"]
categories = ["network-programming", "asynchronous"]

[dependencies]
asn1_der = "0.6.1"
asn1_der = "0.7.4"
bs58 = "0.4.0"
ed25519-dalek = "1.0.1"
either = "1.5"
Expand Down Expand Up @@ -47,6 +47,8 @@ libp2p-tcp = { path = "../transports/tcp" }
multihash = { version = "0.13", default-features = false, features = ["arb"] }
quickcheck = "0.9.0"
wasm-timer = "0.2"
# TODO: For testing only. Remove.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will remove this before merging. Leaving it for now to ensure I don't introduce a regression.

libp2p-core-v026 = { version = "0.26.0", package = "libp2p-core" }

[build-dependencies]
prost-build = "0.7"
Expand Down
198 changes: 144 additions & 54 deletions core/src/identity/rsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@

//! RSA keys.

use asn1_der::{Asn1Der, FromDerObject, IntoDerObject, DerObject, DerTag, DerValue, Asn1DerError};
use lazy_static::lazy_static;
use asn1_der::{typed::{DerEncodable, DerDecodable, DerTypeView, Sequence}, DerObject, Asn1DerError, Asn1DerErrorVariant, Sink, VecBacking};
use super::error::*;
use ring::rand::SystemRandom;
use ring::signature::{self, RsaKeyPair, RSA_PKCS1_SHA256, RSA_PKCS1_2048_8192_SHA256};
Expand Down Expand Up @@ -88,20 +87,21 @@ impl PublicKey {
pub fn encode_x509(&self) -> Vec<u8> {
let spki = Asn1SubjectPublicKeyInfo {
algorithmIdentifier: Asn1RsaEncryption {
algorithm: Asn1OidRsaEncryption(),
algorithm: Asn1OidRsaEncryption,
parameters: ()
},
subjectPublicKey: Asn1SubjectPublicKey(self.clone())
};
let mut buf = vec![0u8; spki.serialized_len()];
spki.serialize(buf.iter_mut()).map(|_| buf)
.expect("RSA X.509 public key encoding failed.")
let mut buf = Vec::new();
let buf = spki.encode(&mut buf).map(|_| buf)
.expect("RSA X.509 public key encoding failed.");
buf
}

/// 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::deserialize(pk.iter())
Asn1SubjectPublicKeyInfo::decode(pk)
.map_err(|e| DecodingError::new("RSA X.509").source(e))
.map(|spki| spki.subjectPublicKey.0)
}
Expand All @@ -123,93 +123,172 @@ impl fmt::Debug for PublicKey {
// Primer: http://luca.ntop.org/Teaching/Appunti/asn1.html
// Playground: https://lapo.it/asn1js/

lazy_static! {
/// 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
static ref OID_RSA_ENCRYPTION_DER: DerObject =
DerObject {
tag: DerTag::x06,
value: DerValue {
data: vec![ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 ]
}
};
const OID: [u8;9] = [ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 ];
}

/// The ASN.1 OID for "rsaEncryption".
#[derive(Clone)]
struct Asn1OidRsaEncryption();

impl IntoDerObject for Asn1OidRsaEncryption {
fn into_der_object(self) -> DerObject {
OID_RSA_ENCRYPTION_DER.clone()
}
fn serialized_len(&self) -> usize {
OID_RSA_ENCRYPTION_DER.serialized_len()
impl DerEncodable for Asn1OidRsaEncryption {
fn encode<S: Sink>(&self, sink: &mut S) -> Result<(), Asn1DerError> {
Asn1RawOid::write(&Self::OID, sink)
}
}

impl FromDerObject for Asn1OidRsaEncryption {
fn from_der_object(o: DerObject) -> Result<Self, Asn1DerError> {
if o.tag != DerTag::x06 {
return Err(Asn1DerError::InvalidTag)
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.",
)))
}
if o.value != OID_RSA_ENCRYPTION_DER.value {
return Err(Asn1DerError::InvalidEncoding)
}
Ok(Asn1OidRsaEncryption())
}
}

/// The ASN.1 AlgorithmIdentifier for "rsaEncryption".
#[derive(Asn1Der)]
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 IntoDerObject for Asn1SubjectPublicKey {
fn into_der_object(self) -> DerObject {
let pk_der = (self.0).0;
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::new(DerTag::x03, bit_string.into())
}
fn serialized_len(&self) -> usize {
DerObject::compute_serialized_len((self.0).0.len() + 1)
DerObject::write(3, bit_string.len(), &mut bit_string.iter(), sink)?;
Ok(())
}
}

impl FromDerObject for Asn1SubjectPublicKey {
fn from_der_object(o: DerObject) -> Result<Self, Asn1DerError> {
if o.tag != DerTag::x03 {
return Err(Asn1DerError::InvalidTag)
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> = o.value.data.into_iter().skip(1).collect();

let pk_der: Vec<u8> = object.value().into_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(Asn1SubjectPublicKey(PublicKey(pk_der)))
Ok(Self(PublicKey(pk_der)))
}
}

/// ASN.1 SubjectPublicKeyInfo
#[derive(Asn1Der)]
#[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::*;
Expand All @@ -221,8 +300,9 @@ mod tests {
const KEY2: &'static [u8] = include_bytes!("test/rsa-3072.pk8");
const KEY3: &'static [u8] = include_bytes!("test/rsa-4096.pk8");

// TODO: Remove libp2p_core_v026. For compatibility testing only.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will remove this before merging. Leaving it for now to ensure I don't introduce a regression.

#[derive(Clone)]
struct SomeKeypair(Keypair);
struct SomeKeypair(Keypair, libp2p_core_v026::identity::rsa::Keypair);

impl fmt::Debug for SomeKeypair {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Expand All @@ -233,7 +313,11 @@ mod tests {
impl Arbitrary for SomeKeypair {
fn arbitrary<G: Gen>(g: &mut G) -> SomeKeypair {
let mut key = [KEY1, KEY2, KEY3].choose(g).unwrap().to_vec();
SomeKeypair(Keypair::from_pkcs8(&mut key).unwrap())
let mut key2 = key.clone();
SomeKeypair(
Keypair::from_pkcs8(&mut key).unwrap(),
libp2p_core_v026::identity::rsa::Keypair::from_pkcs8(&mut key2).unwrap(),
)
}
}

Expand All @@ -246,9 +330,16 @@ mod tests {

#[test]
fn rsa_x509_encode_decode() {
fn prop(SomeKeypair(kp): SomeKeypair) -> Result<bool, String> {
fn prop(SomeKeypair(kp, old_kp): SomeKeypair) -> Result<bool, String> {
let pk = kp.public();
PublicKey::decode_x509(&pk.encode_x509())
let old_kp = old_kp.public();

let x509 = pk.encode_x509();
let x509_old = old_kp.encode_x509();

assert_eq!(x509, x509_old);

PublicKey::decode_x509(&x509)
.map_err(|e| e.to_string())
.map(|pk2| pk2 == pk)
}
Expand All @@ -257,10 +348,9 @@ mod tests {

#[test]
fn rsa_sign_verify() {
fn prop(SomeKeypair(kp): SomeKeypair, msg: Vec<u8>) -> Result<bool, SigningError> {
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(_,_) -> _);
}
}

14 changes: 7 additions & 7 deletions core/src/identity/secp256k1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

//! Secp256k1 keys.

use asn1_der::{FromDerObject, DerObject};
use asn1_der::typed::{DerDecodable, Sequence};
use rand::RngCore;
use sha2::{Digest as ShaDigestTrait, Sha256};
use secp256k1::{Message, Signature};
Expand Down Expand Up @@ -110,21 +110,21 @@ impl SecretKey {
}

/// Decode a DER-encoded Secp256k1 secret key in an ECPrivateKey
/// structure as defined in [RFC5915].
/// 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 obj: Vec<DerObject> = FromDerObject::deserialize((&*der_obj).iter())
let obj: Sequence = DerDecodable::decode(der_obj)
.map_err(|e| DecodingError::new("Secp256k1 DER ECPrivateKey").source(e))?;
der_obj.zeroize();
let sk_obj = obj.into_iter().nth(1)
.ok_or_else(|| DecodingError::new("Not enough elements in DER"))?;
let mut sk_bytes: Vec<u8> = FromDerObject::from_der_object(sk_obj)
let sk_obj = obj.get(1)
.map_err(|e| DecodingError::new("Not enough elements in DER").source(e))?;
let mut sk_bytes: Vec<u8> = asn1_der::typed::DerDecodable::load(sk_obj)
.map_err(DecodingError::new)?;
let sk = SecretKey::from_bytes(&mut sk_bytes)?;
sk_bytes.zeroize();
der_obj.zeroize();
Ok(sk)
}

Expand Down