oberon/
secret_key.rs

1/*
2    Copyright Michael Lodder. All Rights Reserved.
3    SPDX-License-Identifier: Apache-2.0
4*/
5use crate::inner_types::*;
6use crate::{util::*, Token};
7use core::convert::TryFrom;
8use rand_core::*;
9use serde::{Deserialize, Serialize};
10use subtle::{Choice, ConstantTimeEq, CtOption};
11use zeroize::ZeroizeOnDrop;
12
13/// The secret key used for signing tokens
14/// Display is not implemented to prevent accidental leak of the key
15///
16/// To generate a random secret key, select a random number generator
17/// to pass to `new`
18///
19/// ```
20/// use oberon::*;
21/// let sk = SecretKey::new(rand::thread_rng());
22/// ```
23///
24/// or to generate a secret key from a known seed
25///
26/// ```
27/// use oberon::*;
28/// let sk = SecretKey::hash(b"my seed");
29/// ```
30#[derive(Clone, Debug, Eq, Deserialize, Serialize, ZeroizeOnDrop)]
31#[zeroize(drop)]
32pub struct SecretKey {
33    pub(crate) w: Scalar,
34    pub(crate) x: Scalar,
35    pub(crate) y: Scalar,
36}
37
38impl Default for SecretKey {
39    fn default() -> Self {
40        Self {
41            w: Scalar::ZERO,
42            x: Scalar::ZERO,
43            y: Scalar::ZERO,
44        }
45    }
46}
47
48impl From<&[u8; SecretKey::BYTES]> for SecretKey {
49    fn from(data: &[u8; Self::BYTES]) -> Self {
50        Self::from_bytes(data).unwrap()
51    }
52}
53
54impl PartialEq for SecretKey {
55    fn eq(&self, other: &Self) -> bool {
56        self.ct_eq(other).unwrap_u8() == 1
57    }
58}
59
60impl ConstantTimeEq for SecretKey {
61    fn ct_eq(&self, rhs: &Self) -> Choice {
62        self.x.ct_eq(&rhs.x) & self.y.ct_eq(&rhs.y) & self.w.ct_eq(&rhs.w)
63    }
64}
65
66#[cfg(feature = "wasm")]
67wasm_slice_impl!(SecretKey);
68
69impl SecretKey {
70    /// The number of bytes in a secret key
71    pub const BYTES: usize = 96;
72
73    /// Generate a new random key
74    pub fn new(mut rng: impl RngCore + CryptoRng) -> Self {
75        Self {
76            w: Scalar::random(&mut rng),
77            x: Scalar::random(&mut rng),
78            y: Scalar::random(&mut rng),
79        }
80    }
81
82    /// Generate a new key from a seed using SHAKE-256
83    pub fn hash(data: &[u8]) -> Self {
84        let mut values = [Scalar::ZERO; 3];
85        hash_to_scalars(&[data], &mut values);
86        Self {
87            w: values[0],
88            x: values[1],
89            y: values[2],
90        }
91    }
92
93    /// Convert this secret key into a byte sequence
94    pub fn to_bytes(&self) -> [u8; Self::BYTES] {
95        let mut out = [0u8; Self::BYTES];
96        out[..32].copy_from_slice(&self.w.to_le_bytes()[..]);
97        out[32..64].copy_from_slice(&self.x.to_le_bytes()[..]);
98        out[64..].copy_from_slice(&self.y.to_le_bytes()[..]);
99        out
100    }
101
102    /// Convert a byte sequence to a secret key
103    pub fn from_bytes(data: &[u8; Self::BYTES]) -> CtOption<Self> {
104        let ww = Scalar::from_le_bytes(&<[u8; 32]>::try_from(&data[..32]).unwrap());
105        let xx = Scalar::from_le_bytes(&<[u8; 32]>::try_from(&data[32..64]).unwrap());
106        let yy = Scalar::from_le_bytes(&<[u8; 32]>::try_from(&data[64..]).unwrap());
107
108        ww.and_then(|w| {
109            xx.and_then(|x| yy.and_then(|y| CtOption::new(Self { w, x, y }, Choice::from(1u8))))
110        })
111    }
112
113    /// Sign an `id` to a token
114    pub fn sign<B: AsRef<[u8]>>(&self, id: B) -> Option<Token> {
115        Token::new(self, id)
116    }
117}