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
/*
    Copyright Michael Lodder. All Rights Reserved.
    SPDX-License-Identifier: Apache-2.0
*/
use crate::inner_types::*;
use crate::{util::*, Token};
use core::convert::TryFrom;
use rand_core::*;
use serde::{Deserialize, Serialize};
use subtle::{Choice, ConstantTimeEq, CtOption};
use zeroize::ZeroizeOnDrop;

/// The secret key used for signing tokens
/// Display is not implemented to prevent accidental leak of the key
///
/// To generate a random secret key, select a random number generator
/// to pass to `new`
///
/// ```
/// use oberon::*;
/// let sk = SecretKey::new(rand::thread_rng());
/// ```
///
/// or to generate a secret key from a known seed
///
/// ```
/// use oberon::*;
/// let sk = SecretKey::hash(b"my seed");
/// ```
#[derive(Clone, Debug, Eq, Deserialize, Serialize, ZeroizeOnDrop)]
#[zeroize(drop)]
pub struct SecretKey {
    pub(crate) w: Scalar,
    pub(crate) x: Scalar,
    pub(crate) y: Scalar,
}

impl Default for SecretKey {
    fn default() -> Self {
        Self {
            w: Scalar::ZERO,
            x: Scalar::ZERO,
            y: Scalar::ZERO,
        }
    }
}

impl From<&[u8; SecretKey::BYTES]> for SecretKey {
    fn from(data: &[u8; Self::BYTES]) -> Self {
        Self::from_bytes(data).unwrap()
    }
}

impl PartialEq for SecretKey {
    fn eq(&self, other: &Self) -> bool {
        self.ct_eq(other).unwrap_u8() == 1
    }
}

impl ConstantTimeEq for SecretKey {
    fn ct_eq(&self, rhs: &Self) -> Choice {
        self.x.ct_eq(&rhs.x) & self.y.ct_eq(&rhs.y) & self.w.ct_eq(&rhs.w)
    }
}

#[cfg(feature = "wasm")]
wasm_slice_impl!(SecretKey);

impl SecretKey {
    /// The number of bytes in a secret key
    pub const BYTES: usize = 96;

    /// Generate a new random key
    pub fn new(mut rng: impl RngCore + CryptoRng) -> Self {
        Self {
            w: Scalar::random(&mut rng),
            x: Scalar::random(&mut rng),
            y: Scalar::random(&mut rng),
        }
    }

    /// Generate a new key from a seed using SHAKE-256
    pub fn hash(data: &[u8]) -> Self {
        let mut values = [Scalar::ZERO; 3];
        hash_to_scalars(&[data], &mut values);
        Self {
            w: values[0],
            x: values[1],
            y: values[2],
        }
    }

    /// Convert this secret key into a byte sequence
    pub fn to_bytes(&self) -> [u8; Self::BYTES] {
        let mut out = [0u8; Self::BYTES];
        out[..32].copy_from_slice(&self.w.to_le_bytes()[..]);
        out[32..64].copy_from_slice(&self.x.to_le_bytes()[..]);
        out[64..].copy_from_slice(&self.y.to_le_bytes()[..]);
        out
    }

    /// Convert a byte sequence to a secret key
    pub fn from_bytes(data: &[u8; Self::BYTES]) -> CtOption<Self> {
        let ww = Scalar::from_le_bytes(&<[u8; 32]>::try_from(&data[..32]).unwrap());
        let xx = Scalar::from_le_bytes(&<[u8; 32]>::try_from(&data[32..64]).unwrap());
        let yy = Scalar::from_le_bytes(&<[u8; 32]>::try_from(&data[64..]).unwrap());

        ww.and_then(|w| {
            xx.and_then(|x| yy.and_then(|y| CtOption::new(Self { w, x, y }, Choice::from(1u8))))
        })
    }

    /// Sign an `id` to a token
    pub fn sign<B: AsRef<[u8]>>(&self, id: B) -> Option<Token> {
        Token::new(self, id)
    }
}