2025-01-21 18:29:39 +01:00

129 lines
3.3 KiB
TypeScript

import AES from '@cryptography/aes';
import { ab2i, i2ab } from './converters';
import { getWords } from './words';
class Counter {
_counter: Buffer;
constructor(initialValue: Buffer) {
this._counter = Buffer.from(initialValue);
}
increment() {
for (let i = 15; i >= 0; i--) {
if (this._counter[i] === 255) {
this._counter[i] = 0;
} else {
this._counter[i]++;
break;
}
}
}
}
class CTR {
private _counter: Counter;
private _remainingCounter?: Buffer;
private _remainingCounterIndex: number;
private _aes: AES;
constructor(key: Buffer, counter: Counter | Buffer) {
if (!(counter instanceof Counter)) {
counter = new Counter(counter);
}
this._counter = counter;
this._remainingCounter = undefined;
this._remainingCounterIndex = 16;
this._aes = new AES(getWords(key));
}
update(plainText: Buffer) {
return this.encrypt(plainText);
}
encrypt(plainText: Buffer) {
const encrypted = Buffer.from(plainText);
for (let i = 0; i < encrypted.length; i++) {
if (this._remainingCounterIndex === 16) {
this._remainingCounter = Buffer.from(i2ab(this._aes.encrypt(ab2i(this._counter._counter))));
this._remainingCounterIndex = 0;
this._counter.increment();
}
if (this._remainingCounter) {
encrypted[i] ^= this._remainingCounter[this._remainingCounterIndex++];
}
}
return encrypted;
}
}
export type CtrImpl = CTR;
// endregion
export function createDecipheriv(algorithm: string, key: Buffer, iv: Buffer) {
if (algorithm.includes('ECB')) {
throw new Error('Not supported');
} else {
return new CTR(key, iv);
}
}
export function createCipheriv(algorithm: string, key: Buffer, iv: Buffer) {
if (algorithm.includes('ECB')) {
throw new Error('Not supported');
} else {
return new CTR(key, iv);
}
}
export function randomBytes(count: number) {
const bytes = new Uint8Array(count);
crypto.getRandomValues(bytes);
return bytes;
}
class Hash {
private data = new Uint8Array(0);
constructor(private algorithm: 'sha1' | 'sha256') {}
update(data: ArrayLike<number>) {
// We shouldn't be needing new Uint8Array but it doesn't
// work without it
this.data = new Uint8Array(data);
}
async digest() {
if (this.algorithm === 'sha1') {
// eslint-disable-next-line no-restricted-globals
return Buffer.from(await self.crypto.subtle.digest('SHA-1', this.data));
} else {
// eslint-disable-next-line no-restricted-globals
return Buffer.from(await self.crypto.subtle.digest('SHA-256', this.data));
}
}
}
export async function pbkdf2(password: Buffer, salt: Buffer, iterations: number) {
const passwordKey = await crypto.subtle.importKey('raw', password, { name: 'PBKDF2' }, false, ['deriveBits']);
return Buffer.from(await crypto.subtle.deriveBits({
name: 'PBKDF2',
hash: 'SHA-512',
salt,
iterations,
}, passwordKey, 512));
}
export function createHash(algorithm: 'sha1' | 'sha256') {
return new Hash(algorithm);
}