use der_parser::der::*;
use der_parser::error::BerError;
use der_parser::oid::Oid;
use der_parser::*;
use nom::combinator::{complete, eof};
use nom::IResult;
use std::convert::{From, TryInto};
use curve25519_dalek::edwards::CompressedEdwardsY;
use curve25519_dalek::montgomery::MontgomeryPoint;
use sha2::{Digest, Sha512};
pub use x25519_dalek::{PublicKey, StaticSecret};
use rand_core::{CryptoRng, RngCore};
use std::fmt;
const ED_25519_OID: Oid<'static> = oid!(1.3.101 .112);
const X_25519_OID: Oid<'static> = oid!(1.3.101 .110);
#[derive(Debug)]
pub enum Curve25519ParserError {
BerError(der_parser::error::BerError),
PemError(pem::PemError),
NomError(nom::Err<der_parser::error::BerError>),
UnknownOid,
InvalidData,
InvalidPEMTag,
}
impl From<der_parser::error::BerError> for Curve25519ParserError {
fn from(error: der_parser::error::BerError) -> Self {
Curve25519ParserError::BerError(error)
}
}
impl From<pem::PemError> for Curve25519ParserError {
fn from(error: pem::PemError) -> Self {
Curve25519ParserError::PemError(error)
}
}
impl From<nom::Err<der_parser::error::BerError>> for Curve25519ParserError {
fn from(error: nom::Err<der_parser::error::BerError>) -> Self {
Curve25519ParserError::NomError(error)
}
}
impl fmt::Display for Curve25519ParserError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{self:?}")
}
}
#[derive(Debug, PartialEq)]
struct Der25519PrivateHeader<'a> {
tag: DerObject<'a>,
}
#[derive(Debug, PartialEq)]
struct Der25519PrivateStruct<'a> {
header: Der25519PrivateHeader<'a>,
data: DerObject<'a>,
}
fn parse_25519_private_header(i: &[u8]) -> IResult<&[u8], Der25519PrivateHeader, BerError> {
parse_der_container(|i: &[u8], hdr| {
if hdr.tag() != Tag::Sequence {
return Err(nom::Err::Error(BerError::InvalidTag));
}
let (i, tag) = parse_der_oid(i)?;
eof(i)?;
Ok((i, Der25519PrivateHeader { tag }))
})(i)
}
fn parse_25519_private(i: &[u8]) -> IResult<&[u8], Der25519PrivateStruct, BerError> {
parse_der_container(|i: &[u8], hdr| {
if hdr.tag() != Tag::Sequence {
return Err(nom::Err::Error(BerError::InvalidTag));
}
let (i, _unk) = parse_der_integer(i)?;
let (i, header) = complete(parse_25519_private_header)(i)?;
let (i, data) = parse_der_octetstring(i)?;
eof(i)?;
Ok((i, Der25519PrivateStruct { header, data }))
})(i)
}
const TAG_OCTETSTRING: u8 = 4;
pub fn parse_openssl_25519_privkey_der(data: &[u8]) -> Result<StaticSecret, Curve25519ParserError> {
let (_remain, private) = parse_25519_private(data)?;
let data = private.data.content.as_slice()?;
if data.len() != 34 || data[0] != TAG_OCTETSTRING || data[1] != 32 {
return Err(Curve25519ParserError::InvalidData);
}
let mut key_data = [0u8; 32];
let read_oid = private.header.tag.as_oid()?;
if read_oid == &ED_25519_OID {
key_data.copy_from_slice(&Sha512::digest(&data[2..34])[0..32]);
} else if read_oid == &X_25519_OID {
key_data.copy_from_slice(&data[2..34]);
} else {
return Err(Curve25519ParserError::UnknownOid);
}
Ok(StaticSecret::from(key_data))
}
#[derive(Debug, PartialEq)]
struct DerEd25519PublicHeader<'a> {
tag: DerObject<'a>,
}
#[derive(Debug, PartialEq)]
struct DerEd25519PublicStruct<'a> {
header: DerEd25519PublicHeader<'a>,
data: DerObject<'a>,
}
fn parse_25519_public_header(i: &[u8]) -> IResult<&[u8], DerEd25519PublicHeader, BerError> {
parse_der_container(|i: &[u8], hdr| {
if hdr.tag() != Tag::Sequence {
return Err(nom::Err::Error(BerError::InvalidTag));
}
let (i, tag) = parse_der_oid(i)?;
eof(i)?;
Ok((i, DerEd25519PublicHeader { tag }))
})(i)
}
fn parse_25519_public(i: &[u8]) -> IResult<&[u8], DerEd25519PublicStruct, BerError> {
parse_der_container(|i: &[u8], hdr| {
if hdr.tag() != Tag::Sequence {
return Err(nom::Err::Error(BerError::InvalidTag));
}
let (i, header) = complete(parse_25519_public_header)(i)?;
let (i, data) = parse_der_bitstring(i)?;
eof(i)?;
Ok((i, DerEd25519PublicStruct { header, data }))
})(i)
}
pub fn parse_openssl_25519_pubkey_der(data: &[u8]) -> Result<PublicKey, Curve25519ParserError> {
let (_remain, ed25519_public) = parse_25519_public(data)?;
let data = ed25519_public.data.content.as_slice()?;
let data: [u8; 32] = data
.try_into()
.map_err(|_| Curve25519ParserError::InvalidData)?;
let read_oid = ed25519_public.header.tag.as_oid()?;
if read_oid == &ED_25519_OID {
CompressedEdwardsY::from_slice(&data)
.ok()
.and_then(|c| c.decompress())
.map(|v| PublicKey::from(v.to_montgomery().to_bytes()))
.ok_or(Curve25519ParserError::InvalidData)
} else if read_oid == &X_25519_OID {
Ok(PublicKey::from(MontgomeryPoint(data).to_bytes()))
} else {
Err(Curve25519ParserError::UnknownOid)
}
}
const PUBLIC_TAG: &[u8] = b"PUBLIC KEY";
const PRIVATE_TAG: &[u8] = b"PRIVATE KEY";
pub fn parse_openssl_25519_pubkey(data: &[u8]) -> Result<PublicKey, Curve25519ParserError> {
if let Ok(pem_data) = pem::parse(data) {
if pem_data.tag().as_bytes() != PUBLIC_TAG {
return Err(Curve25519ParserError::InvalidPEMTag);
}
parse_openssl_25519_pubkey_der(pem_data.contents())
} else {
parse_openssl_25519_pubkey_der(data)
}
}
pub fn parse_openssl_25519_privkey(data: &[u8]) -> Result<StaticSecret, Curve25519ParserError> {
if let Ok(pem_data) = pem::parse(data) {
if pem_data.tag().as_bytes() != PRIVATE_TAG {
return Err(Curve25519ParserError::InvalidPEMTag);
}
parse_openssl_25519_privkey_der(pem_data.contents())
} else {
parse_openssl_25519_privkey_der(data)
}
}
pub fn parse_openssl_25519_pubkeys_pem_many(
data: &[u8],
) -> Result<Vec<PublicKey>, Curve25519ParserError> {
let mut output = Vec::new();
for pem_data in pem::parse_many(data)? {
if pem_data.tag().as_bytes() != PUBLIC_TAG {
return Err(Curve25519ParserError::InvalidPEMTag);
}
output.push(parse_openssl_25519_pubkey_der(pem_data.contents())?);
}
Ok(output)
}
const PRIV_KEY_PREFIX: &[u8] = b"\x30\x2e\x02\x01\x00\x30\x05\x06\x03\x2b\x65\x6e\x04\x22\x04\x20";
const PUB_KEY_PREFIX: &[u8] = b"\x30\x2a\x30\x05\x06\x03\x2b\x65\x6e\x03\x21\x00";
const PRIV_KEY_TAG: &str = "PRIVATE KEY";
const PUB_KEY_TAG: &str = "PUBLIC KEY";
pub struct KeyPair {
pub public_der: [u8; PUB_KEY_PREFIX.len() + 32],
pub private_der: [u8; PRIV_KEY_PREFIX.len() + 32],
}
impl KeyPair {
pub fn public_as_pem(&self) -> String {
let out = pem::Pem::new(PUB_KEY_TAG, self.public_der.to_vec());
pem::encode(&out)
}
pub fn private_as_pem(&self) -> String {
let out = pem::Pem::new(PRIV_KEY_TAG, self.private_der.to_vec());
pem::encode(&out)
}
}
pub fn generate_keypair<T>(csprng: &mut T) -> Option<KeyPair>
where
T: RngCore + CryptoRng,
{
let mut private = [0u8; 32];
csprng.fill_bytes(&mut private);
let priv_key = StaticSecret::from(private);
let pubkey = PublicKey::from(&priv_key);
let public = pubkey.as_bytes();
let mut private_der = [0u8; PRIV_KEY_PREFIX.len() + 32];
private_der[..PRIV_KEY_PREFIX.len()].copy_from_slice(PRIV_KEY_PREFIX);
private_der[PRIV_KEY_PREFIX.len()..].copy_from_slice(&private);
let mut public_der = [0u8; PUB_KEY_PREFIX.len() + 32];
public_der[..PUB_KEY_PREFIX.len()].copy_from_slice(PUB_KEY_PREFIX);
public_der[PUB_KEY_PREFIX.len()..].copy_from_slice(&public[..]);
Some(KeyPair {
public_der,
private_der,
})
}
#[cfg(test)]
mod tests {
use super::*;
use rand::rngs::OsRng;
use x25519_dalek::PublicKey;
static X_DER_PRIV: &[u8] = include_bytes!("../../samples/test_x25519.der");
static X_DER_PUB: &[u8] = include_bytes!("../../samples/test_x25519_pub.der");
static ED_DER_PRIV: &[u8] = include_bytes!("../../samples/test_ed25519.der");
static ED_DER_PUB: &[u8] = include_bytes!("../../samples/test_ed25519_pub.der");
static PEM_PUB: &[u8] = include_bytes!("../../samples/test_ed25519_pub.pem");
static PEM_PRIV: &[u8] = include_bytes!("../../samples/test_ed25519.pem");
static PEM_PUB_MANY: &[u8] = include_bytes!("../../samples/test_25519_pub_many.pem");
#[test]
fn parse_and_check_ed_pubkeys_der() {
let priv_key = parse_openssl_25519_privkey_der(ED_DER_PRIV).unwrap();
let pub_key = parse_openssl_25519_pubkey_der(ED_DER_PUB).unwrap();
let computed_pub_key = PublicKey::from(&priv_key);
assert_eq!(pub_key.as_bytes().len(), 32);
assert_eq!(priv_key.to_bytes().len(), 32);
assert_eq!(computed_pub_key.as_bytes(), pub_key.as_bytes());
}
#[test]
fn parse_and_check_x_pubkeys_der() {
let priv_key = parse_openssl_25519_privkey_der(X_DER_PRIV).unwrap();
let pub_key = parse_openssl_25519_pubkey_der(X_DER_PUB).unwrap();
let computed_pub_key = PublicKey::from(&priv_key);
assert_eq!(pub_key.as_bytes().len(), 32);
assert_eq!(priv_key.to_bytes().len(), 32);
assert_eq!(computed_pub_key.as_bytes(), pub_key.as_bytes());
}
#[test]
fn parse_and_check_pubkeys_multi_format() {
let pub_key_pem = parse_openssl_25519_pubkey(PEM_PUB).unwrap();
let pub_key_der = parse_openssl_25519_pubkey(ED_DER_PUB).unwrap();
assert_eq!(pub_key_der.as_bytes().len(), 32);
assert_eq!(pub_key_der.as_bytes(), pub_key_pem.as_bytes());
let priv_key_pem = parse_openssl_25519_privkey(PEM_PRIV).unwrap();
let priv_key_der = parse_openssl_25519_privkey(ED_DER_PRIV).unwrap();
assert_eq!(priv_key_der.to_bytes().len(), 32);
assert_eq!(priv_key_der.to_bytes(), priv_key_pem.to_bytes());
}
#[test]
fn parse_many_pubkeys() {
let pub_keys_pem = parse_openssl_25519_pubkeys_pem_many(PEM_PUB).unwrap();
assert_eq!(pub_keys_pem.len(), 1);
let pub_key_der = parse_openssl_25519_pubkey(ED_DER_PUB).unwrap();
assert_eq!(pub_key_der.as_bytes().len(), 32);
assert_eq!(pub_key_der.as_bytes(), pub_keys_pem[0].as_bytes());
let pub_keys_pem = parse_openssl_25519_pubkeys_pem_many(PEM_PUB_MANY).unwrap();
assert_eq!(pub_keys_pem.len(), 3);
assert_eq!(pub_key_der.as_bytes(), pub_keys_pem[0].as_bytes());
assert_ne!(pub_key_der.as_bytes(), pub_keys_pem[1].as_bytes());
let pub_x_key_der = parse_openssl_25519_pubkey(X_DER_PUB).unwrap();
assert_eq!(pub_x_key_der.as_bytes(), pub_keys_pem[2].as_bytes());
}
#[test]
fn exports() {
let mut csprng = OsRng {};
let keypair = generate_keypair(&mut csprng).unwrap();
let priv_key = parse_openssl_25519_privkey_der(&keypair.private_der).unwrap();
let pub_key = parse_openssl_25519_pubkey_der(&keypair.public_der).unwrap();
let computed_pub_key = PublicKey::from(&priv_key);
assert_eq!(pub_key.as_bytes().len(), 32);
assert_eq!(priv_key.to_bytes().len(), 32);
assert_eq!(computed_pub_key.as_bytes(), pub_key.as_bytes());
let pub_pem_key = keypair.public_as_pem();
assert_eq!(
parse_openssl_25519_pubkey(pub_pem_key.as_bytes())
.unwrap()
.as_bytes(),
pub_key.as_bytes()
);
let priv_pem_key = keypair.private_as_pem();
assert_eq!(
&parse_openssl_25519_privkey(priv_pem_key.as_bytes())
.unwrap()
.to_bytes(),
&priv_key.to_bytes()
);
}
}