aws_lc_rs/
ed25519.rs

1// Copyright 2015-2016 Brian Smith.
2// SPDX-License-Identifier: ISC
3// Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4// SPDX-License-Identifier: Apache-2.0 OR ISC
5
6use core::fmt;
7use core::fmt::{Debug, Formatter};
8use std::marker::PhantomData;
9
10#[cfg(feature = "ring-sig-verify")]
11use untrusted::Input;
12
13use crate::aws_lc::{EVP_PKEY, EVP_PKEY_ED25519};
14
15use crate::buffer::Buffer;
16use crate::encoding::{
17    AsBigEndian, AsDer, Curve25519SeedBin, Pkcs8V1Der, Pkcs8V2Der, PublicKeyX509Der,
18};
19use crate::error::{KeyRejected, Unspecified};
20use crate::evp_pkey::No_EVP_PKEY_CTX_consumer;
21use crate::pkcs8::{Document, Version};
22use crate::ptr::LcPtr;
23use crate::rand::SecureRandom;
24use crate::signature::{KeyPair, Signature, VerificationAlgorithm};
25use crate::{constant_time, hex, sealed};
26
27/// The length of an Ed25519 public key.
28pub const ED25519_PUBLIC_KEY_LEN: usize = crate::aws_lc::ED25519_PUBLIC_KEY_LEN as usize;
29const ED25519_SIGNATURE_LEN: usize = crate::aws_lc::ED25519_SIGNATURE_LEN as usize;
30const ED25519_SEED_LEN: usize = 32;
31
32/// Parameters for `EdDSA` signing and verification.
33#[derive(Debug)]
34pub struct EdDSAParameters;
35
36impl sealed::Sealed for EdDSAParameters {}
37
38impl VerificationAlgorithm for EdDSAParameters {
39    #[inline]
40    #[cfg(feature = "ring-sig-verify")]
41    fn verify(
42        &self,
43        public_key: Input<'_>,
44        msg: Input<'_>,
45        signature: Input<'_>,
46    ) -> Result<(), Unspecified> {
47        let evp_pkey = try_ed25519_public_key_from_bytes(public_key.as_slice_less_safe())?;
48        evp_pkey.verify(
49            msg.as_slice_less_safe(),
50            None,
51            No_EVP_PKEY_CTX_consumer,
52            signature.as_slice_less_safe(),
53        )
54    }
55
56    fn verify_sig(
57        &self,
58        public_key: &[u8],
59        msg: &[u8],
60        signature: &[u8],
61    ) -> Result<(), Unspecified> {
62        let evp_pkey = try_ed25519_public_key_from_bytes(public_key)?;
63        evp_pkey.verify(msg, None, No_EVP_PKEY_CTX_consumer, signature)
64    }
65}
66
67fn try_ed25519_public_key_from_bytes(key_bytes: &[u8]) -> Result<LcPtr<EVP_PKEY>, KeyRejected> {
68    // If the length of key bytes matches the raw public key size then it has to be that
69    if key_bytes.len() == ED25519_PUBLIC_KEY_LEN {
70        return LcPtr::<EVP_PKEY>::parse_raw_public_key(key_bytes, EVP_PKEY_ED25519);
71    }
72    // Otherwise we support X.509 SubjectPublicKeyInfo formatted keys which are inherently larger
73    LcPtr::<EVP_PKEY>::parse_rfc5280_public_key(key_bytes, EVP_PKEY_ED25519)
74}
75
76/// An Ed25519 key pair, for signing.
77#[allow(clippy::module_name_repetitions)]
78pub struct Ed25519KeyPair {
79    evp_pkey: LcPtr<EVP_PKEY>,
80    public_key: PublicKey,
81}
82
83impl Debug for Ed25519KeyPair {
84    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
85        f.write_str(&format!(
86            "Ed25519KeyPair {{ public_key: PublicKey(\"{}\") }}",
87            hex::encode(&self.public_key)
88        ))
89    }
90}
91
92#[derive(Clone)]
93#[allow(clippy::module_name_repetitions)]
94/// The seed value for the `EdDSA` signature scheme using Curve25519
95pub struct Seed<'a> {
96    bytes: Box<[u8]>,
97    phantom: PhantomData<&'a [u8]>,
98}
99
100impl AsBigEndian<Curve25519SeedBin<'static>> for Seed<'_> {
101    /// Exposes the seed encoded as a big-endian fixed-length integer.
102    ///
103    /// For most use-cases, `EcdsaKeyPair::to_pkcs8()` should be preferred.
104    ///
105    /// # Errors
106    /// `error::Unspecified` if serialization failed.
107    fn as_be_bytes(&self) -> Result<Curve25519SeedBin<'static>, Unspecified> {
108        Ok(Curve25519SeedBin::new(self.bytes.to_vec()))
109    }
110}
111
112impl Debug for Seed<'_> {
113    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
114        f.write_str("Ed25519Seed()")
115    }
116}
117
118#[derive(Clone)]
119#[allow(clippy::module_name_repetitions)]
120/// Ed25519 Public Key
121pub struct PublicKey {
122    evp_pkey: LcPtr<EVP_PKEY>,
123    public_key_bytes: [u8; ED25519_PUBLIC_KEY_LEN],
124}
125
126impl AsRef<[u8]> for PublicKey {
127    #[inline]
128    /// Returns the "raw" bytes of the ED25519 public key
129    fn as_ref(&self) -> &[u8] {
130        &self.public_key_bytes
131    }
132}
133
134impl Debug for PublicKey {
135    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
136        f.write_str(&format!(
137            "PublicKey(\"{}\")",
138            hex::encode(self.public_key_bytes)
139        ))
140    }
141}
142
143unsafe impl Send for PublicKey {}
144unsafe impl Sync for PublicKey {}
145
146impl AsDer<PublicKeyX509Der<'static>> for PublicKey {
147    /// Provides the public key as a DER-encoded (X.509) `SubjectPublicKeyInfo` structure.
148    /// # Errors
149    /// Returns an error if the public key fails to marshal to X.509.
150    fn as_der(&self) -> Result<PublicKeyX509Der<'static>, crate::error::Unspecified> {
151        // Initial size of 44 based on:
152        // 0:d=0  hl=2 l=  42 cons: SEQUENCE
153        // 2:d=1  hl=2 l=   5 cons:  SEQUENCE
154        // 4:d=2  hl=2 l=   3 prim:   OBJECT            :ED25519
155        // 9:d=1  hl=2 l=  33 prim:  BIT STRING
156        let der = self.evp_pkey.marshal_rfc5280_public_key()?;
157        Ok(PublicKeyX509Der::from(Buffer::new(der)))
158    }
159}
160
161impl KeyPair for Ed25519KeyPair {
162    type PublicKey = PublicKey;
163    #[inline]
164    fn public_key(&self) -> &Self::PublicKey {
165        &self.public_key
166    }
167}
168
169unsafe impl Send for Ed25519KeyPair {}
170unsafe impl Sync for Ed25519KeyPair {}
171
172pub(crate) fn generate_key() -> Result<LcPtr<EVP_PKEY>, Unspecified> {
173    LcPtr::<EVP_PKEY>::generate(EVP_PKEY_ED25519, No_EVP_PKEY_CTX_consumer)
174}
175
176impl Ed25519KeyPair {
177    /// Generates a new key pair and returns the key pair.
178    ///
179    /// # Errors
180    /// `error::Unspecified` if key generation fails.
181    pub fn generate() -> Result<Self, Unspecified> {
182        let evp_pkey = generate_key()?;
183
184        let mut public_key = [0u8; ED25519_PUBLIC_KEY_LEN];
185        let out_len: usize = evp_pkey.marshal_raw_public_to_buffer(&mut public_key)?;
186        debug_assert_eq!(public_key.len(), out_len);
187
188        Ok(Self {
189            public_key: PublicKey {
190                public_key_bytes: public_key,
191                evp_pkey: evp_pkey.clone(),
192            },
193            evp_pkey,
194        })
195    }
196
197    /// Generates a new key pair and returns the key pair serialized as a
198    /// PKCS#8 document.
199    ///
200    /// The PKCS#8 document will be a v2 `OneAsymmetricKey` with the public key,
201    /// as described in [RFC 5958 Section 2]; see [RFC 8410 Section 10.3] for an
202    /// example.
203    ///
204    /// [RFC 5958 Section 2]: https://tools.ietf.org/html/rfc5958#section-2
205    /// [RFC 8410 Section 10.3]: https://tools.ietf.org/html/rfc8410#section-10.3
206    ///
207    /// # *ring* Compatibility
208    /// The ring 0.16.x API did not produce encoded v2 documents that were compliant with RFC 5958.
209    /// The aws-lc-ring implementation produces PKCS#8 v2 encoded documents that are compliant per
210    /// the RFC specification.
211    ///
212    /// Our implementation ignores the `SecureRandom` parameter.
213    ///
214    // # FIPS
215    // This function must not be used.
216    //
217    /// # Errors
218    /// `error::Unspecified` if `rng` cannot provide enough bits or if there's an internal error.
219    pub fn generate_pkcs8(_rng: &dyn SecureRandom) -> Result<Document, Unspecified> {
220        let evp_pkey = generate_key()?;
221        Ok(Document::new(
222            evp_pkey.marshal_rfc5208_private_key(Version::V2)?,
223        ))
224    }
225
226    /// Serializes this `Ed25519KeyPair` into a PKCS#8 v2 document.
227    ///
228    /// # Errors
229    /// `error::Unspecified` on internal error.
230    ///
231    pub fn to_pkcs8(&self) -> Result<Document, Unspecified> {
232        Ok(Document::new(
233            self.evp_pkey.marshal_rfc5208_private_key(Version::V2)?,
234        ))
235    }
236
237    /// Generates a `Ed25519KeyPair` using the `rng` provided, then serializes that key as a
238    /// PKCS#8 document.
239    ///
240    /// The PKCS#8 document will be a v1 `PrivateKeyInfo` structure (RFC5208). Use this method
241    /// when needing to produce documents that are compatible with the OpenSSL CLI.
242    ///
243    /// # *ring* Compatibility
244    ///  Our implementation ignores the `SecureRandom` parameter.
245    ///
246    // # FIPS
247    // This function must not be used.
248    //
249    /// # Errors
250    /// `error::Unspecified` if `rng` cannot provide enough bits or if there's an internal error.
251    pub fn generate_pkcs8v1(_rng: &dyn SecureRandom) -> Result<Document, Unspecified> {
252        let evp_pkey = generate_key()?;
253        Ok(Document::new(
254            evp_pkey.marshal_rfc5208_private_key(Version::V1)?,
255        ))
256    }
257
258    /// Serializes this `Ed25519KeyPair` into a PKCS#8 v1 document.
259    ///
260    /// # Errors
261    /// `error::Unspecified` on internal error.
262    ///
263    pub fn to_pkcs8v1(&self) -> Result<Document, Unspecified> {
264        Ok(Document::new(
265            self.evp_pkey.marshal_rfc5208_private_key(Version::V1)?,
266        ))
267    }
268
269    /// Constructs an Ed25519 key pair from the private key seed `seed` and its
270    /// public key `public_key`.
271    ///
272    /// It is recommended to use `Ed25519KeyPair::from_pkcs8()` instead.
273    ///
274    /// The private and public keys will be verified to be consistent with each
275    /// other. This helps avoid misuse of the key (e.g. accidentally swapping
276    /// the private key and public key, or using the wrong private key for the
277    /// public key). This also detects any corruption of the public or private
278    /// key.
279    ///
280    /// # Errors
281    /// `error::KeyRejected` if parse error, or if key is otherwise unacceptable.
282    pub fn from_seed_and_public_key(seed: &[u8], public_key: &[u8]) -> Result<Self, KeyRejected> {
283        let this = Self::from_seed_unchecked(seed)?;
284
285        constant_time::verify_slices_are_equal(public_key, &this.public_key.public_key_bytes)
286            .map_err(|_| KeyRejected::inconsistent_components())?;
287        Ok(this)
288    }
289
290    /// Constructs an Ed25519 key pair from the private key seed `seed`.
291    ///
292    /// It is recommended to use `Ed25519KeyPair::from_pkcs8()` instead. If the public key is
293    /// available, prefer to use `Ed25519KeyPair::from_seed_and_public_key()` as it will verify
294    /// the validity of the key pair.
295    ///
296    /// CAUTION: Both an Ed25519 seed and its public key are 32-bytes. If the bytes of a public key
297    /// are provided this function will create an (effectively) invalid `Ed25519KeyPair`. This
298    /// problem is undetectable by the API.
299    ///
300    /// # Errors
301    /// `error::KeyRejected` if parse error, or if key is otherwise unacceptable.
302    pub fn from_seed_unchecked(seed: &[u8]) -> Result<Self, KeyRejected> {
303        if seed.len() < ED25519_SEED_LEN {
304            return Err(KeyRejected::inconsistent_components());
305        }
306
307        let evp_pkey = LcPtr::<EVP_PKEY>::parse_raw_private_key(seed, EVP_PKEY_ED25519)?;
308
309        let mut derived_public_key = [0u8; ED25519_PUBLIC_KEY_LEN];
310        let out_len: usize = evp_pkey.marshal_raw_public_to_buffer(&mut derived_public_key)?;
311        debug_assert_eq!(derived_public_key.len(), out_len);
312
313        Ok(Self {
314            public_key: PublicKey {
315                public_key_bytes: derived_public_key,
316                evp_pkey: evp_pkey.clone(),
317            },
318            evp_pkey,
319        })
320    }
321
322    /// Constructs an Ed25519 key pair by parsing an unencrypted PKCS#8 v1 or v2
323    /// Ed25519 private key.
324    ///
325    /// `openssl genpkey -algorithm ED25519` generates PKCS#8 v1 keys.
326    ///
327    /// # Ring Compatibility
328    /// * This method accepts either v1 or v2 encoded keys, if a v2 encoded key is provided, with the
329    ///   public key component present, it will be verified to match the one derived from the
330    ///   encoded private key.
331    /// * The ring 0.16.x API did not produce encoded v2 documents that were compliant with RFC 5958.
332    ///   The aws-lc-ring implementation produces PKCS#8 v2 encoded documents that are compliant per
333    ///   the RFC specification.
334    ///
335    /// # Errors
336    /// `error::KeyRejected` on parse error, or if key is otherwise unacceptable.
337    pub fn from_pkcs8(pkcs8: &[u8]) -> Result<Self, KeyRejected> {
338        Self::parse_pkcs8(pkcs8)
339    }
340
341    /// Constructs an Ed25519 key pair by parsing an unencrypted PKCS#8 v1 or v2
342    /// Ed25519 private key.
343    ///
344    /// `openssl genpkey -algorithm ED25519` generates PKCS# v1 keys.
345    ///
346    /// # Ring Compatibility
347    /// * This method accepts either v1 or v2 encoded keys, if a v2 encoded key is provided, with the
348    ///   public key component present, it will be verified to match the one derived from the
349    ///   encoded private key.
350    /// * The ring 0.16.x API did not produce encoded v2 documents that were compliant with RFC 5958.
351    ///   The aws-lc-ring implementation produces PKCS#8 v2 encoded documents that are compliant per
352    ///   the RFC specification.
353    ///
354    /// # Errors
355    /// `error::KeyRejected` on parse error, or if key is otherwise unacceptable.
356    pub fn from_pkcs8_maybe_unchecked(pkcs8: &[u8]) -> Result<Self, KeyRejected> {
357        Self::parse_pkcs8(pkcs8)
358    }
359
360    fn parse_pkcs8(pkcs8: &[u8]) -> Result<Self, KeyRejected> {
361        let evp_pkey = LcPtr::<EVP_PKEY>::parse_rfc5208_private_key(pkcs8, EVP_PKEY_ED25519)?;
362
363        evp_pkey.validate_as_ed25519()?;
364
365        let mut public_key = [0u8; ED25519_PUBLIC_KEY_LEN];
366        let out_len: usize = evp_pkey.marshal_raw_public_to_buffer(&mut public_key)?;
367        debug_assert_eq!(public_key.len(), out_len);
368
369        Ok(Self {
370            public_key: PublicKey {
371                public_key_bytes: public_key,
372                evp_pkey: evp_pkey.clone(),
373            },
374            evp_pkey,
375        })
376    }
377
378    /// Returns the signature of the message msg.
379    ///
380    // # FIPS
381    // This method must not be used.
382    //
383    /// # Panics
384    /// Panics if the message is unable to be signed
385    #[inline]
386    #[must_use]
387    pub fn sign(&self, msg: &[u8]) -> Signature {
388        Self::try_sign(self, msg).expect("ED25519 signing failed")
389    }
390
391    #[inline]
392    fn try_sign(&self, msg: &[u8]) -> Result<Signature, Unspecified> {
393        let sig_bytes = self.evp_pkey.sign(msg, None, No_EVP_PKEY_CTX_consumer)?;
394
395        Ok(Signature::new(|slice| {
396            slice[0..ED25519_SIGNATURE_LEN].copy_from_slice(&sig_bytes);
397            ED25519_SIGNATURE_LEN
398        }))
399    }
400
401    /// Provides the private key "seed" for this `Ed25519` key pair.
402    ///
403    /// For serialization of the key pair, `Ed25519KeyPair::to_pkcs8()` is preferred.
404    ///
405    /// # Errors
406    /// Currently the function cannot fail, but it might in future implementations.
407    pub fn seed(&self) -> Result<Seed<'static>, Unspecified> {
408        Ok(Seed {
409            bytes: self.evp_pkey.marshal_raw_private_key()?.into_boxed_slice(),
410            phantom: PhantomData,
411        })
412    }
413}
414
415impl AsDer<Pkcs8V1Der<'static>> for Ed25519KeyPair {
416    /// Serializes this `Ed25519KeyPair` into a PKCS#8 v1 document.
417    ///
418    /// # Errors
419    /// `error::Unspecified` on internal error.
420    fn as_der(&self) -> Result<Pkcs8V1Der<'static>, crate::error::Unspecified> {
421        Ok(Pkcs8V1Der::new(
422            self.evp_pkey.marshal_rfc5208_private_key(Version::V1)?,
423        ))
424    }
425}
426
427impl AsDer<Pkcs8V2Der<'static>> for Ed25519KeyPair {
428    /// Serializes this `Ed25519KeyPair` into a PKCS#8 v1 document.
429    ///
430    /// # Errors
431    /// `error::Unspecified` on internal error.
432    fn as_der(&self) -> Result<Pkcs8V2Der<'static>, crate::error::Unspecified> {
433        Ok(Pkcs8V2Der::new(
434            self.evp_pkey.marshal_rfc5208_private_key(Version::V2)?,
435        ))
436    }
437}
438
439#[cfg(test)]
440mod tests {
441    use crate::ed25519::Ed25519KeyPair;
442    use crate::encoding::{AsBigEndian, AsDer, Pkcs8V1Der, Pkcs8V2Der, PublicKeyX509Der};
443    use crate::rand::SystemRandom;
444    use crate::signature::{KeyPair, UnparsedPublicKey, ED25519};
445    use crate::{hex, test};
446
447    #[test]
448    fn test_generate() {
449        const MESSAGE: &[u8] = b"test message";
450        let key_pair = Ed25519KeyPair::generate().unwrap();
451        let public_key = key_pair.public_key();
452        let signature = key_pair.sign(MESSAGE);
453        let unparsed_public_key = UnparsedPublicKey::new(&ED25519, public_key.as_ref());
454        unparsed_public_key
455            .verify(MESSAGE, signature.as_ref())
456            .unwrap();
457    }
458
459    #[test]
460    fn test_generate_pkcs8() {
461        let rng = SystemRandom::new();
462        let document = Ed25519KeyPair::generate_pkcs8(&rng).unwrap();
463        let kp1: Ed25519KeyPair = Ed25519KeyPair::from_pkcs8(document.as_ref()).unwrap();
464        assert_eq!(
465            document.as_ref(),
466            AsDer::<Pkcs8V2Der>::as_der(&kp1).unwrap().as_ref()
467        );
468        let kp2: Ed25519KeyPair =
469            Ed25519KeyPair::from_pkcs8_maybe_unchecked(document.as_ref()).unwrap();
470        assert_eq!(
471            kp1.seed().unwrap().as_be_bytes().unwrap().as_ref(),
472            kp2.seed().unwrap().as_be_bytes().unwrap().as_ref(),
473        );
474        assert_eq!(kp1.public_key.as_ref(), kp2.public_key.as_ref());
475
476        let document = Ed25519KeyPair::generate_pkcs8v1(&rng).unwrap();
477        let kp1: Ed25519KeyPair = Ed25519KeyPair::from_pkcs8(document.as_ref()).unwrap();
478        assert_eq!(
479            document.as_ref(),
480            AsDer::<Pkcs8V1Der>::as_der(&kp1).unwrap().as_ref()
481        );
482        let kp2: Ed25519KeyPair =
483            Ed25519KeyPair::from_pkcs8_maybe_unchecked(document.as_ref()).unwrap();
484        assert_eq!(
485            kp1.seed().unwrap().as_be_bytes().unwrap().as_ref(),
486            kp2.seed().unwrap().as_be_bytes().unwrap().as_ref(),
487        );
488        assert_eq!(kp1.public_key.as_ref(), kp2.public_key.as_ref());
489        let seed = kp1.seed().unwrap();
490        assert_eq!("Ed25519Seed()", format!("{seed:?}"));
491    }
492
493    #[test]
494    fn test_from_pkcs8() {
495        struct TestCase {
496            key: &'static str,
497            expected_public: &'static str,
498        }
499
500        for case in [
501            TestCase {
502                key: "302e020100300506032b6570042204209d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
503                expected_public: "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
504            },
505            TestCase {
506                key: "3051020101300506032b657004220420756434bd5b824753007a138d27abbc14b5cc786adb78fb62435e6419a2b2e72b8121000faccd81e57de15fa6343a7fbb43b2b93f28be6435100ae8bd633c6dfee3d198",
507                expected_public: "0faccd81e57de15fa6343a7fbb43b2b93f28be6435100ae8bd633c6dfee3d198",
508            },
509            TestCase {
510                key: "304f020100300506032b657004220420d4ee72dbf913584ad5b6d8f1f769f8ad3afe7c28cbf1d4fbe097a88f44755842a01f301d060a2a864886f70d01090914310f0c0d437572646c6520436861697273",
511                expected_public: "19bf44096984cdfe8541bac167dc3b96c85086aa30b6b6cb0c5c38ad703166e1",
512            },
513            TestCase {
514                key: "3072020101300506032b657004220420d4ee72dbf913584ad5b6d8f1f769f8ad3afe7c28cbf1d4fbe097a88f44755842a01f301d060a2a864886f70d01090914310f0c0d437572646c652043686169727381210019bf44096984cdfe8541bac167dc3b96c85086aa30b6b6cb0c5c38ad703166e1",
515                expected_public: "19bf44096984cdfe8541bac167dc3b96c85086aa30b6b6cb0c5c38ad703166e1",
516            }
517        ] {
518            let key_pair = Ed25519KeyPair::from_pkcs8(&test::from_dirty_hex(case.key)).unwrap();
519            assert_eq!(
520                format!(
521                    r#"Ed25519KeyPair {{ public_key: PublicKey("{}") }}"#,
522                    case.expected_public
523                ),
524                format!("{key_pair:?}")
525            );
526            let key_pair = Ed25519KeyPair::from_pkcs8_maybe_unchecked(&test::from_dirty_hex(case.key)).unwrap();
527            assert_eq!(
528                format!(
529                    r#"Ed25519KeyPair {{ public_key: PublicKey("{}") }}"#,
530                    case.expected_public
531                ),
532                format!("{key_pair:?}")
533            );
534        }
535    }
536
537    #[test]
538    fn test_public_key_as_der_x509() {
539        let key_pair = Ed25519KeyPair::from_pkcs8(&hex::decode("302e020100300506032b6570042204209d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60").unwrap()).unwrap();
540        let public_key = key_pair.public_key();
541        let x509der = AsDer::<PublicKeyX509Der>::as_der(public_key).unwrap();
542        assert_eq!(
543            x509der.as_ref(),
544            &[
545                0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x03, 0x21, 0x00, 0xd7, 0x5a,
546                0x98, 0x01, 0x82, 0xb1, 0x0a, 0xb7, 0xd5, 0x4b, 0xfe, 0xd3, 0xc9, 0x64, 0x07, 0x3a,
547                0x0e, 0xe1, 0x72, 0xf3, 0xda, 0xa6, 0x23, 0x25, 0xaf, 0x02, 0x1a, 0x68, 0xf7, 0x07,
548                0x51, 0x1a
549            ]
550        );
551    }
552}