1
1
use hkdf:: Hkdf ;
2
2
use libp2p:: identity:: { self as identity, Keypair } ;
3
+ use p256:: ecdsa:: signature:: Signer ;
3
4
use rand:: SeedableRng ;
4
5
use rand_chacha:: ChaCha20Rng ;
5
6
use rcgen:: { Certificate , CertificateParams , DnType , KeyPair } ;
@@ -8,8 +9,24 @@ use sha2::Sha256;
8
9
use std:: io;
9
10
use web_time:: { Duration , SystemTime } ;
10
11
12
+ /// The year 2000.
13
+ const UNIX_2000 : i64 = 946645200 ;
14
+
15
+ /// The year 3000.
11
16
const UNIX_3000 : i64 = 32503640400 ;
12
17
18
+ /// OID for the organisation name. See <http://oid-info.com/get/2.5.4.10>.
19
+ const ORGANISATION_NAME_OID : [ u64 ; 4 ] = [ 2 , 5 , 4 , 10 ] ;
20
+
21
+ /// OID for Elliptic Curve Public Key Cryptography. See <http://oid-info.com/get/1.2.840.10045.2.1>.
22
+ const EC_OID : [ u64 ; 6 ] = [ 1 , 2 , 840 , 10045 , 2 , 1 ] ;
23
+
24
+ /// OID for 256-bit Elliptic Curve Cryptography (ECC) with the P256 curve. See <http://oid-info.com/get/1.2.840.10045.3.1.7>.
25
+ const P256_OID : [ u64 ; 7 ] = [ 1 , 2 , 840 , 10045 , 3 , 1 , 7 ] ;
26
+
27
+ /// OID for the ECDSA signature algorithm with using SHA256 as the hash function. See <http://oid-info.com/get/1.2.840.10045.4.3.2>.
28
+ const ECDSA_SHA256_OID : [ u64 ; 7 ] = [ 1 , 2 , 840 , 10045 , 4 , 3 , 2 ] ;
29
+
13
30
const ENCODE_CONFIG : pem:: EncodeConfig = {
14
31
let line_ending = match cfg ! ( target_family = "windows" ) {
15
32
true => pem:: LineEnding :: CRLF ,
@@ -20,7 +37,7 @@ const ENCODE_CONFIG: pem::EncodeConfig = {
20
37
21
38
/// Generates a TLS certificate that derives from libp2p `Keypair` with a salt.
22
39
/// Note: If `expire` is true, it will produce a expired pem that can be appended for webrtc transport
23
- /// Additionally, this function does not generate deterministic certs *yet* due to
40
+ /// Additionally, this function does not generate deterministic certs *yet* due to
24
41
/// `CertificateParams::self_signed` using ring rng. This may change in the future
25
42
pub fn generate_cert (
26
43
keypair : & Keypair ,
@@ -56,8 +73,79 @@ pub fn generate_cert(
56
73
Ok ( ( cert, internal_keypair, expired_pem) )
57
74
}
58
75
76
+ /// Used to generate webrtc certificates.
77
+ /// Note: Although simple_x509 does not deal with crypto directly (eg signing certificate)
78
+ /// we would still have to be careful of any changes upstream that may cause a change in the certificate
79
+ #[ allow( dead_code) ]
80
+ pub ( 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
+ & [ ] , // We close over the keypair so no need to pass it.
102
+ )
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
+
59
135
fn derive_keypair ( keypair : & Keypair , salt : & [ u8 ] ) -> io:: Result < KeyPair > {
60
- //Note: We could use `Keypair::derive_secret`, but this seems more sensible?
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
+
145
+ fn derive_keypair_secret (
146
+ keypair : & Keypair ,
147
+ salt : & [ u8 ] ,
148
+ ) -> io:: Result < ( p256:: ecdsa:: SigningKey , p256:: ecdsa:: VerifyingKey ) > {
61
149
let secret = keypair_secret ( keypair) . ok_or ( io:: Error :: from ( io:: ErrorKind :: Unsupported ) ) ?;
62
150
let hkdf_gen = Hkdf :: < Sha256 > :: from_prk ( secret. as_ref ( ) ) . expect ( "key length to be valid" ) ;
63
151
@@ -69,12 +157,9 @@ fn derive_keypair(keypair: &Keypair, salt: &[u8]) -> io::Result<KeyPair> {
69
157
let mut rng = ChaCha20Rng :: from_seed ( seed) ;
70
158
71
159
let secret = p256:: ecdsa:: SigningKey :: random ( & mut rng) ;
160
+ let public = p256:: ecdsa:: VerifyingKey :: from ( & secret) ;
72
161
73
- let pem = secret
74
- . to_pkcs8_pem ( Default :: default ( ) )
75
- . map_err ( std:: io:: Error :: other) ?;
76
-
77
- KeyPair :: from_pem ( & pem) . map_err ( std:: io:: Error :: other)
162
+ Ok ( ( secret, public) )
78
163
}
79
164
80
165
fn keypair_secret ( keypair : & Keypair ) -> Option < [ u8 ; 32 ] > {
@@ -102,3 +187,48 @@ fn keypair_secret(keypair: &Keypair) -> Option<[u8; 32]> {
102
187
}
103
188
}
104
189
}
190
+
191
+ #[ cfg( test) ]
192
+ mod test {
193
+ use libp2p:: identity:: Keypair ;
194
+
195
+ use crate :: p2p:: transport:: misc:: generate_wrtc_cert;
196
+
197
+ const PEM : & str = r#"-----BEGIN EXPIRES-----
198
+ GA8yOTk5MTIzMTEzMDAwMFo=
199
+ -----END EXPIRES-----
200
+
201
+
202
+ -----BEGIN PRIVATE_KEY-----
203
+ MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgXARqgq74dVCrVR6G
204
+ VT/iHnwBmx9s217QqvegG1xKNpqhRANCAAQvm08WYqoMCCEF36I5OAhA/XS7SqhR
205
+ 7n2CahGwC/fEqtvRrwAfZGejF21lzOW/m+A3EbDIzjy+xpUY+zaCE57V
206
+ -----END PRIVATE_KEY-----
207
+
208
+
209
+ -----BEGIN CERTIFICATE-----
210
+ MIIBPjCB5QIBADAKBggqhkjOPQQDAjAUMRIwEAYDVQQKDAlydXN0LWlwZnMwIhgP
211
+ MTk5OTEyMzExMzAwMDBaGA8yOTk5MTIzMTEzMDAwMFowPzE9MDsGA1UECgw0MTJE
212
+ M0tvb1dQamNlUXJTd2RXWFB5TExlQUJSWG11cXQ2OVJnM3NCWWJVMU5mdDlIeVE2
213
+ WDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABC+bTxZiqgwIIQXfojk4CED9dLtK
214
+ qFHufYJqEbAL98Sq29GvAB9kZ6MXbWXM5b+b4DcRsMjOPL7GlRj7NoITntUwCgYI
215
+ KoZIzj0EAwIDSAAwRQIhAP+F5COvtCQbZiyBQpAoiIoQP12KwIsNe1zhumki4bkU
216
+ AiAH43Q833G8p1eXxqJr2xRrA1B5vCZ1qgl/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
+
234
+ }
0 commit comments