import * as asn1js from "asn1js"; import * as pvutils from "pvutils"; import { AlgorithmIdentifier } from "./AlgorithmIdentifier"; import { EMPTY_BUFFER } from "./constants"; import type { CryptoEngineAlgorithmOperation, CryptoEngineAlgorithmParams, ICryptoEngine } from "./CryptoEngine/CryptoEngineInterface"; import { ArgumentError } from "./errors";
//#region Crypto engine related function
export { ICryptoEngine } from "./CryptoEngine/CryptoEngineInterface";
function isCryptoEngine(engine: unknown): engine is ICryptoEngine { return engine
&& typeof engine === "object"
&& "crypto" in engine
? true
: false;
}
/** * Sets global crypto engine * @param name Name of the crypto engine * @param crypto * @param subtle * @deprecated Since version 3.0.0
*/
export function setEngine(name: string, crypto: ICryptoEngine | Crypto, subtle: ICryptoEngine | SubtleCrypto): void; /** * Sets global crypto engine * @param name Name of the crypto engine * @param crypto Crypto engine. If the parameter is omitted, `CryptoEngine` with `self.crypto` are used * @since 3.0.0
*/
export function setEngine(name: string, crypto?: ICryptoEngine): void;
export function setEngine(name: string, ...args: any[]): void {
let crypto: ICryptoEngine | null = null; if (args.length < 2) { // v3.0.0 implementation if (args.length) {
crypto = args[0];
} else { // crypto param is omitted, use CryptoEngine(self.crypto)
crypto = typeof self !== "undefined" && self.crypto ? new CryptoEngine({ name: "browser", crypto: self.crypto }) : null;
}
} else { // prev implementation const cryptoArg = args[0]; const subtleArg = args[1]; if (isCryptoEngine(subtleArg)) {
crypto = subtleArg;
} elseif (isCryptoEngine(cryptoArg)) {
crypto = cryptoArg;
} elseif ("subtle" in cryptoArg && "getRandomValues" in cryptoArg) {
crypto = new CryptoEngine({
crypto: cryptoArg,
});
}
}
if ((typeof process !== "undefined") && ("pid" in process) && (typeof global !== "undefined") && (typeof window === "undefined")) { // We are in Node if (typeof (global as any)[process.pid] === "undefined") {
(global as any)[process.pid] = {};
} else { if (typeof (global as any)[process.pid] !== "object") { thrownew Error(`Name global.${process.pid} already exists and it is not an object`);
}
}
if (typeof (global as any)[process.pid].pkijs === "undefined") {
(global as any)[process.pid].pkijs = {};
} else { if (typeof (global as any)[process.pid].pkijs !== "object") { thrownew Error(`Name global.${process.pid}.pkijs already exists and it is not an object`);
}
}
(global as any)[process.pid].pkijs.engine = {
name: name,
crypto,
};
} else { // We are in browser
engine = {
name: name,
crypto,
};
}
}
export function getEngine(): GlobalCryptoEngine { //#region We are in Node if ((typeof process !== "undefined") && ("pid" in process) && (typeof global !== "undefined") && (typeof window === "undefined")) {
let _engine;
try {
_engine = (global as any)[process.pid].pkijs.engine;
} catch (ex) { thrownew Error("Please call 'setEngine' before call to 'getEngine'");
}
return _engine;
} //#endregion
return engine;
} //#endregion
//#region Declaration of common functions
/** * Gets crypto subtle from the current "crypto engine" * @param safety * @returns Reruns {@link ICryptoEngine} or `null`
*/
export function getCrypto(safety?: boolean): ICryptoEngine | null; /** * Gets crypto subtle from the current "crypto engine" * @param safety * @returns Reruns {@link ICryptoEngine} or throws en exception * @throws Throws {@link Error} if `subtle` is empty
*/
export function getCrypto(safety: true): ICryptoEngine;
export function getCrypto(safety = false): ICryptoEngine | null { const _engine = getEngine();
if (!_engine.crypto && safety) { thrownew Error("Unable to create WebCrypto object");
}
return _engine.crypto;
}
/** * Initialize input Uint8Array by random values (with help from current "crypto engine") * @param view
*/
export function getRandomValues(view: Uint8Array) { return getCrypto(true).getRandomValues(view);
}
/** * Get OID for each specific algorithm * @param algorithm WebCrypto Algorithm * @param safety if `true` throws exception on unknown algorithm * @param target name of the target * @throws Throws {@link Error} exception if unknown WebCrypto algorithm
*/
export function getOIDByAlgorithm(algorithm: Algorithm, safety?: boolean, target?: string) { return getCrypto(true).getOIDByAlgorithm(algorithm, safety, target);
}
/** * Get default algorithm parameters for each kind of operation * @param algorithmName Algorithm name to get common parameters for * @param operation Kind of operation: "sign", "encrypt", "generateKey", "importKey", "exportKey", "verify"
*/ // TODO Add safety
export function getAlgorithmParameters(algorithmName: string, operation: CryptoEngineAlgorithmOperation): CryptoEngineAlgorithmParams { return getCrypto(true).getAlgorithmParameters(algorithmName, operation);
}
/** * Create CMS ECDSA signature from WebCrypto ECDSA signature * @param signatureBuffer WebCrypto result of "sign" function
*/
export function createCMSECDSASignature(signatureBuffer: ArrayBuffer): ArrayBuffer { //#region Initial check for correct length if ((signatureBuffer.byteLength % 2) !== 0) return EMPTY_BUFFER; //#endregion
//#region Initial variables const length = signatureBuffer.byteLength / 2; // There are two equal parts inside incoming ArrayBuffer
const rBuffer = new ArrayBuffer(length); const rView = new Uint8Array(rBuffer);
rView.set(new Uint8Array(signatureBuffer, 0, length));
const rInteger = new asn1js.Integer({ valueHex: rBuffer });
const sBuffer = new ArrayBuffer(length); const sView = new Uint8Array(sBuffer);
sView.set(new Uint8Array(signatureBuffer, length, length));
const sInteger = new asn1js.Integer({ valueHex: sBuffer }); //#endregion
return (new asn1js.Sequence({
value: [
rInteger.convertToDER(),
sInteger.convertToDER()
]
})).toBER(false);
}
/** * Create a single ArrayBuffer from CMS ECDSA signature * @param cmsSignature ASN.1 SEQUENCE contains CMS ECDSA signature * @param pointSize Size of EC point. Use {@link ECNamedCurves.find} to get correct point size * @returns WebCrypto signature
*/
export function createECDSASignatureFromCMS(cmsSignature: asn1js.AsnType, pointSize: number): ArrayBuffer { // Check input variables if (!(cmsSignature instanceof asn1js.Sequence
&& cmsSignature.valueBlock.value.length === 2
&& cmsSignature.valueBlock.value[0] instanceof asn1js.Integer
&& cmsSignature.valueBlock.value[1] instanceof asn1js.Integer)) return EMPTY_BUFFER;
/** * ANS X9.63 Key Derivation Function having a "Counter" as a parameter * @param hashFunction Used hash function * @param zBuffer ArrayBuffer containing ECDH shared secret to derive from * @param Counter * @param SharedInfo Usually DER encoded "ECC_CMS_SharedInfo" structure * @param crypto Crypto engine
*/
async function kdfWithCounter(hashFunction: string, zBuffer: ArrayBuffer, Counter: number, SharedInfo: ArrayBuffer, crypto: ICryptoEngine): Promise<{ counter: number; result: ArrayBuffer; }> { //#region Check of input parameters switch (hashFunction.toUpperCase()) { case"SHA-1": case"SHA-256": case"SHA-384": case"SHA-512": break; default: thrownew ArgumentError(`Unknown hash function: ${hashFunction}`);
}
ArgumentError.assert(zBuffer, "zBuffer", "ArrayBuffer"); if (zBuffer.byteLength === 0) thrownew ArgumentError("'zBuffer' has zero length, error");
ArgumentError.assert(SharedInfo, "SharedInfo", "ArrayBuffer"); if (Counter > 255) thrownew ArgumentError("Please set 'Counter' argument to value less or equal to 255"); //#endregion
//#region Create a combined ArrayBuffer for digesting
combinedBuffer = pvutils.utilConcatBuf(combinedBuffer, zBuffer);
combinedBuffer = pvutils.utilConcatBuf(combinedBuffer, counterBuffer);
combinedBuffer = pvutils.utilConcatBuf(combinedBuffer, SharedInfo); //#endregion
//#region Return digest of combined ArrayBuffer and information about current counter const result = await crypto.digest(
{ name: hashFunction },
combinedBuffer);
return {
counter: Counter,
result
}; //#endregion
}
/** * ANS X9.63 Key Derivation Function * @param hashFunction Used hash function * @param Zbuffer ArrayBuffer containing ECDH shared secret to derive from * @param keydatalen Length (!!! in BITS !!!) of used kew derivation function * @param SharedInfo Usually DER encoded "ECC_CMS_SharedInfo" structure * @param crypto Crypto engine
*/
export async function kdf(hashFunction: string, Zbuffer: ArrayBuffer, keydatalen: number, SharedInfo: ArrayBuffer, crypto = getCrypto(true)) { //#region Initial variables
let hashLength = 0;
let maxCounter = 1; //#endregion
//#region Check of input parameters switch (hashFunction.toUpperCase()) { case"SHA-1":
hashLength = 160; // In bits break; case"SHA-256":
hashLength = 256; // In bits break; case"SHA-384":
hashLength = 384; // In bits break; case"SHA-512":
hashLength = 512; // In bits break; default: thrownew ArgumentError(`Unknown hash function: ${hashFunction}`);
}
ArgumentError.assert(Zbuffer, "Zbuffer", "ArrayBuffer"); if (Zbuffer.byteLength === 0) thrownew ArgumentError("'Zbuffer' has zero length, error");
ArgumentError.assert(SharedInfo, "SharedInfo", "ArrayBuffer"); //#endregion
//#region Calculated maximum value of "Counter" variable const quotient = keydatalen / hashLength;
if (Math.floor(quotient) > 0) {
maxCounter = Math.floor(quotient);
if ((quotient - maxCounter) > 0)
maxCounter++;
} //#endregion
//#region Create an array of "kdfWithCounter" const incomingResult = []; for (let i = 1; i <= maxCounter; i++)
incomingResult.push(await kdfWithCounter(hashFunction, Zbuffer, i, SharedInfo, crypto)); //#endregion
//#region Return combined digest with specified length //#region Initial variables
let combinedBuffer = EMPTY_BUFFER;
let currentCounter = 1;
let found = true; //#endregion
//#region Combine all buffer together while (found) {
found = false;
for (const result of incomingResult) { if (result.counter === currentCounter) {
combinedBuffer = pvutils.utilConcatBuf(combinedBuffer, result.result);
found = true; break;
}
}
currentCounter++;
} //#endregion
//#region Create output buffer with specified length
keydatalen >>= 3; // Divide by 8 since "keydatalen" is in bits
if (combinedBuffer.byteLength > keydatalen) { const newBuffer = new ArrayBuffer(keydatalen); const newView = new Uint8Array(newBuffer); const combinedView = new Uint8Array(combinedBuffer);
for (let i = 0; i < keydatalen; i++)
newView[i] = combinedView[i];
return newBuffer;
}
return combinedBuffer; // Since the situation when "combinedBuffer.byteLength < keydatalen" here we have only "combinedBuffer.byteLength === keydatalen" //#endregion //#endregion
} //#endregion
import { CryptoEngine } from "./CryptoEngine/CryptoEngine";
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.17 Sekunden
(vorverarbeitet am 2026-06-06)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.