if [ $njs_found = yes ]; then
- njs_feature="EVP_aes_128_wrap()"
- njs_feature_name=NJS_HAVE_AES_WRAP
- njs_feature_run=
- njs_feature_test="#include <openssl/evp.h>
-
- int main() {
- (void) EVP_aes_128_wrap();
- return 0;
- }"
- . auto/feature
-
- # Ed25519 and X25519 share the same Curve25519 implementation and
- # were introduced together in OpenSSL 1.1.1, LibreSSL 3.7.0,
- # BoringSSL, and AWS-LC. No known build configuration enables
- # one without the other, so a single probe is sufficient.
-
- njs_feature="EVP_PKEY_ED25519"
- njs_feature_name=NJS_HAVE_ED25519
- njs_feature_run=
- njs_feature_test="#include <openssl/evp.h>
-
- int main() {
- return EVP_PKEY_new_raw_public_key(
- EVP_PKEY_ED25519, NULL, NULL, 0) == NULL;
- }"
- . auto/feature
-
njs_feature="OpenSSL version"
njs_feature_name=NJS_OPENSSL_VERSION
njs_feature_run=value
#endif
+#if defined(EVP_PKEY_ED25519) && defined(EVP_PKEY_X25519)
+#define NJS_OPENSSL_HAS_25519 1
+#else
+#define NJS_OPENSSL_HAS_25519 0
+#endif
+
+
#if (OPENSSL_VERSION_NUMBER >= 0x10002000L \
&& (!defined LIBRESSL_VERSION_NUMBER \
|| LIBRESSL_VERSION_NUMBER >= 0x3010000fL))
static njs_int_t njs_cipher_aes_cbc(njs_vm_t *vm, njs_str_t *data,
njs_webcrypto_key_t *key, njs_value_t *options, njs_bool_t encrypt,
njs_value_t *retval);
-#if (NJS_HAVE_AES_WRAP)
+#if defined(EVP_CIPHER_CTX_FLAG_WRAP_ALLOW)
static njs_int_t njs_cipher_aes_kw(njs_vm_t *vm, njs_str_t *data,
njs_webcrypto_key_t *key, njs_bool_t encrypt, njs_value_t *retval);
#endif
njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
static njs_int_t njs_ext_generate_key(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
-#if (NJS_HAVE_ED25519)
+#if (NJS_OPENSSL_HAS_25519)
static njs_int_t njs_webcrypto_generate_25519_keypair(njs_vm_t *vm,
int pkey_id, njs_webcrypto_key_t *key, njs_webcrypto_algorithm_t *alg,
unsigned usage, njs_bool_t extractable, unsigned priv_usage,
1)
},
-#if (NJS_HAVE_AES_WRAP)
+#if defined(EVP_CIPHER_CTX_FLAG_WRAP_ALLOW)
{
njs_str("AES-KW"),
njs_webcrypto_algorithm(NJS_ALGORITHM_AES_KW,
0)
},
-#if (NJS_HAVE_ED25519)
+#if (NJS_OPENSSL_HAS_25519)
{
njs_str("Ed25519"),
njs_webcrypto_algorithm(NJS_ALGORITHM_ED25519,
}
-#if (NJS_HAVE_AES_WRAP)
+#if defined(EVP_CIPHER_CTX_FLAG_WRAP_ALLOW)
static njs_int_t
njs_cipher_aes_kw(njs_vm_t *vm, njs_str_t *data, njs_webcrypto_key_t *key,
njs_bool_t encrypt, njs_value_t *retval)
switch (key->u.s.raw.length) {
case 16:
- cipher = EVP_aes_128_wrap();
+ cipher = EVP_get_cipherbyname("id-aes128-wrap");
break;
case 24:
- cipher = EVP_aes_192_wrap();
+ cipher = EVP_get_cipherbyname("id-aes192-wrap");
break;
case 32:
- cipher = EVP_aes_256_wrap();
+ cipher = EVP_get_cipherbyname("id-aes256-wrap");
break;
default:
return NJS_ERROR;
}
+ if (njs_slow_path(cipher == NULL)) {
+ njs_vm_type_error(vm, "AES-KW is not supported by this OpenSSL build");
+ return NJS_ERROR;
+ }
+
ctx = EVP_CIPHER_CTX_new();
if (njs_slow_path(ctx == NULL)) {
njs_webcrypto_error(vm, "EVP_CIPHER_CTX_new() failed");
}
-#if (NJS_HAVE_ED25519)
+#if (NJS_OPENSSL_HAS_25519)
static njs_int_t
njs_export_jwk_okp(njs_vm_t *vm, njs_webcrypto_key_t *key,
njs_value_t *retval)
}
-#if (NJS_HAVE_ED25519)
+#if (NJS_OPENSSL_HAS_25519)
static njs_int_t
njs_webcrypto_generate_25519_keypair(njs_vm_t *vm, int pkey_id,
njs_webcrypto_key_t *key, njs_webcrypto_algorithm_t *alg, unsigned usage,
break;
-#if (NJS_HAVE_ED25519)
+#if (NJS_OPENSSL_HAS_25519)
case NJS_ALGORITHM_ED25519:
ret = njs_webcrypto_generate_25519_keypair(vm, EVP_PKEY_ED25519,
key, alg, usage,
}
-#if (NJS_HAVE_ED25519)
+#if (NJS_OPENSSL_HAS_25519)
static EVP_PKEY *
njs_import_jwk_okp(njs_vm_t *vm, njs_value_t *jwk, njs_webcrypto_key_t *key)
{
goto fail;
}
-#if (NJS_HAVE_ED25519)
+#if (NJS_OPENSSL_HAS_25519)
} else if (njs_strstr_eq(&kty, &njs_str_value("OKP"))) {
if (alg->type != NJS_ALGORITHM_ED25519
&& alg->type != NJS_ALGORITHM_X25519)
break;
-#if (NJS_HAVE_ED25519)
+#if (NJS_OPENSSL_HAS_25519)
case NJS_ALGORITHM_ED25519:
if (fmt == NJS_KEY_FORMAT_RAW) {
pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL,
outlen = 0;
switch (alg->type) {
-#if (NJS_HAVE_ED25519)
+#if (NJS_OPENSSL_HAS_25519)
case NJS_ALGORITHM_ED25519:
mctx = njs_evp_md_ctx_new();
if (njs_slow_path(mctx == NULL)) {
case NJS_ALGORITHM_ECDH:
return njs_export_jwk_asymmetric(vm, key, retval);
-#if (NJS_HAVE_ED25519)
+#if (NJS_OPENSSL_HAS_25519)
case NJS_ALGORITHM_ED25519:
case NJS_ALGORITHM_X25519:
return njs_export_jwk_okp(vm, key, retval);
return njs_export_raw_ec(vm, key, retval);
}
-#if (NJS_HAVE_ED25519)
+#if (NJS_OPENSSL_HAS_25519)
if (key->alg->type == NJS_ALGORITHM_ED25519
|| key->alg->type == NJS_ALGORITHM_X25519)
{
case NJS_ALGORITHM_AES_CBC:
return njs_cipher_aes_cbc(vm, data, key, options, encrypt, retval);
-#if (NJS_HAVE_AES_WRAP)
+#if defined(EVP_CIPHER_CTX_FLAG_WRAP_ALLOW)
case NJS_ALGORITHM_AES_KW:
return njs_cipher_aes_kw(vm, data, key, encrypt, retval);
#endif
qjs_webcrypto_key_t *key, JSValue options, int encrypt);
static JSValue qjs_cipher_aes_cbc(JSContext *cx, njs_str_t *data,
qjs_webcrypto_key_t *key, JSValue options, int encrypt);
-#if (NJS_HAVE_AES_WRAP)
+#if defined(EVP_CIPHER_CTX_FLAG_WRAP_ALLOW)
static JSValue qjs_cipher_aes_kw(JSContext *cx, njs_str_t *data,
qjs_webcrypto_key_t *key, int encrypt);
#endif
int argc, JSValueConst *argv);
static JSValue qjs_webcrypto_generate_key(JSContext *cx, JSValueConst this_val,
int argc, JSValueConst *argv);
-#if (NJS_HAVE_ED25519)
+#if (NJS_OPENSSL_HAS_25519)
static int qjs_webcrypto_generate_25519_keypair(JSContext *cx, int pkey_id,
qjs_webcrypto_key_t *wkey, qjs_webcrypto_algorithm_t *alg,
unsigned usage, int extractable, unsigned priv_usage,
1)
},
-#if (NJS_HAVE_AES_WRAP)
+#if defined(EVP_CIPHER_CTX_FLAG_WRAP_ALLOW)
{
njs_str("AES-KW"),
qjs_webcrypto_algorithm(QJS_ALGORITHM_AES_KW,
0)
},
-#if (NJS_HAVE_ED25519)
+#if (NJS_OPENSSL_HAS_25519)
{
njs_str("Ed25519"),
qjs_webcrypto_algorithm(QJS_ALGORITHM_ED25519,
}
-#if (NJS_HAVE_AES_WRAP)
+#if defined(EVP_CIPHER_CTX_FLAG_WRAP_ALLOW)
static JSValue
qjs_cipher_aes_kw(JSContext *cx, njs_str_t *data, qjs_webcrypto_key_t *key,
int encrypt)
switch (key->u.s.raw.length) {
case 16:
- cipher = EVP_aes_128_wrap();
+ cipher = EVP_get_cipherbyname("id-aes128-wrap");
break;
case 24:
- cipher = EVP_aes_192_wrap();
+ cipher = EVP_get_cipherbyname("id-aes192-wrap");
break;
case 32:
- cipher = EVP_aes_256_wrap();
+ cipher = EVP_get_cipherbyname("id-aes256-wrap");
break;
default:
return JS_EXCEPTION;
}
+ if (cipher == NULL) {
+ return JS_ThrowTypeError(cx,
+ "AES-KW is not supported by this OpenSSL build");
+ }
+
ctx = EVP_CIPHER_CTX_new();
if (ctx == NULL) {
qjs_webcrypto_error(cx, "EVP_CIPHER_CTX_new() failed");
}
-#if (NJS_HAVE_ED25519)
+#if (NJS_OPENSSL_HAS_25519)
static JSValue
qjs_export_jwk_okp(JSContext *cx, qjs_webcrypto_key_t *key)
{
case QJS_ALGORITHM_ECDH:
return qjs_export_jwk_asymmetric(cx, key);
-#if (NJS_HAVE_ED25519)
+#if (NJS_OPENSSL_HAS_25519)
case QJS_ALGORITHM_ED25519:
case QJS_ALGORITHM_X25519:
return qjs_export_jwk_okp(cx, key);
return qjs_export_raw_ec(cx, key);
}
-#if (NJS_HAVE_ED25519)
+#if (NJS_OPENSSL_HAS_25519)
if (key->alg->type == QJS_ALGORITHM_ED25519
|| key->alg->type == QJS_ALGORITHM_X25519)
{
case QJS_ALGORITHM_AES_CBC:
return qjs_cipher_aes_cbc(cx, data, key, options, encrypt);
-#if (NJS_HAVE_AES_WRAP)
+#if defined(EVP_CIPHER_CTX_FLAG_WRAP_ALLOW)
case QJS_ALGORITHM_AES_KW:
return qjs_cipher_aes_kw(cx, data, key, encrypt);
#endif
}
-#if (NJS_HAVE_ED25519)
+#if (NJS_OPENSSL_HAS_25519)
static int
qjs_webcrypto_generate_25519_keypair(JSContext *cx, int pkey_id,
qjs_webcrypto_key_t *wkey, qjs_webcrypto_algorithm_t *alg, unsigned usage,
break;
-#if (NJS_HAVE_ED25519)
+#if (NJS_OPENSSL_HAS_25519)
case QJS_ALGORITHM_ED25519:
n = qjs_webcrypto_generate_25519_keypair(cx, EVP_PKEY_ED25519,
wkey, alg, usage,
}
-#if (NJS_HAVE_ED25519)
+#if (NJS_OPENSSL_HAS_25519)
static EVP_PKEY *
qjs_import_jwk_okp(JSContext *cx, JSValue jwk, qjs_webcrypto_key_t *key)
{
break;
-#if (NJS_HAVE_ED25519)
+#if (NJS_OPENSSL_HAS_25519)
case QJS_KEY_JWK_KTY_OKP:
if (alg->type != QJS_ALGORITHM_ED25519
&& alg->type != QJS_ALGORITHM_X25519)
wkey->u.a.pkey = pkey;
break;
-#if (NJS_HAVE_ED25519)
+#if (NJS_OPENSSL_HAS_25519)
case QJS_ALGORITHM_ED25519:
if (fmt == QJS_KEY_FORMAT_RAW) {
pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL,
outlen = 0;
switch (alg->type) {
-#if (NJS_HAVE_ED25519)
+#if (NJS_OPENSSL_HAS_25519)
case QJS_ALGORITHM_ED25519:
mctx = njs_evp_md_ctx_new();
if (mctx == NULL) {
JSValue crypto, proto, global_obj;
JSModuleDef *m;
+#if (OPENSSL_VERSION_NUMBER < 0x10100003L)
+ OpenSSL_add_all_algorithms();
+#endif
+
if (!JS_IsRegisteredClass(JS_GetRuntime(cx),
QJS_CORE_CLASS_ID_WEBCRYPTO_KEY))
{
location /random_values_test {
js_content test.random_values_test;
}
+
+ location /algorithms {
+ js_content test.algorithms;
+ }
}
}
r.return(200, bits1 > (mean - 10 * stdd) && bits1 < (mean + 10 * stdd));
}
- export default {has_crypto, random_values_test};
+ async function ed25519() {
+ let data = new Uint8Array(16);
+ let ed = await crypto.subtle.generateKey("Ed25519", false,
+ ["sign", "verify"]);
+ let signature = await crypto.subtle.sign("Ed25519",
+ ed.privateKey, data);
+ if (!await crypto.subtle.verify("Ed25519", ed.publicKey,
+ signature, data))
+ {
+ throw Error("Ed25519 verification failed");
+ }
+ }
+
+ async function x25519() {
+ let x = await crypto.subtle.generateKey("X25519", false,
+ ["deriveBits"]);
+ let bits = await crypto.subtle.deriveBits(
+ {name: "X25519", public: x.publicKey}, x.privateKey, 256);
+ if (bits.byteLength !== 32) {
+ throw Error("X25519 derivation failed");
+ }
+ }
+
+ async function aes_kw() {
+ let data = new Uint8Array(16);
+ let key = await crypto.subtle.importKey("raw", data,
+ "AES-GCM", true, ["encrypt"]);
+ for (let length = 128; length <= 256; length += 64) {
+ let kw = await crypto.subtle.generateKey(
+ {name: "AES-KW", length}, false, ["wrapKey", "unwrapKey"]);
+ let wrapped = await crypto.subtle.wrapKey("raw", key, kw,
+ "AES-KW");
+ let unwrapped = await crypto.subtle.unwrapKey("raw", wrapped,
+ kw, "AES-KW", "AES-GCM", true, ["encrypt"]);
+ let raw = await crypto.subtle.exportKey("raw", unwrapped);
+ if (Buffer.from(raw).compare(Buffer.from(data)) !== 0) {
+ throw Error("AES-KW roundtrip failed");
+ }
+ }
+ }
+
+ async function algorithms(r) {
+ try {
+ switch (r.args.algorithm) {
+ case "Ed25519":
+ await ed25519();
+ break;
+ case "X25519":
+ await x25519();
+ break;
+ case "AES-KW":
+ await aes_kw();
+ break;
+ default:
+ throw Error("unknown test algorithm");
+ }
+
+ r.return(200, "SUCCESS");
+ } catch (e) {
+ r.return(500, e.message);
+ }
+ }
+
+ export default {has_crypto, random_values_test, algorithms};
EOF
plan(skip_all => 'njs crypto module not available')
if http_get('/has_crypto') !~ /true/;
-$t->plan(1);
+$t->plan(4);
###############################################################################
like(http_get('/random_values_test'), qr/true/, 'random_values_test');
+SKIP: {
+ skip 'no 25519 support in SSL library', 2
+ unless $t->has_feature('openssl:1.1.1')
+ || $t->has_feature('libressl:3.7.0')
+ || $t->has_module('BoringSSL|AWS-LC');
+
+ like(http_get('/algorithms?algorithm=Ed25519'), qr/SUCCESS/, 'Ed25519');
+ like(http_get('/algorithms?algorithm=X25519'), qr/SUCCESS/, 'X25519');
+}
+
+SKIP: {
+ skip 'no AES-KW support in SSL library', 1
+ unless $t->has_feature('openssl:1.0.2')
+ || $t->has_feature('libressl:3.0.0');
+
+ like(http_get('/algorithms?algorithm=AES-KW'), qr/SUCCESS/, 'AES-KW');
+}
+
###############################################################################
listen 127.0.0.1:8081;
return $test;
}
+
+ server {
+ listen 127.0.0.1:8082;
+ js_preread test.test_ed25519;
+ js_var $result;
+ return $result;
+ }
+
+ server {
+ listen 127.0.0.1:8083;
+ js_preread test.test_x25519;
+ js_var $result;
+ return $result;
+ }
+
+ server {
+ listen 127.0.0.1:8084;
+ js_preread test.test_aes_kw;
+ js_var $result;
+ return $result;
+ }
}
EOF
return bits1 > (mean - 10 * stdd) && bits1 < (mean + 10 * stdd);
}
- export default {has_crypto, random_values_test};
+ async function ed25519() {
+ let data = new Uint8Array(16);
+ let ed = await crypto.subtle.generateKey("Ed25519", false,
+ ["sign", "verify"]);
+ let signature = await crypto.subtle.sign("Ed25519",
+ ed.privateKey, data);
+ if (!await crypto.subtle.verify("Ed25519", ed.publicKey,
+ signature, data))
+ {
+ throw Error("Ed25519 verification failed");
+ }
+ }
+
+ async function x25519() {
+ let x = await crypto.subtle.generateKey("X25519", false,
+ ["deriveBits"]);
+ let bits = await crypto.subtle.deriveBits(
+ {name: "X25519", public: x.publicKey}, x.privateKey, 256);
+ if (bits.byteLength !== 32) {
+ throw Error("X25519 derivation failed");
+ }
+ }
+
+ async function aes_kw() {
+ let data = new Uint8Array(16);
+ let key = await crypto.subtle.importKey("raw", data,
+ "AES-GCM", true, ["encrypt"]);
+ for (let length = 128; length <= 256; length += 64) {
+ let kw = await crypto.subtle.generateKey(
+ {name: "AES-KW", length}, false, ["wrapKey", "unwrapKey"]);
+ let wrapped = await crypto.subtle.wrapKey("raw", key, kw,
+ "AES-KW");
+ let unwrapped = await crypto.subtle.unwrapKey("raw", wrapped,
+ kw, "AES-KW", "AES-GCM", true, ["encrypt"]);
+ let raw = await crypto.subtle.exportKey("raw", unwrapped);
+ if (Buffer.from(raw).compare(Buffer.from(data)) !== 0) {
+ throw Error("AES-KW roundtrip failed");
+ }
+ }
+ }
+
+ async function algorithms(s, operation) {
+ try {
+ await operation();
+
+ s.variables.result = "SUCCESS";
+ } catch (e) {
+ s.variables.result = e.message;
+ }
+
+ s.done();
+ }
+
+ async function test_ed25519(s) {
+ await algorithms(s, ed25519);
+ }
+
+ async function test_x25519(s) {
+ await algorithms(s, x25519);
+ }
+
+ async function test_aes_kw(s) {
+ await algorithms(s, aes_kw);
+ }
+
+ export default {has_crypto, random_values_test,
+ test_ed25519, test_x25519, test_aes_kw};
EOF
$t->try_run('no stream js_var');
plan(skip_all => 'njs crypto module not available')
if http_get('/has_crypto') !~ /true/;
-$t->plan(1);
+$t->plan(4);
###############################################################################
is(stream('127.0.0.1:' . port(8081))->io('###'), 'true', 'random values test');
+SKIP: {
+ skip 'no 25519 support in SSL library', 2
+ unless $t->has_feature('openssl:1.1.1')
+ || $t->has_feature('libressl:3.7.0')
+ || $t->has_module('BoringSSL|AWS-LC');
+
+ is(stream('127.0.0.1:' . port(8082))->read(), 'SUCCESS', 'Ed25519');
+ is(stream('127.0.0.1:' . port(8083))->read(), 'SUCCESS', 'X25519');
+}
+
+SKIP: {
+ skip 'no AES-KW support in SSL library', 1
+ unless $t->has_feature('openssl:1.0.2')
+ || $t->has_feature('libressl:3.0.0');
+
+ is(stream('127.0.0.1:' . port(8084))->read(), 'SUCCESS', 'AES-KW');
+}
+
###############################################################################
return (typeof crypto != 'undefined') ? crypto : null;
}
+async function has_webcrypto_algorithm(name, length) {
+ if (name !== 'Ed25519' && name !== 'X25519' && name !== 'AES-KW') {
+ throw Error('unknown test algorithm: ' + name);
+ }
+
+ if (!has_buffer() || !has_webcrypto()) {
+ return false;
+ }
+
+ try {
+ if (name === 'AES-KW') {
+ await crypto.subtle.generateKey(
+ {name, length: length || 128}, false, ['wrapKey']);
+
+ } else {
+ await crypto.subtle.generateKey(name, false,
+ name === 'Ed25519' ? ['sign', 'verify'] : ['deriveBits']);
+ }
+
+ } catch (e) {
+ if (e.message === 'unknown algorithm name: "' + name + '"'
+ || (e.name === 'NotSupportedError'
+ && e.message === 'Unrecognized algorithm name'))
+ {
+ return false;
+ }
+
+ throw e;
+ }
+
+ return true;
+}
return false;
}
- function map(ts) {
- return ts.tests.map(t => {
+ async function map(ts) {
+ let skipped = [];
+
+ for (let i = 0; i < ts.tests.length; i++) {
+ let t = ts.tests[i];
+
+ skipped[i] = t.skip && await t.skip();
+ }
+
+ return ts.tests.map((t, i) => {
try {
- if (t.skip && t.skip()) {
+ if (skipped[i]) {
return Promise.resolve("SKIPPED");
}
for (let k = 0; k < tlist.length; k++) {
let ts = tlist[k];
- if (ts.skip && ts.skip()) {
+ if (ts.skip && await ts.skip()) {
continue;
}
- let results = await Promise.allSettled(map(ts));
+ let results = await Promise.allSettled(await map(ts));
let r = results.map((r, i) => { r.passed = validate(ts.tests, r, i); return r; });
let passed = r.filter(r => r.passed).length;
async function test(params) {
- try {
- await crypto.subtle.generateKey(
- {name: "AES-KW", length: 128}, true, ["wrapKey", "unwrapKey"]);
- } catch (e) {
- if (e.message.indexOf("AES-KW") !== -1) {
- return 'SKIPPED';
- }
-
- throw e;
- }
-
let key;
if (params.generate) {
throw Error(`AES-KW unwrap failed`);
}
+ let tampered = Buffer.from(expected);
+ tampered[0] ^= 1;
+ let rejected = false;
+
+ try {
+ await crypto.subtle.unwrapKey("raw", tampered, kek,
+ "AES-KW", "AES-GCM", true, ["encrypt"]);
+ } catch (e) {
+ rejected = true;
+ }
+
+ if (!rejected) {
+ throw Error("AES-KW accepted a tampered key");
+ }
+
return 'SUCCESS';
}
let aes_kw_tsuite = {
name: "AES-KW wrap/unwrap",
- skip: () => (!has_buffer() || !has_webcrypto()),
+ skip: async () => !await has_webcrypto_algorithm("AES-KW"),
T: test,
prepare_args: (args) => args,
let aes_kw_error_tsuite = {
name: "AES-KW errors",
- skip: () => (!has_buffer() || !has_webcrypto()),
+ skip: async () => !await has_webcrypto_algorithm("AES-KW"),
T: (params) => params.T(),
prepare_args: (args) => args,
---*/
async function test(params) {
- try {
- await crypto.subtle.generateKey("Ed25519", true, ["sign", "verify"]);
- } catch (e) {
- if (e.message.indexOf("Ed25519") !== -1) {
- return 'SKIPPED';
- }
-
- throw e;
- }
-
/* generateKey + sign/verify roundtrip */
if (params.generate) {
let kp = await crypto.subtle.generateKey("Ed25519", true,
return 'SUCCESS';
}
- /* RFC 8032 test vector: sign-only (verify uses derived public key) */
+ /* RFC 8032 test vector */
if (params.rfc8032) {
let priv_jwk = {
kty: "OKP",
+ "expected:\n" + params.expected);
}
+ let pub = await crypto.subtle.importKey("jwk",
+ {kty: "OKP", crv: "Ed25519", x: params.x},
+ "Ed25519", false, ["verify"]);
+
+ if (!await crypto.subtle.verify("Ed25519", pub, expected, data)) {
+ throw Error("RFC 8032 signature verification failed");
+ }
+
+ expected[0] ^= 1;
+ if (await crypto.subtle.verify("Ed25519", pub, expected, data)) {
+ throw Error("Ed25519 accepted a tampered signature");
+ }
+
return 'SUCCESS';
}
let ed25519_tsuite = {
name: "Ed25519 sign/verify",
- skip: () => (!has_buffer() || !has_webcrypto()),
+ skip: async () => !await has_webcrypto_algorithm("Ed25519"),
T: test,
prepare_args: (args) => args,
]};
async function test_sign_with_x25519() {
- try {
- await crypto.subtle.generateKey("X25519", true, ["deriveBits"]);
- } catch (e) {
- if (e.message.indexOf("X25519") !== -1) {
- return 'SKIPPED';
- }
-
- throw e;
- }
-
let kp = await crypto.subtle.generateKey("X25519", true,
["deriveBits"]);
let data = new TextEncoder().encode("test");
}
async function test_derive_with_ed25519() {
- try {
- await crypto.subtle.generateKey("Ed25519", true,
- ["sign", "verify"]);
- } catch (e) {
- if (e.message.indexOf("Ed25519") !== -1) {
- return 'SKIPPED';
- }
-
- throw e;
- }
-
let kp = await crypto.subtle.generateKey("Ed25519", true,
["sign", "verify"]);
await crypto.subtle.deriveBits(
let ed25519_error_tsuite = {
name: "Ed25519 errors",
- skip: () => (!has_buffer() || !has_webcrypto()),
+ skip: async () => !await has_webcrypto_algorithm("Ed25519")
+ || !await has_webcrypto_algorithm("X25519"),
T: (params) => params.T(),
prepare_args: (args) => args,
---*/
async function test(params) {
- let wrapping_key;
-
- try {
- wrapping_key = await crypto.subtle.generateKey(
- params.wrap_alg, true, ["wrapKey", "unwrapKey"]);
- } catch (e) {
- if (e.message.indexOf("unknown algorithm") !== -1
- || e.message.indexOf("Unrecognized algorithm") !== -1)
- {
- return 'SKIPPED';
- }
-
- throw e;
- }
+ let wrapping_key = await crypto.subtle.generateKey(
+ params.wrap_alg, true, ["wrapKey", "unwrapKey"]);
let key = await crypto.subtle.generateKey(
params.key_alg, true, params.key_usage);
tests: [
/* AES-KW wrapping an AES-GCM key (raw format) */
{ wrap_alg: {name: "AES-KW", length: 256},
+ skip: async () => !await has_webcrypto_algorithm("AES-KW", 256),
key_alg: {name: "AES-GCM", length: 128},
key_usage: ["encrypt", "decrypt"],
format: "raw",
/* AES-KW wrapping an AES-CBC key */
{ wrap_alg: {name: "AES-KW", length: 128},
+ skip: async () => !await has_webcrypto_algorithm("AES-KW", 128),
key_alg: {name: "AES-CBC", length: 256},
key_usage: ["encrypt", "decrypt"],
format: "raw",
/* AES-KW wrapping an HMAC key */
{ wrap_alg: {name: "AES-KW", length: 256},
+ skip: async () => !await has_webcrypto_algorithm("AES-KW", 256),
key_alg: {name: "HMAC", hash: "SHA-256"},
key_usage: ["sign", "verify"],
format: "raw",
let wrap_error_tsuite = {
name: "wrapKey/unwrapKey errors",
- skip: () => (!has_buffer() || !has_webcrypto()),
+ skip: async () => !await has_webcrypto_algorithm("AES-KW", 128)
+ || !await has_webcrypto_algorithm("AES-KW", 256),
T: (params) => params.T(),
prepare_args: (args) => args,
async function test(params) {
- try {
- await crypto.subtle.generateKey("X25519", true, ["deriveBits"]);
- } catch (e) {
- if (e.message.indexOf("X25519") !== -1) {
- return 'SKIPPED';
- }
-
- throw e;
- }
-
/* generateKey + deriveBits roundtrip */
if (params.derive_bits) {
let alice = await crypto.subtle.generateKey("X25519", true,
+ `${params.expected}`);
}
+ let jwk = await crypto.subtle.exportKey("jwk", alice.privateKey);
+ let raw = await crypto.subtle.exportKey("raw", bob.publicKey);
+ let privateKey = await crypto.subtle.importKey("jwk", jwk,
+ "X25519", false, ["deriveBits"]);
+ let publicKey = await crypto.subtle.importKey("raw", raw,
+ "X25519", false, []);
+ let secret = await crypto.subtle.deriveBits(
+ {name: "X25519", public: publicKey}, privateKey, 256);
+
+ if (Buffer.from(secret).toString("base64url") !== params.expected) {
+ throw Error("X25519 JWK/raw key agreement failed");
+ }
+
return 'SUCCESS';
}
let x25519_tsuite = {
name: "X25519 key agreement",
- skip: () => (!has_buffer() || !has_webcrypto()),
+ skip: async () => !await has_webcrypto_algorithm("X25519"),
T: test,
prepare_args: (args) => args,
]};
async function test_derive_with_ed25519_key() {
- try {
- await crypto.subtle.generateKey("Ed25519", true, ["sign"]);
- } catch (e) {
- if (e.message.indexOf("Ed25519") !== -1) {
- return 'SKIPPED';
- }
-
- throw e;
- }
-
let x_kp = await crypto.subtle.generateKey("X25519", true,
["deriveBits"]);
let ed_kp = await crypto.subtle.generateKey("Ed25519", true,
}
async function test_sign_with_x25519_key() {
- try {
- await crypto.subtle.generateKey("X25519", true, ["deriveBits"]);
- } catch (e) {
- if (e.message.indexOf("X25519") !== -1) {
- return 'SKIPPED';
- }
-
- throw e;
- }
-
let kp = await crypto.subtle.generateKey("X25519", true,
["deriveBits"]);
let data = new TextEncoder().encode("test");
let x25519_error_tsuite = {
name: "X25519 errors",
- skip: () => (!has_buffer() || !has_webcrypto()),
+ skip: async () => !await has_webcrypto_algorithm("X25519")
+ || !await has_webcrypto_algorithm("Ed25519"),
T: (params) => params.T(),
prepare_args: (args) => args,