11cb0ef41Sopenharmony_ci'use strict'; 21cb0ef41Sopenharmony_ci 31cb0ef41Sopenharmony_ciconst common = require('../common'); 41cb0ef41Sopenharmony_ci 51cb0ef41Sopenharmony_ciif (!common.hasCrypto) 61cb0ef41Sopenharmony_ci common.skip('missing crypto'); 71cb0ef41Sopenharmony_ci 81cb0ef41Sopenharmony_ciconst assert = require('assert'); 91cb0ef41Sopenharmony_ciconst { subtle } = require('crypto').webcrypto; 101cb0ef41Sopenharmony_ci 111cb0ef41Sopenharmony_ciconst kWrappingData = { 121cb0ef41Sopenharmony_ci 'RSA-OAEP': { 131cb0ef41Sopenharmony_ci generate: { 141cb0ef41Sopenharmony_ci modulusLength: 4096, 151cb0ef41Sopenharmony_ci publicExponent: new Uint8Array([1, 0, 1]), 161cb0ef41Sopenharmony_ci hash: 'SHA-256', 171cb0ef41Sopenharmony_ci }, 181cb0ef41Sopenharmony_ci wrap: { label: new Uint8Array(8) }, 191cb0ef41Sopenharmony_ci pair: true 201cb0ef41Sopenharmony_ci }, 211cb0ef41Sopenharmony_ci 'AES-CTR': { 221cb0ef41Sopenharmony_ci generate: { length: 128 }, 231cb0ef41Sopenharmony_ci wrap: { counter: new Uint8Array(16), length: 64 }, 241cb0ef41Sopenharmony_ci pair: false 251cb0ef41Sopenharmony_ci }, 261cb0ef41Sopenharmony_ci 'AES-CBC': { 271cb0ef41Sopenharmony_ci generate: { length: 128 }, 281cb0ef41Sopenharmony_ci wrap: { iv: new Uint8Array(16) }, 291cb0ef41Sopenharmony_ci pair: false 301cb0ef41Sopenharmony_ci }, 311cb0ef41Sopenharmony_ci 'AES-GCM': { 321cb0ef41Sopenharmony_ci generate: { length: 128 }, 331cb0ef41Sopenharmony_ci wrap: { 341cb0ef41Sopenharmony_ci iv: new Uint8Array(16), 351cb0ef41Sopenharmony_ci additionalData: new Uint8Array(16), 361cb0ef41Sopenharmony_ci tagLength: 64 371cb0ef41Sopenharmony_ci }, 381cb0ef41Sopenharmony_ci pair: false 391cb0ef41Sopenharmony_ci }, 401cb0ef41Sopenharmony_ci 'AES-KW': { 411cb0ef41Sopenharmony_ci generate: { length: 128 }, 421cb0ef41Sopenharmony_ci wrap: { }, 431cb0ef41Sopenharmony_ci pair: false 441cb0ef41Sopenharmony_ci } 451cb0ef41Sopenharmony_ci}; 461cb0ef41Sopenharmony_ci 471cb0ef41Sopenharmony_cifunction generateWrappingKeys() { 481cb0ef41Sopenharmony_ci return Promise.all(Object.keys(kWrappingData).map(async (name) => { 491cb0ef41Sopenharmony_ci const keys = await subtle.generateKey( 501cb0ef41Sopenharmony_ci { name, ...kWrappingData[name].generate }, 511cb0ef41Sopenharmony_ci true, 521cb0ef41Sopenharmony_ci ['wrapKey', 'unwrapKey']); 531cb0ef41Sopenharmony_ci if (kWrappingData[name].pair) { 541cb0ef41Sopenharmony_ci kWrappingData[name].wrappingKey = keys.publicKey; 551cb0ef41Sopenharmony_ci kWrappingData[name].unwrappingKey = keys.privateKey; 561cb0ef41Sopenharmony_ci } else { 571cb0ef41Sopenharmony_ci kWrappingData[name].wrappingKey = keys; 581cb0ef41Sopenharmony_ci kWrappingData[name].unwrappingKey = keys; 591cb0ef41Sopenharmony_ci } 601cb0ef41Sopenharmony_ci })); 611cb0ef41Sopenharmony_ci} 621cb0ef41Sopenharmony_ci 631cb0ef41Sopenharmony_ciasync function generateKeysToWrap() { 641cb0ef41Sopenharmony_ci const parameters = [ 651cb0ef41Sopenharmony_ci { 661cb0ef41Sopenharmony_ci algorithm: { 671cb0ef41Sopenharmony_ci name: 'RSASSA-PKCS1-v1_5', 681cb0ef41Sopenharmony_ci modulusLength: 1024, 691cb0ef41Sopenharmony_ci publicExponent: new Uint8Array([1, 0, 1]), 701cb0ef41Sopenharmony_ci hash: 'SHA-256' 711cb0ef41Sopenharmony_ci }, 721cb0ef41Sopenharmony_ci privateUsages: ['sign'], 731cb0ef41Sopenharmony_ci publicUsages: ['verify'], 741cb0ef41Sopenharmony_ci pair: true, 751cb0ef41Sopenharmony_ci }, 761cb0ef41Sopenharmony_ci { 771cb0ef41Sopenharmony_ci algorithm: { 781cb0ef41Sopenharmony_ci name: 'RSA-PSS', 791cb0ef41Sopenharmony_ci modulusLength: 1024, 801cb0ef41Sopenharmony_ci publicExponent: new Uint8Array([1, 0, 1]), 811cb0ef41Sopenharmony_ci hash: 'SHA-256' 821cb0ef41Sopenharmony_ci }, 831cb0ef41Sopenharmony_ci privateUsages: ['sign'], 841cb0ef41Sopenharmony_ci publicUsages: ['verify'], 851cb0ef41Sopenharmony_ci pair: true, 861cb0ef41Sopenharmony_ci }, 871cb0ef41Sopenharmony_ci { 881cb0ef41Sopenharmony_ci algorithm: { 891cb0ef41Sopenharmony_ci name: 'RSA-OAEP', 901cb0ef41Sopenharmony_ci modulusLength: 1024, 911cb0ef41Sopenharmony_ci publicExponent: new Uint8Array([1, 0, 1]), 921cb0ef41Sopenharmony_ci hash: 'SHA-256' 931cb0ef41Sopenharmony_ci }, 941cb0ef41Sopenharmony_ci privateUsages: ['decrypt'], 951cb0ef41Sopenharmony_ci publicUsages: ['encrypt'], 961cb0ef41Sopenharmony_ci pair: true, 971cb0ef41Sopenharmony_ci }, 981cb0ef41Sopenharmony_ci { 991cb0ef41Sopenharmony_ci algorithm: { 1001cb0ef41Sopenharmony_ci name: 'ECDSA', 1011cb0ef41Sopenharmony_ci namedCurve: 'P-384' 1021cb0ef41Sopenharmony_ci }, 1031cb0ef41Sopenharmony_ci privateUsages: ['sign'], 1041cb0ef41Sopenharmony_ci publicUsages: ['verify'], 1051cb0ef41Sopenharmony_ci pair: true, 1061cb0ef41Sopenharmony_ci }, 1071cb0ef41Sopenharmony_ci { 1081cb0ef41Sopenharmony_ci algorithm: { 1091cb0ef41Sopenharmony_ci name: 'ECDH', 1101cb0ef41Sopenharmony_ci namedCurve: 'P-384' 1111cb0ef41Sopenharmony_ci }, 1121cb0ef41Sopenharmony_ci privateUsages: ['deriveBits'], 1131cb0ef41Sopenharmony_ci publicUsages: [], 1141cb0ef41Sopenharmony_ci pair: true, 1151cb0ef41Sopenharmony_ci }, 1161cb0ef41Sopenharmony_ci { 1171cb0ef41Sopenharmony_ci algorithm: { 1181cb0ef41Sopenharmony_ci name: 'Ed25519', 1191cb0ef41Sopenharmony_ci }, 1201cb0ef41Sopenharmony_ci privateUsages: ['sign'], 1211cb0ef41Sopenharmony_ci publicUsages: ['verify'], 1221cb0ef41Sopenharmony_ci pair: true, 1231cb0ef41Sopenharmony_ci }, 1241cb0ef41Sopenharmony_ci { 1251cb0ef41Sopenharmony_ci algorithm: { 1261cb0ef41Sopenharmony_ci name: 'Ed448', 1271cb0ef41Sopenharmony_ci }, 1281cb0ef41Sopenharmony_ci privateUsages: ['sign'], 1291cb0ef41Sopenharmony_ci publicUsages: ['verify'], 1301cb0ef41Sopenharmony_ci pair: true, 1311cb0ef41Sopenharmony_ci }, 1321cb0ef41Sopenharmony_ci { 1331cb0ef41Sopenharmony_ci algorithm: { 1341cb0ef41Sopenharmony_ci name: 'X25519', 1351cb0ef41Sopenharmony_ci }, 1361cb0ef41Sopenharmony_ci privateUsages: ['deriveBits'], 1371cb0ef41Sopenharmony_ci publicUsages: [], 1381cb0ef41Sopenharmony_ci pair: true, 1391cb0ef41Sopenharmony_ci }, 1401cb0ef41Sopenharmony_ci { 1411cb0ef41Sopenharmony_ci algorithm: { 1421cb0ef41Sopenharmony_ci name: 'X448', 1431cb0ef41Sopenharmony_ci }, 1441cb0ef41Sopenharmony_ci privateUsages: ['deriveBits'], 1451cb0ef41Sopenharmony_ci publicUsages: [], 1461cb0ef41Sopenharmony_ci pair: true, 1471cb0ef41Sopenharmony_ci }, 1481cb0ef41Sopenharmony_ci { 1491cb0ef41Sopenharmony_ci algorithm: { 1501cb0ef41Sopenharmony_ci name: 'AES-CTR', 1511cb0ef41Sopenharmony_ci length: 128 1521cb0ef41Sopenharmony_ci }, 1531cb0ef41Sopenharmony_ci usages: ['encrypt', 'decrypt'], 1541cb0ef41Sopenharmony_ci pair: false, 1551cb0ef41Sopenharmony_ci }, 1561cb0ef41Sopenharmony_ci { 1571cb0ef41Sopenharmony_ci algorithm: { 1581cb0ef41Sopenharmony_ci name: 'AES-CBC', 1591cb0ef41Sopenharmony_ci length: 128 1601cb0ef41Sopenharmony_ci }, 1611cb0ef41Sopenharmony_ci usages: ['encrypt', 'decrypt'], 1621cb0ef41Sopenharmony_ci pair: false, 1631cb0ef41Sopenharmony_ci }, 1641cb0ef41Sopenharmony_ci { 1651cb0ef41Sopenharmony_ci algorithm: { 1661cb0ef41Sopenharmony_ci name: 'AES-GCM', length: 128 1671cb0ef41Sopenharmony_ci }, 1681cb0ef41Sopenharmony_ci usages: ['encrypt', 'decrypt'], 1691cb0ef41Sopenharmony_ci pair: false, 1701cb0ef41Sopenharmony_ci }, 1711cb0ef41Sopenharmony_ci { 1721cb0ef41Sopenharmony_ci algorithm: { 1731cb0ef41Sopenharmony_ci name: 'AES-KW', 1741cb0ef41Sopenharmony_ci length: 128 1751cb0ef41Sopenharmony_ci }, 1761cb0ef41Sopenharmony_ci usages: ['wrapKey', 'unwrapKey'], 1771cb0ef41Sopenharmony_ci pair: false, 1781cb0ef41Sopenharmony_ci }, 1791cb0ef41Sopenharmony_ci { 1801cb0ef41Sopenharmony_ci algorithm: { 1811cb0ef41Sopenharmony_ci name: 'HMAC', 1821cb0ef41Sopenharmony_ci length: 128, 1831cb0ef41Sopenharmony_ci hash: 'SHA-256' 1841cb0ef41Sopenharmony_ci }, 1851cb0ef41Sopenharmony_ci usages: ['sign', 'verify'], 1861cb0ef41Sopenharmony_ci pair: false, 1871cb0ef41Sopenharmony_ci }, 1881cb0ef41Sopenharmony_ci ]; 1891cb0ef41Sopenharmony_ci 1901cb0ef41Sopenharmony_ci const allkeys = await Promise.all(parameters.map(async (params) => { 1911cb0ef41Sopenharmony_ci const usages = 'usages' in params ? 1921cb0ef41Sopenharmony_ci params.usages : 1931cb0ef41Sopenharmony_ci params.publicUsages.concat(params.privateUsages); 1941cb0ef41Sopenharmony_ci 1951cb0ef41Sopenharmony_ci const keys = await subtle.generateKey(params.algorithm, true, usages); 1961cb0ef41Sopenharmony_ci 1971cb0ef41Sopenharmony_ci if (params.pair) { 1981cb0ef41Sopenharmony_ci return [ 1991cb0ef41Sopenharmony_ci { 2001cb0ef41Sopenharmony_ci algorithm: params.algorithm, 2011cb0ef41Sopenharmony_ci usages: params.publicUsages, 2021cb0ef41Sopenharmony_ci key: keys.publicKey, 2031cb0ef41Sopenharmony_ci }, 2041cb0ef41Sopenharmony_ci { 2051cb0ef41Sopenharmony_ci algorithm: params.algorithm, 2061cb0ef41Sopenharmony_ci usages: params.privateUsages, 2071cb0ef41Sopenharmony_ci key: keys.privateKey, 2081cb0ef41Sopenharmony_ci }, 2091cb0ef41Sopenharmony_ci ]; 2101cb0ef41Sopenharmony_ci } 2111cb0ef41Sopenharmony_ci 2121cb0ef41Sopenharmony_ci return [{ 2131cb0ef41Sopenharmony_ci algorithm: params.algorithm, 2141cb0ef41Sopenharmony_ci usages: params.usages, 2151cb0ef41Sopenharmony_ci key: keys, 2161cb0ef41Sopenharmony_ci }]; 2171cb0ef41Sopenharmony_ci })); 2181cb0ef41Sopenharmony_ci 2191cb0ef41Sopenharmony_ci return allkeys.flat(); 2201cb0ef41Sopenharmony_ci} 2211cb0ef41Sopenharmony_ci 2221cb0ef41Sopenharmony_cifunction getFormats(key) { 2231cb0ef41Sopenharmony_ci switch (key.key.type) { 2241cb0ef41Sopenharmony_ci case 'secret': return ['raw', 'jwk']; 2251cb0ef41Sopenharmony_ci case 'public': return ['spki', 'jwk']; 2261cb0ef41Sopenharmony_ci case 'private': return ['pkcs8', 'jwk']; 2271cb0ef41Sopenharmony_ci } 2281cb0ef41Sopenharmony_ci} 2291cb0ef41Sopenharmony_ci 2301cb0ef41Sopenharmony_ci// If the wrapping algorithm is AES-KW, the exported key 2311cb0ef41Sopenharmony_ci// material length must be a multiple of 8. 2321cb0ef41Sopenharmony_ci// If the wrapping algorithm is RSA-OAEP, the exported key 2331cb0ef41Sopenharmony_ci// material maximum length is a factor of the modulusLength 2341cb0ef41Sopenharmony_ci// 2351cb0ef41Sopenharmony_ci// As per the NOTE in step 13 https://w3c.github.io/webcrypto/#SubtleCrypto-method-wrapKey 2361cb0ef41Sopenharmony_ci// we're padding AES-KW wrapped JWK to make sure it is always a multiple of 8 bytes 2371cb0ef41Sopenharmony_ci// in length 2381cb0ef41Sopenharmony_ciasync function wrappingIsPossible(name, exported) { 2391cb0ef41Sopenharmony_ci if ('byteLength' in exported) { 2401cb0ef41Sopenharmony_ci switch (name) { 2411cb0ef41Sopenharmony_ci case 'AES-KW': 2421cb0ef41Sopenharmony_ci return exported.byteLength % 8 === 0; 2431cb0ef41Sopenharmony_ci case 'RSA-OAEP': 2441cb0ef41Sopenharmony_ci return exported.byteLength <= 446; 2451cb0ef41Sopenharmony_ci } 2461cb0ef41Sopenharmony_ci } else if ('kty' in exported && name === 'RSA-OAEP') { 2471cb0ef41Sopenharmony_ci return JSON.stringify(exported).length <= 478; 2481cb0ef41Sopenharmony_ci } 2491cb0ef41Sopenharmony_ci return true; 2501cb0ef41Sopenharmony_ci} 2511cb0ef41Sopenharmony_ci 2521cb0ef41Sopenharmony_ciasync function testWrap(wrappingKey, unwrappingKey, key, wrap, format) { 2531cb0ef41Sopenharmony_ci const exported = await subtle.exportKey(format, key.key); 2541cb0ef41Sopenharmony_ci if (!(await wrappingIsPossible(wrappingKey.algorithm.name, exported))) 2551cb0ef41Sopenharmony_ci return; 2561cb0ef41Sopenharmony_ci 2571cb0ef41Sopenharmony_ci const wrapped = 2581cb0ef41Sopenharmony_ci await subtle.wrapKey( 2591cb0ef41Sopenharmony_ci format, 2601cb0ef41Sopenharmony_ci key.key, 2611cb0ef41Sopenharmony_ci wrappingKey, 2621cb0ef41Sopenharmony_ci { name: wrappingKey.algorithm.name, ...wrap }); 2631cb0ef41Sopenharmony_ci const unwrapped = 2641cb0ef41Sopenharmony_ci await subtle.unwrapKey( 2651cb0ef41Sopenharmony_ci format, 2661cb0ef41Sopenharmony_ci wrapped, 2671cb0ef41Sopenharmony_ci unwrappingKey, 2681cb0ef41Sopenharmony_ci { name: wrappingKey.algorithm.name, ...wrap }, 2691cb0ef41Sopenharmony_ci key.algorithm, 2701cb0ef41Sopenharmony_ci true, 2711cb0ef41Sopenharmony_ci key.usages); 2721cb0ef41Sopenharmony_ci assert(unwrapped.extractable); 2731cb0ef41Sopenharmony_ci 2741cb0ef41Sopenharmony_ci const exportedAgain = await subtle.exportKey(format, unwrapped); 2751cb0ef41Sopenharmony_ci assert.deepStrictEqual(exported, exportedAgain); 2761cb0ef41Sopenharmony_ci} 2771cb0ef41Sopenharmony_ci 2781cb0ef41Sopenharmony_cifunction testWrapping(name, keys) { 2791cb0ef41Sopenharmony_ci const variations = []; 2801cb0ef41Sopenharmony_ci 2811cb0ef41Sopenharmony_ci const { 2821cb0ef41Sopenharmony_ci wrappingKey, 2831cb0ef41Sopenharmony_ci unwrappingKey, 2841cb0ef41Sopenharmony_ci wrap 2851cb0ef41Sopenharmony_ci } = kWrappingData[name]; 2861cb0ef41Sopenharmony_ci 2871cb0ef41Sopenharmony_ci keys.forEach((key) => { 2881cb0ef41Sopenharmony_ci getFormats(key).forEach((format) => { 2891cb0ef41Sopenharmony_ci variations.push(testWrap(wrappingKey, unwrappingKey, key, wrap, format)); 2901cb0ef41Sopenharmony_ci }); 2911cb0ef41Sopenharmony_ci }); 2921cb0ef41Sopenharmony_ci 2931cb0ef41Sopenharmony_ci return variations; 2941cb0ef41Sopenharmony_ci} 2951cb0ef41Sopenharmony_ci 2961cb0ef41Sopenharmony_ci(async function() { 2971cb0ef41Sopenharmony_ci await generateWrappingKeys(); 2981cb0ef41Sopenharmony_ci const keys = await generateKeysToWrap(); 2991cb0ef41Sopenharmony_ci const variations = []; 3001cb0ef41Sopenharmony_ci Object.keys(kWrappingData).forEach((name) => { 3011cb0ef41Sopenharmony_ci variations.push(...testWrapping(name, keys)); 3021cb0ef41Sopenharmony_ci }); 3031cb0ef41Sopenharmony_ci await Promise.all(variations); 3041cb0ef41Sopenharmony_ci})().then(common.mustCall()); 305