feat: implement SHA3 256/384/512
This commit is contained in:
parent
75d4f9a453
commit
8b1347df43
|
@ -4,6 +4,7 @@ import { JsonParser, JsonSerializer } from "@peculiar/json-schema";
|
||||||
import {BufferSourceConverter} from "pvtsutils";
|
import {BufferSourceConverter} from "pvtsutils";
|
||||||
import * as core from "webcrypto-core";
|
import * as core from "webcrypto-core";
|
||||||
import { CryptoKey } from "../../keys";
|
import { CryptoKey } from "../../keys";
|
||||||
|
import { ShaCrypto } from "../sha";
|
||||||
import { getOidByNamedCurve } from "./helper";
|
import { getOidByNamedCurve } from "./helper";
|
||||||
import { EcPrivateKey } from "./private_key";
|
import { EcPrivateKey } from "./private_key";
|
||||||
import { EcPublicKey } from "./public_key";
|
import { EcPublicKey } from "./public_key";
|
||||||
|
@ -48,7 +49,7 @@ export class EcCrypto {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async sign(algorithm: EcdsaParams, key: EcPrivateKey, data: Uint8Array): Promise<ArrayBuffer> {
|
public static async sign(algorithm: EcdsaParams, key: EcPrivateKey, data: Uint8Array): Promise<ArrayBuffer> {
|
||||||
const cryptoAlg = (algorithm.hash as Algorithm).name.replace("-", "");
|
const cryptoAlg = ShaCrypto.getAlgorithmName(algorithm.hash as Algorithm);
|
||||||
const signer = crypto.createSign(cryptoAlg);
|
const signer = crypto.createSign(cryptoAlg);
|
||||||
signer.update(Buffer.from(data));
|
signer.update(Buffer.from(data));
|
||||||
|
|
||||||
|
@ -68,7 +69,7 @@ export class EcCrypto {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async verify(algorithm: EcdsaParams, key: EcPublicKey, signature: Uint8Array, data: Uint8Array): Promise<boolean> {
|
public static async verify(algorithm: EcdsaParams, key: EcPublicKey, signature: Uint8Array, data: Uint8Array): Promise<boolean> {
|
||||||
const cryptoAlg = (algorithm.hash as Algorithm).name.replace("-", "");
|
const cryptoAlg = ShaCrypto.getAlgorithmName(algorithm.hash as Algorithm);
|
||||||
const signer = crypto.createVerify(cryptoAlg);
|
const signer = crypto.createVerify(cryptoAlg);
|
||||||
signer.update(Buffer.from(data));
|
signer.update(Buffer.from(data));
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,11 @@ export class EcdsaProvider extends core.EcdsaProvider {
|
||||||
|
|
||||||
public override namedCurves = core.EcCurves.names;
|
public override namedCurves = core.EcCurves.names;
|
||||||
|
|
||||||
|
public override hashAlgorithms = [
|
||||||
|
"SHA-1", "SHA-256", "SHA-384", "SHA-512",
|
||||||
|
"shake128", "shake256",
|
||||||
|
"SHA3-256", "SHA3-384", "SHA3-512"];
|
||||||
|
|
||||||
public async onGenerateKey(algorithm: EcKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKeyPair> {
|
public async onGenerateKey(algorithm: EcKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKeyPair> {
|
||||||
const keys = await EcCrypto.generateKey(
|
const keys = await EcCrypto.generateKey(
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import crypto from "crypto";
|
import crypto from "crypto";
|
||||||
import { JsonParser, JsonSerializer } from "@peculiar/json-schema";
|
import { JsonParser, JsonSerializer } from "@peculiar/json-schema";
|
||||||
import * as core from "webcrypto-core";
|
import * as core from "webcrypto-core";
|
||||||
|
import { ShaCrypto } from "../sha";
|
||||||
import { setCryptoKey, getCryptoKey } from "../storage";
|
import { setCryptoKey, getCryptoKey } from "../storage";
|
||||||
import { HmacCryptoKey } from "./key";
|
import { HmacCryptoKey } from "./key";
|
||||||
|
|
||||||
|
@ -22,16 +23,16 @@ export class HmacProvider extends core.HmacProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async onSign(algorithm: Algorithm, key: HmacCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
|
public override async onSign(algorithm: Algorithm, key: HmacCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
|
||||||
const hash = key.algorithm.hash.name.replace("-", "");
|
const cryptoAlg = ShaCrypto.getAlgorithmName(key.algorithm.hash);
|
||||||
const hmac = crypto.createHmac(hash, getCryptoKey(key).data)
|
const hmac = crypto.createHmac(cryptoAlg, getCryptoKey(key).data)
|
||||||
.update(Buffer.from(data)).digest();
|
.update(Buffer.from(data)).digest();
|
||||||
|
|
||||||
return new Uint8Array(hmac).buffer;
|
return new Uint8Array(hmac).buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async onVerify(algorithm: Algorithm, key: HmacCryptoKey, signature: ArrayBuffer, data: ArrayBuffer): Promise<boolean> {
|
public override async onVerify(algorithm: Algorithm, key: HmacCryptoKey, signature: ArrayBuffer, data: ArrayBuffer): Promise<boolean> {
|
||||||
const hash = key.algorithm.hash.name.replace("-", "");
|
const cryptoAlg = ShaCrypto.getAlgorithmName(key.algorithm.hash);
|
||||||
const hmac = crypto.createHmac(hash, getCryptoKey(key).data)
|
const hmac = crypto.createHmac(cryptoAlg, getCryptoKey(key).data)
|
||||||
.update(Buffer.from(data)).digest();
|
.update(Buffer.from(data)).digest();
|
||||||
|
|
||||||
return hmac.compare(Buffer.from(signature)) === 0;
|
return hmac.compare(Buffer.from(signature)) === 0;
|
||||||
|
|
|
@ -181,6 +181,12 @@ export class RsaCrypto {
|
||||||
return "RSA-SHA384";
|
return "RSA-SHA384";
|
||||||
case "SHA-512":
|
case "SHA-512":
|
||||||
return "RSA-SHA512";
|
return "RSA-SHA512";
|
||||||
|
case "SHA3-256":
|
||||||
|
return "RSA-SHA3-256";
|
||||||
|
case "SHA3-384":
|
||||||
|
return "RSA-SHA3-384";
|
||||||
|
case "SHA3-512":
|
||||||
|
return "RSA-SHA3-512";
|
||||||
default:
|
default:
|
||||||
throw new core.OperationError("algorithm.hash: Is not recognized");
|
throw new core.OperationError("algorithm.hash: Is not recognized");
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,11 @@ import { RsaPublicKey } from "./public_key";
|
||||||
|
|
||||||
export class RsaPssProvider extends core.RsaPssProvider {
|
export class RsaPssProvider extends core.RsaPssProvider {
|
||||||
|
|
||||||
|
public override hashAlgorithms = [
|
||||||
|
"SHA-1", "SHA-256", "SHA-384", "SHA-512",
|
||||||
|
"shake128", "shake256",
|
||||||
|
"SHA3-256", "SHA3-384", "SHA3-512"];
|
||||||
|
|
||||||
public async onGenerateKey(algorithm: RsaHashedKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKeyPair> {
|
public async onGenerateKey(algorithm: RsaHashedKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKeyPair> {
|
||||||
const keys = await RsaCrypto.generateKey(
|
const keys = await RsaCrypto.generateKey(
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,6 +6,11 @@ import { RsaPublicKey } from "./public_key";
|
||||||
|
|
||||||
export class RsaSsaProvider extends core.RsaSsaProvider {
|
export class RsaSsaProvider extends core.RsaSsaProvider {
|
||||||
|
|
||||||
|
public override hashAlgorithms = [
|
||||||
|
"SHA-1", "SHA-256", "SHA-384", "SHA-512",
|
||||||
|
"shake128", "shake256",
|
||||||
|
"SHA3-256", "SHA3-384", "SHA3-512"];
|
||||||
|
|
||||||
public async onGenerateKey(algorithm: RsaHashedKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<globalThis.CryptoKeyPair> {
|
public async onGenerateKey(algorithm: RsaHashedKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<globalThis.CryptoKeyPair> {
|
||||||
const keys = await RsaCrypto.generateKey(
|
const keys = await RsaCrypto.generateKey(
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,18 +12,48 @@ export class ShaCrypto {
|
||||||
case "SHA-1":
|
case "SHA-1":
|
||||||
return 160;
|
return 160;
|
||||||
case "SHA-256":
|
case "SHA-256":
|
||||||
|
case "SHA3-256":
|
||||||
return 256;
|
return 256;
|
||||||
case "SHA-384":
|
case "SHA-384":
|
||||||
|
case "SHA3-384":
|
||||||
return 384;
|
return 384;
|
||||||
case "SHA-512":
|
case "SHA-512":
|
||||||
|
case "SHA3-512":
|
||||||
return 512;
|
return 512;
|
||||||
default:
|
default:
|
||||||
throw new Error("Unrecognized name");
|
throw new Error("Unrecognized name");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns NodeJS Crypto algorithm name from WebCrypto algorithm
|
||||||
|
* @param algorithm WebCRypto algorithm
|
||||||
|
* @throws Throws Error if an unrecognized name
|
||||||
|
*/
|
||||||
|
public static getAlgorithmName(algorithm: Algorithm): string {
|
||||||
|
switch (algorithm.name.toUpperCase()) {
|
||||||
|
case "SHA-1":
|
||||||
|
return "sha1";
|
||||||
|
case "SHA-256":
|
||||||
|
return "sha256";
|
||||||
|
case "SHA-384":
|
||||||
|
return "sha384";
|
||||||
|
case "SHA-512":
|
||||||
|
return "sha512";
|
||||||
|
case "SHA3-256":
|
||||||
|
return "sha3-256";
|
||||||
|
case "SHA3-384":
|
||||||
|
return "sha3-384";
|
||||||
|
case "SHA3-512":
|
||||||
|
return "sha3-512";
|
||||||
|
default:
|
||||||
|
throw new Error("Unrecognized name");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static digest(algorithm: Algorithm, data: ArrayBuffer) {
|
public static digest(algorithm: Algorithm, data: ArrayBuffer) {
|
||||||
const hash = crypto.createHash(algorithm.name.replace("-", ""))
|
const hashAlg = this.getAlgorithmName(algorithm);
|
||||||
|
const hash = crypto.createHash(hashAlg)
|
||||||
.update(Buffer.from(data)).digest();
|
.update(Buffer.from(data)).digest();
|
||||||
return new Uint8Array(hash).buffer;
|
return new Uint8Array(hash).buffer;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
|
export * from "./crypto";
|
||||||
export * from "./sha_1";
|
export * from "./sha_1";
|
||||||
export * from "./sha_256";
|
export * from "./sha_256";
|
||||||
export * from "./sha_384";
|
export * from "./sha_384";
|
||||||
export * from "./sha_512";
|
export * from "./sha_512";
|
||||||
|
export * from "./sha3_256";
|
||||||
|
export * from "./sha3_384";
|
||||||
|
export * from "./sha3_512";
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import * as core from "webcrypto-core";
|
||||||
|
import { ShaCrypto } from "./crypto";
|
||||||
|
|
||||||
|
export class Sha3256Provider extends core.ProviderCrypto {
|
||||||
|
public name = "SHA3-256";
|
||||||
|
public usages = [];
|
||||||
|
|
||||||
|
public override async onDigest(algorithm: Algorithm, data: ArrayBuffer): Promise<ArrayBuffer> {
|
||||||
|
return ShaCrypto.digest(algorithm, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
import * as core from "webcrypto-core";
|
||||||
|
import { ShaCrypto } from "./crypto";
|
||||||
|
|
||||||
|
export class Sha3384Provider extends core.ProviderCrypto {
|
||||||
|
public name = "SHA3-384";
|
||||||
|
public usages = [];
|
||||||
|
|
||||||
|
public override async onDigest(algorithm: Algorithm, data: ArrayBuffer): Promise<ArrayBuffer> {
|
||||||
|
return ShaCrypto.digest(algorithm, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
import * as core from "webcrypto-core";
|
||||||
|
import { ShaCrypto } from "./crypto";
|
||||||
|
|
||||||
|
export class Sha3512Provider extends core.ProviderCrypto {
|
||||||
|
public name = "SHA3-512";
|
||||||
|
public usages = [];
|
||||||
|
|
||||||
|
public override async onDigest(algorithm: Algorithm, data: ArrayBuffer): Promise<ArrayBuffer> {
|
||||||
|
return ShaCrypto.digest(algorithm, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
import * as crypto from "crypto";
|
||||||
import * as process from "process";
|
import * as process from "process";
|
||||||
import * as core from "webcrypto-core";
|
import * as core from "webcrypto-core";
|
||||||
import {
|
import {
|
||||||
|
@ -12,6 +13,7 @@ import {
|
||||||
RsaEsProvider, RsaOaepProvider, RsaPssProvider, RsaSsaProvider,
|
RsaEsProvider, RsaOaepProvider, RsaPssProvider, RsaSsaProvider,
|
||||||
Sha1Provider, Sha256Provider, Sha384Provider, Sha512Provider,
|
Sha1Provider, Sha256Provider, Sha384Provider, Sha512Provider,
|
||||||
Shake128Provider, Shake256Provider,
|
Shake128Provider, Shake256Provider,
|
||||||
|
Sha3256Provider, Sha3384Provider, Sha3512Provider,
|
||||||
} from "./mechs";
|
} from "./mechs";
|
||||||
|
|
||||||
export class SubtleCrypto extends core.SubtleCrypto {
|
export class SubtleCrypto extends core.SubtleCrypto {
|
||||||
|
@ -71,6 +73,17 @@ export class SubtleCrypto extends core.SubtleCrypto {
|
||||||
//#endregion
|
//#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hashes = crypto.getHashes();
|
||||||
|
if (hashes.includes("sha3-256")) {
|
||||||
|
this.providers.set(new Sha3256Provider());
|
||||||
|
}
|
||||||
|
if (hashes.includes("sha3-384")) {
|
||||||
|
this.providers.set(new Sha3384Provider());
|
||||||
|
}
|
||||||
|
if (hashes.includes("sha3-512")) {
|
||||||
|
this.providers.set(new Sha3512Provider());
|
||||||
|
}
|
||||||
|
|
||||||
if (nodeMajorVersion && parseInt(nodeMajorVersion, 10) >= 14) {
|
if (nodeMajorVersion && parseInt(nodeMajorVersion, 10) >= 14) {
|
||||||
//#region EdDSA
|
//#region EdDSA
|
||||||
this.providers.set(new EdDsaProvider());
|
this.providers.set(new EdDsaProvider());
|
||||||
|
|
|
@ -264,4 +264,20 @@ context("Crypto", () => {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
context("SHA3", () => {
|
||||||
|
const data = new Uint8Array(10);
|
||||||
|
it("SHA3-256", async () => {
|
||||||
|
const digest = await crypto.subtle.digest("SHA3-256", data);
|
||||||
|
assert.strictEqual(Convert.ToHex(digest), "0cd5285ba8524fe42ac8f0076de9135d056132a9996213ae1c0f1420c908418b");
|
||||||
|
});
|
||||||
|
it("SHA3-384", async () => {
|
||||||
|
const digest = await crypto.subtle.digest("SHA3-384", data);
|
||||||
|
assert.strictEqual(Convert.ToHex(digest), "f54cecb8c160015f87b9e51edd087e10479d60479a42ff7e907ddf129fd7cb2782eb5624c43b453a24cffd8cbe42d0ec");
|
||||||
|
});
|
||||||
|
it("SHA3-512", async () => {
|
||||||
|
const digest = await crypto.subtle.digest("SHA3-512", data);
|
||||||
|
assert.strictEqual(Convert.ToHex(digest), "e12f775adfb4e440b74af7b670849a44b7efd1612a97a3a201080cb31944f1f2d9f0eae6b7c0cdb602f6ff0ba181add9997fd06e43f992df577aa52153ca0d27");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
Reference in New Issue