oprf/coprf/coprf_setup.rs
1#![warn(missing_docs)]
2//! ## E.1. CoPRF Setup
3//!
4//! CoPRFs are defined in a multi-key setting, such that CoPRF evaluation
5//! keys are derived from a master secret.
6
7use hacspec_lib::Randomness;
8use p256::P256Scalar;
9
10use crate::{
11 protocol::configuration::{create_context_string, ModeID},
12 Error,
13};
14
15/// Domain separator for scalar sampling during CoPRF key generation.
16const DST_KEYGEN: &[u8] = b"CoPRF-KeyGeneration";
17
18/// As blinding is performed by Elgamal encryption, the blinding public
19/// key is an Elgamal encryption key.
20pub type BlindingPublicKey = elgamal::EncryptionKey;
21/// As unblinding is performed by Elgamal decryption, the unblinding
22/// private key is an Elgamal decryption key.
23pub type BlindingPrivateKey = elgamal::DecryptionKey;
24
25/// The master secret for generating coPRF keys. It is fixed to a
26/// length 32 bytes since that is the number of bytes necessary as an
27/// input for HPKE-style key derivation when targeting scalars in
28/// P256. Per the HPKE RFC [RFC9180] it is crucial that a minimum of
29/// `Nsk` bytes of entropy is provided to the key derivation
30/// algorithm, where `Nsk` is the number of bytes to represent a valid
31/// private key, i.e. a P256 scalar in our case.
32pub type CoPRFMasterSecret = Vec<u8>;
33const COPRF_MSK_BYTES: usize = 32;
34
35/// A coPRF evaluation key is identified by a bytestring of arbitrary
36/// length.
37pub type CoPRFKeyID = Vec<u8>;
38/// A coPRF evaluation key is a scalar for the base group of the scheme,
39/// in our case P256.
40pub type CoPRFKey = P256Scalar;
41
42/// The coPRF requester requires the blinding public key of the intended
43/// receiver of the PRF output.
44pub struct CoPRFRequesterContext {
45 pub(crate) _string: Vec<u8>,
46 pub(crate) _bpk: BlindingPublicKey,
47}
48
49/// The coPRF evaluator holds the coPRF master secret.
50pub struct CoPRFEvaluatorContext {
51 pub(crate) msk: CoPRFMasterSecret,
52}
53
54/// The coPRF receiver needs an unblinding private key in order to obtain
55/// the final coPRF output from the blinded evaluation result.
56pub struct CoPRFReceiverContext {
57 pub(crate) bsk: BlindingPrivateKey,
58 pub(crate) bpk: BlindingPublicKey,
59}
60
61impl CoPRFReceiverContext {
62 /// Retrieves the receivers blinding public key. This is needed by the
63 /// requester to perform initial blinding and by the Evaluator to
64 /// rerandomize to evaluation result.
65 pub fn get_bpk(&self) -> BlindingPublicKey {
66 self.bpk
67 }
68}
69impl CoPRFRequesterContext {
70 /// ### E.1.1. Requester Setup
71 /// The requesting party requires the blinding public key of the receiving
72 /// party on whose behalf PRF evaluation queries should be carried out.
73 pub fn new(identifier: &[u8], bpk: BlindingPublicKey) -> Self {
74 CoPRFRequesterContext {
75 _string: create_context_string(ModeID::modecoPRF, identifier),
76 _bpk: bpk,
77 }
78 }
79}
80
81impl CoPRFEvaluatorContext {
82 /// ### E.1.2. Evaluator Setup
83 /// The coPRF evaluator holds the master secret as well as any PRF
84 /// evaluation keys derived from it.
85 pub fn new(randomness: &mut Randomness) -> Result<Self, Error> {
86 let msk = randomness.bytes(COPRF_MSK_BYTES)?.to_vec();
87 Ok(CoPRFEvaluatorContext { msk })
88 }
89}
90
91impl CoPRFReceiverContext {
92 /// ### E.1.3. Receiver Setup
93 /// The coPRF receiver holds a pair of corresponding blinding and
94 /// unblinding keys.
95 pub fn new(randomness: &mut Randomness) -> Self {
96 let (bsk, bpk) = generate_blinding_key_pair(randomness).unwrap();
97 CoPRFReceiverContext { bsk, bpk }
98 }
99}
100
101/// ### E.1.4. Blinding Key Generation
102/// Following the instantiation presented by [Lehmann], blinding is
103/// implemented using a rerandomizable homomorphic encryption scheme, in
104/// this case the Elgamal public key encryption scheme.
105///
106/// In this intance, blinding and unblinding correspond to encryption and
107/// decryption using the encryption scheme, hence blinding key generation
108/// is the key generation procedure for the encryption scheme.
109///
110fn generate_blinding_key_pair(
111 uniform_bytes: &mut Randomness,
112) -> Result<(BlindingPrivateKey, BlindingPublicKey), Error> {
113 let (bsk, bpk) = elgamal::generate_keys(uniform_bytes)?;
114 Ok((bsk, bpk))
115}
116
117/// ### E.1.5. Evaluation Key Derivation
118/// [Lehman] recommends a key derivation procedure using an underlying PRF
119/// which maps from bitstrings to a finite field, such that the field is
120/// compatible with the homomorphism afforded by the encryption scheme.
121///
122/// Concretely in our case, PRF evaluation keys should be scalars in
123/// P256. To achieve this, we use the rejection sampling method outlined in [RFC9180].
124pub fn derive_key(context: &CoPRFEvaluatorContext, key_id: &[u8]) -> Result<CoPRFKey, Error> {
125 let mut key_material = context.msk.to_vec();
126 key_material.extend_from_slice(key_id);
127
128 let random_bytes = sha256::hash(&key_material);
129
130 p256::random_scalar(&mut Randomness::new(random_bytes.to_vec()), DST_KEYGEN)
131 .map_err(|e| e.into())
132}