Skip to content
This repository was archived by the owner on Feb 10, 2024. It is now read-only.

Commit

Permalink
Add option to pre-compute encryption keys
Browse files Browse the repository at this point in the history
  • Loading branch information
corrideat committed May 28, 2023
1 parent c8502ae commit e907005
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 30 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@exact-realty/encrypted-s3-store",
"version": "1.0.0",
"version": "1.0.1",
"description": "Simple wrapper for aws4fetch to store encrypted files",
"main": "dist/index.js",
"module": "./dist/index.mjs",
Expand Down
101 changes: 74 additions & 27 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,42 +13,75 @@
* PERFORMANCE OF THIS SOFTWARE.
*/

import { AwsClient } from 'aws4fetch';
import type { AwsClient } from 'aws4fetch';
import { autobb, btoau } from './lib/base64url.js';

const encryptionAlgorithm = {
['name']: 'AES-GCM',
['length']: 256,
};
const keyWrappingAlgorithm = { ['name']: 'AES-KW' };

const encryptFile = async (
awsClient: AwsClient,
baseUri: string,
name: string,
data: string | BufferSource,
wrappingKey: CryptoKey,
userWrappedEncryptionKeyB64?: string,
requestInit?: RequestInit,
): Promise<[string, string]> => {
const iv = globalThis.crypto.getRandomValues(new Uint8Array(12));

const encryptionKey = await globalThis.crypto.subtle.generateKey(
{
['name']: 'AES-GCM',
['length']: 256,
},
true,
['encrypt'],
);

const wrappedEncryptionKey = await globalThis.crypto.subtle.wrapKey(
'raw',
encryptionKey,
wrappingKey,
{ ['name']: 'AES-KW' },
);
const [encryptionKey, wrappedEncryptionKeyB64] =
await (userWrappedEncryptionKeyB64
? async (): Promise<[CryptoKey, string]> => {
const wrappedEncryptionKey = autobb(
userWrappedEncryptionKeyB64,
);

const encryptionKey =
await globalThis.crypto.subtle.unwrapKey(
'raw',
wrappedEncryptionKey,
wrappingKey,
keyWrappingAlgorithm,
encryptionAlgorithm,
false,
['encrypt'],
);

return [encryptionKey, userWrappedEncryptionKeyB64];
}
: async (): Promise<[CryptoKey, string]> => {
const encryptionKey =
await globalThis.crypto.subtle.generateKey(
encryptionAlgorithm,
true,
['encrypt'],
);

const wrappedEncryptionKey =
await globalThis.crypto.subtle.wrapKey(
'raw',
encryptionKey,
wrappingKey,
keyWrappingAlgorithm,
);

return [
encryptionKey,
btoau(new Uint8Array(wrappedEncryptionKey)),
];
})();

const dataBuffer =
typeof data === 'string' || data instanceof String
? new Uint8Array(data.split('').map((c) => c.charCodeAt(0)))
: data;

const encryptedData = await globalThis.crypto.subtle.encrypt(
{ ['name']: 'AES-GCM', ['iv']: iv },
{ ['name']: encryptionAlgorithm['name'], ['iv']: iv },
encryptionKey,
dataBuffer,
);
Expand All @@ -66,18 +99,18 @@ const encryptFile = async (
throw new Error('Unexpected response code: ' + response.status);
}

return [btoau(new Uint8Array(wrappedEncryptionKey)), btoau(iv)];
return [wrappedEncryptionKeyB64, btoau(iv)];
};

const deleteFile = async (
const deleteFile = (
awsClient: AwsClient,
baseUri: string,
name: string,
requestInit?: RequestInit,
): Promise<Response> => {
return awsClient.fetch(`${baseUri}/${encodeURIComponent(name)}`, {
...requestInit,
['method']: 'GET',
['method']: 'DELETE',
});
};

Expand All @@ -97,11 +130,8 @@ const decryptFile = async (
'raw',
wrappedDecryptionKey,
unwrappingKey,
{ ['name']: 'AES-KW' },
{
['name']: 'AES-GCM',
['length']: 256,
},
keyWrappingAlgorithm,
encryptionAlgorithm,
false,
['decrypt'],
);
Expand All @@ -119,12 +149,29 @@ const decryptFile = async (
}

const decryptedData = await globalThis.crypto.subtle.decrypt(
{ ['name']: 'AES-GCM', ['iv']: iv },
{ ['name']: encryptionAlgorithm['name'], ['iv']: iv },
decryptionKey,
await response.arrayBuffer(),
);

return decryptedData;
};

export { decryptFile, deleteFile, encryptFile };
const generateWrappedKey = async (wrappingKey: CryptoKey): Promise<string> => {
const encryptionKey = await globalThis.crypto.subtle.generateKey(
encryptionAlgorithm,
true,
['encrypt'],
);

const wrappedEncryptionKey = await globalThis.crypto.subtle.wrapKey(
'raw',
encryptionKey,
wrappingKey,
keyWrappingAlgorithm,
);

return btoau(new Uint8Array(wrappedEncryptionKey));
};

export { decryptFile, deleteFile, encryptFile, generateWrappedKey };

0 comments on commit e907005

Please sign in to comment.