decaf377_ka/
lib.rs

1#![deny(clippy::unwrap_used)]
2// Requires nightly.
3#![cfg_attr(docsrs, feature(doc_auto_cfg))]
4
5use std::convert::{TryFrom, TryInto};
6
7use decaf377::{self};
8use rand_core::{CryptoRng, RngCore};
9use zeroize::Zeroize;
10
11/// A public key sent to the counterparty in the key agreement protocol.
12///
13/// This is a refinement type around `[u8; 32]` that marks the bytes as being a
14/// public key.  Not all 32-byte arrays are valid public keys; invalid public
15/// keys will error during key agreement.
16#[derive(Clone, Copy, PartialEq, Eq)]
17pub struct Public(pub [u8; 32]);
18
19/// A secret key used to perform key agreement using the counterparty's public key.
20#[derive(Clone, Zeroize, PartialEq, Eq)]
21#[zeroize(drop)]
22pub struct Secret(decaf377::Fr);
23
24/// The shared secret derived at the end of the key agreement protocol.
25#[derive(PartialEq, Eq, Clone, Zeroize)]
26#[zeroize(drop)]
27pub struct SharedSecret(pub [u8; 32]);
28
29/// An error during key agreement.
30#[derive(thiserror::Error, Debug)]
31pub enum Error {
32    #[error("Invalid public key")]
33    InvalidPublic(Public),
34    #[error("Invalid secret key")]
35    InvalidSecret,
36    #[error("Supplied bytes are incorrect length")]
37    SliceLenError,
38}
39
40impl Secret {
41    /// Generate a new secret key using `rng`.
42    pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
43        Self(decaf377::Fr::rand(rng))
44    }
45
46    /// Use the supplied field element as the secret key directly.
47    ///
48    /// # Warning
49    ///
50    /// This function exists to allow custom key derivation; it's the caller's
51    /// responsibility to ensure that the input was generated securely.
52    pub fn new_from_field(sk: decaf377::Fr) -> Self {
53        Self(sk)
54    }
55
56    /// Derive a public key for this secret key, using the conventional
57    /// `decaf377` generator.
58    pub fn public(&self) -> Public {
59        self.diversified_public(&decaf377::Element::GENERATOR)
60    }
61
62    /// Derive a diversified public key for this secret key, using the provided
63    /// `diversified_generator`.
64    ///
65    /// Since key agreement does not depend on the basepoint, only on the secret
66    /// key and the public key, a single secret key can correspond to many
67    /// different (unlinkable) public keys.
68    pub fn diversified_public(&self, diversified_generator: &decaf377::Element) -> Public {
69        Public((self.0 * diversified_generator).vartime_compress().into())
70    }
71
72    /// Perform key agreement with the provided public key.
73    ///
74    /// Fails if the provided public key is invalid.
75    pub fn key_agreement_with(&self, other: &Public) -> Result<SharedSecret, Error> {
76        let pk = decaf377::Encoding(other.0)
77            .vartime_decompress()
78            .map_err(|_| Error::InvalidPublic(*other))?;
79
80        Ok(SharedSecret((self.0 * pk).vartime_compress().into()))
81    }
82
83    /// Convert this shared secret to bytes.
84    ///
85    /// Convenience wrapper around an [`Into`] impl.
86    pub fn to_bytes(&self) -> [u8; 32] {
87        self.into()
88    }
89}
90
91impl std::fmt::Debug for Public {
92    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93        f.write_fmt(format_args!(
94            "decaf377_ka::Public({})",
95            hex::encode(&self.0[..])
96        ))
97    }
98}
99
100impl std::fmt::Debug for Secret {
101    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102        let bytes = self.0.to_bytes();
103        f.write_fmt(format_args!(
104            "decaf377_ka::Secret({})",
105            hex::encode(&bytes[..])
106        ))
107    }
108}
109
110impl std::fmt::Debug for SharedSecret {
111    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112        f.write_fmt(format_args!(
113            "decaf377_ka::SharedSecret({})",
114            hex::encode(&self.0[..])
115        ))
116    }
117}
118
119impl TryFrom<&[u8]> for Public {
120    type Error = Error;
121
122    fn try_from(slice: &[u8]) -> Result<Public, Error> {
123        let bytes: [u8; 32] = slice.try_into().map_err(|_| Error::SliceLenError)?;
124        Ok(Public(bytes))
125    }
126}
127
128impl TryFrom<&[u8]> for Secret {
129    type Error = Error;
130
131    fn try_from(slice: &[u8]) -> Result<Secret, Error> {
132        let bytes: [u8; 32] = slice.try_into().map_err(|_| Error::SliceLenError)?;
133        bytes.try_into()
134    }
135}
136
137impl TryFrom<[u8; 32]> for Secret {
138    type Error = Error;
139    fn try_from(bytes: [u8; 32]) -> Result<Secret, Error> {
140        let x = decaf377::Fr::from_bytes_checked(&bytes).map_err(|_| Error::InvalidSecret)?;
141        Ok(Secret(x))
142    }
143}
144
145impl TryFrom<[u8; 32]> for SharedSecret {
146    type Error = Error;
147    fn try_from(bytes: [u8; 32]) -> Result<SharedSecret, Error> {
148        decaf377::Encoding(bytes)
149            .vartime_decompress()
150            .map_err(|_| Error::InvalidSecret)?;
151
152        Ok(SharedSecret(bytes))
153    }
154}
155
156impl From<&Secret> for [u8; 32] {
157    fn from(s: &Secret) -> Self {
158        s.0.to_bytes()
159    }
160}