dograma
Version:
NodeJS/Browser MTProto API Telegram client library,
153 lines (130 loc) • 3.97 kB
text/typescript
import { default as AES } from "@cryptography/aes";
import { i2ab, ab2i } from "./converters";
import { getWords } from "./words";
import crypto from "crypto";
export class Counter {
_counter: Buffer;
constructor(initialValue: any) {
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;
}
}
}
}
export class CTR {
private _counter: Counter;
private _remainingCounter?: Buffer;
private _remainingCounterIndex: number;
private _aes: AES;
constructor(key: Buffer, counter: any) {
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: any) {
return this.encrypt(plainText);
}
encrypt(plainText: any) {
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;
}
}
// 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 as any).webCrypto.getRandomValues(bytes);
return bytes;
}
export class Hash {
private readonly algorithm: string;
private data?: Uint8Array;
constructor(algorithm: string) {
this.algorithm = algorithm;
}
update(data: Buffer) {
//We shouldn't be needing new Uint8Array but it doesn't
//work without it
this.data = new Uint8Array(data);
}
async digest() {
if (this.data) {
if (this.algorithm === "sha1") {
return Buffer.from(
await self.crypto.subtle.digest("SHA-1", this.data)
);
} else if (this.algorithm === "sha256") {
return Buffer.from(
await self.crypto.subtle.digest("SHA-256", this.data)
);
}
}
return Buffer.alloc(0);
}
}
export async function pbkdf2Sync(
password: any,
salt: any,
iterations: any,
...args: any
) {
const passwordKey = await (crypto as any).webCrypto.subtle.importKey(
"raw",
password,
{ name: "PBKDF2" },
false,
["deriveBits"]
);
return Buffer.from(
await (crypto as any).webCrypto.subtle.deriveBits(
{
name: "PBKDF2",
hash: "SHA-512",
salt,
iterations,
},
passwordKey,
512
)
);
}
export function createHash(algorithm: string) {
return new Hash(algorithm);
}