Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(crypto): Reuse of Hash instance failures #2824

Merged
merged 4 commits into from
Dec 7, 2023
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
15 changes: 7 additions & 8 deletions modules/crypto/src/lib/crc32-hash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,10 @@ export class CRC32Hash extends Hash {
readonly name = 'crc32';

options;
private _hash: CRC32;

constructor(options = {}) {
super();
this.options = {crypto: {}, ...options};
this._hash = new CRC32();
this.hashBatches = this.hashBatches.bind(this);
}

Expand All @@ -30,21 +28,22 @@ export class CRC32Hash extends Hash {
}

hashSync(input: ArrayBuffer, encoding: 'hex' | 'base64'): string {
this._hash.update(input);
const digest = this._hash.finalize();
const hash = new CRC32();
hash.update(input);
const digest = hash.finalize();
return encodeNumber(digest, encoding);
}

async *hashBatches(
asyncIterator: AsyncIterable<ArrayBuffer> | Iterable<ArrayBuffer>,
encoding: 'hex' | 'base64' = 'base64'
): AsyncIterable<ArrayBuffer> {
const hash = new CRC32();
for await (const chunk of asyncIterator) {
this._hash.update(chunk);
hash.update(chunk);
yield chunk;
}
const digest = this._hash.finalize();
const hash = encodeNumber(digest, encoding);
this.options.crypto?.onEnd?.({hash});
const digest = hash.finalize();
this.options.crypto?.onEnd?.({hash: encodeNumber(digest, encoding)});
}
}
15 changes: 7 additions & 8 deletions modules/crypto/src/lib/crc32c-hash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ export class CRC32CHash extends Hash {
readonly name = 'crc32c';

options;
private _hash: CRC32C;

/**
* Atomic hash calculation
Expand All @@ -21,7 +20,6 @@ export class CRC32CHash extends Hash {
constructor(options = {}) {
super();
this.options = {crypto: {}, ...options};
this._hash = new CRC32C(options);
}

/**
Expand All @@ -33,8 +31,9 @@ export class CRC32CHash extends Hash {
}

hashSync(input: ArrayBuffer, encoding: 'hex' | 'base64'): string {
this._hash.update(input);
const digest = this._hash.finalize();
const hash = new CRC32C(this.options);
hash.update(input);
const digest = hash.finalize();
return encodeNumber(digest, encoding);
}

Expand All @@ -44,12 +43,12 @@ export class CRC32CHash extends Hash {
asyncIterator: AsyncIterable<ArrayBuffer> | Iterable<ArrayBuffer>,
encoding: 'hex' | 'base64' = 'base64'
): AsyncIterable<ArrayBuffer> {
const hash = new CRC32C(this.options);
for await (const chunk of asyncIterator) {
this._hash.update(chunk);
hash.update(chunk);
yield chunk;
}
const digest = this._hash.finalize();
const hash = encodeNumber(digest, encoding);
this.options.crypto?.onEnd?.({hash});
const digest = hash.finalize();
this.options.crypto?.onEnd?.({hash: encodeNumber(digest, encoding)});
}
}
31 changes: 20 additions & 11 deletions modules/crypto/src/lib/crypto-hash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ export class CryptoHash extends Hash {
readonly name;

options: CryptoHashOptions;
/** Name of digest algorithm */
private _algorithm;
private _hash;
/** CryptoJS algorithm */
private _algo;

constructor(options: CryptoHashOptions) {
super();
Expand All @@ -38,13 +40,7 @@ export class CryptoHash extends Hash {
if (!CryptoJS) {
throw new Error(this.name);
}
if (!this._hash) {
const algo = CryptoJS.algo[this._algorithm];
this._hash = algo.create();
}
if (!this._hash) {
throw new Error(this.name);
}
this._algo = CryptoJS.algo[this._algorithm];
}

/**
Expand All @@ -53,29 +49,42 @@ export class CryptoHash extends Hash {
*/
async hash(input: ArrayBuffer, encoding: 'hex' | 'base64'): Promise<string> {
await this.preload();

const hash = this._algo.create();
if (!hash) {
throw new Error(this.name);
}

// arrayBuffer is accepted, even though types and docs say no
// https://stackoverflow.com/questions/25567468/how-to-decrypt-an-arraybuffer
const typedWordArray = CryptoJS.lib.WordArray.create(input);
// Map our encoding constant to Crypto library
const enc = encoding === 'base64' ? CryptoJS.enc.Base64 : CryptoJS.enc.Hex;
return this._hash.update(typedWordArray).finalize().toString(enc);
return hash.update(typedWordArray).finalize().toString(enc);
}

async *hashBatches(
asyncIterator: AsyncIterable<ArrayBuffer> | Iterable<ArrayBuffer>,
encoding: 'hex' | 'base64' = 'base64'
): AsyncIterable<ArrayBuffer> {
await this.preload();

const hash = this._algo.create();
if (!hash) {
throw new Error(this.name);
}

for await (const chunk of asyncIterator) {
// arrayBuffer is accepted, even though types and docs say no
// https://stackoverflow.com/questions/25567468/how-to-decrypt-an-arraybuffer
const typedWordArray = CryptoJS.lib.WordArray.create(chunk);
this._hash.update(typedWordArray);
hash.update(typedWordArray);
yield chunk;
}

// Map our encoding constant to Crypto library
const enc = encoding === 'base64' ? CryptoJS.enc.Base64 : CryptoJS.enc.Hex;
const digest = this._hash.finalize().toString(enc);
const digest = hash.finalize().toString(enc);
this.options?.crypto?.onEnd?.({hash: digest});
}
}
3 changes: 1 addition & 2 deletions modules/crypto/test/crypto.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ const TEST_CASES = [
title: 'binary data (repeated)',
data: repeatedData,
digests: {
sha256: 'SnGMX2AgkPh21d2sxow8phQa8lh8rjf2Vc7GFCIwj2g='
// 'bSCTuOJei5XsmAnqtmm2Aw/2EvUHldNdAxYb3mjSK9s=',
sha256: 'bSCTuOJei5XsmAnqtmm2Aw/2EvUHldNdAxYb3mjSK9s='
}
}
];
Expand Down
7 changes: 1 addition & 6 deletions modules/crypto/test/lib/crypto-hash.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,7 @@ test('CryptoHash#hash(MD5 = default)', async (t) => {
t.equal(hash, 'YnxTb+lyen1CsNkpmLv+qA==', 'binary data MD5 hash is correct');

hash = await cryptoHash.hash(repeatedData, 'base64');
t.equal(
hash,
// '2d4uZUoLXXO/XWJGnrVl5Q==',
'uZ5c9e72WDu/VNYYdsg/gg==',
'repeated data MD5 hash is correct'
);
t.equal(hash, '2d4uZUoLXXO/XWJGnrVl5Q==', 'repeated data MD5 hash is correct');

t.end();
});
Expand Down
Loading