nucypher_core/
key_frag.rs

1use alloc::boxed::Box;
2use alloc::string::String;
3use alloc::vec::Vec;
4use core::fmt;
5
6use serde::{Deserialize, Serialize};
7use umbral_pre::{
8    decrypt_original, encrypt, serde_bytes, Capsule, DecryptionError as UmbralDecryptionError,
9    EncryptionError, KeyFrag, PublicKey, SecretKey, Signature, Signer, VerifiedKeyFrag,
10};
11
12use crate::hrac::HRAC;
13use crate::versioning::{
14    messagepack_deserialize, messagepack_serialize, DeserializationError, ProtocolObject,
15    ProtocolObjectInner,
16};
17
18#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
19struct AuthorizedKeyFrag {
20    signature: Signature,
21    kfrag: KeyFrag,
22}
23
24fn signed_message(hrac: &HRAC, kfrag: &KeyFrag) -> Vec<u8> {
25    [hrac.as_ref(), messagepack_serialize(kfrag).as_ref()].concat()
26}
27
28impl AuthorizedKeyFrag {
29    fn new(signer: &Signer, hrac: &HRAC, verified_kfrag: VerifiedKeyFrag) -> Self {
30        // Alice makes plain to Ursula that, upon decrypting this message,
31        // this particular KFrag is authorized for use in the policy identified by this HRAC.
32
33        // TODO (rust-umbral#73): add VerifiedKeyFrag::unverify()?
34        let kfrag = verified_kfrag.unverify();
35
36        let signature = signer.sign(&signed_message(hrac, &kfrag));
37
38        Self { signature, kfrag }
39    }
40
41    fn verify(self, hrac: &HRAC, publisher_verifying_key: &PublicKey) -> Option<VerifiedKeyFrag> {
42        if !self
43            .signature
44            .verify(publisher_verifying_key, &signed_message(hrac, &self.kfrag))
45        {
46            return None;
47        }
48
49        // Ursula has no side channel to get the KeyFrag author's key,
50        // so verifying the keyfrag is useless.
51        Some(self.kfrag.skip_verification())
52    }
53}
54
55impl<'a> ProtocolObjectInner<'a> for AuthorizedKeyFrag {
56    fn brand() -> [u8; 4] {
57        *b"AKFr"
58    }
59
60    fn version() -> (u16, u16) {
61        (3, 0)
62    }
63
64    fn unversioned_to_bytes(&self) -> Box<[u8]> {
65        messagepack_serialize(&self)
66    }
67
68    fn unversioned_from_bytes(minor_version: u16, bytes: &[u8]) -> Option<Result<Self, String>> {
69        if minor_version == 0 {
70            Some(messagepack_deserialize(bytes))
71        } else {
72            None
73        }
74    }
75}
76
77impl<'a> ProtocolObject<'a> for AuthorizedKeyFrag {}
78
79#[allow(clippy::enum_variant_names)]
80#[derive(Debug)]
81pub enum DecryptionError {
82    DecryptionFailed(UmbralDecryptionError),
83    DeserializationFailed(DeserializationError),
84    VerificationFailed,
85}
86
87impl fmt::Display for DecryptionError {
88    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89        match self {
90            Self::DecryptionFailed(err) => write!(f, "decryption failed: {err}"),
91            Self::DeserializationFailed(err) => write!(f, "deserialization failed: {err}"),
92            Self::VerificationFailed => write!(f, "verification failed"),
93        }
94    }
95}
96
97/// Encrypted and signed key frag.
98#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
99pub struct EncryptedKeyFrag {
100    capsule: Capsule,
101    #[serde(with = "serde_bytes::as_base64")]
102    ciphertext: Box<[u8]>,
103}
104
105impl EncryptedKeyFrag {
106    /// Encrypts and signs a key frag.
107    pub fn new(
108        signer: &Signer,
109        recipient_key: &PublicKey,
110        hrac: &HRAC,
111        verified_kfrag: VerifiedKeyFrag,
112    ) -> Self {
113        let auth_kfrag = AuthorizedKeyFrag::new(signer, hrac, verified_kfrag);
114        // Using Umbral for asymmetric encryption here for simplicity,
115        // even though we do not plan to re-encrypt the capsule.
116        let (capsule, ciphertext) = match encrypt(recipient_key, &auth_kfrag.to_bytes()) {
117            Ok(result) => result,
118            Err(err) => match err {
119                // For now this is the only error that can happen during encryption,
120                // and there's really no point in propagating it.
121                EncryptionError::PlaintextTooLarge => panic!("encryption failed - out of memory?"),
122            },
123        };
124        Self {
125            capsule,
126            ciphertext,
127        }
128    }
129
130    /// Decrypts and verifies a key frag.
131    pub fn decrypt(
132        &self,
133        sk: &SecretKey,
134        hrac: &HRAC,
135        publisher_verifying_key: &PublicKey,
136    ) -> Result<VerifiedKeyFrag, DecryptionError> {
137        let auth_kfrag_bytes = decrypt_original(sk, &self.capsule, &self.ciphertext)
138            .map_err(DecryptionError::DecryptionFailed)?;
139        let auth_kfrag = AuthorizedKeyFrag::from_bytes(&auth_kfrag_bytes)
140            .map_err(DecryptionError::DeserializationFailed)?;
141        auth_kfrag
142            .verify(hrac, publisher_verifying_key)
143            .ok_or(DecryptionError::VerificationFailed)
144    }
145}
146
147impl<'a> ProtocolObjectInner<'a> for EncryptedKeyFrag {
148    fn brand() -> [u8; 4] {
149        *b"EKFr"
150    }
151
152    fn version() -> (u16, u16) {
153        (3, 0)
154    }
155
156    fn unversioned_to_bytes(&self) -> Box<[u8]> {
157        messagepack_serialize(&self)
158    }
159
160    fn unversioned_from_bytes(minor_version: u16, bytes: &[u8]) -> Option<Result<Self, String>> {
161        if minor_version == 0 {
162            Some(messagepack_deserialize(bytes))
163        } else {
164            None
165        }
166    }
167}
168
169impl<'a> ProtocolObject<'a> for EncryptedKeyFrag {}