RTCCertificate.html (10740B)
1 <!doctype html> 2 <meta charset="utf-8"> 3 <title>RTCCertificate Tests</title> 4 <script src="/resources/testharness.js"></script> 5 <script src="/resources/testharnessreport.js"></script> 6 <script> 7 'use strict'; 8 9 // Test is based on the Candidate Recommendation: 10 // https://www.w3.org/TR/webrtc/ 11 12 /* 13 4.2.1. RTCConfiguration Dictionary 14 dictionary RTCConfiguration { 15 sequence<RTCCertificate> certificates; 16 ... 17 }; 18 19 certificates of type sequence<RTCCertificate> 20 If this value is absent, then a default set of certificates is 21 generated for each RTCPeerConnection instance. 22 23 The value for this configuration option cannot change after its 24 value is initially selected. 25 26 4.10.2. RTCCertificate Interface 27 interface RTCCertificate { 28 readonly attribute DOMTimeStamp expires; 29 static sequence<AlgorithmIdentifier> getSupportedAlgorithms(); 30 sequence<RTCDtlsFingerprint> getFingerprints(); 31 }; 32 33 5.5.1 The RTCDtlsFingerprint Dictionary 34 dictionary RTCDtlsFingerprint { 35 DOMString algorithm; 36 DOMString value; 37 }; 38 39 [RFC4572] Comedia over TLS in SDP 40 5. Fingerprint Attribute 41 Figure 2. Augmented Backus-Naur Syntax for the Fingerprint Attribute 42 43 attribute =/ fingerprint-attribute 44 45 fingerprint-attribute = "fingerprint" ":" hash-func SP fingerprint 46 47 hash-func = "sha-1" / "sha-224" / "sha-256" / 48 "sha-384" / "sha-512" / 49 "md5" / "md2" / token 50 ; Additional hash functions can only come 51 ; from updates to RFC 3279 52 53 fingerprint = 2UHEX *(":" 2UHEX) 54 ; Each byte in upper-case hex, separated 55 ; by colons. 56 57 UHEX = DIGIT / %x41-46 ; A-F uppercase 58 */ 59 60 // Helper function to generate certificate with a set of 61 // default parameters 62 function generateCertificate() { 63 return RTCPeerConnection.generateCertificate({ 64 name: 'ECDSA', 65 namedCurve: 'P-256' 66 }); 67 } 68 69 // Helper function that takes in an RTCDtlsFingerprint 70 // and return an a=fingerprint SDP line 71 function fingerprintToSdpLine(fingerprint) { 72 return `\r\na=fingerprint:${fingerprint.algorithm} ${fingerprint.value.toUpperCase()}\r\n`; 73 } 74 75 // Assert that an SDP string has fingerprint line for all the cert's fingerprints 76 function assert_sdp_has_cert_fingerprints(sdp, cert) { 77 for(const fingerprint of cert.getFingerprints()) { 78 const fingerprintLine = fingerprintToSdpLine(fingerprint); 79 assert_true(sdp.includes(fingerprintLine), 80 'Expect fingerprint line to be found in SDP'); 81 } 82 } 83 84 /* 85 4.3.1. Operation 86 When the RTCPeerConnection() constructor is invoked 87 2. If the certificates value in configuration is non-empty, 88 check that the expires on each value is in the future. 89 If a certificate has expired, throw an InvalidAccessError; 90 otherwise, store the certificates. If no certificates value 91 was specified, one or more new RTCCertificate instances are 92 generated for use with this RTCPeerConnection instance. 93 This may happen asynchronously and the value of certificates 94 remains undefined for the subsequent steps. 95 */ 96 promise_test(t => { 97 return RTCPeerConnection.generateCertificate({ 98 name: 'ECDSA', 99 namedCurve: 'P-256', 100 expires: 0 101 }).then(cert => { 102 assert_less_than_equal(cert.expires, Date.now()); 103 assert_throws_dom('InvalidAccessError', () => 104 new RTCPeerConnection({ certificates: [cert] })); 105 }); 106 }, 'Constructing RTCPeerConnection with expired certificate should reject with InvalidAccessError'); 107 108 /* 109 4.3.2 Interface Definition 110 setConfiguration 111 4. If configuration.certificates is set and the set of 112 certificates differs from the ones used when connection 113 was constructed, throw an InvalidModificationError. 114 */ 115 promise_test(t => { 116 return Promise.all([ 117 generateCertificate(), 118 generateCertificate() 119 ]).then(([cert1, cert2]) => { 120 const pc = new RTCPeerConnection({ 121 certificates: [cert1] 122 }); 123 124 // should not throw 125 pc.setConfiguration({ 126 certificates: [cert1] 127 }); 128 129 assert_throws_dom('InvalidModificationError', () => 130 pc.setConfiguration({ 131 certificates: [cert2] 132 })); 133 134 assert_throws_dom('InvalidModificationError', () => 135 pc.setConfiguration({ 136 certificates: [cert1, cert2] 137 })); 138 }); 139 }, 'Calling setConfiguration with different set of certs should reject with InvalidModificationError'); 140 141 /* 142 4.10.2. RTCCertificate Interface 143 getFingerprints 144 Returns the list of certificate fingerprints, one of which is 145 computed with the digest algorithm used in the certificate signature. 146 147 5.5.1 The RTCDtlsFingerprint Dictionary 148 algorithm of type DOMString 149 One of the hash function algorithms defined in the 'Hash function 150 Textual Names' registry, initially specified in [RFC4572] Section 8. 151 As noted in [JSEP] Section 5.2.1, the digest algorithm used for the 152 fingerprint matches that used in the certificate signature. 153 154 value of type DOMString 155 The value of the certificate fingerprint in lowercase hex string as 156 expressed utilizing the syntax of 'fingerprint' in [ RFC4572] Section 5. 157 158 */ 159 promise_test(t => { 160 return generateCertificate() 161 .then(cert => { 162 assert_idl_attribute(cert, 'getFingerprints'); 163 164 const fingerprints = cert.getFingerprints(); 165 assert_true(Array.isArray(fingerprints), 166 'Expect fingerprints to return an array'); 167 168 assert_greater_than_equal(fingerprints.length, 1, 169 'Expect at last one fingerprint in array'); 170 171 for(const fingerprint of fingerprints) { 172 assert_equals(typeof fingerprint, 'object', 173 'Expect fingerprint to be an object (dictionary)'); 174 175 // https://www.iana.org/assignments/hash-function-text-names/hash-function-text-names.xml 176 const algorithms = ['md2', 'md5', 'sha-1', 'sha-224', 'sha-256', 'sha-384', 'sha-512']; 177 assert_in_array(fingerprint.algorithm, algorithms, 178 'Expect fingerprint.algorithm to be string of algorithm identifier'); 179 180 assert_true(/^([0-9a-f]{2}\:)+[0-9a-f]{2}$/.test(fingerprint.value), 181 'Expect fingerprint.value to be lowercase hexadecimal separated by colon'); 182 } 183 }); 184 }, 'RTCCertificate should have at least one fingerprint'); 185 186 /* 187 4.3.2 Interface Definition 188 createOffer 189 The value for certificates in the RTCConfiguration for the 190 RTCPeerConnection is used to produce a set of certificate 191 fingerprints. These certificate fingerprints are used in the 192 construction of SDP and as input to requests for identity 193 assertions. 194 195 [JSEP] 196 5.2.1. Initial Offers 197 For DTLS, all m= sections MUST use all the certificate(s) that have 198 been specified for the PeerConnection; as a result, they MUST all 199 have the same [I-D.ietf-mmusic-4572-update] fingerprint value(s), or 200 these value(s) MUST be session-level attributes. 201 202 The following attributes, which are of category IDENTICAL or 203 TRANSPORT, MUST appear only in "m=" sections which either have a 204 unique address or which are associated with the bundle-tag. (In 205 initial offers, this means those "m=" sections which do not contain 206 an "a=bundle-only" attribute.) 207 208 - An "a=fingerprint" line for each of the endpoint's certificates, 209 as specified in [RFC4572], Section 5; the digest algorithm used 210 for the fingerprint MUST match that used in the certificate 211 signature. 212 213 Each m= section which is not bundled into another m= section, MUST 214 contain the following attributes (which are of category IDENTICAL or 215 TRANSPORT): 216 217 - An "a=fingerprint" line for each of the endpoint's certificates, 218 as specified in [RFC4572], Section 5; the digest algorithm used 219 for the fingerprint MUST match that used in the certificate 220 signature. 221 */ 222 promise_test(t => { 223 return generateCertificate() 224 .then(cert => { 225 const pc = new RTCPeerConnection({ 226 certificates: [cert] 227 }); 228 pc.createDataChannel('test'); 229 230 return pc.createOffer() 231 .then(offer => { 232 assert_sdp_has_cert_fingerprints(offer.sdp, cert); 233 }); 234 }); 235 }, 'RTCPeerConnection({ certificates }) should generate offer SDP with fingerprint of provided certificate'); 236 237 promise_test(t => { 238 return Promise.all([ 239 generateCertificate(), 240 generateCertificate() 241 ]).then(certs => { 242 const pc = new RTCPeerConnection({ 243 certificates: certs 244 }); 245 pc.createDataChannel('test'); 246 247 return pc.createOffer() 248 .then(offer => { 249 for(const cert of certs) { 250 assert_sdp_has_cert_fingerprints(offer.sdp, cert); 251 } 252 }); 253 }); 254 }, 'RTCPeerConnection({ certificates }) should generate offer SDP with fingerprint of all provided certificates'); 255 256 /* 257 TODO 258 259 4.10.2. RTCCertificate Interface 260 getSupportedAlgorithms 261 Returns a sequence providing a representative set of supported 262 certificate algorithms. At least one algorithm MUST be returned. 263 264 The RTCCertificate object can be stored and retrieved from persistent 265 storage by an application. When a user agent is required to obtain a 266 structured clone [HTML5] of a RTCCertificate object, it performs the 267 following steps: 268 1. Let input and memory be the corresponding inputs defined by the 269 internal structured cloning algorithm, where input represents a 270 RTCCertificate object to be cloned. 271 2. Let output be a newly constructed RTCCertificate object. 272 3. Copy the value of the expires attribute from input to output. 273 4. Let the [[certificate]] internal slot of output be set to the 274 result of invoking the internal structured clone algorithm 275 recursively on the corresponding internal slots of input, with 276 the slot contents as the new " input" argument and memory as 277 the new " memory" argument. 278 5. Let the [[handle]] internal slot of output refer to the same 279 private keying material represented by the [[handle]] internal 280 slot of input. 281 */ 282 283 </script>