bc_components/
lib.rs

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