rust_ipfs/p2p/transport/
misc.rs1use hkdf::Hkdf;
2use libp2p::identity::{self as identity, Keypair};
3use p256::ecdsa::signature::Signer;
4use rand::SeedableRng;
5use rand_chacha::ChaCha20Rng;
6use rcgen::{Certificate, CertificateParams, DnType, KeyPair};
7use sec1::{der::Encode, pkcs8::EncodePrivateKey};
8use sha2::Sha256;
9use std::io;
10use web_time::{Duration, SystemTime};
11
12const UNIX_2000: i64 = 946645200;
14
15const UNIX_3000: i64 = 32503640400;
17
18const ORGANISATION_NAME_OID: [u64; 4] = [2, 5, 4, 10];
20
21const EC_OID: [u64; 6] = [1, 2, 840, 10045, 2, 1];
23
24const P256_OID: [u64; 7] = [1, 2, 840, 10045, 3, 1, 7];
26
27const ECDSA_SHA256_OID: [u64; 7] = [1, 2, 840, 10045, 4, 3, 2];
29
30const ENCODE_CONFIG: pem::EncodeConfig = {
31 let line_ending = match cfg!(target_family = "windows") {
32 true => pem::LineEnding::CRLF,
33 false => pem::LineEnding::LF,
34 };
35 pem::EncodeConfig::new().set_line_ending(line_ending)
36};
37
38pub fn generate_cert(
43 keypair: &Keypair,
44 salt: &[u8],
45 expire: bool,
46) -> io::Result<(Certificate, KeyPair, Option<String>)> {
47 let internal_keypair = derive_keypair(keypair, salt)?;
48 let mut param =
49 CertificateParams::new(vec!["localhost".into()]).map_err(std::io::Error::other)?;
50 param.distinguished_name.push(
51 DnType::CommonName,
52 keypair.public().to_peer_id().to_string().as_str(),
53 );
54
55 let cert = param
57 .self_signed(&internal_keypair)
58 .map_err(std::io::Error::other)?;
59
60 let expired_pem = expire.then(|| {
61 let expired = SystemTime::UNIX_EPOCH
62 .checked_add(Duration::from_secs(UNIX_3000 as u64))
63 .expect("year 3000 to be representable by SystemTime")
64 .to_der()
65 .unwrap();
66
67 pem::encode_config(
68 &pem::Pem::new("EXPIRES".to_string(), expired),
69 ENCODE_CONFIG,
70 )
71 });
72
73 Ok((cert, internal_keypair, expired_pem))
74}
75
76#[allow(dead_code)]
80pub(crate) fn generate_wrtc_cert(keypair: &Keypair) -> io::Result<String> {
81 let (secret, public_key) = derive_keypair_secret(keypair, b"libp2p-webrtc")?;
82 let peer_id = keypair.public().to_peer_id();
83
84 let certificate = simple_x509::X509::builder()
85 .issuer_utf8(Vec::from(ORGANISATION_NAME_OID), "rust-ipfs")
86 .subject_utf8(Vec::from(ORGANISATION_NAME_OID), &peer_id.to_string())
87 .not_before_gen(UNIX_2000)
88 .not_after_gen(UNIX_3000)
89 .pub_key_ec(
90 Vec::from(EC_OID),
91 public_key.to_encoded_point(false).as_bytes().to_owned(),
92 Vec::from(P256_OID),
93 )
94 .sign_oid(Vec::from(ECDSA_SHA256_OID))
95 .build()
96 .sign(
97 |cert, _| {
98 let signature: p256::ecdsa::DerSignature = secret.sign(cert);
99 Some(signature.as_bytes().to_owned())
100 },
101 &[], )
103 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("{e:?}")))?;
104
105 let der_bytes = certificate.x509_enc().unwrap();
106
107 let cert_pem = pem::encode_config(
108 &pem::Pem::new("CERTIFICATE".to_string(), der_bytes),
109 ENCODE_CONFIG,
110 );
111
112 let private_pem = secret
113 .to_pkcs8_pem(Default::default())
114 .map_err(std::io::Error::other)?
115 .replace("PRIVATE KEY", "PRIVATE_KEY");
116
117 let expired_pem = {
118 let expired = SystemTime::UNIX_EPOCH
119 .checked_add(Duration::from_secs(UNIX_3000 as u64))
120 .expect("year 3000 to be representable by SystemTime")
121 .to_der()
122 .unwrap();
123
124 pem::encode_config(
125 &pem::Pem::new("EXPIRES".to_string(), expired),
126 ENCODE_CONFIG,
127 )
128 };
129
130 let pem = expired_pem + "\n\n" + &private_pem + "\n\n" + &cert_pem;
131
132 Ok(pem)
133}
134
135fn derive_keypair(keypair: &Keypair, salt: &[u8]) -> io::Result<KeyPair> {
136 let (secret, _) = derive_keypair_secret(keypair, salt)?;
137
138 let pem = secret
139 .to_pkcs8_pem(Default::default())
140 .map_err(std::io::Error::other)?;
141
142 KeyPair::from_pem(&pem).map_err(std::io::Error::other)
143}
144
145fn derive_keypair_secret(
146 keypair: &Keypair,
147 salt: &[u8],
148) -> io::Result<(p256::ecdsa::SigningKey, p256::ecdsa::VerifyingKey)> {
149 let secret = keypair_secret(keypair).ok_or(io::Error::from(io::ErrorKind::Unsupported))?;
150 let hkdf_gen = Hkdf::<Sha256>::from_prk(secret.as_ref()).expect("key length to be valid");
151
152 let mut seed = [0u8; 32];
153 hkdf_gen
154 .expand(salt, &mut seed)
155 .expect("key length to be valid");
156
157 let mut rng = ChaCha20Rng::from_seed(seed);
158
159 let secret = p256::ecdsa::SigningKey::random(&mut rng);
160 let public = p256::ecdsa::VerifyingKey::from(&secret);
161
162 Ok((secret, public))
163}
164
165fn keypair_secret(keypair: &Keypair) -> Option<[u8; 32]> {
166 match keypair.key_type() {
167 identity::KeyType::Ed25519 => {
168 let keypair = keypair.clone().try_into_ed25519().ok()?;
169 let secret = keypair.secret();
170 Some(secret.as_ref().try_into().expect("secret is 32 bytes"))
171 }
172 identity::KeyType::RSA => None,
173 identity::KeyType::Secp256k1 => {
174 let keypair = keypair.clone().try_into_secp256k1().ok()?;
175 let secret = keypair.secret();
176 Some(secret.to_bytes())
177 }
178 identity::KeyType::Ecdsa => {
179 let keypair = keypair.clone().try_into_ecdsa().ok()?;
180 Some(
181 keypair
182 .secret()
183 .to_bytes()
184 .try_into()
185 .expect("secret is 32 bytes"),
186 )
187 }
188 }
189}
190
191#[cfg(test)]
192mod test {
193 use libp2p::identity::Keypair;
194
195 use crate::p2p::transport::misc::generate_wrtc_cert;
196
197 const PEM: &str = r#"-----BEGIN EXPIRES-----
198GA8yOTk5MTIzMTEzMDAwMFo=
199-----END EXPIRES-----
200
201
202-----BEGIN PRIVATE_KEY-----
203MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgXARqgq74dVCrVR6G
204VT/iHnwBmx9s217QqvegG1xKNpqhRANCAAQvm08WYqoMCCEF36I5OAhA/XS7SqhR
2057n2CahGwC/fEqtvRrwAfZGejF21lzOW/m+A3EbDIzjy+xpUY+zaCE57V
206-----END PRIVATE_KEY-----
207
208
209-----BEGIN CERTIFICATE-----
210MIIBPjCB5QIBADAKBggqhkjOPQQDAjAUMRIwEAYDVQQKDAlydXN0LWlwZnMwIhgP
211MTk5OTEyMzExMzAwMDBaGA8yOTk5MTIzMTEzMDAwMFowPzE9MDsGA1UECgw0MTJE
212M0tvb1dQamNlUXJTd2RXWFB5TExlQUJSWG11cXQ2OVJnM3NCWWJVMU5mdDlIeVE2
213WDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABC+bTxZiqgwIIQXfojk4CED9dLtK
214qFHufYJqEbAL98Sq29GvAB9kZ6MXbWXM5b+b4DcRsMjOPL7GlRj7NoITntUwCgYI
215KoZIzj0EAwIDSAAwRQIhAP+F5COvtCQbZiyBQpAoiIoQP12KwIsNe1zhumki4bkU
216AiAH43Q833G8p1eXxqJr2xRrA1B5vCZ1qgl/44Z++NDMqQ==
217-----END CERTIFICATE-----
218"#;
219
220 #[test]
221 fn generate_cert() {
222 let keypair = generate_ed25519();
223 let pem = generate_wrtc_cert(&keypair).expect("not to fail");
224 assert_eq!(pem, PEM)
225 }
226
227 fn generate_ed25519() -> Keypair {
228 let mut bytes = [0u8; 32];
229 bytes[0] = 1;
230
231 Keypair::ed25519_from_bytes(bytes).expect("only errors on wrong length")
232 }
233}