javascriptphpk6webcrypto-apiwebcrypto

How to implement k6 webcrypto encryption same way as PHP openssl_encrypt function


I got a code to encrypt some text in PHP, it looks like this:

$key = '1234aaaff80b56233525ac2355ac3456'; // something like this
$utf16Content = \mb_convert_encoding('Some text', 'UTF-16LE');
$cipher = 'aes-256-gcm';
$nonceLength = \openssl_cipher_iv_length($cipher);
$nonce = \openssl_random_pseudo_bytes($nonceLength);

$encryptedContent = \openssl_encrypt($utf16Content, $cipher, $key, $options=0, $nonce, $tag);

echo 'Nonce: '.\base64_encode($nonce);
echo 'Auth Tag: '.\base64_encode($tag); 
echo 'Content: '.$encryptedContent; 

How could I implement the same behavior in JS webcrypto? (I need that for the k6 tests) I found a documentation but it isn't clear for me.

For now I have code like this but I cannot decrypt that successfully so I guess something isn't right.

const rawKey = Uint8Array.from(new String('1234aaaff80b56233525ac2355ac3456'), (x) => x.charCodeAt(0));

const key = await crypto.subtle.importKey(
     'raw',
     rawKey,
     { name: 'AES-GCM', length: 256 },
     false,
     ['encrypt', 'decrypt']
);
const nonce = crypto.getRandomValues(new Uint8Array(12));

const encrypted = await crypto.subtle.encrypt({
       name: 'AES-GCM',
       iv: nonce,
       tagLength: 128,
    },
    key,
    stringToArrayBuffer('Some text')
);

const sentData = {};
sentData.content = b64encode(encrypted);
sentData.nonce = b64encode(nonce);
sentData.authTag = b64encode(GetTag(encrypted, 128));


function stringToArrayBuffer(str) {
    const buf = new ArrayBuffer(str.length * 2);
    const bufView = new Uint16Array(buf);
    for (let i = 0, strLen = str.length; i < strLen; i++) {
        bufView[i] = str.charCodeAt(i);
    }
    return buf;
}

function GetTag(encrypted, tagLength) {
    if (tagLength === void 0) tagLength = 128;
    return encrypted.slice(encrypted.byteLength - ((tagLength + 7) >> 3))
}

I think the main problem is creating correct authTag and changing String rawKey to ArrayBuffer.

BTW decription code looks like this (PHP):

// $content, $nonce, $authenticationTag - from PHP encription script output 
$content = \base64_decode($content, true);
$nonce = \base64_decode($nonce, true);
$authenticationTag = \base64_decode($authenticationTag, true);
        
$result = \openssl_decrypt(
    $content,
    'aes-256-gcm',
    '1234aaaff80b56233525ac2355ac3456',
    \OPENSSL_RAW_DATA,
    $nonce,
    $authenticationTag
);

Solution

  • For the record: I figured out how I can get authTag from encrypted text using k6/experimental/webcrypto

    import { crypto } from 'k6/experimental/webcrypto';
    
    const encrypted = await crypto.subtle.encrypt(
    {
            name: 'AES-GCM',
            iv: nonce,
            tagLength: 128,
        },
        key,
        plaintext
    );
    
    const [ value, authTag] = [
        encrypted.slice(0, encrypted.byteLength - 16),
        encrypted.slice(encrypted.byteLength - 16),
    ];