Source code
Revision control
Copy as Markdown
Other Tools
function run_test() {
setup({ explicit_done: true });
var subtle = self.crypto.subtle; // Change to test prefixed implementations
// When are all these tests really done? When all the promises they use have resolved.
var all_promises = [];
// Source file [algorithm_name]_vectors.js provides the getTestVectors method
// for the algorithm that drives these tests.
var testVectors = getTestVectors();
var invalidTestVectors = getInvalidTestVectors();
// Test verification first, because signing tests rely on that working
testVectors.forEach(function (vector) {
var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
function (vectors) {
var algorithm = vector.algorithmName;
promise_test(function (test) {
var operation = subtle
.verify(algorithm, vector.publicKey, vector.signature, vector.data)
.then(
function (is_verified) {
assert_true(is_verified, 'Signature verified');
},
function (err) {
assert_unreached(
'Verification should not throw error ' +
vector.name +
': ' +
err.message +
"'"
);
}
);
return operation;
}, vector.name + ' verification');
},
function (err) {
// We need a failed test if the importVectorKey operation fails, so
// we know we never tested verification.
promise_test(function (test) {
assert_unreached(
'importVectorKeys failed for ' +
vector.name +
". Message: ''" +
err.message +
"''"
);
}, 'importVectorKeys step: ' + vector.name + ' verification');
}
);
all_promises.push(promise);
});
// Test verification with an altered buffer after call
testVectors.forEach(function (vector) {
var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
function (vectors) {
var algorithm = vector.algorithmName;
promise_test(function (test) {
var signature = copyBuffer(vector.signature);
var operation = subtle
.verify(algorithm, vector.publicKey, signature, vector.data)
.then(
function (is_verified) {
assert_true(is_verified, 'Signature verified');
},
function (err) {
assert_unreached(
'Verification should not throw error ' +
vector.name +
': ' +
err.message +
"'"
);
}
);
signature[0] = 255 - signature[0];
return operation;
}, vector.name + ' verification with altered signature after call');
},
function (err) {
promise_test(function (test) {
assert_unreached(
'importVectorKeys failed for ' +
vector.name +
". Message: ''" +
err.message +
"''"
);
}, 'importVectorKeys step: ' +
vector.name +
' verification with altered signature after call');
}
);
all_promises.push(promise);
});
// Check for successful verification even if plaintext is altered after call.
testVectors.forEach(function (vector) {
var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
function (vectors) {
var algorithm = vector.algorithmName;
promise_test(function (test) {
var plaintext = copyBuffer(vector.data);
var operation = subtle
.verify(algorithm, vector.publicKey, vector.signature, plaintext)
.then(
function (is_verified) {
assert_true(is_verified, 'Signature verified');
},
function (err) {
assert_unreached(
'Verification should not throw error ' +
vector.name +
': ' +
err.message +
"'"
);
}
);
plaintext[0] = 255 - plaintext[0];
return operation;
}, vector.name + ' with altered plaintext after call');
},
function (err) {
promise_test(function (test) {
assert_unreached(
'importVectorKeys failed for ' +
vector.name +
". Message: ''" +
err.message +
"''"
);
}, 'importVectorKeys step: ' +
vector.name +
' with altered plaintext after call');
}
);
all_promises.push(promise);
});
// Check for failures due to using privateKey to verify.
testVectors.forEach(function (vector) {
var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
function (vectors) {
var algorithm = vector.algorithmName;
promise_test(function (test) {
return subtle
.verify(algorithm, vector.privateKey, vector.signature, vector.data)
.then(
function (plaintext) {
assert_unreached(
'Should have thrown error for using privateKey to verify in ' +
vector.name +
': ' +
err.message +
"'"
);
},
function (err) {
assert_equals(
err.name,
'InvalidAccessError',
"Should throw InvalidAccessError instead of '" +
err.message +
"'"
);
}
);
}, vector.name + ' using privateKey to verify');
},
function (err) {
promise_test(function (test) {
assert_unreached(
'importVectorKeys failed for ' +
vector.name +
". Message: ''" +
err.message +
"''"
);
}, 'importVectorKeys step: ' +
vector.name +
' using privateKey to verify');
}
);
all_promises.push(promise);
});
// Check for failures due to using publicKey to sign.
testVectors.forEach(function (vector) {
var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
function (vectors) {
var algorithm = vector.algorithmName;
promise_test(function (test) {
return subtle.sign(algorithm, vector.publicKey, vector.data).then(
function (signature) {
assert_unreached(
'Should have thrown error for using publicKey to sign in ' +
vector.name +
': ' +
err.message +
"'"
);
},
function (err) {
assert_equals(
err.name,
'InvalidAccessError',
"Should throw InvalidAccessError instead of '" +
err.message +
"'"
);
}
);
}, vector.name + ' using publicKey to sign');
},
function (err) {
promise_test(function (test) {
assert_unreached(
'importVectorKeys failed for ' +
vector.name +
". Message: ''" +
err.message +
"''"
);
}, 'importVectorKeys step: ' +
vector.name +
' using publicKey to sign');
}
);
all_promises.push(promise);
});
// Check for failures due to no "verify" usage.
testVectors.forEach(function (originalVector) {
var vector = Object.assign({}, originalVector);
var promise = importVectorKeys(vector, [], ['sign']).then(
function (vectors) {
var algorithm = vector.algorithmName;
promise_test(function (test) {
return subtle
.verify(algorithm, vector.publicKey, vector.signature, vector.data)
.then(
function (plaintext) {
assert_unreached(
'Should have thrown error for no verify usage in ' +
vector.name +
': ' +
err.message +
"'"
);
},
function (err) {
assert_equals(
err.name,
'InvalidAccessError',
"Should throw InvalidAccessError instead of '" +
err.message +
"'"
);
}
);
}, vector.name + ' no verify usage');
},
function (err) {
promise_test(function (test) {
assert_unreached(
'importVectorKeys failed for ' +
vector.name +
". Message: ''" +
err.message +
"''"
);
}, 'importVectorKeys step: ' + vector.name + ' no verify usage');
}
);
all_promises.push(promise);
});
// Check for successful signing and verification.
testVectors.forEach(function (vector) {
var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
function (vectors) {
var algorithm = vector.algorithmName;
promise_test(function (test) {
return subtle.sign(algorithm, vector.privateKey, vector.data).then(
function (signature) {
// Can we verify the signature?
return subtle
.verify(algorithm, vector.publicKey, signature, vector.data)
.then(
function (is_verified) {
assert_true(is_verified, 'Round trip verification works');
return signature;
},
function (err) {
assert_unreached(
'verify error for test ' +
vector.name +
': ' +
err.message +
"'"
);
}
);
},
function (err) {
assert_unreached(
'sign error for test ' + vector.name + ": '" + err.message + "'"
);
}
);
}, vector.name + ' round trip');
},
function (err) {
// We need a failed test if the importVectorKey operation fails, so
// we know we never tested signing or verifying
promise_test(function (test) {
assert_unreached(
'importVectorKeys failed for ' +
vector.name +
". Message: ''" +
err.message +
"''"
);
}, 'importVectorKeys step: ' + vector.name + ' round trip');
}
);
all_promises.push(promise);
});
// Test signing with the wrong algorithm
testVectors.forEach(function (vector) {
// Want to get the key for the wrong algorithm
var promise = subtle
.generateKey({ name: 'HMAC', hash: 'SHA-1' }, false, ['sign', 'verify'])
.then(
function (wrongKey) {
var algorithm = vector.algorithmName;
return importVectorKeys(vector, ['verify'], ['sign']).then(
function (vectors) {
promise_test(function (test) {
var operation = subtle
.sign(algorithm, wrongKey, vector.data)
.then(
function (signature) {
assert_unreached(
'Signing should not have succeeded for ' + vector.name
);
},
function (err) {
assert_equals(
err.name,
'InvalidAccessError',
"Should have thrown InvalidAccessError instead of '" +
err.message +
"'"
);
}
);
return operation;
}, vector.name + ' signing with wrong algorithm name');
},
function (err) {
// We need a failed test if the importVectorKey operation fails, so
// we know we never tested verification.
promise_test(function (test) {
assert_unreached(
'importVectorKeys failed for ' +
vector.name +
". Message: ''" +
err.message +
"''"
);
}, 'importVectorKeys step: ' +
vector.name +
' signing with wrong algorithm name');
}
);
},
function (err) {
promise_test(function (test) {
assert_unreached(
'Generate wrong key for test ' +
vector.name +
" failed: '" +
err.message +
"'"
);
}, 'generate wrong key step: ' +
vector.name +
' signing with wrong algorithm name');
}
);
all_promises.push(promise);
});
// Test verification with the wrong algorithm
testVectors.forEach(function (vector) {
// Want to get the key for the wrong algorithm
var promise = subtle
.generateKey({ name: 'HMAC', hash: 'SHA-1' }, false, ['sign', 'verify'])
.then(
function (wrongKey) {
return importVectorKeys(vector, ['verify'], ['sign']).then(
function (vectors) {
var algorithm = vector.algorithmName;
promise_test(function (test) {
var operation = subtle
.verify(algorithm, wrongKey, vector.signature, vector.data)
.then(
function (signature) {
assert_unreached(
'Verifying should not have succeeded for ' + vector.name
);
},
function (err) {
assert_equals(
err.name,
'InvalidAccessError',
"Should have thrown InvalidAccessError instead of '" +
err.message +
"'"
);
}
);
return operation;
}, vector.name + ' verifying with wrong algorithm name');
},
function (err) {
// We need a failed test if the importVectorKey operation fails, so
// we know we never tested verification.
promise_test(function (test) {
assert_unreached(
'importVectorKeys failed for ' +
vector.name +
". Message: ''" +
err.message +
"''"
);
}, 'importVectorKeys step: ' +
vector.name +
' verifying with wrong algorithm name');
}
);
},
function (err) {
promise_test(function (test) {
assert_unreached(
'Generate wrong key for test ' +
vector.name +
" failed: '" +
err.message +
"'"
);
}, 'generate wrong key step: ' +
vector.name +
' verifying with wrong algorithm name');
}
);
all_promises.push(promise);
});
// Test verification fails with wrong signature
testVectors.forEach(function (vector) {
var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
function (vectors) {
var algorithm = vector.algorithmName;
var signature = copyBuffer(vector.signature);
signature[0] = 255 - signature[0];
promise_test(function (test) {
var operation = subtle
.verify(algorithm, vector.publicKey, signature, vector.data)
.then(
function (is_verified) {
assert_false(is_verified, 'Signature NOT verified');
},
function (err) {
assert_unreached(
'Verification should not throw error ' +
vector.name +
': ' +
err.message +
"'"
);
}
);
return operation;
}, vector.name + ' verification failure due to altered signature');
},
function (err) {
// We need a failed test if the importVectorKey operation fails, so
// we know we never tested verification.
promise_test(function (test) {
assert_unreached(
'importVectorKeys failed for ' +
vector.name +
". Message: ''" +
err.message +
"''"
);
}, 'importVectorKeys step: ' +
vector.name +
' verification failure due to altered signature');
}
);
all_promises.push(promise);
});
// Test verification fails with short (odd length) signature
testVectors.forEach(function (vector) {
var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
function (vectors) {
var algorithm = vector.algorithmName;
var signature = vector.signature.slice(1); // Skip the first byte
promise_test(function (test) {
var operation = subtle
.verify(algorithm, vector.publicKey, signature, vector.data)
.then(
function (is_verified) {
assert_false(is_verified, 'Signature NOT verified');
},
function (err) {
assert_unreached(
'Verification should not throw error ' +
vector.name +
': ' +
err.message +
"'"
);
}
);
return operation;
}, vector.name + ' verification failure due to shortened signature');
},
function (err) {
// We need a failed test if the importVectorKey operation fails, so
// we know we never tested verification.
promise_test(function (test) {
assert_unreached(
'importVectorKeys failed for ' +
vector.name +
". Message: ''" +
err.message +
"''"
);
}, 'importVectorKeys step: ' +
vector.name +
' verification failure due to shortened signature');
}
);
all_promises.push(promise);
});
// Test verification fails with wrong plaintext
testVectors.forEach(function (vector) {
var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
function (vectors) {
var algorithm = vector.algorithmName;
var plaintext = copyBuffer(vector.data);
plaintext[0] = 255 - plaintext[0];
promise_test(function (test) {
var operation = subtle
.verify(algorithm, vector.publicKey, vector.signature, plaintext)
.then(
function (is_verified) {
assert_false(is_verified, 'Signature NOT verified');
},
function (err) {
assert_unreached(
'Verification should not throw error ' +
vector.name +
': ' +
err.message +
"'"
);
}
);
return operation;
}, vector.name + ' verification failure due to altered plaintext');
},
function (err) {
// We need a failed test if the importVectorKey operation fails, so
// we know we never tested verification.
promise_test(function (test) {
assert_unreached(
'importVectorKeys failed for ' +
vector.name +
". Message: ''" +
err.message +
"''"
);
}, 'importVectorKeys step: ' +
vector.name +
' verification failure due to altered plaintext');
}
);
all_promises.push(promise);
});
// Test invalid signatures
invalidTestVectors.forEach(function (vector) {
var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
function (vectors) {
var algorithm = vector.algorithmName;
promise_test(function (test) {
var operation = subtle
.verify(algorithm, vector.publicKey, vector.signature, vector.data)
.then(
function (is_verified) {
assert_false(is_verified, 'Signature unexpectedly verified');
},
function (err) {
assert_unreached(
'Verification should not throw error ' +
vector.name +
': ' +
err.message +
"'"
);
}
);
return operation;
}, vector.name + ' verification');
},
function (err) {
// We need a failed test if the importVectorKey operation fails, so
// we know we never tested verification.
promise_test(function (test) {
assert_unreached(
'importVectorKeys failed for ' +
vector.name +
". Message: ''" +
err.message +
"''"
);
}, 'importVectorKeys step: ' + vector.name + ' verification');
}
);
all_promises.push(promise);
});
promise_test(function () {
return Promise.all(all_promises)
.then(function () {
done();
})
.catch(function () {
done();
});
}, 'setup');
// A test vector has all needed fields for signing and verifying, EXCEPT that the
// key field may be null. This function replaces that null with the Correct
// CryptoKey object.
//
// Returns a Promise that yields an updated vector on success.
function importVectorKeys(vector, publicKeyUsages, privateKeyUsages) {
var publicPromise, privatePromise;
if (vector.publicKey !== null) {
publicPromise = new Promise(function (resolve, reject) {
resolve(vector);
});
} else {
publicPromise = subtle
.importKey(
vector.publicKeyFormat,
vector.publicKeyBuffer,
{ name: vector.algorithmName },
false,
publicKeyUsages
)
.then(function (key) {
vector.publicKey = key;
return vector;
}); // Returns a copy of the sourceBuffer it is sent.
}
if (vector.privateKey !== null) {
privatePromise = new Promise(function (resolve, reject) {
resolve(vector);
});
} else {
privatePromise = subtle
.importKey(
vector.privateKeyFormat,
vector.privateKeyBuffer,
{ name: vector.algorithmName },
false,
privateKeyUsages
)
.then(function (key) {
vector.privateKey = key;
return vector;
});
}
return Promise.all([publicPromise, privatePromise]);
}
// Returns a copy of the sourceBuffer it is sent.
function copyBuffer(sourceBuffer) {
var source = new Uint8Array(sourceBuffer);
var copy = new Uint8Array(sourceBuffer.byteLength);
for (var i = 0; i < source.byteLength; i++) {
copy[i] = source[i];
}
return copy;
}
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;
}
return;
}