Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

<!doctype html>
<meta charset="utf-8">
<title>RTCCertificateStats describes the local and remote certificates</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="RTCPeerConnection-helper.js"></script>
<script>
'use strict';
// The following helpers come from RTCPeerConnection-helper.js:
// exchangeIceCandidates, exchangeOfferAnswer, trackFactories, waitForState
// Normalize an RFC 4572 fingerprint (colon-separated, any case) to bare
// lowercase hex so values from different sources can be compared.
function normalizeFingerprint(value) {
return value.replace(/:/g, '').toLowerCase();
}
// transport.localCertificateId and transport.remoteCertificateId are
// references to the RTCCertificateStats describing each end's DTLS
// certificate. By configuring both peers with certificates whose fingerprints
// we know in advance, we can assert that each id resolves to a certificate
// stat that actually describes the expected certificate -- not merely that
// the field is present. Served over HTTPS because generateCertificate() and
// getFingerprints() require a secure context.
promise_test(async t => {
const [localCert, remoteCert] = await Promise.all([
RTCPeerConnection.generateCertificate(
{name: 'ECDSA', namedCurve: 'P-256'}),
RTCPeerConnection.generateCertificate(
{name: 'ECDSA', namedCurve: 'P-256'}),
]);
const pc1 = new RTCPeerConnection({certificates: [localCert]});
t.add_cleanup(() => pc1.close());
const pc2 = new RTCPeerConnection({certificates: [remoteCert]});
t.add_cleanup(() => pc2.close());
pc1.addTrack(trackFactories.audio());
exchangeIceCandidates(pc1, pc2);
await exchangeOfferAnswer(pc1, pc2);
const dtlsTransport = pc1.getSenders()[0].transport;
await waitForState(dtlsTransport, 'connected');
const report = await pc1.getStats();
const transport = [...report.values()].find(s => s.type === 'transport');
assert_not_equals(transport, undefined, 'Expect a transport stat');
async function checkCertificate(id, cert, label) {
assert_not_equals(id, undefined,
`Expect transport.${label} to be set when connected`);
const stat = report.get(id);
assert_not_equals(stat, undefined,
`Expect transport.${label} to resolve to a stats object`);
assert_equals(stat.type, 'certificate',
`Expect transport.${label} to reference a certificate stat`);
// Both peers use a single self-signed certificate, so there is no issuer.
assert_equals(stat.issuerCertificateId, undefined,
`Expect transport.${label} to be self-signed with no ` +
`issuerCertificateId`);
const fingerprint = cert.getFingerprints().find(
f => f.algorithm === stat.fingerprintAlgorithm);
assert_not_equals(fingerprint, undefined,
`Expect the certificate to have a ${stat.fingerprintAlgorithm} ` +
`fingerprint`);
assert_equals(normalizeFingerprint(stat.fingerprint),
normalizeFingerprint(fingerprint.value),
`Expect transport.${label} to describe the expected certificate`);
const der = Uint8Array.fromBase64(stat.base64Certificate);
const digest = new Uint8Array(
await crypto.subtle.digest(stat.fingerprintAlgorithm, der)).toHex();
assert_equals(digest, normalizeFingerprint(stat.fingerprint),
`Expect transport.${label} base64Certificate to hash to its ` +
`fingerprint`);
}
await checkCertificate(transport.localCertificateId, localCert,
'localCertificateId');
await checkCertificate(transport.remoteCertificateId, remoteCert,
'remoteCertificateId');
});
</script>