forked from mirrors/gecko-dev
		
	MozReview-Commit-ID: LvpIb2OK2GS --HG-- rename : testing/web-platform/tests/conformance-checkers/html/elements/keygen/challenge-isvalid.html => testing/web-platform/tests/conformance-checkers/html/elements/keygen/challenge-novalid.html rename : testing/web-platform/tests/conformance-checkers/html/elements/keygen/keytype-isvalid.html => testing/web-platform/tests/conformance-checkers/html/elements/keygen/keytype-novalid.html rename : testing/web-platform/tests/conformance-checkers/html/elements/keygen/model-isvalid.html => testing/web-platform/tests/conformance-checkers/html/elements/keygen/model-also-novalid.html rename : testing/web-platform/tests/conformance-checkers/html/elements/keygen/no-attributes-isvalid.html => testing/web-platform/tests/conformance-checkers/html/elements/keygen/no-attributes-novalid.html rename : testing/web-platform/tests/conformance-checkers/xhtml/elements/keygen/054-isvalid.xhtml => testing/web-platform/tests/conformance-checkers/xhtml/elements/keygen/054-also-novalid.xhtml rename : testing/web-platform/tests/conformance-checkers/xhtml/elements/keygen/055-isvalid.xhtml => testing/web-platform/tests/conformance-checkers/xhtml/elements/keygen/055-also-novalid.xhtml rename : testing/web-platform/tests/conformance-checkers/xhtml/elements/keygen/056-isvalid.xhtml => testing/web-platform/tests/conformance-checkers/xhtml/elements/keygen/056-also-novalid.xhtml rename : testing/web-platform/tests/conformance-checkers/xhtml/elements/keygen/057-isvalid.xhtml => testing/web-platform/tests/conformance-checkers/xhtml/elements/keygen/057-also-novalid.xhtml rename : testing/web-platform/tests/conformance-checkers/xhtml/elements/keygen/058-isvalid.xhtml => testing/web-platform/tests/conformance-checkers/xhtml/elements/keygen/058-also-novalid.xhtml rename : testing/web-platform/tests/html-media-capture/capture_fallback_file_upload.html => testing/web-platform/tests/html-media-capture/capture_fallback_file_upload-manual.html rename : testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-start.html => testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-start-manual.html rename : testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-stop.html => testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-stop-manual.html rename : testing/web-platform/tests/service-workers/cache-storage/serviceworker/credentials.html => testing/web-platform/tests/service-workers/cache-storage/serviceworker/credentials.https.html
		
			
				
	
	
		
			516 lines
		
	
	
	
		
			23 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			516 lines
		
	
	
	
		
			23 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
// Tests for wrapKey and unwrapKey round tripping
 | 
						|
 | 
						|
function run_test() {
 | 
						|
    var subtle = self.crypto.subtle;
 | 
						|
 | 
						|
    var wrappers = [];  // Things we wrap (and upwrap) keys with
 | 
						|
    var keys = [];      // Things to wrap and unwrap
 | 
						|
    var ecdhPeerKey;    // ECDH peer public key needed for non-extractable ECDH key comparison
 | 
						|
 | 
						|
    // Generate all the keys needed, then iterate over all combinations
 | 
						|
    // to test wrapping and unwrapping.
 | 
						|
    Promise.all([generateWrappingKeys(), generateKeysToWrap(), generateEcdhPeerKey()])
 | 
						|
    .then(function(results) {
 | 
						|
        var promises = [];
 | 
						|
        wrappers.forEach(function(wrapper) {
 | 
						|
            keys.forEach(function(key) {
 | 
						|
                promises.push(testWrapping(wrapper, key));
 | 
						|
            })
 | 
						|
        });
 | 
						|
        return Promise.all(promises);
 | 
						|
    }, function(err) {
 | 
						|
        promise_test(function(test) {
 | 
						|
            assert_unreached("A key failed to generate: " + err.name + ": " + err.message)
 | 
						|
        }, "Could not run all tests")
 | 
						|
    })
 | 
						|
    .then(function() {
 | 
						|
        done();
 | 
						|
    }, function(err) {
 | 
						|
        promise_test(function(test) {
 | 
						|
            assert_unreached("A test failed to run: " + err.name + ": " + err.message)
 | 
						|
        }, "Could not run all tests")
 | 
						|
    });
 | 
						|
 | 
						|
 | 
						|
    function generateWrappingKeys() {
 | 
						|
        // There are five algorithms that can be used for wrapKey/unwrapKey.
 | 
						|
        // Generate one key with typical parameters for each kind.
 | 
						|
        //
 | 
						|
        // Note: we don't need cryptographically strong parameters for things
 | 
						|
        // like IV - just any legal value will do.
 | 
						|
        var parameters = [
 | 
						|
            {
 | 
						|
                name: "RSA-OAEP",
 | 
						|
                generateParameters: {name: "RSA-OAEP", modulusLength: 4096, publicExponent: new Uint8Array([1,0,1]), hash: "SHA-256"},
 | 
						|
                wrapParameters: {name: "RSA-OAEP", label: new Uint8Array(8)}
 | 
						|
            },
 | 
						|
            {
 | 
						|
                name: "AES-CTR",
 | 
						|
                generateParameters: {name: "AES-CTR", length: 128},
 | 
						|
                wrapParameters: {name: "AES-CTR", counter: new Uint8Array(16), length: 64}
 | 
						|
            },
 | 
						|
            {
 | 
						|
                name: "AES-CBC",
 | 
						|
                generateParameters: {name: "AES-CBC", length: 128},
 | 
						|
                wrapParameters: {name: "AES-CBC", iv: new Uint8Array(16)}
 | 
						|
            },
 | 
						|
            {
 | 
						|
                name: "AES-GCM",
 | 
						|
                generateParameters: {name: "AES-GCM", length: 128},
 | 
						|
                wrapParameters: {name: "AES-GCM", iv: new Uint8Array(16), additionalData: new Uint8Array(16), tagLength: 64}
 | 
						|
            },
 | 
						|
            {
 | 
						|
                name: "AES-KW",
 | 
						|
                generateParameters: {name: "AES-KW", length: 128},
 | 
						|
                wrapParameters: {name: "AES-KW"}
 | 
						|
            }
 | 
						|
        ];
 | 
						|
 | 
						|
        return Promise.all(parameters.map(function(params) {
 | 
						|
            return subtle.generateKey(params.generateParameters, true, ["wrapKey", "unwrapKey"])
 | 
						|
            .then(function(key) {
 | 
						|
                var wrapper;
 | 
						|
                if (params.name === "RSA-OAEP") { // we have a key pair, not just a key
 | 
						|
                    wrapper = {wrappingKey: key.publicKey, unwrappingKey: key.privateKey, parameters: params};
 | 
						|
                } else {
 | 
						|
                    wrapper = {wrappingKey: key, unwrappingKey: key, parameters: params};
 | 
						|
                }
 | 
						|
                wrappers.push(wrapper);
 | 
						|
                return true;
 | 
						|
            })
 | 
						|
        }));
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    function generateKeysToWrap() {
 | 
						|
        var parameters = [
 | 
						|
            {algorithm: {name: "RSASSA-PKCS1-v1_5", modulusLength: 1024, publicExponent: new Uint8Array([1,0,1]), hash: "SHA-256"}, privateUsages: ["sign"], publicUsages: ["verify"]},
 | 
						|
            {algorithm: {name: "RSA-PSS", modulusLength: 1024, publicExponent: new Uint8Array([1,0,1]), hash: "SHA-256"}, privateUsages: ["sign"], publicUsages: ["verify"]},
 | 
						|
            {algorithm: {name: "RSA-OAEP", modulusLength: 1024, publicExponent: new Uint8Array([1,0,1]), hash: "SHA-256"}, privateUsages: ["decrypt"], publicUsages: ["encrypt"]},
 | 
						|
            {algorithm: {name: "ECDSA", namedCurve: "P-256"}, privateUsages: ["sign"], publicUsages: ["verify"]},
 | 
						|
            {algorithm: {name: "ECDH", namedCurve: "P-256"}, privateUsages: ["deriveBits"], publicUsages: []},
 | 
						|
            {algorithm: {name: "AES-CTR", length: 128}, usages: ["encrypt", "decrypt"]},
 | 
						|
            {algorithm: {name: "AES-CBC", length: 128}, usages: ["encrypt", "decrypt"]},
 | 
						|
            {algorithm: {name: "AES-GCM", length: 128}, usages: ["encrypt", "decrypt"]},
 | 
						|
            {algorithm: {name: "AES-KW", length: 128}, usages: ["wrapKey", "unwrapKey"]},
 | 
						|
            {algorithm: {name: "HMAC", length: 128, hash: "SHA-256"}, usages: ["sign", "verify"]}
 | 
						|
        ];
 | 
						|
 | 
						|
        return Promise.all(parameters.map(function(params) {
 | 
						|
            var usages;
 | 
						|
            if ("usages" in params) {
 | 
						|
                usages = params.usages;
 | 
						|
            } else {
 | 
						|
                usages = params.publicUsages.concat(params.privateUsages);
 | 
						|
            }
 | 
						|
 | 
						|
            return subtle.generateKey(params.algorithm, true, usages)
 | 
						|
            .then(function(result) {
 | 
						|
                if (result.constructor === CryptoKey) {
 | 
						|
                    keys.push({name: params.algorithm.name, algorithm: params.algorithm, usages: params.usages, key: result});
 | 
						|
                } else {
 | 
						|
                    keys.push({name: params.algorithm.name + " public key", algorithm: params.algorithm, usages: params.publicUsages, key: result.publicKey});
 | 
						|
                    keys.push({name: params.algorithm.name + " private key", algorithm: params.algorithm, usages: params.privateUsages, key: result.privateKey});
 | 
						|
                }
 | 
						|
                return true;
 | 
						|
            });
 | 
						|
        }));
 | 
						|
    }
 | 
						|
 | 
						|
    function generateEcdhPeerKey() {
 | 
						|
        return subtle.generateKey({name: "ECDH", namedCurve: "P-256"},true,["deriveBits"])
 | 
						|
        .then(function(result){
 | 
						|
            ecdhPeerKey = result.publicKey;
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
    // Can we successfully "round-trip" (wrap, then unwrap, a key)?
 | 
						|
    function testWrapping(wrapper, toWrap) {
 | 
						|
        var formats;
 | 
						|
 | 
						|
        if (toWrap.name.includes("private")) {
 | 
						|
            formats = ["pkcs8", "jwk"];
 | 
						|
        } else if (toWrap.name.includes("public")) {
 | 
						|
            formats = ["spki", "jwk"]
 | 
						|
        } else {
 | 
						|
            formats = ["raw", "jwk"]
 | 
						|
        }
 | 
						|
 | 
						|
        return Promise.all(formats.map(function(fmt) {
 | 
						|
            var originalExport;
 | 
						|
            return subtle.exportKey(fmt, toWrap.key).then(function(exportedKey) {
 | 
						|
                originalExport = exportedKey;
 | 
						|
                if (wrappingIsPossible(originalExport, wrapper.parameters.name)) {
 | 
						|
                    promise_test(function(test) {
 | 
						|
                        return subtle.wrapKey(fmt, toWrap.key, wrapper.wrappingKey, wrapper.parameters.wrapParameters)
 | 
						|
                        .then(function(wrappedResult) {
 | 
						|
                            return subtle.unwrapKey(fmt, wrappedResult, wrapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, true, toWrap.usages);
 | 
						|
                        }).then(function(unwrappedResult) {
 | 
						|
                            assert_true(unwrappedResult.extractable, "Unwrapped result is extractable");
 | 
						|
                            return subtle.exportKey(fmt, unwrappedResult)
 | 
						|
                        }).then(function(roundTripExport) {
 | 
						|
                            assert_true(equalExport(originalExport, roundTripExport), "Post-wrap export matches original export");
 | 
						|
                        }, function(err) {
 | 
						|
                            assert_unreached("Round trip for extractable key threw an error - " + err.name + ': "' + err.message + '"');
 | 
						|
                        });
 | 
						|
                    }, "Can wrap and unwrap " + toWrap.name + " keys using " + fmt + " and " + wrapper.parameters.name);
 | 
						|
 | 
						|
                    if (canCompareNonExtractableKeys(toWrap.key)) {
 | 
						|
                        promise_test(function(test){
 | 
						|
                            return subtle.wrapKey(fmt, toWrap.key, wrapper.wrappingKey, wrapper.parameters.wrapParameters)
 | 
						|
                            .then(function(wrappedResult) {
 | 
						|
                                return subtle.unwrapKey(fmt, wrappedResult, wrapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, false, toWrap.usages);
 | 
						|
                            }).then(function(unwrappedResult){
 | 
						|
                                assert_false(unwrappedResult.extractable, "Unwrapped result is non-extractable");
 | 
						|
                                return equalKeys(toWrap.key, unwrappedResult);
 | 
						|
                            }).then(function(result){
 | 
						|
                                assert_true(result, "Unwrapped key matches original");
 | 
						|
                            }).catch(function(err){
 | 
						|
                                assert_unreached("Round trip for key unwrapped non-extractable threw an error - " + err.name + ': "' + err.message + '"');
 | 
						|
                            });
 | 
						|
                        }, "Can wrap and unwrap " + toWrap.name + " keys as non-extractable using " + fmt + " and " + wrapper.parameters.name);
 | 
						|
 | 
						|
                        if (fmt === "jwk") {
 | 
						|
                            promise_test(function(test){
 | 
						|
                                var wrappedKey;
 | 
						|
                                return wrapAsNonExtractableJwk(toWrap.key,wrapper).then(function(wrappedResult){
 | 
						|
                                    wrappedKey = wrappedResult;
 | 
						|
                                    return subtle.unwrapKey("jwk", wrappedKey, wrapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, false, toWrap.usages);
 | 
						|
                                }).then(function(unwrappedResult){
 | 
						|
                                    assert_false(unwrappedResult.extractable, "Unwrapped key is non-extractable");
 | 
						|
                                    return equalKeys(toWrap.key,unwrappedResult);
 | 
						|
                                }).then(function(result){
 | 
						|
                                    assert_true(result, "Unwrapped key matches original");
 | 
						|
                                }).catch(function(err){
 | 
						|
                                    assert_unreached("Round trip for non-extractable key threw an error - " + err.name + ': "' + err.message + '"');
 | 
						|
                                }).then(function(){
 | 
						|
                                    return subtle.unwrapKey("jwk", wrappedKey, wrapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, true, toWrap.usages);
 | 
						|
                                }).then(function(unwrappedResult){
 | 
						|
                                    assert_unreached("Unwrapping a non-extractable JWK as extractable should fail");
 | 
						|
                                }).catch(function(err){
 | 
						|
                                    assert_equals(err.name, "DataError", "Unwrapping a non-extractable JWK as extractable fails with DataError");
 | 
						|
                                });
 | 
						|
                            }, "Can unwrap " + toWrap.name + " non-extractable keys using jwk and " + wrapper.parameters.name);
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            });
 | 
						|
        }));
 | 
						|
    }
 | 
						|
 | 
						|
    // Implement key wrapping by hand to wrap a key as non-extractable JWK
 | 
						|
    function wrapAsNonExtractableJwk(key, wrapper){
 | 
						|
        var wrappingKey = wrapper.wrappingKey,
 | 
						|
            encryptKey;
 | 
						|
 | 
						|
        return subtle.exportKey("jwk",wrappingKey)
 | 
						|
        .then(function(jwkWrappingKey){
 | 
						|
            // Update the key generation parameters to work as key import parameters
 | 
						|
            var params = Object.create(wrapper.parameters.generateParameters);
 | 
						|
            if(params.name === "AES-KW") {
 | 
						|
                params.name = "AES-CBC";
 | 
						|
                jwkWrappingKey.alg = "A"+params.length+"CBC";
 | 
						|
            } else if (params.name === "RSA-OAEP") {
 | 
						|
                params.modulusLength = undefined;
 | 
						|
                params.publicExponent = undefined;
 | 
						|
            }
 | 
						|
            jwkWrappingKey.key_ops = ["encrypt"];
 | 
						|
            return subtle.importKey("jwk", jwkWrappingKey, params, true, ["encrypt"]);
 | 
						|
        }).then(function(importedWrappingKey){
 | 
						|
            encryptKey = importedWrappingKey;
 | 
						|
            return subtle.exportKey("jwk",key);
 | 
						|
        }).then(function(exportedKey){
 | 
						|
            exportedKey.ext = false;
 | 
						|
            var jwk = JSON.stringify(exportedKey)
 | 
						|
            if (wrappingKey.algorithm.name === "AES-KW") {
 | 
						|
                return aeskw(encryptKey, str2ab(jwk.slice(0,-1) + " ".repeat(jwk.length%8 ? 8-jwk.length%8 : 0) + "}"));
 | 
						|
            } else {
 | 
						|
                return subtle.encrypt(wrapper.parameters.wrapParameters,encryptKey,str2ab(jwk));
 | 
						|
            }
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    // RSA-OAEP can only wrap relatively small payloads. AES-KW can only
 | 
						|
    // wrap payloads a multiple of 8 bytes long.
 | 
						|
    //
 | 
						|
    // Note that JWK payloads will be converted to ArrayBuffer for wrapping,
 | 
						|
    // and should automatically be padded if needed for AES-KW.
 | 
						|
    function wrappingIsPossible(exportedKey, algorithmName) {
 | 
						|
        if ("byteLength" in exportedKey && algorithmName === "AES-KW") {
 | 
						|
            return exportedKey.byteLength % 8 === 0;
 | 
						|
        }
 | 
						|
 | 
						|
        if ("byteLength" in exportedKey && algorithmName === "RSA-OAEP") {
 | 
						|
            // RSA-OAEP can only encrypt payloads with lengths shorter
 | 
						|
            // than modulusLength - 2*hashLength - 1 bytes long. For
 | 
						|
            // a 4096 bit modulus and SHA-256, that comes to
 | 
						|
            // 4096/8 - 2*(256/8) - 1 = 512 - 2*32 - 1 = 447 bytes.
 | 
						|
            return exportedKey.byteLength <= 446;
 | 
						|
        }
 | 
						|
 | 
						|
        if ("kty" in exportedKey && algorithmName === "RSA-OAEP") {
 | 
						|
            return JSON.stringify(exportedKey).length <= 478;
 | 
						|
        }
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    // Helper methods follow:
 | 
						|
 | 
						|
    // Are two exported keys equal
 | 
						|
    function equalExport(originalExport, roundTripExport) {
 | 
						|
        if ("byteLength" in originalExport) {
 | 
						|
            return equalBuffers(originalExport, roundTripExport);
 | 
						|
        } else {
 | 
						|
            return equalJwk(originalExport, roundTripExport);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // Are two array buffers the same?
 | 
						|
    function equalBuffers(a, b) {
 | 
						|
        if (a.byteLength !== b.byteLength) {
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        var aBytes = new Uint8Array(a);
 | 
						|
        var bBytes = new Uint8Array(b);
 | 
						|
 | 
						|
        for (var i=0; i<a.byteLength; i++) {
 | 
						|
            if (aBytes[i] !== bBytes[i]) {
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    // Are two Jwk objects "the same"? That is, does the object returned include
 | 
						|
    // matching values for each property that was expected? It's okay if the
 | 
						|
    // returned object has extra methods; they aren't checked.
 | 
						|
    function equalJwk(expected, got) {
 | 
						|
        var fields = Object.keys(expected);
 | 
						|
        var fieldName;
 | 
						|
 | 
						|
        for(var i=0; i<fields.length; i++) {
 | 
						|
            fieldName = fields[i];
 | 
						|
            if (!(fieldName in got)) {
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
            if (objectToString(expected[fieldName]) !== objectToString(got[fieldName])) {
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    // Character representation of any object we may use as a parameter.
 | 
						|
    function objectToString(obj) {
 | 
						|
        var keyValuePairs = [];
 | 
						|
 | 
						|
        if (Array.isArray(obj)) {
 | 
						|
            return "[" + obj.map(function(elem){return objectToString(elem);}).join(", ") + "]";
 | 
						|
        } else if (typeof obj === "object") {
 | 
						|
            Object.keys(obj).sort().forEach(function(keyName) {
 | 
						|
                keyValuePairs.push(keyName + ": " + objectToString(obj[keyName]));
 | 
						|
            });
 | 
						|
            return "{" + keyValuePairs.join(", ") + "}";
 | 
						|
        } else if (typeof obj === "undefined") {
 | 
						|
            return "undefined";
 | 
						|
        } else {
 | 
						|
            return obj.toString();
 | 
						|
        }
 | 
						|
 | 
						|
        var keyValuePairs = [];
 | 
						|
 | 
						|
        Object.keys(obj).sort().forEach(function(keyName) {
 | 
						|
            var value = obj[keyName];
 | 
						|
            if (typeof value === "object") {
 | 
						|
                value = objectToString(value);
 | 
						|
            } else if (typeof value === "array") {
 | 
						|
                value = "[" + value.map(function(elem){return objectToString(elem);}).join(", ") + "]";
 | 
						|
            } else {
 | 
						|
                value = value.toString();
 | 
						|
            }
 | 
						|
 | 
						|
            keyValuePairs.push(keyName + ": " + value);
 | 
						|
        });
 | 
						|
 | 
						|
        return "{" + keyValuePairs.join(", ") + "}";
 | 
						|
    }
 | 
						|
 | 
						|
    // Can we compare key values by using them
 | 
						|
    function canCompareNonExtractableKeys(key){
 | 
						|
        if (key.usages.indexOf("decrypt") !== -1) {
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
        if (key.usages.indexOf("sign") !== -1) {
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
        if (key.usages.indexOf("wrapKey") !== -1) {
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
        if (key.usages.indexOf("deriveBits") !== -1) {
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    // Compare two keys by using them (works for non-extractable keys)
 | 
						|
    function equalKeys(expected, got){
 | 
						|
        if ( expected.algorithm.name !== got.algorithm.name ) {
 | 
						|
            return Promise.resolve(false);
 | 
						|
        }
 | 
						|
 | 
						|
        var cryptParams, signParams, wrapParams, deriveParams;
 | 
						|
        switch(expected.algorithm.name){
 | 
						|
            case "AES-CTR" :
 | 
						|
                cryptParams = {name: "AES-CTR", counter: new Uint8Array(16), length: 64};
 | 
						|
                break;
 | 
						|
            case "AES-CBC" :
 | 
						|
                cryptParams = {name: "AES-CBC", iv: new Uint8Array(16) };
 | 
						|
                break;
 | 
						|
            case "AES-GCM" :
 | 
						|
                cryptParams = {name: "AES-GCM", iv: new Uint8Array(16) };
 | 
						|
                break;
 | 
						|
            case "RSA-OAEP" :
 | 
						|
                cryptParams = {name: "RSA-OAEP", label: new Uint8Array(8) };
 | 
						|
                break;
 | 
						|
            case "RSASSA-PKCS1-v1_5" :
 | 
						|
                signParams = {name: "RSASSA-PKCS1-v1_5"};
 | 
						|
                break;
 | 
						|
            case "RSA-PSS" :
 | 
						|
                signParams = {name: "RSA-PSS", saltLength: 32 };
 | 
						|
                break;
 | 
						|
            case "ECDSA" :
 | 
						|
                signParams = {name: "ECDSA", hash: "SHA-256"};
 | 
						|
                break;
 | 
						|
            case "HMAC" :
 | 
						|
                signParams = {name: "HMAC"};
 | 
						|
                break;
 | 
						|
            case "AES-KW" :
 | 
						|
                wrapParams = {name: "AES-KW"};
 | 
						|
                break;
 | 
						|
            case "ECDH" :
 | 
						|
                deriveParams = {name: "ECDH", public: ecdhPeerKey};
 | 
						|
                break;
 | 
						|
            default:
 | 
						|
                throw new Error("Unsupported algorithm for key comparison");
 | 
						|
        }
 | 
						|
 | 
						|
        if (cryptParams) {
 | 
						|
            return subtle.exportKey("jwk",expected)
 | 
						|
            .then(function(jwkExpectedKey){
 | 
						|
                if (expected.algorithm.name === "RSA-OAEP") {
 | 
						|
                    ["d","p","q","dp","dq","qi","oth"].forEach(function(field){ delete jwkExpectedKey[field]; });
 | 
						|
                }
 | 
						|
                jwkExpectedKey.key_ops = ["encrypt"];
 | 
						|
                return subtle.importKey("jwk", jwkExpectedKey, expected.algorithm, true, ["encrypt"]);
 | 
						|
            }).then(function(expectedEncryptKey){
 | 
						|
                return subtle.encrypt(cryptParams, expectedEncryptKey, new Uint8Array(32));
 | 
						|
            }).then(function(encryptedData){
 | 
						|
                return subtle.decrypt(cryptParams, got, encryptedData);
 | 
						|
            }).then(function(decryptedData){
 | 
						|
                var result = new Uint8Array(decryptedData);
 | 
						|
                return !result.some(x => x);
 | 
						|
            });
 | 
						|
        } else if (signParams) {
 | 
						|
            var verifyKey;
 | 
						|
            return subtle.exportKey("jwk",expected)
 | 
						|
            .then(function(jwkExpectedKey){
 | 
						|
                if (expected.algorithm.name === "RSA-PSS" || expected.algorithm.name === "RSASSA-PKCS1-v1_5") {
 | 
						|
                    ["d","p","q","dp","dq","qi","oth"].forEach(function(field){ delete jwkExpectedKey[field]; });
 | 
						|
                }
 | 
						|
                if (expected.algorithm.name === "ECDSA") {
 | 
						|
                    delete jwkExpectedKey["d"];
 | 
						|
                }
 | 
						|
                jwkExpectedKey.key_ops = ["verify"];
 | 
						|
                return subtle.importKey("jwk", jwkExpectedKey, expected.algorithm, true, ["verify"]);
 | 
						|
            }).then(function(expectedVerifyKey){
 | 
						|
                verifyKey = expectedVerifyKey;
 | 
						|
                return subtle.sign(signParams, got, new Uint8Array(32));
 | 
						|
            }).then(function(signature){
 | 
						|
                return subtle.verify(signParams, verifyKey, signature, new Uint8Array(32));
 | 
						|
            });
 | 
						|
        } else if (wrapParams) {
 | 
						|
            var aKeyToWrap, wrappedWithExpected;
 | 
						|
            return subtle.importKey("raw", new Uint8Array(16), "AES-CBC", true, ["encrypt"])
 | 
						|
            .then(function(key){
 | 
						|
                aKeyToWrap = key;
 | 
						|
                return subtle.wrapKey("raw", aKeyToWrap, expected, wrapParams);
 | 
						|
            }).then(function(wrapResult){
 | 
						|
                wrappedWithExpected = Array.from((new Uint8Array(wrapResult)).values());
 | 
						|
                return subtle.wrapKey("raw", aKeyToWrap, got, wrapParams);
 | 
						|
            }).then(function(wrapResult){
 | 
						|
                var wrappedWithGot = Array.from((new Uint8Array(wrapResult)).values());
 | 
						|
                return wrappedWithGot.every((x,i) => x === wrappedWithExpected[i]);
 | 
						|
            });
 | 
						|
        } else {
 | 
						|
            var expectedDerivedBits;
 | 
						|
            return subtle.deriveBits(deriveParams, expected, 128)
 | 
						|
            .then(function(result){
 | 
						|
                expectedDerivedBits = Array.from((new Uint8Array(result)).values());
 | 
						|
                return subtle.deriveBits(deriveParams, got, 128);
 | 
						|
            }).then(function(result){
 | 
						|
                var gotDerivedBits = Array.from((new Uint8Array(result)).values());
 | 
						|
                return gotDerivedBits.every((x,i) => x === expectedDerivedBits[i]);
 | 
						|
            });
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // Raw AES encryption
 | 
						|
    function aes( k, p ) {
 | 
						|
        return subtle.encrypt({name: "AES-CBC", iv: new Uint8Array(16) }, k, p).then(function(ciphertext){return ciphertext.slice(0,16);});
 | 
						|
    }
 | 
						|
 | 
						|
    // AES Key Wrap
 | 
						|
    function aeskw(key, data) {
 | 
						|
        if (data.byteLength % 8 !== 0) {
 | 
						|
            throw new Error("AES Key Wrap data must be a multiple of 8 bytes in length");
 | 
						|
        }
 | 
						|
 | 
						|
        var A = Uint8Array.from([0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0, 0, 0, 0, 0, 0, 0, 0]),
 | 
						|
            Av = new DataView(A.buffer),
 | 
						|
            R = [],
 | 
						|
            n = data.byteLength / 8;
 | 
						|
 | 
						|
        for(var i = 0; i<data.byteLength; i+=8) {
 | 
						|
            R.push(new Uint8Array(data.slice(i,i+8)));
 | 
						|
        }
 | 
						|
 | 
						|
        function aeskw_step(j, i, final, B) {
 | 
						|
            A.set(new Uint8Array(B.slice(0,8)));
 | 
						|
            Av.setUint32(4,Av.getUint32(4) ^ (n*j+i+1));
 | 
						|
            R[i] = new Uint8Array(B.slice(8,16));
 | 
						|
            if (final) {
 | 
						|
                R.unshift(A.slice(0,8));
 | 
						|
                var result = new Uint8Array(R.length * 8);
 | 
						|
                R.forEach(function(Ri,i){ result.set(Ri, i*8); });
 | 
						|
                return result;
 | 
						|
            } else {
 | 
						|
                A.set(R[(i+1)%n],8);
 | 
						|
                return aes(key,A);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        var p = new Promise(function(resolve){
 | 
						|
            A.set(R[0],8);
 | 
						|
            resolve(aes(key,A));
 | 
						|
        });
 | 
						|
 | 
						|
        for(var j=0;j<6;++j) {
 | 
						|
            for(var i=0;i<n;++i) {
 | 
						|
                p = p.then(aeskw_step.bind(undefined, j, i,j===5 && i===(n-1)));
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return p;
 | 
						|
    }
 | 
						|
 | 
						|
    function str2ab(str)        { return Uint8Array.from( str.split(''), function(s){return s.charCodeAt(0)} ); }
 | 
						|
    function ab2str(ab)         { return String.fromCharCode.apply(null, new Uint8Array(ab)); }
 | 
						|
 | 
						|
 | 
						|
}
 |