Skip to content

Commit 56e67bc

Browse files
feat: add async versions of the encrypt and decrypt functions (#83)
1 parent 7cb80eb commit 56e67bc

File tree

19 files changed

+2621
-1224
lines changed

19 files changed

+2621
-1224
lines changed

.changeset/dry-walls-care.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@sigmacomputing/node-embed-sdk": minor
3+
---
4+
5+
Add async versions of encryption and decryption function

packages/node-embed-sdk/README.md

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,26 @@ Sigma expects you to encrypt OAuth tokens using the same embed secret used to si
3636

3737
#### Example Usage
3838

39+
##### Asynchronous
40+
41+
```typescript
42+
import { encrypt, decrypt } from "@sigmacomputing/node-embed-sdk/promises";
43+
44+
// Encrypt an OAuth token
45+
const encryptedToken = await encrypt("your-embed-secret", "your-oauth-token");
46+
47+
// Decrypt an encrypted token
48+
const decryptedToken = await decrypt("your-embed-secret", encryptedToken);
49+
```
50+
51+
##### Synchronous
52+
3953
```typescript
40-
import { encrypt, decrypt } from '@sigmacomputing/node-embed-sdk';
54+
import { encrypt, decrypt } from "@sigmacomputing/node-embed-sdk";
4155

4256
// Encrypt an OAuth token
43-
const encryptedToken = encrypt(
44-
'your-embed-secret',
45-
'your-oauth-token'
46-
);
57+
const encryptedToken = encrypt("your-embed-secret", "your-oauth-token");
4758

4859
// Decrypt an encrypted token
49-
const decryptedToken = decrypt(
50-
'your-embed-secret',
51-
encryptedToken
52-
);
60+
const decryptedToken = decrypt("your-embed-secret", encryptedToken);
5361
```

packages/node-embed-sdk/package.json

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,15 @@
3131
"module": "./dist/index.mjs",
3232
"types": "./dist/index.d.ts",
3333
"exports": {
34-
"import": {
34+
".": {
35+
"types": "./dist/index.d.ts",
3536
"import": "./dist/index.mjs",
36-
"types": "./dist/index.d.mts"
37+
"require": "./dist/index.js"
3738
},
38-
"require": {
39-
"require": "./dist/index.js",
40-
"types": "./dist/index.d.ts"
39+
"./promises": {
40+
"types": "./dist/promises.d.ts",
41+
"import": "./dist/promises.mjs",
42+
"require": "./dist/promises.js"
4143
}
4244
},
4345
"files": [
@@ -48,9 +50,9 @@
4850
"@sigmacomputing/typescript-config": "workspace:*",
4951
"@swc/core": "^1.11.29",
5052
"@swc/jest": "^0.2.38",
51-
"@types/jest": "^29.5.14",
53+
"@types/jest": "^30.0.0",
5254
"@types/node": "^20.17.16",
53-
"jest": "^29.7.0"
55+
"jest": "^30.2.0"
5456
},
5557
"engines": {
5658
"node": ">=18"
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { decrypt, encrypt } from "../promises";
2+
3+
const EMBED_SECRET = "my fake embed secret";
4+
5+
describe("oauth token encryption", () => {
6+
it("can encrypt and decrypt using a passphrase", async () => {
7+
const plaintext = "hello, world!";
8+
9+
// Should be able to encrypt and then immediately decrypt.
10+
const encryptedToken = await encrypt(EMBED_SECRET, plaintext);
11+
const decryptedToken = await decrypt(EMBED_SECRET, encryptedToken);
12+
expect(decryptedToken).toBe(plaintext);
13+
});
14+
});
Lines changed: 1 addition & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { decrypt, encrypt } from "../index";
2-
import { _testExports } from "../encryption";
1+
import { decrypt, encrypt } from "../encryption";
32

43
const EMBED_SECRET = "my fake embed secret";
54

@@ -12,55 +11,4 @@ describe("oauth token encryption", () => {
1211
const decryptedToken = decrypt(EMBED_SECRET, encryptedToken);
1312
expect(decryptedToken).toBe(plaintext);
1413
});
15-
16-
function toEncodedString(
17-
salt: string,
18-
iv: string,
19-
tag: string,
20-
ciphertext: string,
21-
): string {
22-
return `${salt}.${iv}.${tag}.${ciphertext}`;
23-
}
24-
25-
it("only throws error when reading an incorrectly encoded string", () => {
26-
// Throws for invalid format.
27-
expect(() => {
28-
_testExports.asEncodedPassphraseEncryptionOutput("hello, world!");
29-
}).toThrow();
30-
31-
// Throws for valid format, but with non-base64 components.
32-
expect(() => {
33-
_testExports.asEncodedPassphraseEncryptionOutput(
34-
toEncodedString("(salt)", "(iv)", "(tag)", "(ciphertext)"),
35-
);
36-
}).toThrow();
37-
38-
// Throws for valid format with base64 components of invalid length.
39-
expect(() => {
40-
_testExports.asEncodedPassphraseEncryptionOutput(
41-
toEncodedString("YQ==", "Yg==", "Yw==", "ZA=="),
42-
);
43-
}).toThrow();
44-
45-
// Does not throw for valid format with base64 components of valid length.
46-
const salt = Buffer.from(
47-
"s".repeat(
48-
_testExports.PBKDF2_HMAC_SHA256_KEY_DERIVATION.SALT_LENGTH_BYTES,
49-
),
50-
).toString("base64");
51-
const iv = Buffer.from(
52-
"i".repeat(_testExports.AES_256_GCM_ENCRYPTION.IV_LENGTH_BYTES),
53-
).toString("base64");
54-
const tag = Buffer.from(
55-
"t".repeat(_testExports.AES_256_GCM_ENCRYPTION.TAG_LENGTH_BYTES),
56-
).toString("base64");
57-
const ciphertext = Buffer.from(
58-
"c".repeat(10 /* arbitrary length */),
59-
).toString("base64");
60-
expect(() => {
61-
_testExports.asEncodedPassphraseEncryptionOutput(
62-
toEncodedString(salt, iv, tag, ciphertext),
63-
);
64-
}).not.toThrow();
65-
});
6614
});
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import {
2+
PBKDF2_HMAC_SHA256_KEY_DERIVATION,
3+
AES_256_GCM_ENCRYPTION,
4+
asEncodedPassphraseEncryptionOutput,
5+
} from "../encryption_utils";
6+
7+
describe("oauth token encryption", () => {
8+
function toEncodedString(
9+
salt: string,
10+
iv: string,
11+
tag: string,
12+
ciphertext: string,
13+
): string {
14+
return `${salt}.${iv}.${tag}.${ciphertext}`;
15+
}
16+
17+
it("only throws error when reading an incorrectly encoded string", () => {
18+
// Throws for invalid format.
19+
expect(() => {
20+
asEncodedPassphraseEncryptionOutput("hello, world!");
21+
}).toThrow();
22+
23+
// Throws for valid format, but with non-base64 components.
24+
expect(() => {
25+
asEncodedPassphraseEncryptionOutput(
26+
toEncodedString("(salt)", "(iv)", "(tag)", "(ciphertext)"),
27+
);
28+
}).toThrow();
29+
30+
// Throws for valid format with base64 components of invalid length.
31+
expect(() => {
32+
asEncodedPassphraseEncryptionOutput(
33+
toEncodedString("YQ==", "Yg==", "Yw==", "ZA=="),
34+
);
35+
}).toThrow();
36+
37+
// Does not throw for valid format with base64 components of valid length.
38+
const salt = Buffer.from(
39+
"s".repeat(PBKDF2_HMAC_SHA256_KEY_DERIVATION.SALT_LENGTH_BYTES),
40+
).toString("base64");
41+
const iv = Buffer.from(
42+
"i".repeat(AES_256_GCM_ENCRYPTION.IV_LENGTH_BYTES),
43+
).toString("base64");
44+
const tag = Buffer.from(
45+
"t".repeat(AES_256_GCM_ENCRYPTION.TAG_LENGTH_BYTES),
46+
).toString("base64");
47+
const ciphertext = Buffer.from(
48+
"c".repeat(10 /* arbitrary length */),
49+
).toString("base64");
50+
expect(() => {
51+
asEncodedPassphraseEncryptionOutput(
52+
toEncodedString(salt, iv, tag, ciphertext),
53+
);
54+
}).not.toThrow();
55+
});
56+
});

0 commit comments

Comments
 (0)