1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#![warn(missing_docs)]
//! ## E.1. CoPRF Setup
//!
//! CoPRFs are defined in a multi-key setting, such that CoPRF evaluation
//! keys are derived from a master secret.

use hacspec_lib::Randomness;
use p256::P256Scalar;

use crate::{
    protocol::configuration::{create_context_string, ModeID},
    Error,
};

/// Domain separator for scalar sampling during CoPRF key generation.
const DST_KEYGEN: &[u8] = b"CoPRF-KeyGeneration";

/// As blinding is performed by Elgamal encryption, the blinding public
/// key is an Elgamal encryption key.
pub type BlindingPublicKey = elgamal::EncryptionKey;
/// As unblinding is performed by Elgamal decryption, the unblinding
/// private key is an Elgamal decryption key.
pub type BlindingPrivateKey = elgamal::DecryptionKey;

/// The master secret for generating coPRF keys. It is fixed to a
/// length 32 bytes since that is the number of bytes necessary as an
/// input for HPKE-style key derivation when targeting scalars in
/// P256.  Per the HPKE RFC [RFC9180] it is crucial that a minimum of
/// `Nsk` bytes of entropy is provided to the key derivation
/// algorithm, where `Nsk` is the number of bytes to represent a valid
/// private key, i.e. a P256 scalar in our case.
pub type CoPRFMasterSecret = Vec<u8>;
const COPRF_MSK_BYTES: usize = 32;

/// A coPRF evaluation key is identified by a bytestring of arbitrary
/// length.
pub type CoPRFKeyID = Vec<u8>;
/// A coPRF evaluation key is a scalar for the base group of the scheme,
/// in our case P256.
pub type CoPRFKey = P256Scalar;

/// The coPRF requester requires the blinding public key of the intended
/// receiver of the PRF output.
pub struct CoPRFRequesterContext {
    pub(crate) _string: Vec<u8>,
    pub(crate) _bpk: BlindingPublicKey,
}

/// The coPRF evaluator holds the coPRF master secret.
pub struct CoPRFEvaluatorContext {
    pub(crate) msk: CoPRFMasterSecret,
}

/// The coPRF receiver needs an unblinding private key in order to obtain
/// the final coPRF output from the blinded evaluation result.
pub struct CoPRFReceiverContext {
    pub(crate) bsk: BlindingPrivateKey,
    pub(crate) bpk: BlindingPublicKey,
}

impl CoPRFReceiverContext {
    /// Retrieves the receivers blinding public key. This is needed by the
    /// requester to perform initial blinding and by the Evaluator to
    /// rerandomize to evaluation result.
    pub fn get_bpk(&self) -> BlindingPublicKey {
        self.bpk
    }
}
impl CoPRFRequesterContext {
    /// ### E.1.1. Requester Setup
    /// The requesting party requires the blinding public key of the receiving
    /// party on whose behalf PRF evaluation queries should be carried out.
    pub fn new(identifier: &[u8], bpk: BlindingPublicKey) -> Self {
        CoPRFRequesterContext {
            _string: create_context_string(ModeID::modecoPRF, identifier),
            _bpk: bpk,
        }
    }
}

impl CoPRFEvaluatorContext {
    /// ### E.1.2. Evaluator Setup
    /// The coPRF evaluator holds the master secret as well as any PRF
    /// evaluation keys derived from it.
    pub fn new(randomness: &mut Randomness) -> Result<Self, Error> {
        let msk = randomness.bytes(COPRF_MSK_BYTES)?.to_vec();
        Ok(CoPRFEvaluatorContext { msk })
    }
}

impl CoPRFReceiverContext {
    /// ### E.1.3. Receiver Setup
    /// The coPRF receiver holds a pair of corresponding blinding and
    /// unblinding keys.
    pub fn new(randomness: &mut Randomness) -> Self {
        let (bsk, bpk) = generate_blinding_key_pair(randomness).unwrap();
        CoPRFReceiverContext { bsk, bpk }
    }
}

/// ### E.1.4. Blinding Key Generation
/// Following the instantiation presented by [Lehmann], blinding is
/// implemented using a rerandomizable homomorphic encryption scheme, in
/// this case the Elgamal public key encryption scheme.
///
/// In this intance, blinding and unblinding correspond to encryption and
/// decryption using the encryption scheme, hence blinding key generation
/// is the key generation procedure for the encryption scheme.
///
fn generate_blinding_key_pair(
    uniform_bytes: &mut Randomness,
) -> Result<(BlindingPrivateKey, BlindingPublicKey), Error> {
    let (bsk, bpk) = elgamal::generate_keys(uniform_bytes)?;
    Ok((bsk, bpk))
}

/// ### E.1.5. Evaluation Key Derivation
/// [Lehman] recommends a key derivation procedure using an underlying PRF
/// which maps from bitstrings to a finite field, such that the field is
/// compatible with the homomorphism afforded by the encryption scheme.
///
/// Concretely in our case, PRF evaluation keys should be scalars in
/// P256. To achieve this, we use the rejection sampling method outlined in [RFC9180].
pub fn derive_key(context: &CoPRFEvaluatorContext, key_id: &[u8]) -> Result<CoPRFKey, Error> {
    let mut key_material = context.msk.to_vec();
    key_material.extend_from_slice(key_id);

    let random_bytes = sha256::hash(&key_material);

    p256::random_scalar(&mut Randomness::new(random_bytes.to_vec()), DST_KEYGEN)
        .map_err(|e| e.into())
}