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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
//! # Setup
use hacspec_lib::Randomness;
use libcrux::hpke::{
    kem::{GenerateKeyPair, Nsk},
    HPKEConfig,
};
use oprf::coprf::{
    coprf_online,
    coprf_setup::{BlindingPublicKey, CoPRFEvaluatorContext, CoPRFReceiverContext},
};
use p256::P256Point;

use crate::{
    data_types::{BlindedPseudonymizedHandle, FinalizedPseudonym},
    error::Error,
};

pub struct ConverterContext {
    pub(crate) coprf_context: CoPRFEvaluatorContext,
}

/// A data store's private decryption key.
pub struct StoreDecryptionKey(pub(crate) Vec<u8>);

/// A data store's public encryption key.
#[derive(Clone)]
pub struct StoreEncryptionKey(pub(crate) Vec<u8>);

pub struct StoreContext {
    coprf_receiver_context: CoPRFReceiverContext,
    pub(crate) dk: StoreDecryptionKey,
    ek: StoreEncryptionKey,
    k_prp: [u8; 32],
}

pub type LakeContext = StoreContext;
pub type ProcessorContext = StoreContext;

impl ConverterContext {
    /// ## Converter Setup
    /// On setup the converter initializes a coPRF evaluator context.
    ///
    /// ``` text
    /// Inputs:
    ///     msk: Nmsk uniformly random bytes
    ///
    /// Output:
    ///     coprf_evaluator_context: coPRFEvaluatorContext
    ///
    /// fn setup_converter_context(msk) -> ConverterContext:
    ///     return coPRFEvaluatorContext::new(msk)
    /// ```
    pub fn setup(randomness: &mut Randomness) -> Result<Self, Error> {
        Ok(ConverterContext {
            coprf_context: CoPRFEvaluatorContext::new(randomness)?,
        })
    }
}

impl StoreContext {
    /// ## Data Store Setup
    /// On setup, a data store initializes a coPRFReceiverContext, derives a
    /// pair of encryption and decryption keys for the RPKE as well as a
    /// private PRP key.
    ///
    /// ``` text
    /// Inputs:
    ///     randomness: (NcoPRFReceiver + NRPKEKeyGen + NPRP) uniformly random bytes
    ///
    /// Outputs:
    ///     coprf_receiver_context: CoPRFReceiverContext
    ///     ek: RPKE.EncryptionKey
    ///     dk: RPKE.DecryptionKey
    ///     k_prp: PRP.PRPKey
    ///
    /// fn setup(randomness) -> StoreContext:
    ///     let coprf_receiver_context =
    ///     CoPRFReceiverContext::new(randomness[NcoPRFReceiver]);
    ///     let (ek, dk) = RPKE.generate_keys(randomness[NRPKEKeyGen]);
    ///     let k_prp = PRP.KeyGen(randomness[NPRP]);
    ///     StoreContext{
    ///       coprf_receiver_context,
    ///       ek,
    ///       dk,
    ///       k_prp
    ///     }
    /// ```
    pub fn setup(randomness: &mut Randomness) -> Result<Self, Error> {
        let receiver_context = CoPRFReceiverContext::new(randomness);

        let (dk, ek) = generate_store_keys(randomness)?;

        let k_prp = randomness.bytes(32)?.try_into()?;

        Ok(Self {
            coprf_receiver_context: receiver_context,
            dk,
            ek,
            k_prp,
        })
    }

    /// Given a store context generated as above, the following methods are
    /// available:
    ///
    /// - Retrieve store public keys for encryption and coPRF blinding.
    /// ``` text
    /// Input:
    ///     context: StoreContext
    /// Output:
    ///     ek: RPKE.EncryptionKey
    ///     bpk: CoPRF.BlindingPublicKey
    ///
    /// fn public_keys(context):
    ///     let ek = context.ek;
    ///     let bpk = context.coprf_receiver_context.public_key()
    ///     return (ek, bpk);
    /// ```
    pub fn public_keys(&self) -> (StoreEncryptionKey, BlindingPublicKey) {
        (self.ek.clone(), self.coprf_receiver_context.get_bpk())
    }

    /// - Finalize Pseudonym: As part of the finalization of a split or join
    ///   conversion the raw pseudonyms that are the unblinded result of coPRF
    ///   evaluation are further hardened by application of a PRP.
    ///
    /// ``` text
    /// Input:
    ///     context: StoreContext
    ///     blind_pseudonym: CoPRFBlindOutput
    /// Output:
    ///     pseudonym: Pseudonym
    ///
    /// fn finalize_pseudonym(context, blind_pseudonym):
    ///     let raw_pseudonym =
    ///     context.coprf_receiver_context.finalize(blind_pseudonym);
    ///     return PRP.eval(context.k_prp, raw_pseudonym)
    /// ```
    pub fn finalize_pseudonym(
        &self,
        blind_pseudonym: BlindedPseudonymizedHandle,
    ) -> Result<FinalizedPseudonym, Error> {
        let raw_pseudonym =
            coprf_online::finalize(&self.coprf_receiver_context, blind_pseudonym.0)?;
        Ok(FinalizedPseudonym(prp::prp(
            raw_pseudonym.raw_bytes(),
            &self.k_prp,
        )))
    }

    /// - Recover Raw Pseudonym: In preparation of a join conversion, the raw
    ///   pseudonyms, i.e. coPRF outputs must be recovered from the hardened
    ///   pseudonyms before they can be sent to the converter for blind
    ///   conversion.
    ///
    ///   ``` text
    ///   Inputs:
    ///       context: StoreContext
    ///       pseudonym: Pseudonym
    ///
    ///   Output:
    ///       raw_pseudonym: CoPRFOutput
    ///
    ///   fn recover_raw_pseudonym(context, pseudonym):
    ///       return PRP.invert(context.k_prp, pseudonym)
    ///   ```
    pub fn recover_raw_pseudonym(&self, pseudonym: FinalizedPseudonym) -> Result<P256Point, Error> {
        P256Point::from_raw_bytes(prp::prp(pseudonym.0, &self.k_prp)).map_err(|e| e.into())
    }
}

fn generate_store_keys(
    randomness: &mut Randomness,
) -> Result<(StoreDecryptionKey, StoreEncryptionKey), Error> {
    let HPKEConfig(_, kem, _, _) = crate::HPKE_CONF;
    let (hpke_sk, hpke_pk) = GenerateKeyPair(kem, randomness.bytes(Nsk(kem)).unwrap().to_vec())?;
    Ok((StoreDecryptionKey(hpke_sk), StoreEncryptionKey(hpke_pk)))
}