bc_components/
lib.rs

1#![doc(html_root_url = "https://docs.rs/bc-components/0.28.0")]
2#![warn(rust_2018_idioms)]
3
4//! # Introduction
5//!
6//! A collection of useful primitives for cryptography, semantic graphs, and
7//! cryptocurrency, primarily for use in higher-level [Blockchain
8//! Commons](https://blockchaincommons.com) projects like [Gordian
9//! Envelope](https://crates.io/crates/bc-envelope). All the types are
10//! [CBOR](https://cbor.io) serializable, and a number of them can also be
11//! serialized to and from [URs](https://crates.io/crates/bc-ur).
12//!
13//! Also includes a library of CBOR tags and UR types for use with these types.
14//!
15//! # Getting Started
16//!
17//! ```toml
18//! [dependencies]
19//! bc-components = "0.28.0"
20//! ```
21
22mod error;
23pub use error::{Error, Result};
24
25mod digest;
26pub use digest::Digest;
27
28mod id;
29pub use id::{ARID, URI, UUID, XID, XIDProvider};
30
31mod digest_provider;
32pub use digest_provider::DigestProvider;
33
34mod compressed;
35pub use compressed::Compressed;
36
37mod nonce;
38pub use nonce::Nonce;
39
40mod symmetric;
41pub use symmetric::{AuthenticationTag, EncryptedMessage, SymmetricKey};
42
43mod encrypted_key;
44pub use encrypted_key::*;
45
46mod salt;
47pub use salt::Salt;
48
49mod x25519;
50pub use x25519::{X25519PrivateKey, X25519PublicKey};
51
52#[cfg(feature = "ed25519")]
53mod ed25519;
54#[cfg(feature = "ed25519")]
55pub use ed25519::{Ed25519PrivateKey, Ed25519PublicKey};
56
57mod seed;
58pub use seed::Seed;
59
60mod signing;
61pub use signing::{
62    Signature, SignatureScheme, Signer, SigningOptions, SigningPrivateKey,
63    SigningPublicKey, Verifier,
64};
65
66mod encrypter;
67pub use encrypter::{Decrypter, Encrypter};
68
69#[cfg(feature = "secp256k1")]
70mod ec_key;
71#[cfg(feature = "secp256k1")]
72pub use ec_key::*;
73
74mod reference;
75/// CBOR Tags used or defined by this crate.
76pub use bc_tags as tags;
77pub use reference::*;
78pub mod tags_registry;
79pub use tags_registry::{register_tags, register_tags_in};
80
81mod private_key_data_provider;
82pub use private_key_data_provider::PrivateKeyDataProvider;
83
84mod private_key_base;
85pub use private_key_base::PrivateKeyBase;
86
87mod private_keys;
88pub use private_keys::{PrivateKeys, PrivateKeysProvider};
89
90mod public_keys;
91pub use public_keys::{PublicKeys, PublicKeysProvider};
92
93#[cfg(feature = "pqcrypto")]
94mod mldsa;
95#[cfg(feature = "pqcrypto")]
96pub use mldsa::{MLDSA, MLDSAPrivateKey, MLDSAPublicKey, MLDSASignature};
97
98#[cfg(feature = "pqcrypto")]
99mod mlkem;
100#[cfg(feature = "pqcrypto")]
101pub use mlkem::{MLKEM, MLKEMCiphertext, MLKEMPrivateKey, MLKEMPublicKey};
102
103mod encapsulation;
104pub use encapsulation::{
105    EncapsulationCiphertext, EncapsulationPrivateKey, EncapsulationPublicKey,
106    EncapsulationScheme, SealedMessage,
107};
108
109mod sskr_mod;
110pub use sskr::Error as SSKRError;
111pub use sskr_mod::{
112    SSKRGroupSpec, SSKRSecret, SSKRShare, SSKRSpec, sskr_combine,
113    sskr_generate, sskr_generate_using,
114};
115
116mod hkdf_rng;
117pub use hkdf_rng::HKDFRng;
118
119mod keypair;
120#[cfg(any(feature = "secp256k1", feature = "ed25519"))]
121pub use keypair::{keypair, keypair_using};
122pub use keypair::{keypair_opt, keypair_opt_using};
123
124#[cfg(test)]
125mod tests {
126    #[cfg(feature = "ssh")]
127    use std::ops::Deref;
128
129    #[cfg(feature = "secp256k1")]
130    use bc_crypto::{
131        ecdsa_new_private_key_using, ecdsa_public_key_from_private_key,
132        ecdsa_sign, ecdsa_verify, schnorr_public_key_from_private_key,
133        schnorr_sign_using, schnorr_verify,
134    };
135    use bc_rand::make_fake_random_number_generator;
136    use bc_ur::{URDecodable, UREncodable};
137    #[cfg(any(feature = "secp256k1", feature = "ssh"))]
138    use hex_literal::hex;
139    #[cfg(feature = "ssh")]
140    use indoc::indoc;
141    #[cfg(feature = "ssh")]
142    use ssh_key::{
143        Algorithm as SSHAlgorithm, EcdsaCurve, HashAlg, LineEnding,
144        PrivateKey as SSHPrivateKey, PublicKey as SSHPublicKey,
145    };
146
147    #[cfg(feature = "secp256k1")]
148    use crate::ECPrivateKey;
149    #[cfg(feature = "ssh")]
150    use crate::{PrivateKeyBase, Signature, Signer, SigningOptions, Verifier};
151    #[cfg(any(feature = "secp256k1", feature = "ssh"))]
152    use crate::{SigningPrivateKey, SigningPublicKey};
153    use crate::{X25519PrivateKey, X25519PublicKey};
154
155    #[test]
156    fn test_x25519_keys() {
157        crate::register_tags();
158        let mut rng = make_fake_random_number_generator();
159        let private_key = X25519PrivateKey::new_using(&mut rng);
160        let private_key_ur = private_key.ur_string();
161        assert_eq!(
162            private_key_ur,
163            "ur:agreement-private-key/hdcxkbrehkrkrsjztodseytknecfgewmgdmwfsvdvysbpmghuozsprknfwkpnehydlweynwkrtct"
164        );
165        assert_eq!(
166            X25519PrivateKey::from_ur_string(private_key_ur).unwrap(),
167            private_key
168        );
169
170        let public_key = private_key.public_key();
171        let public_key_ur = public_key.ur_string();
172        assert_eq!(
173            public_key_ur,
174            "ur:agreement-public-key/hdcxwnryknkbbymnoxhswmptgydsotwswsghfmrkksfxntbzjyrnuornkildchgswtdahehpwkrl"
175        );
176        assert_eq!(
177            X25519PublicKey::from_ur_string(public_key_ur).unwrap(),
178            public_key
179        );
180
181        let derived_private_key =
182            X25519PrivateKey::derive_from_key_material("password".as_bytes());
183        assert_eq!(
184            derived_private_key.ur_string(),
185            "ur:agreement-private-key/hdcxkgcfkomeeyiemywkftvabnrdolmttlrnfhjnguvaiehlrldmdpemgyjlatdthsnecytdoxat"
186        );
187    }
188
189    #[test]
190    fn test_agreement() {
191        let mut rng = make_fake_random_number_generator();
192        let alice_private_key = X25519PrivateKey::new_using(&mut rng);
193        let alice_public_key = alice_private_key.public_key();
194
195        let bob_private_key = X25519PrivateKey::new_using(&mut rng);
196        let bob_public_key = bob_private_key.public_key();
197
198        let alice_shared_key =
199            alice_private_key.shared_key_with(&bob_public_key);
200        let bob_shared_key = bob_private_key.shared_key_with(&alice_public_key);
201        assert_eq!(alice_shared_key, bob_shared_key);
202    }
203
204    #[test]
205    #[cfg(feature = "secp256k1")]
206    fn test_ecdsa_signing_keys() {
207        crate::register_tags();
208        let mut rng = make_fake_random_number_generator();
209        let schnorr_private_key =
210            SigningPrivateKey::new_schnorr(ECPrivateKey::new_using(&mut rng));
211        let schnorr_private_key_ur = schnorr_private_key.ur_string();
212        assert_eq!(
213            schnorr_private_key_ur,
214            "ur:signing-private-key/hdcxkbrehkrkrsjztodseytknecfgewmgdmwfsvdvysbpmghuozsprknfwkpnehydlweynwkrtct"
215        );
216        assert_eq!(
217            SigningPrivateKey::from_ur_string(schnorr_private_key_ur).unwrap(),
218            schnorr_private_key
219        );
220
221        let ecdsa_private_key =
222            SigningPrivateKey::new_ecdsa(ECPrivateKey::new_using(&mut rng));
223        let ecdsa_public_key = ecdsa_private_key.public_key().unwrap();
224        let ecdsa_public_key_ur = ecdsa_public_key.ur_string();
225        assert_eq!(
226            ecdsa_public_key_ur,
227            "ur:signing-public-key/lfadhdclaxbzutckgevlpkmdfnuoemlnvsgllokicfdekesswnfdtibkylrskomwgubaahyntaktbksbdt"
228        );
229        assert_eq!(
230            SigningPublicKey::from_ur_string(ecdsa_public_key_ur).unwrap(),
231            ecdsa_public_key
232        );
233
234        let schnorr_public_key = schnorr_private_key.public_key().unwrap();
235        let schnorr_public_key_ur = schnorr_public_key.ur_string();
236        assert_eq!(
237            schnorr_public_key_ur,
238            "ur:signing-public-key/hdcxjsrhdnidbgosndmobzwntdglzonnidmwoyrnuomdrpsptkcskerhfljssgaoidjewyjymhcp"
239        );
240        assert_eq!(
241            SigningPublicKey::from_ur_string(schnorr_public_key_ur).unwrap(),
242            schnorr_public_key
243        );
244
245        let derived_private_key = SigningPrivateKey::new_schnorr(
246            ECPrivateKey::derive_from_key_material("password".as_bytes()),
247        );
248        assert_eq!(
249            derived_private_key.ur_string(),
250            "ur:signing-private-key/hdcxahsfgobtpkkpahmnhsfmhnjnmkmkzeuraonneshkbysseyjkoeayrlvtvsmndicwkkvattfs"
251        );
252    }
253
254    #[cfg(feature = "ssh")]
255    fn test_ssh_signing(
256        algorithm: SSHAlgorithm,
257        expected_private_key: Option<&str>,
258        expected_public_key: Option<&str>,
259    ) {
260        const SEED: [u8; 16] = hex!("59f2293a5bce7d4de59e71b4207ac5d2");
261        let private_key_base = PrivateKeyBase::from_data(SEED);
262
263        let private_key: SigningPrivateKey = private_key_base
264            .ssh_signing_private_key(algorithm, "Key comment.")
265            .unwrap();
266        let ssh_private_key: &SSHPrivateKey = private_key.to_ssh().unwrap();
267        let ssh_private_key_string =
268            ssh_private_key.to_openssh(LineEnding::default()).unwrap();
269
270        let public_key: SigningPublicKey = private_key.public_key().unwrap();
271        let ssh_public_key: &SSHPublicKey = public_key.to_ssh().unwrap();
272        let ssh_public_key_string = ssh_public_key.to_openssh().unwrap();
273
274        if let Some(expected_private_key) = expected_private_key {
275            assert_eq!(ssh_private_key_string.deref(), expected_private_key);
276        } else {
277            println!("{}", *ssh_private_key_string);
278        }
279
280        if let Some(expected_public_key) = expected_public_key {
281            assert_eq!(ssh_public_key_string.deref(), expected_public_key);
282        } else {
283            println!("{}", ssh_public_key_string);
284        }
285
286        const MESSAGE: &dyn AsRef<[u8]> = b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";
287        let options = SigningOptions::Ssh {
288            namespace: "test".to_string(),
289            hash_alg: HashAlg::Sha256,
290        };
291        let signature: Signature = private_key
292            .sign_with_options(MESSAGE, Some(options))
293            .unwrap();
294        assert!(public_key.verify(&signature, MESSAGE));
295    }
296
297    #[test]
298    #[cfg(feature = "ssh")]
299    fn test_ssh_dsa_signing() {
300        #[rustfmt::skip]
301        let expected_private_key = Some(indoc! {r#"
302            -----BEGIN OPENSSH PRIVATE KEY-----
303            b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABsgAAAAdzc2gtZH
304            NzAAAAgQCWG4f7r8FAMT/IL11w9OfM/ZduIQ8vEq1Ub+uMdyJS8wS/jXL5OB2/dPnXCNSt
305            L4vjSqpDzMs+Dtd5wJy6baSQ3zGEbYv71mkIRJB/AtSVmd8FZe5AEjLFvHxYMSlO0jpi1Y
306            /1nLM7vLQu4QByDCLhYYjPxgrZKXB3cLxtjvly5wAAABUA4fIZLivnDVcg9PXzwcb5m07H
307            9k0AAACBAJK5Vm6t1Sg7n+C63wrNgDA6LTNyGzxqRVM2unI16jisCOzuC98Dgs+IbAkLhT
308            qWSY+nI+U9HBHc7sr+KKdWCzR76NLK5eSilXvtt8g+LfHIXvCjD4Q2puowtjDoXSEQAJYd
309            c1gtef21KZ2eoKoyAwzQIehCbvLpwYbxnhap5usVAAAAgGCrsbfReaDZo1Cw4/dFlJWBDP
310            sMGeG04/2hCThNmU+zLiKCwsEg0X6onOTMTonCXve3fVb5lNjIU92iTmt5QkmOj2hjsbgo
311            q/0sa0lALHp7UcK/W4IdU4Abtc4m0SUflgJcds1nsy2rKUNEtAfRa/WwtDResWOa4T7L+3
312            FEUdavAAAB6F0RJ3hdESd4AAAAB3NzaC1kc3MAAACBAJYbh/uvwUAxP8gvXXD058z9l24h
313            Dy8SrVRv64x3IlLzBL+Ncvk4Hb90+dcI1K0vi+NKqkPMyz4O13nAnLptpJDfMYRti/vWaQ
314            hEkH8C1JWZ3wVl7kASMsW8fFgxKU7SOmLVj/Wcszu8tC7hAHIMIuFhiM/GCtkpcHdwvG2O
315            +XLnAAAAFQDh8hkuK+cNVyD09fPBxvmbTsf2TQAAAIEAkrlWbq3VKDuf4LrfCs2AMDotM3
316            IbPGpFUza6cjXqOKwI7O4L3wOCz4hsCQuFOpZJj6cj5T0cEdzuyv4op1YLNHvo0srl5KKV
317            e+23yD4t8che8KMPhDam6jC2MOhdIRAAlh1zWC15/bUpnZ6gqjIDDNAh6EJu8unBhvGeFq
318            nm6xUAAACAYKuxt9F5oNmjULDj90WUlYEM+wwZ4bTj/aEJOE2ZT7MuIoLCwSDRfqic5MxO
319            icJe97d9VvmU2MhT3aJOa3lCSY6PaGOxuCir/SxrSUAsentRwr9bgh1TgBu1zibRJR+WAl
320            x2zWezLaspQ0S0B9Fr9bC0NF6xY5rhPsv7cURR1q8AAAAVANWljfuxQcmJ/T7wSmAUXmXo
321            6ZI0AAAADEtleSBjb21tZW50LgECAwQF
322            -----END OPENSSH PRIVATE KEY-----
323        "#});
324        let expected_public_key = Some(
325            "ssh-dss AAAAB3NzaC1kc3MAAACBAJYbh/uvwUAxP8gvXXD058z9l24hDy8SrVRv64x3IlLzBL+Ncvk4Hb90+dcI1K0vi+NKqkPMyz4O13nAnLptpJDfMYRti/vWaQhEkH8C1JWZ3wVl7kASMsW8fFgxKU7SOmLVj/Wcszu8tC7hAHIMIuFhiM/GCtkpcHdwvG2O+XLnAAAAFQDh8hkuK+cNVyD09fPBxvmbTsf2TQAAAIEAkrlWbq3VKDuf4LrfCs2AMDotM3IbPGpFUza6cjXqOKwI7O4L3wOCz4hsCQuFOpZJj6cj5T0cEdzuyv4op1YLNHvo0srl5KKVe+23yD4t8che8KMPhDam6jC2MOhdIRAAlh1zWC15/bUpnZ6gqjIDDNAh6EJu8unBhvGeFqnm6xUAAACAYKuxt9F5oNmjULDj90WUlYEM+wwZ4bTj/aEJOE2ZT7MuIoLCwSDRfqic5MxOicJe97d9VvmU2MhT3aJOa3lCSY6PaGOxuCir/SxrSUAsentRwr9bgh1TgBu1zibRJR+WAlx2zWezLaspQ0S0B9Fr9bC0NF6xY5rhPsv7cURR1q8= Key comment.",
326        );
327        test_ssh_signing(
328            SSHAlgorithm::Dsa,
329            expected_private_key,
330            expected_public_key,
331        );
332    }
333
334    #[test]
335    #[cfg(feature = "ssh")]
336    #[ignore]
337    fn test_ssh_dsa_nistp256_signing() {
338        #[rustfmt::skip]
339        let expected_private_key = Some(indoc! {r#"
340            -----BEGIN OPENSSH PRIVATE KEY-----
341            b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS
342            1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQTtBE6+WTueAierXl/c/f83JAmoxm0k
343            YlGMVMofLOUFeKx3FqUW0VRVljx1wHL03faFhiTPVR9CNG5iZCUqa4eLAAAAqPC+XgXwvl
344            4FAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBO0ETr5ZO54CJ6te
345            X9z9/zckCajGbSRiUYxUyh8s5QV4rHcWpRbRVFWWPHXAcvTd9oWGJM9VH0I0bmJkJSprh4
346            sAAAAgAVk1Bq0ILFsF/ADaUq8G5Tow0Xv+Qs8V21gfOBSWQDEAAAAMS2V5IGNvbW1lbnQu
347            AQIDBA==
348            -----END OPENSSH PRIVATE KEY-----
349        "#});
350        let expected_public_key = Some(
351            "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBO0ETr5ZO54CJ6teX9z9/zckCajGbSRiUYxUyh8s5QV4rHcWpRbRVFWWPHXAcvTd9oWGJM9VH0I0bmJkJSprh4s= Key comment.",
352        );
353        test_ssh_signing(
354            SSHAlgorithm::Ecdsa { curve: EcdsaCurve::NistP256 },
355            expected_private_key,
356            expected_public_key,
357        );
358    }
359
360    #[test]
361    #[cfg(feature = "ssh")]
362    #[ignore]
363    fn test_ssh_dsa_nistp384_signing() {
364        #[rustfmt::skip]
365        let expected_private_key = Some(indoc! {r#"
366            -----BEGIN OPENSSH PRIVATE KEY-----
367            b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAiAAAABNlY2RzYS
368            1zaGEyLW5pc3RwMzg0AAAACG5pc3RwMzg0AAAAYQSdYtV5QyUoBDDJX9gOG3DcJyv4qhjV
369            L7ntdIlyOCVCdqMMWa2EUsyxV/PLrrDYGCDUruf83rRNdJwuZ+7oZWm0N6yfLOT4QPQNxv
370            LqMJJ1hvw/xBxZVjMsr2gb/ohSG6IAAADYBFI75gRSO+YAAAATZWNkc2Etc2hhMi1uaXN0
371            cDM4NAAAAAhuaXN0cDM4NAAAAGEEnWLVeUMlKAQwyV/YDhtw3Ccr+KoY1S+57XSJcjglQn
372            ajDFmthFLMsVfzy66w2Bgg1K7n/N60TXScLmfu6GVptDesnyzk+ED0Dcby6jCSdYb8P8Qc
373            WVYzLK9oG/6IUhuiAAAAMQCFOcU/ldvVE92+kXn2C/q5+wuGX3Q61YHG3LNn4655GZeL7a
374            rH0jbCy0lsAQ5WbsMAAAAMS2V5IGNvbW1lbnQuAQID
375            -----END OPENSSH PRIVATE KEY-----
376        "#});
377        let expected_public_key = Some(
378            "ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBJ1i1XlDJSgEMMlf2A4bcNwnK/iqGNUvue10iXI4JUJ2owxZrYRSzLFX88uusNgYINSu5/zetE10nC5n7uhlabQ3rJ8s5PhA9A3G8uowknWG/D/EHFlWMyyvaBv+iFIbog== Key comment.",
379        );
380        test_ssh_signing(
381            SSHAlgorithm::Ecdsa { curve: EcdsaCurve::NistP384 },
382            expected_private_key,
383            expected_public_key,
384        );
385    }
386
387    // Should succeed but fails part of the time. See next test.
388    #[test]
389    #[cfg(feature = "ssh")]
390    #[ignore]
391    fn test_ssh_dsa_nistp521_signing() {
392        #[rustfmt::skip]
393        let expected_private_key = Some(indoc! {r#"
394            -----BEGIN OPENSSH PRIVATE KEY-----
395            b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAArAAAABNlY2RzYS
396            1zaGEyLW5pc3RwNTIxAAAACG5pc3RwNTIxAAAAhQQBD3AAo2UN1WreSuQWtp4DTbfzQ+D2
397            LyK9u5ykCfFXd/AMpQbyIyEQGbAiLNyAhGOfLgarJiAv4myKcHSGW2fTxQUB3V09IqubOw
398            JdNLaCJbszLQQSqoZlIWrXD51X7FdQFtXYY4GKmVeMKuK+u9Iby6F41nSrYpHlaFzzxr+D
399            5n1uq7cAAAEQrIPPE6yDzxMAAAATZWNkc2Etc2hhMi1uaXN0cDUyMQAAAAhuaXN0cDUyMQ
400            AAAIUEAQ9wAKNlDdVq3krkFraeA02380Pg9i8ivbucpAnxV3fwDKUG8iMhEBmwIizcgIRj
401            ny4GqyYgL+JsinB0hltn08UFAd1dPSKrmzsCXTS2giW7My0EEqqGZSFq1w+dV+xXUBbV2G
402            OBiplXjCrivrvSG8uheNZ0q2KR5Whc88a/g+Z9bqu3AAAAQgGDA9XptdyVFY5Svw8XXSJ5
403            7lrvc2R/T2CBthF0FgxqlNF5oTdqmrFuEqJ34oxIvhd9sJB/3qBpoJnPVKcuVmGC6gAAAA
404            xLZXkgY29tbWVudC4BAgMEBQY=
405            -----END OPENSSH PRIVATE KEY-----
406        "#});
407        let expected_public_key = Some(
408            "ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAEPcACjZQ3Vat5K5Ba2ngNNt/ND4PYvIr27nKQJ8Vd38AylBvIjIRAZsCIs3ICEY58uBqsmIC/ibIpwdIZbZ9PFBQHdXT0iq5s7Al00toIluzMtBBKqhmUhatcPnVfsV1AW1dhjgYqZV4wq4r670hvLoXjWdKtikeVoXPPGv4PmfW6rtw== Key comment.",
409        );
410        test_ssh_signing(
411            SSHAlgorithm::Ecdsa { curve: EcdsaCurve::NistP521 },
412            expected_private_key,
413            expected_public_key,
414        );
415    }
416
417    // Filed as https://github.com/RustCrypto/SSH/issues/232
418    #[cfg(feature = "ssh")]
419    #[ignore]
420    #[test]
421    fn test_dsa_nistp521() {
422        let encoded_key = r#"
423-----BEGIN OPENSSH PRIVATE KEY-----
424b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAArAAAABNlY2RzYS
4251zaGEyLW5pc3RwNTIxAAAACG5pc3RwNTIxAAAAhQQBD3AAo2UN1WreSuQWtp4DTbfzQ+D2
426LyK9u5ykCfFXd/AMpQbyIyEQGbAiLNyAhGOfLgarJiAv4myKcHSGW2fTxQUB3V09IqubOw
427JdNLaCJbszLQQSqoZlIWrXD51X7FdQFtXYY4GKmVeMKuK+u9Iby6F41nSrYpHlaFzzxr+D
4285n1uq7cAAAEQrIPPE6yDzxMAAAATZWNkc2Etc2hhMi1uaXN0cDUyMQAAAAhuaXN0cDUyMQ
429AAAIUEAQ9wAKNlDdVq3krkFraeA02380Pg9i8ivbucpAnxV3fwDKUG8iMhEBmwIizcgIRj
430ny4GqyYgL+JsinB0hltn08UFAd1dPSKrmzsCXTS2giW7My0EEqqGZSFq1w+dV+xXUBbV2G
431OBiplXjCrivrvSG8uheNZ0q2KR5Whc88a/g+Z9bqu3AAAAQgGDA9XptdyVFY5Svw8XXSJ5
4327lrvc2R/T2CBthF0FgxqlNF5oTdqmrFuEqJ34oxIvhd9sJB/3qBpoJnPVKcuVmGC6gAAAA
433xLZXkgY29tbWVudC4BAgMEBQY=
434-----END OPENSSH PRIVATE KEY-----
435"#;
436        let private_key = SSHPrivateKey::from_openssh(encoded_key).unwrap();
437        const MESSAGE: &[u8] = b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";
438        const NAMESPACE: &str = "test";
439        let signature = private_key
440            .sign(NAMESPACE, HashAlg::Sha256, MESSAGE)
441            .unwrap();
442        let public_key = private_key.public_key();
443        public_key.verify(NAMESPACE, MESSAGE, &signature).unwrap();
444    }
445
446    #[test]
447    #[cfg(feature = "ssh")]
448    fn test_ssh_ed25519_signing() {
449        #[rustfmt::skip]
450        let expected_private_key = Some(indoc! {r#"
451            -----BEGIN OPENSSH PRIVATE KEY-----
452            b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
453            QyNTUxOQAAACBUe4FDGyGIgHf75yVdE4hYl9guj02FdsIadgLC04zObQAAAJA+TyZiPk8m
454            YgAAAAtzc2gtZWQyNTUxOQAAACBUe4FDGyGIgHf75yVdE4hYl9guj02FdsIadgLC04zObQ
455            AAAECsX3CKi3hm5VrrU26ffa2FB2YrFogg45ucOVbIz4FQo1R7gUMbIYiAd/vnJV0TiFiX
456            2C6PTYV2whp2AsLTjM5tAAAADEtleSBjb21tZW50LgE=
457            -----END OPENSSH PRIVATE KEY-----
458        "#});
459        let expected_public_key = Some(
460            "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFR7gUMbIYiAd/vnJV0TiFiX2C6PTYV2whp2AsLTjM5t Key comment.",
461        );
462        test_ssh_signing(
463            SSHAlgorithm::Ed25519,
464            expected_private_key,
465            expected_public_key,
466        );
467    }
468
469    #[test]
470    #[cfg(feature = "secp256k1")]
471    fn test_ecdsa_signing() {
472        let mut rng = make_fake_random_number_generator();
473        let private_key = ecdsa_new_private_key_using(&mut rng);
474        const MESSAGE: &[u8] = b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";
475
476        let ecdsa_public_key = ecdsa_public_key_from_private_key(&private_key);
477        let ecdsa_signature = ecdsa_sign(&private_key, MESSAGE);
478        assert_eq!(
479            ecdsa_signature,
480            hex!(
481                "e75702ed8f645ce7fe510507b2403029e461ef4570d12aa440e4f81385546a13740b7d16878ff0b46b1cbe08bc218ccb0b00937b61c4707de2ca6148508e51fb"
482            )
483        );
484        assert!(ecdsa_verify(&ecdsa_public_key, &ecdsa_signature, MESSAGE));
485
486        let schnorr_public_key =
487            schnorr_public_key_from_private_key(&private_key);
488        let schnorr_signature =
489            schnorr_sign_using(&private_key, MESSAGE, &mut rng);
490        assert_eq!(
491            schnorr_signature,
492            hex!(
493                "df3e33900f0b94e23b6f8685f620ed92705ebfcf885ccb321620acb9927bce1e2218dcfba7cb9c3bba11611446f38774a564f265917899194e82945c8b60a996"
494            )
495        );
496        assert!(schnorr_verify(
497            &schnorr_public_key,
498            &schnorr_signature,
499            MESSAGE
500        ));
501    }
502
503    #[test]
504    fn test_readme_deps() {
505        version_sync::assert_markdown_deps_updated!("README.md");
506    }
507
508    #[test]
509    fn test_html_root_url() {
510        version_sync::assert_html_root_url_updated!("src/lib.rs");
511    }
512}