Skip to content

Commit

Permalink
feat: implement SHA3 256/384/512
Browse files Browse the repository at this point in the history
  • Loading branch information
microshine committed May 12, 2022
1 parent 75d4f9a commit 8b1347d
Show file tree
Hide file tree
Showing 13 changed files with 129 additions and 7 deletions.
5 changes: 3 additions & 2 deletions src/mechs/ec/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -48,7 +49,7 @@ export class EcCrypto {
}

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);
signer.update(Buffer.from(data));

Expand All @@ -68,7 +69,7 @@ export class EcCrypto {
}

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);
signer.update(Buffer.from(data));

Expand Down
5 changes: 5 additions & 0 deletions src/mechs/ec/ec_dsa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<CryptoKeyPair> {
const keys = await EcCrypto.generateKey(
{
Expand Down
9 changes: 5 additions & 4 deletions src/mechs/hmac/hmac.ts
Original file line number Diff line number Diff line change
@@ -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";

Expand All @@ -22,16 +23,16 @@ export class HmacProvider extends core.HmacProvider {
}

public override async onSign(algorithm: Algorithm, key: HmacCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
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<boolean> {
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;
Expand Down
6 changes: 6 additions & 0 deletions src/mechs/rsa/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down
5 changes: 5 additions & 0 deletions src/mechs/rsa/rsa_pss.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<CryptoKeyPair> {
const keys = await RsaCrypto.generateKey(
{
Expand Down
5 changes: 5 additions & 0 deletions src/mechs/rsa/rsa_ssa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<globalThis.CryptoKeyPair> {
const keys = await RsaCrypto.generateKey(
{
Expand Down
32 changes: 31 additions & 1 deletion src/mechs/sha/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
4 changes: 4 additions & 0 deletions src/mechs/sha/index.ts
Original file line number Diff line number Diff line change
@@ -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";
12 changes: 12 additions & 0 deletions src/mechs/sha/sha3_256.ts
Original file line number Diff line number Diff line change
@@ -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 changes: 12 additions & 0 deletions src/mechs/sha/sha3_384.ts
Original file line number Diff line number Diff line change
@@ -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 changes: 12 additions & 0 deletions src/mechs/sha/sha3_512.ts
Original file line number Diff line number Diff line change
@@ -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);
}

}
13 changes: 13 additions & 0 deletions src/subtle.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as crypto from "crypto";
import * as process from "process";
import * as core from "webcrypto-core";
import {
Expand All @@ -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 {
Expand Down Expand Up @@ -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());
Expand Down
16 changes: 16 additions & 0 deletions test/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
});
});

});

0 comments on commit 8b1347d

Please sign in to comment.