bc_components/
lib.rs

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