diff options
Diffstat (limited to 'testing/web-platform/tests/webrtc/RTCCertificate.html')
-rw-r--r-- | testing/web-platform/tests/webrtc/RTCCertificate.html | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webrtc/RTCCertificate.html b/testing/web-platform/tests/webrtc/RTCCertificate.html new file mode 100644 index 0000000000..6b7626c92e --- /dev/null +++ b/testing/web-platform/tests/webrtc/RTCCertificate.html @@ -0,0 +1,283 @@ +<!doctype html> +<meta charset="utf-8"> +<title>RTCCertificate Tests</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + 'use strict'; + + // Test is based on the Candidate Recommendation: + // https://www.w3.org/TR/webrtc/ + + /* + 4.2.1. RTCConfiguration Dictionary + dictionary RTCConfiguration { + sequence<RTCCertificate> certificates; + ... + }; + + certificates of type sequence<RTCCertificate> + If this value is absent, then a default set of certificates is + generated for each RTCPeerConnection instance. + + The value for this configuration option cannot change after its + value is initially selected. + + 4.10.2. RTCCertificate Interface + interface RTCCertificate { + readonly attribute DOMTimeStamp expires; + static sequence<AlgorithmIdentifier> getSupportedAlgorithms(); + sequence<RTCDtlsFingerprint> getFingerprints(); + }; + + 5.5.1 The RTCDtlsFingerprint Dictionary + dictionary RTCDtlsFingerprint { + DOMString algorithm; + DOMString value; + }; + + [RFC4572] Comedia over TLS in SDP + 5. Fingerprint Attribute + Figure 2. Augmented Backus-Naur Syntax for the Fingerprint Attribute + + attribute =/ fingerprint-attribute + + fingerprint-attribute = "fingerprint" ":" hash-func SP fingerprint + + hash-func = "sha-1" / "sha-224" / "sha-256" / + "sha-384" / "sha-512" / + "md5" / "md2" / token + ; Additional hash functions can only come + ; from updates to RFC 3279 + + fingerprint = 2UHEX *(":" 2UHEX) + ; Each byte in upper-case hex, separated + ; by colons. + + UHEX = DIGIT / %x41-46 ; A-F uppercase + */ + + // Helper function to generate certificate with a set of + // default parameters + function generateCertificate() { + return RTCPeerConnection.generateCertificate({ + name: 'ECDSA', + namedCurve: 'P-256' + }); + } + + // Helper function that takes in an RTCDtlsFingerprint + // and return an a=fingerprint SDP line + function fingerprintToSdpLine(fingerprint) { + return `\r\na=fingerprint:${fingerprint.algorithm} ${fingerprint.value.toUpperCase()}\r\n`; + } + + // Assert that an SDP string has fingerprint line for all the cert's fingerprints + function assert_sdp_has_cert_fingerprints(sdp, cert) { + for(const fingerprint of cert.getFingerprints()) { + const fingerprintLine = fingerprintToSdpLine(fingerprint); + assert_true(sdp.includes(fingerprintLine), + 'Expect fingerprint line to be found in SDP'); + } + } + + /* + 4.3.1. Operation + When the RTCPeerConnection() constructor is invoked + 2. If the certificates value in configuration is non-empty, + check that the expires on each value is in the future. + If a certificate has expired, throw an InvalidAccessError; + otherwise, store the certificates. If no certificates value + was specified, one or more new RTCCertificate instances are + generated for use with this RTCPeerConnection instance. + This may happen asynchronously and the value of certificates + remains undefined for the subsequent steps. + */ + promise_test(t => { + return RTCPeerConnection.generateCertificate({ + name: 'ECDSA', + namedCurve: 'P-256', + expires: 0 + }).then(cert => { + assert_less_than_equal(cert.expires, Date.now()); + assert_throws_dom('InvalidAccessError', () => + new RTCPeerConnection({ certificates: [cert] })); + }); + }, 'Constructing RTCPeerConnection with expired certificate should reject with InvalidAccessError'); + + /* + 4.3.2 Interface Definition + setConfiguration + 4. If configuration.certificates is set and the set of + certificates differs from the ones used when connection + was constructed, throw an InvalidModificationError. + */ + promise_test(t => { + return Promise.all([ + generateCertificate(), + generateCertificate() + ]).then(([cert1, cert2]) => { + const pc = new RTCPeerConnection({ + certificates: [cert1] + }); + + // should not throw + pc.setConfiguration({ + certificates: [cert1] + }); + + assert_throws_dom('InvalidModificationError', () => + pc.setConfiguration({ + certificates: [cert2] + })); + + assert_throws_dom('InvalidModificationError', () => + pc.setConfiguration({ + certificates: [cert1, cert2] + })); + }); + }, 'Calling setConfiguration with different set of certs should reject with InvalidModificationError'); + + /* + 4.10.2. RTCCertificate Interface + getFingerprints + Returns the list of certificate fingerprints, one of which is + computed with the digest algorithm used in the certificate signature. + + 5.5.1 The RTCDtlsFingerprint Dictionary + algorithm of type DOMString + One of the the hash function algorithms defined in the 'Hash function + Textual Names' registry, initially specified in [RFC4572] Section 8. + As noted in [JSEP] Section 5.2.1, the digest algorithm used for the + fingerprint matches that used in the certificate signature. + + value of type DOMString + The value of the certificate fingerprint in lowercase hex string as + expressed utilizing the syntax of 'fingerprint' in [ RFC4572] Section 5. + + */ + promise_test(t => { + return generateCertificate() + .then(cert => { + assert_idl_attribute(cert, 'getFingerprints'); + + const fingerprints = cert.getFingerprints(); + assert_true(Array.isArray(fingerprints), + 'Expect fingerprints to return an array'); + + assert_greater_than_equal(fingerprints.length, 1, + 'Expect at last one fingerprint in array'); + + for(const fingerprint of fingerprints) { + assert_equals(typeof fingerprint, 'object', + 'Expect fingerprint to be an object (dictionary)'); + + // https://www.iana.org/assignments/hash-function-text-names/hash-function-text-names.xml + const algorithms = ['md2', 'md5', 'sha-1', 'sha-224', 'sha-256', 'sha-384', 'sha-512']; + assert_in_array(fingerprint.algorithm, algorithms, + 'Expect fingerprint.algorithm to be string of algorithm identifier'); + + assert_true(/^([0-9a-f]{2}\:)+[0-9a-f]{2}$/.test(fingerprint.value), + 'Expect fingerprint.value to be lowercase hexadecimal separated by colon'); + } + }); + }, 'RTCCertificate should have at least one fingerprint'); + + /* + 4.3.2 Interface Definition + createOffer + The value for certificates in the RTCConfiguration for the + RTCPeerConnection is used to produce a set of certificate + fingerprints. These certificate fingerprints are used in the + construction of SDP and as input to requests for identity + assertions. + + [JSEP] + 5.2.1. Initial Offers + For DTLS, all m= sections MUST use all the certificate(s) that have + been specified for the PeerConnection; as a result, they MUST all + have the same [I-D.ietf-mmusic-4572-update] fingerprint value(s), or + these value(s) MUST be session-level attributes. + + The following attributes, which are of category IDENTICAL or + TRANSPORT, MUST appear only in "m=" sections which either have a + unique address or which are associated with the bundle-tag. (In + initial offers, this means those "m=" sections which do not contain + an "a=bundle-only" attribute.) + + - An "a=fingerprint" line for each of the endpoint's certificates, + as specified in [RFC4572], Section 5; the digest algorithm used + for the fingerprint MUST match that used in the certificate + signature. + + Each m= section which is not bundled into another m= section, MUST + contain the following attributes (which are of category IDENTICAL or + TRANSPORT): + + - An "a=fingerprint" line for each of the endpoint's certificates, + as specified in [RFC4572], Section 5; the digest algorithm used + for the fingerprint MUST match that used in the certificate + signature. + */ + promise_test(t => { + return generateCertificate() + .then(cert => { + const pc = new RTCPeerConnection({ + certificates: [cert] + }); + pc.createDataChannel('test'); + + return pc.createOffer() + .then(offer => { + assert_sdp_has_cert_fingerprints(offer.sdp, cert); + }); + }); + }, 'RTCPeerConnection({ certificates }) should generate offer SDP with fingerprint of provided certificate'); + + promise_test(t => { + return Promise.all([ + generateCertificate(), + generateCertificate() + ]).then(certs => { + const pc = new RTCPeerConnection({ + certificates: certs + }); + pc.createDataChannel('test'); + + return pc.createOffer() + .then(offer => { + for(const cert of certs) { + assert_sdp_has_cert_fingerprints(offer.sdp, cert); + } + }); + }); + }, 'RTCPeerConnection({ certificates }) should generate offer SDP with fingerprint of all provided certificates'); + + /* + TODO + + 4.10.2. RTCCertificate Interface + getSupportedAlgorithms + Returns a sequence providing a representative set of supported + certificate algorithms. At least one algorithm MUST be returned. + + The RTCCertificate object can be stored and retrieved from persistent + storage by an application. When a user agent is required to obtain a + structured clone [HTML5] of a RTCCertificate object, it performs the + following steps: + 1. Let input and memory be the corresponding inputs defined by the + internal structured cloning algorithm, where input represents a + RTCCertificate object to be cloned. + 2. Let output be a newly constructed RTCCertificate object. + 3. Copy the value of the expires attribute from input to output. + 4. Let the [[certificate]] internal slot of output be set to the + result of invoking the internal structured clone algorithm + recursively on the corresponding internal slots of input, with + the slot contents as the new " input" argument and memory as + the new " memory" argument. + 5. Let the [[handle]] internal slot of output refer to the same + private keying material represented by the [[handle]] internal + slot of input. + */ + +</script> |