use std::fmt::Display;
use base64::decoded_len_estimate;
use base64ct::Encoding;
use curve25519_dalek::EdwardsPoint;
#[cfg(not(fuzzing))]
use ed25519_dalek::Verifier;
use ed25519_dalek::{
Signature, Signer, SigningKey, VerifyingKey, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH,
};
use rand::thread_rng;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes};
use sha2::Sha512;
use thiserror::Error;
use zeroize::Zeroize;
use crate::utilities::{base64_decode, base64_encode};
#[derive(Debug, Error)]
pub enum SignatureError {
#[error("The signature couldn't be decoded: {0}")]
Base64(#[from] base64::DecodeError),
#[error("The signature was invalid: {0}")]
Signature(#[from] ed25519_dalek::SignatureError),
}
#[derive(Deserialize, Serialize)]
#[serde(try_from = "Ed25519KeypairPickle")]
#[serde(into = "Ed25519KeypairPickle")]
pub struct Ed25519Keypair {
secret_key: SecretKeys,
public_key: Ed25519PublicKey,
}
struct ExpandedSecretKey {
source: Box<[u8; 64]>,
inner: Box<ed25519_dalek::hazmat::ExpandedSecretKey>,
}
impl ExpandedSecretKey {
fn from_bytes(bytes: &[u8; 64]) -> Self {
let mut source = Box::new([0u8; 64]);
source.copy_from_slice(bytes);
Self { source, inner: ed25519_dalek::hazmat::ExpandedSecretKey::from_bytes(bytes).into() }
}
const fn as_bytes(&self) -> &[u8; 64] {
&self.source
}
fn sign(&self, message: &[u8]) -> Signature {
ed25519_dalek::hazmat::raw_sign::<Sha512>(&self.inner, message, &self.public_key().0)
}
fn public_key(&self) -> Ed25519PublicKey {
let point = EdwardsPoint::mul_base(&self.inner.scalar);
Ed25519PublicKey(VerifyingKey::from(point))
}
}
impl Clone for ExpandedSecretKey {
fn clone(&self) -> Self {
let source = self.source.clone();
Self {
source,
inner: ed25519_dalek::hazmat::ExpandedSecretKey::from_bytes(&self.source).into(),
}
}
}
impl Serialize for ExpandedSecretKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let bytes = self.as_bytes();
SerdeBytes::new(bytes).serialize(serializer)
}
}
impl<'d> Deserialize<'d> for ExpandedSecretKey {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'d>,
{
let mut bytes = <SerdeByteBuf>::deserialize(deserializer)?;
let length = bytes.len();
if bytes.len() != 64 {
bytes.zeroize();
Err(serde::de::Error::custom(format!(
"Invalid secret key length: expected 64 bytes, got {length}"
)))
} else {
let mut slice = [0u8; 64];
slice.copy_from_slice(&bytes);
let ret = ExpandedSecretKey::from_bytes(&slice);
slice.zeroize();
bytes.zeroize();
Ok(ret)
}
}
}
impl Ed25519Keypair {
pub fn new() -> Self {
let mut rng = thread_rng();
let signing_key = SigningKey::generate(&mut rng);
Self {
public_key: Ed25519PublicKey(signing_key.verifying_key()),
secret_key: signing_key.into(),
}
}
#[cfg(feature = "libolm-compat")]
pub(crate) fn from_expanded_key(secret_key: &[u8; 64]) -> Result<Self, crate::KeyError> {
let secret_key = ExpandedSecretKey::from_bytes(secret_key);
let public_key = secret_key.public_key();
Ok(Self { secret_key: secret_key.into(), public_key })
}
#[cfg(feature = "libolm-compat")]
pub(crate) fn expanded_secret_key(&self) -> Box<[u8; 64]> {
use sha2::Digest;
let mut expanded = Box::new([0u8; 64]);
match &self.secret_key {
SecretKeys::Normal(k) => {
let mut k = k.to_bytes();
Sha512::new().chain_update(k).finalize_into(expanded.as_mut_slice().into());
k.zeroize();
}
SecretKeys::Expanded(k) => expanded.copy_from_slice(k.as_bytes()),
}
expanded
}
pub const fn public_key(&self) -> Ed25519PublicKey {
self.public_key
}
pub fn sign(&self, message: &[u8]) -> Ed25519Signature {
self.secret_key.sign(message)
}
}
impl Default for Ed25519Keypair {
fn default() -> Self {
Self::new()
}
}
#[derive(Deserialize, Serialize)]
#[serde(transparent)]
pub struct Ed25519SecretKey(Box<SigningKey>);
impl Ed25519SecretKey {
pub const LENGTH: usize = ed25519_dalek::SECRET_KEY_LENGTH;
const BASE64_LENGTH: usize = 43;
const PADDED_BASE64_LENGTH: usize = 44;
pub fn new() -> Self {
let mut rng = thread_rng();
let signing_key = SigningKey::generate(&mut rng);
let key = Box::new(signing_key);
Self(key)
}
pub fn to_bytes(&self) -> Box<[u8; 32]> {
Box::new(self.0.to_bytes())
}
pub fn from_slice(bytes: &[u8; 32]) -> Self {
Self(Box::new(SigningKey::from_bytes(bytes)))
}
pub fn to_base64(&self) -> String {
let mut bytes = self.to_bytes();
let ret = base64ct::Base64Unpadded::encode_string(bytes.as_ref());
bytes.zeroize();
ret
}
pub fn from_base64(input: &str) -> Result<Self, crate::KeyError> {
if input.len() != Self::BASE64_LENGTH && input.len() != Self::PADDED_BASE64_LENGTH {
Err(crate::KeyError::InvalidKeyLength {
key_type: "Ed25519",
expected_length: ed25519_dalek::SECRET_KEY_LENGTH,
length: decoded_len_estimate(input.len()),
})
} else {
let mut bytes = if let Ok(bytes) = base64ct::Base64Unpadded::decode_vec(input) {
bytes
} else {
base64ct::Base64::decode_vec(input)?
};
let mut key_bytes = [0u8; 32];
key_bytes.copy_from_slice(&bytes);
let key = Self::from_slice(&key_bytes);
bytes.zeroize();
key_bytes.zeroize();
Ok(key)
}
}
pub fn public_key(&self) -> Ed25519PublicKey {
Ed25519PublicKey(self.0.verifying_key())
}
pub fn sign(&self, message: &[u8]) -> Ed25519Signature {
Ed25519Signature(self.0.sign(message))
}
}
impl Default for Ed25519SecretKey {
fn default() -> Self {
Self::new()
}
}
#[derive(Serialize, Deserialize)]
enum SecretKeys {
Normal(Box<SigningKey>),
Expanded(Box<ExpandedSecretKey>),
}
impl SecretKeys {
fn public_key(&self) -> Ed25519PublicKey {
match &self {
SecretKeys::Normal(k) => Ed25519PublicKey(k.verifying_key()),
SecretKeys::Expanded(k) => k.public_key(),
}
}
fn sign(&self, message: &[u8]) -> Ed25519Signature {
let signature = match &self {
SecretKeys::Normal(k) => k.sign(message),
SecretKeys::Expanded(k) => k.sign(message),
};
Ed25519Signature(signature)
}
}
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
#[serde(transparent)]
pub struct Ed25519PublicKey(VerifyingKey);
impl Ed25519PublicKey {
pub const LENGTH: usize = PUBLIC_KEY_LENGTH;
const BASE64_LENGTH: usize = 43;
const PADDED_BASE64_LENGTH: usize = 44;
pub fn from_slice(bytes: &[u8; 32]) -> Result<Self, crate::KeyError> {
Ok(Self(VerifyingKey::from_bytes(bytes).map_err(SignatureError::from)?))
}
pub fn as_bytes(&self) -> &[u8; Self::LENGTH] {
self.0.as_bytes()
}
pub fn from_base64(input: &str) -> Result<Self, crate::KeyError> {
if input.len() != Self::BASE64_LENGTH && input.len() != Self::PADDED_BASE64_LENGTH {
Err(crate::KeyError::InvalidKeyLength {
key_type: "Ed25519",
expected_length: Self::LENGTH,
length: decoded_len_estimate(input.len()),
})
} else {
let mut bytes = base64_decode(input)?;
let mut key_bytes = [0u8; 32];
key_bytes.copy_from_slice(&bytes);
let key = Self::from_slice(&key_bytes);
bytes.zeroize();
key_bytes.zeroize();
key
}
}
pub fn to_base64(&self) -> String {
base64_encode(self.as_bytes())
}
#[cfg(not(fuzzing))]
pub fn verify(
&self,
message: &[u8],
signature: &Ed25519Signature,
) -> Result<(), SignatureError> {
if cfg!(feature = "strict-signatures") {
Ok(self.0.verify_strict(message, &signature.0)?)
} else {
Ok(self.0.verify(message, &signature.0)?)
}
}
#[cfg(fuzzing)]
pub fn verify(
&self,
_message: &[u8],
_signature: &Ed25519Signature,
) -> Result<(), SignatureError> {
Ok(())
}
}
impl Display for Ed25519PublicKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_base64())
}
}
impl std::fmt::Debug for Ed25519PublicKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = format!("ed25519:{self}");
<str as std::fmt::Debug>::fmt(&s, f)
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Ed25519Signature(pub(crate) Signature);
impl Ed25519Signature {
pub const LENGTH: usize = SIGNATURE_LENGTH;
pub fn from_slice(bytes: &[u8]) -> Result<Self, SignatureError> {
Ok(Self(Signature::try_from(bytes)?))
}
pub fn from_base64(signature: &str) -> Result<Self, SignatureError> {
Ok(Self(Signature::try_from(base64_decode(signature)?.as_slice())?))
}
pub fn to_base64(&self) -> String {
base64_encode(self.0.to_bytes())
}
pub fn to_bytes(&self) -> [u8; Self::LENGTH] {
self.0.to_bytes()
}
}
impl Display for Ed25519Signature {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_base64())
}
}
impl std::fmt::Debug for Ed25519Signature {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = format!("ed25519:{self}");
<str as std::fmt::Debug>::fmt(&s, f)
}
}
impl Clone for Ed25519Keypair {
fn clone(&self) -> Self {
let secret_key: SecretKeys = match &self.secret_key {
SecretKeys::Normal(k) => SecretKeys::Normal(k.clone()),
SecretKeys::Expanded(k) => SecretKeys::Expanded(k.clone()),
};
Self { secret_key, public_key: self.public_key }
}
}
impl From<Ed25519Keypair> for Ed25519KeypairPickle {
fn from(key: Ed25519Keypair) -> Self {
Self(key.secret_key)
}
}
impl From<SigningKey> for SecretKeys {
fn from(key: SigningKey) -> Self {
Self::Normal(Box::new(key))
}
}
impl From<ExpandedSecretKey> for SecretKeys {
fn from(key: ExpandedSecretKey) -> Self {
Self::Expanded(Box::new(key))
}
}
#[derive(Serialize, Deserialize)]
#[serde(transparent)]
pub struct Ed25519KeypairPickle(SecretKeys);
impl From<Ed25519KeypairPickle> for Ed25519Keypair {
fn from(pickle: Ed25519KeypairPickle) -> Self {
let secret_key = pickle.0;
let public_key = secret_key.public_key();
Self { secret_key, public_key }
}
}
#[cfg(test)]
mod tests {
use super::ExpandedSecretKey;
use crate::{Ed25519Keypair, Ed25519PublicKey, Ed25519SecretKey, KeyError};
#[test]
fn byte_decoding_roundtrip_succeeds_for_secret_key() {
let bytes = *b"oooooooooooooooooooooooooooooooo";
let key = Ed25519SecretKey::from_slice(&bytes);
assert_eq!(*(key.to_bytes()), bytes);
}
#[test]
fn base64_decoding_incorrect_num_of_bytes_fails_for_secret_key() {
assert!(matches!(
Ed25519SecretKey::from_base64("foo"),
Err(KeyError::InvalidKeyLength { .. })
));
}
#[test]
fn unpadded_base64_decoding_roundtrip_succeeds_for_secret_key() {
let base64 = "MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTE";
let key = Ed25519SecretKey::from_base64(base64).expect("Should decode key from base64");
assert_eq!(key.to_base64(), base64);
}
#[test]
fn padded_base64_decoding_roundtrip_succeeds_for_secret_key() {
let base64 = "MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTE=";
let key = Ed25519SecretKey::from_base64(base64).expect("Should decode key from base64");
assert_eq!(key.to_base64(), base64.trim_end_matches('='));
}
#[test]
fn byte_decoding_roundtrip_succeeds_for_public_key() {
let bytes = *b"oooooooooooooooooooooooooooooooo";
let key = Ed25519PublicKey::from_slice(&bytes).expect("Should decode key from bytes");
assert_eq!(key.as_bytes(), &bytes);
}
#[test]
fn base64_decoding_incorrect_num_of_bytes_fails_for_public_key() {
assert!(matches!(
Ed25519PublicKey::from_base64("foo"),
Err(KeyError::InvalidKeyLength { .. })
));
}
#[test]
fn unpadded_base64_decoding_roundtrip_succeeds_for_public_key() {
let base64 = "b29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb28";
let key = Ed25519PublicKey::from_base64(base64).expect("Should decode key from base64");
assert_eq!(key.to_base64(), base64);
}
#[test]
fn padded_base64_decoding_roundtrip_succeeds_for_public_key() {
let base64 = "b29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb28=";
let key = Ed25519PublicKey::from_base64(base64).expect("Should decode key from base64");
assert_eq!(key.to_base64(), base64.trim_end_matches('='));
}
#[test]
fn verifying_valid_signature_succeeds() {
let key_pair = Ed25519Keypair::new();
let signature = key_pair.secret_key.sign(b"foo");
key_pair.public_key().verify(b"foo", &signature).expect("Should verify valid signature");
}
#[test]
fn verifying_invalid_signature_fails() {
let key_pair = Ed25519Keypair::new();
let signature = key_pair.secret_key.sign(b"foo");
key_pair
.public_key()
.verify(b"bar", &signature)
.expect_err("Should reject invalid signature");
}
#[test]
#[cfg(feature = "libolm-compat")]
fn can_only_expand_secret_key_once() {
let key_pair = Ed25519Keypair::new();
assert!(matches!(key_pair.secret_key, crate::types::ed25519::SecretKeys::Normal(_)));
let expanded_key = key_pair.expanded_secret_key();
let expanded_key_pair = Ed25519Keypair::from_expanded_key(&expanded_key).unwrap();
assert!(matches!(
expanded_key_pair.secret_key,
crate::types::ed25519::SecretKeys::Expanded(_)
));
assert_eq!(expanded_key_pair.public_key(), key_pair.public_key());
let reexpanded_key = expanded_key_pair.expanded_secret_key();
assert_eq!(reexpanded_key, expanded_key);
}
#[test]
fn serialization_roundtrip_succeeds() {
let bytes = b"9999999999999999999999999999999999999999999999999999999999999999";
let key = ExpandedSecretKey::from_bytes(bytes);
let serialized = serde_json::to_value(key).expect("Should serialize key");
let deserialized = serde_json::from_value::<ExpandedSecretKey>(serialized)
.expect("Should deserialize key");
assert_eq!(deserialized.as_bytes(), bytes);
}
#[test]
fn deserializing_from_invalid_length_fails() {
let serialized = serde_json::to_value(b"foo").expect("Should serialize key");
let deserialized = serde_json::from_value::<ExpandedSecretKey>(serialized);
assert!(deserialized.is_err());
}
}