From 8b1347df434ba1d8d973e0c08d61c0dc54f38432 Mon Sep 17 00:00:00 2001 From: microshine Date: Thu, 12 May 2022 13:58:48 +0300 Subject: [PATCH] feat: implement SHA3 256/384/512 --- src/mechs/ec/crypto.ts | 5 +++-- src/mechs/ec/ec_dsa.ts | 5 +++++ src/mechs/hmac/hmac.ts | 9 +++++---- src/mechs/rsa/crypto.ts | 6 ++++++ src/mechs/rsa/rsa_pss.ts | 5 +++++ src/mechs/rsa/rsa_ssa.ts | 5 +++++ src/mechs/sha/crypto.ts | 32 +++++++++++++++++++++++++++++++- src/mechs/sha/index.ts | 4 ++++ src/mechs/sha/sha3_256.ts | 12 ++++++++++++ src/mechs/sha/sha3_384.ts | 12 ++++++++++++ src/mechs/sha/sha3_512.ts | 12 ++++++++++++ src/subtle.ts | 13 +++++++++++++ test/crypto.ts | 16 ++++++++++++++++ 13 files changed, 129 insertions(+), 7 deletions(-) create mode 100644 src/mechs/sha/sha3_256.ts create mode 100644 src/mechs/sha/sha3_384.ts create mode 100644 src/mechs/sha/sha3_512.ts diff --git a/src/mechs/ec/crypto.ts b/src/mechs/ec/crypto.ts index 0a7721e..f05d501 100644 --- a/src/mechs/ec/crypto.ts +++ b/src/mechs/ec/crypto.ts @@ -4,6 +4,7 @@ import { JsonParser, JsonSerializer } from "@peculiar/json-schema"; import {BufferSourceConverter} from "pvtsutils"; import * as core from "webcrypto-core"; import { CryptoKey } from "../../keys"; +import { ShaCrypto } from "../sha"; import { getOidByNamedCurve } from "./helper"; import { EcPrivateKey } from "./private_key"; import { EcPublicKey } from "./public_key"; @@ -48,7 +49,7 @@ export class EcCrypto { } public static async sign(algorithm: EcdsaParams, key: EcPrivateKey, data: Uint8Array): Promise { - const cryptoAlg = (algorithm.hash as Algorithm).name.replace("-", ""); + const cryptoAlg = ShaCrypto.getAlgorithmName(algorithm.hash as Algorithm); const signer = crypto.createSign(cryptoAlg); 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 { - const cryptoAlg = (algorithm.hash as Algorithm).name.replace("-", ""); + const cryptoAlg = ShaCrypto.getAlgorithmName(algorithm.hash as Algorithm); const signer = crypto.createVerify(cryptoAlg); signer.update(Buffer.from(data)); diff --git a/src/mechs/ec/ec_dsa.ts b/src/mechs/ec/ec_dsa.ts index eec7798..fc88ab9 100644 --- a/src/mechs/ec/ec_dsa.ts +++ b/src/mechs/ec/ec_dsa.ts @@ -8,6 +8,11 @@ export class EcdsaProvider extends core.EcdsaProvider { 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 { const keys = await EcCrypto.generateKey( { diff --git a/src/mechs/hmac/hmac.ts b/src/mechs/hmac/hmac.ts index 15d1044..1421af4 100644 --- a/src/mechs/hmac/hmac.ts +++ b/src/mechs/hmac/hmac.ts @@ -1,6 +1,7 @@ import crypto from "crypto"; import { JsonParser, JsonSerializer } from "@peculiar/json-schema"; import * as core from "webcrypto-core"; +import { ShaCrypto } from "../sha"; import { setCryptoKey, getCryptoKey } from "../storage"; 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 { - const hash = key.algorithm.hash.name.replace("-", ""); - const hmac = crypto.createHmac(hash, getCryptoKey(key).data) + const cryptoAlg = ShaCrypto.getAlgorithmName(key.algorithm.hash); + const hmac = crypto.createHmac(cryptoAlg, getCryptoKey(key).data) .update(Buffer.from(data)).digest(); return new Uint8Array(hmac).buffer; } public override async onVerify(algorithm: Algorithm, key: HmacCryptoKey, signature: ArrayBuffer, data: ArrayBuffer): Promise { - const hash = key.algorithm.hash.name.replace("-", ""); - const hmac = crypto.createHmac(hash, getCryptoKey(key).data) + const cryptoAlg = ShaCrypto.getAlgorithmName(key.algorithm.hash); + const hmac = crypto.createHmac(cryptoAlg, getCryptoKey(key).data) .update(Buffer.from(data)).digest(); return hmac.compare(Buffer.from(signature)) === 0; diff --git a/src/mechs/rsa/crypto.ts b/src/mechs/rsa/crypto.ts index 807ac26..a2fa260 100644 --- a/src/mechs/rsa/crypto.ts +++ b/src/mechs/rsa/crypto.ts @@ -181,6 +181,12 @@ export class RsaCrypto { return "RSA-SHA384"; case "SHA-512": 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: throw new core.OperationError("algorithm.hash: Is not recognized"); } diff --git a/src/mechs/rsa/rsa_pss.ts b/src/mechs/rsa/rsa_pss.ts index 56a3bfb..12ddb0d 100644 --- a/src/mechs/rsa/rsa_pss.ts +++ b/src/mechs/rsa/rsa_pss.ts @@ -6,6 +6,11 @@ import { RsaPublicKey } from "./public_key"; 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 { const keys = await RsaCrypto.generateKey( { diff --git a/src/mechs/rsa/rsa_ssa.ts b/src/mechs/rsa/rsa_ssa.ts index 18bdf6e..3b6bb98 100644 --- a/src/mechs/rsa/rsa_ssa.ts +++ b/src/mechs/rsa/rsa_ssa.ts @@ -6,6 +6,11 @@ import { RsaPublicKey } from "./public_key"; 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 { const keys = await RsaCrypto.generateKey( { diff --git a/src/mechs/sha/crypto.ts b/src/mechs/sha/crypto.ts index e0bb1be..8cc13d8 100644 --- a/src/mechs/sha/crypto.ts +++ b/src/mechs/sha/crypto.ts @@ -12,18 +12,48 @@ export class ShaCrypto { case "SHA-1": return 160; case "SHA-256": + case "SHA3-256": return 256; case "SHA-384": + case "SHA3-384": return 384; case "SHA-512": + case "SHA3-512": return 512; default: 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) { - const hash = crypto.createHash(algorithm.name.replace("-", "")) + const hashAlg = this.getAlgorithmName(algorithm); + const hash = crypto.createHash(hashAlg) .update(Buffer.from(data)).digest(); return new Uint8Array(hash).buffer; } diff --git a/src/mechs/sha/index.ts b/src/mechs/sha/index.ts index f85391e..b8725bf 100644 --- a/src/mechs/sha/index.ts +++ b/src/mechs/sha/index.ts @@ -1,4 +1,8 @@ +export * from "./crypto"; export * from "./sha_1"; export * from "./sha_256"; export * from "./sha_384"; export * from "./sha_512"; +export * from "./sha3_256"; +export * from "./sha3_384"; +export * from "./sha3_512"; diff --git a/src/mechs/sha/sha3_256.ts b/src/mechs/sha/sha3_256.ts new file mode 100644 index 0000000..907ec60 --- /dev/null +++ b/src/mechs/sha/sha3_256.ts @@ -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 { + return ShaCrypto.digest(algorithm, data); + } + +} diff --git a/src/mechs/sha/sha3_384.ts b/src/mechs/sha/sha3_384.ts new file mode 100644 index 0000000..169a605 --- /dev/null +++ b/src/mechs/sha/sha3_384.ts @@ -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 { + return ShaCrypto.digest(algorithm, data); + } + +} diff --git a/src/mechs/sha/sha3_512.ts b/src/mechs/sha/sha3_512.ts new file mode 100644 index 0000000..8b10713 --- /dev/null +++ b/src/mechs/sha/sha3_512.ts @@ -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 { + return ShaCrypto.digest(algorithm, data); + } + +} diff --git a/src/subtle.ts b/src/subtle.ts index a4a91f3..47141c5 100644 --- a/src/subtle.ts +++ b/src/subtle.ts @@ -1,3 +1,4 @@ +import * as crypto from "crypto"; import * as process from "process"; import * as core from "webcrypto-core"; import { @@ -12,6 +13,7 @@ import { RsaEsProvider, RsaOaepProvider, RsaPssProvider, RsaSsaProvider, Sha1Provider, Sha256Provider, Sha384Provider, Sha512Provider, Shake128Provider, Shake256Provider, + Sha3256Provider, Sha3384Provider, Sha3512Provider, } from "./mechs"; export class SubtleCrypto extends core.SubtleCrypto { @@ -71,6 +73,17 @@ export class SubtleCrypto extends core.SubtleCrypto { //#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) { //#region EdDSA this.providers.set(new EdDsaProvider()); diff --git a/test/crypto.ts b/test/crypto.ts index 3efd478..1f2106c 100644 --- a/test/crypto.ts +++ b/test/crypto.ts @@ -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"); + }); + }); + });