feat: implement SHA3 256/384/512

This commit is contained in:
microshine 2022-05-12 13:58:48 +03:00
parent 75d4f9a453
commit 8b1347df43
13 changed files with 129 additions and 7 deletions

View File

@ -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));

View File

@ -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(
{ {

View File

@ -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;

View File

@ -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");
} }

View File

@ -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(
{ {

View File

@ -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(
{ {

View File

@ -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;
} }

View File

@ -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";

12
src/mechs/sha/sha3_256.ts Normal file
View File

@ -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);
}
}

12
src/mechs/sha/sha3_384.ts Normal file
View File

@ -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);
}
}

12
src/mechs/sha/sha3_512.ts Normal file
View File

@ -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);
}
}

View File

@ -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());

View File

@ -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");
});
});
}); });