lms_signature/lms/
private.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
use crate::constants::{D_INTR, D_LEAF, ID_LEN};
use crate::error::LmsDeserializeError;
use crate::lms::error::LmsOutOfPrivateKeys;
use crate::lms::{LmsMode, Signature, VerifyingKey};
use crate::ots::SigningKey as OtsPrivateKey;
use crate::types::{Identifier, Typecode};

use digest::{Digest, Output, OutputSizeUser};
use generic_array::{ArrayLength, GenericArray};
use rand::{CryptoRng, Rng};
use signature::{Error, RandomizedSignerMut};

use std::cmp::Ordering;
use std::ops::Add;
use typenum::{Sum, U28};

/// Opaque struct representing a LMS private key
///
/// Note: there is no requirement to map specific LMS algorithms to specific
/// LM-OTS algorithms so it must be parametrized. With the algorithms provided
/// by this crate, this is done via
/// [LmsSha256M32H10](crate::lms::LmsSha256M32H10)<[LmsOtsSha256N32W4](crate::ots::LmsOtsSha256N32W4)>.
pub struct SigningKey<Mode: LmsMode> {
    id: Identifier,
    seed: Output<Mode::Hasher>, // Re-generate the leaf privkeys as-needed from a seed
    auth_tree: GenericArray<Output<Mode::Hasher>, Mode::TreeLen>, // TODO: Decide whether/when to precompute
    q: u32,
}

impl<Mode: LmsMode> SigningKey<Mode> {
    /// Creates a new private key with a random identifier using
    /// algorithm 5 from <https://datatracker.ietf.org/doc/html/rfc8554#section-5.2>
    pub fn new(mut rng: impl Rng + CryptoRng) -> Self {
        let mut id = Identifier::default();
        rng.fill_bytes(id.as_mut());

        let mut seed = Output::<Mode::Hasher>::default();
        rng.fill_bytes(seed.as_mut());
        Self::new_from_seed(id, seed)
    }

    // Returns a new LMS private key generated pseudorandomly from an identifier
    // and secret seed. The seed must be equal to the hash output length of the
    // LMS mode ([Mode::M])
    //
    // TODO: Return error rather than panic? Or just make the input a
    // GenericArray? This is the algorithm from Appendix A of
    // <https://datatracker.ietf.org/doc/html/rfc8554#appendix-A>
    pub fn new_from_seed(id: Identifier, seed: impl AsRef<[u8]>) -> Self {
        //let seed = seed.as_ref();
        let seed = GenericArray::clone_from_slice(seed.as_ref());
        let mut sk = Self {
            id,
            seed,
            auth_tree: GenericArray::default(),
            q: 0, // we set q = 0 when generating keys; it will change
        };
        sk.gen_pk_tree(); // TODO: Use lazy generation / MTT
        sk
    }

    /// Generates a Merkle tree of OTS public key hashes, using the indexing scheme of RFC 8554 offset by 1
    /// auth_tree[i] == T[i+1]
    /// (i.e. the root is at index 0, the internal nodes are at indices 1..MODE_LEAVES-1, and the leaves are at indices MODE_LEAVES-1..2*MODE_LEAVES-1)
    fn gen_pk_tree(&mut self) {
        for i in 0..Mode::LEAVES {
            let mut r: u32 = i + Mode::LEAVES;
            let ots_priv = OtsPrivateKey::<Mode::OtsMode>::new_from_seed(i, self.id, &self.seed);
            Mode::Hasher::new()
                .chain_update(self.id)
                .chain_update(r.to_be_bytes())
                .chain_update(D_LEAF)
                .chain_update(ots_priv.public().k)
                .finalize_into(&mut self.auth_tree[(r - 1) as usize]);
            let mut j: u32 = i;
            while (j % 2) == 1 {
                r = (r - 1) >> 1;
                j = (j - 1) >> 1;
                Mode::Hasher::new()
                    .chain_update(self.id)
                    .chain_update(r.to_be_bytes())
                    .chain_update(D_INTR)
                    .chain_update(self.auth_tree[(2 * r - 1) as usize].clone())
                    .chain_update(self.auth_tree[(2 * r) as usize].clone())
                    .finalize_into(&mut self.auth_tree[(r - 1) as usize]);
            }
        }
    }

    /// this implements algorithm 1 from <https://datatracker.ietf.org/doc/html/rfc8554#section-4.3>
    pub fn public(&self) -> VerifyingKey<Mode> {
        VerifyingKey::<Mode>::new(self.id, self.auth_tree[0].clone())
    }

    /// Returns the 16-byte identifier of the key pair
    pub fn id(&self) -> &Identifier {
        &self.id
    }

    /// Returns the current value of the signing index q
    pub fn q(&self) -> u32 {
        self.q
    }
}

// this implements the algorithm from Appendix D in <https://datatracker.ietf.org/doc/html/rfc8554#appendix-D>
impl<Mode: LmsMode> RandomizedSignerMut<Signature<Mode>> for SigningKey<Mode> {
    fn try_sign_with_rng(
        &mut self,
        rng: &mut impl rand_core::CryptoRngCore,
        msg: &[u8],
    ) -> Result<Signature<Mode>, Error> {
        if self.q >= Mode::LEAVES {
            return Err(Error::from_source(LmsOutOfPrivateKeys {}));
        }

        let mut ots_priv_key =
            OtsPrivateKey::<Mode::OtsMode>::new_from_seed(self.q, self.id, &self.seed);
        let ots_sig = ots_priv_key.try_sign_with_rng(rng, msg)?;

        let r = (1 << Mode::H) + self.q;

        let auth_path = (0..Mode::H).map(|i| self.auth_tree[(((r >> i) ^ 1) - 1) as usize].clone());

        // increment q
        self.q += 1;

        Ok(Signature::<Mode> {
            q: self.q - 1,
            lmots_sig: ots_sig,
            path: auth_path.collect(),
        })
    }
}

/// Converts a [PrivateKey] into its byte representation
impl<Mode: LmsMode> From<SigningKey<Mode>>
    for GenericArray<u8, Sum<<Mode::Hasher as OutputSizeUser>::OutputSize, U28>>
where
    <Mode::Hasher as OutputSizeUser>::OutputSize: Add<U28>,
    Sum<<Mode::Hasher as OutputSizeUser>::OutputSize, U28>: ArrayLength<u8>,
{
    fn from(pk: SigningKey<Mode>) -> Self {
        // Return u32(type) || u32(otstype) || u32(q) || id || seed
        GenericArray::from_exact_iter(
            std::iter::empty()
                .chain(Mode::TYPECODE.to_be_bytes())
                .chain(Mode::OtsMode::TYPECODE.to_be_bytes())
                .chain(pk.q.to_be_bytes())
                .chain(pk.id)
                .chain(pk.seed),
        )
        .unwrap()
    }
}

/// Tries to parse a [PrivateKey] from an exact slice
impl<'a, Mode: LmsMode> TryFrom<&'a [u8]> for SigningKey<Mode> {
    type Error = LmsDeserializeError;

    fn try_from(pk: &'a [u8]) -> Result<Self, Self::Error> {
        if pk.len() < 4 {
            return Err(LmsDeserializeError::NoAlgorithm);
        }

        let (alg, pk) = pk.split_at(4);
        let expected = Mode::M + ID_LEN + 8;

        // will never panic because alg is a 4 byte slice
        if u32::from_be_bytes(alg.try_into().unwrap()) != Mode::TYPECODE {
            return Err(LmsDeserializeError::WrongAlgorithm);
        }

        match pk.len().cmp(&expected) {
            Ordering::Less => Err(LmsDeserializeError::TooShort),
            Ordering::Greater => Err(LmsDeserializeError::TooLong),
            Ordering::Equal => {
                // pk is now guaranteed to be of the form otstype || q || id || seed
                let (otstype, qk) = pk.split_at(ID_LEN);
                let (q, idseed) = qk.split_at(4);
                let (id, seed) = idseed.split_at(ID_LEN);

                // check the OTS type
                if u32::from_be_bytes(otstype.try_into().unwrap()) != Mode::OtsMode::TYPECODE {
                    return Err(LmsDeserializeError::WrongAlgorithm);
                }

                let mut key = Self {
                    q: u32::from_be_bytes(q.try_into().expect("ok")),
                    id: id.try_into().expect("ok"),
                    seed: GenericArray::clone_from_slice(seed),
                    auth_tree: GenericArray::default(),
                };
                key.gen_pk_tree();
                Ok(key)
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::SigningKey;
    use crate::lms::modes::{LmsSha256M32H10, LmsSha256M32H5};
    use crate::ots::modes::{LmsOtsSha256N32W4, LmsOtsSha256N32W8};
    use hex_literal::hex;
    use signature::{RandomizedSignerMut, SignatureEncoding};

    // Known-Answer Test vectors from <https://datatracker.ietf.org/doc/html/rfc8554#appendix-F>
    #[test]
    // Generate Test Case 2 top-level LMS public key
    // LM_SHA256_M32_H10 / LMOTS_SHA256_N32_W4
    fn test_pk_gen_rfc8554_testcase_2_top_level() {
        let seed = hex!("558b8966c48ae9cb898b423c83443aae014a72f1b1ab5cc85cf1d892903b5439");
        let id = hex!("d08fabd4a2091ff0a8cb4ed834e74534");
        let expected_k = hex!("32a58885cd9ba0431235466bff9651c6c92124404d45fa53cf161c28f1ad5a8e");

        let lms_priv = SigningKey::<LmsSha256M32H10<LmsOtsSha256N32W4>>::new_from_seed(id, seed);
        let lms_pub = lms_priv.public();
        assert_eq!(lms_pub.k(), expected_k);
        assert_eq!(lms_pub.id(), &id);
    }

    #[test]
    // Generate Test Case 2 leaf-level LMS public key
    // LM_SHA256_M32_H5 / LMOTS_SHA256_N32_W8
    fn test_pk_gen_rfc8554_testcase_2_leaf_level() {
        let seed = hex!("a1c4696e2608035a886100d05cd99945eb3370731884a8235e2fb3d4d71f2547");
        let id = hex!("215f83b7ccb9acbcd08db97b0d04dc2b");
        let expected_k = hex!("a1cd035833e0e90059603f26e07ad2aad152338e7a5e5984bcd5f7bb4eba40b7");

        let lms_priv = SigningKey::<LmsSha256M32H5<LmsOtsSha256N32W8>>::new_from_seed(id, seed);
        let lms_pub = lms_priv.public();
        assert_eq!(lms_pub.k(), expected_k);
        assert_eq!(lms_pub.id(), &id);
    }

    #[test]
    // Byte-for-byte signature equivalence test with RFC 8554 Test Case 2
    // Leaf-level LMS signature. LM_SHA256_M32_H5 / LMOTS_SHA256_N32_W8
    fn test_sign_rfc8554_testcase_2() {
        let expected_signature = [
            0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x0e, 0xb1, 0xed, 0x54, 0xa2, 0x46,
            0x0d, 0x51, 0x23, 0x88, 0xca, 0xd5, 0x33, 0x13, 0x8d, 0x24, 0x05, 0x34, 0xe9, 0x7b,
            0x1e, 0x82, 0xd3, 0x3b, 0xd9, 0x27, 0xd2, 0x01, 0xdf, 0xc2, 0x4e, 0xbb, 0x11, 0xb3,
            0x64, 0x90, 0x23, 0x69, 0x6f, 0x85, 0x15, 0x0b, 0x18, 0x9e, 0x50, 0xc0, 0x0e, 0x98,
            0x85, 0x0a, 0xc3, 0x43, 0xa7, 0x7b, 0x36, 0x38, 0x31, 0x9c, 0x34, 0x7d, 0x73, 0x10,
            0x26, 0x9d, 0x3b, 0x77, 0x14, 0xfa, 0x40, 0x6b, 0x8c, 0x35, 0xb0, 0x21, 0xd5, 0x4d,
            0x4f, 0xda, 0xda, 0x7b, 0x9c, 0xe5, 0xd4, 0xba, 0x5b, 0x06, 0x71, 0x9e, 0x72, 0xaa,
            0xf5, 0x8c, 0x5a, 0xae, 0x7a, 0xca, 0x05, 0x7a, 0xa0, 0xe2, 0xe7, 0x4e, 0x7d, 0xcf,
            0xd1, 0x7a, 0x08, 0x23, 0x42, 0x9d, 0xb6, 0x29, 0x65, 0xb7, 0xd5, 0x63, 0xc5, 0x7b,
            0x4c, 0xec, 0x94, 0x2c, 0xc8, 0x65, 0xe2, 0x9c, 0x1d, 0xad, 0x83, 0xca, 0xc8, 0xb4,
            0xd6, 0x1a, 0xac, 0xc4, 0x57, 0xf3, 0x36, 0xe6, 0xa1, 0x0b, 0x66, 0x32, 0x3f, 0x58,
            0x87, 0xbf, 0x35, 0x23, 0xdf, 0xca, 0xde, 0xe1, 0x58, 0x50, 0x3b, 0xfa, 0xa8, 0x9d,
            0xc6, 0xbf, 0x59, 0xda, 0xa8, 0x2a, 0xfd, 0x2b, 0x5e, 0xbb, 0x2a, 0x9c, 0xa6, 0x57,
            0x2a, 0x60, 0x67, 0xce, 0xe7, 0xc3, 0x27, 0xe9, 0x03, 0x9b, 0x3b, 0x6e, 0xa6, 0xa1,
            0xed, 0xc7, 0xfd, 0xc3, 0xdf, 0x92, 0x7a, 0xad, 0xe1, 0x0c, 0x1c, 0x9f, 0x2d, 0x5f,
            0xf4, 0x46, 0x45, 0x0d, 0x2a, 0x39, 0x98, 0xd0, 0xf9, 0xf6, 0x20, 0x2b, 0x5e, 0x07,
            0xc3, 0xf9, 0x7d, 0x24, 0x58, 0xc6, 0x9d, 0x3c, 0x81, 0x90, 0x64, 0x39, 0x78, 0xd7,
            0xa7, 0xf4, 0xd6, 0x4e, 0x97, 0xe3, 0xf1, 0xc4, 0xa0, 0x8a, 0x7c, 0x5b, 0xc0, 0x3f,
            0xd5, 0x56, 0x82, 0xc0, 0x17, 0xe2, 0x90, 0x7e, 0xab, 0x07, 0xe5, 0xbb, 0x2f, 0x19,
            0x01, 0x43, 0x47, 0x5a, 0x60, 0x43, 0xd5, 0xe6, 0xd5, 0x26, 0x34, 0x71, 0xf4, 0xee,
            0xcf, 0x6e, 0x25, 0x75, 0xfb, 0xc6, 0xff, 0x37, 0xed, 0xfa, 0x24, 0x9d, 0x6c, 0xda,
            0x1a, 0x09, 0xf7, 0x97, 0xfd, 0x5a, 0x3c, 0xd5, 0x3a, 0x06, 0x67, 0x00, 0xf4, 0x58,
            0x63, 0xf0, 0x4b, 0x6c, 0x8a, 0x58, 0xcf, 0xd3, 0x41, 0x24, 0x1e, 0x00, 0x2d, 0x0d,
            0x2c, 0x02, 0x17, 0x47, 0x2b, 0xf1, 0x8b, 0x63, 0x6a, 0xe5, 0x47, 0xc1, 0x77, 0x13,
            0x68, 0xd9, 0xf3, 0x17, 0x83, 0x5c, 0x9b, 0x0e, 0xf4, 0x30, 0xb3, 0xdf, 0x40, 0x34,
            0xf6, 0xaf, 0x00, 0xd0, 0xda, 0x44, 0xf4, 0xaf, 0x78, 0x00, 0xbc, 0x7a, 0x5c, 0xf8,
            0xa5, 0xab, 0xdb, 0x12, 0xdc, 0x71, 0x8b, 0x55, 0x9b, 0x74, 0xca, 0xb9, 0x09, 0x0e,
            0x33, 0xcc, 0x58, 0xa9, 0x55, 0x30, 0x09, 0x81, 0xc4, 0x20, 0xc4, 0xda, 0x8f, 0xfd,
            0x67, 0xdf, 0x54, 0x08, 0x90, 0xa0, 0x62, 0xfe, 0x40, 0xdb, 0xa8, 0xb2, 0xc1, 0xc5,
            0x48, 0xce, 0xd2, 0x24, 0x73, 0x21, 0x9c, 0x53, 0x49, 0x11, 0xd4, 0x8c, 0xca, 0xab,
            0xfb, 0x71, 0xbc, 0x71, 0x86, 0x2f, 0x4a, 0x24, 0xeb, 0xd3, 0x76, 0xd2, 0x88, 0xfd,
            0x4e, 0x6f, 0xb0, 0x6e, 0xd8, 0x70, 0x57, 0x87, 0xc5, 0xfe, 0xdc, 0x81, 0x3c, 0xd2,
            0x69, 0x7e, 0x5b, 0x1a, 0xac, 0x1c, 0xed, 0x45, 0x76, 0x7b, 0x14, 0xce, 0x88, 0x40,
            0x9e, 0xae, 0xbb, 0x60, 0x1a, 0x93, 0x55, 0x9a, 0xae, 0x89, 0x3e, 0x14, 0x3d, 0x1c,
            0x39, 0x5b, 0xc3, 0x26, 0xda, 0x82, 0x1d, 0x79, 0xa9, 0xed, 0x41, 0xdc, 0xfb, 0xe5,
            0x49, 0x14, 0x7f, 0x71, 0xc0, 0x92, 0xf4, 0xf3, 0xac, 0x52, 0x2b, 0x5c, 0xc5, 0x72,
            0x90, 0x70, 0x66, 0x50, 0x48, 0x7b, 0xae, 0x9b, 0xb5, 0x67, 0x1e, 0xcc, 0x9c, 0xcc,
            0x2c, 0xe5, 0x1e, 0xad, 0x87, 0xac, 0x01, 0x98, 0x52, 0x68, 0x52, 0x12, 0x22, 0xfb,
            0x90, 0x57, 0xdf, 0x7e, 0xd4, 0x18, 0x10, 0xb5, 0xef, 0x0d, 0x4f, 0x7c, 0xc6, 0x73,
            0x68, 0xc9, 0x0f, 0x57, 0x3b, 0x1a, 0xc2, 0xce, 0x95, 0x6c, 0x36, 0x5e, 0xd3, 0x8e,
            0x89, 0x3c, 0xe7, 0xb2, 0xfa, 0xe1, 0x5d, 0x36, 0x85, 0xa3, 0xdf, 0x2f, 0xa3, 0xd4,
            0xcc, 0x09, 0x8f, 0xa5, 0x7d, 0xd6, 0x0d, 0x2c, 0x97, 0x54, 0xa8, 0xad, 0xe9, 0x80,
            0xad, 0x0f, 0x93, 0xf6, 0x78, 0x70, 0x75, 0xc3, 0xf6, 0x80, 0xa2, 0xba, 0x19, 0x36,
            0xa8, 0xc6, 0x1d, 0x1a, 0xf5, 0x2a, 0xb7, 0xe2, 0x1f, 0x41, 0x6b, 0xe0, 0x9d, 0x2a,
            0x8d, 0x64, 0xc3, 0xd3, 0xd8, 0x58, 0x29, 0x68, 0xc2, 0x83, 0x99, 0x02, 0x22, 0x9f,
            0x85, 0xae, 0xe2, 0x97, 0xe7, 0x17, 0xc0, 0x94, 0xc8, 0xdf, 0x4a, 0x23, 0xbb, 0x5d,
            0xb6, 0x58, 0xdd, 0x37, 0x7b, 0xf0, 0xf4, 0xff, 0x3f, 0xfd, 0x8f, 0xba, 0x5e, 0x38,
            0x3a, 0x48, 0x57, 0x48, 0x02, 0xed, 0x54, 0x5b, 0xbe, 0x7a, 0x6b, 0x47, 0x53, 0x53,
            0x33, 0x53, 0xd7, 0x37, 0x06, 0x06, 0x76, 0x40, 0x13, 0x5a, 0x7c, 0xe5, 0x17, 0x27,
            0x9c, 0xd6, 0x83, 0x03, 0x97, 0x47, 0xd2, 0x18, 0x64, 0x7c, 0x86, 0xe0, 0x97, 0xb0,
            0xda, 0xa2, 0x87, 0x2d, 0x54, 0xb8, 0xf3, 0xe5, 0x08, 0x59, 0x87, 0x62, 0x95, 0x47,
            0xb8, 0x30, 0xd8, 0x11, 0x81, 0x61, 0xb6, 0x50, 0x79, 0xfe, 0x7b, 0xc5, 0x9a, 0x99,
            0xe9, 0xc3, 0xc7, 0x38, 0x0e, 0x3e, 0x70, 0xb7, 0x13, 0x8f, 0xe5, 0xd9, 0xbe, 0x25,
            0x51, 0x50, 0x2b, 0x69, 0x8d, 0x09, 0xae, 0x19, 0x39, 0x72, 0xf2, 0x7d, 0x40, 0xf3,
            0x8d, 0xea, 0x26, 0x4a, 0x01, 0x26, 0xe6, 0x37, 0xd7, 0x4a, 0xe4, 0xc9, 0x2a, 0x62,
            0x49, 0xfa, 0x10, 0x34, 0x36, 0xd3, 0xeb, 0x0d, 0x40, 0x29, 0xac, 0x71, 0x2b, 0xfc,
            0x7a, 0x5e, 0xac, 0xbd, 0xd7, 0x51, 0x8d, 0x6d, 0x4f, 0xe9, 0x03, 0xa5, 0xae, 0x65,
            0x52, 0x7c, 0xd6, 0x5b, 0xb0, 0xd4, 0xe9, 0x92, 0x5c, 0xa2, 0x4f, 0xd7, 0x21, 0x4d,
            0xc6, 0x17, 0xc1, 0x50, 0x54, 0x4e, 0x42, 0x3f, 0x45, 0x0c, 0x99, 0xce, 0x51, 0xac,
            0x80, 0x05, 0xd3, 0x3a, 0xcd, 0x74, 0xf1, 0xbe, 0xd3, 0xb1, 0x7b, 0x72, 0x66, 0xa4,
            0xa3, 0xbb, 0x86, 0xda, 0x7e, 0xba, 0x80, 0xb1, 0x01, 0xe1, 0x5c, 0xb7, 0x9d, 0xe9,
            0xa2, 0x07, 0x85, 0x2c, 0xf9, 0x12, 0x49, 0xef, 0x48, 0x06, 0x19, 0xff, 0x2a, 0xf8,
            0xca, 0xbc, 0xa8, 0x31, 0x25, 0xd1, 0xfa, 0xa9, 0x4c, 0xbb, 0x0a, 0x03, 0xa9, 0x06,
            0xf6, 0x83, 0xb3, 0xf4, 0x7a, 0x97, 0xc8, 0x71, 0xfd, 0x51, 0x3e, 0x51, 0x0a, 0x7a,
            0x25, 0xf2, 0x83, 0xb1, 0x96, 0x07, 0x57, 0x78, 0x49, 0x61, 0x52, 0xa9, 0x1c, 0x2b,
            0xf9, 0xda, 0x76, 0xeb, 0xe0, 0x89, 0xf4, 0x65, 0x48, 0x77, 0xf2, 0xd5, 0x86, 0xae,
            0x71, 0x49, 0xc4, 0x06, 0xe6, 0x63, 0xea, 0xde, 0xb2, 0xb5, 0xc7, 0xe8, 0x24, 0x29,
            0xb9, 0xe8, 0xcb, 0x48, 0x34, 0xc8, 0x34, 0x64, 0xf0, 0x79, 0x99, 0x53, 0x32, 0xe4,
            0xb3, 0xc8, 0xf5, 0xa7, 0x2b, 0xb4, 0xb8, 0xc6, 0xf7, 0x4b, 0x0d, 0x45, 0xdc, 0x6c,
            0x1f, 0x79, 0x95, 0x2c, 0x0b, 0x74, 0x20, 0xdf, 0x52, 0x5e, 0x37, 0xc1, 0x53, 0x77,
            0xb5, 0xf0, 0x98, 0x43, 0x19, 0xc3, 0x99, 0x39, 0x21, 0xe5, 0xcc, 0xd9, 0x7e, 0x09,
            0x75, 0x92, 0x06, 0x45, 0x30, 0xd3, 0x3d, 0xe3, 0xaf, 0xad, 0x57, 0x33, 0xcb, 0xe7,
            0x70, 0x3c, 0x52, 0x96, 0x26, 0x3f, 0x77, 0x34, 0x2e, 0xfb, 0xf5, 0xa0, 0x47, 0x55,
            0xb0, 0xb3, 0xc9, 0x97, 0xc4, 0x32, 0x84, 0x63, 0xe8, 0x4c, 0xaa, 0x2d, 0xe3, 0xff,
            0xdc, 0xd2, 0x97, 0xba, 0xaa, 0xac, 0xd7, 0xae, 0x64, 0x6e, 0x44, 0xb5, 0xc0, 0xf1,
            0x60, 0x44, 0xdf, 0x38, 0xfa, 0xbd, 0x29, 0x6a, 0x47, 0xb3, 0xa8, 0x38, 0xa9, 0x13,
            0x98, 0x2f, 0xb2, 0xe3, 0x70, 0xc0, 0x78, 0xed, 0xb0, 0x42, 0xc8, 0x4d, 0xb3, 0x4c,
            0xe3, 0x6b, 0x46, 0xcc, 0xb7, 0x64, 0x60, 0xa6, 0x90, 0xcc, 0x86, 0xc3, 0x02, 0x45,
            0x7d, 0xd1, 0xcd, 0xe1, 0x97, 0xec, 0x80, 0x75, 0xe8, 0x2b, 0x39, 0x3d, 0x54, 0x20,
            0x75, 0x13, 0x4e, 0x2a, 0x17, 0xee, 0x70, 0xa5, 0xe1, 0x87, 0x07, 0x5d, 0x03, 0xae,
            0x3c, 0x85, 0x3c, 0xff, 0x60, 0x72, 0x9b, 0xa4, 0x00, 0x00, 0x00, 0x05, 0x4d, 0xe1,
            0xf6, 0x96, 0x5b, 0xda, 0xbc, 0x67, 0x6c, 0x5a, 0x4d, 0xc7, 0xc3, 0x5f, 0x97, 0xf8,
            0x2c, 0xb0, 0xe3, 0x1c, 0x68, 0xd0, 0x4f, 0x1d, 0xad, 0x96, 0x31, 0x4f, 0xf0, 0x9e,
            0x6b, 0x3d, 0xe9, 0x6a, 0xee, 0xe3, 0x00, 0xd1, 0xf6, 0x8b, 0xf1, 0xbc, 0xa9, 0xfc,
            0x58, 0xe4, 0x03, 0x23, 0x36, 0xcd, 0x81, 0x9a, 0xaf, 0x57, 0x87, 0x44, 0xe5, 0x0d,
            0x13, 0x57, 0xa0, 0xe4, 0x28, 0x67, 0x04, 0xd3, 0x41, 0xaa, 0x0a, 0x33, 0x7b, 0x19,
            0xfe, 0x4b, 0xc4, 0x3c, 0x2e, 0x79, 0x96, 0x4d, 0x4f, 0x35, 0x10, 0x89, 0xf2, 0xe0,
            0xe4, 0x1c, 0x7c, 0x43, 0xae, 0x0d, 0x49, 0xe7, 0xf4, 0x04, 0xb0, 0xf7, 0x5b, 0xe8,
            0x0e, 0xa3, 0xaf, 0x09, 0x8c, 0x97, 0x52, 0x42, 0x0a, 0x8a, 0xc0, 0xea, 0x2b, 0xbb,
            0x1f, 0x4e, 0xeb, 0xa0, 0x52, 0x38, 0xae, 0xf0, 0xd8, 0xce, 0x63, 0xf0, 0xc6, 0xe5,
            0xe4, 0x04, 0x1d, 0x95, 0x39, 0x8a, 0x6f, 0x7f, 0x3e, 0x0e, 0xe9, 0x7c, 0xc1, 0x59,
            0x18, 0x49, 0xd4, 0xed, 0x23, 0x63, 0x38, 0xb1, 0x47, 0xab, 0xde, 0x9f, 0x51, 0xef,
            0x9f, 0xd4, 0xe1, 0xc1,
        ];

        let seed = hex!("a1c4696e2608035a886100d05cd99945eb3370731884a8235e2fb3d4d71f2547");
        let id = hex!("215f83b7ccb9acbcd08db97b0d04dc2b");
        let _expected_k = hex!("a1cd035833e0e90059603f26e07ad2aad152338e7a5e5984bcd5f7bb4eba40b7");

        let mut lms_priv = SigningKey::<LmsSha256M32H5<LmsOtsSha256N32W8>>::new_from_seed(id, seed);
        lms_priv.q = 4;
        let _lms_pub = lms_priv.public();

        let msg = "The enumeration in the Constitution, of certain rights, shall not be construed to deny or disparage others retained by the people.\n".as_bytes();

        use crate::ots::tests::ConstantRng;
        let c = hex!("0eb1ed54a2460d512388cad533138d240534e97b1e82d33bd927d201dfc24ebb");

        let mut rng = ConstantRng(&c);
        let sig = lms_priv
            .try_sign_with_rng(&mut rng, msg)
            .unwrap()
            .to_bytes();
        assert_eq!(sig.len(), expected_signature.len());
        assert_eq!(sig, expected_signature)
    }
}