use crate::pem;
use base64::Engine;
use base64::engine::general_purpose::STANDARD as BASE64;
use der::asn1::{OctetString, OctetStringRef};
use der::{Decode, Encode};
use ed25519_dalek::pkcs8::{DecodePrivateKey, DecodePublicKey, EncodePublicKey};
use ed25519_dalek::{Signer, Verifier};
use pkcs8::PrivateKeyInfoRef;
use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
use sha2::Digest;
use spki::der::AnyRef;
use spki::der::asn1::BitStringRef;
use spki::{AlgorithmIdentifier, ObjectIdentifier, SubjectPublicKeyInfo};
use zeroize::Zeroizing;
pub const OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.101.112");
pub const SECRET_KEY_SIZE: usize = 32;
pub const PUBLIC_KEY_SIZE: usize = 32;
pub const SIGNATURE_SIZE: usize = 64;
pub const FINGERPRINT_SIZE: usize = 32;
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
pub enum Error {
#[error("pem: {0}")]
Pem(#[from] pem::Error),
#[error("invalid PEM tag {0}")]
UnexpectedPemTag(String),
#[error("not an Ed25519 key")]
UnexpectedAlgorithm,
#[error("malformed key: {0}")]
MalformedKey(String),
#[error("trailing data in key encoding")]
TrailingData,
#[error("signature verification failed")]
InvalidSignature,
}
#[derive(Clone)]
pub struct SecretKey {
inner: ed25519_dalek::SigningKey,
}
impl SecretKey {
pub fn generate() -> SecretKey {
let mut seed = Zeroizing::new([0u8; SECRET_KEY_SIZE]);
getrandom::fill(seed.as_mut()).unwrap();
Self::from_bytes(&seed)
}
pub fn from_bytes(bin: &[u8; SECRET_KEY_SIZE]) -> Self {
let key = Zeroizing::new(ed25519_dalek::SecretKey::from(*bin));
let sig = ed25519_dalek::SigningKey::from(&*key);
Self { inner: sig }
}
pub fn from_der(der: &[u8]) -> Result<Self, Error> {
let info =
PrivateKeyInfoRef::try_from(der).map_err(|err| Error::MalformedKey(err.to_string()))?;
let length = info
.encoded_len()
.map_err(|err| Error::MalformedKey(err.to_string()))?;
if length.try_into() != Ok(der.len()) {
return Err(Error::TrailingData);
}
if info.algorithm.oid != OID {
return Err(Error::UnexpectedAlgorithm);
}
if info.public_key.is_some() {
return Err(Error::MalformedKey("unsupported PKCS#8 version".into()));
}
let inner = ed25519_dalek::SigningKey::from_pkcs8_der(der)
.map_err(|err| Error::MalformedKey(err.to_string()))?;
Ok(Self { inner })
}
pub fn from_pem(pem_str: &str) -> Result<Self, Error> {
let (kind, data) = pem::decode(pem_str.as_bytes())?;
if kind != "PRIVATE KEY" {
return Err(Error::UnexpectedPemTag(kind));
}
Self::from_der(&Zeroizing::new(data))
}
pub fn to_bytes(&self) -> Zeroizing<[u8; SECRET_KEY_SIZE]> {
Zeroizing::new(self.inner.to_bytes())
}
pub fn to_der(&self) -> Zeroizing<Vec<u8>> {
let seed = OctetString::new(self.inner.to_bytes()).unwrap();
let inner = Zeroizing::new(seed.to_der().unwrap());
let alg = pkcs8::AlgorithmIdentifierRef {
oid: OID,
parameters: None::<AnyRef>,
};
let info = PrivateKeyInfoRef {
algorithm: alg,
private_key: OctetStringRef::new(&inner).unwrap(),
public_key: None,
};
Zeroizing::new(info.to_der().unwrap())
}
pub fn to_pem(&self) -> Zeroizing<String> {
Zeroizing::new(pem::encode("PRIVATE KEY", &self.to_der()))
}
pub fn public_key(&self) -> PublicKey {
PublicKey {
inner: self.inner.verifying_key(),
}
}
pub fn fingerprint(&self) -> Fingerprint {
self.public_key().fingerprint()
}
pub fn sign(&self, message: &[u8]) -> Signature {
Signature(self.inner.sign(message).to_bytes())
}
}
#[derive(Debug, Clone)]
pub struct PublicKey {
inner: ed25519_dalek::VerifyingKey,
}
impl PublicKey {
pub fn from_bytes(bin: &[u8; PUBLIC_KEY_SIZE]) -> Result<Self, Error> {
let inner = ed25519_dalek::VerifyingKey::from_bytes(bin)
.map_err(|err| Error::MalformedKey(err.to_string()))?;
Ok(Self { inner })
}
pub fn from_der(der: &[u8]) -> Result<Self, Error> {
let info: SubjectPublicKeyInfo<AlgorithmIdentifier<AnyRef>, BitStringRef> =
SubjectPublicKeyInfo::from_der(der)
.map_err(|err| Error::MalformedKey(err.to_string()))?;
let length = info
.encoded_len()
.map_err(|err| Error::MalformedKey(err.to_string()))?;
if length.try_into() != Ok(der.len()) {
return Err(Error::TrailingData);
}
if info.algorithm.oid != OID {
return Err(Error::UnexpectedAlgorithm);
}
let inner = ed25519_dalek::VerifyingKey::from_public_key_der(der)
.map_err(|err| Error::MalformedKey(err.to_string()))?;
Ok(Self { inner })
}
pub fn from_pem(pem_str: &str) -> Result<Self, Error> {
let (kind, data) = pem::decode(pem_str.as_bytes())?;
if kind != "PUBLIC KEY" {
return Err(Error::UnexpectedPemTag(kind));
}
Self::from_der(&data)
}
pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_SIZE] {
self.inner.to_bytes()
}
pub fn to_der(&self) -> Vec<u8> {
self.inner.to_public_key_der().unwrap().as_bytes().to_vec()
}
pub fn to_pem(&self) -> String {
pem::encode("PUBLIC KEY", &self.to_der())
}
pub fn fingerprint(&self) -> Fingerprint {
let mut hasher = sha2::Sha256::new();
hasher.update(self.to_bytes());
Fingerprint(hasher.finalize().into())
}
pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), Error> {
let sig = ed25519_dalek::Signature::from_bytes(&signature.to_bytes());
self.inner
.verify(message, &sig)
.map_err(|_| Error::InvalidSignature)
}
}
impl Serialize for PublicKey {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&BASE64.encode(self.to_bytes()))
}
}
impl<'de> Deserialize<'de> for PublicKey {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let s = String::deserialize(deserializer)?;
let bytes = BASE64.decode(&s).map_err(de::Error::custom)?;
let arr: [u8; PUBLIC_KEY_SIZE] = bytes
.try_into()
.map_err(|_| de::Error::custom("invalid public key length"))?;
PublicKey::from_bytes(&arr).map_err(de::Error::custom)
}
}
#[cfg(feature = "cbor")]
impl crate::cbor::Encode for PublicKey {
fn encode_cbor_to(&self, buf: &mut Vec<u8>) -> Result<(), crate::cbor::Error> {
self.to_bytes().encode_cbor_to(buf)
}
}
#[cfg(feature = "cbor")]
impl crate::cbor::Decode for PublicKey {
fn decode_cbor(data: &[u8]) -> Result<Self, crate::cbor::Error> {
let bytes = <[u8; PUBLIC_KEY_SIZE]>::decode_cbor(data)?;
Self::from_bytes(&bytes).map_err(|e| crate::cbor::Error::DecodeFailed(e.to_string()))
}
fn decode_cbor_notrail(
decoder: &mut crate::cbor::Decoder<'_>,
) -> Result<Self, crate::cbor::Error> {
let bytes = decoder.decode_bytes_fixed::<PUBLIC_KEY_SIZE>()?;
Self::from_bytes(&bytes).map_err(|e| crate::cbor::Error::DecodeFailed(e.to_string()))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Signature([u8; SIGNATURE_SIZE]);
impl Signature {
pub fn from_bytes(bytes: &[u8; SIGNATURE_SIZE]) -> Self {
Self(*bytes)
}
pub fn to_bytes(&self) -> [u8; SIGNATURE_SIZE] {
self.0
}
}
impl Serialize for Signature {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&BASE64.encode(self.to_bytes()))
}
}
impl<'de> Deserialize<'de> for Signature {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let s = String::deserialize(deserializer)?;
let bytes = BASE64.decode(&s).map_err(de::Error::custom)?;
let arr: [u8; SIGNATURE_SIZE] = bytes
.try_into()
.map_err(|_| de::Error::custom("invalid signature length"))?;
Ok(Signature::from_bytes(&arr))
}
}
#[cfg(feature = "cbor")]
impl crate::cbor::Encode for Signature {
fn encode_cbor_to(&self, buf: &mut Vec<u8>) -> Result<(), crate::cbor::Error> {
self.to_bytes().encode_cbor_to(buf)
}
}
#[cfg(feature = "cbor")]
impl crate::cbor::Decode for Signature {
fn decode_cbor(data: &[u8]) -> Result<Self, crate::cbor::Error> {
let bytes = <[u8; SIGNATURE_SIZE]>::decode_cbor(data)?;
Ok(Self::from_bytes(&bytes))
}
fn decode_cbor_notrail(
decoder: &mut crate::cbor::Decoder<'_>,
) -> Result<Self, crate::cbor::Error> {
let bytes = decoder.decode_bytes_fixed::<SIGNATURE_SIZE>()?;
Ok(Self::from_bytes(&bytes))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Fingerprint([u8; FINGERPRINT_SIZE]);
impl Fingerprint {
pub fn from_bytes(bytes: &[u8; FINGERPRINT_SIZE]) -> Self {
Self(*bytes)
}
pub fn to_bytes(&self) -> [u8; FINGERPRINT_SIZE] {
self.0
}
}
impl Serialize for Fingerprint {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&BASE64.encode(self.to_bytes()))
}
}
impl<'de> Deserialize<'de> for Fingerprint {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let s = String::deserialize(deserializer)?;
let bytes = BASE64.decode(&s).map_err(de::Error::custom)?;
let arr: [u8; FINGERPRINT_SIZE] = bytes
.try_into()
.map_err(|_| de::Error::custom("invalid fingerprint length"))?;
Ok(Fingerprint::from_bytes(&arr))
}
}
#[cfg(feature = "cbor")]
impl crate::cbor::Encode for Fingerprint {
fn encode_cbor_to(&self, buf: &mut Vec<u8>) -> Result<(), crate::cbor::Error> {
self.to_bytes().encode_cbor_to(buf)
}
}
#[cfg(feature = "cbor")]
impl crate::cbor::Decode for Fingerprint {
fn decode_cbor(data: &[u8]) -> Result<Self, crate::cbor::Error> {
let bytes = <[u8; FINGERPRINT_SIZE]>::decode_cbor(data)?;
Ok(Self::from_bytes(&bytes))
}
fn decode_cbor_notrail(
decoder: &mut crate::cbor::Decoder<'_>,
) -> Result<Self, crate::cbor::Error> {
let bytes = decoder.decode_bytes_fixed::<FINGERPRINT_SIZE>()?;
Ok(Self::from_bytes(&bytes))
}
}
#[cfg(test)]
mod tests {
use super::*;
mod ietf_vectors {
pub const SECKEY_SEED: &str =
"d4ee72dbf913584ad5b6d8f1f769f8ad3afe7c28cbf1d4fbe097a88f44755842";
pub const SECKEY_PEM: &str = "\
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC
-----END PRIVATE KEY-----";
pub const SECKEY_V2_PEM: &str = "\
-----BEGIN PRIVATE KEY-----
MHICAQEwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC
oB8wHQYKKoZIhvcNAQkJFDEPDA1DdXJkbGUgQ2hhaXJzgSEAGb9ECWmEzf6FQbrB
Z9w7lshQhqowtrbLDFw4rXAxZuE=
-----END PRIVATE KEY-----";
pub const PUBKEY_PEM: &str = "\
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE=
-----END PUBLIC KEY-----";
}
#[test]
fn test_ietf_vectors() {
let key = SecretKey::from_pem(ietf_vectors::SECKEY_PEM).unwrap();
assert_eq!(hex::encode(*key.to_bytes()), ietf_vectors::SECKEY_SEED);
assert_eq!(key.to_pem().trim(), ietf_vectors::SECKEY_PEM.trim());
let (_, der) = pem::decode(ietf_vectors::SECKEY_PEM.as_bytes()).unwrap();
assert_eq!(*key.to_der(), der);
assert_eq!(
key.public_key().to_pem().trim(),
ietf_vectors::PUBKEY_PEM.trim()
);
let key = PublicKey::from_pem(ietf_vectors::PUBKEY_PEM).unwrap();
assert_eq!(key.to_pem().trim(), ietf_vectors::PUBKEY_PEM.trim());
let (_, der) = pem::decode(ietf_vectors::PUBKEY_PEM.as_bytes()).unwrap();
assert_eq!(key.to_der(), der);
}
#[test]
fn test_ietf_v2_rejected() {
assert!(SecretKey::from_pem(ietf_vectors::SECKEY_V2_PEM).is_err());
}
#[test]
fn test_sign_verify() {
let secret = SecretKey::generate();
let public = secret.public_key();
struct TestCase<'a> {
message: &'a [u8],
}
let tests = [TestCase {
message: b"message to authenticate",
}];
for tt in &tests {
let signature = secret.sign(tt.message);
public
.verify(tt.message, &signature)
.unwrap_or_else(|e| panic!("failed to verify message: {}", e));
}
}
}