RTCPeerConnection-generateCertificate.html (6229B)
1 <!doctype html> 2 <meta charset="utf-8"> 3 <meta name="timeout" content="long"> 4 <title>Test RTCPeerConnection.generateCertificate</title> 5 <script src="/resources/testharness.js"></script> 6 <script src="/resources/testharnessreport.js"></script> 7 <script src="./third_party/sdp/sdp.js"></script> 8 <script> 9 'use strict'; 10 11 // Test is based on the following editor draft: 12 // https://w3c.github.io/webrtc-pc/archives/20170515/webrtc.html 13 14 /* 15 * 4.10. Certificate Management 16 * partial interface RTCPeerConnection { 17 * static Promise<RTCCertificate> generateCertificate( 18 * AlgorithmIdentifier keygenAlgorithm); 19 * }; 20 * 21 * 4.10.2. RTCCertificate Interface 22 * interface RTCCertificate { 23 * readonly attribute DOMTimeStamp expires; 24 * ... 25 * }; 26 * 27 * [WebCrypto] 28 * 11. Algorithm Dictionary 29 * typedef (object or DOMString) AlgorithmIdentifier; 30 */ 31 32 /* 33 * 4.10. The following values must be supported by a user agent: 34 * { name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, 35 * publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256" }, 36 * and { name: "ECDSA", namedCurve: "P-256" }. 37 */ 38 [1024, 2048].forEach(modulusLength => { 39 promise_test(t => 40 // Test common RSA key sizes. Only 2048 is mandatory to support. 41 RTCPeerConnection.generateCertificate({ 42 name: 'RSASSA-PKCS1-v1_5', 43 modulusLength, 44 publicExponent: new Uint8Array([1, 0, 1]), 45 hash: 'SHA-256' 46 }).then(cert => { 47 assert_true(cert instanceof RTCCertificate, 48 'Expect cert to be instance of RTCCertificate'); 49 50 assert_greater_than(cert.expires, Date.now(), 51 'Expect generated certificate to expire reasonably long after current time'); 52 }), 53 `generateCertificate({modulusLength: ${modulusLength}}) with RSASSA-PKCS1-v1_5 parameters should succeed`); 54 }); 55 56 promise_test(t => 57 RTCPeerConnection.generateCertificate({ 58 name: 'ECDSA', 59 namedCurve: 'P-256' 60 }).then(cert => { 61 assert_true(cert instanceof RTCCertificate, 62 'Expect cert to be instance of RTCCertificate'); 63 64 assert_greater_than(cert.expires, Date.now(), 65 'Expect generated certificate to expire reasonably long after current time'); 66 }), 67 'generateCertificate() with compulsary ECDSA parameters should succeed'); 68 69 /* 70 * 4.10. A user agent must reject a call to generateCertificate() with a 71 * DOMException of type NotSupportedError if the keygenAlgorithm 72 * parameter identifies an algorithm that the user agent cannot or 73 * will not use to generate a certificate for RTCPeerConnection. 74 */ 75 promise_test(t => 76 promise_rejects_dom(t, 'NotSupportedError', 77 RTCPeerConnection.generateCertificate('invalid-algo')), 78 'generateCertificate() with invalid string algorithm should reject with NotSupportedError'); 79 80 promise_test(t => 81 promise_rejects_dom(t, 'NotSupportedError', 82 RTCPeerConnection.generateCertificate({ 83 name: 'invalid-algo' 84 })), 85 'generateCertificate() with invalid algorithm dict should reject with NotSupportedError'); 86 87 promise_test(t => 88 promise_rejects_dom(t, 'NotSupportedError', 89 RTCPeerConnection.generateCertificate({ 90 name: 'RSASSA-PKCS1-v1_5', 91 modulusLength: 2048, 92 publicExponent: new Uint8Array([1, 0, 1]), 93 hash: 'SHA-1' 94 })), 95 'generateCertificate with RSASSA-PKCS1-v1_5 parameters and SHA-1 signature should reject with NotSupportedError'); 96 97 /* 98 * 4.10.1. Dictionary RTCCertificateExpiration 99 * dictionary RTCCertificateExpiration { 100 * [EnforceRange] 101 * DOMTimeStamp expires; 102 * }; 103 * 104 * If this parameter is present it indicates the maximum time that 105 * the RTCCertificate is valid for relative to the current time. 106 * 107 * When generateCertificate is called with an object argument, 108 * the user agent attempts to convert the object into a 109 * RTCCertificateExpiration. If this is unsuccessful, immediately 110 * return a promise that is rejected with a newly created TypeError 111 * and abort processing. 112 */ 113 114 promise_test(t => { 115 const start = Date.now(); 116 return RTCPeerConnection.generateCertificate({ 117 name: 'ECDSA', 118 namedCurve: 'P-256', 119 expires: 2000 120 }).then(cert => { 121 assert_approx_equals(cert.expires, start+2000, 1000); 122 }) 123 }, 'generateCertificate() with valid expires parameter should succeed'); 124 125 promise_test(t => { 126 return RTCPeerConnection.generateCertificate({ 127 name: 'ECDSA', 128 namedCurve: 'P-256', 129 expires: 0 130 }).then(cert => { 131 assert_less_than_equal(cert.expires, Date.now()); 132 }) 133 }, 'generateCertificate() with 0 expires parameter should generate expired cert'); 134 135 promise_test(t => { 136 return promise_rejects_js(t, TypeError, 137 RTCPeerConnection.generateCertificate({ 138 name: 'ECDSA', 139 namedCurve: 'P-256', 140 expires: -1 141 })) 142 }, 'generateCertificate() with invalid range for expires should reject with TypeError'); 143 144 promise_test(t => { 145 return promise_rejects_js(t, TypeError, 146 RTCPeerConnection.generateCertificate({ 147 name: 'ECDSA', 148 namedCurve: 'P-256', 149 expires: 'invalid' 150 })) 151 }, 'generateCertificate() with invalid type for expires should reject with TypeError'); 152 153 promise_test(t => { 154 return RTCPeerConnection.generateCertificate({ 155 name: 'ECDSA', 156 namedCurve: 'P-256', 157 }).then(async cert => { 158 const pc = new RTCPeerConnection({certificates: [cert]}); 159 pc.createDataChannel('wpt'); 160 const offer = await pc.createOffer(); 161 const sections = SDPUtils.splitSections(offer.sdp); 162 const dtlsParameters = SDPUtils.getDtlsParameters(sections[1], sections[0]); 163 assert_equals(dtlsParameters.fingerprints[0].algorithm, cert.getFingerprints()[0].algorithm); 164 // https://www.rfc-editor.org/rfc/rfc4572#section-5 requires uppercase hex in the SDP. 165 assert_equals(dtlsParameters.fingerprints[0].value, cert.getFingerprints()[0].value.toUpperCase()); 166 }) 167 }, 'generateCertificate() certificate fingerprint shows up in the SDP'); 168 169 </script>