bee_signing/ternary/wots/
mod.rs

1// Copyright 2020-2021 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4//! Winternitz One Time Signature scheme.
5//! <https://eprint.iacr.org/2011/191.pdf>.
6
7mod normalize;
8mod shake;
9mod sponge;
10
11pub use normalize::{normalize, Error as NormalizeError};
12pub use shake::{WotsShakePrivateKeyGenerator, WotsShakePrivateKeyGeneratorBuilder};
13pub use sponge::{WotsSpongePrivateKeyGenerator, WotsSpongePrivateKeyGeneratorBuilder};
14
15use crate::ternary::{PrivateKey, PublicKey, RecoverableSignature, Signature, SIGNATURE_FRAGMENT_LENGTH};
16
17use bee_common_derive::{SecretDebug, SecretDisplay, SecretDrop};
18use bee_crypto::ternary::{sponge::Sponge, HASH_LENGTH};
19use bee_ternary::{T1B1Buf, TritBuf, Trits, Tryte, T1B1};
20
21use thiserror::Error;
22use zeroize::Zeroize;
23
24use std::{
25    fmt::{self, Display, Formatter},
26    marker::PhantomData,
27};
28
29/// Errors occuring during WOTS operations.
30#[derive(Debug, Error, PartialEq)]
31pub enum Error {
32    /// Missing security level in generator.
33    #[error("Missing security level in generator.")]
34    MissingSecurityLevel,
35    /// Failed sponge operation.
36    #[error("Failed sponge operation.")]
37    FailedSpongeOperation,
38    /// Invalid entropy length.
39    #[error("Invalid entropy length, should be 243 trits, was {0}.")]
40    InvalidEntropyLength(usize),
41    /// Invalid message length.
42    #[error("Invalid message length, should be 243 trits, was {0}.")]
43    InvalidMessageLength(usize),
44    /// Invalid public key length.
45    #[error("Invalid public key length, should be 243 trits, was {0}.")]
46    InvalidPublicKeyLength(usize),
47    /// Invalid signature length.
48    #[error("Invalid signature length, should be a multiple of 6561 trits, was {0}.")]
49    InvalidSignatureLength(usize),
50    /// Last trit of the entropy is not null.
51    #[error("Last trit of the entropy is not null.")]
52    NonNullEntropyLastTrit,
53}
54
55/// Available WOTS security levels.
56#[derive(Clone, Copy)]
57#[repr(u8)]
58pub enum WotsSecurityLevel {
59    /// Low security.
60    Low = 1,
61    /// Medium security.
62    Medium = 2,
63    /// High security.
64    High = 3,
65}
66
67impl Default for WotsSecurityLevel {
68    fn default() -> Self {
69        WotsSecurityLevel::Medium
70    }
71}
72
73/// Winternitz One Time Signature private key.
74#[derive(SecretDebug, SecretDisplay, SecretDrop)]
75pub struct WotsPrivateKey<S> {
76    pub(crate) state: TritBuf<T1B1Buf>,
77    pub(crate) marker: PhantomData<S>,
78}
79
80impl<S> Zeroize for WotsPrivateKey<S> {
81    fn zeroize(&mut self) {
82        // This unsafe is fine since we only reset the whole buffer with zeros, there is no alignement issues.
83        unsafe { self.state.as_i8_slice_mut().zeroize() }
84    }
85}
86
87impl<S: Sponge + Default> PrivateKey for WotsPrivateKey<S> {
88    type PublicKey = WotsPublicKey<S>;
89    type Signature = WotsSignature<S>;
90    type Error = Error;
91
92    fn generate_public_key(&self) -> Result<Self::PublicKey, Self::Error> {
93        let mut sponge = S::default();
94        let mut hashed_private_key = self.state.clone();
95        let security = self.state.len() / SIGNATURE_FRAGMENT_LENGTH;
96        let mut digests = TritBuf::<T1B1Buf>::zeros(security * HASH_LENGTH);
97        let mut public_key_state = TritBuf::<T1B1Buf>::zeros(HASH_LENGTH);
98
99        // Hash each chunk of the private key the maximum amount of times.
100        for chunk in hashed_private_key.chunks_mut(HASH_LENGTH) {
101            for _ in 0..Tryte::MAX_VALUE as i8 - Tryte::MIN_VALUE as i8 {
102                sponge
103                    .absorb(chunk)
104                    .and_then(|_| sponge.squeeze_into(chunk))
105                    .map_err(|_| Self::Error::FailedSpongeOperation)?;
106                sponge.reset();
107            }
108        }
109
110        // Create one digest per fragment of the private key.
111        for (i, chunk) in hashed_private_key.chunks(SIGNATURE_FRAGMENT_LENGTH).enumerate() {
112            sponge
113                .digest_into(chunk, &mut digests[i * HASH_LENGTH..(i + 1) * HASH_LENGTH])
114                .map_err(|_| Self::Error::FailedSpongeOperation)?;
115        }
116
117        // Hash the digests together to produce the public key.
118        sponge
119            .digest_into(&digests, &mut public_key_state)
120            .map_err(|_| Self::Error::FailedSpongeOperation)?;
121
122        Ok(Self::PublicKey {
123            state: public_key_state,
124            marker: PhantomData,
125        })
126    }
127
128    fn sign(&mut self, message: &Trits<T1B1>) -> Result<Self::Signature, Self::Error> {
129        if message.len() != HASH_LENGTH {
130            return Err(Error::InvalidMessageLength(message.len()));
131        }
132
133        let mut sponge = S::default();
134        let mut signature = self.state.clone();
135
136        for (i, chunk) in signature.chunks_mut(HASH_LENGTH).enumerate() {
137            // Safe to unwrap because 3 trits can't underflow/overflow an i8.
138            let val = i8::try_from(&message[i * 3..i * 3 + 3]).unwrap();
139
140            // Hash each chunk of the private key an amount of times given by the corresponding byte of the message.
141            for _ in 0..(Tryte::MAX_VALUE as i8 - val) {
142                sponge
143                    .absorb(chunk)
144                    .and_then(|_| sponge.squeeze_into(chunk))
145                    .map_err(|_| Self::Error::FailedSpongeOperation)?;
146                sponge.reset();
147            }
148        }
149
150        Ok(Self::Signature {
151            state: signature,
152            marker: PhantomData,
153        })
154    }
155}
156
157impl<S: Sponge + Default> WotsPrivateKey<S> {
158    /// Returns the inner trits.
159    pub fn as_trits(&self) -> &Trits<T1B1> {
160        &self.state
161    }
162}
163
164/// Winternitz One Time Signature public key.
165pub struct WotsPublicKey<S> {
166    state: TritBuf<T1B1Buf>,
167    marker: PhantomData<S>,
168}
169
170impl<S: Sponge + Default> PublicKey for WotsPublicKey<S> {
171    type Signature = WotsSignature<S>;
172    type Error = Error;
173
174    fn verify(&self, message: &Trits<T1B1>, signature: &Self::Signature) -> Result<bool, Self::Error> {
175        Ok(signature.recover_public_key(message)?.state == self.state)
176    }
177
178    fn size(&self) -> usize {
179        self.state.len()
180    }
181
182    fn from_trits(state: TritBuf<T1B1Buf>) -> Result<Self, Self::Error> {
183        if state.len() != HASH_LENGTH {
184            return Err(Error::InvalidPublicKeyLength(state.len()));
185        }
186
187        Ok(Self {
188            state,
189            marker: PhantomData,
190        })
191    }
192
193    fn as_trits(&self) -> &Trits<T1B1> {
194        &self.state
195    }
196}
197
198impl<S: Sponge + Default> Display for WotsPublicKey<S> {
199    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
200        write!(
201            f,
202            "{}",
203            self.as_trits().iter_trytes().map(char::from).collect::<String>()
204        )
205    }
206}
207
208/// Winternitz One Time Signature signature.
209pub struct WotsSignature<S> {
210    state: TritBuf<T1B1Buf>,
211    marker: PhantomData<S>,
212}
213
214impl<S: Sponge + Default> Signature for WotsSignature<S> {
215    type Error = Error;
216
217    fn size(&self) -> usize {
218        self.state.len()
219    }
220
221    fn from_trits(state: TritBuf<T1B1Buf>) -> Result<Self, Self::Error> {
222        if state.len() % SIGNATURE_FRAGMENT_LENGTH != 0 {
223            return Err(Error::InvalidSignatureLength(state.len()));
224        }
225
226        Ok(Self {
227            state,
228            marker: PhantomData,
229        })
230    }
231
232    fn as_trits(&self) -> &Trits<T1B1> {
233        &self.state
234    }
235}
236
237impl<S: Sponge + Default> RecoverableSignature for WotsSignature<S> {
238    type PublicKey = WotsPublicKey<S>;
239    type Error = Error;
240
241    fn recover_public_key(
242        &self,
243        message: &Trits<T1B1>,
244    ) -> Result<Self::PublicKey, <Self as RecoverableSignature>::Error> {
245        if message.len() != HASH_LENGTH {
246            return Err(Error::InvalidMessageLength(message.len()));
247        }
248
249        let mut sponge = S::default();
250        let mut public_key_state = TritBuf::<T1B1Buf>::zeros(HASH_LENGTH);
251        let security = self.state.len() / SIGNATURE_FRAGMENT_LENGTH;
252        let mut digests = TritBuf::<T1B1Buf>::zeros(security * HASH_LENGTH);
253        let mut hashed_signature = self.state.clone();
254
255        for (i, chunk) in hashed_signature.chunks_mut(HASH_LENGTH).enumerate() {
256            // Safe to unwrap because 3 trits can't underflow/overflow an i8.
257            let val = i8::try_from(&message[i * 3..i * 3 + 3]).unwrap();
258
259            // Hash each chunk of the signature an amount of times given by the corresponding byte of the message.
260            for _ in 0..(val - Tryte::MIN_VALUE as i8) {
261                sponge
262                    .absorb(chunk)
263                    .and_then(|_| sponge.squeeze_into(chunk))
264                    .map_err(|_| <Self as RecoverableSignature>::Error::FailedSpongeOperation)?;
265                sponge.reset();
266            }
267        }
268
269        // Create one digest per fragment of the signature.
270        for (i, chunk) in hashed_signature.chunks(SIGNATURE_FRAGMENT_LENGTH).enumerate() {
271            sponge
272                .digest_into(chunk, &mut digests[i * HASH_LENGTH..(i + 1) * HASH_LENGTH])
273                .map_err(|_| <Self as RecoverableSignature>::Error::FailedSpongeOperation)?;
274        }
275
276        // Hash the digests together to recover the public key.
277        sponge
278            .digest_into(&digests, &mut public_key_state)
279            .map_err(|_| <Self as RecoverableSignature>::Error::FailedSpongeOperation)?;
280
281        Ok(Self::PublicKey {
282            state: public_key_state,
283            marker: PhantomData,
284        })
285    }
286}
287
288impl<S: Sponge + Default> Display for WotsSignature<S> {
289    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
290        write!(
291            f,
292            "{}",
293            self.as_trits().iter_trytes().map(char::from).collect::<String>()
294        )
295    }
296}