use random bytes for each HMAC comparison
This commit is contained in:
parent
335c94e24c
commit
0bbb118691
|
@ -414,7 +414,7 @@ export class CryptoService implements CryptoServiceInterface {
|
||||||
const ctBytes: string = forge.util.decode64(encPieces[0]);
|
const ctBytes: string = forge.util.decode64(encPieces[0]);
|
||||||
const macBytes: string = forge.util.decode64(encPieces[1]);
|
const macBytes: string = forge.util.decode64(encPieces[1]);
|
||||||
const computedMacBytes = await this.computeMac(ctBytes, key.macKey, false);
|
const computedMacBytes = await this.computeMac(ctBytes, key.macKey, false);
|
||||||
const macsEqual = await this.macsEqual(key.macKey, macBytes, computedMacBytes);
|
const macsEqual = await this.macsEqual(macBytes, computedMacBytes);
|
||||||
if (!macsEqual) {
|
if (!macsEqual) {
|
||||||
throw new Error('MAC failed.');
|
throw new Error('MAC failed.');
|
||||||
}
|
}
|
||||||
|
@ -495,7 +495,7 @@ export class CryptoService implements CryptoServiceInterface {
|
||||||
|
|
||||||
if (theKey.macKey != null && macBytes != null) {
|
if (theKey.macKey != null && macBytes != null) {
|
||||||
const computedMacBytes = this.computeMac(ivBytes + ctBytes, theKey.macKey, false);
|
const computedMacBytes = this.computeMac(ivBytes + ctBytes, theKey.macKey, false);
|
||||||
if (!this.macsEqual(theKey.macKey, computedMacBytes, macBytes)) {
|
if (!this.macsEqual(computedMacBytes, macBytes)) {
|
||||||
// tslint:disable-next-line
|
// tslint:disable-next-line
|
||||||
console.error('MAC failed.');
|
console.error('MAC failed.');
|
||||||
return null;
|
return null;
|
||||||
|
@ -528,7 +528,7 @@ export class CryptoService implements CryptoServiceInterface {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const macsMatch = await this.macsEqualWC(keyBuf.macKey, macBuf, computedMacBuf);
|
const macsMatch = await this.macsEqualWC(macBuf, computedMacBuf);
|
||||||
if (macsMatch === false) {
|
if (macsMatch === false) {
|
||||||
// tslint:disable-next-line
|
// tslint:disable-next-line
|
||||||
console.error('MAC failed.');
|
console.error('MAC failed.');
|
||||||
|
@ -553,10 +553,11 @@ export class CryptoService implements CryptoServiceInterface {
|
||||||
|
|
||||||
// Safely compare two MACs in a way that protects against timing attacks (Double HMAC Verification).
|
// Safely compare two MACs in a way that protects against timing attacks (Double HMAC Verification).
|
||||||
// ref: https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/february/double-hmac-verification/
|
// ref: https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/february/double-hmac-verification/
|
||||||
private macsEqual(macKey: string, mac1: string, mac2: string): boolean {
|
// ref: https://paragonie.com/blog/2015/11/preventing-timing-attacks-on-string-comparison-with-double-hmac-strategy
|
||||||
|
private macsEqual(mac1: string, mac2: string): boolean {
|
||||||
const hmac = (forge as any).hmac.create();
|
const hmac = (forge as any).hmac.create();
|
||||||
|
|
||||||
hmac.start('sha256', macKey);
|
hmac.start('sha256', this.getRandomBytes(32));
|
||||||
hmac.update(mac1);
|
hmac.update(mac1);
|
||||||
const mac1Bytes = hmac.digest().getBytes();
|
const mac1Bytes = hmac.digest().getBytes();
|
||||||
|
|
||||||
|
@ -567,8 +568,10 @@ export class CryptoService implements CryptoServiceInterface {
|
||||||
return mac1Bytes === mac2Bytes;
|
return mac1Bytes === mac2Bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async macsEqualWC(macKeyBuf: ArrayBuffer, mac1Buf: ArrayBuffer, mac2Buf: ArrayBuffer): Promise<boolean> {
|
private async macsEqualWC(mac1Buf: ArrayBuffer, mac2Buf: ArrayBuffer): Promise<boolean> {
|
||||||
const macKey = await Subtle.importKey('raw', macKeyBuf, SigningAlgorithm, false, ['sign']);
|
const compareKey = new Uint8Array(32);
|
||||||
|
Crypto.getRandomValues(compareKey);
|
||||||
|
const macKey = await Subtle.importKey('raw', compareKey.buffer, SigningAlgorithm, false, ['sign']);
|
||||||
const mac1 = await Subtle.sign(SigningAlgorithm, macKey, mac1Buf);
|
const mac1 = await Subtle.sign(SigningAlgorithm, macKey, mac1Buf);
|
||||||
const mac2 = await Subtle.sign(SigningAlgorithm, macKey, mac2Buf);
|
const mac2 = await Subtle.sign(SigningAlgorithm, macKey, mac2Buf);
|
||||||
|
|
||||||
|
@ -608,4 +611,14 @@ export class CryptoService implements CryptoServiceInterface {
|
||||||
|
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getRandomBytes(byteLength: number): string {
|
||||||
|
const bytes = new Uint32Array(byteLength / 4);
|
||||||
|
Crypto.getRandomValues(bytes);
|
||||||
|
const buffer = forge.util.createBuffer();
|
||||||
|
for (let i = 0; i < bytes.length; i++) {
|
||||||
|
buffer.putInt32(bytes[i]);
|
||||||
|
}
|
||||||
|
return buffer.getBytes();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue