bee_signing/ternary/wots/
normalize.rs

1// Copyright 2020-2021 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::ternary::{constants::MESSAGE_FRAGMENT_LENGTH, wots::WotsSecurityLevel};
5
6use bee_crypto::ternary::HASH_LENGTH;
7use bee_ternary::{T1B1Buf, T3B1Buf, TritBuf, Trits, Tryte, T1B1, T3B1};
8
9use thiserror::Error;
10
11/// Errors occuring during normalization.
12#[derive(Debug, Error, PartialEq)]
13pub enum Error {
14    /// Invalid message length.
15    #[error("Invalid message length, should be 243 trits, was {0}.")]
16    InvalidMessageLength(usize),
17}
18
19/// When applying WOTS on a non-normalized message, the amount of private key data leaked is not uniform and some
20/// messages could result in most of (or all of) the key being leaked. As a consequence, even after one signature there
21/// is a varying chance that brute forcing another message becomes feasible. By normalizing the message, such "extreme"
22/// cases get alleviated, so that every signature exactly leaks half of the private key.
23pub fn normalize(message: &Trits<T1B1>) -> Result<TritBuf<T1B1Buf>, Error> {
24    if message.len() != HASH_LENGTH {
25        return Err(Error::InvalidMessageLength(message.len()));
26    }
27
28    let mut normalized = [0i8; WotsSecurityLevel::High as usize * MESSAGE_FRAGMENT_LENGTH];
29
30    for i in 0..WotsSecurityLevel::High as usize {
31        let mut sum: i16 = 0;
32
33        for j in (i * MESSAGE_FRAGMENT_LENGTH)..((i + 1) * MESSAGE_FRAGMENT_LENGTH) {
34            // Safe to unwrap because 3 trits can't underflow/overflow an i8.
35            normalized[j] = i8::try_from(&message[j * 3..j * 3 + 3]).unwrap();
36            sum += i16::from(normalized[j]);
37        }
38
39        while sum > 0 {
40            for t in &mut normalized[i * MESSAGE_FRAGMENT_LENGTH..(i + 1) * MESSAGE_FRAGMENT_LENGTH] {
41                if (*t as i8) > Tryte::MIN_VALUE as i8 {
42                    *t -= 1;
43                    break;
44                }
45            }
46            sum -= 1;
47        }
48
49        while sum < 0 {
50            for t in &mut normalized[i * MESSAGE_FRAGMENT_LENGTH..(i + 1) * MESSAGE_FRAGMENT_LENGTH] {
51                if (*t as i8) < Tryte::MAX_VALUE as i8 {
52                    *t += 1;
53                    break;
54                }
55            }
56            sum += 1;
57        }
58    }
59
60    // This usage of unsafe is fine since we are creating the normalized trits inside this function and we know that the
61    // content can't go wrong.
62    Ok(unsafe {
63        Trits::<T3B1>::from_raw_unchecked(&normalized, normalized.len() * 3)
64            .to_buf::<T3B1Buf>()
65            .encode::<T1B1Buf>()
66    })
67}