|
| 1 | +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. |
| 2 | +// SPDX-License-Identifier: Apache-2.0 |
| 3 | +import { |
| 4 | + KmsKeyringNode, |
| 5 | + buildClient, |
| 6 | + CommitmentPolicy |
| 7 | +} from '@aws-crypto/client-node' |
| 8 | +import {AlgorithmSuiteIdentifier} from '@aws-crypto/material-management' |
| 9 | + |
| 10 | +/* This builds client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy. |
| 11 | + * REQUIRE_ENCRYPT_REQUIRE_DECRYPT enforces that this client only encrypts using committing algorithm suites |
| 12 | + * and enforces that this client will only decrypt encrypted messages |
| 13 | + * that were created with a committing algorithm suite. |
| 14 | + * This is the default if you build `buildClient()`. |
| 15 | + */ |
| 16 | +const { encryptStream, decryptUnsignedMessageStream } = buildClient( |
| 17 | + CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT |
| 18 | +) |
| 19 | + |
| 20 | +import { finished } from 'stream' |
| 21 | +import { createReadStream, createWriteStream } from 'fs' |
| 22 | +import { promisify } from 'util' |
| 23 | +const finishedAsync = promisify(finished) |
| 24 | + |
| 25 | +/* A KMS CMK is required to generate the data key. |
| 26 | + * You need kms:GenerateDataKey permission on the CMK in generatorKeyId. |
| 27 | + * This key is public, DO NOT USE in production environment. |
| 28 | + */ |
| 29 | +const generatorKeyId = |
| 30 | + 'arn:aws:kms:us-west-2:658956600833:alias/EncryptDecrypt' |
| 31 | + |
| 32 | +// configure keyring with CMK(s) you wish to work with |
| 33 | +const keyring = new KmsKeyringNode({ generatorKeyId }) |
| 34 | + |
| 35 | +/** |
| 36 | + * Encryption Context is very useful if you want to assert things about the encrypted data |
| 37 | + * See: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context |
| 38 | + */ |
| 39 | +const context = { |
| 40 | + stage: 'demo', |
| 41 | + purpose: 'streaming memory stress test', |
| 42 | + origin: 'us-west-2', |
| 43 | +} |
| 44 | + |
| 45 | +/** |
| 46 | + * kmsEncryptStream will use the encryptStream function and create a pipeline to encrypt a stream of data |
| 47 | + * from a file and write it to destination `./{filename}.encrypted` |
| 48 | + * @param filename string of file name you wish to encrypt |
| 49 | + * @param framesize optional parameter to determine frame size; default is 4096 bytes |
| 50 | + */ |
| 51 | +export async function kmsEncryptStream(filename: string, framesize?: number) { |
| 52 | + const readable = createReadStream(filename) |
| 53 | + const encFile = filename + '.encrypted' |
| 54 | + const writeable = createWriteStream(encFile) |
| 55 | + |
| 56 | + // pipeline of read stream |
| 57 | + readable.pipe( |
| 58 | + encryptStream(keyring, { |
| 59 | + /** |
| 60 | + * Since we are streaming, and assuming that the encryption and decryption contexts |
| 61 | + * are equally trusted, using an unsigned algorithm suite is faster and avoids |
| 62 | + * the possibility of processing plaintext before the signature is verified. |
| 63 | + */ |
| 64 | + suiteId: |
| 65 | + AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA512_COMMIT_KEY, |
| 66 | + encryptionContext: context, |
| 67 | + frameLength: framesize |
| 68 | + }) |
| 69 | + ).pipe(writeable.on('finish', () => { |
| 70 | + // output from encryptStream will get piped to writeable |
| 71 | + console.log(`The new file name is ${encFile}.`); |
| 72 | + })) |
| 73 | + |
| 74 | + await finishedAsync(writeable) |
| 75 | + console.log("Finished Encrypting"); |
| 76 | + |
| 77 | +} |
| 78 | +/** |
| 79 | + * kmsDecryptStream will take a filename and create a decryption stream to decrypt contents of file |
| 80 | + * and write it to destination `./{filename}.decrypted |
| 81 | + * @param filename string of file name you wish to encrypt |
| 82 | + */ |
| 83 | +export async function kmsDecryptStream(filename: string) { |
| 84 | + const readable = createReadStream(filename) |
| 85 | + const decFile = filename + '.decrypted' |
| 86 | + const writeable = createWriteStream(decFile) |
| 87 | + |
| 88 | + readable.pipe( |
| 89 | + /** |
| 90 | + * decryptUnsignedMessageStream is recommended when streaming if you don't need |
| 91 | + * digital signatures. |
| 92 | + */ |
| 93 | + decryptUnsignedMessageStream(keyring) |
| 94 | + ).pipe(writeable.on('finish', () => { |
| 95 | + console.log(`The new file name is ${decFile}.`); |
| 96 | + })) |
| 97 | + |
| 98 | + await finishedAsync(writeable) |
| 99 | + console.log("Finished Decrypting") |
| 100 | +} |
| 101 | + |
0 commit comments