Skip to content

Commit

Permalink
chore!(sdk): Remove html wrapper creation (#406)
Browse files Browse the repository at this point in the history
- still allow reading html wrapped files, for transitional support
  • Loading branch information
dmihalcik-virtru authored Dec 13, 2024
1 parent a5c1167 commit 1beb02c
Show file tree
Hide file tree
Showing 14 changed files with 67 additions and 354 deletions.
36 changes: 11 additions & 25 deletions lib/tdf3/src/client/builders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,21 +40,22 @@ export type EncryptParams = {
scope?: Scope;
metadata?: Metadata;
keypair?: CryptoKeyPair;
// Deprecated: Only offline more is currently supported
offline?: boolean;
windowSize?: number;
asHtml?: boolean;
getPolicyId?: () => Scope['policyId'];
mimeType?: string;
payloadKey?: Binary;
keyMiddleware?: EncryptKeyMiddleware;
splitPlan?: SplitStep[];
streamMiddleware?: EncryptStreamMiddleware;
assertionConfigs?: AssertionConfig[];

// Unsupported
asHtml?: boolean;
// Unsupported
offline?: boolean;
};

// 'Readonly<EncryptParams>': scope, metadata, offline, windowSize, asHtml

// deep copy is expensive, could be faster is Immer used, but to keep SDK work
// stable we can just make this object readonly
function freeze<Type>(obj: Type): Readonly<Type> {
Expand All @@ -75,9 +76,7 @@ class EncryptParamsBuilder {
attributes: [],
},
keypair: undefined,
offline: false,
windowSize: DEFAULT_SEGMENT_SIZE,
asHtml: false,
assertionConfigs: [],
}
) {
Expand Down Expand Up @@ -382,37 +381,24 @@ class EncryptParamsBuilder {
}

/**
* Whether the encrypted data should be formatted using html. This allows authorized users to
* double click and read using the Virtru Secure Reader, at the cost of reduced space efficiency.
* <br/><br/>
* This is enabled by default.
* @return {boolean} true if the encrypted data will be in html format.
* @deprecated This feature is not supported
*/
hasHtmlFormat(): boolean {
return !!this._params.asHtml;
return false;
}

/**
* Specify that the encrypted data should be formatted using html. This allows authorized users to
* double click and read using the Virtru Secure Reader, at the cost of reduced space efficiency.
* <br/><br/>
* This is enabled by default.
* @deprecated This feature is not supported
*/
setHtmlFormat() {
this._params.asHtml = true;
throw new ConfigurationError('HTML format is not supported');
}

/**
* Specify that the encrypted data should be formatted using html. This allows authorized users to
* double click and read using the Virtru Secure Reader, at the cost of reduced space efficiency.
* Returns this object for method chaining.
* <br/><br/>
* This is enabled by default.
* @return {EncryptParamsBuilder} - this object.
* @deprecated This feature is not supported
*/
withHtmlFormat(): EncryptParamsBuilder {
this.setHtmlFormat();
return this;
throw new ConfigurationError('HTML format is not supported');
}

/**
Expand Down
30 changes: 7 additions & 23 deletions lib/tdf3/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,11 @@ import {
EncryptConfiguration,
fetchKasPublicKey,
loadTDFStream,
unwrapHtml,
validatePolicyObject,
readStream,
wrapHtml,
writeStream,
} from '../tdf.js';
import { unwrapHtml } from '../utils/unwrap.js';
import { OIDCRefreshTokenProvider } from '../../../src/auth/oidc-refreshtoken-provider.js';
import { OIDCExternalJwtProvider } from '../../../src/auth/oidc-externaljwt-provider.js';
import { CryptoService } from '../crypto/declarations.js';
Expand Down Expand Up @@ -52,7 +51,6 @@ import { attributeFQNsAsValues } from '../../../src/policy/api.js';
import { type Value } from '../../../src/policy/attributes.js';

const GLOBAL_BYTE_LIMIT = 64 * 1000 * 1000 * 1000; // 64 GB, see WS-9363.
const HTML_BYTE_LIMIT = 100 * 1000 * 1000; // 100 MB, see WS-9476.

// No default config for now. Delegate to Virtru wrapper for endpoints.
const defaultClientConfig = { oidcOrigin: '', cryptoService: defaultCryptoService };
Expand Down Expand Up @@ -350,7 +348,7 @@ export class Client {
scope = { attributes: [], dissem: [] },
autoconfigure,
source,
asHtml = false,
asHtml,
metadata,
mimeType,
offline = true,
Expand All @@ -363,6 +361,9 @@ export class Client {
if (!offline) {
throw new ConfigurationError('online mode not supported');
}
if (asHtml) {
throw new ConfigurationError('html mode not supported');
}
const dpopKeys = await this.dpopKeys;

const policyObject = asPolicy(scope);
Expand Down Expand Up @@ -426,7 +427,7 @@ export class Client {

// TODO: Refactor underlying builder to remove some of this unnecessary config.

const byteLimit = asHtml ? HTML_BYTE_LIMIT : GLOBAL_BYTE_LIMIT;
const byteLimit = GLOBAL_BYTE_LIMIT;
const encryptionInformation = new SplitKey(new AesGcmCipher(this.cryptoService));
const splits: SplitStep[] = splitPlan?.length ? splitPlan : [{ kas: this.kasEndpoint }];
encryptionInformation.keyAccess = await Promise.all(
Expand Down Expand Up @@ -465,24 +466,7 @@ export class Client {
assertionConfigs,
};

const stream = await (streamMiddleware as EncryptStreamMiddleware)(await writeStream(ecfg));

if (!asHtml) {
return stream;
}

// Wrap if it's html.
if (!stream.manifest) {
throw new Error('internal: missing manifest in encrypt function');
}
const htmlBuf = wrapHtml(await stream.toBuffer(), stream.manifest, this.readerUrl ?? '');

return new DecoratedReadableStream({
pull(controller: ReadableStreamDefaultController) {
controller.enqueue(htmlBuf);
controller.close();
},
});
return (streamMiddleware as EncryptStreamMiddleware)(await writeStream(ecfg));
}

/**
Expand Down
55 changes: 1 addition & 54 deletions lib/tdf3/src/tdf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,7 @@ import {
SplitType,
} from './models/index.js';
import { base64 } from '../../src/encodings/index.js';
import {
ZipReader,
ZipWriter,
base64ToBuffer,
keyMerge,
buffToString,
concatUint8,
} from './utils/index.js';
import { ZipReader, ZipWriter, keyMerge, buffToString, concatUint8 } from './utils/index.js';
import { Binary } from './binary.js';
import { KasPublicKeyAlgorithm, KasPublicKeyInfo, OriginAllowList } from '../../src/access.js';
import { allPool, anyPool } from '../../src/concurrency.js';
Expand All @@ -40,7 +33,6 @@ import {
UnsupportedFeatureError as UnsupportedError,
} from '../../src/errors.js';
import { type Chunker } from '../../src/seekable.js';
import { htmlWrapperTemplate } from './templates/index.js';

// configurable
// TODO: remove dependencies from ciphers so that we can open-source instead of relying on other Virtru libs
Expand Down Expand Up @@ -180,51 +172,6 @@ export async function fetchKasPublicKey(
return fetchKasPubKeyV2(kas, algorithm || 'rsa:2048');
}

/**
*
* @param payload The TDF content to encode in HTML
* @param manifest A copy of the manifest
* @param transferUrl reader web-service start page
* @return utf-8 encoded HTML data
*/
export function wrapHtml(
payload: Uint8Array,
manifest: Manifest | string,
transferUrl: string
): Uint8Array {
const { origin } = new URL(transferUrl);
const exportManifest: string = typeof manifest === 'string' ? manifest : JSON.stringify(manifest);

const fullHtmlString = htmlWrapperTemplate({
transferUrl,
transferBaseUrl: origin,
manifest: base64.encode(exportManifest),
payload: buffToString(payload, 'base64'),
});

return new TextEncoder().encode(fullHtmlString);
}

export function unwrapHtml(htmlPayload: ArrayBuffer | Uint8Array | Binary | string) {
let html;
if (htmlPayload instanceof ArrayBuffer || ArrayBuffer.isView(htmlPayload)) {
html = new TextDecoder().decode(htmlPayload);
} else {
html = htmlPayload.toString();
}
const payloadRe = /<input id=['"]?data-input['"]?[^>]*?value=['"]?([a-zA-Z0-9+/=]+)['"]?/;
const reResult = payloadRe.exec(html);
if (reResult === null) {
throw new InvalidFileError('Payload is missing');
}
const base64Payload = reResult[1];
try {
return base64ToBuffer(base64Payload);
} catch (e) {
throw new InvalidFileError('There was a problem extracting the TDF3 payload', e);
}
}

export async function extractPemFromKeyString(keyString: string): Promise<string> {
let pem: string = keyString;

Expand Down
105 changes: 0 additions & 105 deletions lib/tdf3/src/templates/default.html.ts

This file was deleted.

10 changes: 0 additions & 10 deletions lib/tdf3/src/templates/escaper.ts

This file was deleted.

2 changes: 0 additions & 2 deletions lib/tdf3/src/templates/index.ts

This file was deleted.

4 changes: 0 additions & 4 deletions lib/tdf3/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ const hexSliceLookupTable = (() => {
return table;
})();

export function base64ToBuffer(b64: string): Uint8Array {
return Uint8Array.from(atob(b64).split(''), (c) => c.charCodeAt(0));
}

export function concatUint8(uint8Arrays: Uint8Array[]): Uint8Array {
const newLength = uint8Arrays.reduce(
(accumulator, currentValue) => accumulator + currentValue.length,
Expand Down
17 changes: 17 additions & 0 deletions lib/tdf3/src/utils/unwrap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { decodeArrayBuffer } from '../../../src/encodings/base64.js';
import { InvalidFileError } from '../../../src/errors.js';

export function unwrapHtml(htmlPayload: Uint8Array): Uint8Array {
const html = new TextDecoder().decode(htmlPayload);
const payloadRe = /<input id=['"]?data-input['"]?[^>]*?value=['"]?([a-zA-Z0-9+/=]+)['"]?/;
const reResult = payloadRe.exec(html);
if (!reResult) {
throw new InvalidFileError('Payload is missing');
}
const base64Payload = reResult[1];
try {
return new Uint8Array(decodeArrayBuffer(base64Payload));
} catch (e) {
throw new InvalidFileError('There was a problem extracting the TDF3 payload', e);
}
}
1 change: 0 additions & 1 deletion lib/tests/mocha/unit/crypto-di.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ describe('CryptoService DI', () => {
});
try {
await c.encrypt({
asHtml: false,
source: new ReadableStream({
pull(controller) {
controller.enqueue(new TextEncoder().encode('hello world'));
Expand Down
Loading

0 comments on commit 1beb02c

Please sign in to comment.