1use std::{
4 collections::BTreeMap,
5 fmt::{Debug, Formatter, Result as FmtResult},
6};
7
8use ed25519_dalek::{pkcs8::ALGORITHM_OID, SecretKey, Signer, SigningKey, PUBLIC_KEY_LENGTH};
9use pkcs8::{
10 der::zeroize::Zeroizing, DecodePrivateKey, EncodePrivateKey, ObjectIdentifier, PrivateKeyInfo,
11};
12use ruma_common::{serde::Base64, SigningKeyAlgorithm, SigningKeyId};
13
14use crate::{signatures::Signature, Error, ParseError};
15
16#[cfg(feature = "ring-compat")]
17mod compat;
18
19pub trait KeyPair: Sized {
21 fn sign(&self, message: &[u8]) -> Signature;
27}
28
29pub struct Ed25519KeyPair {
31 signing_key: SigningKey,
32 version: String,
34}
35
36impl Ed25519KeyPair {
37 pub fn new(
39 oid: ObjectIdentifier,
40 privkey: &[u8],
41 pubkey: Option<&[u8]>,
42 version: String,
43 ) -> Result<Self, Error> {
44 if oid != ALGORITHM_OID {
45 return Err(ParseError::Oid { expected: ALGORITHM_OID, found: oid }.into());
46 }
47
48 let secret_key = Self::correct_privkey_from_octolet(privkey)?;
49 let signing_key = SigningKey::from_bytes(secret_key);
50
51 if let Some(oak_key) = pubkey {
52 let verifying_key = signing_key.verifying_key();
54
55 if oak_key != verifying_key.as_bytes() {
56 return Err(ParseError::derived_vs_parsed_mismatch(
57 oak_key,
58 verifying_key.as_bytes().to_vec(),
59 ));
60 }
61 }
62
63 Ok(Self { signing_key, version })
64 }
65
66 pub fn from_der(document: &[u8], version: String) -> Result<Self, Error> {
85 #[cfg(feature = "ring-compat")]
86 use self::compat::CompatibleDocument;
87
88 let signing_key;
89
90 #[cfg(feature = "ring-compat")]
91 {
92 signing_key = match CompatibleDocument::from_bytes(document) {
93 CompatibleDocument::WellFormed(bytes) => {
94 SigningKey::from_pkcs8_der(bytes).map_err(Error::DerParse)?
95 }
96 CompatibleDocument::CleanedFromRing(vec) => {
97 SigningKey::from_pkcs8_der(&vec).map_err(Error::DerParse)?
98 }
99 }
100 }
101 #[cfg(not(feature = "ring-compat"))]
102 {
103 signing_key = SigningKey::from_pkcs8_der(document).map_err(Error::DerParse)?;
104 }
105
106 Ok(Self { signing_key, version })
107 }
108
109 pub fn from_pkcs8_oak(oak: PrivateKeyInfo<'_>, version: String) -> Result<Self, Error> {
111 Self::new(oak.algorithm.oid, oak.private_key, oak.public_key, version)
112 }
113
114 pub fn from_pkcs8_pki(oak: PrivateKeyInfo<'_>, version: String) -> Result<Self, Error> {
116 Self::new(oak.algorithm.oid, oak.private_key, None, version)
117 }
118
119 fn correct_privkey_from_octolet(key: &[u8]) -> Result<&SecretKey, ParseError> {
124 if key.len() == 34 && key[..2] == [0x04, 0x20] {
125 Ok(key[2..].try_into().unwrap())
126 } else {
127 key.try_into().map_err(|_| ParseError::SecretKey)
128 }
129 }
130
131 pub fn generate() -> Result<Zeroizing<Vec<u8>>, Error> {
141 let signing_key = SigningKey::generate(&mut rand::rngs::OsRng);
142 Ok(signing_key.to_pkcs8_der().map_err(Error::DerParse)?.to_bytes())
143 }
144
145 pub fn version(&self) -> &str {
147 &self.version
148 }
149
150 pub fn public_key(&self) -> [u8; PUBLIC_KEY_LENGTH] {
152 self.signing_key.verifying_key().to_bytes()
153 }
154}
155
156impl KeyPair for Ed25519KeyPair {
157 fn sign(&self, message: &[u8]) -> Signature {
158 Signature {
159 key_id: SigningKeyId::from_parts(
160 SigningKeyAlgorithm::Ed25519,
161 self.version.as_str().into(),
162 ),
163 signature: self.signing_key.sign(message).to_bytes().to_vec(),
164 }
165 }
166}
167
168impl Debug for Ed25519KeyPair {
169 fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
170 formatter
171 .debug_struct("Ed25519KeyPair")
172 .field("verifying_key", &self.signing_key.verifying_key().as_bytes())
173 .field("version", &self.version)
174 .finish()
175 }
176}
177
178pub type PublicKeyMap = BTreeMap<String, PublicKeySet>;
182
183pub type PublicKeySet = BTreeMap<String, Base64>;
187
188#[cfg(test)]
189mod tests {
190 use super::Ed25519KeyPair;
191
192 const WELL_FORMED_DOC: &[u8] = &[
193 0x30, 0x72, 0x02, 0x01, 0x01, 0x30, 0x05, 0x06, 0x03, 0x2B, 0x65, 0x70, 0x04, 0x22, 0x04,
194 0x20, 0xD4, 0xEE, 0x72, 0xDB, 0xF9, 0x13, 0x58, 0x4A, 0xD5, 0xB6, 0xD8, 0xF1, 0xF7, 0x69,
195 0xF8, 0xAD, 0x3A, 0xFE, 0x7C, 0x28, 0xCB, 0xF1, 0xD4, 0xFB, 0xE0, 0x97, 0xA8, 0x8F, 0x44,
196 0x75, 0x58, 0x42, 0xA0, 0x1F, 0x30, 0x1D, 0x06, 0x0A, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D,
197 0x01, 0x09, 0x09, 0x14, 0x31, 0x0F, 0x0C, 0x0D, 0x43, 0x75, 0x72, 0x64, 0x6C, 0x65, 0x20,
198 0x43, 0x68, 0x61, 0x69, 0x72, 0x73, 0x81, 0x21, 0x00, 0x19, 0xBF, 0x44, 0x09, 0x69, 0x84,
199 0xCD, 0xFE, 0x85, 0x41, 0xBA, 0xC1, 0x67, 0xDC, 0x3B, 0x96, 0xC8, 0x50, 0x86, 0xAA, 0x30,
200 0xB6, 0xB6, 0xCB, 0x0C, 0x5C, 0x38, 0xAD, 0x70, 0x31, 0x66, 0xE1,
201 ];
202
203 const WELL_FORMED_PUBKEY: &[u8] = &[
204 0x19, 0xBF, 0x44, 0x09, 0x69, 0x84, 0xCD, 0xFE, 0x85, 0x41, 0xBA, 0xC1, 0x67, 0xDC, 0x3B,
205 0x96, 0xC8, 0x50, 0x86, 0xAA, 0x30, 0xB6, 0xB6, 0xCB, 0x0C, 0x5C, 0x38, 0xAD, 0x70, 0x31,
206 0x66, 0xE1,
207 ];
208
209 #[test]
210 fn generate_key() {
211 Ed25519KeyPair::generate().unwrap();
212 }
213
214 #[test]
215 fn well_formed_key() {
216 let keypair = Ed25519KeyPair::from_der(WELL_FORMED_DOC, "".to_owned()).unwrap();
217
218 assert_eq!(keypair.public_key(), WELL_FORMED_PUBKEY);
219 }
220
221 #[cfg(feature = "ring-compat")]
222 mod ring_compat {
223 use super::Ed25519KeyPair;
224
225 const RING_DOC: &[u8] = &[
226 0x30, 0x53, 0x02, 0x01, 0x01, 0x30, 0x05, 0x06, 0x03, 0x2B, 0x65, 0x70, 0x04, 0x22,
227 0x04, 0x20, 0x61, 0x9E, 0xD8, 0x25, 0xA6, 0x1D, 0x32, 0x29, 0xD7, 0xD8, 0x22, 0x03,
228 0xC6, 0x0E, 0x37, 0x48, 0xE9, 0xC9, 0x11, 0x96, 0x3B, 0x03, 0x15, 0x94, 0x19, 0x3A,
229 0x86, 0xEC, 0xE6, 0x2D, 0x73, 0xC0, 0xA1, 0x23, 0x03, 0x21, 0x00, 0x3D, 0xA6, 0xC8,
230 0xD1, 0x76, 0x2F, 0xD6, 0x49, 0xB8, 0x4F, 0xF6, 0xC6, 0x1D, 0x04, 0xEA, 0x4A, 0x70,
231 0xA8, 0xC9, 0xF0, 0x8F, 0x96, 0x7F, 0x6B, 0xD7, 0xDA, 0xE5, 0x2E, 0x88, 0x8D, 0xBA,
232 0x3E,
233 ];
234
235 const RING_PUBKEY: &[u8] = &[
236 0x3D, 0xA6, 0xC8, 0xD1, 0x76, 0x2F, 0xD6, 0x49, 0xB8, 0x4F, 0xF6, 0xC6, 0x1D, 0x04,
237 0xEA, 0x4A, 0x70, 0xA8, 0xC9, 0xF0, 0x8F, 0x96, 0x7F, 0x6B, 0xD7, 0xDA, 0xE5, 0x2E,
238 0x88, 0x8D, 0xBA, 0x3E,
239 ];
240
241 #[test]
242 fn ring_key() {
243 let keypair = Ed25519KeyPair::from_der(RING_DOC, "".to_owned()).unwrap();
244
245 assert_eq!(keypair.public_key(), RING_PUBKEY);
246 }
247 }
248}