cmail_rpgp/crypto/
eddsa.rs1use rand::{CryptoRng, Rng};
16use signature::{Signer as _, Verifier};
17use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
18
19use crate::crypto::ecc_curve::ECCCurve;
20use crate::crypto::hash::HashAlgorithm;
21use crate::crypto::Signer;
22use crate::errors::Result;
23use crate::types::{Mpi, PlainSecretParams, PublicParams};
24
25pub enum Mode {
28 EdDSALegacy,
32
33 Ed25519,
37}
38
39#[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop, derive_more::Debug)]
41pub struct SecretKey {
42 #[debug("..")]
44 pub secret: [u8; 32],
45 #[debug("{}", hex::encode(oid))]
46 pub oid: Vec<u8>,
47}
48
49impl Signer for SecretKey {
50 fn sign(
51 &self,
52 _hash: HashAlgorithm,
53 digest: &[u8],
54 pub_params: &PublicParams,
55 ) -> Result<Vec<Vec<u8>>> {
56 let curve = match pub_params {
57 PublicParams::EdDSALegacy { curve, q } => {
58 ensure_eq!(q.len(), 33, "invalid Q (len)");
59 ensure_eq!(q[0], 0x40, "invalid Q (prefix)");
60
61 curve
62 }
63 PublicParams::Ed25519 { .. } => &ECCCurve::Ed25519,
64 _ => bail!("invalid public params"),
65 };
66
67 if curve != &ECCCurve::Ed25519 {
68 unsupported_err!("curve {:?} for EdDSA", curve.to_string());
69 }
70
71 let key = ed25519_dalek::SigningKey::from_bytes(&self.secret);
72
73 let signature = key.sign(digest);
74 let bytes = signature.to_bytes();
75
76 let r = bytes[..32].to_vec();
77 let s = bytes[32..].to_vec();
78
79 Ok(vec![r, s])
80 }
81}
82
83pub fn generate_key<R: Rng + CryptoRng>(
87 mut rng: R,
88 mode: Mode,
89) -> (PublicParams, PlainSecretParams) {
90 let mut bytes = Zeroizing::new([0u8; ed25519_dalek::SECRET_KEY_LENGTH]);
91 rng.fill_bytes(&mut *bytes);
92
93 let secret = ed25519_dalek::SigningKey::from_bytes(&bytes);
94 drop(bytes); let public = ed25519_dalek::VerifyingKey::from(&secret);
97
98 match mode {
99 Mode::EdDSALegacy => {
100 let mut q = Vec::with_capacity(33);
102 q.push(0x40);
103 q.extend_from_slice(&public.to_bytes());
104
105 let p = Mpi::from_slice(&secret.to_bytes());
107
108 (
109 PublicParams::EdDSALegacy {
110 curve: ECCCurve::Ed25519,
111 q: Mpi::from_raw(q),
112 },
113 PlainSecretParams::EdDSALegacy(p),
114 )
115 }
116 Mode::Ed25519 => (
117 PublicParams::Ed25519 {
118 public: public.to_bytes(),
119 },
120 PlainSecretParams::Ed25519(secret.to_bytes()),
121 ),
122 }
123}
124
125pub fn verify(
127 curve: &ECCCurve,
128 public: &[u8],
129 hash: HashAlgorithm,
130 hashed: &[u8],
131 sig_bytes: &[u8],
132) -> Result<()> {
133 match *curve {
134 ECCCurve::Ed25519 => {
135 let Some(digest_size) = hash.digest_size() else {
136 bail!("EdDSA signature: invalid hash algorithm: {:?}", hash);
137 };
138 ensure!(
139 digest_size * 8 >= 256,
140 "EdDSA signature: hash algorithm {:?} is too weak for Ed25519",
141 hash,
142 );
143
144 let pk: ed25519_dalek::VerifyingKey = public.try_into()?;
145 let sig = sig_bytes.try_into()?;
146
147 Ok(pk.verify(hashed, &sig)?)
148 }
149 _ => unsupported_err!("curve {:?} for EdDSA", curve.to_string()),
150 }
151}