use core::fmt::Debug;
#[cfg(feature = "pkcs8")]
use ed25519::pkcs8;
#[cfg(any(test, feature = "rand_core"))]
use rand_core::CryptoRngCore;
#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use sha2::Sha512;
use subtle::{Choice, ConstantTimeEq};
use curve25519_dalek::{
digest::{generic_array::typenum::U64, Digest},
edwards::{CompressedEdwardsY, EdwardsPoint},
scalar::Scalar,
};
use ed25519::signature::{KeypairRef, Signer, Verifier};
#[cfg(feature = "digest")]
use crate::context::Context;
#[cfg(feature = "digest")]
use signature::DigestSigner;
#[cfg(feature = "zeroize")]
use zeroize::{Zeroize, ZeroizeOnDrop};
#[cfg(feature = "hazmat")]
use crate::verifying::StreamVerifier;
use crate::{
constants::{KEYPAIR_LENGTH, SECRET_KEY_LENGTH},
errors::{InternalError, SignatureError},
hazmat::ExpandedSecretKey,
signature::InternalSignature,
verifying::VerifyingKey,
Signature,
};
pub type SecretKey = [u8; SECRET_KEY_LENGTH];
#[derive(Clone)]
pub struct SigningKey {
pub(crate) secret_key: SecretKey,
pub(crate) verifying_key: VerifyingKey,
}
impl SigningKey {
#[inline]
pub fn from_bytes(secret_key: &SecretKey) -> Self {
let verifying_key = VerifyingKey::from(&ExpandedSecretKey::from(secret_key));
Self {
secret_key: *secret_key,
verifying_key,
}
}
#[inline]
pub fn to_bytes(&self) -> SecretKey {
self.secret_key
}
#[inline]
pub fn as_bytes(&self) -> &SecretKey {
&self.secret_key
}
#[inline]
pub fn from_keypair_bytes(bytes: &[u8; 64]) -> Result<SigningKey, SignatureError> {
let (secret_key, verifying_key) = bytes.split_at(SECRET_KEY_LENGTH);
let signing_key = SigningKey::try_from(secret_key)?;
let verifying_key = VerifyingKey::try_from(verifying_key)?;
if signing_key.verifying_key() != verifying_key {
return Err(InternalError::MismatchedKeypair.into());
}
Ok(signing_key)
}
pub fn to_keypair_bytes(&self) -> [u8; KEYPAIR_LENGTH] {
let mut bytes: [u8; KEYPAIR_LENGTH] = [0u8; KEYPAIR_LENGTH];
bytes[..SECRET_KEY_LENGTH].copy_from_slice(&self.secret_key);
bytes[SECRET_KEY_LENGTH..].copy_from_slice(self.verifying_key.as_bytes());
bytes
}
pub fn verifying_key(&self) -> VerifyingKey {
self.verifying_key
}
#[cfg(feature = "digest")]
pub fn with_context<'k, 'v>(
&'k self,
context_value: &'v [u8],
) -> Result<Context<'k, 'v, Self>, SignatureError> {
Context::new(self, context_value)
}
#[cfg_attr(feature = "rand_core", doc = "```")]
#[cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
#[cfg(any(test, feature = "rand_core"))]
pub fn generate<R: CryptoRngCore + ?Sized>(csprng: &mut R) -> SigningKey {
let mut secret = SecretKey::default();
csprng.fill_bytes(&mut secret);
Self::from_bytes(&secret)
}
#[cfg_attr(all(feature = "rand_core", feature = "digest"), doc = "```")]
#[cfg_attr(
any(not(feature = "rand_core"), not(feature = "digest")),
doc = "```ignore"
)]
#[cfg_attr(all(feature = "rand_core", feature = "digest"), doc = "```")]
#[cfg_attr(
any(not(feature = "rand_core"), not(feature = "digest")),
doc = "```ignore"
)]
#[cfg(feature = "digest")]
pub fn sign_prehashed<MsgDigest>(
&self,
prehashed_message: MsgDigest,
context: Option<&[u8]>,
) -> Result<Signature, SignatureError>
where
MsgDigest: Digest<OutputSize = U64>,
{
ExpandedSecretKey::from(&self.secret_key).raw_sign_prehashed::<Sha512, MsgDigest>(
prehashed_message,
&self.verifying_key,
context,
)
}
pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> {
self.verifying_key.verify(message, signature)
}
#[cfg_attr(all(feature = "rand_core", feature = "digest"), doc = "```")]
#[cfg_attr(
any(not(feature = "rand_core"), not(feature = "digest")),
doc = "```ignore"
)]
#[cfg(feature = "digest")]
pub fn verify_prehashed<MsgDigest>(
&self,
prehashed_message: MsgDigest,
context: Option<&[u8]>,
signature: &Signature,
) -> Result<(), SignatureError>
where
MsgDigest: Digest<OutputSize = U64>,
{
self.verifying_key
.verify_prehashed(prehashed_message, context, signature)
}
#[allow(non_snake_case)]
pub fn verify_strict(
&self,
message: &[u8],
signature: &Signature,
) -> Result<(), SignatureError> {
self.verifying_key.verify_strict(message, signature)
}
#[cfg(feature = "hazmat")]
pub fn verify_stream(
&self,
signature: &ed25519::Signature,
) -> Result<StreamVerifier, SignatureError> {
self.verifying_key.verify_stream(signature)
}
pub fn to_scalar_bytes(&self) -> [u8; 32] {
let mut buf = [0u8; 32];
let scalar_and_hash_prefix = Sha512::default().chain_update(self.secret_key).finalize();
buf.copy_from_slice(&scalar_and_hash_prefix[..32]);
buf
}
pub fn to_scalar(&self) -> Scalar {
ExpandedSecretKey::from(&self.secret_key).scalar
}
}
impl AsRef<VerifyingKey> for SigningKey {
fn as_ref(&self) -> &VerifyingKey {
&self.verifying_key
}
}
impl Debug for SigningKey {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("SigningKey")
.field("verifying_key", &self.verifying_key)
.finish_non_exhaustive() }
}
impl KeypairRef for SigningKey {
type VerifyingKey = VerifyingKey;
}
impl Signer<Signature> for SigningKey {
fn try_sign(&self, message: &[u8]) -> Result<Signature, SignatureError> {
let expanded: ExpandedSecretKey = (&self.secret_key).into();
Ok(expanded.raw_sign::<Sha512>(message, &self.verifying_key))
}
}
#[cfg(feature = "digest")]
impl<D> DigestSigner<D, Signature> for SigningKey
where
D: Digest<OutputSize = U64>,
{
fn try_sign_digest(&self, msg_digest: D) -> Result<Signature, SignatureError> {
self.sign_prehashed(msg_digest, None)
}
}
#[cfg(feature = "digest")]
impl<D> DigestSigner<D, Signature> for Context<'_, '_, SigningKey>
where
D: Digest<OutputSize = U64>,
{
fn try_sign_digest(&self, msg_digest: D) -> Result<Signature, SignatureError> {
self.key().sign_prehashed(msg_digest, Some(self.value()))
}
}
impl Verifier<Signature> for SigningKey {
fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> {
self.verifying_key.verify(message, signature)
}
}
impl From<SecretKey> for SigningKey {
#[inline]
fn from(secret: SecretKey) -> Self {
Self::from_bytes(&secret)
}
}
impl From<&SecretKey> for SigningKey {
#[inline]
fn from(secret: &SecretKey) -> Self {
Self::from_bytes(secret)
}
}
impl TryFrom<&[u8]> for SigningKey {
type Error = SignatureError;
fn try_from(bytes: &[u8]) -> Result<SigningKey, SignatureError> {
SecretKey::try_from(bytes)
.map(|bytes| Self::from_bytes(&bytes))
.map_err(|_| {
InternalError::BytesLength {
name: "SecretKey",
length: SECRET_KEY_LENGTH,
}
.into()
})
}
}
impl ConstantTimeEq for SigningKey {
fn ct_eq(&self, other: &Self) -> Choice {
self.secret_key.ct_eq(&other.secret_key)
}
}
impl PartialEq for SigningKey {
fn eq(&self, other: &Self) -> bool {
self.ct_eq(other).into()
}
}
impl Eq for SigningKey {}
#[cfg(feature = "zeroize")]
impl Drop for SigningKey {
fn drop(&mut self) {
self.secret_key.zeroize();
}
}
#[cfg(feature = "zeroize")]
impl ZeroizeOnDrop for SigningKey {}
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
impl pkcs8::EncodePrivateKey for SigningKey {
fn to_pkcs8_der(&self) -> pkcs8::Result<pkcs8::SecretDocument> {
pkcs8::KeypairBytes::from(self).to_pkcs8_der()
}
}
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
impl pkcs8::spki::DynSignatureAlgorithmIdentifier for SigningKey {
fn signature_algorithm_identifier(
&self,
) -> pkcs8::spki::Result<pkcs8::spki::AlgorithmIdentifierOwned> {
Ok(pkcs8::spki::AlgorithmIdentifier {
oid: ed25519::pkcs8::ALGORITHM_OID,
parameters: None,
})
}
}
#[cfg(feature = "pkcs8")]
impl TryFrom<pkcs8::KeypairBytes> for SigningKey {
type Error = pkcs8::Error;
fn try_from(pkcs8_key: pkcs8::KeypairBytes) -> pkcs8::Result<Self> {
SigningKey::try_from(&pkcs8_key)
}
}
#[cfg(feature = "pkcs8")]
impl TryFrom<&pkcs8::KeypairBytes> for SigningKey {
type Error = pkcs8::Error;
fn try_from(pkcs8_key: &pkcs8::KeypairBytes) -> pkcs8::Result<Self> {
let signing_key = SigningKey::from_bytes(&pkcs8_key.secret_key);
if let Some(public_bytes) = &pkcs8_key.public_key {
let expected_verifying_key = VerifyingKey::from_bytes(public_bytes.as_ref())
.map_err(|_| pkcs8::Error::KeyMalformed)?;
if signing_key.verifying_key() != expected_verifying_key {
return Err(pkcs8::Error::KeyMalformed);
}
}
Ok(signing_key)
}
}
#[cfg(feature = "pkcs8")]
impl From<SigningKey> for pkcs8::KeypairBytes {
fn from(signing_key: SigningKey) -> pkcs8::KeypairBytes {
pkcs8::KeypairBytes::from(&signing_key)
}
}
#[cfg(feature = "pkcs8")]
impl From<&SigningKey> for pkcs8::KeypairBytes {
fn from(signing_key: &SigningKey) -> pkcs8::KeypairBytes {
pkcs8::KeypairBytes {
secret_key: signing_key.to_bytes(),
public_key: Some(pkcs8::PublicKeyBytes(signing_key.verifying_key.to_bytes())),
}
}
}
#[cfg(feature = "pkcs8")]
impl TryFrom<pkcs8::PrivateKeyInfo<'_>> for SigningKey {
type Error = pkcs8::Error;
fn try_from(private_key: pkcs8::PrivateKeyInfo<'_>) -> pkcs8::Result<Self> {
pkcs8::KeypairBytes::try_from(private_key)?.try_into()
}
}
#[cfg(feature = "serde")]
impl Serialize for SigningKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_bytes(&self.secret_key)
}
}
#[cfg(feature = "serde")]
impl<'d> Deserialize<'d> for SigningKey {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'d>,
{
struct SigningKeyVisitor;
impl<'de> serde::de::Visitor<'de> for SigningKeyVisitor {
type Value = SigningKey;
fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(formatter, concat!("An ed25519 signing (private) key"))
}
fn visit_bytes<E: serde::de::Error>(self, bytes: &[u8]) -> Result<Self::Value, E> {
SigningKey::try_from(bytes).map_err(E::custom)
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'de>,
{
let mut bytes = [0u8; 32];
#[allow(clippy::needless_range_loop)]
for i in 0..32 {
bytes[i] = seq
.next_element()?
.ok_or_else(|| serde::de::Error::invalid_length(i, &"expected 32 bytes"))?;
}
let remaining = (0..)
.map(|_| seq.next_element::<u8>())
.take_while(|el| matches!(el, Ok(Some(_))))
.count();
if remaining > 0 {
return Err(serde::de::Error::invalid_length(
32 + remaining,
&"expected 32 bytes",
));
}
Ok(SigningKey::from(bytes))
}
}
deserializer.deserialize_bytes(SigningKeyVisitor)
}
}
impl From<&SecretKey> for ExpandedSecretKey {
#[allow(clippy::unwrap_used)]
fn from(secret_key: &SecretKey) -> ExpandedSecretKey {
let hash = Sha512::default().chain_update(secret_key).finalize();
ExpandedSecretKey::from_bytes(hash.as_ref())
}
}
impl ExpandedSecretKey {
#[allow(non_snake_case)]
#[allow(clippy::unwrap_used)]
#[inline(always)]
pub(crate) fn raw_sign<CtxDigest>(
&self,
message: &[u8],
verifying_key: &VerifyingKey,
) -> Signature
where
CtxDigest: Digest<OutputSize = U64>,
{
self.raw_sign_byupdate(
|h: &mut CtxDigest| {
h.update(message);
Ok(())
},
verifying_key,
)
.unwrap()
}
#[allow(non_snake_case)]
#[inline(always)]
pub(crate) fn raw_sign_byupdate<CtxDigest, F>(
&self,
msg_update: F,
verifying_key: &VerifyingKey,
) -> Result<Signature, SignatureError>
where
CtxDigest: Digest<OutputSize = U64>,
F: Fn(&mut CtxDigest) -> Result<(), SignatureError>,
{
let mut h = CtxDigest::new();
h.update(self.hash_prefix);
msg_update(&mut h)?;
let r = Scalar::from_hash(h);
let R: CompressedEdwardsY = EdwardsPoint::mul_base(&r).compress();
h = CtxDigest::new();
h.update(R.as_bytes());
h.update(verifying_key.as_bytes());
msg_update(&mut h)?;
let k = Scalar::from_hash(h);
let s: Scalar = (k * self.scalar) + r;
Ok(InternalSignature { R, s }.into())
}
#[cfg(feature = "digest")]
#[allow(non_snake_case)]
#[inline(always)]
pub(crate) fn raw_sign_prehashed<CtxDigest, MsgDigest>(
&self,
prehashed_message: MsgDigest,
verifying_key: &VerifyingKey,
context: Option<&[u8]>,
) -> Result<Signature, SignatureError>
where
CtxDigest: Digest<OutputSize = U64>,
MsgDigest: Digest<OutputSize = U64>,
{
let mut prehash: [u8; 64] = [0u8; 64];
let ctx: &[u8] = context.unwrap_or(b"");
if ctx.len() > 255 {
return Err(SignatureError::from(InternalError::PrehashedContextLength));
}
let ctx_len: u8 = ctx.len() as u8;
prehash.copy_from_slice(prehashed_message.finalize().as_slice());
let mut h = CtxDigest::new()
.chain_update(b"SigEd25519 no Ed25519 collisions")
.chain_update([1]) .chain_update([ctx_len])
.chain_update(ctx)
.chain_update(self.hash_prefix)
.chain_update(&prehash[..]);
let r = Scalar::from_hash(h);
let R: CompressedEdwardsY = EdwardsPoint::mul_base(&r).compress();
h = CtxDigest::new()
.chain_update(b"SigEd25519 no Ed25519 collisions")
.chain_update([1]) .chain_update([ctx_len])
.chain_update(ctx)
.chain_update(R.as_bytes())
.chain_update(verifying_key.as_bytes())
.chain_update(&prehash[..]);
let k = Scalar::from_hash(h);
let s: Scalar = (k * self.scalar) + r;
Ok(InternalSignature { R, s }.into())
}
}