From d09d98c3630d81213bf2569e85951983496de10f Mon Sep 17 00:00:00 2001 From: Oleg Bespalov Date: Fri, 15 Mar 2024 13:43:51 +0100 Subject: [PATCH 1/3] Document JWK keys export/import with examples --- .../webcrypto/subtlecrypto/exportkey.md | 4 +- .../webcrypto/subtlecrypto/importkey.md | 155 +++++++++++++++++- 2 files changed, 154 insertions(+), 5 deletions(-) diff --git a/docs/sources/next/javascript-api/k6-experimental/webcrypto/subtlecrypto/exportkey.md b/docs/sources/next/javascript-api/k6-experimental/webcrypto/subtlecrypto/exportkey.md index 9385de76cc..b14ba851b4 100644 --- a/docs/sources/next/javascript-api/k6-experimental/webcrypto/subtlecrypto/exportkey.md +++ b/docs/sources/next/javascript-api/k6-experimental/webcrypto/subtlecrypto/exportkey.md @@ -20,7 +20,7 @@ exportKey(format, key) | Name | Type | Description | | :------- | :------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------- | -| `format` | `string` | Defines the data format the key should be exported in. Currently supported formats: `raw`. | +| `format` | `string` | Defines the data format the key should be exported in. Currently supported formats: `raw`, `jwk`. | | `key` | [CryptoKey](https://grafana.com/docs/k6//javascript-api/k6-experimental/webcrypto/cryptokey) | The [key](https://grafana.com/docs/k6//javascript-api/k6-experimental/webcrypto/cryptokey) to export. | ## Return Value @@ -61,7 +61,7 @@ export default async function () { const exportedKey = await crypto.subtle.exportKey('raw', generatedKey); /** - * Reimport the key in raw format to verfiy its integrity. + * Reimport the key in raw format to verify its integrity. */ const importedKey = await crypto.subtle.importKey('raw', exportedKey, 'AES-CBC', true, [ 'encrypt', diff --git a/docs/sources/next/javascript-api/k6-experimental/webcrypto/subtlecrypto/importkey.md b/docs/sources/next/javascript-api/k6-experimental/webcrypto/subtlecrypto/importkey.md index 501d232013..516287cd88 100644 --- a/docs/sources/next/javascript-api/k6-experimental/webcrypto/subtlecrypto/importkey.md +++ b/docs/sources/next/javascript-api/k6-experimental/webcrypto/subtlecrypto/importkey.md @@ -18,7 +18,7 @@ importKey(format, keyData, algorithm, extractable, keyUsages) | Name | Type | Description | | :------------ | :-------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `format` | `string` | Defines the data format of the key to import. Currently supported formats: `raw`. | +| `format` | `string` | Defines the data format of the key to import. Currently supported formats: `raw`, `jwk`. | | `keyData` | `ArrayBuffer`, `TypedArray` or `DataView` | the data to import the key from. | | `algorithm` | a `string` or object with a single `name` string property | The algorithm to use to import the key. Currently supported algorithms: `AES-CBC`, `AES-GCM`, `AES-CTR`, and `HMAC`. | | `extractable` | `boolean` | Indicates whether it will be possible to export the key using [exportKey](https://grafana.com/docs/k6//javascript-api/k6-experimental/webcrypto/subtlecrypto/exportkey). | @@ -35,7 +35,9 @@ A `Promise` that resolves with the imported key as a [CryptoKey](https://grafana | `SyntaxError` | Raised when the `keyUsages` parameter is empty but the key is of type `secret` or `private`. | | `TypeError` | Raised when trying to use an invalid format, or if the `keyData` is not suited for that format. | -## Example +## Examples + +### Round-trip key export/import {{< code >}} @@ -61,7 +63,7 @@ export default async function () { const exportedKey = await crypto.subtle.exportKey('raw', generatedKey); /** - * Reimport the key in raw format to verfiy its integrity. + * Reimport the key in raw format to verify its integrity. */ const importedKey = await crypto.subtle.importKey('raw', exportedKey, 'AES-CBC', true, [ 'encrypt', @@ -73,3 +75,150 @@ export default async function () { ``` {{< /code >}} + +### Import a static raw key and decrypt transmitted data + +This example demonstrates how to import a static `raw` key and decrypt some transmitted data (which contains initialization vector and encoded data) in `base64`. + +{{< code >}} + +```javascript +import { crypto } from 'k6/experimental/webcrypto'; +import { b64decode } from 'k6/encoding'; + +export default async function () { + // transmitted data is the base64 of the initialization vector + encrypted data + // that unusually transmitted over the network + const transmittedData = base64Decode( + 'whzEN310mrlWIH/icf0dMquRZ2ENyfOzkvPuu92WR/9F8dbeFM8EGUVNIhaS' + ); + + // keyData is the key used to decrypt the data, that is usually stored in a secure location + // for the purpose of this example, we are using a static key + const keyData = new Uint8Array([ + 109, 151, 76, 33, 232, 253, 176, 90, 94, 40, 146, 227, 139, 208, 245, 139, 69, 215, 55, 197, 43, + 122, 160, 178, 228, 104, 4, 115, 138, 159, 119, 49, + ]); + + try { + const result = await decrypt(keyData, transmittedData); + + // should output decrypted message + // INFO[0000] result: 'my secret message' source=console + console.log("result: '" + result + "'"); + } catch (e) { + console.log('Error: ' + JSON.stringify(e)); + } +} + +const decrypt = async (keyData, transmittedData) => { + const initializeVectorLength = 12; + + // the first 12 bytes are the initialization vector + const iv = new Uint8Array(transmittedData.subarray(0, initializeVectorLength)); + + // the rest of the transmitted data is the encrypted data + const encryptedData = new Uint8Array(transmittedData.subarray(initializeVectorLength)); + + const importedKey = await crypto.subtle.importKey( + 'raw', + keyData, + { name: 'AES-GCM', length: '256' }, + true, + ['decrypt'] + ); + + const plain = await crypto.subtle.decrypt( + { name: 'AES-GCM', iv: iv }, + importedKey, + encryptedData + ); + + return arrayBufferToString(plain); +}; + +const arrayBufferToString = (buffer) => { + return String.fromCharCode.apply(null, new Uint8Array(buffer)); +}; + +const base64Decode = (base64String) => { + return new Uint8Array(b64decode(base64String)); +}; +``` + +{{< /code >}} + +### Import a static JWK key and decrypt transmitted data + +This example is almost the same as the previous one. Still, it demonstrates how to import a static `jwk` key and decrypt some transmitted data (which contains initialization vector and encoded data) in `base64`. + +{{< code >}} + +```javascript +import { crypto } from 'k6/experimental/webcrypto'; +import { b64decode } from 'k6/encoding'; + +export default async function () { + // transmitted data is the base64 of the initialization vector + encrypted data + // that unusually transmitted over the network + const transmittedData = base64Decode( + 'drCfxl4O+5FcrHe8Bs0CvKlw3gZpv+S5if3zn7c4BJzHJ35QDFV4sJB0pbDT' + ); + + // keyData is the key used to decrypt the data, that is usually stored in a secure location + // for the purpose of this example, we are using a static key + const jwkKeyData = { + kty: 'oct', + ext: true, + key_ops: ['decrypt', 'encrypt'], + alg: 'A256GCM', + k: '9Id_8iG6FkGOWmc1S203vGVnTExtpDGxdQN7v7OV9Uc', + }; + + try { + const result = await decrypt(jwkKeyData, transmittedData); + + // should output decrypted message + // INFO[0000] result: 'my secret message' source=console + console.log("result: '" + result + "'"); + } catch (e) { + console.log('Error: ' + JSON.stringify(e)); + } +} + +const decrypt = async (keyData, transmittedData) => { + const initializeVectorLength = 12; + + // the first 12 bytes are the initialization vector + const iv = new Uint8Array(transmittedData.subarray(0, initializeVectorLength)); + + // the rest of the transmitted data is the encrypted data + const encryptedData = new Uint8Array(transmittedData.subarray(initializeVectorLength)); + + const importedKey = await crypto.subtle.importKey( + 'jwk', + keyData, + { name: 'AES-GCM', length: 256 }, + true, + ['encrypt', 'decrypt'] + ); + + const plain = await crypto.subtle.decrypt( + { name: 'AES-GCM', iv: iv }, + importedKey, + encryptedData + ); + + return arrayBufferToString(plain); +}; + +const arrayBufferToString = (buffer) => { + return String.fromCharCode.apply(null, new Uint8Array(buffer)); +}; + +const base64Decode = (base64String) => { + return new Uint8Array(b64decode(base64String)); +}; +``` + +{{< /code >}} From c5cef5e9bb471974b78698ee99dc15af76a59ba6 Mon Sep 17 00:00:00 2001 From: Oleg Bespalov Date: Fri, 15 Mar 2024 16:32:51 +0100 Subject: [PATCH 2/3] Apply suggestions from code review Co-authored-by: Heitor Tashiro Sergent --- .../webcrypto/subtlecrypto/importkey.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/docs/sources/next/javascript-api/k6-experimental/webcrypto/subtlecrypto/importkey.md b/docs/sources/next/javascript-api/k6-experimental/webcrypto/subtlecrypto/importkey.md index 516287cd88..f43e750284 100644 --- a/docs/sources/next/javascript-api/k6-experimental/webcrypto/subtlecrypto/importkey.md +++ b/docs/sources/next/javascript-api/k6-experimental/webcrypto/subtlecrypto/importkey.md @@ -78,7 +78,7 @@ export default async function () { ### Import a static raw key and decrypt transmitted data -This example demonstrates how to import a static `raw` key and decrypt some transmitted data (which contains initialization vector and encoded data) in `base64`. +This example demonstrates how to import a static `raw` key and decrypt some transmitted data (concatenation of initialization vector and encoded data) in `base64`. {{< code >}} @@ -87,14 +87,12 @@ import { crypto } from 'k6/experimental/webcrypto'; import { b64decode } from 'k6/encoding'; export default async function () { - // transmitted data is the base64 of the initialization vector + encrypted data - // that unusually transmitted over the network const transmittedData = base64Decode( 'whzEN310mrlWIH/icf0dMquRZ2ENyfOzkvPuu92WR/9F8dbeFM8EGUVNIhaS' ); - // keyData is the key used to decrypt the data, that is usually stored in a secure location - // for the purpose of this example, we are using a static key + // keyData is the key used to decrypt the data, which is usually stored in a secure location + // for this example, we are using a static key const keyData = new Uint8Array([ 109, 151, 76, 33, 232, 253, 176, 90, 94, 40, 146, 227, 139, 208, 245, 139, 69, 215, 55, 197, 43, 122, 160, 178, 228, 104, 4, 115, 138, 159, 119, 49, @@ -150,7 +148,7 @@ const base64Decode = (base64String) => { ### Import a static JWK key and decrypt transmitted data -This example is almost the same as the previous one. Still, it demonstrates how to import a static `jwk` key and decrypt some transmitted data (which contains initialization vector and encoded data) in `base64`. +This example is similar to the previous one. It demonstrates how to import a static `jwk` key and decrypt some transmitted data (which contains the initialization vector and encoded data) in `base64`. {{< code >}} @@ -165,8 +163,8 @@ export default async function () { 'drCfxl4O+5FcrHe8Bs0CvKlw3gZpv+S5if3zn7c4BJzHJ35QDFV4sJB0pbDT' ); - // keyData is the key used to decrypt the data, that is usually stored in a secure location - // for the purpose of this example, we are using a static key + // keyData is the key used to decrypt the data, which is usually stored in a secure location + // for this example, we are using a static key const jwkKeyData = { kty: 'oct', ext: true, From 9e8a3cbf046908816474b5b5837b36bf643b2b2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Crevon?= Date: Wed, 20 Mar 2024 11:42:58 +0100 Subject: [PATCH 3/3] Update docs/sources/next/javascript-api/k6-experimental/webcrypto/subtlecrypto/importkey.md Co-authored-by: Heitor Tashiro Sergent --- .../k6-experimental/webcrypto/subtlecrypto/importkey.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/next/javascript-api/k6-experimental/webcrypto/subtlecrypto/importkey.md b/docs/sources/next/javascript-api/k6-experimental/webcrypto/subtlecrypto/importkey.md index f43e750284..83d72666c7 100644 --- a/docs/sources/next/javascript-api/k6-experimental/webcrypto/subtlecrypto/importkey.md +++ b/docs/sources/next/javascript-api/k6-experimental/webcrypto/subtlecrypto/importkey.md @@ -78,7 +78,7 @@ export default async function () { ### Import a static raw key and decrypt transmitted data -This example demonstrates how to import a static `raw` key and decrypt some transmitted data (concatenation of initialization vector and encoded data) in `base64`. +This example demonstrates how to import a static `raw` key and decrypt some transmitted data in `base64`. The transmitted data in this example represents an initialization vector and encoded data, and in a real-world scenario, it can be a response body or other data received from a request. {{< code >}}