bc_components/
lib.rs

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