Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

// META: title=WebCryptoAPI: getPublicKey() method
// META: timeout=long
// META: script=util/helpers.js
"use strict";
const algorithms = [
{
name: "ECDH",
generateKeyParams: { name: "ECDH", namedCurve: "P-256" },
usages: ["deriveKey", "deriveBits"],
publicKeyUsages: []
},
{
name: "ECDSA",
generateKeyParams: { name: "ECDSA", namedCurve: "P-256" },
usages: ["sign", "verify"],
publicKeyUsages: ["verify"]
},
{
name: "Ed25519",
generateKeyParams: { name: "Ed25519" },
usages: ["sign", "verify"],
publicKeyUsages: ["verify"]
},
{
name: "RSA-OAEP",
generateKeyParams: {
name: "RSA-OAEP",
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256"
},
usages: ["encrypt", "decrypt"],
publicKeyUsages: ["encrypt"]
},
{
name: "RSA-PSS",
generateKeyParams: {
name: "RSA-PSS",
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256"
},
usages: ["sign", "verify"],
publicKeyUsages: ["verify"]
},
{
name: "RSASSA-PKCS1-v1_5",
generateKeyParams: {
name: "RSASSA-PKCS1-v1_5",
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256"
},
usages: ["sign", "verify"],
publicKeyUsages: ["verify"]
},
{
name: "X25519",
generateKeyParams: { name: "X25519" },
usages: ["deriveKey", "deriveBits"],
publicKeyUsages: []
}
];
// Test basic functionality for supported algorithms
algorithms.forEach(function(algorithm) {
promise_test(async function(t) {
// Generate a key pair
const keyPair = await crypto.subtle.generateKey(
algorithm.generateKeyParams,
false, // extractable
algorithm.usages
);
assert_true(keyPair.privateKey instanceof CryptoKey, "Generated private key");
assert_equals(keyPair.privateKey.type, "private", "Private key type");
// Test getPublicKey with valid usages
const publicKey = await crypto.subtle.getPublicKey(
keyPair.privateKey,
algorithm.publicKeyUsages
);
// Verify the returned public key
assert_true(publicKey instanceof CryptoKey, "getPublicKey returns a CryptoKey");
assert_equals(publicKey.type, "public", "Returned key is public");
assert_equals(publicKey.algorithm.name, algorithm.name, "Algorithm name matches");
assert_true(publicKey.extractable, "Public key is extractable");
// Verify usages
assert_equals(publicKey.usages.length, algorithm.publicKeyUsages.length, "Usage count matches");
algorithm.publicKeyUsages.forEach(function(usage) {
assert_true(publicKey.usages.includes(usage), `Has ${usage} usage`);
});
// Verify that the derived public key matches the original public key
// by comparing their exported forms
const originalExported = await crypto.subtle.exportKey("spki", keyPair.publicKey);
const derivedExported = await crypto.subtle.exportKey("spki", publicKey);
assert_array_equals(
new Uint8Array(originalExported),
new Uint8Array(derivedExported),
"Exported public keys match"
);
}, `getPublicKey works for ${algorithm.name}`);
});
// Test functional equivalence - ensure derived public key works for crypto operations
promise_test(async function(t) {
const keyPair = await crypto.subtle.generateKey(
{ name: "ECDSA", namedCurve: "P-256" },
false,
["sign", "verify"]
);
const derivedPublicKey = await crypto.subtle.getPublicKey(
keyPair.privateKey,
["verify"]
);
// Create test data
const data = new TextEncoder().encode("test message");
// Sign with private key
const signature = await crypto.subtle.sign(
{ name: "ECDSA", hash: "SHA-256" },
keyPair.privateKey,
data
);
// Verify with both original and derived public keys
const verifyOriginal = await crypto.subtle.verify(
{ name: "ECDSA", hash: "SHA-256" },
keyPair.publicKey,
signature,
data
);
const verifyDerived = await crypto.subtle.verify(
{ name: "ECDSA", hash: "SHA-256" },
derivedPublicKey,
signature,
data
);
assert_true(verifyOriginal, "Original public key verifies signature");
assert_true(verifyDerived, "Derived public key verifies signature");
}, "Derived public key is functionally equivalent to original public key");
// Test with empty usages array
algorithms.forEach(function(algorithm) {
promise_test(async function(t) {
// Skip X25519 if not supported
if (algorithm.name === "X25519") {
try {
await crypto.subtle.generateKey(algorithm.generateKeyParams, false, algorithm.usages);
} catch (e) {
if (e.name === "NotSupportedError") {
return;
}
throw e;
}
}
const keyPair = await crypto.subtle.generateKey(
algorithm.generateKeyParams,
false,
algorithm.usages
);
// Test with empty usages array
const publicKey = await crypto.subtle.getPublicKey(
keyPair.privateKey,
[]
);
assert_true(publicKey instanceof CryptoKey, "getPublicKey returns a CryptoKey");
assert_equals(publicKey.type, "public", "Returned key is public");
assert_equals(publicKey.usages.length, 0, "Public key has no usages");
}, `getPublicKey works with empty usages for ${algorithm.name}`);
});
// Test error cases
// Test with non-private key (should throw InvalidAccessError)
promise_test(async function(t) {
const keyPair = await crypto.subtle.generateKey(
{ name: "ECDSA", namedCurve: "P-256" },
false,
["sign", "verify"]
);
await promise_rejects_dom(t, "InvalidAccessError",
crypto.subtle.getPublicKey(keyPair.publicKey, ["verify"]),
"getPublicKey should reject when called with a public key"
);
}, "getPublicKey rejects with InvalidAccessError when given a public key");
// Test with symmetric keys (should throw NotSupportedError for non-private keys)
promise_test(async function(t) {
const aesKey = await crypto.subtle.generateKey(
{ name: "AES-GCM", length: 256 },
false,
["encrypt", "decrypt"]
);
await promise_rejects_dom(t, "NotSupportedError",
crypto.subtle.getPublicKey(aesKey, []),
"getPublicKey should reject AES-GCM keys"
);
}, "getPublicKey rejects with NotSupportedError for AES-GCM symmetric keys");
promise_test(async function(t) {
const hmacKey = await crypto.subtle.generateKey(
{ name: "HMAC", hash: "SHA-256" },
false,
["sign", "verify"]
);
await promise_rejects_dom(t, "NotSupportedError",
crypto.subtle.getPublicKey(hmacKey, []),
"getPublicKey should reject HMAC keys"
);
}, "getPublicKey rejects with NotSupportedError for HMAC symmetric keys");
// Test with invalid usages for the algorithm
promise_test(async function(t) {
const keyPair = await crypto.subtle.generateKey(
{ name: "ECDSA", namedCurve: "P-256" },
false,
["sign", "verify"]
);
// Try to use "encrypt" usage with ECDSA (not supported for ECDSA public keys)
await promise_rejects_dom(t, "SyntaxError",
crypto.subtle.getPublicKey(keyPair.privateKey, ["encrypt"]),
"getPublicKey should reject invalid usages for the algorithm"
);
}, "getPublicKey rejects with SyntaxError for invalid usages");
// Test with mixed valid and invalid usages
promise_test(async function(t) {
const keyPair = await crypto.subtle.generateKey(
{ name: "ECDSA", namedCurve: "P-256" },
false,
["sign", "verify"]
);
// Mix valid ("verify") and invalid ("encrypt") usages
await promise_rejects_dom(t, "SyntaxError",
crypto.subtle.getPublicKey(keyPair.privateKey, ["verify", "encrypt"]),
"getPublicKey should reject when any usage is invalid"
);
}, "getPublicKey rejects with SyntaxError when any usage is invalid for the algorithm");
// Test that the method exists
test(function() {
assert_true("getPublicKey" in crypto.subtle, "getPublicKey method exists on SubtleCrypto");
assert_equals(typeof crypto.subtle.getPublicKey, "function", "getPublicKey is a function");
}, "getPublicKey method is available");