1use crate::{
38 cbb_to_buffer, scoped, with_output_array, Buffer, FfiMutSlice, FfiSlice, InvalidSignatureError,
39};
40
41pub const PUBLIC_KEY_LEN: usize = bssl_sys::ED25519_PUBLIC_KEY_LEN as usize;
43
44pub const SEED_LEN: usize =
47 (bssl_sys::ED25519_PRIVATE_KEY_LEN - bssl_sys::ED25519_PUBLIC_KEY_LEN) as usize;
48
49pub const SIGNATURE_LEN: usize = bssl_sys::ED25519_SIGNATURE_LEN as usize;
51
52const KEYPAIR_LEN: usize = bssl_sys::ED25519_PRIVATE_KEY_LEN as usize;
55
56const MAX_CONTEXT_LEN: usize = 255;
58
59#[derive(Clone)]
61pub struct PrivateKey([u8; KEYPAIR_LEN]);
62
63pub struct PublicKey([u8; PUBLIC_KEY_LEN]);
65
66pub type Signature = [u8; SIGNATURE_LEN];
68
69impl PrivateKey {
70 pub fn generate() -> Self {
72 let mut public_key = [0u8; PUBLIC_KEY_LEN];
73 let mut private_key = [0u8; KEYPAIR_LEN];
74
75 unsafe {
78 bssl_sys::ED25519_keypair(public_key.as_mut_ffi_ptr(), private_key.as_mut_ffi_ptr())
79 }
80
81 PrivateKey(private_key)
82 }
83
84 pub fn to_seed(&self) -> [u8; SEED_LEN] {
86 #[allow(clippy::expect_used)]
89 self.0[..SEED_LEN]
90 .try_into()
91 .expect("A slice of length SEED_LEN will always fit into an array of length SEED_LEN")
92 }
93
94 pub fn from_seed(seed: &[u8; SEED_LEN]) -> Self {
97 let mut public_key = [0u8; PUBLIC_KEY_LEN];
98 let mut private_key = [0u8; KEYPAIR_LEN];
99
100 unsafe {
103 bssl_sys::ED25519_keypair_from_seed(
104 public_key.as_mut_ffi_ptr(),
105 private_key.as_mut_ffi_ptr(),
106 seed.as_ffi_ptr(),
107 )
108 }
109 PrivateKey(private_key)
110 }
111
112 pub fn sign(&self, msg: &[u8]) -> Signature {
114 let mut sig_bytes = [0u8; SIGNATURE_LEN];
115
116 let result = unsafe {
120 bssl_sys::ED25519_sign(
121 sig_bytes.as_mut_ffi_ptr(),
122 msg.as_ffi_ptr(),
123 msg.len(),
124 self.0.as_ffi_ptr(),
125 )
126 };
127 assert_eq!(result, 1, "allocation failure in bssl_sys::ED25519_sign");
128
129 sig_bytes
130 }
131
132 pub fn sign_prehashed(&self, context: &[u8], sha512_digest: &[u8; 64]) -> Option<Signature> {
140 if context.len() > MAX_CONTEXT_LEN {
141 return None;
142 }
143
144 let mut sig_bytes = [0u8; SIGNATURE_LEN];
145
146 let result = unsafe {
151 bssl_sys::ED25519_sign_prehashed(
152 sig_bytes.as_mut_ffi_ptr(),
153 context.as_ffi_ptr(),
154 context.len(),
155 sha512_digest.as_ffi_ptr(),
156 self.0.as_ffi_ptr(),
157 )
158 };
159 assert_eq!(
160 result, 1,
161 "allocation failure in bssl_sys::ED25519_sign_prehashed"
162 );
163
164 Some(sig_bytes)
165 }
166
167 pub fn to_public(&self) -> PublicKey {
169 let keypair_bytes = &self.0;
170
171 #[allow(clippy::expect_used)]
174 PublicKey(
175 keypair_bytes[PUBLIC_KEY_LEN..]
176 .try_into()
177 .expect("The slice is always the correct size for a public key"),
178 )
179 }
180
181 pub(crate) unsafe fn from_evp_pkey(mut pkey: scoped::EvpPkey) -> Self {
183 let mut seed = [0; SEED_LEN];
184 let len = &mut { SEED_LEN };
185 let ret = unsafe {
187 bssl_sys::EVP_PKEY_get_raw_private_key(
188 pkey.as_ffi_ptr(),
189 seed.as_mut_ptr(),
190 len as *mut _,
191 )
192 };
193 assert_eq!(ret, 1);
195 assert_eq!(*len, SEED_LEN);
196 Self::from_seed(&seed)
197 }
198}
199
200impl PublicKey {
201 pub fn from_bytes(bytes: &[u8; PUBLIC_KEY_LEN]) -> Self {
203 PublicKey(*bytes)
204 }
205
206 pub fn as_bytes(&self) -> &[u8; PUBLIC_KEY_LEN] {
208 &self.0
209 }
210
211 pub fn from_der_subject_public_key_info(spki: &[u8]) -> Option<Self> {
213 let alg = unsafe { bssl_sys::EVP_pkey_ed25519() };
215 let mut pkey =
216 scoped::EvpPkey::from_der_subject_public_key_info(spki, core::slice::from_ref(&alg))?;
217 let raw_pkey: [u8; PUBLIC_KEY_LEN] = unsafe {
218 with_output_array(|out, mut out_len| {
219 assert_eq!(
223 1,
224 bssl_sys::EVP_PKEY_get_raw_public_key(pkey.as_ffi_ptr(), out, &mut out_len)
225 );
226 assert_eq!(out_len, PUBLIC_KEY_LEN);
227 })
228 };
229 Some(PublicKey(raw_pkey))
230 }
231
232 pub fn to_der_subject_public_key_info(&self) -> Buffer {
234 let mut pkey = scoped::EvpPkey::from_ptr(unsafe {
236 bssl_sys::EVP_PKEY_from_raw_public_key(
237 bssl_sys::EVP_pkey_ed25519(),
238 self.0.as_ffi_ptr(),
239 PUBLIC_KEY_LEN,
240 )
241 });
242 assert!(!pkey.as_ffi_ptr().is_null());
244
245 cbb_to_buffer(PUBLIC_KEY_LEN + 32, |cbb| unsafe {
246 assert_eq!(1, bssl_sys::EVP_marshal_public_key(cbb, pkey.as_ffi_ptr()));
249 })
250 }
251
252 pub fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), InvalidSignatureError> {
254 let ret = unsafe {
255 bssl_sys::ED25519_verify(
257 msg.as_ffi_ptr(),
258 msg.len(),
259 signature.as_ffi_ptr(),
260 self.0.as_ffi_ptr(),
261 )
262 };
263 if ret == 1 {
264 Ok(())
265 } else {
266 Err(InvalidSignatureError)
267 }
268 }
269
270 pub fn verify_prehashed(
280 &self,
281 context: &[u8],
282 sha512_digest: &[u8; 64],
283 signature: &Signature,
284 ) -> Result<(), InvalidSignatureError> {
285 let ret = unsafe {
286 bssl_sys::ED25519_verify_prehashed(
288 context.as_ffi_ptr(),
289 context.len(),
290 sha512_digest.as_ffi_ptr(),
291 signature.as_ffi_ptr(),
292 self.0.as_ffi_ptr(),
293 )
294 };
295 if ret == 1 {
296 Ok(())
297 } else {
298 Err(InvalidSignatureError)
299 }
300 }
301}
302
303#[cfg(test)]
304mod test {
305 use super::*;
306 use crate::digest::Sha512;
307 use crate::test_helpers;
308
309 #[test]
310 fn gen_roundtrip() {
311 let private_key = PrivateKey::generate();
312 assert_ne!([0u8; 64], private_key.0);
313 let seed = private_key.to_seed();
314 let new_private_key = PrivateKey::from_seed(&seed);
315 assert_eq!(private_key.0, new_private_key.0);
316 }
317
318 #[test]
319 fn der_subject_public_key_info() {
320 let priv_key = PrivateKey::generate();
321 let msg = [0u8; 0];
322 let sig = priv_key.sign(&msg);
323
324 let pub_key = priv_key.to_public();
325 assert!(pub_key.verify(&msg, &sig).is_ok());
326
327 let pub_key_der = pub_key.to_der_subject_public_key_info();
328 let pub_key_from_der =
329 PublicKey::from_der_subject_public_key_info(pub_key_der.as_ref()).unwrap();
330 assert_eq!(pub_key.as_bytes(), pub_key_from_der.as_bytes());
331 assert!(pub_key_from_der.verify(&msg, &sig).is_ok());
332
333 assert!(PublicKey::from_der_subject_public_key_info(
334 &pub_key_from_der.as_bytes()[0..PUBLIC_KEY_LEN / 2]
335 )
336 .is_none());
337
338 assert!(PublicKey::from_der_subject_public_key_info(b"").is_none());
339 }
340
341 #[test]
342 fn der_subject_public_key_info_wrong_type() {
343 let spki = test_helpers::decode_hex_into_vec("302a300506032b656e032100e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c");
345 assert!(PublicKey::from_der_subject_public_key_info(&spki).is_none());
347 }
348
349 #[test]
350 fn empty_msg() {
351 let pk = test_helpers::decode_hex(
353 "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
354 );
355 let seed = test_helpers::decode_hex(
356 "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
357 );
358 let msg = [0u8; 0];
359 let sig_expected = test_helpers::decode_hex("e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b");
360 let kp = PrivateKey::from_seed(&seed);
361 let sig = kp.sign(&msg);
362 assert_eq!(sig_expected, sig);
363
364 let pub_key = PublicKey::from_bytes(&pk);
365 assert_eq!(pub_key.as_bytes(), kp.to_public().as_bytes());
366 assert!(pub_key.verify(&msg, &sig).is_ok());
367 }
368
369 #[test]
370 fn ed25519_sign_and_verify() {
371 let pk = test_helpers::decode_hex(
373 "cf3af898467a5b7a52d33d53bc037e2642a8da996903fc252217e9c033e2f291",
374 );
375 let sk = test_helpers::decode_hex(
376 "9acad959d216212d789a119252ebfe0c96512a23c73bd9f3b202292d6916a738",
377 );
378 let msg: [u8; 14] = test_helpers::decode_hex("55c7fa434f5ed8cdec2b7aeac173");
379 let sig_expected = test_helpers::decode_hex("6ee3fe81e23c60eb2312b2006b3b25e6838e02106623f844c44edb8dafd66ab0671087fd195df5b8f58a1d6e52af42908053d55c7321010092748795ef94cf06");
380 let kp = PrivateKey::from_seed(&sk);
381
382 let sig = kp.sign(&msg);
383 assert_eq!(sig_expected, sig);
384
385 let pub_key = PublicKey::from_bytes(&pk);
386 assert_eq!(pub_key.as_bytes(), kp.to_public().as_bytes());
387 assert!(pub_key.verify(&msg, &sig).is_ok());
388 }
389
390 #[test]
391 fn ed25519ph_sign_and_verify() {
392 let pk = test_helpers::decode_hex(
394 "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf",
395 );
396 let sk = test_helpers::decode_hex(
397 "833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42",
398 );
399 let msg: [u8; 3] = test_helpers::decode_hex("616263");
400 let mut ctx = Sha512::new();
401 ctx.update(&msg);
402 let sha512_digest = ctx.digest();
403 let sig_expected = test_helpers::decode_hex("98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083406");
404 let kp = PrivateKey::from_seed(&sk);
405
406 let sig = kp.sign_prehashed(&[], &sha512_digest).unwrap();
407 assert_eq!(sig_expected, sig);
408
409 let pub_key = PublicKey::from_bytes(&pk);
410 assert_eq!(pub_key.as_bytes(), kp.to_public().as_bytes());
411 assert!(pub_key.verify_prehashed(&[], &sha512_digest, &sig).is_ok());
412 }
413
414 #[test]
415 fn ed25519ph_max_context_length() {
416 let kp = PrivateKey::generate();
417 let pub_key = kp.to_public();
418 let mut ctx = Sha512::new();
419 ctx.update(b"test message");
420 let sha512_digest = ctx.digest();
421
422 let context = [0u8; 255]; let sig = kp.sign_prehashed(&context, &sha512_digest).unwrap();
424 assert!(pub_key
425 .verify_prehashed(&context, &sha512_digest, &sig)
426 .is_ok());
427
428 let too_long_context = [0u8; 256];
429 assert!(kp
430 .sign_prehashed(&too_long_context, &sha512_digest)
431 .is_none());
432 assert!(pub_key
433 .verify_prehashed(&too_long_context, &sha512_digest, &sig)
434 .is_err());
435 }
436}