tutanota/packages/tutanota-crypto/lib/random/Randomizer.ts

82 lines
2.4 KiB
TypeScript
Raw Normal View History

2021-12-27 17:48:21 +01:00
// @ts-ignore[untyped-import]
import sjcl from "../internal/sjcl.js"
2022-12-27 15:37:40 +01:00
import type { EntropySource } from "../misc/Constants.js"
import { CryptoError } from "../misc/CryptoError.js"
2021-12-27 17:48:21 +01:00
/**
* This Interface provides an abstraction of the random number generator implementation.
*/
export class Randomizer {
2022-12-27 15:37:40 +01:00
random: any
2021-12-27 17:48:21 +01:00
2022-12-27 15:37:40 +01:00
constructor() {
this.random = new sjcl.prng(6)
}
2021-12-27 17:48:21 +01:00
2022-12-27 15:37:40 +01:00
/**
* Adds entropy to the random number generator algorithm
* @param entropyCache with: number Any number value, entropy The amount of entropy in the number in bit,
* source The source of the number.
*/
addEntropy(
entropyCache: Array<{
source: EntropySource
entropy: number
data: number | Array<number>
}>,
): Promise<void> {
2023-09-07 17:56:39 +02:00
for (const entry of entropyCache) {
2022-12-27 15:37:40 +01:00
this.random.addEntropy(entry.data, entry.entropy, entry.source)
2023-09-07 17:56:39 +02:00
}
2022-12-27 15:37:40 +01:00
return Promise.resolve()
}
2021-12-27 17:48:21 +01:00
2022-12-27 15:37:40 +01:00
addStaticEntropy(bytes: Uint8Array) {
2023-09-07 17:56:39 +02:00
for (const byte of bytes) {
2022-12-27 15:37:40 +01:00
this.random.addEntropy(byte, 8, "static")
2023-09-07 17:56:39 +02:00
}
2022-12-27 15:37:40 +01:00
}
2021-12-27 17:48:21 +01:00
2022-12-27 15:37:40 +01:00
/**
* Not used currently because we always have enough entropy using getRandomValues()
*/
isReady(): boolean {
return this.random.isReady() !== 0
}
2021-12-27 17:48:21 +01:00
2022-12-27 15:37:40 +01:00
/**
* Generates random data. The function initRandomDataGenerator must have been called prior to the first call to this function.
* @param nbrOfBytes The number of bytes the random data shall have.
* @return A hex coded string of random data.
* @throws {CryptoError} if the randomizer is not seeded (isReady == false)
*/
2025-12-05 09:35:58 +01:00
generateRandomData(nbrOfBytes: number): Uint8Array<ArrayBuffer> {
2022-12-27 15:37:40 +01:00
try {
// read the minimal number of words to get nbrOfBytes
let nbrOfWords = Math.floor((nbrOfBytes + 3) / 4)
let words = this.random.randomWords(nbrOfWords)
let arrayBuffer = sjcl.codec.arrayBuffer.fromBits(words, false)
// simply cut off the exceeding bytes
return new Uint8Array(arrayBuffer, 0, nbrOfBytes) // truncate the arraybuffer as precaution
} catch (e) {
throw new CryptoError("error during random number generation", e as Error)
}
}
/**
* Generate a number that fits in the range of an n-byte integer
*/
generateRandomNumber(nbrOfBytes: number): number {
const bytes = this.generateRandomData(nbrOfBytes)
let result = 0
for (let i = 0; i < bytes.length; ++i) {
result += bytes[i] << (i * 8)
}
return result
}
2021-12-27 17:48:21 +01:00
}
// TODO singleton should be created in the app?
// the randomizer instance (singleton) that should be used throughout the app
2022-12-27 15:37:40 +01:00
export const random: Randomizer = new Randomizer()