'use strict'; const { ArrayBufferIsView, ArrayBufferPrototypeSlice, ArrayFrom, SafeSet, TypedArrayPrototypeSlice, } = primordials; const { ChaCha20Poly1305CipherJob, KeyObjectHandle, kCryptoJobAsync, kWebCryptoCipherDecrypt, kWebCryptoCipherEncrypt, } = internalBinding('crypto'); const { hasAnyNotIn, jobPromise, validateKeyOps, kHandle, kKeyObject, } = require('internal/crypto/util'); const { lazyDOMException, promisify, } = require('internal/util'); const { InternalCryptoKey, SecretKeyObject, createSecretKey, } = require('internal/crypto/keys'); const { randomBytes: _randomBytes, } = require('internal/crypto/random'); const randomBytes = promisify(_randomBytes); function validateKeyLength(length) { if (length !== 256) throw lazyDOMException('Invalid key length', 'DataError'); } async function c20pCipher(mode, key, data, algorithm) { let tag; switch (mode) { case kWebCryptoCipherDecrypt: { const slice = ArrayBufferIsView(data) ? TypedArrayPrototypeSlice : ArrayBufferPrototypeSlice; if (data.byteLength < 16) { throw lazyDOMException( 'The provided data is too small.', 'OperationError'); } tag = slice(data, -16); data = slice(data, 0, -16); break; } case kWebCryptoCipherEncrypt: tag = 16; break; } return await jobPromise(() => new ChaCha20Poly1305CipherJob( kCryptoJobAsync, mode, key[kKeyObject][kHandle], data, algorithm.iv, tag, algorithm.additionalData)); } async function c20pGenerateKey(algorithm, extractable, keyUsages) { const { name } = algorithm; const checkUsages = ['encrypt', 'decrypt', 'wrapKey', 'unwrapKey']; const usagesSet = new SafeSet(keyUsages); if (hasAnyNotIn(usagesSet, checkUsages)) { throw lazyDOMException( `Unsupported key usage for a ${algorithm.name} key`, 'SyntaxError'); } let keyData; try { keyData = await randomBytes(32); } catch (err) { throw lazyDOMException( 'The operation failed for an operation-specific reason' + `[${err.message}]`, { name: 'OperationError', cause: err }); } return new InternalCryptoKey( createSecretKey(keyData), { name }, ArrayFrom(usagesSet), extractable); } function c20pImportKey( algorithm, format, keyData, extractable, keyUsages) { const { name } = algorithm; const checkUsages = ['encrypt', 'decrypt', 'wrapKey', 'unwrapKey']; const usagesSet = new SafeSet(keyUsages); if (hasAnyNotIn(usagesSet, checkUsages)) { throw lazyDOMException( `Unsupported key usage for a ${algorithm.name} key`, 'SyntaxError'); } let keyObject; switch (format) { case 'KeyObject': { keyObject = keyData; break; } case 'raw-secret': { keyObject = createSecretKey(keyData); break; } case 'jwk': { if (!keyData.kty) throw lazyDOMException('Invalid keyData', 'DataError'); if (keyData.kty !== 'oct') throw lazyDOMException('Invalid JWK "kty" Parameter', 'DataError'); if (usagesSet.size > 0 && keyData.use !== undefined && keyData.use !== 'enc') { throw lazyDOMException('Invalid JWK "use" Parameter', 'DataError'); } validateKeyOps(keyData.key_ops, usagesSet); if (keyData.ext !== undefined && keyData.ext === false && extractable === true) { throw lazyDOMException( 'JWK "ext" Parameter and extractable mismatch', 'DataError'); } const handle = new KeyObjectHandle(); try { handle.initJwk(keyData); } catch (err) { throw lazyDOMException( 'Invalid keyData', { name: 'DataError', cause: err }); } if (keyData.alg !== undefined && keyData.alg !== 'C20P') { throw lazyDOMException( 'JWK "alg" does not match the requested algorithm', 'DataError'); } keyObject = new SecretKeyObject(handle); break; } default: return undefined; } validateKeyLength(keyObject.symmetricKeySize * 8); return new InternalCryptoKey( keyObject, { name }, keyUsages, extractable); } module.exports = { c20pCipher, c20pGenerateKey, c20pImportKey, };