111 lines
3.4 KiB
TypeScript
111 lines
3.4 KiB
TypeScript
export const base64ToBytes = (b64: string): Uint8Array => {
|
|
const binaryString = atob(b64);
|
|
const bytes = new Uint8Array(binaryString.length);
|
|
for (let i = 0; i < binaryString.length; i++) {
|
|
bytes[i] = binaryString.charCodeAt(i);
|
|
}
|
|
return bytes;
|
|
}
|
|
|
|
export const bytesToBase64 = (buf: Uint8Array): string => {
|
|
const bytes: string[] = new Array(buf.length);
|
|
for (let i = 0; i < buf.length; i++) {
|
|
bytes[i] = String.fromCharCode(buf[i]);
|
|
}
|
|
return btoa(bytes.join(''));
|
|
}
|
|
|
|
/**
|
|
* Do semi constant time comparison of two Uint8Arrays to prevent timing attacks.
|
|
* Returns true if the arrays are equal, false otherwise.
|
|
*
|
|
* Note: This is not truly constant time, but it is designed to take the same amount of time regardless of where the first difference is.
|
|
* It will still take longer for longer arrays, but it will not short-circuit on the first difference.
|
|
*/
|
|
export const constantTimeEqual = (a: Uint8Array, b: Uint8Array): boolean => {
|
|
if (a.length !== b.length) return false;
|
|
|
|
let result = 0;
|
|
for (let i = 0; i < a.length; i++) {
|
|
result |= a[i] ^ b[i];
|
|
}
|
|
return result === 0;
|
|
}
|
|
|
|
/* BufferReader is a utility class for reading and writing binary data from a Uint8Array buffer.
|
|
* It maintains an internal offset and length to track the current position in the buffer. It
|
|
* provides methods for reading unsigned integers in little-endian format, as well as arbitrary
|
|
* byte arrays. It also allows writing unsigned integers back into the buffer. This class is
|
|
* useful for parsing and constructing binary protocols.
|
|
*/
|
|
export class BufferReader {
|
|
protected buffer: Uint8Array;
|
|
protected offset: number = 0;
|
|
public length: number = 0;
|
|
|
|
constructor(buffer: Uint8Array) {
|
|
this.buffer = new Uint8Array(buffer.length);
|
|
for (let i = 0; i < buffer.length; i++) {
|
|
this.buffer[i] = buffer[i];
|
|
}
|
|
this.length = buffer.length;
|
|
}
|
|
|
|
public reset(buffer: Uint8Array) {
|
|
this.buffer = new Uint8Array(buffer.length);
|
|
for (let i = 0; i < buffer.length; i++) {
|
|
this.buffer[i] = buffer[i];
|
|
}
|
|
this.offset = 0;
|
|
this.length = buffer.length;
|
|
}
|
|
|
|
public readUint8(): number {
|
|
const value = this.buffer[this.offset];
|
|
this.offset++;
|
|
this.length--;
|
|
return value;
|
|
}
|
|
|
|
public readUint16LE(): number {
|
|
const value = this.buffer[this.offset] | (this.buffer[this.offset + 1] << 8);
|
|
this.offset += 2;
|
|
this.length -= 2;
|
|
return value;
|
|
}
|
|
|
|
public readUint32LE(): number {
|
|
const value = this.buffer[this.offset] |
|
|
(this.buffer[this.offset + 1] << 8) |
|
|
(this.buffer[this.offset + 2] << 16) |
|
|
(this.buffer[this.offset + 3] << 24);
|
|
this.offset += 4;
|
|
this.length -= 4;
|
|
return value;
|
|
}
|
|
|
|
public readBytes(size?: number): Uint8Array {
|
|
size ||= (this.buffer.length - this.offset);
|
|
const value = this.buffer.slice(this.offset, this.offset + size);
|
|
this.offset += size;
|
|
this.length -= size;
|
|
return value;
|
|
}
|
|
|
|
public writeUint16LE(value: number): void {
|
|
this.buffer[this.offset] = value & 0xFF;
|
|
this.buffer[this.offset + 1] = (value >> 8) & 0xFF;
|
|
this.offset += 2;
|
|
this.length += 2;
|
|
}
|
|
|
|
public writeUint32LE(value: number): void {
|
|
this.buffer[this.offset] = value & 0xFF;
|
|
this.buffer[this.offset + 1] = (value >> 8) & 0xFF;
|
|
this.buffer[this.offset + 2] = (value >> 16) & 0xFF;
|
|
this.buffer[this.offset + 3] = (value >> 24) & 0xFF;
|
|
this.offset += 4;
|
|
this.length += 4;
|
|
}
|
|
}
|