challenge_bypass_ristretto/
oprf.rs

1use core::fmt::Debug;
2
3use curve25519_dalek::constants;
4use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};
5use curve25519_dalek::scalar::Scalar;
6use digest::generic_array::typenum::U64;
7use digest::{Digest, KeyInit};
8use hmac::digest::generic_array::GenericArray;
9use hmac::Mac;
10use rand::{CryptoRng, Rng};
11use subtle::{Choice, ConstantTimeEq};
12use zeroize::Zeroize;
13
14use crate::errors::{InternalError, TokenError};
15
16/// The length of a `TokenPreimage`, in bytes.
17pub const TOKEN_PREIMAGE_LENGTH: usize = 64;
18/// The length of a `Token`, in bytes.
19pub const TOKEN_LENGTH: usize = 96;
20/// The length of a `BlindedToken`, in bytes.
21pub const BLINDED_TOKEN_LENGTH: usize = 32;
22/// The length of a `PublicKey`, in bytes.
23pub const PUBLIC_KEY_LENGTH: usize = 32;
24/// The length of a `SigningKey`, in bytes.
25pub const SIGNING_KEY_LENGTH: usize = 32;
26/// The length of a `SignedToken`, in bytes.
27pub const SIGNED_TOKEN_LENGTH: usize = 32;
28/// The length of a `UnblindedToken`, in bytes.
29pub const UNBLINDED_TOKEN_LENGTH: usize = 96;
30/// The length of a `VerificationSignature`, in bytes.
31pub const VERIFICATION_SIGNATURE_LENGTH: usize = 64;
32
33/// A `TokenPreimage` is a slice of bytes which can be hashed to a `RistrettoPoint`.
34///
35/// The hash function must ensure the discrete log with respect to other points is unknown.
36/// In this construction `RistrettoPoint::from_uniform_bytes` is used as the hash function.
37#[cfg_attr(not(feature = "cbindgen"), repr(C))]
38#[derive(Copy, Clone)]
39pub struct TokenPreimage([u8; TOKEN_PREIMAGE_LENGTH]);
40
41impl PartialEq for TokenPreimage {
42    fn eq(&self, other: &TokenPreimage) -> bool {
43        self.0[..] == other.0[..]
44    }
45}
46
47#[cfg(any(test, feature = "base64"))]
48impl_base64!(TokenPreimage);
49
50#[cfg(feature = "serde")]
51impl_serde!(TokenPreimage);
52
53impl Debug for TokenPreimage {
54    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
55        write!(f, "TokenPreimage: {:?}", &self.0[..])
56    }
57}
58
59#[allow(non_snake_case)]
60impl TokenPreimage {
61    pub(crate) fn T(&self) -> RistrettoPoint {
62        RistrettoPoint::from_uniform_bytes(&self.0)
63    }
64
65    /// Convert this `TokenPreimage` to a byte array.
66    pub fn to_bytes(&self) -> [u8; TOKEN_PREIMAGE_LENGTH] {
67        self.0
68    }
69
70    fn bytes_length_error() -> TokenError {
71        TokenError(InternalError::BytesLengthError {
72            name: "TokenPreimage",
73            length: TOKEN_PREIMAGE_LENGTH,
74        })
75    }
76
77    /// Construct a `TokenPreimage` from a slice of bytes.
78    pub fn from_bytes(bytes: &[u8]) -> Result<TokenPreimage, TokenError> {
79        if bytes.len() != TOKEN_PREIMAGE_LENGTH {
80            return Err(TokenPreimage::bytes_length_error());
81        }
82
83        let mut bits: [u8; TOKEN_PREIMAGE_LENGTH] = [0u8; TOKEN_PREIMAGE_LENGTH];
84        bits.copy_from_slice(bytes);
85        Ok(TokenPreimage(bits))
86    }
87}
88
89/// A `Token` consists of a randomly chosen preimage and blinding factor.
90///
91/// Since a token includes the blinding factor it should be treated
92/// as a client secret and NEVER revealed to the server.
93#[cfg_attr(not(feature = "cbindgen"), repr(C))]
94#[derive(Debug, Clone)]
95pub struct Token {
96    /// `t` is a `TokenPreimage`
97    pub(crate) t: TokenPreimage,
98    /// `r` is a `Scalar` which is the blinding factor
99    r: Scalar,
100}
101
102/// Overwrite the token blinding factor with null when it goes out of scope.
103impl Drop for Token {
104    fn drop(&mut self) {
105        self.r.zeroize();
106    }
107}
108
109#[cfg(any(test, feature = "base64"))]
110impl_base64!(Token);
111
112#[cfg(feature = "serde")]
113impl_serde!(Token);
114
115#[allow(non_snake_case)]
116impl Token {
117    /// Generates a new random `Token` using the provided random number generator.
118    pub fn random<D, T>(rng: &mut T) -> Self
119    where
120        D: Digest<OutputSize = U64> + Default,
121        T: Rng + CryptoRng,
122    {
123        let mut seed = [0u8; 64];
124        rng.fill(&mut seed);
125        let blinding_scalar = Scalar::random(rng);
126        Self::hash_from_bytes_with_blind::<D>(&seed, blinding_scalar)
127    }
128
129    /// Creates a new `Token`, using hashing to derive a `TokenPreimage` and the specified blind
130    pub(crate) fn hash_from_bytes_with_blind<D>(bytes: &[u8], blinding_scalar: Scalar) -> Self
131    where
132        D: Digest<OutputSize = U64> + Default,
133    {
134        let mut hash = D::default();
135        let mut seed = [0u8; 64];
136        hash.update(bytes);
137        seed.copy_from_slice(hash.finalize().as_slice());
138
139        Token {
140            t: TokenPreimage(seed),
141            r: blinding_scalar,
142        }
143    }
144
145    /// Creates a new `Token`, using hashing to derive a `TokenPreimage` and a random blind
146    pub fn hash_from_bytes<D, T>(rng: &mut T, bytes: &[u8]) -> Self
147    where
148        D: Digest<OutputSize = U64> + Default,
149        T: Rng + CryptoRng,
150    {
151        let blinding_scalar = Scalar::random(rng);
152        Self::hash_from_bytes_with_blind::<D>(bytes, blinding_scalar)
153    }
154
155    /// Blinds the `Token`, returning a `BlindedToken` to be sent to the server.
156    pub fn blind(&self) -> BlindedToken {
157        BlindedToken((self.r * self.t.T()).compress())
158    }
159
160    /// Using the blinding factor of the original `Token`, unblind a `SignedToken`
161    /// returned from the server.
162    ///
163    /// Returns a `TokenError` if the `SignedToken` point is not valid.
164    pub(crate) fn unblind(&self, Q: &SignedToken) -> Result<UnblindedToken, TokenError> {
165        Ok(UnblindedToken {
166            t: self.t,
167            W: (self.r.invert()
168                * Q.0
169                    .decompress()
170                    .ok_or(TokenError(InternalError::PointDecompressionError))?)
171            .compress(),
172        })
173    }
174
175    /// Convert this `Token` to a byte array.
176    pub fn to_bytes(&self) -> [u8; TOKEN_LENGTH] {
177        let mut token_bytes: [u8; TOKEN_LENGTH] = [0u8; TOKEN_LENGTH];
178
179        token_bytes[..TOKEN_PREIMAGE_LENGTH].copy_from_slice(&self.t.to_bytes());
180        token_bytes[TOKEN_PREIMAGE_LENGTH..].copy_from_slice(&self.r.to_bytes());
181        token_bytes
182    }
183
184    fn bytes_length_error() -> TokenError {
185        TokenError(InternalError::BytesLengthError {
186            name: "Token",
187            length: TOKEN_LENGTH,
188        })
189    }
190
191    /// Construct a `Token` from a slice of bytes.
192    pub fn from_bytes(bytes: &[u8]) -> Result<Token, TokenError> {
193        if bytes.len() != TOKEN_LENGTH {
194            return Err(Token::bytes_length_error());
195        }
196
197        let preimage = TokenPreimage::from_bytes(&bytes[..TOKEN_PREIMAGE_LENGTH])?;
198
199        let mut blinding_factor_bits: [u8; 32] = [0u8; 32];
200        blinding_factor_bits.copy_from_slice(&bytes[TOKEN_PREIMAGE_LENGTH..]);
201        let blinding_factor = Option::from(Scalar::from_canonical_bytes(blinding_factor_bits))
202            .ok_or(TokenError(InternalError::ScalarFormatError))?;
203
204        Ok(Token {
205            t: preimage,
206            r: blinding_factor,
207        })
208    }
209}
210
211/// A `BlindedToken` is sent to the server for signing.
212///
213/// It is the result of the scalar multiplication of the point derived from the token
214/// preimage with the blinding factor.
215///
216/// \\(P = T^r = H_1(t)^r\\)
217#[cfg_attr(not(feature = "cbindgen"), repr(C))]
218#[derive(Copy, Clone, Debug)]
219pub struct BlindedToken(pub(crate) CompressedRistretto);
220
221#[cfg(any(test, feature = "base64"))]
222impl_base64!(BlindedToken);
223
224#[cfg(feature = "serde")]
225impl_serde!(BlindedToken);
226
227impl BlindedToken {
228    /// Convert this `BlindedToken` to a byte array.
229    pub fn to_bytes(&self) -> [u8; BLINDED_TOKEN_LENGTH] {
230        self.0.to_bytes()
231    }
232
233    fn bytes_length_error() -> TokenError {
234        TokenError(InternalError::BytesLengthError {
235            name: "BlindedToken",
236            length: BLINDED_TOKEN_LENGTH,
237        })
238    }
239
240    /// Construct a `BlindedToken` from a slice of bytes.
241    pub fn from_bytes(bytes: &[u8]) -> Result<BlindedToken, TokenError> {
242        if bytes.len() != BLINDED_TOKEN_LENGTH {
243            return Err(BlindedToken::bytes_length_error());
244        }
245
246        let mut bits: [u8; 32] = [0u8; 32];
247        bits.copy_from_slice(&bytes[..32]);
248        Ok(BlindedToken(CompressedRistretto(bits)))
249    }
250}
251
252/// A `PublicKey` is a committment by the server to a particular `SigningKey`.
253///
254/// \\(Y = X^k\\)
255#[cfg_attr(not(feature = "cbindgen"), repr(C))]
256#[derive(Copy, Clone, Debug)]
257#[allow(non_snake_case)]
258pub struct PublicKey(pub(crate) CompressedRistretto);
259
260#[cfg(any(test, feature = "base64"))]
261impl_base64!(PublicKey);
262
263#[cfg(feature = "serde")]
264impl_serde!(PublicKey);
265
266impl PublicKey {
267    /// Convert this `PublicKey` to a byte array.
268    pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_LENGTH] {
269        self.0.to_bytes()
270    }
271
272    fn bytes_length_error() -> TokenError {
273        TokenError(InternalError::BytesLengthError {
274            name: "PublicKey",
275            length: PUBLIC_KEY_LENGTH,
276        })
277    }
278
279    /// Construct a `PublicKey` from a slice of bytes.
280    pub fn from_bytes(bytes: &[u8]) -> Result<PublicKey, TokenError> {
281        if bytes.len() != PUBLIC_KEY_LENGTH {
282            return Err(PublicKey::bytes_length_error());
283        }
284
285        let mut bits: [u8; 32] = [0u8; 32];
286        bits.copy_from_slice(&bytes[..32]);
287
288        Ok(PublicKey(CompressedRistretto(bits)))
289    }
290}
291
292/// A `SigningKey` is used to sign a `BlindedToken` and verify an `UnblindedToken`.
293///
294/// This is a server secret and should NEVER be revealed to the client.
295#[cfg_attr(not(feature = "cbindgen"), repr(C))]
296#[derive(Debug, Clone)]
297pub struct SigningKey {
298    /// A `PublicKey` showing a committment to this particular key
299    pub public_key: PublicKey,
300    /// `k` is the actual key
301    pub(crate) k: Scalar,
302}
303
304#[cfg(any(test, feature = "base64"))]
305impl_base64!(SigningKey);
306
307#[cfg(feature = "serde")]
308impl_serde!(SigningKey);
309
310/// Overwrite signing key with null when it goes out of scope.
311impl Drop for SigningKey {
312    fn drop(&mut self) {
313        self.k.zeroize();
314    }
315}
316
317#[allow(non_snake_case)]
318impl SigningKey {
319    /// Generates a new random `SigningKey` using the provided random number generator.
320    pub fn random<T: Rng + CryptoRng>(rng: &mut T) -> Self {
321        let k = Scalar::random(rng);
322        let Y = k * constants::RISTRETTO_BASEPOINT_POINT;
323        SigningKey {
324            k,
325            public_key: PublicKey(Y.compress()),
326        }
327    }
328
329    /// Signs the provided `BlindedToken`
330    ///
331    /// Returns None if the `BlindedToken` point is not valid.
332    pub fn sign(&self, P: &BlindedToken) -> Result<SignedToken, TokenError> {
333        Ok(SignedToken(
334            (self.k
335                * P.0
336                    .decompress()
337                    .ok_or(TokenError(InternalError::PointDecompressionError))?)
338            .compress(),
339        ))
340    }
341
342    /// Rederives an `UnblindedToken` via the token preimage of the provided `UnblindedToken`
343    ///
344    /// W' = T^k = H_1(t)^k
345    pub fn rederive_unblinded_token(&self, t: &TokenPreimage) -> UnblindedToken {
346        UnblindedToken {
347            t: *t,
348            W: (self.k * t.T()).compress(),
349        }
350    }
351
352    /// Convert this `SigningKey` to a byte array.
353    pub fn to_bytes(&self) -> [u8; SIGNING_KEY_LENGTH] {
354        self.k.to_bytes()
355    }
356
357    fn bytes_length_error() -> TokenError {
358        TokenError(InternalError::BytesLengthError {
359            name: "SigningKey",
360            length: SIGNING_KEY_LENGTH,
361        })
362    }
363
364    /// Construct a `SigningKey` from a slice of bytes.
365    pub fn from_bytes(bytes: &[u8]) -> Result<SigningKey, TokenError> {
366        if bytes.len() != SIGNING_KEY_LENGTH {
367            return Err(SigningKey::bytes_length_error());
368        }
369
370        let mut bits: [u8; 32] = [0u8; 32];
371        bits.copy_from_slice(&bytes[..32]);
372        let k = Option::from(Scalar::from_canonical_bytes(bits))
373            .ok_or(TokenError(InternalError::ScalarFormatError))?;
374
375        let Y: RistrettoPoint = k * constants::RISTRETTO_BASEPOINT_POINT;
376
377        Ok(SigningKey {
378            public_key: PublicKey(Y.compress()),
379            k,
380        })
381    }
382}
383
384/// A `SignedToken` is the result of signing a `BlindedToken`.
385///
386/// \\(Q = P^k = (T^r)^k\\)
387#[cfg_attr(not(feature = "cbindgen"), repr(C))]
388#[derive(Copy, Clone, Debug)]
389pub struct SignedToken(pub(crate) CompressedRistretto);
390
391#[cfg(any(test, feature = "base64"))]
392impl_base64!(SignedToken);
393
394#[cfg(feature = "serde")]
395impl_serde!(SignedToken);
396
397impl SignedToken {
398    /// Convert this `SignedToken` to a byte array.
399    pub fn to_bytes(&self) -> [u8; SIGNED_TOKEN_LENGTH] {
400        self.0.to_bytes()
401    }
402
403    fn bytes_length_error() -> TokenError {
404        TokenError(InternalError::BytesLengthError {
405            name: "SignedToken",
406            length: SIGNED_TOKEN_LENGTH,
407        })
408    }
409
410    /// Construct a `SignedToken` from a slice of bytes.
411    pub fn from_bytes(bytes: &[u8]) -> Result<SignedToken, TokenError> {
412        if bytes.len() != SIGNED_TOKEN_LENGTH {
413            return Err(SignedToken::bytes_length_error());
414        }
415
416        let mut bits: [u8; 32] = [0u8; 32];
417        bits.copy_from_slice(&bytes[..32]);
418        Ok(SignedToken(CompressedRistretto(bits)))
419    }
420}
421
422/// An `UnblindedToken` is the result of unblinding a `SignedToken`.
423///
424/// While both the client and server both "know" this value,
425/// it should nevertheless not be sent between the two.
426#[cfg_attr(not(feature = "cbindgen"), repr(C))]
427#[allow(non_snake_case)]
428#[derive(Debug, Clone)]
429pub struct UnblindedToken {
430    /// `t` is the `TokenPreimage`
431    pub t: TokenPreimage,
432    /// `W` is the unblinded signed `CompressedRistretto` point
433    ///
434    /// \\(W = Q^{1/r} = P^{k(1/r)} = T^{rk(1/r)} = T^k\\)
435    W: CompressedRistretto,
436}
437
438#[cfg(any(test, feature = "base64"))]
439impl_base64!(UnblindedToken);
440
441#[cfg(feature = "serde")]
442impl_serde!(UnblindedToken);
443
444impl UnblindedToken {
445    /// Derive the `VerificationKey` for this particular `UnblindedToken`
446    pub fn derive_verification_key<D>(&self) -> VerificationKey
447    where
448        D: Digest<OutputSize = U64> + Default,
449    {
450        let mut hash = D::default();
451        hash.update(b"hash_derive_key");
452
453        hash.update(self.t.0.as_ref());
454        hash.update(self.W.as_bytes());
455
456        let output = hash.finalize();
457        let mut output_bytes = [0u8; 64];
458        output_bytes.copy_from_slice(output.as_slice());
459
460        VerificationKey(output_bytes)
461    }
462
463    /// Convert this `UnblindedToken` to a byte array.
464    pub fn to_bytes(&self) -> [u8; UNBLINDED_TOKEN_LENGTH] {
465        let mut unblinded_token_bytes: [u8; UNBLINDED_TOKEN_LENGTH] = [0u8; UNBLINDED_TOKEN_LENGTH];
466
467        unblinded_token_bytes[..TOKEN_PREIMAGE_LENGTH].copy_from_slice(&self.t.to_bytes());
468        unblinded_token_bytes[TOKEN_PREIMAGE_LENGTH..].copy_from_slice(&self.W.to_bytes());
469        unblinded_token_bytes
470    }
471
472    fn bytes_length_error() -> TokenError {
473        TokenError(InternalError::BytesLengthError {
474            name: "UnblindedToken",
475            length: UNBLINDED_TOKEN_LENGTH,
476        })
477    }
478
479    /// Construct a `UnblindedToken` from a slice of bytes.
480    pub fn from_bytes(bytes: &[u8]) -> Result<UnblindedToken, TokenError> {
481        if bytes.len() != UNBLINDED_TOKEN_LENGTH {
482            return Err(UnblindedToken::bytes_length_error());
483        }
484
485        let preimage = TokenPreimage::from_bytes(&bytes[..TOKEN_PREIMAGE_LENGTH])?;
486
487        let mut w_bits: [u8; 32] = [0u8; 32];
488        w_bits.copy_from_slice(&bytes[TOKEN_PREIMAGE_LENGTH..]);
489        Ok(UnblindedToken {
490            t: preimage,
491            W: CompressedRistretto(w_bits),
492        })
493    }
494}
495
496/// The shared `VerificationKey` for proving / verifying the validity of an `UnblindedToken`.
497///
498/// \\(K = H_2(t, W)\\)
499#[cfg_attr(not(feature = "cbindgen"), repr(C))]
500#[derive(Clone)]
501pub struct VerificationKey([u8; 64]);
502
503impl Debug for VerificationKey {
504    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
505        write!(f, "VerificationKey: {:?}", &self.0[..])
506    }
507}
508
509impl VerificationKey {
510    /// Use the `VerificationKey` to "sign" a message, producing a `VerificationSignature`
511    pub fn sign<D>(&self, message: &[u8]) -> VerificationSignature
512    where
513        D: Mac<OutputSize = U64> + KeyInit,
514    {
515        let mut mac = <D as Mac>::new_from_slice(self.0.as_ref()).unwrap();
516        mac.update(message);
517
518        VerificationSignature(mac.finalize().into_bytes())
519    }
520
521    /// Use the `VerificationKey` to check that the signature of a message matches the
522    /// provided `VerificationSignature`
523    pub fn verify<D>(&self, sig: &VerificationSignature, message: &[u8]) -> bool
524    where
525        D: Mac<OutputSize = U64> + KeyInit,
526    {
527        &self.sign::<D>(message) == sig
528    }
529}
530
531/// A `VerificationSignature` which can be verified given the `VerificationKey` and message
532#[cfg_attr(not(feature = "cbindgen"), repr(C))]
533#[derive(Clone)]
534pub struct VerificationSignature(GenericArray<u8, U64>);
535
536#[cfg(any(test, feature = "base64"))]
537impl_base64!(VerificationSignature);
538
539#[cfg(feature = "serde")]
540impl_serde!(VerificationSignature);
541
542impl ConstantTimeEq for VerificationSignature {
543    fn ct_eq(&self, other: &VerificationSignature) -> Choice {
544        self.0.ct_eq(&other.0)
545    }
546}
547impl PartialEq for VerificationSignature {
548    fn eq(&self, other: &VerificationSignature) -> bool {
549        self.ct_eq(other).unwrap_u8() == 1
550    }
551}
552
553#[cfg(any(test, feature = "serde", feature = "base64"))]
554impl VerificationSignature {
555    /// Convert this `VerificationSignature` to a byte array.
556    /// We intentionally keep this private to avoid accidental non constant time comparisons
557    fn to_bytes(&self) -> [u8; VERIFICATION_SIGNATURE_LENGTH] {
558        let mut bytes: [u8; VERIFICATION_SIGNATURE_LENGTH] = [0u8; VERIFICATION_SIGNATURE_LENGTH];
559        bytes.copy_from_slice(self.0.as_slice());
560        bytes
561    }
562
563    fn bytes_length_error() -> TokenError {
564        TokenError(InternalError::BytesLengthError {
565            name: "VerificationSignature",
566            length: VERIFICATION_SIGNATURE_LENGTH,
567        })
568    }
569
570    /// Construct a `VerificationSignature` from a slice of bytes.
571    fn from_bytes(bytes: &[u8]) -> Result<VerificationSignature, TokenError> {
572        if bytes.len() != VERIFICATION_SIGNATURE_LENGTH {
573            return Err(VerificationSignature::bytes_length_error());
574        }
575
576        let arr: &GenericArray<u8, U64> = GenericArray::from_slice(bytes);
577        Ok(VerificationSignature(*arr))
578    }
579}
580
581#[cfg(test)]
582mod tests {
583    use base64::{engine::Engine as _, prelude::BASE64_STANDARD};
584    use hmac::Hmac;
585    use rand::rngs::OsRng;
586    use sha2::Sha512;
587
588    use super::*;
589    type HmacSha512 = Hmac<Sha512>;
590
591    #[allow(non_snake_case)]
592    #[test]
593    fn vector_tests() {
594        // Generated using tools/oprf-test-gen
595        let vectors = [
596            ("SlPD+7xZlw7l+Fr4E4dd/8E6kEouU65+ZfoN6m5iyQE=", "nGajOcg0T5IvwyBstdroFKWUwBd90yNcJU2cQJpluAg=", "nwfnvlVROHqYupd8cy0IDcsPKaBI42VpEsZTPjLueu0ptyF2nOZOQ9VxM7B02DnVMe0fKFEK+0Ws4QofS3lNbw==", "rBKvQjzCywrH+WAHjvVpB4P59cy1A0CCcYjeUioWdA0=", "iKt8hXS7Zyqy5/xbbknh/CuCmQM+Cti6uOibdKZBlEM=", "OFccZ1mrx9SSrRSoj95nEVmkbMAdggfpj6haKO0BrQQ=", "JFJyI4tUdjjtud9a4qZalp5i9QY4I0x/VhChVu4P714="),
597            ("7oD3U1ZwWQN/2eZhiXfHtnwmhR+yl3P7Gta+T123awI=", "vtiIh6vgqE9kaR/gvfo9rxps1pehPweuB1iJEM45ySc=", "5aaIdCtHxa37WdTfdv0dseUe4Dscqfgqyhc+24tyk0dOvpgPkE0QyRZEK0eDoOmEhgy2yVeznDjtj1HP+qaKTQ==", "yhpWSFSxFQRlZH9QtcmCrL1p27dYMEKs+sub7hfVbA8=", "qDUfb1GhqEsJg2MEo0jI5fUDsKitwSSkV6kaF3wWHBU=", "goTV+GGlPyIodeEfRu62nWVJFpj3lXMjZY6w4ABaolc=", "sgJfuuExkd+VoIXOr9gv+M7VlRnjnUtveVzWcOY6YzM="),
598            ("tviSLm/W8oFds67y9lMs990fjh08hQNV17/4V2bmOQY=", "5ufRlCvVKvXp1yuxxS7Jvw9LSwQUl6Q/MlT6HY2l1Hc=", "7aq3TaFBD8BV6gaMmekmCsvjN89dPgDlsyMP/tsLmQeH0McOC/5BmOpnWN1aYftf7C35gfMb7+FT+B0XFheE2w==", "Ge3prZ2jJSoh1A3ZvrSfaSA1kDziGW2I+Gmh6jniaAs=", "pOTANELrS8oor67hIYyCvrbmlMrn6Fr+04nBmFgrvxU=", "JjbfZ+UifRtHLdxcvVdI6C2SXYls9aWS0UyS6vyfCAY=", "Ki8Vb+Fm7qqeeL63/Oco95UaMOO62bRGq2fMTz5xPU8="),
599            ("2srhyAUoqF+s2y+NfSXluDXVxC7JBgiD2ttOSXBYPww=", "HhdUX1s812RxwznoreV7i/BHvj2Xy9tJo24GxNDmtF8=", "bQqtuvgVfQYqyyQYwXakdVEbNcP2IYpWaOpbxvVLh4L4s0DwCsG+ul5izWMqfOeAnHJWCKyl7798QfI3ZD8GwA==", "gfes2hjQSpt6QOBJnz4t/N/utBkdDS+W4GRQIYjb/wQ=", "ZnaqI3kpS5nh9B3jw6uOeUld//Q2+olaAlimWRFvcDE=", "0pISjRRLsiYWLzqiukHe1xkIEuDmibUcg9m/5zcPwSo=", "2lSAwKDv4mdzZuMSEEngBXSQBJRJoprzqKMtX9Bi/zs="),
600            ("pfwD6XL8PnQfJPuzg/LKKVf3QRLc4ZclvC+6GBBETgo=", "Vv9rfOewgYx7jHMyfAZfBFEBt5IuwIKwz2NbDpra+UM=", "FtP6gVFikwP+l0FWLtBl2068AFDvVYNkroESSij1wBMTIy+8SW39iiVoZXtobXIUOCoaAJAwF92paYeEQrpa/w==", "xrF8ZzEtEsGbRfJGjULVkNisgpaAO69AHZKYhyQmng4=", "rHUztBaoMDDVwOTnOxFQgEOEeG6lKBL7Tb+fluztCxo=", "dhQv3WoCFW+EcV1dpCARarugjhU/enn0UlamXDPoFxc=", "GgWeq8r+ZRsPJa50bP7y3kVAq7yBSSN8eM0oOn/U2CM="),
601            ("xNWxYjBW6Sty2Yy33S38IPkX6v4zAwK10Ge/WPxVVwU=", "GrqZp9KBIAi1mExq2VUhG8lIuNO/J9Ap4xATdJ5TfmM=", "xP/wuGwC7WYtLSUiZHofCaey+e6lbn4gsfBszdnOw347LSCBLfNpl4Y2wiZGYDZ1gEEEdF0pl+KN9dgkKq0ZyA==", "RclUsYJkWG7Uw0tM/rn+nyvDey/Ibwl7WkmmvkIPWwc=", "fiD5jlU2Bhu+chVrhWKvZTaVJnbmBTpDcfEAH9Kcjg4=", "mCFMTFdnxZ/gvTnVNGMmZkXWqlnnZH3JnwDKhTBT3x0=", "krmSP8LTAA0O35g7uk+o7MMYTl2qACiWu+CDZXtQJSo="),
602            ("lE2Tu2LzhNgU77KnsEFbqVYOc5wsbMYYzBQOcpi32Ag=", "EuJk2I4y6ZrbIn04deR+lzJS1xrBIpN+RthbPknv+gA=", "DPw4xN363pkKIT0gj794mDNPTe7X5YMP0mZ1ExVDWuGbuU9NPckmUvJD3R+W81latHPSNW2PqPbWTMT2SxLKmQ==", "Dbpt5WypybRRaQiInYndWLf/O2ewT3IEYOOdxKywNg8=", "QJH62wtVRKX0Eq1GTWVyAVuML0mpEl18VvxFn3TvTDI=", "TMRELsL1kWbyRNLQhWuIyU2j7M2FJk0trp+uR1w4hHw=", "nDiCPlbQ6HfR3MX9jRqY3id4DWo3GaZ1FvUfkmAjWyg="),
603            ("Tmfvkm6Kvi/BWAvqNsGsdQzJTI9tkGa4Sr0dNPTMCwc=", "iPXJcLCmT9SYYVYVfNx6br3VG1rHHF2hIrD2cVx8YGE=", "BdOrSDfezktj/f1d0HordqjIJWbfGiFn2uXhJbaqDna52ZyoRmT1Du6lTkSfDlhwORN/V1Q9iSBUgxQckzFmtg==", "0web3hpMwnqhIhu9mjAKNLfmFdJUfY3pu4clcs0G8AQ=", "Qll/1fI1hlcc3Dm6xtfo3LlJwu6fgoffCZ3VzzQvCAs=", "KNbnK4jL8SThHByYWWrzdpZHxvrTVid7aBYHnZD2BW0=", "Lti4tRDBQzwNIiTbGpPVVViHMvCEfC7ov2Ne3LrQdRg="),
604            ("N8oRiMuSrYdp9TMKp++AP8ridXqdX6BoPOucx2eRCQE=", "mnikks9ySHzZGMgoPZ0SRA8/JJkMh5aA+m3eqeMfqTE=", "9sNH3G618rH0vy3TKBMNRQDKOb66LUKBo9jOtMsezeN4sgAp+2pMVDMS5BATkVxXAW5dpoGUTMJ3+cfnX0plSg==", "f44zH9r/YnCyaHZnKtEc/68diotEo1GjQ5MWepNEXAk=", "EEH0FTbmxN5XoXnAHmIH0y4VjcixJ5U9T8WqXgP2IAg=", "Km0KASMeIqj0s5vswz+WEYptTx2Y0fOb9cVjb+UKexw=", "lNDdKND+R/JmDrM08Q7w7ePoXT7/hgzGU6xVBU5RFig="),
605            ("Nye8fMOQJv1HjCY6qxG0Br661wjd8OwNI1O0ZbkmGAc=", "5szoRS3/9jdVTmhswiS9yyaLeC2I0CfBAUzfe0zGjz8=", "OkOqxU+boJmNIhmzusoRGUDVJLfPlGd9bFV3UPpNueEHfu21um4zwQSuJUQ8hr8VgzU63fb93Rmk/0kRiOPUhw==", "ZBztTnJvQKmPkxfgzGzufhRa6o4oUPublpOIhODHKA4=", "lD1eLLmRw7ebLOd51OQSps51cZGTIg2DM+GL38bQQww=", "qA27hu9S60UX0jfnWJQgUBllQvfOPu+jQVkphi6Sv24=", "HhPZFQiNAYzG+niNmUiWut2g/YMhox86h1XyZypQfVk="),
606        ];
607        for (k, Y, seed, r, P, Q, W) in vectors {
608            let server_key = SigningKey::decode_base64(k).unwrap();
609            let seed = BASE64_STANDARD.decode(seed).unwrap();
610
611            assert!(server_key.public_key.encode_base64() == Y);
612
613            let r_bytes = BASE64_STANDARD.decode(r).unwrap();
614            let mut r_bits: [u8; 32] = [0u8; 32];
615            r_bits.copy_from_slice(&r_bytes);
616            let r = Scalar::from_canonical_bytes(r_bits).unwrap();
617
618            let token = Token::hash_from_bytes_with_blind::<Sha512>(&seed, r);
619
620            let blinded_token = token.blind();
621
622            assert!(blinded_token.encode_base64() == P);
623
624            let signed_token = server_key.sign(&blinded_token).unwrap();
625
626            assert!(signed_token.encode_base64() == Q);
627
628            let unblinded_token = token.unblind(&signed_token).unwrap();
629
630            let W_bytes = BASE64_STANDARD.decode(W).unwrap();
631            let mut W_bits: [u8; 32] = [0u8; 32];
632            W_bits.copy_from_slice(&W_bytes[..32]);
633            let W = CompressedRistretto(W_bits);
634
635            let unblinded_token_expected = UnblindedToken { W, t: token.t };
636            assert!(unblinded_token.encode_base64() == unblinded_token_expected.encode_base64());
637        }
638    }
639
640    #[test]
641    fn works() {
642        let mut rng = OsRng;
643
644        // Server setup
645
646        let server_key = SigningKey::random(&mut rng);
647
648        // Signing
649
650        // client prepares a random token and blinding scalar
651        let token = Token::random::<Sha512, _>(&mut rng);
652        // client blinds the token and sends it to the server
653        let blinded_token = token.blind();
654
655        // server signs the blinded token and returns it to the client
656        let signed_token = server_key.sign(&blinded_token).unwrap();
657
658        // client uses the blinding scalar to unblind the returned signed token
659        let unblinded_token = token.unblind(&signed_token).unwrap();
660
661        // Redemption
662
663        // client derives the shared key from the unblinded token
664        let client_verification_key = unblinded_token.derive_verification_key::<Sha512>();
665        // client signs a message using the shared key
666        let client_sig = client_verification_key.sign::<HmacSha512>(b"test message");
667
668        // client sends the token preimage, signature and message to the server
669
670        // server derives the unblinded token using it's key and the clients token preimage
671        let server_unblinded_token = server_key.rederive_unblinded_token(&unblinded_token.t);
672        // server derives the shared key from the unblinded token
673        let server_verification_key = server_unblinded_token.derive_verification_key::<Sha512>();
674        // server signs the same message using the shared key
675        let server_sig = server_verification_key.sign::<HmacSha512>(b"test message");
676
677        // The server compares the client signature to it's own
678        assert!(client_sig == server_sig);
679
680        // and a failing equality
681        let server_sig_fail = server_verification_key.sign::<HmacSha512>(b"failing test message");
682        assert!(!(client_sig == server_sig_fail));
683    }
684}