ic_vetkeys/utils/
mod.rs

1//! Verifiably Encrypted Threshold Key Derivation Utilities
2//!
3//! See the ePrint paper <https://eprint.iacr.org/2023/616> for protocol details
4
5#![forbid(unsafe_code)]
6#![warn(rust_2018_idioms)]
7#![forbid(missing_docs)]
8
9use hex_literal::hex;
10use ic_bls12_381::{
11    hash_to_curve::{ExpandMsgXmd, HashToCurve},
12    G1Affine, G1Projective, G2Affine, G2Prepared, Gt, Scalar,
13};
14use ic_cdk::management_canister::{VetKDCurve, VetKDDeriveKeyArgs, VetKDKeyId};
15use rand::SeedableRng;
16use rand_chacha::ChaCha20Rng;
17use std::array::TryFromSliceError;
18use std::ops::Neg;
19use zeroize::{Zeroize, ZeroizeOnDrop};
20
21const MASTER_PUBLIC_KEY_BYTES_KEY_1 : [u8; 96] = hex!("a9caf9ae8af0c7c7272f8a122133e2e0c7c0899b75e502bda9e109ca8193ded3ef042ed96db1125e1bdaad77d8cc60d917e122fe2501c45b96274f43705edf0cfd455bc66c3c060faa2fcd15486e76351edf91fecb993797273bbc8beaa47404");
22
23const MASTER_PUBLIC_KEY_BYTES_TEST_KEY_1 : [u8; 96] = hex!("ad86e8ff845912f022a0838a502d763fdea547c9948f8cb20ea7738dd52c1c38dcb4c6ca9ac29f9ac690fc5ad7681cb41922b8dffbd65d94bff141f5fb5b6624eccc03bf850f222052df888cf9b1e47203556d7522271cbb879b2ef4b8c2bfb1");
24
25const POCKETIC_MASTER_PUBLIC_KEY_BYTES_KEY_1 : [u8; 96] = hex!("8c800b5cff00463d26e8167369168827f1e48f4d8d60f71dd6a295580f65275b5f5f8e6a792c876b2c72492136530d0710a27522ee63977a76216c3cef9e70bfcb45b88736fc62142e7e0737848ce06cbb1f45a4a6a349b142ae5cf7853561e0");
26
27const POCKETIC_MASTER_PUBLIC_KEY_BYTES_TEST_KEY_1 : [u8; 96] = hex!("9069b82c7aae418cef27678291e7f2cb1a008a500eceba7199bffca12421b07c158987c6a22618af3d1958738b2835691028801f7663d311799733286c557c8979184bb62cb559a4d582fca7d2e48b860f08ed6641aef66a059ec891889a6218");
28
29const POCKETIC_MASTER_PUBLIC_KEY_BYTES_DFX_TEST_KEY : [u8; 96] = hex!("b181c14cf9d04ba45d782c0067a44b0aaa9fc2acf94f1a875f0dae801af4f80339a7e6bf8b09fcf993824c8df3080b3f1409b688ca08cbd44d2cb28db9899f4aa3b5f06b9174240448e10be2f01f9f80079ea5431ce2d11d1c8d1c775333315f");
30
31fn decode_g2_mpk(bytes: &[u8; 96]) -> G2Affine {
32    G2Affine::from_compressed(bytes).expect("Hardcoded master public key not a valid point")
33}
34
35lazy_static::lazy_static! {
36    static ref G2PREPARED_NEG_G : G2Prepared = G2Affine::generator().neg().into();
37
38    static ref PROD_G2_KEY_1: G2Affine = decode_g2_mpk(&MASTER_PUBLIC_KEY_BYTES_KEY_1);
39    static ref PROD_G2_TEST_KEY_1: G2Affine = decode_g2_mpk(&MASTER_PUBLIC_KEY_BYTES_TEST_KEY_1);
40
41    static ref POCKETIC_G2_KEY_1: G2Affine = decode_g2_mpk(&POCKETIC_MASTER_PUBLIC_KEY_BYTES_KEY_1);
42    static ref POCKETIC_G2_TEST_KEY_1: G2Affine = decode_g2_mpk(&POCKETIC_MASTER_PUBLIC_KEY_BYTES_TEST_KEY_1);
43    static ref POCKETIC_G2_DFX_TEST_KEY: G2Affine = decode_g2_mpk(&POCKETIC_MASTER_PUBLIC_KEY_BYTES_DFX_TEST_KEY);
44}
45
46const G1AFFINE_BYTES: usize = 48; // Size of compressed form
47const G2AFFINE_BYTES: usize = 96; // Size of compressed form
48
49struct G2PrecomputedTable {
50    tbl: Vec<G2Affine>,
51}
52
53impl G2PrecomputedTable {
54    /// The size of the windows
55    ///
56    /// This algorithm uses just `SUBGROUP_BITS/WINDOW_BITS` additions in
57    /// the online phase, at the cost of storing a table of size
58    /// `(SUBGROUP_BITS + WINDOW_BITS - 1)/WINDOW_BITS * (1 << WINDOW_BITS - 1)`
59    ///
60    /// This constant is configurable and can take values between 1 and 7
61    /// (inclusive)
62    ///
63    /// | WINDOW_BITS | TABLE_SIZE | online additions |
64    /// | ----------- | ---------- | ---------------- |
65    /// |           1 |       255  |              255 |
66    /// |           2 |       384  |              128 |
67    /// |           3 |       595  |               85 |
68    /// |           4 |       960  |               64 |
69    /// |           5 |      1581  |               51 |
70    /// |           6 |      2709  |               43 |
71    /// |           7 |      4699  |               37 |
72    ///
73    const WINDOW_BITS: usize = 4;
74
75    /// The bit length of the BLS12-381 subgroup
76    const SUBGROUP_BITS: usize = 255;
77
78    // A bitmask of all 1s that is WINDOW_BITS long
79    const WINDOW_MASK: u8 = (1 << Self::WINDOW_BITS) - 1;
80
81    // The total number of windows in a scalar
82    const WINDOWS: usize = Self::SUBGROUP_BITS.div_ceil(Self::WINDOW_BITS);
83
84    // We must select from 2^WINDOW_BITS elements in each table
85    // group. However one element of the table group is always the
86    // identity, and so can be omitted, which is the reason for the
87    // subtraction by 1 here.
88    const WINDOW_ELEMENTS: usize = (1 << Self::WINDOW_BITS) - 1;
89
90    // The total size of the table we will use
91    const TABLE_SIZE: usize = Self::WINDOW_ELEMENTS * Self::WINDOWS;
92
93    /// Precompute a table for fast multiplication
94    fn new(pt: &G2Affine) -> Self {
95        let mut ptbl = vec![ic_bls12_381::G2Projective::identity(); Self::TABLE_SIZE];
96
97        let mut accum = ic_bls12_381::G2Projective::from(pt);
98
99        for i in 0..Self::WINDOWS {
100            let tbl_i = &mut ptbl[Self::WINDOW_ELEMENTS * i..Self::WINDOW_ELEMENTS * (i + 1)];
101
102            tbl_i[0] = accum;
103            for j in 1..Self::WINDOW_ELEMENTS {
104                // Our table indexes are off by one due to the omitted
105                // identity element. So here we are checking if we are
106                // about to compute a point that is a doubling of a point
107                // we have previously computed. If so we can compute it
108                // using a (faster) doubling rather than using addition.
109
110                tbl_i[j] = if j % 2 == 1 {
111                    tbl_i[j / 2].double()
112                } else {
113                    tbl_i[j - 1] + tbl_i[0]
114                };
115            }
116
117            // move on to the next power
118            accum = tbl_i[Self::WINDOW_ELEMENTS / 2].double();
119        }
120
121        // batch convert the table to affine form, so we can use mixed addition
122        // in the online phase.
123        let mut tbl = vec![ic_bls12_381::G2Affine::identity(); Self::TABLE_SIZE];
124        ic_bls12_381::G2Projective::batch_normalize(&ptbl, &mut tbl);
125
126        Self { tbl }
127    }
128
129    /// Perform variable-time scalar multiplication using the precomputed table plus extra addition
130    fn mul_vartime(&self, scalar: &Scalar, extra_add: Option<&G2Affine>) -> ic_bls12_381::G2Affine {
131        let s = {
132            let mut s = scalar.to_bytes();
133            s.reverse(); // zkcrypto/bls12_381 uses little-endian
134            s
135        };
136
137        let mut accum = if let Some(add) = extra_add {
138            ic_bls12_381::G2Projective::from(add)
139        } else {
140            ic_bls12_381::G2Projective::identity()
141        };
142
143        for i in 0..Self::WINDOWS {
144            let tbl_for_i = &self.tbl[Self::WINDOW_ELEMENTS * i..Self::WINDOW_ELEMENTS * (i + 1)];
145
146            let b = Self::get_window(&s, Self::WINDOW_BITS * i);
147            if b > 0 {
148                accum += tbl_for_i[b as usize - 1];
149            }
150        }
151
152        G2Affine::from(accum)
153    }
154
155    /// Perform scalar multiplication using the precomputed table
156    fn mul(&self, scalar: &Scalar) -> ic_bls12_381::G2Affine {
157        let s = {
158            let mut s = scalar.to_bytes();
159            s.reverse(); // zkcrypto/bls12_381 uses little-endian
160            s
161        };
162
163        let mut accum = ic_bls12_381::G2Projective::identity();
164
165        for i in 0..Self::WINDOWS {
166            let tbl_for_i = &self.tbl[Self::WINDOW_ELEMENTS * i..Self::WINDOW_ELEMENTS * (i + 1)];
167
168            let b = Self::get_window(&s, Self::WINDOW_BITS * i);
169            accum += Self::ct_select(tbl_for_i, b as usize);
170        }
171
172        G2Affine::from(accum)
173    }
174
175    // Extract a WINDOW_BITS sized window out of s, depending on offset.
176    #[inline(always)]
177    fn get_window(s: &[u8], offset: usize) -> u8 {
178        const BITS_IN_BYTE: usize = 8;
179
180        let shift = offset % BITS_IN_BYTE;
181        let byte_offset = s.len() - 1 - (offset / BITS_IN_BYTE);
182
183        let w0 = s[byte_offset];
184
185        let single_byte_window = shift <= (BITS_IN_BYTE - Self::WINDOW_BITS) || byte_offset == 0;
186
187        let bits = if single_byte_window {
188            // If we can get the window out of single byte, do so
189            w0 >> shift
190        } else {
191            // Otherwise we must join two bytes and extract the result
192            let w1 = s[byte_offset - 1];
193            (w0 >> shift) | (w1 << (BITS_IN_BYTE - shift))
194        };
195
196        bits & Self::WINDOW_MASK
197    }
198
199    // Constant time table lookup
200    //
201    // This version is specifically adapted to this algorithm. If
202    // index is zero, then it returns the identity element. Otherwise
203    // it returns from[index-1].
204    #[inline(always)]
205    fn ct_select(from: &[ic_bls12_381::G2Affine], index: usize) -> ic_bls12_381::G2Affine {
206        use subtle::{ConditionallySelectable, ConstantTimeEq};
207
208        let mut val = ic_bls12_381::G2Affine::identity();
209
210        let index = index.wrapping_sub(1);
211        for (idx, v) in from.iter().enumerate() {
212            val.conditional_assign(v, usize::ct_eq(&idx, &index));
213        }
214
215        val
216    }
217}
218
219lazy_static::lazy_static! {
220    static ref G2_MUL_TABLE: G2PrecomputedTable = G2PrecomputedTable::new(&G2Affine::generator());
221}
222
223/// Derive a symmetric key using HKDF-SHA256
224fn hkdf(okm: &mut [u8], input: &[u8], domain_sep: &str) {
225    let hk = hkdf::Hkdf::<sha2::Sha256>::new(None, input);
226    hk.expand(domain_sep.as_bytes(), okm)
227        .expect("Unsupported output length for HKDF");
228}
229
230/// Derive a symmetric key using HKDF-SHA256
231///
232/// The `input` parameter should be a sufficiently long random input generated
233/// in a secure way. 256 bits (32 bytes) or longer is preferable.
234///
235/// The `domain_sep` should be a string that uniquely identifies the
236/// context for which this key is used.
237///
238/// The returned vector will be `len` bytes long.
239pub fn derive_symmetric_key(input: &[u8], domain_sep: &str, len: usize) -> Vec<u8> {
240    let mut okm = vec![0u8; len];
241    hkdf(&mut okm, input, domain_sep);
242    okm
243}
244
245fn hash_to_scalar(input: &[u8], domain_sep: &str) -> ic_bls12_381::Scalar {
246    use ic_bls12_381::hash_to_curve::HashToField;
247
248    let mut s = [ic_bls12_381::Scalar::zero()];
249    <ic_bls12_381::Scalar as HashToField>::hash_to_field::<ExpandMsgXmd<sha2::Sha256>>(
250        input,
251        domain_sep.as_bytes(),
252        &mut s,
253    );
254    s[0]
255}
256
257fn extend_with_length_prefix(vec: &mut Vec<u8>, data: &[u8]) {
258    vec.extend_from_slice(&(data.len() as u64).to_be_bytes());
259    vec.extend(data);
260}
261
262fn hash_to_scalar_two_inputs(
263    input1: &[u8],
264    input2: &[u8],
265    domain_sep: &str,
266) -> ic_bls12_381::Scalar {
267    let combined_input = {
268        let mut c = Vec::with_capacity(2 * 8 + input1.len() + input2.len());
269        extend_with_length_prefix(&mut c, input1);
270        extend_with_length_prefix(&mut c, input2);
271        c
272    };
273
274    hash_to_scalar(&combined_input, domain_sep)
275}
276
277#[derive(Clone, Zeroize, ZeroizeOnDrop)]
278/// Secret key of the transport key pair
279pub struct TransportSecretKey {
280    // Note that we Box the value here
281    //
282    // This is done because in Rust, even if the type does not derive Copy, any
283    // object can be moved, and Rust assumes that memcpy is sufficient to move
284    // any object. This move effectively creates a copy on the stack that we do
285    // not know about and which will not be zeroized.
286    //
287    // By putting the value into a Box, the object can still be moved, but the move
288    // will happen by copying the pointer value of the Box rather than the secret itself.
289    //
290    // See the zeroize docs (<https://docs.rs/zeroize/1.8.1/zeroize/#stackheap-zeroing-notes>)
291    // for further information about this issue.
292    secret_key: Box<Scalar>,
293}
294
295impl TransportSecretKey {
296    /// Creates a transport secret key from a 32-byte seed.
297    pub fn from_seed(seed: Vec<u8>) -> Result<TransportSecretKey, String> {
298        let seed_32_bytes: [u8; 32] = seed.try_into().map_err(|_e| "seed not 32 bytes")?;
299        let rng = &mut ChaCha20Rng::from_seed(seed_32_bytes);
300        use pairing::group::ff::Field;
301        let secret_key = Box::new(Scalar::random(rng));
302        Ok(Self { secret_key })
303    }
304
305    /// Returns the serialized public key associated with this secret key
306    pub fn public_key(&self) -> Vec<u8> {
307        let public_key = G1Affine::generator() * (*self.secret_key);
308        use pairing::group::Curve;
309        public_key.to_affine().to_compressed().to_vec()
310    }
311
312    /// Serialize this transport secret key to a bytestring
313    pub fn serialize(&self) -> Vec<u8> {
314        self.secret_key.to_bytes().to_vec()
315    }
316
317    /// Deserialize this transport secret key from a bytestring
318    pub fn deserialize(bytes: &[u8]) -> Result<Self, String> {
319        if bytes.len() != 32 {
320            return Err(format!(
321                "TransportSecretKey must be exactly 32 bytes not {}",
322                bytes.len()
323            ));
324        }
325
326        let bytes: [u8; 32] = bytes.try_into().expect("Length already checked");
327
328        if let Some(s) = Scalar::from_bytes(&bytes).into_option() {
329            Ok(Self {
330                secret_key: Box::new(s),
331            })
332        } else {
333            Err("Invalid TransportSecretKey bytes".to_string())
334        }
335    }
336}
337
338/// Return true iff the argument is a valid encoding of a transport public key
339pub fn is_valid_transport_public_key_encoding(bytes: &[u8]) -> bool {
340    match bytes.try_into() {
341        Ok(bytes) => G1Affine::from_compressed(&bytes).into_option().is_some(),
342        Err(_) => false,
343    }
344}
345
346#[derive(Copy, Clone, Debug)]
347/// Error indicating deserializing a derived public key failed
348pub enum PublicKeyDeserializationError {
349    /// The public key is invalid
350    InvalidPublicKey,
351}
352
353#[derive(Clone, Debug, Eq, PartialEq)]
354/// A master VetKD public key
355pub struct MasterPublicKey {
356    point: G2Affine,
357}
358
359impl MasterPublicKey {
360    const BYTES: usize = G2AFFINE_BYTES;
361
362    /// Deserializes a (derived) public key.
363    ///
364    /// Only compressed points are supported.
365    ///
366    /// Normally the bytes provided here will have been returned by the
367    /// Internet Computer's `vetkd_public_key`` management canister interface.
368    ///
369    /// Returns an error if the key is invalid (e.g., it has invalid length,
370    /// i.e., not 96 bytes, it is not in compressed format, is is not a point
371    /// on the curve, it is not torsion-free).
372    pub fn deserialize(bytes: &[u8]) -> Result<Self, PublicKeyDeserializationError> {
373        let dpk_bytes: &[u8; Self::BYTES] = bytes
374            .try_into()
375            .map_err(|_e: TryFromSliceError| PublicKeyDeserializationError::InvalidPublicKey)?;
376        let dpk = G2Affine::from_compressed(dpk_bytes)
377            .into_option()
378            .ok_or(PublicKeyDeserializationError::InvalidPublicKey)?;
379        Ok(Self { point: dpk })
380    }
381
382    /// Perform first-stage derivation of a canister public key from the master public key
383    ///
384    /// To create the derived public key in VetKD, a two step derivation is performed;
385    ///
386    /// - The first step creates a canister public key, sometimes called canister master key.
387    ///   This step is implemented by the `derive_canister_key` method.
388    ///
389    /// - The second step derives a canister sub-key which incorporates the "context" value provided to the
390    ///   `vetkd_public_key` management canister interface. This step is implemented by the
391    ///   `DerivedPublicKey::derive_sub_key` method.
392    pub fn derive_canister_key(&self, canister_id: &[u8]) -> DerivedPublicKey {
393        let dst = "ic-vetkd-bls12-381-g2-canister-id";
394
395        let offset = hash_to_scalar_two_inputs(&self.serialize(), canister_id, dst);
396
397        let derived_key = G2_MUL_TABLE.mul_vartime(&offset, Some(&self.point));
398        DerivedPublicKey { point: derived_key }
399    }
400
401    /// Return the byte encoding of this master public key
402    pub fn serialize(&self) -> Vec<u8> {
403        self.point.to_compressed().to_vec()
404    }
405
406    /// Return the hardcoded master public key used on IC
407    ///
408    /// This allows performing public key derivation offline
409    ///
410    /// Returns None if the provided key_id is not known
411    pub fn for_mainnet_key(key_id: &VetKDKeyId) -> Option<Self> {
412        match (key_id.curve, key_id.name.as_str()) {
413            (VetKDCurve::Bls12_381_G2, "key_1") => Some(Self::new(*PROD_G2_KEY_1)),
414            (VetKDCurve::Bls12_381_G2, "test_key_1") => Some(Self::new(*PROD_G2_TEST_KEY_1)),
415            (_, _) => None,
416        }
417    }
418
419    /// Return the hardcoded master public key used for testing in PocketIC
420    ///
421    /// Returns None if the provided key_id is not known
422    pub fn for_pocketic_key(key_id: &VetKDKeyId) -> Option<Self> {
423        match (key_id.curve, key_id.name.as_str()) {
424            (VetKDCurve::Bls12_381_G2, "key_1") => Some(Self::new(*POCKETIC_G2_KEY_1)),
425            (VetKDCurve::Bls12_381_G2, "test_key_1") => Some(Self::new(*POCKETIC_G2_TEST_KEY_1)),
426            (VetKDCurve::Bls12_381_G2, "dfx_test_key") => {
427                Some(Self::new(*POCKETIC_G2_DFX_TEST_KEY))
428            }
429            (_, _) => None,
430        }
431    }
432
433    fn new(point: G2Affine) -> Self {
434        Self { point }
435    }
436}
437
438#[derive(Clone, Debug, Eq, PartialEq)]
439/// A derived public key
440pub struct DerivedPublicKey {
441    point: G2Affine,
442}
443
444impl From<DerivedPublicKey> for G2Affine {
445    fn from(public_key: DerivedPublicKey) -> Self {
446        public_key.point
447    }
448}
449
450impl DerivedPublicKey {
451    const BYTES: usize = G2AFFINE_BYTES;
452
453    /// Deserializes a (derived) public key.
454    ///
455    /// Only compressed points are supported.
456    ///
457    /// Normally the bytes provided here will have been returned by the
458    /// Internet Computer's `vetkd_public_key`` management canister interface.
459    ///
460    /// Returns an error if the key is invalid (e.g., it has invalid length,
461    /// i.e., not 96 bytes, it is not in compressed format, is is not a point
462    /// on the curve, it is not torsion-free).
463    pub fn deserialize(bytes: &[u8]) -> Result<Self, PublicKeyDeserializationError> {
464        let dpk_bytes: &[u8; Self::BYTES] = bytes
465            .try_into()
466            .map_err(|_e: TryFromSliceError| PublicKeyDeserializationError::InvalidPublicKey)?;
467        let dpk = G2Affine::from_compressed(dpk_bytes)
468            .into_option()
469            .ok_or(PublicKeyDeserializationError::InvalidPublicKey)?;
470        Ok(Self { point: dpk })
471    }
472
473    /// Perform second-stage derivation of a public key from a canister public key
474    ///
475    /// To create the derived public key in VetKD, a two step derivation is performed;
476    ///
477    /// - The first step creates a canister public key, sometimes called canister master key. This step is implemented
478    ///   by the `MasterKey::derive_canister_key` method.
479    /// - The second step derives a canister sub-key which incorporates the "context" value provided to the
480    ///   `vetkd_public_key` management canister interface. This step is implemented by the `derive_sub_key` method.
481    ///
482    /// If `vetkd_public_key` is invoked with an empty derivation context, it simply returns the
483    /// canister master key. Then the second derivation step can be done offline, using this
484    /// function. This is useful if you wish to derive multiple keys without having to interact with
485    /// the IC each time.
486    pub fn derive_sub_key(&self, context: &[u8]) -> Self {
487        if context.is_empty() {
488            return self.clone();
489        }
490
491        let dst = "ic-vetkd-bls12-381-g2-context";
492
493        let offset = hash_to_scalar_two_inputs(&self.serialize(), context, dst);
494
495        let derived_key = G2_MUL_TABLE.mul_vartime(&offset, Some(&self.point));
496        Self { point: derived_key }
497    }
498
499    /// Return the byte encoding of this derived public key
500    pub fn serialize(&self) -> Vec<u8> {
501        self.point.to_compressed().to_vec()
502    }
503}
504
505/// A verifiably encrypted threshold key derived by the VetKD protocol
506///
507/// A VetKey is a valid BLS signature created for an input specified
508/// by the user
509///
510#[derive(Clone, Debug, Eq, PartialEq, Zeroize, ZeroizeOnDrop)]
511pub struct VetKey {
512    // See the comment regarding Boxing in the definition of TransportSecretKey
513    vetkey: Box<(G1Affine, [u8; 48])>,
514}
515
516impl VetKey {
517    fn new(pt: G1Affine) -> Self {
518        let vetkey = Box::new((pt, pt.to_compressed()));
519        Self { vetkey }
520    }
521
522    /**
523     * Return the VetKey bytes, aka the BLS signature
524     *
525     * Use the raw bytes only if your design makes use of the fact that VetKeys
526     * are BLS signatures (eg for random beacon or threshold BLS signature
527     * generation). If you are using VetKD for key distribution, instead use
528     * [`derive_symmetric_key`]
529     */
530    pub fn signature_bytes(&self) -> &[u8; 48] {
531        &self.vetkey.1
532    }
533
534    /**
535     * Serialize the VetKey to a byte string
536     *
537     * The return value here is the VetKey itself which in most uses is a
538     * secret value.
539     */
540    pub fn serialize(&self) -> &[u8; 48] {
541        &self.vetkey.1
542    }
543
544    pub(crate) fn point(&self) -> &G1Affine {
545        &self.vetkey.0
546    }
547
548    /**
549     * Derive a symmetric key of the requested length from the VetKey
550     *
551     * The `domain_sep` parameter should be a string unique to your application and
552     * also your usage of the resulting key. For example say your application
553     * "my-app" is deriving two keys, one for usage "foo" and the other for
554     * "bar". You might use as domain separators "my-app-foo" and "my-app-bar".
555     */
556    pub fn derive_symmetric_key(&self, domain_sep: &str, output_len: usize) -> Vec<u8> {
557        derive_symmetric_key(self.serialize(), domain_sep, output_len)
558    }
559
560    /**
561     * Return a DerivedKeyMaterial
562     *
563     * This class allows further key derivation and encryption but the underlying
564     * secret key cannot be extracted.
565     */
566    pub fn as_derived_key_material(&self) -> DerivedKeyMaterial {
567        let key = self.derive_symmetric_key("ic-vetkd-bls12-381-g2-derived-key-material", 32);
568        DerivedKeyMaterial {
569            key,
570            raw_vetkey: self.vetkey.1.to_vec(),
571        }
572    }
573
574    /**
575     * Deserialize a VetKey from the byte encoding
576     *
577     * Typically this would have been created using [`VetKey::signature_bytes`]
578     */
579    pub fn deserialize(bytes: &[u8]) -> Result<Self, String> {
580        let bytes48: [u8; 48] = bytes.try_into().map_err(|_e: TryFromSliceError| {
581            format!("Vetkey is unexpected length {}", bytes.len())
582        })?;
583
584        if let Some(pt) = G1Affine::from_compressed(&bytes48).into_option() {
585            Ok(Self::new(pt))
586        } else {
587            Err("Invalid VetKey".to_string())
588        }
589    }
590}
591
592/// Key material derived from a VetKey
593///
594/// This struct allows deriving further keys from the VetKey without
595/// allowing direct access to the VetKey secret key, preventing it
596/// from being reused inappropriately.
597///
598/// As a convenience this struct also offers AES-GCM encryption/decryption
599#[derive(Clone, Zeroize, ZeroizeOnDrop)]
600pub struct DerivedKeyMaterial {
601    key: Vec<u8>,
602    raw_vetkey: Vec<u8>,
603}
604
605#[derive(Copy, Clone, Eq, PartialEq, Debug)]
606/// An error while encrypting
607pub enum EncryptionError {
608    /// The provided message was too long to be encrypted
609    PlaintextTooLong,
610}
611
612#[derive(Copy, Clone, Eq, PartialEq, Debug)]
613/// An error while decrypting
614pub enum DecryptionError {
615    /// The ciphertext was too short to possibly be valid
616    MessageTooShort,
617    /// The GCM tag did not validate
618    InvalidCiphertext,
619    /// The expected message header did not appear in the ciphertext
620    ///
621    /// Either the ciphertext was invalid, or possibly the decrypting side
622    /// needs to be upgraded to support a new format
623    UnknownHeader,
624}
625
626impl DerivedKeyMaterial {
627    const GCM_KEY_SIZE: usize = 32;
628    const GCM_TAG_SIZE: usize = 16;
629    const GCM_NONCE_SIZE: usize = 12;
630
631    const GCM_HEADER_VERSION: u8 = 2;
632    const GCM_HEADER_SIZE: usize = 8;
633    const GCM_HEADER: [u8; Self::GCM_HEADER_SIZE] = *b"IC GCMv2";
634
635    /// Derive a new key for AES-GCM
636    ///
637    /// Note that the domain separator provided by the user is prefixed
638    /// with `ic-vetkd-bls12-381-g2-aes-gcm-`
639    fn derive_aes_gcm_key(&self, domain_sep: &str, version: u8) -> Vec<u8> {
640        derive_symmetric_key(
641            &self.key,
642            &format!("ic-vetkd-bls12-381-g2-aes-gcm-v{}-{}", version, domain_sep),
643            Self::GCM_KEY_SIZE,
644        )
645    }
646
647    /// Encrypt a message
648    ///
649    /// The decryption used here is interoperable with the TypeScript
650    /// library ic_vetkeys function `DerivedKeyMaterial.decryptMessage`
651    ///
652    /// The domain separator should be unique for this usage, for example
653    /// by including the identities of the sender and receiver.
654    ///
655    /// The associated data field is information which will be authenticated
656    /// but not included in the ciphertext. This can be useful for binding
657    /// additional contextual data (eg a protocol identifier) or information
658    /// which should be authenticated but does not need to be encrypted.
659    /// If not needed, it can be left empty or an application-specific constant
660    /// value can be used,
661    ///
662    /// The format of the returned message is, in order
663    ///  * 8 byte header
664    ///  * 12 byte nonce
665    ///  * Ciphertext of length equal to the message length
666    ///  * 16 byte GCM authentication tag
667    ///
668    pub fn encrypt_message<R: rand::RngCore + rand::CryptoRng>(
669        &self,
670        message: &[u8],
671        domain_sep: &str,
672        associated_data: &[u8],
673        rng: &mut R,
674    ) -> Result<Vec<u8>, EncryptionError> {
675        use aes_gcm::{aead::Aead, aead::AeadCore, Aes256Gcm, Key, KeyInit};
676        let key = self.derive_aes_gcm_key(domain_sep, Self::GCM_HEADER_VERSION);
677        let key = Key::<Aes256Gcm>::from_slice(&key);
678        // aes_gcm::Aes256Gcm only supports/uses 12 byte nonces
679        let nonce = Aes256Gcm::generate_nonce(rng);
680        assert_eq!(nonce.len(), Self::GCM_NONCE_SIZE);
681        let gcm = Aes256Gcm::new(key);
682
683        // Unfortunately aes_gcm does not allow a vector of AAD inputs
684        // so we have to allocate a copy. Typically associated data is short
685        let prefixed_aad = {
686            let mut r = Vec::with_capacity(Self::GCM_HEADER.len() + associated_data.len());
687            r.extend_from_slice(&Self::GCM_HEADER); // assumed fixed length
688            r.extend_from_slice(associated_data);
689            r
690        };
691
692        let msg = aes_gcm::aead::Payload {
693            msg: message,
694            aad: &prefixed_aad,
695        };
696
697        // The function returns an opaque `Error` with no details, but upon
698        // examination, the only way it can fail is if the plaintext is larger
699        // than GCM's maximum input length of 2^36 bytes.
700        let ctext = gcm
701            .encrypt(&nonce, msg)
702            .map_err(|_| EncryptionError::PlaintextTooLong)?;
703
704        let mut res = vec![];
705        res.extend_from_slice(&Self::GCM_HEADER);
706        res.extend_from_slice(nonce.as_slice());
707        res.extend_from_slice(ctext.as_slice());
708        Ok(res)
709    }
710
711    /// Decrypt a message
712    ///
713    /// The decryption used here is interoperable with the TypeScript
714    /// library ic_vetkeys function `DerivedKeyMaterial.encryptMessage`
715    pub fn decrypt_message(
716        &self,
717        ctext: &[u8],
718        domain_sep: &str,
719        associated_data: &[u8],
720    ) -> Result<Vec<u8>, DecryptionError> {
721        use aes_gcm::{aead::Aead, Aes256Gcm, Key, KeyInit};
722
723        // Minimum possible length is 8 byte header + 12 bytes nonce + 16 bytes GCM tag
724        if ctext.len() < Self::GCM_HEADER_SIZE + Self::GCM_NONCE_SIZE + Self::GCM_TAG_SIZE {
725            return Err(DecryptionError::MessageTooShort);
726        }
727
728        // If multiple versions are ever supported in the future, and we
729        // must retain backward compatability, then this would need to be
730        // extended to check for multiple different headers and process
731        // the ciphertext accordingly.
732        if ctext[0..Self::GCM_HEADER_SIZE] != Self::GCM_HEADER {
733            if associated_data.is_empty() {
734                // Try decrypting using the old headerless format which did not
735                // support associated data
736
737                let key = derive_symmetric_key(&self.raw_vetkey, domain_sep, Self::GCM_KEY_SIZE);
738
739                let nonce = aes_gcm::Nonce::from_slice(&ctext[0..Self::GCM_NONCE_SIZE]);
740                let gcm = Aes256Gcm::new(Key::<Aes256Gcm>::from_slice(&key));
741
742                let ptext = gcm
743                    .decrypt(nonce, &ctext[Self::GCM_NONCE_SIZE..])
744                    .map_err(|_| DecryptionError::InvalidCiphertext)?;
745
746                return Ok(ptext.as_slice().to_vec());
747            } else {
748                return Err(DecryptionError::UnknownHeader);
749            }
750        }
751
752        let key = self.derive_aes_gcm_key(domain_sep, Self::GCM_HEADER_VERSION);
753        let key = Key::<Aes256Gcm>::from_slice(&key);
754
755        let nonce = aes_gcm::Nonce::from_slice(
756            &ctext[Self::GCM_HEADER_SIZE..Self::GCM_HEADER_SIZE + Self::GCM_NONCE_SIZE],
757        );
758        let gcm = Aes256Gcm::new(key);
759
760        let prefixed_aad = {
761            let mut r = Vec::with_capacity(Self::GCM_HEADER.len() + associated_data.len());
762            r.extend_from_slice(&ctext[0..Self::GCM_HEADER_SIZE]);
763            r.extend_from_slice(associated_data);
764            r
765        };
766
767        let msg = aes_gcm::aead::Payload {
768            msg: &ctext[Self::GCM_HEADER_SIZE + Self::GCM_NONCE_SIZE..],
769            aad: &prefixed_aad,
770        };
771
772        let ptext = gcm
773            .decrypt(nonce, msg)
774            .map_err(|_| DecryptionError::InvalidCiphertext)?;
775
776        Ok(ptext.as_slice().to_vec())
777    }
778}
779
780#[derive(Copy, Clone, Debug)]
781/// Error indicating that deserializing an encrypted key failed
782pub enum EncryptedVetKeyDeserializationError {
783    /// Error indicating one or more of the points was invalid
784    InvalidEncryptedVetKey,
785}
786
787/// An encrypted VetKey
788#[derive(Clone, Debug, Eq, PartialEq)]
789pub struct EncryptedVetKey {
790    c1: G1Affine,
791    c2: G2Affine,
792    c3: G1Affine,
793}
794
795impl EncryptedVetKey {
796    /// The length of the serialized encoding of this type
797    const BYTES: usize = 2 * G1AFFINE_BYTES + G2AFFINE_BYTES;
798
799    const C2_OFFSET: usize = G1AFFINE_BYTES;
800    const C3_OFFSET: usize = G1AFFINE_BYTES + G2AFFINE_BYTES;
801
802    /// Decrypts and verifies the VetKey
803    pub fn decrypt_and_verify(
804        &self,
805        tsk: &TransportSecretKey,
806        derived_public_key: &DerivedPublicKey,
807        input: &[u8],
808    ) -> Result<VetKey, String> {
809        use pairing::group::Group;
810
811        // Check that c1 and c2 have the same discrete logarithm
812
813        let c2_prep = G2Prepared::from(self.c2);
814
815        let c1_c2 = gt_multipairing(&[
816            (&self.c1, &G2PREPARED_NEG_G),
817            (&G1Affine::generator(), &c2_prep),
818        ]);
819
820        if !bool::from(c1_c2.is_identity()) {
821            return Err("invalid encrypted key: c1 inconsistent with c2".to_string());
822        }
823
824        // Recover the purported VetKey
825        let k = G1Affine::from(G1Projective::from(&self.c3) - self.c1 * (*tsk.secret_key));
826
827        // Check that the VetKey is a valid BLS signature
828        if verify_bls_signature_pt(derived_public_key, input, &k) {
829            Ok(VetKey::new(k))
830        } else {
831            Err("invalid encrypted key: verification failed".to_string())
832        }
833    }
834
835    /// Serialize the encrypted VetKey
836    pub fn serialize(&self) -> Vec<u8> {
837        let mut result = vec![];
838
839        result.extend_from_slice(&self.c1.to_compressed());
840        result.extend_from_slice(&self.c2.to_compressed());
841        result.extend_from_slice(&self.c3.to_compressed());
842
843        result
844    }
845
846    /// Deserializes an encrypted key from a byte vector
847    pub fn deserialize(bytes: &[u8]) -> Result<EncryptedVetKey, String> {
848        let ek_bytes: &[u8; Self::BYTES] = bytes.try_into().map_err(|_e: TryFromSliceError| {
849            format!("key not {} bytes but {}", Self::BYTES, bytes.len())
850        })?;
851        Self::deserialize_array(ek_bytes).map_err(|e| format!("{e:?}"))
852    }
853
854    /// Deserializes an encrypted key from a byte array
855    pub fn deserialize_array(
856        val: &[u8; Self::BYTES],
857    ) -> Result<Self, EncryptedVetKeyDeserializationError> {
858        let c1_bytes: &[u8; G1AFFINE_BYTES] = &val[..Self::C2_OFFSET]
859            .try_into()
860            .map_err(|_e| EncryptedVetKeyDeserializationError::InvalidEncryptedVetKey)?;
861        let c2_bytes: &[u8; G2AFFINE_BYTES] = &val[Self::C2_OFFSET..Self::C3_OFFSET]
862            .try_into()
863            .map_err(|_e| EncryptedVetKeyDeserializationError::InvalidEncryptedVetKey)?;
864        let c3_bytes: &[u8; G1AFFINE_BYTES] = &val[Self::C3_OFFSET..]
865            .try_into()
866            .map_err(|_e| EncryptedVetKeyDeserializationError::InvalidEncryptedVetKey)?;
867
868        let c1 = G1Affine::from_compressed(c1_bytes).into_option();
869        let c2 = G2Affine::from_compressed(c2_bytes).into_option();
870        let c3 = G1Affine::from_compressed(c3_bytes).into_option();
871
872        match (c1, c2, c3) {
873            (Some(c1), Some(c2), Some(c3)) => Ok(Self { c1, c2, c3 }),
874            (_, _, _) => Err(EncryptedVetKeyDeserializationError::InvalidEncryptedVetKey),
875        }
876    }
877}
878
879#[derive(Clone, Debug, Eq, PartialEq)]
880/// An identity, used for identity based encryption (IBE)
881///
882/// As far as the IBE scheme goes this is simply an opauqe bytestring
883/// We provide a type to make code using the IBE a bit easier to understand
884pub struct IbeIdentity {
885    val: Vec<u8>,
886}
887
888impl IbeIdentity {
889    /// Create an identity from a byte string
890    pub fn from_bytes(bytes: &[u8]) -> Self {
891        Self {
892            val: bytes.to_vec(),
893        }
894    }
895
896    /// Create an identity from a UTF8 string
897    pub fn from_string(str: &str) -> Self {
898        Self::from_bytes(str.as_bytes())
899    }
900
901    /// Create an identity from a Principal
902    pub fn from_principal(principal: &candid::Principal) -> Self {
903        Self::from_bytes(principal.as_slice())
904    }
905
906    /// Return the bytestring of this identity
907    pub fn value(&self) -> &[u8] {
908        &self.val
909    }
910}
911
912/*
913* Amount of randomness generated during the IBE encryption process
914*/
915const IBE_SEED_BYTES: usize = 32;
916
917/// A random seed, used for identity based encryption
918#[derive(Zeroize, ZeroizeOnDrop)]
919pub struct IbeSeed {
920    // See the comment regarding Boxing in the definition of TransportSecretKey
921    val: Box<[u8; IBE_SEED_BYTES]>,
922}
923
924impl IbeSeed {
925    /// Create a random seed for IBE encryption
926    pub fn random<R: rand::CryptoRng + rand::RngCore>(rng: &mut R) -> Self {
927        use rand::Rng;
928        Self {
929            val: Box::new(rng.gen::<[u8; IBE_SEED_BYTES]>()),
930        }
931    }
932
933    /// Create a seed for IBE encryption from a byte string
934    ///
935    /// This input should be randomly chosen by a secure random number generator.
936    /// If the seed is not securely generated the IBE scheme will be insecure.
937    ///
938    /// At least 128 bits (16 bytes) must be provided.
939    ///
940    /// If the input is exactly 256 bits it is used directly. Otherwise the input
941    /// is hashed with HKDF to produce a 256 bit seed.
942    pub fn from_bytes(bytes: &[u8]) -> Result<Self, String> {
943        if bytes.len() < 16 {
944            return Err("Insufficient input material for IbeSeed derivation".to_string());
945        }
946
947        let mut val = Box::new([0u8; IBE_SEED_BYTES]);
948        if bytes.len() == IBE_SEED_BYTES {
949            val.copy_from_slice(bytes)
950        } else {
951            let hkdf =
952                derive_symmetric_key(bytes, "ic-vetkd-bls12-381-ibe-hash-seed", IBE_SEED_BYTES);
953            val.copy_from_slice(&hkdf);
954        }
955
956        Ok(Self { val })
957    }
958
959    fn value(&self) -> &[u8; IBE_SEED_BYTES] {
960        &self.val
961    }
962}
963
964/*
965 * IBE ciphertexts are prefixed with a header to identity the protocol and provide
966 * an extension point if needed in the future eg for changing to a different cipher.
967 *
968 * The header consists of "IC IBE" (ASCII) plus two bytes 0x00 and 0x01 which
969 * here are just fixed and effectively arbitrary values, but could be used to
970 * indicate for example a version in the future should we need to support multiple
971 * variants of the IBE scheme.
972*/
973const IBE_HEADER: [u8; 8] = [b'I', b'C', b' ', b'I', b'B', b'E', 0x00, 0x01];
974
975const IBE_HEADER_BYTES: usize = IBE_HEADER.len();
976
977const IBE_OVERHEAD: usize = IBE_HEADER_BYTES + IBE_SEED_BYTES + G2AFFINE_BYTES;
978
979#[derive(Clone, Debug, Eq, PartialEq)]
980/// An IBE (identity based encryption) ciphertext
981pub struct IbeCiphertext {
982    header: Vec<u8>,
983    c1: G2Affine,
984    c2: [u8; IBE_SEED_BYTES],
985    c3: Vec<u8>,
986}
987
988enum IbeDomainSep {
989    HashToMask,
990    MaskSeed,
991    MaskMsg(usize),
992}
993
994impl IbeDomainSep {
995    #[allow(clippy::inherent_to_string)]
996    fn to_string(&self) -> String {
997        match self {
998            Self::HashToMask => "ic-vetkd-bls12-381-ibe-hash-to-mask".to_owned(),
999            Self::MaskSeed => "ic-vetkd-bls12-381-ibe-mask-seed".to_owned(),
1000            // Zero prefix the length up to 20 digits, which is sufficient to be fixed
1001            // length for any 64-bit length. This ensures all of the MaskMsg domain
1002            // separators are of equal length. With how we use the domain separators, this
1003            // padding isn't required - we only need uniquness - but having variable
1004            // length domain separators is generally not considered a good practice and is
1005            // easily avoidable here.
1006            Self::MaskMsg(len) => format!("ic-vetkd-bls12-381-ibe-mask-msg-{len:020}"),
1007        }
1008    }
1009}
1010
1011impl IbeCiphertext {
1012    /// Serialize this IBE ciphertext
1013    pub fn serialize(&self) -> Vec<u8> {
1014        let mut output = Vec::with_capacity(IBE_OVERHEAD + self.c3.len());
1015
1016        output.extend_from_slice(&self.header);
1017        output.extend_from_slice(&self.c1.to_compressed());
1018        output.extend_from_slice(&self.c2);
1019        output.extend_from_slice(&self.c3);
1020
1021        output
1022    }
1023
1024    /// Deserialize an IBE ciphertext
1025    ///
1026    /// Returns Err if the encoding is not valid
1027    pub fn deserialize(bytes: &[u8]) -> Result<Self, String> {
1028        if bytes.len() < IBE_OVERHEAD {
1029            return Err("IbeCiphertext too short to be valid".to_string());
1030        }
1031
1032        let header = bytes[0..IBE_HEADER_BYTES].to_vec();
1033        let c1 = deserialize_g2(&bytes[IBE_HEADER_BYTES..(IBE_HEADER_BYTES + G2AFFINE_BYTES)])?;
1034
1035        let mut c2 = [0u8; IBE_SEED_BYTES];
1036        c2.copy_from_slice(
1037            &bytes[IBE_HEADER_BYTES + G2AFFINE_BYTES
1038                ..(IBE_HEADER_BYTES + G2AFFINE_BYTES + IBE_SEED_BYTES)],
1039        );
1040
1041        let c3 = bytes[IBE_HEADER_BYTES + G2AFFINE_BYTES + IBE_SEED_BYTES..].to_vec();
1042
1043        if header != IBE_HEADER {
1044            return Err("IbeCiphertext has unknown header".to_string());
1045        }
1046
1047        Ok(Self { header, c1, c2, c3 })
1048    }
1049
1050    fn hash_to_mask(header: &[u8], seed: &[u8; IBE_SEED_BYTES], msg: &[u8]) -> Scalar {
1051        /*
1052        It would have been better to instead use the SHA-256 of the message instead of the
1053        message directly, since that would avoid having to allocate an extra buffer of
1054        length proportional to the message. If in the future any change is made to the
1055        IBE scheme, consider also changing this.
1056        */
1057
1058        let domain_sep = IbeDomainSep::HashToMask;
1059        let mut ro_input = Vec::with_capacity(seed.len() + msg.len());
1060        ro_input.extend_from_slice(header);
1061        ro_input.extend_from_slice(seed);
1062        ro_input.extend_from_slice(msg);
1063
1064        hash_to_scalar(&ro_input, &domain_sep.to_string())
1065    }
1066
1067    fn mask_seed(seed: &[u8; IBE_SEED_BYTES], t: &Gt) -> [u8; IBE_SEED_BYTES] {
1068        let domain_sep = IbeDomainSep::MaskSeed;
1069        let mask = derive_symmetric_key(&t.to_bytes(), &domain_sep.to_string(), IBE_SEED_BYTES);
1070
1071        let mut masked_seed = [0u8; IBE_SEED_BYTES];
1072        for i in 0..IBE_SEED_BYTES {
1073            masked_seed[i] = mask[i] ^ seed[i];
1074        }
1075        masked_seed
1076    }
1077
1078    fn mask_msg(msg: &[u8], seed: &[u8; IBE_SEED_BYTES]) -> Vec<u8> {
1079        fn derive_ibe_ctext_mask(seed: &[u8], msg_len: usize) -> Vec<u8> {
1080            use sha3::{
1081                digest::{ExtendableOutputReset, Update, XofReader},
1082                Shake256,
1083            };
1084
1085            let mut shake = Shake256::default();
1086            shake.update(seed);
1087
1088            let mut xof = shake.finalize_xof_reset();
1089            let mut mask = vec![0u8; msg_len];
1090            xof.read(&mut mask);
1091            mask
1092        }
1093
1094        let domain_sep = IbeDomainSep::MaskMsg(msg.len());
1095
1096        let mut shake_seed = derive_symmetric_key(seed, &domain_sep.to_string(), IBE_SEED_BYTES);
1097
1098        let mut mask = derive_ibe_ctext_mask(&shake_seed, msg.len());
1099        shake_seed.zeroize();
1100
1101        for i in 0..msg.len() {
1102            mask[i] ^= msg[i];
1103        }
1104
1105        mask
1106    }
1107
1108    /// Encrypt a message using IBE
1109    ///
1110    /// There is no fixed upper bound on the size of the message that can be encrypted using
1111    /// this scheme. However, internally during the encryption process several heap allocations
1112    /// are performed which are approximately the same length as the message itself, so
1113    /// encrypting or decrypting very large messages may result in memory allocation errors.
1114    ///
1115    /// If you anticipate using IBE to encrypt very large messages, consider using IBE just to
1116    /// encrypt a symmetric key, and then using a standard cipher such as AES-GCM to encrypt the
1117    /// data.
1118    ///
1119    /// The seed should be generated with a cryptographically secure random
1120    /// number generator. Do not reuse the seed for encrypting another message
1121    /// or any other purpose.
1122    ///
1123    /// To decrypt this message requires using the VetKey associated with the
1124    /// provided derived public key (ie the same master key and context string),
1125    /// and with an `input` equal to the provided `identity` parameter.
1126    pub fn encrypt(
1127        dpk: &DerivedPublicKey,
1128        identity: &IbeIdentity,
1129        msg: &[u8],
1130        seed: &IbeSeed,
1131    ) -> Self {
1132        let header = IBE_HEADER.to_vec();
1133
1134        let t = Self::hash_to_mask(&header, seed.value(), msg);
1135
1136        let pt = augmented_hash_to_g1(&dpk.point, identity.value());
1137
1138        let tsig = ic_bls12_381::pairing(&pt, &dpk.point) * t;
1139
1140        let c1 = G2_MUL_TABLE.mul(&t);
1141        let c2 = Self::mask_seed(seed.value(), &tsig);
1142        let c3 = Self::mask_msg(msg, seed.value());
1143
1144        Self { header, c1, c2, c3 }
1145    }
1146
1147    /// Decrypt an IBE ciphertext
1148    ///
1149    /// There is no fixed upper bound on the size of the message that can be encrypted using
1150    /// this scheme. However, internally during the encryption process several heap allocations
1151    /// are performed which are approximately the same length as the message itself, so
1152    /// encrypting or decrypting very large messages may result in memory allocation errors.
1153    ///
1154    /// The VetKey provided must be the VetKey produced by a request to the IC
1155    /// for a given `identity` (aka `input`) and `context` both matching the
1156    /// values used during encryption.
1157    ///
1158    /// Returns the plaintext, or Err if decryption failed
1159    pub fn decrypt(&self, vetkey: &VetKey) -> Result<Vec<u8>, String> {
1160        let tsig = ic_bls12_381::pairing(vetkey.point(), &self.c1);
1161
1162        let seed = Self::mask_seed(&self.c2, &tsig);
1163
1164        let msg = Self::mask_msg(&self.c3, &seed);
1165
1166        let t = Self::hash_to_mask(&self.header, &seed, &msg);
1167
1168        let g_t = G2_MUL_TABLE.mul(&t);
1169
1170        if self.c1 == g_t {
1171            Ok(msg)
1172        } else {
1173            Err("decryption failed".to_string())
1174        }
1175    }
1176
1177    /// Helper function for determining the size of an IBE ciphertext in bytes.
1178    pub fn ciphertext_size(plaintext_size: usize) -> usize {
1179        plaintext_size + IBE_OVERHEAD
1180    }
1181
1182    /// Helper function for determining the size of an IBE plaintext in bytes.
1183    ///
1184    /// Returns None if the indicated length would be a ciphertext
1185    /// that is not possibly valid (due to missing required elements)
1186    pub fn plaintext_size(ciphertext_size: usize) -> Option<usize> {
1187        if ciphertext_size >= IBE_OVERHEAD {
1188            Some(ciphertext_size - IBE_OVERHEAD)
1189        } else {
1190            None
1191        }
1192    }
1193}
1194
1195/// An error occured while decoding or checking a VrfOutput
1196#[derive(Copy, Clone, Debug)]
1197pub enum InvalidVrfOutput {
1198    /// The serialization has an incorrect/impossible length
1199    UnexpectedLength,
1200    /// The serialization contains invalid data
1201    InvalidData,
1202    /// The VRF proof was invalid
1203    InvalidProof,
1204}
1205
1206/// VRF (Verifiable Random Function) Output
1207///
1208/// VetKD can be used to construct a VRF, which is a public key version of a
1209/// keyed hash. Like a standard keyed hash, it takes an input string and produces
1210/// a output string which is indistinguishable from random. The difference
1211/// between a VRF and a normal keyed hash is that a VRF can only be computed
1212/// by someone with access to the VRF secret key, while the VRF output can be verified
1213/// by any party with access to the public key.
1214///
1215/// For some general background on VRFs consult [RFC 9381](https://www.rfc-editor.org/rfc/rfc9381.html)
1216///
1217/// Create a new [`VrfOutput`] using [`management_canister::compute_vrf`]
1218#[derive(Eq, PartialEq)]
1219pub struct VrfOutput {
1220    proof: VetKey,
1221    dpk: DerivedPublicKey,
1222    output: [u8; Self::VRF_BYTES],
1223    input: Vec<u8>,
1224}
1225
1226impl VrfOutput {
1227    /// The size of the hashed VRF
1228    pub const VRF_BYTES: usize = 32;
1229
1230    fn compute_vrf_hash(
1231        vetkey: &VetKey,
1232        dpk: &DerivedPublicKey,
1233        input: &[u8],
1234    ) -> [u8; Self::VRF_BYTES] {
1235        /*
1236        We instantiate the VRF by hashing with HKDF the prefix-free concatenation of
1237
1238        - The vetKey (ie the BLS signature)
1239        - The compressed serialization of the derived public key
1240        - The input that was used to construct the signature
1241
1242        Strictly speaking only the vetKey itself is required but binding all available
1243        context is generally considered a good practice.
1244         */
1245        let mut ro_input =
1246            Vec::with_capacity(G1AFFINE_BYTES + G2AFFINE_BYTES + input.len() + 3 * 8);
1247        extend_with_length_prefix(&mut ro_input, vetkey.serialize());
1248        extend_with_length_prefix(&mut ro_input, &dpk.serialize());
1249        extend_with_length_prefix(&mut ro_input, input);
1250
1251        let mut output = [0u8; Self::VRF_BYTES];
1252        hkdf(&mut output, &ro_input, "ic-vetkd-bls12-381-g2-vrf");
1253        output
1254    }
1255
1256    /// Create a new VrfOutput from a VetKey
1257    ///
1258    /// The provided input and derived public key must be the same values
1259    /// which were used to create the VetKey.
1260    pub(crate) fn create(
1261        proof: VetKey,
1262        input: Vec<u8>,
1263        dpk: DerivedPublicKey,
1264    ) -> Result<Self, InvalidVrfOutput> {
1265        if !verify_bls_signature_pt(&dpk, &input, proof.point()) {
1266            return Err(InvalidVrfOutput::InvalidProof);
1267        }
1268
1269        let output = Self::compute_vrf_hash(&proof, &dpk, &input);
1270        Ok(Self {
1271            proof,
1272            dpk,
1273            output,
1274            input,
1275        })
1276    }
1277
1278    /// Serialize the VrfOutput
1279    pub fn serialize(&self) -> Vec<u8> {
1280        // Note that we do not include the VRF output here - instead we rederive it
1281        //
1282        // The first two outputs are fixed length so the encoding here is unambigious
1283        let mut output = Vec::with_capacity(G1AFFINE_BYTES + G2AFFINE_BYTES + self.input.len());
1284        output.extend_from_slice(self.proof.serialize());
1285        output.extend_from_slice(&self.dpk.serialize());
1286        output.extend_from_slice(&self.input);
1287        output
1288    }
1289
1290    /// Deserialize and verify a VrfOutput
1291    ///
1292    /// Note this verifies the VrfOutput with respect to the derived public key
1293    /// and VRF input which are included in the struct. It is the responsibility
1294    /// of the application to examine the return value of [`VrfOutput::public_key`]
1295    /// and [`VrfOutput::input`] and ensure these values make sense in the context
1296    /// where this VRF is being used.
1297    pub fn deserialize(bytes: &[u8]) -> Result<Self, InvalidVrfOutput> {
1298        if bytes.len() < G1AFFINE_BYTES + G2AFFINE_BYTES {
1299            return Err(InvalidVrfOutput::UnexpectedLength);
1300        }
1301
1302        let proof = VetKey::deserialize(&bytes[0..G1AFFINE_BYTES])
1303            .map_err(|_| InvalidVrfOutput::InvalidData)?;
1304
1305        let dpk =
1306            DerivedPublicKey::deserialize(&bytes[G1AFFINE_BYTES..G1AFFINE_BYTES + G2AFFINE_BYTES])
1307                .map_err(|_| InvalidVrfOutput::InvalidData)?;
1308
1309        let input = bytes[G1AFFINE_BYTES + G2AFFINE_BYTES..].to_vec();
1310
1311        if !verify_bls_signature_pt(&dpk, &input, proof.point()) {
1312            return Err(InvalidVrfOutput::InvalidProof);
1313        }
1314
1315        let output = Self::compute_vrf_hash(&proof, &dpk, &input);
1316
1317        Ok(Self {
1318            proof,
1319            dpk,
1320            output,
1321            input,
1322        })
1323    }
1324
1325    /// Return the input that was used to create this VRF output
1326    pub fn input(&self) -> &[u8] {
1327        &self.input
1328    }
1329
1330    /// Return the key under which this VRF output was derived
1331    pub fn public_key(&self) -> &DerivedPublicKey {
1332        &self.dpk
1333    }
1334
1335    /// Return the VRF output
1336    ///
1337    /// This is a random-looking value which was provably generated by some party with
1338    /// access to the VRF secret key.
1339    ///
1340    /// If your application needs more than [`Self::VRF_BYTES`] of randomness, the easiest
1341    /// approach would be to derive a longer value using a KDF, for example using
1342    /// [`derive_symmetric_key`].
1343    ///
1344    /// Another option would be to seed a PRNG ([`ChaCha20Rng::from_seed`]) and then
1345    /// invoke the RNG to generate outputs as required.
1346    pub fn output(&self) -> &[u8; Self::VRF_BYTES] {
1347        &self.output
1348    }
1349}
1350
1351/// Verify an augmented BLS signature
1352///
1353/// Augmented BLS signatures include the public key as part of the input, and
1354/// "under the hood" a vetKey is an augmented BLS signature. This function allows
1355/// verifying, for example, that a vetKey used as a VRF output is in fact a valid
1356/// signature.
1357///
1358/// See <https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bls-signature#name-message-augmentation>
1359/// for more details on BLS message augmentation.
1360///
1361/// Returns true if and only if the provided signature is valid with respect to
1362/// the provided public key and input
1363pub fn verify_bls_signature(dpk: &DerivedPublicKey, input: &[u8], signature: &[u8]) -> bool {
1364    let signature: G1Affine = match <[u8; 48]>::try_from(signature) {
1365        Ok(bytes) => match G1Affine::from_compressed(&bytes).into_option() {
1366            Some(pt) => pt,
1367            None => return false,
1368        },
1369        Err(_) => return false,
1370    };
1371
1372    verify_bls_signature_pt(dpk, input, &signature)
1373}
1374
1375/// Verify an augmented BLS signature
1376///
1377/// Returns true if and only if the provided signature is valid with respect to
1378/// the provided public key and input
1379fn verify_bls_signature_pt(dpk: &DerivedPublicKey, input: &[u8], signature: &G1Affine) -> bool {
1380    if dpk.point.is_identity().into() {
1381        return false;
1382    }
1383
1384    let msg = augmented_hash_to_g1(&dpk.point, input);
1385    let dpk_prep = G2Prepared::from(dpk.point);
1386
1387    // Check that `e(sig, G2) == e(msg, dpk)` using a multipairing
1388
1389    use pairing::group::Group;
1390    let is_valid =
1391        gt_multipairing(&[(signature, &G2PREPARED_NEG_G), (&msg, &dpk_prep)]).is_identity();
1392    bool::from(is_valid)
1393}
1394
1395fn augmented_hash_to_g1(pk: &G2Affine, data: &[u8]) -> G1Affine {
1396    let domain_sep = b"BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_AUG_";
1397
1398    let mut signature_input = Vec::with_capacity(G2AFFINE_BYTES + data.len());
1399    signature_input.extend_from_slice(&pk.to_compressed());
1400    signature_input.extend_from_slice(data);
1401
1402    let pt = <G1Projective as HashToCurve<ExpandMsgXmd<sha2::Sha256>>>::hash_to_curve(
1403        signature_input,
1404        domain_sep,
1405    );
1406    G1Affine::from(pt)
1407}
1408
1409fn gt_multipairing(terms: &[(&G1Affine, &G2Prepared)]) -> Gt {
1410    ic_bls12_381::multi_miller_loop(terms).final_exponentiation()
1411}
1412
1413fn deserialize_g2(bytes: &[u8]) -> Result<G2Affine, String> {
1414    let bytes: &[u8; G2AFFINE_BYTES] = bytes
1415        .try_into()
1416        .map_err(|_| "Invalid length for G2".to_string())?;
1417
1418    let pt = G2Affine::from_compressed(bytes);
1419    if bool::from(pt.is_some()) {
1420        Ok(pt.unwrap())
1421    } else {
1422        Err("Invalid G2 elliptic curve point".to_string())
1423    }
1424}
1425
1426/// This module contains functions for calling the ICP management canister's `vetkd_derive_key` endpoint from within a canister.
1427pub mod management_canister {
1428    use ic_cdk::{call::CallResult, management_canister::VetKDPublicKeyArgs};
1429
1430    use crate::types::CanisterId;
1431
1432    use super::*;
1433
1434    /// Derives an unencrypted vetKey.
1435    ///
1436    /// Because the vetKey returned by this function is unencrypted, it is public to the canister and ICP nodes. Using this function is equivalent to decrypting the vetKey directly by the canister itself.
1437    /// Therefore, this function shall only be used if the vetKey is used as public information by the canister, e.g., when it is used as BLS signature, for timelock encryption, or for producing verifiable randomness.
1438    ///
1439    /// **Warning**: A vetKey produced by this function is *insecure* to use as a private key by a user.
1440    ///
1441    /// A public vetKey is derived by calling the ICP management canister's `vetkd_derive_key` endpoint with a **fixed public transport key** that produces an **unencrypted vetKey**.
1442    /// Therefore, this function is more efficient than actually retrieving the encrypted vetKey and calling [`EncryptedVetKey::decrypt_and_verify`].
1443    ///
1444    /// # Arguments
1445    /// * `input` - corresponds to `input` in `vetkd_derive_key`
1446    /// * `context` - corresponds to `context` in `vetkd_derive_key`
1447    /// * `key_id` - corresponds to `key_id` in `vetkd_derive_key`
1448    ///
1449    /// # Returns
1450    /// * `Ok(VetKey)` - The derived vetKey on success
1451    /// * `Err(DeriveUnencryptedVetkeyError)` - If derivation fails due to unsupported curve or canister call error
1452    async fn derive_unencrypted_vetkey(
1453        input: Vec<u8>,
1454        context: Vec<u8>,
1455        key_id: VetKDKeyId,
1456    ) -> Result<Vec<u8>, VetKDDeriveKeyCallError> {
1457        if key_id.curve != VetKDCurve::Bls12_381_G2 {
1458            return Err(VetKDDeriveKeyCallError::UnsupportedCurve);
1459        }
1460
1461        let request = VetKDDeriveKeyArgs {
1462            input,
1463            context,
1464            key_id,
1465            // Encryption with the G1 identity element produces unencrypted vetKeys
1466            transport_public_key: G1Affine::identity().to_compressed().to_vec(),
1467        };
1468
1469        let reply = ic_cdk::management_canister::vetkd_derive_key(&request)
1470            .await
1471            .map_err(VetKDDeriveKeyCallError::CallFailed)?;
1472
1473        if reply.encrypted_key.len() != EncryptedVetKey::BYTES {
1474            return Err(VetKDDeriveKeyCallError::InvalidReply);
1475        }
1476
1477        Ok(reply.encrypted_key
1478            [EncryptedVetKey::C3_OFFSET..EncryptedVetKey::C3_OFFSET + G1AFFINE_BYTES]
1479            .to_vec())
1480    }
1481
1482    #[derive(Debug)]
1483    /// Errors that can occur when deriving an unencrypted vetKey
1484    pub enum VetKDDeriveKeyCallError {
1485        /// The curve is currently not supported
1486        UnsupportedCurve,
1487        /// The canister call failed
1488        CallFailed(ic_cdk::management_canister::SignCallError),
1489        /// Invalid reply from the management canister
1490        InvalidReply,
1491    }
1492
1493    /// Creates a threshold BLS12-381 signature for the given `message`.
1494    ///
1495    /// The `context` parameter defines signer's identity.
1496    /// The returned signature can be verified with the public key retrieved via [`bls_public_key`] with the same `context` and `key_id`.
1497    /// Having the public key, message, and signature, we now can verify that the signature is valid.
1498    /// For that, we can call [`verify_bls_signature`] from this crate in Rust or `verifyBlsSignature` from the `@dfinity/vetkeys` package in TypeScript/JavaScript.
1499    ///
1500    /// This function will use `ic0_cost_vetkd_derive_key` to calculate the precise number of cycles to attach.
1501    ///
1502    /// # Arguments
1503    /// * `message` - the message to be signed
1504    /// * `context` - the identity of the signer
1505    /// * `key_id` - the key ID of the threshold key deployed on the Internet Computer
1506    ///
1507    /// # Returns
1508    /// * `Ok(Vec<u8>)` - The signature on success
1509    /// * `Err(VetKDDeriveKeyCallError)` - If derivation fails due to unsupported curve or canister call error
1510    pub async fn sign_with_bls(
1511        message: Vec<u8>,
1512        context: Vec<u8>,
1513        key_id: VetKDKeyId,
1514    ) -> Result<Vec<u8>, VetKDDeriveKeyCallError> {
1515        derive_unencrypted_vetkey(message, context, key_id).await
1516    }
1517
1518    /// Returns the public key of a threshold BLS12-381 key.
1519    /// Signatures produced with [`sign_with_bls`] are verifiable under a public key returned by this method iff the public key is for the correct `canister_id` and the same `context` and `key_id` was used.
1520    ///
1521    /// # Arguments
1522    /// * `canister_id` - the canister ID that the public key is computed for. If `canister_id` is `None`, it will default to the canister id of the caller.
1523    /// * `context` - the identity of the signer
1524    /// * `key_id` - the key ID of the threshold key deployed on the Internet Computer
1525    ///
1526    /// # Returns
1527    /// * `Ok(Vec<u8>)` - The public key on success
1528    /// * `Err(ic_cdk::call::Error)` - If the canister call fails
1529    pub async fn bls_public_key(
1530        canister_id: Option<CanisterId>,
1531        context: Vec<u8>,
1532        key_id: VetKDKeyId,
1533    ) -> CallResult<Vec<u8>> {
1534        ic_cdk::management_canister::vetkd_public_key(&VetKDPublicKeyArgs {
1535            canister_id,
1536            context,
1537            key_id,
1538        })
1539        .await
1540        .map(|r| r.public_key)
1541    }
1542
1543    /// Creates a VRF output for the provided input
1544    ///
1545    /// This function will use `ic0_cost_vetkd_derive_key` to calculate the precise number of cycles to attach.
1546    ///
1547    /// # Arguments
1548    /// * `context` - a string identifying the context in which this VRF output
1549    ///   will be used, for example the application
1550    /// * `input` - a value that should be unique to a particular situation
1551    /// * `key_id` - the key ID of the threshold key deployed on the Internet Computer
1552    ///
1553    /// # Examples
1554    ///
1555    /// Examples of possible `(input,context)` pairs in various VRF settings
1556    ///
1557    /// * Lottery: `context` "My Verifiably Random Lottery v1", `input` "Drawing Jan 1, 2028",
1558    ///   "Drawing Jan 2, 2028", ...
1559    /// * Leader Election: "FooProtocol Random Leader Election", `input` "Leader Election #1",
1560    ///   "Leader Election #2", ...
1561    ///
1562    /// # Returns
1563    /// * `Ok(VrfOutput)` - The VRF output structure
1564    /// * `Err(VetKDDeriveKeyCallError)` - If derivation fails due to unsupported curve or canister call error
1565    pub async fn compute_vrf(
1566        input: Vec<u8>,
1567        context: Vec<u8>,
1568        key_id: VetKDKeyId,
1569    ) -> Result<VrfOutput, VetKDDeriveKeyCallError> {
1570        let vetkey_bytes =
1571            derive_unencrypted_vetkey(input.clone(), context.clone(), key_id.clone())
1572                .await
1573                .map_err(|_| VetKDDeriveKeyCallError::InvalidReply)?;
1574
1575        let vetkey = VetKey::deserialize(&vetkey_bytes)
1576            .map_err(|_| VetKDDeriveKeyCallError::InvalidReply)?;
1577        let canister_id = ic_cdk::api::canister_self();
1578
1579        let dpk = match MasterPublicKey::for_mainnet_key(&key_id) {
1580            Some(mk) => mk
1581                .derive_canister_key(canister_id.as_slice())
1582                .derive_sub_key(&context),
1583            None => {
1584                // If the key id is not known we must instead perform an online query
1585                // for the relevant key
1586                let dpk_bytes =
1587                    ic_cdk::management_canister::vetkd_public_key(&VetKDPublicKeyArgs {
1588                        canister_id: Some(canister_id),
1589                        context,
1590                        key_id,
1591                    })
1592                    .await
1593                    .map_err(|_| VetKDDeriveKeyCallError::InvalidReply)?;
1594
1595                DerivedPublicKey::deserialize(&dpk_bytes.public_key)
1596                    .map_err(|_| VetKDDeriveKeyCallError::InvalidReply)?
1597            }
1598        };
1599
1600        VrfOutput::create(vetkey, input, dpk).map_err(|_| VetKDDeriveKeyCallError::InvalidReply)
1601    }
1602}