Skip to content

Commit

Permalink
feat: lets nanoTDF client take options instead (#278)
Browse files Browse the repository at this point in the history
- Changes nanotdf constructor parameter to be an options object instead of ordered parameters
- Lets web-app backend be configurable
- Some fixes for dpop/signing key generation flows
- Adds `tsc` build of web-app to makefile
- fixes existing typescript issues in web app, and some cleanup
  • Loading branch information
dmihalcik-virtru committed May 13, 2024
1 parent e0b7adc commit 737643e
Show file tree
Hide file tree
Showing 19 changed files with 442 additions and 307 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,5 @@ dist

# temporary folders
**/temp/
/web-app/tests/smallfiles
.DS_Store
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ remote-store/opentdf-remote-store-$(version).tgz: lib/opentdf-client-$(version).
(cd remote-store && npm ci ../lib/opentdf-client-$(version).tgz && npm pack)

web-app/opentdf-web-app-$(version).tgz: lib/opentdf-client-$(version).tgz $(shell find web-app -not -path '*/dist*' -and -not -path '*/coverage*' -and -not -path '*/node_modules*')
(cd web-app && npm ci ../lib/opentdf-client-$(version).tgz && npm pack)
(cd web-app && npm ci ../lib/opentdf-client-$(version).tgz && npm pack && npm run build)

lib/opentdf-client-$(version).tgz: $(shell find lib -not -path '*/dist*' -and -not -path '*/coverage*' -and -not -path '*/node_modules*')
(cd lib && npm ci --including=dev && npm pack)
Expand Down
2 changes: 1 addition & 1 deletion lib/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ TDF3 with JSON envelopes.
oidcOrigin: keycloakUrl,
}
const authProvider = await AuthProviders.refreshAuthProvider(oidcCredentials);
const client = new NanoTDFClient(authProvider, access);
const client = new NanoTDFClient({authProvider, kasEndpoint});
const cipherText = await client.encrypt(plainText);
const clearText = await client.decrypt(cipherText);
```
Expand Down
7 changes: 7 additions & 0 deletions lib/src/auth/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,13 @@ export type AuthProvider = {
withCreds(httpReq: HttpRequest): Promise<HttpRequest>;
};

export function isAuthProvider(a?: unknown): a is AuthProvider {
if (!a || typeof a != 'object') {
return false;
}
return 'withCreds' in a;
}

/**
* An AuthProvider encapsulates all logic necessary to authenticate to a backend service, in the
* vein of <a href="https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Credentials.html">AWS.Credentials</a>.
Expand Down
48 changes: 26 additions & 22 deletions lib/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
} from './nanotdf/index.js';
import { keyAgreement, extractPublicFromCertToCrypto } from './nanotdf-crypto/index.js';
import { TypedArray, createAttribute, Policy } from './tdf/index.js';
import { type AuthProvider } from './auth/auth.js';
import { ClientConfig } from './nanotdf/Client.js';

async function fetchKasPubKey(kasUrl: string): Promise<string> {
const kasPubKeyResponse = await fetch(`${kasUrl}/kas_public_key?algorithm=ec:secp256r1`);
Expand All @@ -33,13 +33,14 @@ async function fetchKasPubKey(kasUrl: string): Promise<string> {
* const KAS_URL = 'http://localhost:65432/api/kas/';
*
* const ciphertext = '...';
* const client = new NanoTDFClient(
* await clientSecretAuthProvider({
* const client = new NanoTDFClient({
* authProvider: await clientSecretAuthProvider({
* clientId: 'tdf-client',
* clientSecret: '123-456',
* oidcOrigin: OIDC_ENDPOINT,
* }),
* KAS_URL
* kasEndpoint: KAS_URL
* }
* );
* client.decrypt(ciphertext)
* .then(plaintext => {
Expand Down Expand Up @@ -120,9 +121,9 @@ export class NanoTDFClient extends Client {
*/
async encrypt(data: string | TypedArray | ArrayBuffer): Promise<ArrayBuffer> {
// For encrypt always generate the client ephemeralKeyPair
const ephemeralKeyPair = await this.generateEphemeralKeyPair();

const ephemeralKeyPair = await this.ephemeralKeyPair;
const initializationVector = this.iv;

if (typeof initializationVector !== 'number') {
throw new Error('NanoTDF clients are single use. Please generate a new client and keypair.');
}
Expand Down Expand Up @@ -174,6 +175,10 @@ export class NanoTDFClient extends Client {
}
}

export type DatasetConfig = ClientConfig & {
maxKeyIterations?: number;
};

/**
* NanoTDF Dataset SDK Client
*
Expand All @@ -186,15 +191,15 @@ export class NanoTDFClient extends Client {
* const KAS_URL = 'http://localhost:65432/api/kas/';
*
* const ciphertext = '...';
* const client = new NanoTDFDatasetClient.default(
* await clientSecretAuthProvider({
* const client = new NanoTDFDatasetClient({
* authProvider: await clientSecretAuthProvider({
* clientId: 'tdf-client',
* clientSecret: '123-456',
* exchange: 'client',
* oidcOrigin: OIDC_ENDPOINT,
* }),
* KAS_URL
* );
* kasEndpoint: KAS_URL,
* });
* const plaintext = client.decrypt(ciphertext);
* console.log('Plaintext', plaintext);
* ```
Expand Down Expand Up @@ -223,19 +228,18 @@ export class NanoTDFDatasetClient extends Client {
* @param ephemeralKeyPair (optional) ephemeral key pair to use
* @param maxKeyIterations Max iteration to performe without a key rotation
*/
constructor(
authProvider: AuthProvider,
kasUrl: string,
maxKeyIterations: number = NanoTDFDatasetClient.NTDF_MAX_KEY_ITERATIONS,
ephemeralKeyPair?: Required<Readonly<CryptoKeyPair>>
) {
if (maxKeyIterations > NanoTDFDatasetClient.NTDF_MAX_KEY_ITERATIONS) {
throw new Error('Key iteration exceeds max iterations(8388606)');
constructor(opts: DatasetConfig) {
if (
opts.maxKeyIterations &&
opts.maxKeyIterations > NanoTDFDatasetClient.NTDF_MAX_KEY_ITERATIONS
) {
throw new Error(
`Key iteration exceeds max iterations(${NanoTDFDatasetClient.NTDF_MAX_KEY_ITERATIONS})`
);
}
super(opts);

super(authProvider, kasUrl, ephemeralKeyPair);

this.maxKeyIteration = maxKeyIterations;
this.maxKeyIteration = opts.maxKeyIterations || NanoTDFDatasetClient.NTDF_MAX_KEY_ITERATIONS;
this.keyIterationCount = 0;
}

Expand All @@ -250,7 +254,7 @@ export class NanoTDFDatasetClient extends Client {
// Intial encrypt
if (this.keyIterationCount == 0) {
// For encrypt always generate the client ephemeralKeyPair
const ephemeralKeyPair = await this.generateEphemeralKeyPair();
const ephemeralKeyPair = await this.ephemeralKeyPair;

if (!this.kasPubKey) {
this.kasPubKey = await fetchKasPubKey(this.kasUrl);
Expand Down
Loading

0 comments on commit 737643e

Please sign in to comment.