1#![deny(clippy::unwrap_used)]
2#![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#[derive(Clone, Copy, PartialEq, Eq)]
17pub struct Public(pub [u8; 32]);
18
19#[derive(Clone, Zeroize, PartialEq, Eq)]
21#[zeroize(drop)]
22pub struct Secret(decaf377::Fr);
23
24#[derive(PartialEq, Eq, Clone, Zeroize)]
26#[zeroize(drop)]
27pub struct SharedSecret(pub [u8; 32]);
28
29#[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 pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
43 Self(decaf377::Fr::rand(rng))
44 }
45
46 pub fn new_from_field(sk: decaf377::Fr) -> Self {
53 Self(sk)
54 }
55
56 pub fn public(&self) -> Public {
59 self.diversified_public(&decaf377::Element::GENERATOR)
60 }
61
62 pub fn diversified_public(&self, diversified_generator: &decaf377::Element) -> Public {
69 Public((self.0 * diversified_generator).vartime_compress().into())
70 }
71
72 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 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}