1#![forbid(unsafe_code)]
4
5use core::{borrow::Borrow, fmt, marker::PhantomData, result::Result};
6
7use derive_where::derive_where;
8use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
9use spideroak_crypto::{
10 aead::Tag,
11 csprng::Csprng,
12 import::{Import as _, ImportError},
13 kem::{DecapKey as _, Kem},
14 keys::PublicKey as _,
15 signer::{self, Signer, SigningKey as _, VerifyingKey as _},
16};
17use zerocopy::{ByteEq, Immutable, IntoBytes, KnownLayout, Unaligned};
18
19use crate::{
20 ciphersuite::{CipherSuite, CipherSuiteExt as _},
21 error::Error,
22 groupkey::{EncryptedGroupKey, GroupKey},
23 hpke::{self, Mode},
24 misc::{SigData, kem_key, signing_key},
25 policy::{self, Cmd, CmdId, GroupId},
26};
27
28#[derive_where(Clone, Debug)]
30pub struct Signature<CS: CipherSuite>(pub(crate) <CS::Signer as Signer>::Signature);
31
32impl<CS: CipherSuite> Signature<CS> {
33 pub(crate) fn raw_sig(&self) -> SigData<CS> {
38 signer::Signature::export(&self.0)
39 }
40
41 pub fn to_bytes(&self) -> impl Borrow<[u8]> + use<CS> {
43 self.raw_sig()
44 }
45
46 pub fn from_bytes(data: &[u8]) -> Result<Self, ImportError> {
48 let sig = <CS::Signer as Signer>::Signature::import(data)?;
49 Ok(Self(sig))
50 }
51}
52
53impl<CS: CipherSuite> Serialize for Signature<CS> {
54 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
55 where
56 S: Serializer,
57 {
58 serializer.serialize_bytes(self.to_bytes().borrow())
59 }
60}
61
62impl<'de, CS: CipherSuite> Deserialize<'de> for Signature<CS> {
63 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
64 where
65 D: Deserializer<'de>,
66 {
67 struct SigVisitor<CS>(PhantomData<CS>);
68 impl<'de, G: CipherSuite> de::Visitor<'de> for SigVisitor<G> {
69 type Value = Signature<G>;
70
71 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 write!(f, "a signature")
73 }
74
75 fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E>
76 where
77 E: de::Error,
78 {
79 Signature::<G>::from_bytes(v).map_err(de::Error::custom)
80 }
81
82 fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
83 where
84 E: de::Error,
85 {
86 Signature::<G>::from_bytes(v).map_err(de::Error::custom)
87 }
88 }
89 let sig = deserializer.deserialize_bytes(SigVisitor::<CS>(PhantomData))?;
90 Ok(sig)
91 }
92}
93
94signing_key! {
95 sk = IdentityKey,
97 pk = IdentityVerifyingKey,
98 id = DeviceId,
99 context = "Device Identity Key V1",
100}
101
102impl<CS: CipherSuite> IdentityKey<CS> {
103 pub fn sign(&self, msg: &[u8], context: &[u8]) -> Result<Signature<CS>, Error> {
143 let sum = CS::tuple_hash(b"IdentityKey", [self.id()?.as_bytes(), context, msg]);
151 let sig = self.sk.sign(&sum)?;
152 Ok(Signature(sig))
153 }
154}
155
156impl<CS: CipherSuite> IdentityVerifyingKey<CS> {
157 pub fn verify(&self, msg: &[u8], context: &[u8], sig: &Signature<CS>) -> Result<(), Error> {
162 let sum = CS::tuple_hash(b"IdentityKey", [self.id()?.as_bytes(), context, msg]);
170 Ok(self.pk.verify(&sum, &sig.0)?)
171 }
172}
173
174signing_key! {
175 sk = SigningKey,
177 pk = VerifyingKey,
178 id = SigningKeyId,
179 context = "Device Signing Key V1",
180}
181
182impl<CS: CipherSuite> SigningKey<CS> {
183 pub fn sign(&self, msg: &[u8], context: &[u8]) -> Result<Signature<CS>, Error> {
223 let sum = CS::tuple_hash(b"SigningKey", [self.id()?.as_bytes(), context, msg]);
231 let sig = self.sk.sign(&sum)?;
232 Ok(Signature(sig))
233 }
234
235 pub fn sign_cmd(&self, cmd: Cmd<'_>) -> Result<(Signature<CS>, CmdId), Error> {
299 let digest = cmd.digest::<CS>(self.id()?);
300 let sig = Signature(self.sk.sign(&digest)?);
301 let id = policy::cmd_id(&digest, &sig);
302 Ok((sig, id))
303 }
304}
305
306impl<CS: CipherSuite> VerifyingKey<CS> {
307 pub fn verify(&self, msg: &[u8], context: &[u8], sig: &Signature<CS>) -> Result<(), Error> {
312 let sum = CS::tuple_hash(b"SigningKey", [self.id()?.as_bytes(), context, msg]);
320 Ok(self.pk.verify(&sum, &sig.0)?)
321 }
322
323 pub fn verify_cmd(&self, cmd: Cmd<'_>, sig: &Signature<CS>) -> Result<CmdId, Error> {
326 let digest = cmd.digest::<CS>(self.id()?);
327 self.pk.verify(&digest, &sig.0)?;
328 let id = policy::cmd_id(&digest, sig);
329 Ok(id)
330 }
331}
332
333kem_key! {
334 sk = EncryptionKey,
336 pk = EncryptionPublicKey,
337 id = EncryptionKeyId,
338 context = "Device Encryption Key V1",
339}
340
341impl<CS: CipherSuite> EncryptionKey<CS> {
342 pub fn open_group_key(
345 &self,
346 enc: &Encap<CS>,
347 ciphertext: EncryptedGroupKey<CS>,
348 group: GroupId,
349 ) -> Result<GroupKey<CS>, Error> {
350 let EncryptedGroupKey {
351 mut ciphertext,
352 tag,
353 } = ciphertext;
354
355 let info = GroupKeyInfo {
360 domain: *b"GroupKey-v1",
361 group,
362 };
363 let mut ctx = hpke::setup_recv::<CS>(Mode::Base, &enc.0, &self.sk, [info.as_bytes()])?;
364 ctx.open_in_place(&mut ciphertext, &tag, info.as_bytes())?;
365 Ok(GroupKey::from_seed(ciphertext.into()))
366 }
367}
368
369#[repr(C)]
370#[derive(Copy, Clone, Debug, ByteEq, Immutable, IntoBytes, KnownLayout, Unaligned)]
371struct GroupKeyInfo {
372 domain: [u8; 11],
374 group: GroupId,
375}
376
377impl<CS: CipherSuite> EncryptionPublicKey<CS> {
378 pub fn seal_group_key<R: Csprng>(
382 &self,
383 rng: &mut R,
384 key: &GroupKey<CS>,
385 group: GroupId,
386 ) -> Result<(Encap<CS>, EncryptedGroupKey<CS>), Error> {
387 let info = GroupKeyInfo {
392 domain: *b"GroupKey-v1",
393 group,
394 };
395 let (enc, mut ctx) =
396 hpke::setup_send::<CS, _>(rng, Mode::Base, &self.pk, [info.as_bytes()])?;
397 let mut ciphertext = (*key.raw_seed()).into();
398 let mut tag = Tag::<CS::Aead>::default();
399 ctx.seal_in_place(&mut ciphertext, &mut tag, info.as_bytes())?;
400 Ok((Encap(enc), EncryptedGroupKey { ciphertext, tag }))
401 }
402}
403
404pub struct Encap<CS: CipherSuite>(pub(crate) <CS::Kem as Kem>::Encap);
406
407impl<CS: CipherSuite> Encap<CS> {
408 #[inline]
410 pub fn as_bytes(&self) -> &[u8] {
411 self.0.borrow()
412 }
413
414 pub fn from_bytes(data: &[u8]) -> Result<Self, ImportError> {
416 let enc = <CS::Kem as Kem>::Encap::import(data)?;
417 Ok(Self(enc))
418 }
419
420 #[cfg(feature = "afc")]
421 pub(crate) fn as_inner(&self) -> &<CS::Kem as Kem>::Encap {
422 &self.0
423 }
424}
425
426impl<CS: CipherSuite> fmt::Debug for Encap<CS> {
427 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
428 f.debug_tuple("Encap").field(&self.as_bytes()).finish()
429 }
430}
431
432impl<CS> Serialize for Encap<CS>
433where
434 CS: CipherSuite,
435{
436 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
437 where
438 S: Serializer,
439 {
440 s.serialize_bytes(self.as_bytes())
441 }
442}
443
444impl<'de, CS> Deserialize<'de> for Encap<CS>
445where
446 CS: CipherSuite,
447{
448 fn deserialize<D>(d: D) -> Result<Self, D::Error>
449 where
450 D: Deserializer<'de>,
451 {
452 struct EncapVisitor<G: ?Sized>(PhantomData<G>);
453 impl<'de, G> de::Visitor<'de> for EncapVisitor<G>
454 where
455 G: CipherSuite,
456 {
457 type Value = Encap<G>;
458
459 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
460 write!(f, "a valid encapsulation")
461 }
462
463 fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
464 where
465 E: de::Error,
466 {
467 Encap::<G>::from_bytes(v).map_err(E::custom)
468 }
469
470 fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E>
471 where
472 E: de::Error,
473 {
474 Encap::<G>::from_bytes(v).map_err(E::custom)
475 }
476 }
477 d.deserialize_bytes(EncapVisitor(PhantomData))
478 }
479}
480
481#[cfg(test)]
482mod tests {
483 use core::cell::OnceCell;
484
485 use spideroak_crypto::{ed25519::Ed25519, import::Import as _, kem::Kem, rust, signer::Signer};
486
487 use super::*;
488 use crate::{default::DhKemP256HkdfSha256, test_util::TestCs};
489
490 type CS = TestCs<
491 rust::Aes256Gcm,
492 rust::Sha256,
493 rust::HkdfSha512,
494 DhKemP256HkdfSha256,
495 rust::HmacSha512,
496 Ed25519,
497 >;
498
499 #[test]
501 fn test_identity_key_id() {
502 let tests = [(
503 [
505 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
506 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
507 0x1d, 0x1e, 0x1f, 0x20,
508 ],
509 "FzsznndyXSmwS8LjWbg2g7CGp1jAD8RMArG1BCdWYkRE",
510 )];
511
512 for (i, (key_bytes, expected_id)) in tests.iter().enumerate() {
513 let sk = <<CS as CipherSuite>::Signer as Signer>::SigningKey::import(key_bytes)
514 .expect("should import signing key");
515 let identity_key: IdentityKey<CS> = IdentityKey {
516 sk,
517 id: OnceCell::new(),
518 };
519
520 let got_id = identity_key.id().expect("should compute ID");
521 let expected = DeviceId::decode(expected_id).expect("should decode expected ID");
522
523 assert_eq!(got_id, expected, "test case #{i}");
524 }
525 }
526
527 #[test]
529 fn test_signing_key_id() {
530 let tests = [(
531 [
533 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
534 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
535 0x1d, 0x1e, 0x1f, 0x20,
536 ],
537 "4NQYLfhYhMWDR7Rmu3ubH24NP3e4HUP4f6mcpBKdygWF",
538 )];
539
540 for (i, (key_bytes, expected_id)) in tests.iter().enumerate() {
541 let sk = <<CS as CipherSuite>::Signer as Signer>::SigningKey::import(key_bytes)
542 .expect("should import signing key");
543 let signing_key: SigningKey<CS> = SigningKey {
544 sk,
545 id: OnceCell::new(),
546 };
547
548 let got_id = signing_key.id().expect("should compute ID");
549 let expected = SigningKeyId::decode(expected_id).expect("should decode expected ID");
550
551 assert_eq!(got_id, expected, "test case #{i}");
552 }
553 }
554
555 #[test]
557 fn test_encryption_key_id() {
558 let tests = [(
559 [
561 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
562 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
563 0x1d, 0x1e, 0x1f, 0x20,
564 ],
565 "GDi3zb242AU8zW6QQKUypadFffRaDWA5PhX2eQ1ANphz",
566 )];
567
568 for (i, (key_bytes, expected_id)) in tests.iter().enumerate() {
569 let sk = <<CS as CipherSuite>::Kem as Kem>::DecapKey::import(key_bytes)
570 .expect("should import decap key");
571 let encryption_key: EncryptionKey<CS> = EncryptionKey {
572 sk,
573 id: OnceCell::new(),
574 };
575
576 let got_id = encryption_key.id().expect("should compute ID");
577 let expected = EncryptionKeyId::decode(expected_id).expect("should decode expected ID");
578
579 assert_eq!(got_id, expected, "test case #{i}");
580 }
581 }
582}