mirror of
https://github.com/bitwarden/browser.git
synced 2024-12-25 16:59:17 +01:00
hmac implementation for web crypto
This commit is contained in:
parent
719c8aa70c
commit
81f7bd7b76
@ -2,4 +2,5 @@ export abstract class CryptoFunctionService {
|
||||
pbkdf2: (password: string | ArrayBuffer, salt: string | ArrayBuffer, algorithm: 'sha256' | 'sha512',
|
||||
iterations: number, length: number) => Promise<ArrayBuffer>;
|
||||
hash: (value: string | ArrayBuffer, algorithm: 'sha1' | 'sha256' | 'sha512') => Promise<ArrayBuffer>;
|
||||
hmac: (value: ArrayBuffer, key: ArrayBuffer, algorithm: 'sha1' | 'sha256' | 'sha512') => Promise<ArrayBuffer>;
|
||||
}
|
||||
|
@ -25,6 +25,10 @@ export class NodeCryptoFunctionService implements CryptoFunctionService {
|
||||
return hash.digest().buffer;
|
||||
}
|
||||
|
||||
async hmac(value: ArrayBuffer, key: ArrayBuffer, algorithm: 'sha1' | 'sha256' | 'sha512'): Promise<ArrayBuffer> {
|
||||
return new Uint8Array([]).buffer;
|
||||
}
|
||||
|
||||
private toNodeValue(value: string | ArrayBuffer): string | Buffer {
|
||||
let nodeValue: string | Buffer;
|
||||
if (typeof (value) === 'string') {
|
||||
|
@ -47,6 +47,21 @@ describe('WebCrypto Function Service', () => {
|
||||
testHash(true, 'sha256', regular256Hash, utf8256Hash, unicode256Hash);
|
||||
testHash(true, 'sha512', regular512Hash, utf8512Hash, unicode512Hash);
|
||||
});
|
||||
|
||||
describe('hmac', () => {
|
||||
const sha1Mac = '4d4c223f95dc577b665ec4ccbcb680b80a397038';
|
||||
const sha256Mac = '6be3caa84922e12aaaaa2f16c40d44433bb081ef323db584eb616333ab4e874f';
|
||||
const sha512Mac = '21910e341fa12106ca35758a2285374509326c9fbe0bd64e7b99c898f841dc948c58ce66d3504d8883c' +
|
||||
'5ea7817a0b7c5d4d9b00364ccd214669131fc17fe4aca';
|
||||
|
||||
testHmac(false, 'sha1', sha1Mac);
|
||||
testHmac(false, 'sha256', sha256Mac);
|
||||
testHmac(false, 'sha512', sha512Mac);
|
||||
|
||||
testHmac(true, 'sha1', sha1Mac);
|
||||
testHmac(true, 'sha256', sha256Mac);
|
||||
testHmac(true, 'sha512', sha512Mac);
|
||||
});
|
||||
});
|
||||
|
||||
function testPbkdf2ValidKey(edge: boolean, algorithm: 'sha256' | 'sha512', regularKey: string,
|
||||
@ -117,6 +132,15 @@ function testHash(edge: boolean, algorithm: 'sha1' | 'sha256' | 'sha512', regula
|
||||
});
|
||||
}
|
||||
|
||||
function testHmac(edge: boolean, algorithm: 'sha1' | 'sha256' | 'sha512', mac: string) {
|
||||
it('should create valid ' + algorithm + ' hmac' + (edge ? ' for edge' : ''), async () => {
|
||||
const webCryptoFunctionService = getWebCryptoFunctionService(edge);
|
||||
const computedMac = await webCryptoFunctionService.hmac(UtilsService.fromUtf8ToArray('SignMe!!').buffer,
|
||||
UtilsService.fromUtf8ToArray('secretkey').buffer, algorithm);
|
||||
expect(UtilsService.fromBufferToHex(computedMac)).toBe(mac);
|
||||
});
|
||||
}
|
||||
|
||||
function getWebCryptoFunctionService(edge = false) {
|
||||
const platformUtilsService = new BrowserPlatformUtilsService(edge);
|
||||
return new WebCryptoFunctionService(window, platformUtilsService);
|
||||
|
@ -35,7 +35,7 @@ export class WebCryptoFunctionService implements CryptoFunctionService {
|
||||
name: 'PBKDF2',
|
||||
salt: saltBuf,
|
||||
iterations: iterations,
|
||||
hash: { name: algorithm === 'sha256' ? 'SHA-256' : 'SHA-512' },
|
||||
hash: { name: this.toWebCryptoAlgorithm(algorithm) },
|
||||
};
|
||||
|
||||
const keyType: AesDerivedKeyParams = {
|
||||
@ -65,10 +65,29 @@ export class WebCryptoFunctionService implements CryptoFunctionService {
|
||||
|
||||
const valueBuf = this.toBuf(value);
|
||||
return await this.subtle.digest({
|
||||
name: algorithm === 'sha1' ? 'SHA-1' : algorithm === 'sha256' ? 'SHA-256' : 'SHA-512'
|
||||
name: this.toWebCryptoAlgorithm(algorithm)
|
||||
}, valueBuf);
|
||||
}
|
||||
|
||||
async hmac(value: ArrayBuffer, key: ArrayBuffer, algorithm: 'sha1' | 'sha256' | 'sha512'): Promise<ArrayBuffer> {
|
||||
if (this.isEdge) {
|
||||
const valueBytes = this.toForgeBytes(value);
|
||||
const keyBytes = this.toForgeBytes(key);
|
||||
const hmac = (forge as any).hmac.create();
|
||||
hmac.start(algorithm, keyBytes);
|
||||
hmac.update(valueBytes);
|
||||
return this.fromForgeBytesToBuf(hmac.digest().getBytes());
|
||||
}
|
||||
|
||||
const signingAlgorithm = {
|
||||
name: 'HMAC',
|
||||
hash: { name: this.toWebCryptoAlgorithm(algorithm) },
|
||||
};
|
||||
|
||||
const importedKey = await this.subtle.importKey('raw', key, signingAlgorithm, false, ['sign']);
|
||||
return await this.subtle.sign(signingAlgorithm, importedKey, value);
|
||||
}
|
||||
|
||||
private toBuf(value: string | ArrayBuffer): ArrayBuffer {
|
||||
let buf: ArrayBuffer;
|
||||
if (typeof (value) === 'string') {
|
||||
@ -94,4 +113,8 @@ export class WebCryptoFunctionService implements CryptoFunctionService {
|
||||
const b64 = forge.util.encode64(byteString);
|
||||
return UtilsService.fromB64ToArray(b64).buffer;
|
||||
}
|
||||
|
||||
private toWebCryptoAlgorithm(algorithm: 'sha1' | 'sha256' | 'sha512'): string {
|
||||
return algorithm === 'sha1' ? 'SHA-1' : algorithm === 'sha256' ? 'SHA-256' : 'SHA-512';
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user