winterwallet_core/
pubkey.rs1use core::mem::MaybeUninit;
2
3use crate::WinternitzError;
4
5#[repr(transparent)]
9#[derive(Copy, Clone, PartialEq, Eq, Debug)]
10pub struct WinternitzRoot([u8; 32]);
11
12#[repr(C)]
18pub struct WinternitzPubkey<const N: usize> {
19 scalars: [[u8; 32]; N],
20 checksum: [[u8; 32]; 2],
21}
22
23impl<'a, const N: usize> TryFrom<&'a [u8]> for &'a WinternitzPubkey<N> {
24 type Error = WinternitzError;
25
26 fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
27 const { crate::assert_n::<N>() };
28 if value.len() != (N + 2) * 32 {
29 return Err(WinternitzError::InvalidLength);
30 }
31 Ok(unsafe { &*value.as_ptr().cast::<WinternitzPubkey<N>>() })
34 }
35}
36
37impl WinternitzRoot {
38 pub const fn new(bytes: [u8; 32]) -> Self {
42 Self(bytes)
43 }
44
45 pub fn as_bytes(&self) -> &[u8; 32] {
47 &self.0
48 }
49}
50
51impl From<[u8; 32]> for WinternitzRoot {
52 fn from(bytes: [u8; 32]) -> Self {
53 Self(bytes)
54 }
55}
56
57impl core::fmt::Display for WinternitzRoot {
58 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
59 write!(f, "0x")?;
60 for b in &self.0 {
61 write!(f, "{:02x}", b)?;
62 }
63 Ok(())
64 }
65}
66
67impl<const N: usize> WinternitzPubkey<N> {
68 pub(crate) fn new(scalars: [[u8; 32]; N], checksum: [[u8; 32]; 2]) -> Self {
69 const { crate::assert_n::<N>() };
70 Self { scalars, checksum }
71 }
72
73 pub fn as_bytes(&self) -> &[u8] {
76 unsafe {
78 core::slice::from_raw_parts(
79 self as *const Self as *const u8,
80 core::mem::size_of::<Self>(),
81 )
82 }
83 }
84
85 pub fn merklize(&self) -> WinternitzRoot {
93 const { crate::assert_n::<N>() };
94 const LEAF_TAG: &[u8] = &[0x00];
98 const NODE_TAG: &[u8] = &[0x01];
99
100 const MAX_DEPTH: usize = 5;
104 let mut stack: [MaybeUninit<[u8; 32]>; MAX_DEPTH] =
105 [const { MaybeUninit::uninit() }; MAX_DEPTH];
106 let mut levels = [0u8; MAX_DEPTH];
107 let mut len: usize = 0;
108
109 for leaf in self.scalars.iter().chain(self.checksum.iter()) {
110 let mut h: [u8; 32] = solana_sha256_hasher::hashv(&[LEAF_TAG, leaf]).to_bytes();
111 let mut level: u8 = 0;
112 while len > 0 && levels[len - 1] == level {
113 let top = unsafe { stack[len - 1].assume_init_read() };
115 h = solana_sha256_hasher::hashv(&[NODE_TAG, &top, &h]).to_bytes();
116 level += 1;
117 len -= 1;
118 }
119 stack[len].write(h);
120 levels[len] = level;
121 len += 1;
122 }
123
124 while len > 1 {
128 let mut top = unsafe { stack[len - 1].assume_init_read() };
130 let mut top_level = levels[len - 1];
131 let next_level = levels[len - 2];
132 while top_level < next_level {
133 top = solana_sha256_hasher::hashv(&[NODE_TAG, &top, &top]).to_bytes();
134 top_level += 1;
135 }
136 let next = unsafe { stack[len - 2].assume_init_read() };
137 let combined = solana_sha256_hasher::hashv(&[NODE_TAG, &next, &top]).to_bytes();
138 stack[len - 2].write(combined);
139 levels[len - 2] = top_level + 1;
140 len -= 1;
141 }
142
143 let root = unsafe { stack[0].assume_init_read() };
145 WinternitzRoot(root)
146 }
147}
148
149impl<const N: usize> core::fmt::Display for WinternitzPubkey<N> {
150 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
151 write!(f, "0x")?;
152 for s in self.scalars.iter().chain(self.checksum.iter()) {
153 for b in s {
154 write!(f, "{:02x}", b)?;
155 }
156 }
157 Ok(())
158 }
159}
160
161impl<const N: usize> core::fmt::Debug for WinternitzPubkey<N> {
162 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
163 writeln!(f, "WinternitzPubkey {{")?;
164 for (i, s) in self.scalars.iter().enumerate() {
165 write!(f, " scalars[{}] = 0x", i)?;
166 for b in s {
167 write!(f, "{:02x}", b)?;
168 }
169 writeln!(f)?;
170 }
171 for (i, s) in self.checksum.iter().enumerate() {
172 write!(f, " checksum[{}] = 0x", i)?;
173 for b in s {
174 write!(f, "{:02x}", b)?;
175 }
176 writeln!(f)?;
177 }
178 write!(f, "}}")
179 }
180}
181
182impl<const N: usize> From<WinternitzPubkey<N>> for WinternitzRoot {
183 fn from(pk: WinternitzPubkey<N>) -> Self {
184 const { crate::assert_n::<N>() };
185 pk.merklize()
186 }
187}
188
189impl<const N: usize> From<&WinternitzPubkey<N>> for WinternitzRoot {
190 fn from(pk: &WinternitzPubkey<N>) -> Self {
191 const { crate::assert_n::<N>() };
192 pk.merklize()
193 }
194}