Skip to content

Commit

Permalink
lets nano methods return streams
Browse files Browse the repository at this point in the history
  • Loading branch information
dmihalcik-virtru committed Dec 13, 2024
1 parent 37ab34f commit 65d7cca
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 82 deletions.
2 changes: 1 addition & 1 deletion cli/package-lock.json

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

30 changes: 13 additions & 17 deletions cli/src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import './polyfills.js';
import { createWriteStream, openAsBlob } from 'node:fs';
import { stat, writeFile } from 'node:fs/promises';
import { stat } from 'node:fs/promises';
import { Writable } from 'node:stream';
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
Expand All @@ -15,6 +15,7 @@ import {
AuthProviders,
version,
OpenTDF,
DecoratedStream,
} from '@opentdf/sdk';
import { CLIError, Level, log } from './logger.js';
import { webcrypto } from 'crypto';
Expand Down Expand Up @@ -478,26 +479,21 @@ export const handleArgs = (args: string[]) => {
});
log('SILLY', `Initialized client ${JSON.stringify(client)}`);

let ct: DecoratedStream;
if ('tdf3' === argv.containerType || 'ztdf' === argv.containerType) {
log('DEBUG', `TDF3 Create`);
const ct = await client.createZTDF(await parseCreateZTDFOptions(argv));
if (!ct) {
throw new CLIError('CRITICAL', 'Encrypt configuration error: No output?');
}
const destination = createWriteStream(argv.output as string);
await ct.pipeTo(Writable.toWeb(destination));
ct = await client.createZTDF(await parseCreateZTDFOptions(argv));
} else {
log('SILLY', `Initialized client ${JSON.stringify(client)}`);

const cyphertext = await client.createNanoTDF(await parseCreateNanoTDFOptions(argv));

log('DEBUG', `Handle cyphertext output ${JSON.stringify(cyphertext)}`);
if (argv.output) {
await writeFile(argv.output, new Uint8Array(cyphertext));
} else {
console.log(base64.encodeArrayBuffer(cyphertext));
}
log('DEBUG', `Nano Create`);
ct = await client.createNanoTDF(await parseCreateNanoTDFOptions(argv));
}
if (!ct) {
throw new CLIError('CRITICAL', 'Encrypt configuration error: No output?');
}
const destination = argv.output
? process.stdout
: createWriteStream(argv.output as string);
await ct.pipeTo(Writable.toWeb(destination));
}
)
.usage('openTDF CLI\n\nUsage: $0 [options]')
Expand Down
17 changes: 13 additions & 4 deletions lib/src/opentdf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ export class OpenTDF {
);
}

async createNanoTDF(opts: CreateNanoTDFOptions): Promise<ArrayBuffer> {
async createNanoTDF(opts: CreateNanoTDFOptions): Promise<DecoratedStream> {
opts = { ...this.defaultCreateOptions, ...opts };
const collection = await this.createNanoTDFCollection(opts);
const ciphertext = await collection.encrypt(opts.source);
Expand Down Expand Up @@ -362,7 +362,7 @@ async function streamify(ab: Promise<ArrayBuffer>): Promise<ReadableStream<Uint8
}

export type NanoTDFCollection = {
encrypt: (source: Source) => Promise<ArrayBuffer>;
encrypt: (source: Source) => Promise<ReadableStream<Uint8Array>>;
close: () => Promise<void>;
};

Expand All @@ -387,12 +387,21 @@ class Collection {
});
}

async encrypt(source: Source): Promise<ArrayBuffer> {
async encrypt(source: Source): Promise<DecoratedStream> {
if (!this.client) {
throw new ConfigurationError('Collection is closed');
}
const chunker = await fromSource(source);
return this.client.encrypt(await chunker());
const cipherChunk = await this.client.encrypt(await chunker());
const stream: DecoratedStream = new ReadableStream<Uint8Array>({
start(controller) {
controller.enqueue(new Uint8Array(cipherChunk));
controller.close();
},
});
// TODO: client's header object is private
// stream.header = this.client.header;
return stream;
}

async close() {
Expand Down
2 changes: 1 addition & 1 deletion lib/tests/web/roundtrip.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ describe('Local roundtrip Tests', () => {
source: { type: 'chunker', location: fromString('hello world') },
});
const nanotdfParsed = await client.read({
source: { type: 'buffer', location: new Uint8Array(cipherText) },
source: { type: 'stream', location: cipherText },
});
expect(nanotdfParsed.header?.kas?.url).to.equal(kasEndpoint);
expect(nanotdfParsed.header?.kas?.identifier).to.equal('e1');
Expand Down
4 changes: 2 additions & 2 deletions web-app/package-lock.json

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

95 changes: 38 additions & 57 deletions web-app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -383,77 +383,58 @@ function App() {
},
dpopKeys: oidcClient.getSigningKey(),
});
setDownloadState('Encrypting...');
let f: FileSystemFileHandle | undefined;
const downloadName = `${inputFileName}.tdf`;
if (sinkType === 'fsapi') {
f = await getNewFileHandle('tdf', downloadName);
}
const progressTransformers = makeProgressPair(size, 'Encrypt');

let cipherText: ReadableStream<Uint8Array>;
switch (encryptContainerType) {
case 'nano': {
if ('url' in inputSource) {
throw new Error('Unsupported : fetch the url I guess?');
}
setDownloadState('Encrypting...');
const cipherText = await client.createNanoTDF({
case 'nano':
cipherText = await client.createNanoTDF({
source: { type: 'stream', location: source },
});
switch (sinkType) {
case 'file':
{
saver(new Blob([cipherText]), `${inputFileName}.ntdf`);
}
break;
case 'fsapi':
{
const file = await getNewFileHandle('ntdf', `${inputFileName}.ntdf`);
const writable = await file.createWritable();
try {
await writable.write(cipherText);
setDownloadState('Encrypt Complete');
} catch (e) {
setDownloadState(`Encrypt Failed: ${e}`);
} finally {
await writable.close();
}
}
break;
case 'none':
break;
}
break;
}
case 'tdf': {
case 'tdf':
try {
let f: FileSystemFileHandle | undefined;
const downloadName = `${inputFileName}.tdf`;
if (sinkType === 'fsapi') {
f = await getNewFileHandle('tdf', downloadName);
}
const progressTransformers = makeProgressPair(size, 'Encrypt');
const cipherTextFromClient = await client.createZTDF({
cipherText = await client.createZTDF({
source: { type: 'stream', location: source.pipeThrough(progressTransformers.reader) },
});
const cipherTextWithProgress = cipherTextFromClient.pipeThrough(
progressTransformers.writer
);
switch (sinkType) {
case 'file':
await toFile(cipherTextWithProgress, downloadName, { signal: sc.signal });
break;
case 'fsapi':
if (!f) {
throw new Error();
}
const writable = await f.createWritable();
await cipherTextWithProgress.pipeTo(writable, { signal: sc.signal });
break;
case 'none':
await cipherTextWithProgress.pipeTo(drain(), { signal: sc.signal });
break;
}
} catch (e) {
setDownloadState(`Encrypt Failed: ${e}`);
console.error('Encrypt Failed', e);
}
setStreamController(undefined);
break;
default:
setDownloadState(`Unsupported type`);
console.error('Encrypt Failed');
return;
}
const cipherTextWithProgress = cipherText.pipeThrough(progressTransformers.writer);
try {
switch (sinkType) {
case 'file':
await toFile(cipherTextWithProgress, downloadName, { signal: sc.signal });
break;
case 'fsapi':
if (!f) {
throw new Error();
}
const writable = await f.createWritable();
await cipherTextWithProgress.pipeTo(writable, { signal: sc.signal });
break;
case 'none':
await cipherTextWithProgress.pipeTo(drain(), { signal: sc.signal });
break;
}
} catch (e) {
setDownloadState(`Encrypt Failed: ${e}`);
console.error('Encrypt Failed', e);
}
setStreamController(undefined);
return true;
};

Expand Down

0 comments on commit 65d7cca

Please sign in to comment.