1use std::fmt;
4
5use ed25519_dalek::{
6 PUBLIC_KEY_LENGTH, SecretKey, Signer, SigningKey, Verifier as _,
7 VerifyingKey as Ed25519VerifyingKey, ed25519::Signature as Ed25519Signature,
8 pkcs8::ALGORITHM_OID,
9};
10use pkcs8::{
11 DecodePrivateKey, EncodePrivateKey, ObjectIdentifier, PrivateKeyInfoRef,
12 der::zeroize::Zeroizing,
13};
14use rand::TryCryptoRng;
15use ruma_common::{SigningKeyAlgorithm, SigningKeyId};
16use thiserror::Error;
17
18use crate::{KeyPair, Signature, verify::Verifier};
19
20#[cfg(feature = "ring-compat")]
21mod compat;
22
23pub struct Ed25519KeyPair {
25 signing_key: SigningKey,
26 version: String,
28}
29
30impl Ed25519KeyPair {
31 pub fn new(
33 oid: ObjectIdentifier,
34 privkey: &[u8],
35 pubkey: Option<&[u8]>,
36 version: String,
37 ) -> Result<Self, Ed25519KeyPairParseError> {
38 if oid != ALGORITHM_OID {
39 return Err(Ed25519KeyPairParseError::InvalidOid {
40 expected: ALGORITHM_OID,
41 found: oid,
42 });
43 }
44
45 let secret_key = Self::correct_privkey_from_octolet(privkey)?;
46 let signing_key = SigningKey::from_bytes(secret_key);
47
48 if let Some(oak_key) = pubkey {
49 let verifying_key = signing_key.verifying_key();
51
52 if oak_key != verifying_key.as_bytes() {
53 return Err(Ed25519KeyPairParseError::PublicKeyMismatch {
54 derived: verifying_key.as_bytes().to_vec(),
55 parsed: oak_key.to_owned(),
56 });
57 }
58 }
59
60 Ok(Self { signing_key, version })
61 }
62
63 pub fn from_der(document: &[u8], version: String) -> Result<Self, Ed25519KeyPairParseError> {
82 #[cfg(feature = "ring-compat")]
83 use self::compat::CompatibleDocument;
84
85 let signing_key;
86
87 #[cfg(feature = "ring-compat")]
88 {
89 signing_key = match CompatibleDocument::from_bytes(document) {
90 CompatibleDocument::WellFormed(bytes) => SigningKey::from_pkcs8_der(bytes)?,
91 CompatibleDocument::CleanedFromRing(vec) => SigningKey::from_pkcs8_der(&vec)?,
92 }
93 }
94 #[cfg(not(feature = "ring-compat"))]
95 {
96 signing_key = SigningKey::from_pkcs8_der(document)?;
97 }
98
99 Ok(Self { signing_key, version })
100 }
101
102 pub fn from_pkcs8(
104 oak: PrivateKeyInfoRef<'_>,
105 version: String,
106 ) -> Result<Self, Ed25519KeyPairParseError> {
107 Self::new(
108 oak.algorithm.oid,
109 oak.private_key.as_ref(),
110 oak.public_key.and_then(|key| key.as_bytes()),
111 version,
112 )
113 }
114
115 fn correct_privkey_from_octolet(key: &[u8]) -> Result<&SecretKey, Ed25519KeyPairParseError> {
120 if key.len() == 34 && key[..2] == [0x04, 0x20] {
121 Ok(key[2..].try_into().unwrap())
122 } else {
123 key.try_into().map_err(|_| Ed25519KeyPairParseError::InvalidSecretKeyLength {
124 expected: ed25519_dalek::SECRET_KEY_LENGTH,
125 found: key.len(),
126 })
127 }
128 }
129
130 pub fn generate() -> Zeroizing<Vec<u8>> {
140 let signing_key = generate_signing_key(&mut rand::rngs::SysRng).unwrap();
141 signing_key.to_pkcs8_der().unwrap().to_bytes()
142 }
143
144 pub fn version(&self) -> &str {
146 &self.version
147 }
148
149 pub fn public_key(&self) -> [u8; PUBLIC_KEY_LENGTH] {
151 self.signing_key.verifying_key().to_bytes()
152 }
153}
154
155fn generate_signing_key<R: TryCryptoRng + ?Sized>(csprng: &mut R) -> Result<SigningKey, R::Error> {
158 let mut secret = SecretKey::default();
159 csprng.try_fill_bytes(&mut secret)?;
160 Ok(SigningKey::from_bytes(&secret))
161}
162
163impl KeyPair for Ed25519KeyPair {
164 fn sign(&self, message: &[u8]) -> Signature {
165 Signature {
166 key_id: SigningKeyId::from_parts(
167 SigningKeyAlgorithm::Ed25519,
168 self.version.as_str().into(),
169 ),
170 signature: self.signing_key.sign(message).to_bytes().to_vec(),
171 }
172 }
173}
174
175impl fmt::Debug for Ed25519KeyPair {
176 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
177 formatter
178 .debug_struct("Ed25519KeyPair")
179 .field("verifying_key", &self.signing_key.verifying_key().as_bytes())
180 .field("version", &self.version)
181 .finish()
182 }
183}
184
185#[derive(Debug, Error)]
187#[non_exhaustive]
188pub enum Ed25519KeyPairParseError {
189 #[error("algorithm OID does not match ed25519 algorithm: expected {expected}, found {found}")]
193 InvalidOid {
194 expected: ObjectIdentifier,
196
197 found: ObjectIdentifier,
199 },
200
201 #[error("invalid ed25519 secret key length: expected {expected}, found {found}")]
203 InvalidSecretKeyLength {
204 expected: usize,
206
207 found: usize,
209 },
210
211 #[error("PKCS#8 Document public key does not match public key derived from private key: derived {0:X?} (len {}), parsed {1:X?} (len {})", .derived.len(), .parsed.len())]
214 PublicKeyMismatch {
215 derived: Vec<u8>,
217
218 parsed: Vec<u8>,
220 },
221
222 #[error("invalid PKCS#8 document: {0}")]
224 Pkcs8(#[from] pkcs8::Error),
225}
226
227#[derive(Debug, Default)]
229pub(crate) struct Ed25519Verifier;
230
231impl Verifier for Ed25519Verifier {
232 type Error = Ed25519VerificationError;
233
234 fn verify_json(
235 &self,
236 public_key: &[u8],
237 signature: &[u8],
238 message: &[u8],
239 ) -> Result<(), Self::Error> {
240 Ed25519VerifyingKey::try_from(public_key)
241 .map_err(Ed25519VerificationError::InvalidPublicKey)?
242 .verify(
243 message,
244 &Ed25519Signature::from_bytes(&signature.try_into().map_err(|_| {
245 Ed25519VerificationError::InvalidSignatureLength {
246 expected: Ed25519Signature::BYTE_SIZE,
247 found: signature.len(),
248 }
249 })?),
250 )
251 .map_err(Ed25519VerificationError::SignatureVerification)
252 }
253}
254
255#[derive(Debug, Error)]
257#[non_exhaustive]
258pub enum Ed25519VerificationError {
259 #[error("Invalid ed25519 public key: {0}")]
261 InvalidPublicKey(#[source] ed25519_dalek::SignatureError),
262
263 #[error("Invalid ed25519 signature length: expected {expected}, found {found}")]
265 InvalidSignatureLength {
266 expected: usize,
268
269 found: usize,
271 },
272
273 #[error("ed25519 signature verification failed: {0}")]
275 SignatureVerification(#[source] ed25519_dalek::SignatureError),
276}
277
278#[cfg(test)]
279mod tests {
280 use super::Ed25519KeyPair;
281
282 const WELL_FORMED_DOC: &[u8] = &[
283 0x30, 0x72, 0x02, 0x01, 0x01, 0x30, 0x05, 0x06, 0x03, 0x2B, 0x65, 0x70, 0x04, 0x22, 0x04,
284 0x20, 0xD4, 0xEE, 0x72, 0xDB, 0xF9, 0x13, 0x58, 0x4A, 0xD5, 0xB6, 0xD8, 0xF1, 0xF7, 0x69,
285 0xF8, 0xAD, 0x3A, 0xFE, 0x7C, 0x28, 0xCB, 0xF1, 0xD4, 0xFB, 0xE0, 0x97, 0xA8, 0x8F, 0x44,
286 0x75, 0x58, 0x42, 0xA0, 0x1F, 0x30, 0x1D, 0x06, 0x0A, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D,
287 0x01, 0x09, 0x09, 0x14, 0x31, 0x0F, 0x0C, 0x0D, 0x43, 0x75, 0x72, 0x64, 0x6C, 0x65, 0x20,
288 0x43, 0x68, 0x61, 0x69, 0x72, 0x73, 0x81, 0x21, 0x00, 0x19, 0xBF, 0x44, 0x09, 0x69, 0x84,
289 0xCD, 0xFE, 0x85, 0x41, 0xBA, 0xC1, 0x67, 0xDC, 0x3B, 0x96, 0xC8, 0x50, 0x86, 0xAA, 0x30,
290 0xB6, 0xB6, 0xCB, 0x0C, 0x5C, 0x38, 0xAD, 0x70, 0x31, 0x66, 0xE1,
291 ];
292
293 const WELL_FORMED_PUBKEY: &[u8] = &[
294 0x19, 0xBF, 0x44, 0x09, 0x69, 0x84, 0xCD, 0xFE, 0x85, 0x41, 0xBA, 0xC1, 0x67, 0xDC, 0x3B,
295 0x96, 0xC8, 0x50, 0x86, 0xAA, 0x30, 0xB6, 0xB6, 0xCB, 0x0C, 0x5C, 0x38, 0xAD, 0x70, 0x31,
296 0x66, 0xE1,
297 ];
298
299 #[test]
300 fn generate_key() {
301 Ed25519KeyPair::generate();
303 }
304
305 #[test]
306 fn well_formed_key() {
307 let keypair = Ed25519KeyPair::from_der(WELL_FORMED_DOC, "".to_owned()).unwrap();
308
309 assert_eq!(keypair.public_key(), WELL_FORMED_PUBKEY);
310 }
311
312 #[cfg(feature = "ring-compat")]
313 mod ring_compat {
314 use super::Ed25519KeyPair;
315
316 const RING_DOC: &[u8] = &[
317 0x30, 0x53, 0x02, 0x01, 0x01, 0x30, 0x05, 0x06, 0x03, 0x2B, 0x65, 0x70, 0x04, 0x22,
318 0x04, 0x20, 0x61, 0x9E, 0xD8, 0x25, 0xA6, 0x1D, 0x32, 0x29, 0xD7, 0xD8, 0x22, 0x03,
319 0xC6, 0x0E, 0x37, 0x48, 0xE9, 0xC9, 0x11, 0x96, 0x3B, 0x03, 0x15, 0x94, 0x19, 0x3A,
320 0x86, 0xEC, 0xE6, 0x2D, 0x73, 0xC0, 0xA1, 0x23, 0x03, 0x21, 0x00, 0x3D, 0xA6, 0xC8,
321 0xD1, 0x76, 0x2F, 0xD6, 0x49, 0xB8, 0x4F, 0xF6, 0xC6, 0x1D, 0x04, 0xEA, 0x4A, 0x70,
322 0xA8, 0xC9, 0xF0, 0x8F, 0x96, 0x7F, 0x6B, 0xD7, 0xDA, 0xE5, 0x2E, 0x88, 0x8D, 0xBA,
323 0x3E,
324 ];
325
326 const RING_PUBKEY: &[u8] = &[
327 0x3D, 0xA6, 0xC8, 0xD1, 0x76, 0x2F, 0xD6, 0x49, 0xB8, 0x4F, 0xF6, 0xC6, 0x1D, 0x04,
328 0xEA, 0x4A, 0x70, 0xA8, 0xC9, 0xF0, 0x8F, 0x96, 0x7F, 0x6B, 0xD7, 0xDA, 0xE5, 0x2E,
329 0x88, 0x8D, 0xBA, 0x3E,
330 ];
331
332 #[test]
333 fn ring_key() {
334 let keypair = Ed25519KeyPair::from_der(RING_DOC, "".to_owned()).unwrap();
335
336 assert_eq!(keypair.public_key(), RING_PUBKEY);
337 }
338 }
339}