Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/dry-walls-care.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@sigmacomputing/node-embed-sdk": minor
---

Add async versions of encryption and decryption function
26 changes: 17 additions & 9 deletions packages/node-embed-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,26 @@ Sigma expects you to encrypt OAuth tokens using the same embed secret used to si

#### Example Usage

##### Asynchronous

```typescript
import { encrypt, decrypt } from "@sigmacomputing/node-embed-sdk/promises";

// Encrypt an OAuth token
const encryptedToken = await encrypt("your-embed-secret", "your-oauth-token");

// Decrypt an encrypted token
const decryptedToken = await decrypt("your-embed-secret", encryptedToken);
```

##### Synchronous

```typescript
import { encrypt, decrypt } from '@sigmacomputing/node-embed-sdk';
import { encrypt, decrypt } from "@sigmacomputing/node-embed-sdk";

// Encrypt an OAuth token
const encryptedToken = encrypt(
'your-embed-secret',
'your-oauth-token'
);
const encryptedToken = encrypt("your-embed-secret", "your-oauth-token");

// Decrypt an encrypted token
const decryptedToken = decrypt(
'your-embed-secret',
encryptedToken
);
const decryptedToken = decrypt("your-embed-secret", encryptedToken);
```
16 changes: 9 additions & 7 deletions packages/node-embed-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,15 @@
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
"import": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"types": "./dist/index.d.mts"
"require": "./dist/index.js"
},
"require": {
"require": "./dist/index.js",
"types": "./dist/index.d.ts"
"./promises": {
"types": "./dist/promises.d.ts",
"import": "./dist/promises.mjs",
"require": "./dist/promises.js"
}
},
"files": [
Expand All @@ -48,9 +50,9 @@
"@sigmacomputing/typescript-config": "workspace:*",
"@swc/core": "^1.11.29",
"@swc/jest": "^0.2.38",
"@types/jest": "^29.5.14",
"@types/jest": "^30.0.0",
"@types/node": "^20.17.16",
"jest": "^29.7.0"
"jest": "^30.2.0"
},
"engines": {
"node": ">=18"
Expand Down
14 changes: 14 additions & 0 deletions packages/node-embed-sdk/src/__tests__/async_encryption.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { decrypt, encrypt } from "../promises";

const EMBED_SECRET = "my fake embed secret";

describe("oauth token encryption", () => {
it("can encrypt and decrypt using a passphrase", async () => {
const plaintext = "hello, world!";

// Should be able to encrypt and then immediately decrypt.
const encryptedToken = await encrypt(EMBED_SECRET, plaintext);
const decryptedToken = await decrypt(EMBED_SECRET, encryptedToken);
expect(decryptedToken).toBe(plaintext);
});
});
54 changes: 1 addition & 53 deletions packages/node-embed-sdk/src/__tests__/encryption.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { decrypt, encrypt } from "../index";
import { _testExports } from "../encryption";
import { decrypt, encrypt } from "../encryption";

const EMBED_SECRET = "my fake embed secret";

Expand All @@ -12,55 +11,4 @@ describe("oauth token encryption", () => {
const decryptedToken = decrypt(EMBED_SECRET, encryptedToken);
expect(decryptedToken).toBe(plaintext);
});

function toEncodedString(
salt: string,
iv: string,
tag: string,
ciphertext: string,
): string {
return `${salt}.${iv}.${tag}.${ciphertext}`;
}

it("only throws error when reading an incorrectly encoded string", () => {
// Throws for invalid format.
expect(() => {
_testExports.asEncodedPassphraseEncryptionOutput("hello, world!");
}).toThrow();

// Throws for valid format, but with non-base64 components.
expect(() => {
_testExports.asEncodedPassphraseEncryptionOutput(
toEncodedString("(salt)", "(iv)", "(tag)", "(ciphertext)"),
);
}).toThrow();

// Throws for valid format with base64 components of invalid length.
expect(() => {
_testExports.asEncodedPassphraseEncryptionOutput(
toEncodedString("YQ==", "Yg==", "Yw==", "ZA=="),
);
}).toThrow();

// Does not throw for valid format with base64 components of valid length.
const salt = Buffer.from(
"s".repeat(
_testExports.PBKDF2_HMAC_SHA256_KEY_DERIVATION.SALT_LENGTH_BYTES,
),
).toString("base64");
const iv = Buffer.from(
"i".repeat(_testExports.AES_256_GCM_ENCRYPTION.IV_LENGTH_BYTES),
).toString("base64");
const tag = Buffer.from(
"t".repeat(_testExports.AES_256_GCM_ENCRYPTION.TAG_LENGTH_BYTES),
).toString("base64");
const ciphertext = Buffer.from(
"c".repeat(10 /* arbitrary length */),
).toString("base64");
expect(() => {
_testExports.asEncodedPassphraseEncryptionOutput(
toEncodedString(salt, iv, tag, ciphertext),
);
}).not.toThrow();
});
});
56 changes: 56 additions & 0 deletions packages/node-embed-sdk/src/__tests__/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import {
PBKDF2_HMAC_SHA256_KEY_DERIVATION,
AES_256_GCM_ENCRYPTION,
asEncodedPassphraseEncryptionOutput,
} from "../encryption_utils";

describe("oauth token encryption", () => {
function toEncodedString(
salt: string,
iv: string,
tag: string,
ciphertext: string,
): string {
return `${salt}.${iv}.${tag}.${ciphertext}`;
}

it("only throws error when reading an incorrectly encoded string", () => {
// Throws for invalid format.
expect(() => {
asEncodedPassphraseEncryptionOutput("hello, world!");
}).toThrow();

// Throws for valid format, but with non-base64 components.
expect(() => {
asEncodedPassphraseEncryptionOutput(
toEncodedString("(salt)", "(iv)", "(tag)", "(ciphertext)"),
);
}).toThrow();

// Throws for valid format with base64 components of invalid length.
expect(() => {
asEncodedPassphraseEncryptionOutput(
toEncodedString("YQ==", "Yg==", "Yw==", "ZA=="),
);
}).toThrow();

// Does not throw for valid format with base64 components of valid length.
const salt = Buffer.from(
"s".repeat(PBKDF2_HMAC_SHA256_KEY_DERIVATION.SALT_LENGTH_BYTES),
).toString("base64");
const iv = Buffer.from(
"i".repeat(AES_256_GCM_ENCRYPTION.IV_LENGTH_BYTES),
).toString("base64");
const tag = Buffer.from(
"t".repeat(AES_256_GCM_ENCRYPTION.TAG_LENGTH_BYTES),
).toString("base64");
const ciphertext = Buffer.from(
"c".repeat(10 /* arbitrary length */),
).toString("base64");
expect(() => {
asEncodedPassphraseEncryptionOutput(
toEncodedString(salt, iv, tag, ciphertext),
);
}).not.toThrow();
});
});
Loading