-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
145 additions
and
93 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
Test files should always end on `.spec.ts`. | ||
|
||
Indentation is 2 chars. | ||
|
||
Prefer private variable to start with `_`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { beforeAll, describe, expect, it } from 'vitest' | ||
import { hxDecrypt, hxEncrypt } from './aes-sealed' | ||
|
||
describe('aes Encryption and Decryption', () => { | ||
let key: CryptoKey | ||
|
||
beforeAll(async () => { | ||
key = await crypto.subtle.generateKey( | ||
{ | ||
name: 'AES-GCM', | ||
length: 256, | ||
}, | ||
true, | ||
['encrypt', 'decrypt'], | ||
) | ||
}) | ||
|
||
it('should encrypt and decrypt data correctly', async () => { | ||
const data = new TextEncoder().encode('Hello, World!') | ||
const encryptedData = await hxEncrypt(data, key) | ||
const decryptedData = await hxDecrypt(encryptedData, key) | ||
expect(new TextDecoder().decode(decryptedData)).toBe('Hello, World!') | ||
}) | ||
|
||
it('should produce different ciphertexts for the same plaintext', async () => { | ||
const data = new TextEncoder().encode('Hello, World!') | ||
const encryptedData1 = await hxEncrypt(data, key) | ||
const encryptedData2 = await hxEncrypt(data, key) | ||
expect(encryptedData1).not.toEqual(encryptedData2) | ||
}) | ||
|
||
it('should fail to decrypt with a different key', async () => { | ||
const data = new TextEncoder().encode('Hello, World!') | ||
const encryptedData = await hxEncrypt(data, key) | ||
const differentKey = await crypto.subtle.generateKey( | ||
{ | ||
name: 'AES-GCM', | ||
length: 256, | ||
}, | ||
true, | ||
['encrypt', 'decrypt'], | ||
) | ||
await expect(hxDecrypt(encryptedData, differentKey)).rejects.toThrow() | ||
}) | ||
|
||
// it('should decrypt a sample that was generated by Swift code', async () => { | ||
// const key = await deriveKeyPbkdf2CBC(new Uint8Array([1, 2, 3]), { | ||
// salt: new Uint8Array([1, 2, 3]), | ||
// }) | ||
// // expect(toBase64(key)).toMatchInlineSnapshot() | ||
// const sample = new Uint8Array([9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) | ||
// const encryptedData = fromBase64('br6sc+pnZaIXcV1fTygAs/UJlDZIIBY50i56MMGNampZTcSakt0=') | ||
// const decryptedData = await hxDecrypt(encryptedData, key) | ||
// expect(decryptedData).toMatchInlineSnapshot() | ||
// }) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
export async function hxEncrypt(data: Uint8Array, key: CryptoKey, tag?: Uint8Array): Promise<Uint8Array> { | ||
const iv = crypto.getRandomValues(new Uint8Array(12)) // AES-GCM requires a 12-byte IV | ||
if (!tag) { | ||
tag = crypto.getRandomValues(new Uint8Array(16)) | ||
} | ||
|
||
const encrypted = await crypto.subtle.encrypt( | ||
{ | ||
name: 'AES-GCM', | ||
iv, | ||
tagLength: 128, | ||
additionalData: tag, | ||
}, | ||
key, | ||
data, | ||
) | ||
|
||
const encryptedArray = new Uint8Array(encrypted) | ||
const combined = new Uint8Array(iv.length + encryptedArray.length + tag.length) | ||
combined.set(iv) | ||
combined.set(encryptedArray, iv.length) | ||
combined.set(tag, encryptedArray.length + iv.length) | ||
return combined | ||
} | ||
|
||
export async function hxDecrypt(data: Uint8Array, key: CryptoKey): Promise<Uint8Array> { | ||
// The data layout of the combined representation is nonce, ciphertext, then tag. | ||
// The nonce is 12 bytes, the tag is 16 bytes, and the ciphertext is the rest of the data. | ||
const iv = data.slice(0, 12) // nonce is the first 12 bytes | ||
const encrypted = data.slice(12, -16) // The ciphertext is everything between the nonce and the tag. | ||
const tag = data.slice(-16) // The authentication tag has a length of 16 bytes. | ||
// console.log({ iv, encrypted, tag }) | ||
|
||
const decrypted = await crypto.subtle.decrypt({ | ||
name: 'AES-GCM', | ||
iv, | ||
tagLength: 128, | ||
additionalData: tag, | ||
}, key, encrypted) | ||
return new Uint8Array(decrypted) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters