Compare commits

...

51 Commits

Author SHA1 Message Date
Derrick Hammer 9e7cff20ff
*add build 2023-04-04 06:22:33 -04:00
Derrick Hammer 337743ed7f
*prettier format 2023-04-04 06:20:37 -04:00
Derrick Hammer de25aa8863
*add prettier 2023-04-04 06:20:06 -04:00
Derrick Hammer f50bab4814
*add buffer import 2023-04-04 06:19:16 -04:00
github-actions 49f7b85032 chore(release): 1.4.3 2023-03-24 12:32:21 +00:00
Miroshin Stepan 94ae0d3b43
Merge pull request #65 from PeculiarVentures/fix-type-declaration
fix: type declaration for Crypto
2023-03-24 13:31:54 +01:00
microshine 335c2cb452 fix: type declaration for Crypto 2023-03-24 13:16:31 +01:00
github-actions 3e22e86bef chore(release): 1.4.2 2023-03-21 19:13:32 +00:00
Miroshin Stepan 07faa0f1b0
Merge pull request #63 from PeculiarVentures/fix-publish
fix: publish
2023-03-21 20:12:56 +01:00
microshine e2f61b8b56 fix: publish 2023-03-21 20:11:39 +01:00
Miroshin Stepan c74cf09150
Merge pull request #62 from PeculiarVentures/update-dependencies
Update dependencies
2023-03-21 20:08:00 +01:00
microshine 159f0df626 ci: add node v18 2023-03-21 20:06:16 +01:00
microshine 1ebf9006e6 fix: disable DES-CBC for Node v18 2023-03-21 20:04:52 +01:00
microshine 057b7c481f style: fix lint errors 2023-03-21 19:50:09 +01:00
microshine 1dde3c03e9 ci: add global dependencies for code coverage 2023-03-21 19:49:33 +01:00
microshine c9a5e4ec25 chore(deps): update dependencies 2023-03-21 19:48:53 +01:00
Miroshin Stepan 80de476cf7
Merge pull request #58 from PeculiarVentures/update-deps
Update dependencies
2022-11-02 13:29:48 +03:00
microshine d0ea632eea style(lint): fix lint errors 2022-11-02 13:25:33 +03:00
microshine 5175704e8e chore(config): update rollup script 2022-11-02 13:24:33 +03:00
microshine 17e4a9d7d8 chore(deps): update dependencies 2022-11-02 13:23:37 +03:00
Miroshin Stepan a356531929
Merge pull request #57 from panva/fix-null-ecdh
fix: ECDH with null length
2022-11-02 13:05:15 +03:00
Filip Skokan 48f5a8c19d fix: ECDH with null length 2022-11-01 13:07:26 +01:00
github-actions 6b304aedf4 chore(release): 1.4.0 2022-05-12 12:00:53 +00:00
Miroshin Stepan 1255b39324
Merge pull request #52 from PeculiarVentures:sha3
SHA3
2022-05-12 15:00:18 +03:00
microshine b580035b26 ci: remove node 12.x
- it's not supported in latest mocha
2022-05-12 14:29:00 +03:00
microshine 4bc0ac04b6 style: format file 2022-05-12 14:27:25 +03:00
microshine e8c3ab3adc chore: update dependencies 2022-05-12 14:27:11 +03:00
microshine 8b1347df43 feat: implement SHA3 256/384/512 2022-05-12 13:58:48 +03:00
github-actions 75d4f9a453 chore(release): 1.3.3 2022-03-25 07:42:10 +00:00
Miroshin Stepan 7fd451bc80
Merge pull request #48 from PeculiarVentures/fix-types-web
Remove @types/web
2022-03-25 10:41:37 +03:00
microshine a6eefa34dc fix: remove @types/web 2022-03-25 10:37:44 +03:00
github-actions becdb5e25b chore(release): 1.3.2 2022-03-07 22:00:33 +00:00
Miroshin Stepan 3cb260759f
Merge pull request #47 from PeculiarVentures/develop
Update dependencies
2022-03-08 01:00:02 +03:00
Miroshin Stepan 78dda15c59
Merge pull request #46 from miguel-a-calles-mba/master
Dependency updates
2022-03-08 00:58:13 +03:00
Miguel A. Calles MBA de392b458e Dependency updates
Updated outdated deps and the webcrypto-core that fixes a bug with typescript and rush.
2022-03-07 10:44:02 -08:00
github-actions b6008797e9 chore(release): 1.3.1 2022-03-02 18:41:33 +00:00
Miroshin Stepan 8d65733a84
Merge pull request #45 from PeculiarVentures:fix-type-error
Fix type error
2022-03-02 21:40:57 +03:00
microshine 865ff0caa2 ci: update actions 2022-03-02 21:38:12 +03:00
microshine 7a3520e723 refactor: use TS override option 2022-03-02 21:35:31 +03:00
microshine b98aecb553 chore: Update lint config 2022-03-02 21:31:22 +03:00
microshine 85dc108ca6 chore: Update dependencies 2022-03-02 21:31:02 +03:00
github-actions 88c2bf23de chore(release): 1.3.0 2022-02-24 20:52:20 +00:00
Miroshin Stepan 7221414fbb
Merge pull request #43 from PeculiarVentures:shake
Support shake128 and shake256 mechanisms
2022-02-24 23:48:44 +03:00
microshine cd5a451ba5 feat: Support shake128 adn shake256 mechanisms 2022-02-24 23:46:29 +03:00
microshine 942d309225 ci: add publish action 2022-02-24 23:39:53 +03:00
microshine 3c7559a086 chore: Update dependencies 2022-02-24 23:39:41 +03:00
microshine e98c5ba843 fetch: Support shake128 and shake256 mechanisms 2022-02-24 23:37:40 +03:00
microshine e708ea5e9c chore: Update dependencies 2021-12-07 18:45:21 +03:00
microshine e14e507276 1.2.3 2021-11-24 12:22:18 +03:00
microshine a8a6d06461 test: Add RSA 3072bits test 2021-11-24 12:17:47 +03:00
microshine 40b1911433 chore: Update dependencies 2021-11-24 12:12:30 +03:00
73 changed files with 9110 additions and 9459 deletions

View File

@ -11,6 +11,9 @@
"plugin:import/warnings",
"plugin:import/typescript"
],
"ignorePatterns": [
"build/**/*"
],
"rules": {
"@typescript-eslint/ban-ts-ignore": 0,
"@typescript-eslint/camelcase": 0,

103
.github/workflows/publish.yml vendored Normal file
View File

@ -0,0 +1,103 @@
name: publish
on:
pull_request:
branches:
- master
types: [closed]
env:
CI: true
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
jobs:
publish:
runs-on: ubuntu-latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Pull Request Merged
if: github.event.pull_request.merged == false
run: |
echo 'The pull request has not been merged'
exit 1
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set git config
run: |
git config user.name github-actions
git config user.email github-actions@github.com
- name: Use Node.js
uses: actions/setup-node@v2
with:
node-version: 16.x
registry-url: "https://registry.npmjs.org"
- name: Setup .npmrc
shell: bash
run: |
npm set //registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN
env:
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
- name: Ensure access
shell: bash
run: npm whoami --registry https://registry.npmjs.org/
env:
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
- name: Install global dependencies
run: npm i standard-version yarn -g
- name: Install dependencies
run: yarn
- name: Get Prev Version
shell: bash -ex {0}
run: |
PREV_VERSION=$(node -p 'require("./package.json").version')
echo "::set-env name=PREV_VERSION::${PREV_VERSION}"
- name: Bump version
run: |
standard-version
- name: Get Current Version
shell: bash -ex {0}
run: |
CURRENT_VERSION=$(node -p 'require("./package.json").version')
echo "::set-env name=CURRENT_VERSION::${CURRENT_VERSION}"
- name: Publish
if: env.PREV_VERSION != env.CURRENT_VERSION
run: |
npm run build
npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
- name: Push changes
uses: ad-m/github-push-action@v0.6.0
if: env.PREV_VERSION != env.CURRENT_VERSION
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
branch: ${{ github.ref }}
tags: true
- name: Create comment
uses: actions/github-script@0.8.0
if: env.PREV_VERSION != env.CURRENT_VERSION
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
github.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: 'NPM package v${{ env.CURRENT_VERSION }} has been published 🎉'
})

View File

@ -1,6 +1,14 @@
name: test
on: [push, pull_request]
on:
push:
branches:
- "*"
- "!master"
pull_request:
branches:
- "*"
- "!master"
jobs:
build:
@ -8,7 +16,7 @@ jobs:
strategy:
matrix:
node-version: [12.x, 14.x, 16.x]
node-version: [14.x, 16.x, 18.x]
steps:
- uses: actions/checkout@v2
@ -24,14 +32,17 @@ jobs:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Install global dependencies
run: npm i yarn nyc coveralls -g
- name: Install dependencies
run: npm install
run: yarn
- name: Run linter
run: npm run lint

44
CHANGELOG.md Normal file
View File

@ -0,0 +1,44 @@
# Changelog
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
### [1.4.3](https://github.com/PeculiarVentures/webcrypto/compare/v1.4.2...v1.4.3) (2023-03-24)
### Bug Fixes
* type declaration for Crypto ([335c2cb](https://github.com/PeculiarVentures/webcrypto/commit/335c2cb45236a4832b4b5cccb869f19f458bfc2b))
### [1.4.2](https://github.com/PeculiarVentures/webcrypto/compare/v1.4.0...v1.4.2) (2023-03-21)
### Bug Fixes
* disable DES-CBC for Node v18 ([1ebf900](https://github.com/PeculiarVentures/webcrypto/commit/1ebf9006e67102b16aada2e54d5a32419d8cc3b8))
* ECDH with null length ([48f5a8c](https://github.com/PeculiarVentures/webcrypto/commit/48f5a8c19d81732a89b897fad0e6ac0e084c6333))
* publish ([e2f61b8](https://github.com/PeculiarVentures/webcrypto/commit/e2f61b8b5619767a4bd82d91631a4a15e4e5de92))
## [1.4.0](https://github.com/PeculiarVentures/webcrypto/compare/v1.3.3...v1.4.0) (2022-05-12)
### Features
* implement SHA3 256/384/512 ([8b1347d](https://github.com/PeculiarVentures/webcrypto/commit/8b1347df434ba1d8d973e0c08d61c0dc54f38432))
### [1.3.3](https://github.com/PeculiarVentures/webcrypto/compare/v1.3.2...v1.3.3) (2022-03-25)
### Bug Fixes
* remove @types/web ([a6eefa3](https://github.com/PeculiarVentures/webcrypto/commit/a6eefa34dcbcfe5ee59ff09d8c9b7273242b2ffe))
### [1.3.2](https://github.com/PeculiarVentures/webcrypto/compare/v1.3.1...v1.3.2) (2022-03-07)
### [1.3.1](https://github.com/PeculiarVentures/webcrypto/compare/v1.3.0...v1.3.1) (2022-03-02)
## [1.3.0](https://github.com/PeculiarVentures/webcrypto/compare/v1.2.3...v1.3.0) (2022-02-24)
### Features
* Support shake128 adn shake256 mechanisms ([cd5a451](https://github.com/PeculiarVentures/webcrypto/commit/cd5a451ba59618c736ed87dde1c68079bf9d3450))

View File

@ -55,6 +55,8 @@ npm install @peculiar/webcrypto
| PBKDF2 | | | X | | | | X |
| DES-CBC<sup>2</sup>| X | | X | | X | X | |
| DES-EDE3-CBC<sup>2</sup>| X | | X | | X | X | |
| shake128<sup>2</sup>| | X | | | | | |
| shake256<sup>2</sup>| | X | | | | | |
<sup>1</sup> Mechanism supports extended list of named curves `P-256`, `P-384`, `P-521`, `K-256`,
`brainpoolP160r1`, `brainpoolP160t1`, `brainpoolP192r1`, `brainpoolP192t1`, `brainpoolP224r1`, `brainpoolP224t1`, `brainpoolP256r1`, `brainpoolP256t1`, `brainpoolP320r1`, `brainpoolP320t1`, `brainpoolP384r1`, `brainpoolP384t1`, `brainpoolP512r1`, and `brainpoolP512t1`

2353
build/webcrypto.es.js Normal file

File diff suppressed because it is too large Load Diff

2377
build/webcrypto.js Normal file

File diff suppressed because it is too large Load Diff

1
index.d.ts vendored
View File

@ -1,6 +1,7 @@
export declare class Crypto implements globalThis.Crypto {
public subtle: SubtleCrypto;
public getRandomValues<T extends ArrayBufferView | null>(array: T): T;
randomUUID(): `${string}-${string}-${string}-${string}-${string}`;
}
export declare class CryptoKey implements globalThis.CryptoKey {

8763
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "@peculiar/webcrypto",
"version": "1.2.2",
"version": "1.4.3",
"description": "A WebCrypto Polyfill for NodeJS",
"repository": {
"type": "git",
@ -17,21 +17,13 @@
"types": "index.d.ts",
"scripts": {
"test": "mocha",
"prepare": "npm run build",
"coverage": "nyc npm test",
"coveralls": "nyc report --reporter=text-lcov | coveralls",
"build": "npm run build:module",
"build": "rollup -c",
"clear": "rimraf build/*",
"rebuild": "npm run clear && npm run build",
"build:module": "rollup -c",
"lint": "eslint . --ext .ts",
"lint:fix": "eslint --fix . --ext .ts",
"prepub": "npm run lint && npm run rebuild",
"pub": "npm version patch && npm publish",
"postpub": "git push && git push --tags origin master",
"prepub:next": "npm run lint && npm run rebuild",
"pub:next": "npm version prerelease --preid=next && npm publish --tag next",
"postpub:next": "git push"
"lint:fix": "eslint --fix . --ext .ts"
},
"keywords": [
"webcrypto",
@ -47,7 +39,9 @@
"x25519",
"ed25519",
"x448",
"ed448"
"ed448",
"shake128",
"shake256"
],
"author": "PeculiarVentures",
"contributors": [
@ -58,30 +52,29 @@
"url": "https://github.com/PeculiarVentures/webcrypto/issues"
},
"homepage": "https://github.com/PeculiarVentures/webcrypto#readme",
"banner": "// Copyright (c) 2021, Peculiar Ventures, All rights reserved.",
"devDependencies": {
"@peculiar/webcrypto-test": "^1.0.7",
"@types/mocha": "^9.0.0",
"@types/node": "^16.11.4",
"@typescript-eslint/eslint-plugin": "^5.2.0",
"@typescript-eslint/parser": "^5.2.0",
"coveralls": "^3.1.1",
"eslint": "^8.1.0",
"eslint-plugin-import": "^2.25.2",
"mocha": "^9.1.3",
"nyc": "^15.1.0",
"rimraf": "^3.0.2",
"rollup": "^2.58.1",
"rollup-plugin-typescript2": "^0.30.0",
"ts-node": "^10.4.0",
"typescript": "^4.4.4"
"@types/mocha": "^10.0.1",
"@types/node": "^18.15.5",
"@typescript-eslint/eslint-plugin": "^5.56.0",
"@typescript-eslint/parser": "^5.56.0",
"eslint": "^8.36.0",
"eslint-plugin-import": "^2.27.5",
"mocha": "^10.2.0",
"prettier": "^2.8.7",
"rimraf": "^4.4.0",
"rollup": "^3.20.0",
"rollup-plugin-typescript2": "^0.34.1",
"ts-node": "^10.9.1",
"typescript": "^5.0.2"
},
"dependencies": {
"@peculiar/asn1-schema": "^2.0.38",
"@peculiar/asn1-schema": "^2.3.6",
"@peculiar/json-schema": "^1.1.12",
"pvtsutils": "^1.2.1",
"tslib": "^2.3.1",
"webcrypto-core": "^1.3.0"
"buffer": "^6.0.3",
"pvtsutils": "^1.3.2",
"tslib": "^2.5.0",
"webcrypto-core": "^1.7.7"
},
"nyc": {
"extension": [

View File

@ -1,39 +0,0 @@
import typescript from "rollup-plugin-typescript2";
import pkg from "./package.json";
const banner = [
"/**",
" * Copyright (c) 2020 Peculiar Ventures, LLC",
" */",
"",
].join("\n");
const input = "src/index.ts";
const external = Object.keys(pkg.dependencies);
export default {
input,
plugins: [
typescript({
check: true,
clean: true,
tsconfigOverride: {
compilerOptions: {
module: "ES2015",
}
}
}),
],
external: ["crypto", "process", ...external],
output: [
{
banner,
file: pkg.main,
format: "cjs",
},
{
banner,
file: pkg.module,
format: "es",
},
],
};

45
rollup.config.mjs Normal file
View File

@ -0,0 +1,45 @@
import typescript from "rollup-plugin-typescript2";
import pkg from "./package.json" assert { type: "json" };
const banner = [
"/*!",
" Copyright (c) Peculiar Ventures, LLC",
"*/",
"",
].join("\n");
const input = "src/index.ts";
const external = [
...["crypto", "process"],
...Object.keys(pkg.dependencies || {}),
];
export default [
{
input,
plugins: [
typescript({
check: true,
clean: true,
tsconfigOverride: {
compilerOptions: {
module: "ES2015",
removeComments: true,
}
}
}),
],
external: [...external],
output: [
{
banner,
file: pkg.main,
format: "cjs",
},
{
banner,
file: pkg.module,
format: "es",
},
],
},
];

View File

@ -1,3 +1,4 @@
import { Buffer } from "buffer";
import { IJsonConverter } from "@peculiar/json-schema";
import { Convert } from "pvtsutils";

View File

@ -1,18 +1,23 @@
import { Buffer } from "buffer";
import crypto from "crypto";
import * as core from "webcrypto-core";
import { SubtleCrypto } from "./subtle";
export class Crypto extends core.Crypto {
public subtle = new SubtleCrypto();
public getRandomValues<T extends ArrayBufferView | null>(array: T): T {
if (!ArrayBuffer.isView(array)) {
throw new TypeError("Failed to execute 'getRandomValues' on 'Crypto': parameter 1 is not of type 'ArrayBufferView'");
throw new TypeError(
"Failed to execute 'getRandomValues' on 'Crypto': parameter 1 is not of type 'ArrayBufferView'"
);
}
const buffer = Buffer.from(array.buffer, array.byteOffset, array.byteLength);
const buffer = Buffer.from(
array.buffer,
array.byteOffset,
array.byteLength
);
crypto.randomFillSync(buffer);
return array;
}
}

View File

@ -1,8 +1,6 @@
import { CryptoKey } from "./key";
export abstract class AsymmetricKey extends CryptoKey {
public abstract type: "public" | "private";
public abstract override type: "public" | "private";
public pem?: string;
}

View File

@ -1,18 +1,24 @@
import { Buffer } from "buffer";
import { JsonProp, JsonPropTypes } from "@peculiar/json-schema";
import * as core from "webcrypto-core";
export class CryptoKey extends core.CryptoKey {
public data: Buffer = Buffer.alloc(0);
public algorithm: KeyAlgorithm = { name: "" };
public override algorithm: KeyAlgorithm = { name: "" };
@JsonProp({ name: "ext", type: JsonPropTypes.Boolean, optional: true })
public extractable = false;
public override extractable = false;
public type: KeyType = "secret";
public override type: KeyType = "secret";
@JsonProp({ name: "key_ops", type: JsonPropTypes.String, repeated: true, optional: true })
public usages: KeyUsage[] = [];
@JsonProp({
name: "key_ops",
type: JsonPropTypes.String,
repeated: true,
optional: true,
})
public override usages: KeyUsage[] = [];
@JsonProp({ type: JsonPropTypes.String })
protected kty = "oct";

View File

@ -1,8 +1,6 @@
import { CryptoKey } from "./key";
export class SymmetricKey extends CryptoKey {
public readonly kty = "oct";
public readonly type: "secret" = "secret";
public override readonly kty = "oct";
public override readonly type = "secret" as const;
}

View File

@ -1,40 +1,75 @@
import * as core from "webcrypto-core";
import { getCryptoKey, setCryptoKey } from "../storage";
import { AesCrypto } from "./crypto";
import { AesCryptoKey } from "./key";
import { getCryptoKey, setCryptoKey } from "../storage";
export class AesCbcProvider extends core.AesCbcProvider {
public async onGenerateKey(algorithm: AesKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
public async onGenerateKey(
algorithm: AesKeyGenParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKey> {
const key = await AesCrypto.generateKey(
{
name: this.name,
length: algorithm.length,
},
extractable,
keyUsages);
keyUsages
);
return setCryptoKey(key);
}
public async onEncrypt(algorithm: Algorithm, key: AesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return AesCrypto.encrypt(algorithm, getCryptoKey(key) as AesCryptoKey, new Uint8Array(data));
public async onEncrypt(
algorithm: Algorithm,
key: AesCryptoKey,
data: ArrayBuffer
): Promise<ArrayBuffer> {
return AesCrypto.encrypt(
algorithm,
getCryptoKey(key) as AesCryptoKey,
new Uint8Array(data)
);
}
public async onDecrypt(algorithm: Algorithm, key: AesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return AesCrypto.decrypt(algorithm, getCryptoKey(key) as AesCryptoKey, new Uint8Array(data));
public async onDecrypt(
algorithm: Algorithm,
key: AesCryptoKey,
data: ArrayBuffer
): Promise<ArrayBuffer> {
return AesCrypto.decrypt(
algorithm,
getCryptoKey(key) as AesCryptoKey,
new Uint8Array(data)
);
}
public async onExportKey(format: KeyFormat, key: AesCryptoKey): Promise<JsonWebKey | ArrayBuffer> {
public async onExportKey(
format: KeyFormat,
key: AesCryptoKey
): Promise<JsonWebKey | ArrayBuffer> {
return AesCrypto.exportKey(format, getCryptoKey(key) as AesCryptoKey);
}
public async onImportKey(format: KeyFormat, keyData: JsonWebKey | ArrayBuffer, algorithm: Algorithm, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
const key = await AesCrypto.importKey(format, keyData, { name: algorithm.name }, extractable, keyUsages);
public async onImportKey(
format: KeyFormat,
keyData: JsonWebKey | ArrayBuffer,
algorithm: Algorithm,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKey> {
const key = await AesCrypto.importKey(
format,
keyData,
{ name: algorithm.name },
extractable,
keyUsages
);
return setCryptoKey(key);
}
public checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
public override checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
super.checkCryptoKey(key, keyUsage);
if (!(getCryptoKey(key) instanceof AesCryptoKey)) {
throw new TypeError("key: Is not a AesCryptoKey");

View File

@ -1,8 +1,9 @@
import { Buffer } from "buffer";
import * as crypto from "crypto";
import * as core from "webcrypto-core";
import { setCryptoKey, getCryptoKey } from "../storage";
import { AesCrypto } from "./crypto";
import { AesCryptoKey } from "./key";
import { setCryptoKey, getCryptoKey } from "../storage";
/**
* AES-CMAC implementation source code from https://github.com/allan-stewart/node-aes-cmac
@ -90,14 +91,17 @@ function aesCmac(key: Buffer, message: Buffer) {
blockCount = 1;
lastBlockCompleteFlag = false;
} else {
lastBlockCompleteFlag = (message.length % blockSize === 0);
lastBlockCompleteFlag = message.length % blockSize === 0;
}
const lastBlockIndex = blockCount - 1;
if (lastBlockCompleteFlag) {
lastBlock = xor(getMessageBlock(message, lastBlockIndex), subkeys.subkey1);
} else {
lastBlock = xor(getPaddedMessageBlock(message, lastBlockIndex), subkeys.subkey2);
lastBlock = xor(
getPaddedMessageBlock(message, lastBlockIndex),
subkeys.subkey2
);
}
let x = zero;
@ -112,39 +116,67 @@ function aesCmac(key: Buffer, message: Buffer) {
}
export class AesCmacProvider extends core.AesCmacProvider {
public async onGenerateKey(algorithm: AesKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
public async onGenerateKey(
algorithm: AesKeyGenParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKey> {
const key = await AesCrypto.generateKey(
{
name: this.name,
length: algorithm.length,
},
extractable,
keyUsages);
keyUsages
);
return setCryptoKey(key);
}
public async onSign(algorithm: core.AesCmacParams, key: AesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
public async onSign(
algorithm: core.AesCmacParams,
key: AesCryptoKey,
data: ArrayBuffer
): Promise<ArrayBuffer> {
const result = aesCmac(getCryptoKey(key).data, Buffer.from(data));
return new Uint8Array(result).buffer;
}
public async onVerify(algorithm: core.AesCmacParams, key: AesCryptoKey, signature: ArrayBuffer, data: ArrayBuffer): Promise<boolean> {
public async onVerify(
algorithm: core.AesCmacParams,
key: AesCryptoKey,
signature: ArrayBuffer,
data: ArrayBuffer
): Promise<boolean> {
const signature2 = await this.sign(algorithm, key, data);
return Buffer.from(signature).compare(Buffer.from(signature2)) === 0;
}
public async onExportKey(format: KeyFormat, key: AesCryptoKey): Promise<JsonWebKey | ArrayBuffer> {
public async onExportKey(
format: KeyFormat,
key: AesCryptoKey
): Promise<JsonWebKey | ArrayBuffer> {
return AesCrypto.exportKey(format, getCryptoKey(key) as AesCryptoKey);
}
public async onImportKey(format: KeyFormat, keyData: JsonWebKey | ArrayBuffer, algorithm: Algorithm, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
const res = await AesCrypto.importKey(format, keyData, { name: algorithm.name }, extractable, keyUsages);
public async onImportKey(
format: KeyFormat,
keyData: JsonWebKey | ArrayBuffer,
algorithm: Algorithm,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKey> {
const res = await AesCrypto.importKey(
format,
keyData,
{ name: algorithm.name },
extractable,
keyUsages
);
return setCryptoKey(res);
}
public checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
public override checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
super.checkCryptoKey(key, keyUsage);
if (!(getCryptoKey(key) instanceof AesCryptoKey)) {
throw new TypeError("key: Is not a AesCryptoKey");

View File

@ -1,40 +1,75 @@
import * as core from "webcrypto-core";
import { setCryptoKey, getCryptoKey } from "../storage";
import { AesCrypto } from "./crypto";
import { AesCryptoKey } from "./key";
import { setCryptoKey, getCryptoKey } from "../storage";
export class AesCtrProvider extends core.AesCtrProvider {
public async onGenerateKey(algorithm: AesKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
public async onGenerateKey(
algorithm: AesKeyGenParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKey> {
const key = await AesCrypto.generateKey(
{
name: this.name,
length: algorithm.length,
},
extractable,
keyUsages);
keyUsages
);
return setCryptoKey(key);
}
public async onEncrypt(algorithm: AesCtrParams, key: AesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return AesCrypto.encrypt(algorithm, getCryptoKey(key) as AesCryptoKey, new Uint8Array(data));
public async onEncrypt(
algorithm: AesCtrParams,
key: AesCryptoKey,
data: ArrayBuffer
): Promise<ArrayBuffer> {
return AesCrypto.encrypt(
algorithm,
getCryptoKey(key) as AesCryptoKey,
new Uint8Array(data)
);
}
public async onDecrypt(algorithm: AesCtrParams, key: AesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return AesCrypto.decrypt(algorithm, getCryptoKey(key) as AesCryptoKey, new Uint8Array(data));
public async onDecrypt(
algorithm: AesCtrParams,
key: AesCryptoKey,
data: ArrayBuffer
): Promise<ArrayBuffer> {
return AesCrypto.decrypt(
algorithm,
getCryptoKey(key) as AesCryptoKey,
new Uint8Array(data)
);
}
public async onExportKey(format: KeyFormat, key: AesCryptoKey): Promise<JsonWebKey | ArrayBuffer> {
public async onExportKey(
format: KeyFormat,
key: AesCryptoKey
): Promise<JsonWebKey | ArrayBuffer> {
return AesCrypto.exportKey(format, getCryptoKey(key) as AesCryptoKey);
}
public async onImportKey(format: KeyFormat, keyData: JsonWebKey | ArrayBuffer, algorithm: Algorithm, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
const res = await AesCrypto.importKey(format, keyData, { name: algorithm.name }, extractable, keyUsages);
public async onImportKey(
format: KeyFormat,
keyData: JsonWebKey | ArrayBuffer,
algorithm: Algorithm,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKey> {
const res = await AesCrypto.importKey(
format,
keyData,
{ name: algorithm.name },
extractable,
keyUsages
);
return setCryptoKey(res);
}
public checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
public override checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
super.checkCryptoKey(key, keyUsage);
if (!(getCryptoKey(key) instanceof AesCryptoKey)) {
throw new TypeError("key: Is not a AesCryptoKey");

View File

@ -1,40 +1,75 @@
import * as core from "webcrypto-core";
import { setCryptoKey, getCryptoKey } from "../storage";
import { AesCrypto } from "./crypto";
import { AesCryptoKey } from "./key";
import { setCryptoKey, getCryptoKey } from "../storage";
export class AesEcbProvider extends core.AesEcbProvider {
public async onGenerateKey(algorithm: AesKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
public async onGenerateKey(
algorithm: AesKeyGenParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKey> {
const key = await AesCrypto.generateKey(
{
name: this.name,
length: algorithm.length,
},
extractable,
keyUsages);
keyUsages
);
return setCryptoKey(key);
}
public async onEncrypt(algorithm: Algorithm, key: AesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return AesCrypto.encrypt(algorithm, getCryptoKey(key) as AesCryptoKey, new Uint8Array(data));
public async onEncrypt(
algorithm: Algorithm,
key: AesCryptoKey,
data: ArrayBuffer
): Promise<ArrayBuffer> {
return AesCrypto.encrypt(
algorithm,
getCryptoKey(key) as AesCryptoKey,
new Uint8Array(data)
);
}
public async onDecrypt(algorithm: Algorithm, key: AesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return AesCrypto.decrypt(algorithm, getCryptoKey(key) as AesCryptoKey, new Uint8Array(data));
public async onDecrypt(
algorithm: Algorithm,
key: AesCryptoKey,
data: ArrayBuffer
): Promise<ArrayBuffer> {
return AesCrypto.decrypt(
algorithm,
getCryptoKey(key) as AesCryptoKey,
new Uint8Array(data)
);
}
public async onExportKey(format: KeyFormat, key: AesCryptoKey): Promise<JsonWebKey | ArrayBuffer> {
public async onExportKey(
format: KeyFormat,
key: AesCryptoKey
): Promise<JsonWebKey | ArrayBuffer> {
return AesCrypto.exportKey(format, getCryptoKey(key) as AesCryptoKey);
}
public async onImportKey(format: KeyFormat, keyData: JsonWebKey | ArrayBuffer, algorithm: Algorithm, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
const res = await AesCrypto.importKey(format, keyData, { name: algorithm.name }, extractable, keyUsages);
public async onImportKey(
format: KeyFormat,
keyData: JsonWebKey | ArrayBuffer,
algorithm: Algorithm,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKey> {
const res = await AesCrypto.importKey(
format,
keyData,
{ name: algorithm.name },
extractable,
keyUsages
);
return setCryptoKey(res);
}
public checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
public override checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
super.checkCryptoKey(key, keyUsage);
if (!(getCryptoKey(key) instanceof AesCryptoKey)) {
throw new TypeError("key: Is not a AesCryptoKey");

View File

@ -1,40 +1,75 @@
import * as core from "webcrypto-core";
import { setCryptoKey, getCryptoKey } from "../storage";
import { AesCrypto } from "./crypto";
import { AesCryptoKey } from "./key";
import { setCryptoKey, getCryptoKey } from "../storage";
export class AesGcmProvider extends core.AesGcmProvider {
public async onGenerateKey(algorithm: AesKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
public async onGenerateKey(
algorithm: AesKeyGenParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKey> {
const key = await AesCrypto.generateKey(
{
name: this.name,
length: algorithm.length,
},
extractable,
keyUsages);
keyUsages
);
return setCryptoKey(key);
}
public async onEncrypt(algorithm: AesGcmParams, key: AesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return AesCrypto.encrypt(algorithm, getCryptoKey(key) as AesCryptoKey, new Uint8Array(data));
public async onEncrypt(
algorithm: AesGcmParams,
key: AesCryptoKey,
data: ArrayBuffer
): Promise<ArrayBuffer> {
return AesCrypto.encrypt(
algorithm,
getCryptoKey(key) as AesCryptoKey,
new Uint8Array(data)
);
}
public async onDecrypt(algorithm: AesGcmParams, key: AesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return AesCrypto.decrypt(algorithm, getCryptoKey(key) as AesCryptoKey, new Uint8Array(data));
public async onDecrypt(
algorithm: AesGcmParams,
key: AesCryptoKey,
data: ArrayBuffer
): Promise<ArrayBuffer> {
return AesCrypto.decrypt(
algorithm,
getCryptoKey(key) as AesCryptoKey,
new Uint8Array(data)
);
}
public async onExportKey(format: KeyFormat, key: AesCryptoKey): Promise<JsonWebKey | ArrayBuffer> {
public async onExportKey(
format: KeyFormat,
key: AesCryptoKey
): Promise<JsonWebKey | ArrayBuffer> {
return AesCrypto.exportKey(format, getCryptoKey(key) as AesCryptoKey);
}
public async onImportKey(format: KeyFormat, keyData: JsonWebKey | ArrayBuffer, algorithm: Algorithm, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
const res = await AesCrypto.importKey(format, keyData, { name: algorithm.name }, extractable, keyUsages);
public async onImportKey(
format: KeyFormat,
keyData: JsonWebKey | ArrayBuffer,
algorithm: Algorithm,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKey> {
const res = await AesCrypto.importKey(
format,
keyData,
{ name: algorithm.name },
extractable,
keyUsages
);
return setCryptoKey(res);
}
public checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
public override checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
super.checkCryptoKey(key, keyUsage);
if (!(getCryptoKey(key) instanceof AesCryptoKey)) {
throw new TypeError("key: Is not a AesCryptoKey");

View File

@ -1,40 +1,74 @@
import * as core from "webcrypto-core";
import { setCryptoKey, getCryptoKey } from "../storage";
import { AesCrypto } from "./crypto";
import { AesCryptoKey } from "./key";
import { setCryptoKey, getCryptoKey } from "../storage";
export class AesKwProvider extends core.AesKwProvider {
public async onGenerateKey(algorithm: AesKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
public async onGenerateKey(
algorithm: AesKeyGenParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKey> {
const res = await AesCrypto.generateKey(
{
name: this.name,
length: algorithm.length,
},
extractable,
keyUsages,
keyUsages
);
return setCryptoKey(res);
}
public async onExportKey(format: KeyFormat, key: AesCryptoKey): Promise<JsonWebKey | ArrayBuffer> {
public async onExportKey(
format: KeyFormat,
key: AesCryptoKey
): Promise<JsonWebKey | ArrayBuffer> {
return AesCrypto.exportKey(format, getCryptoKey(key) as AesCryptoKey);
}
public async onImportKey(format: KeyFormat, keyData: JsonWebKey | ArrayBuffer, algorithm: Algorithm, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
const res = await AesCrypto.importKey(format, keyData, { name: algorithm.name }, extractable, keyUsages);
public async onImportKey(
format: KeyFormat,
keyData: JsonWebKey | ArrayBuffer,
algorithm: Algorithm,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKey> {
const res = await AesCrypto.importKey(
format,
keyData,
{ name: algorithm.name },
extractable,
keyUsages
);
return setCryptoKey(res);
}
public async onEncrypt(algorithm: Algorithm, key: AesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return AesCrypto.encrypt(algorithm, getCryptoKey(key) as AesCryptoKey, new Uint8Array(data));
public override async onEncrypt(
algorithm: Algorithm,
key: AesCryptoKey,
data: ArrayBuffer
): Promise<ArrayBuffer> {
return AesCrypto.encrypt(
algorithm,
getCryptoKey(key) as AesCryptoKey,
new Uint8Array(data)
);
}
public async onDecrypt(algorithm: Algorithm, key: AesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return AesCrypto.decrypt(algorithm, getCryptoKey(key) as AesCryptoKey, new Uint8Array(data));
public override async onDecrypt(
algorithm: Algorithm,
key: AesCryptoKey,
data: ArrayBuffer
): Promise<ArrayBuffer> {
return AesCrypto.decrypt(
algorithm,
getCryptoKey(key) as AesCryptoKey,
new Uint8Array(data)
);
}
public checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
public override checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
super.checkCryptoKey(key, keyUsage);
if (!(getCryptoKey(key) instanceof AesCryptoKey)) {
throw new TypeError("key: Is not a AesCryptoKey");

View File

@ -1,14 +1,18 @@
import { Buffer } from "buffer";
import crypto, { CipherGCM, DecipherGCM } from "crypto";
import { JsonParser, JsonSerializer } from "@peculiar/json-schema";
import * as core from "webcrypto-core";
import { CryptoKey } from "../../keys";
import { AesCryptoKey } from "./key";
import { CryptoKey } from "../../keys";
export class AesCrypto {
public static AES_KW_IV = Buffer.from("A6A6A6A6A6A6A6A6", "hex");
public static async generateKey(algorithm: AesKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<AesCryptoKey> {
public static async generateKey(
algorithm: AesKeyGenParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<AesCryptoKey> {
const key = new AesCryptoKey();
key.algorithm = algorithm;
key.extractable = extractable;
@ -18,7 +22,10 @@ export class AesCrypto {
return key;
}
public static async exportKey(format: string, key: AesCryptoKey): Promise<JsonWebKey | ArrayBuffer> {
public static async exportKey(
format: string,
key: AesCryptoKey
): Promise<JsonWebKey | ArrayBuffer> {
if (!(key instanceof AesCryptoKey)) {
throw new Error("key: Is not AesCryptoKey");
}
@ -33,7 +40,13 @@ export class AesCrypto {
}
}
public static async importKey(format: string, keyData: JsonWebKey | ArrayBuffer, algorithm: any, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
public static async importKey(
format: string,
keyData: JsonWebKey | ArrayBuffer,
algorithm: any,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKey> {
let key: AesCryptoKey;
switch (format.toLowerCase()) {
@ -66,78 +79,167 @@ export class AesCrypto {
return key;
}
public static async encrypt(algorithm: Algorithm, key: AesCryptoKey, data: Uint8Array): Promise<ArrayBuffer> {
public static async encrypt(
algorithm: Algorithm,
key: AesCryptoKey,
data: Uint8Array
): Promise<ArrayBuffer> {
switch (algorithm.name.toUpperCase()) {
case "AES-CBC":
return this.encryptAesCBC(algorithm as AesCbcParams, key, Buffer.from(data));
return this.encryptAesCBC(
algorithm as AesCbcParams,
key,
Buffer.from(data)
);
case "AES-CTR":
return this.encryptAesCTR(algorithm as AesCtrParams, key, Buffer.from(data));
return this.encryptAesCTR(
algorithm as AesCtrParams,
key,
Buffer.from(data)
);
case "AES-GCM":
return this.encryptAesGCM(algorithm as AesGcmParams, key, Buffer.from(data));
return this.encryptAesGCM(
algorithm as AesGcmParams,
key,
Buffer.from(data)
);
case "AES-KW":
return this.encryptAesKW(algorithm as AesKeyAlgorithm, key, Buffer.from(data));
return this.encryptAesKW(
algorithm as AesKeyAlgorithm,
key,
Buffer.from(data)
);
case "AES-ECB":
return this.encryptAesECB(algorithm as AesKeyAlgorithm, key, Buffer.from(data));
return this.encryptAesECB(
algorithm as AesKeyAlgorithm,
key,
Buffer.from(data)
);
default:
throw new core.OperationError("algorithm: Is not recognized");
}
}
public static async decrypt(algorithm: Algorithm, key: CryptoKey, data: Uint8Array): Promise<ArrayBuffer> {
public static async decrypt(
algorithm: Algorithm,
key: CryptoKey,
data: Uint8Array
): Promise<ArrayBuffer> {
if (!(key instanceof AesCryptoKey)) {
throw new Error("key: Is not AesCryptoKey");
}
switch (algorithm.name.toUpperCase()) {
case "AES-CBC":
return this.decryptAesCBC(algorithm as AesCbcParams, key, Buffer.from(data));
return this.decryptAesCBC(
algorithm as AesCbcParams,
key,
Buffer.from(data)
);
case "AES-CTR":
return this.decryptAesCTR(algorithm as AesCtrParams, key, Buffer.from(data));
return this.decryptAesCTR(
algorithm as AesCtrParams,
key,
Buffer.from(data)
);
case "AES-GCM":
return this.decryptAesGCM(algorithm as AesGcmParams, key, Buffer.from(data));
return this.decryptAesGCM(
algorithm as AesGcmParams,
key,
Buffer.from(data)
);
case "AES-KW":
return this.decryptAesKW(algorithm as AesKeyAlgorithm, key, Buffer.from(data));
return this.decryptAesKW(
algorithm as AesKeyAlgorithm,
key,
Buffer.from(data)
);
case "AES-ECB":
return this.decryptAesECB(algorithm as AesKeyAlgorithm, key, Buffer.from(data));
return this.decryptAesECB(
algorithm as AesKeyAlgorithm,
key,
Buffer.from(data)
);
default:
throw new core.OperationError("algorithm: Is not recognized");
}
}
public static async encryptAesCBC(algorithm: AesCbcParams, key: AesCryptoKey, data: Buffer) {
const cipher = crypto.createCipheriv(`aes-${key.algorithm.length}-cbc`, key.data, new Uint8Array(algorithm.iv as ArrayBuffer));
public static async encryptAesCBC(
algorithm: AesCbcParams,
key: AesCryptoKey,
data: Buffer
) {
const cipher = crypto.createCipheriv(
`aes-${key.algorithm.length}-cbc`,
key.data,
new Uint8Array(algorithm.iv as ArrayBuffer)
);
let enc = cipher.update(data);
enc = Buffer.concat([enc, cipher.final()]);
const res = new Uint8Array(enc).buffer;
return res;
}
public static async decryptAesCBC(algorithm: AesCbcParams, key: AesCryptoKey, data: Buffer) {
const decipher = crypto.createDecipheriv(`aes-${key.algorithm.length}-cbc`, key.data, new Uint8Array(algorithm.iv as ArrayBuffer));
public static async decryptAesCBC(
algorithm: AesCbcParams,
key: AesCryptoKey,
data: Buffer
) {
const decipher = crypto.createDecipheriv(
`aes-${key.algorithm.length}-cbc`,
key.data,
new Uint8Array(algorithm.iv as ArrayBuffer)
);
let dec = decipher.update(data);
dec = Buffer.concat([dec, decipher.final()]);
return new Uint8Array(dec).buffer;
}
public static async encryptAesCTR(algorithm: AesCtrParams, key: AesCryptoKey, data: Buffer) {
const cipher = crypto.createCipheriv(`aes-${key.algorithm.length}-ctr`, key.data, Buffer.from(algorithm.counter as ArrayBuffer));
public static async encryptAesCTR(
algorithm: AesCtrParams,
key: AesCryptoKey,
data: Buffer
) {
const cipher = crypto.createCipheriv(
`aes-${key.algorithm.length}-ctr`,
key.data,
Buffer.from(algorithm.counter as ArrayBuffer)
);
let enc = cipher.update(data);
enc = Buffer.concat([enc, cipher.final()]);
const res = new Uint8Array(enc).buffer;
return res;
}
public static async decryptAesCTR(algorithm: AesCtrParams, key: AesCryptoKey, data: Buffer) {
const decipher = crypto.createDecipheriv(`aes-${key.algorithm.length}-ctr`, key.data, new Uint8Array(algorithm.counter as ArrayBuffer));
public static async decryptAesCTR(
algorithm: AesCtrParams,
key: AesCryptoKey,
data: Buffer
) {
const decipher = crypto.createDecipheriv(
`aes-${key.algorithm.length}-ctr`,
key.data,
new Uint8Array(algorithm.counter as ArrayBuffer)
);
let dec = decipher.update(data);
dec = Buffer.concat([dec, decipher.final()]);
return new Uint8Array(dec).buffer;
}
public static async encryptAesGCM(algorithm: AesGcmParams, key: AesCryptoKey, data: Buffer) {
const cipher = crypto.createCipheriv(`aes-${key.algorithm.length}-gcm`, key.data, Buffer.from(algorithm.iv as ArrayBuffer), {
authTagLength: (algorithm.tagLength || 128) >> 3,
} as any) as CipherGCM; // NodeJs d.ts doesn't support CipherGCMOptions for createCipheriv
public static async encryptAesGCM(
algorithm: AesGcmParams,
key: AesCryptoKey,
data: Buffer
) {
const cipher = crypto.createCipheriv(
`aes-${key.algorithm.length}-gcm`,
key.data,
Buffer.from(algorithm.iv as ArrayBuffer),
{
authTagLength: (algorithm.tagLength || 128) >> 3,
} as any
) as CipherGCM; // NodeJs d.ts doesn't support CipherGCMOptions for createCipheriv
if (algorithm.additionalData) {
cipher.setAAD(Buffer.from(algorithm.additionalData as ArrayBuffer));
}
@ -147,8 +249,16 @@ export class AesCrypto {
return res;
}
public static async decryptAesGCM(algorithm: AesGcmParams, key: AesCryptoKey, data: Buffer) {
const decipher = crypto.createDecipheriv(`aes-${key.algorithm.length}-gcm`, key.data, new Uint8Array(algorithm.iv as ArrayBuffer)) as DecipherGCM;
public static async decryptAesGCM(
algorithm: AesGcmParams,
key: AesCryptoKey,
data: Buffer
) {
const decipher = crypto.createDecipheriv(
`aes-${key.algorithm.length}-gcm`,
key.data,
new Uint8Array(algorithm.iv as ArrayBuffer)
) as DecipherGCM;
const tagLength = (algorithm.tagLength || 128) >> 3;
const enc = data.slice(0, data.length - tagLength);
const tag = data.slice(data.length - tagLength);
@ -161,30 +271,62 @@ export class AesCrypto {
return new Uint8Array(dec).buffer;
}
public static async encryptAesKW(algorithm: Algorithm, key: AesCryptoKey, data: Buffer) {
const cipher = crypto.createCipheriv(`id-aes${key.algorithm.length}-wrap`, key.data, this.AES_KW_IV);
public static async encryptAesKW(
algorithm: Algorithm,
key: AesCryptoKey,
data: Buffer
) {
const cipher = crypto.createCipheriv(
`id-aes${key.algorithm.length}-wrap`,
key.data,
this.AES_KW_IV
);
let enc = cipher.update(data);
enc = Buffer.concat([enc, cipher.final()]);
return new Uint8Array(enc).buffer;
}
public static async decryptAesKW(algorithm: Algorithm, key: AesCryptoKey, data: Buffer) {
const decipher = crypto.createDecipheriv(`id-aes${key.algorithm.length}-wrap`, key.data, this.AES_KW_IV);
public static async decryptAesKW(
algorithm: Algorithm,
key: AesCryptoKey,
data: Buffer
) {
const decipher = crypto.createDecipheriv(
`id-aes${key.algorithm.length}-wrap`,
key.data,
this.AES_KW_IV
);
let dec = decipher.update(data);
dec = Buffer.concat([dec, decipher.final()]);
return new Uint8Array(dec).buffer;
}
public static async encryptAesECB(algorithm: Algorithm, key: AesCryptoKey, data: Buffer) {
const cipher = crypto.createCipheriv(`aes-${key.algorithm.length}-ecb`, key.data, new Uint8Array(0));
public static async encryptAesECB(
algorithm: Algorithm,
key: AesCryptoKey,
data: Buffer
) {
const cipher = crypto.createCipheriv(
`aes-${key.algorithm.length}-ecb`,
key.data,
new Uint8Array(0)
);
let enc = cipher.update(data);
enc = Buffer.concat([enc, cipher.final()]);
const res = new Uint8Array(enc).buffer;
return res;
}
public static async decryptAesECB(algorithm: Algorithm, key: AesCryptoKey, data: Buffer) {
const decipher = crypto.createDecipheriv(`aes-${key.algorithm.length}-ecb`, key.data, new Uint8Array(0));
public static async decryptAesECB(
algorithm: Algorithm,
key: AesCryptoKey,
data: Buffer
) {
const decipher = crypto.createDecipheriv(
`aes-${key.algorithm.length}-ecb`,
key.data,
new Uint8Array(0)
);
let dec = decipher.update(data);
dec = Buffer.concat([dec, decipher.final()]);
return new Uint8Array(dec).buffer;

View File

@ -4,11 +4,10 @@ import { JsonBase64UrlConverter } from "../../converters";
import { SymmetricKey } from "../../keys";
export class AesCryptoKey extends SymmetricKey {
public override algorithm!: AesKeyAlgorithm;
public algorithm!: AesKeyAlgorithm;
@JsonProp({name: "k", converter: JsonBase64UrlConverter})
public data!: Buffer;
@JsonProp({ name: "k", converter: JsonBase64UrlConverter })
public override data!: Buffer;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
@ -31,8 +30,7 @@ export class AesCryptoKey extends SymmetricKey {
}
}
public set alg(value: string) {
public override set alg(value: string) {
// nothing, cause set is needed for json-schema, but is not used by module
}
}

View File

@ -1,13 +1,17 @@
import { Buffer } from "buffer";
import crypto from "crypto";
import { JsonParser, JsonSerializer } from "@peculiar/json-schema";
import * as core from "webcrypto-core";
import { DesParams } from "webcrypto-core";
import { CryptoKey } from "../../keys";
import { DesCryptoKey } from "./key";
import { CryptoKey } from "../../keys";
export class DesCrypto {
public static async generateKey(algorithm: AesKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<DesCryptoKey> {
public static async generateKey(
algorithm: AesKeyGenParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<DesCryptoKey> {
const key = new DesCryptoKey();
key.algorithm = algorithm;
key.extractable = extractable;
@ -17,7 +21,10 @@ export class DesCrypto {
return key;
}
public static async exportKey(format: string, key: CryptoKey): Promise<JsonWebKey | ArrayBuffer> {
public static async exportKey(
format: string,
key: CryptoKey
): Promise<JsonWebKey | ArrayBuffer> {
switch (format.toLowerCase()) {
case "jwk":
return JsonSerializer.toJSON(key);
@ -28,7 +35,13 @@ export class DesCrypto {
}
}
public static async importKey(format: string, keyData: JsonWebKey | ArrayBuffer, algorithm: any, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
public static async importKey(
format: string,
keyData: JsonWebKey | ArrayBuffer,
algorithm: any,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKey> {
let key: DesCryptoKey;
switch (format.toLowerCase()) {
@ -50,7 +63,11 @@ export class DesCrypto {
return key;
}
public static async encrypt(algorithm: DesParams, key: DesCryptoKey, data: Uint8Array): Promise<ArrayBuffer> {
public static async encrypt(
algorithm: DesParams,
key: DesCryptoKey,
data: Uint8Array
): Promise<ArrayBuffer> {
switch (algorithm.name.toUpperCase()) {
case "DES-CBC":
return this.encryptDesCBC(algorithm, key, Buffer.from(data));
@ -61,7 +78,11 @@ export class DesCrypto {
}
}
public static async decrypt(algorithm: DesParams, key: CryptoKey, data: Uint8Array): Promise<ArrayBuffer> {
public static async decrypt(
algorithm: DesParams,
key: CryptoKey,
data: Uint8Array
): Promise<ArrayBuffer> {
if (!(key instanceof DesCryptoKey)) {
throw new Error("key: Is not DesCryptoKey");
}
@ -76,34 +97,65 @@ export class DesCrypto {
}
}
public static async encryptDesCBC(algorithm: DesParams, key: DesCryptoKey, data: Buffer) {
const cipher = crypto.createCipheriv(`des-cbc`, key.data, new Uint8Array(algorithm.iv as ArrayBuffer));
public static async encryptDesCBC(
algorithm: DesParams,
key: DesCryptoKey,
data: Buffer
) {
const cipher = crypto.createCipheriv(
`des-cbc`,
key.data,
new Uint8Array(algorithm.iv as ArrayBuffer)
);
let enc = cipher.update(data);
enc = Buffer.concat([enc, cipher.final()]);
const res = new Uint8Array(enc).buffer;
return res;
}
public static async decryptDesCBC(algorithm: DesParams, key: DesCryptoKey, data: Buffer) {
const decipher = crypto.createDecipheriv(`des-cbc`, key.data, new Uint8Array(algorithm.iv as ArrayBuffer));
public static async decryptDesCBC(
algorithm: DesParams,
key: DesCryptoKey,
data: Buffer
) {
const decipher = crypto.createDecipheriv(
`des-cbc`,
key.data,
new Uint8Array(algorithm.iv as ArrayBuffer)
);
let dec = decipher.update(data);
dec = Buffer.concat([dec, decipher.final()]);
return new Uint8Array(dec).buffer;
}
public static async encryptDesEDE3CBC(algorithm: DesParams, key: DesCryptoKey, data: Buffer) {
const cipher = crypto.createCipheriv(`des-ede3-cbc`, key.data, Buffer.from(algorithm.iv as ArrayBuffer));
public static async encryptDesEDE3CBC(
algorithm: DesParams,
key: DesCryptoKey,
data: Buffer
) {
const cipher = crypto.createCipheriv(
`des-ede3-cbc`,
key.data,
Buffer.from(algorithm.iv as ArrayBuffer)
);
let enc = cipher.update(data);
enc = Buffer.concat([enc, cipher.final()]);
const res = new Uint8Array(enc).buffer;
return res;
}
public static async decryptDesEDE3CBC(algorithm: DesParams, key: DesCryptoKey, data: Buffer) {
const decipher = crypto.createDecipheriv(`des-ede3-cbc`, key.data, new Uint8Array(algorithm.iv as ArrayBuffer));
public static async decryptDesEDE3CBC(
algorithm: DesParams,
key: DesCryptoKey,
data: Buffer
) {
const decipher = crypto.createDecipheriv(
`des-ede3-cbc`,
key.data,
new Uint8Array(algorithm.iv as ArrayBuffer)
);
let dec = decipher.update(data);
dec = Buffer.concat([dec, decipher.final()]);
return new Uint8Array(dec).buffer;
}
}

View File

@ -1,54 +1,88 @@
import * as core from "webcrypto-core";
import { CryptoKey } from "../../keys";
import { setCryptoKey, getCryptoKey } from "../storage";
import { DesCrypto } from "./crypto";
import { DesCryptoKey } from "./key";
import { CryptoKey } from "../../keys";
import { setCryptoKey, getCryptoKey } from "../storage";
export type DesCbcParams = core.DesParams;
export class DesCbcProvider extends core.DesProvider {
public keySizeBits = 64;
public ivSize = 8;
public name = "DES-CBC";
public async onGenerateKey(algorithm: core.DesKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<core.CryptoKey> {
public async onGenerateKey(
algorithm: core.DesKeyGenParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<core.CryptoKey> {
const key = await DesCrypto.generateKey(
{
name: this.name,
length: this.keySizeBits,
},
extractable,
keyUsages);
keyUsages
);
return setCryptoKey(key);
}
public async onEncrypt(algorithm: DesCbcParams, key: DesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return DesCrypto.encrypt(algorithm, getCryptoKey(key) as DesCryptoKey, new Uint8Array(data));
public async onEncrypt(
algorithm: DesCbcParams,
key: DesCryptoKey,
data: ArrayBuffer
): Promise<ArrayBuffer> {
return DesCrypto.encrypt(
algorithm,
getCryptoKey(key) as DesCryptoKey,
new Uint8Array(data)
);
}
public async onDecrypt(algorithm: DesCbcParams, key: DesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return DesCrypto.decrypt(algorithm, getCryptoKey(key) as DesCryptoKey, new Uint8Array(data));
public async onDecrypt(
algorithm: DesCbcParams,
key: DesCryptoKey,
data: ArrayBuffer
): Promise<ArrayBuffer> {
return DesCrypto.decrypt(
algorithm,
getCryptoKey(key) as DesCryptoKey,
new Uint8Array(data)
);
}
public async onExportKey(format: KeyFormat, key: CryptoKey): Promise<JsonWebKey | ArrayBuffer> {
public async onExportKey(
format: KeyFormat,
key: CryptoKey
): Promise<JsonWebKey | ArrayBuffer> {
return DesCrypto.exportKey(format, getCryptoKey(key) as DesCryptoKey);
}
public async onImportKey(format: KeyFormat, keyData: JsonWebKey | ArrayBuffer, algorithm: Algorithm, extractable: boolean, keyUsages: KeyUsage[]): Promise<core.CryptoKey> {
const key = await DesCrypto.importKey(format, keyData, { name: this.name, length: this.keySizeBits }, extractable, keyUsages);
if (key.data.length !== (this.keySizeBits >> 3)) {
public async onImportKey(
format: KeyFormat,
keyData: JsonWebKey | ArrayBuffer,
algorithm: Algorithm,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<core.CryptoKey> {
const key = await DesCrypto.importKey(
format,
keyData,
{ name: this.name, length: this.keySizeBits },
extractable,
keyUsages
);
if (key.data.length !== this.keySizeBits >> 3) {
throw new core.OperationError("keyData: Wrong key size");
}
return setCryptoKey(key);
}
public checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
public override checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
super.checkCryptoKey(key, keyUsage);
if (!(getCryptoKey(key) instanceof DesCryptoKey)) {
throw new TypeError("key: Is not a DesCryptoKey");
}
}
}

View File

@ -1,54 +1,88 @@
import * as core from "webcrypto-core";
import { CryptoKey } from "../../keys";
import { setCryptoKey, getCryptoKey } from "../storage";
import { DesCrypto } from "./crypto";
import { DesCryptoKey } from "./key";
import { CryptoKey } from "../../keys";
import { setCryptoKey, getCryptoKey } from "../storage";
export type DesEde3CbcParams = core.DesParams;
export class DesEde3CbcProvider extends core.DesProvider {
public keySizeBits = 192;
public ivSize = 8;
public name = "DES-EDE3-CBC";
public async onGenerateKey(algorithm: core.DesKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<core.CryptoKey> {
public async onGenerateKey(
algorithm: core.DesKeyGenParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<core.CryptoKey> {
const key = await DesCrypto.generateKey(
{
name: this.name,
length: this.keySizeBits,
},
extractable,
keyUsages);
keyUsages
);
return setCryptoKey(key);
}
public async onEncrypt(algorithm: DesEde3CbcParams, key: DesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return DesCrypto.encrypt(algorithm, getCryptoKey(key) as DesCryptoKey, new Uint8Array(data));
public async onEncrypt(
algorithm: DesEde3CbcParams,
key: DesCryptoKey,
data: ArrayBuffer
): Promise<ArrayBuffer> {
return DesCrypto.encrypt(
algorithm,
getCryptoKey(key) as DesCryptoKey,
new Uint8Array(data)
);
}
public async onDecrypt(algorithm: DesEde3CbcParams, key: DesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return DesCrypto.decrypt(algorithm, getCryptoKey(key) as DesCryptoKey, new Uint8Array(data));
public async onDecrypt(
algorithm: DesEde3CbcParams,
key: DesCryptoKey,
data: ArrayBuffer
): Promise<ArrayBuffer> {
return DesCrypto.decrypt(
algorithm,
getCryptoKey(key) as DesCryptoKey,
new Uint8Array(data)
);
}
public async onExportKey(format: KeyFormat, key: CryptoKey): Promise<JsonWebKey | ArrayBuffer> {
public async onExportKey(
format: KeyFormat,
key: CryptoKey
): Promise<JsonWebKey | ArrayBuffer> {
return DesCrypto.exportKey(format, getCryptoKey(key) as DesCryptoKey);
}
public async onImportKey(format: KeyFormat, keyData: JsonWebKey | ArrayBuffer, algorithm: Algorithm, extractable: boolean, keyUsages: KeyUsage[]): Promise<core.CryptoKey> {
const key = await DesCrypto.importKey(format, keyData, { name: this.name, length: this.keySizeBits }, extractable, keyUsages);
if (key.data.length !== (this.keySizeBits >> 3)) {
public async onImportKey(
format: KeyFormat,
keyData: JsonWebKey | ArrayBuffer,
algorithm: Algorithm,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<core.CryptoKey> {
const key = await DesCrypto.importKey(
format,
keyData,
{ name: this.name, length: this.keySizeBits },
extractable,
keyUsages
);
if (key.data.length !== this.keySizeBits >> 3) {
throw new core.OperationError("keyData: Wrong key size");
}
return setCryptoKey(key);
}
public checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
public override checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
super.checkCryptoKey(key, keyUsage);
if (!(getCryptoKey(key) instanceof DesCryptoKey)) {
throw new TypeError("key: Is not a DesCryptoKey");
}
}
}

View File

@ -4,11 +4,10 @@ import { JsonBase64UrlConverter } from "../../converters";
import { SymmetricKey } from "../../keys";
export class DesCryptoKey extends SymmetricKey {
public override algorithm!: core.DesKeyAlgorithm;
public algorithm!: core.DesKeyAlgorithm;
@JsonProp({name: "k", converter: JsonBase64UrlConverter})
public data!: Buffer;
@JsonProp({ name: "k", converter: JsonBase64UrlConverter })
public override data!: Buffer;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
@ -23,8 +22,7 @@ export class DesCryptoKey extends SymmetricKey {
}
}
public set alg(value: string) {
public override set alg(value: string) {
// nothing, cause set is needed for json-schema, but is not used by module
}
}

View File

@ -1,28 +1,37 @@
import { Buffer } from "buffer";
import crypto from "crypto";
import { AsnParser, AsnSerializer } from "@peculiar/asn1-schema";
import { JsonParser, JsonSerializer } from "@peculiar/json-schema";
import {BufferSourceConverter} from "pvtsutils";
import { BufferSourceConverter } from "pvtsutils";
import * as core from "webcrypto-core";
import { CryptoKey } from "../../keys";
import { getOidByNamedCurve } from "./helper";
import { EcPrivateKey } from "./private_key";
import { EcPublicKey } from "./public_key";
import { CryptoKey } from "../../keys";
import { ShaCrypto } from "../sha";
export class EcCrypto {
public static publicKeyUsages = ["verify"];
public static privateKeyUsages = ["sign", "deriveKey", "deriveBits"];
public static async generateKey(algorithm: EcKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<core.CryptoKeyPair> {
public static async generateKey(
algorithm: EcKeyGenParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKeyPair> {
const privateKey = new EcPrivateKey();
privateKey.algorithm = algorithm;
privateKey.extractable = extractable;
privateKey.usages = keyUsages.filter((usage) => this.privateKeyUsages.indexOf(usage) !== -1);
privateKey.usages = keyUsages.filter(
(usage) => this.privateKeyUsages.indexOf(usage) !== -1
);
const publicKey = new EcPublicKey();
publicKey.algorithm = algorithm;
publicKey.extractable = true;
publicKey.usages = keyUsages.filter((usage) => this.publicKeyUsages.indexOf(usage) !== -1);
publicKey.usages = keyUsages.filter(
(usage) => this.publicKeyUsages.indexOf(usage) !== -1
);
const keys = crypto.generateKeyPairSync("ec", {
namedCurve: this.getOpenSSLNamedCurve(algorithm.namedCurve),
@ -47,13 +56,19 @@ export class EcCrypto {
return res;
}
public static async sign(algorithm: EcdsaParams, key: EcPrivateKey, data: Uint8Array): Promise<ArrayBuffer> {
const cryptoAlg = (algorithm.hash as Algorithm).name.replace("-", "");
public static async sign(
algorithm: EcdsaParams,
key: EcPrivateKey,
data: Uint8Array
): Promise<ArrayBuffer> {
const cryptoAlg = ShaCrypto.getAlgorithmName(algorithm.hash as Algorithm);
const signer = crypto.createSign(cryptoAlg);
signer.update(Buffer.from(data));
if (!key.pem) {
key.pem = `-----BEGIN PRIVATE KEY-----\n${key.data.toString("base64")}\n-----END PRIVATE KEY-----`;
key.pem = `-----BEGIN PRIVATE KEY-----\n${key.data.toString(
"base64"
)}\n-----END PRIVATE KEY-----`;
}
const options = {
key: key.pem,
@ -62,18 +77,28 @@ export class EcCrypto {
const signature = signer.sign(options);
const ecSignature = AsnParser.parse(signature, core.asn1.EcDsaSignature);
const signatureRaw = core.EcUtils.encodeSignature(ecSignature, core.EcCurves.get(key.algorithm.namedCurve).size);
const signatureRaw = core.EcUtils.encodeSignature(
ecSignature,
core.EcCurves.get(key.algorithm.namedCurve).size
);
return signatureRaw.buffer;
}
public static async verify(algorithm: EcdsaParams, key: EcPublicKey, signature: Uint8Array, data: Uint8Array): Promise<boolean> {
const cryptoAlg = (algorithm.hash as Algorithm).name.replace("-", "");
public static async verify(
algorithm: EcdsaParams,
key: EcPublicKey,
signature: Uint8Array,
data: Uint8Array
): Promise<boolean> {
const cryptoAlg = ShaCrypto.getAlgorithmName(algorithm.hash as Algorithm);
const signer = crypto.createVerify(cryptoAlg);
signer.update(Buffer.from(data));
if (!key.pem) {
key.pem = `-----BEGIN PUBLIC KEY-----\n${key.data.toString("base64")}\n-----END PUBLIC KEY-----`;
key.pem = `-----BEGIN PUBLIC KEY-----\n${key.data.toString(
"base64"
)}\n-----END PUBLIC KEY-----`;
}
const options = {
key: key.pem,
@ -81,7 +106,10 @@ export class EcCrypto {
const ecSignature = new core.asn1.EcDsaSignature();
const namedCurve = core.EcCurves.get(key.algorithm.namedCurve);
const signaturePoint = core.EcUtils.decodeSignature(signature, namedCurve.size);
const signaturePoint = core.EcUtils.decodeSignature(
signature,
namedCurve.size
);
ecSignature.r = BufferSourceConverter.toArrayBuffer(signaturePoint.r);
ecSignature.s = BufferSourceConverter.toArrayBuffer(signaturePoint.s);
@ -90,21 +118,43 @@ export class EcCrypto {
return ok;
}
public static async deriveBits(algorithm: EcdhKeyDeriveParams, baseKey: CryptoKey, length: number): Promise<ArrayBuffer> {
const cryptoAlg = this.getOpenSSLNamedCurve((baseKey.algorithm as EcKeyAlgorithm).namedCurve);
public static async deriveBits(
algorithm: EcdhKeyDeriveParams,
baseKey: CryptoKey,
length: number | null
): Promise<ArrayBuffer> {
const cryptoAlg = this.getOpenSSLNamedCurve(
(baseKey.algorithm as EcKeyAlgorithm).namedCurve
);
const ecdh = crypto.createECDH(cryptoAlg);
const asnPrivateKey = AsnParser.parse(baseKey.data, core.asn1.PrivateKeyInfo);
const asnEcPrivateKey = AsnParser.parse(asnPrivateKey.privateKey, core.asn1.EcPrivateKey);
const asnPrivateKey = AsnParser.parse(
baseKey.data,
core.asn1.PrivateKeyInfo
);
const asnEcPrivateKey = AsnParser.parse(
asnPrivateKey.privateKey,
core.asn1.EcPrivateKey
);
ecdh.setPrivateKey(Buffer.from(asnEcPrivateKey.privateKey));
const asnPublicKey = AsnParser.parse((algorithm.public as CryptoKey).data, core.asn1.PublicKeyInfo);
const asnPublicKey = AsnParser.parse(
(algorithm.public as CryptoKey).data,
core.asn1.PublicKeyInfo
);
const bits = ecdh.computeSecret(Buffer.from(asnPublicKey.publicKey));
if (length === null) {
return bits;
}
return new Uint8Array(bits).buffer.slice(0, length >> 3);
}
public static async exportKey(format: KeyFormat, key: CryptoKey): Promise<JsonWebKey | ArrayBuffer> {
public static async exportKey(
format: KeyFormat,
key: CryptoKey
): Promise<JsonWebKey | ArrayBuffer> {
switch (format.toLowerCase()) {
case "jwk":
return JsonSerializer.toJSON(key);
@ -112,24 +162,49 @@ export class EcCrypto {
case "spki":
return new Uint8Array(key.data).buffer;
case "raw": {
const publicKeyInfo = AsnParser.parse(key.data, core.asn1.PublicKeyInfo);
const publicKeyInfo = AsnParser.parse(
key.data,
core.asn1.PublicKeyInfo
);
return publicKeyInfo.publicKey;
}
default:
throw new core.OperationError("format: Must be 'jwk', 'raw', pkcs8' or 'spki'");
throw new core.OperationError(
"format: Must be 'jwk', 'raw', pkcs8' or 'spki'"
);
}
}
public static async importKey(format: KeyFormat, keyData: JsonWebKey | ArrayBuffer, algorithm: EcKeyImportParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
public static async importKey(
format: KeyFormat,
keyData: JsonWebKey | ArrayBuffer,
algorithm: EcKeyImportParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKey> {
switch (format.toLowerCase()) {
case "jwk": {
const jwk = keyData as JsonWebKey;
if (jwk.d) {
const asnKey = JsonParser.fromJSON(keyData, { targetSchema: core.asn1.EcPrivateKey });
return this.importPrivateKey(asnKey, algorithm, extractable, keyUsages);
const asnKey = JsonParser.fromJSON(keyData, {
targetSchema: core.asn1.EcPrivateKey,
});
return this.importPrivateKey(
asnKey,
algorithm,
extractable,
keyUsages
);
} else {
const asnKey = JsonParser.fromJSON(keyData, { targetSchema: core.asn1.EcPublicKey });
return this.importPublicKey(asnKey, algorithm, extractable, keyUsages);
const asnKey = JsonParser.fromJSON(keyData, {
targetSchema: core.asn1.EcPublicKey,
});
return this.importPublicKey(
asnKey,
algorithm,
extractable,
keyUsages
);
}
}
case "raw": {
@ -137,43 +212,75 @@ export class EcCrypto {
return this.importPublicKey(asnKey, algorithm, extractable, keyUsages);
}
case "spki": {
const keyInfo = AsnParser.parse(new Uint8Array(keyData as ArrayBuffer), core.asn1.PublicKeyInfo);
const keyInfo = AsnParser.parse(
new Uint8Array(keyData as ArrayBuffer),
core.asn1.PublicKeyInfo
);
const asnKey = new core.asn1.EcPublicKey(keyInfo.publicKey);
this.assertKeyParameters(keyInfo.publicKeyAlgorithm.parameters, algorithm.namedCurve);
this.assertKeyParameters(
keyInfo.publicKeyAlgorithm.parameters,
algorithm.namedCurve
);
return this.importPublicKey(asnKey, algorithm, extractable, keyUsages);
}
case "pkcs8": {
const keyInfo = AsnParser.parse(new Uint8Array(keyData as ArrayBuffer), core.asn1.PrivateKeyInfo);
const asnKey = AsnParser.parse(keyInfo.privateKey, core.asn1.EcPrivateKey);
this.assertKeyParameters(keyInfo.privateKeyAlgorithm.parameters, algorithm.namedCurve);
const keyInfo = AsnParser.parse(
new Uint8Array(keyData as ArrayBuffer),
core.asn1.PrivateKeyInfo
);
const asnKey = AsnParser.parse(
keyInfo.privateKey,
core.asn1.EcPrivateKey
);
this.assertKeyParameters(
keyInfo.privateKeyAlgorithm.parameters,
algorithm.namedCurve
);
return this.importPrivateKey(asnKey, algorithm, extractable, keyUsages);
}
default:
throw new core.OperationError("format: Must be 'jwk', 'raw', 'pkcs8' or 'spki'");
throw new core.OperationError(
"format: Must be 'jwk', 'raw', 'pkcs8' or 'spki'"
);
}
}
protected static assertKeyParameters(parameters: ArrayBuffer | null | undefined, namedCurve: string) {
protected static assertKeyParameters(
parameters: ArrayBuffer | null | undefined,
namedCurve: string
) {
if (!parameters) {
throw new core.CryptoError("Key info doesn't have required parameters");
}
let namedCurveIdentifier = "";
try {
namedCurveIdentifier = AsnParser.parse(parameters, core.asn1.ObjectIdentifier).value;
namedCurveIdentifier = AsnParser.parse(
parameters,
core.asn1.ObjectIdentifier
).value;
} catch (e) {
throw new core.CryptoError("Cannot read key info parameters");
}
if (getOidByNamedCurve(namedCurve) !== namedCurveIdentifier) {
throw new core.CryptoError("Key info parameter doesn't match to named curve");
throw new core.CryptoError(
"Key info parameter doesn't match to named curve"
);
}
}
protected static async importPrivateKey(asnKey: core.asn1.EcPrivateKey, algorithm: EcKeyImportParams, extractable: boolean, keyUsages: KeyUsage[]) {
protected static async importPrivateKey(
asnKey: core.asn1.EcPrivateKey,
algorithm: EcKeyImportParams,
extractable: boolean,
keyUsages: KeyUsage[]
) {
const keyInfo = new core.asn1.PrivateKeyInfo();
keyInfo.privateKeyAlgorithm.algorithm = "1.2.840.10045.2.1";
keyInfo.privateKeyAlgorithm.parameters = AsnSerializer.serialize(new core.asn1.ObjectIdentifier(getOidByNamedCurve(algorithm.namedCurve)));
keyInfo.privateKeyAlgorithm.parameters = AsnSerializer.serialize(
new core.asn1.ObjectIdentifier(getOidByNamedCurve(algorithm.namedCurve))
);
keyInfo.privateKey = AsnSerializer.serialize(asnKey);
const key = new EcPrivateKey();
@ -186,11 +293,18 @@ export class EcCrypto {
return key;
}
protected static async importPublicKey(asnKey: core.asn1.EcPublicKey, algorithm: EcKeyImportParams, extractable: boolean, keyUsages: KeyUsage[]) {
protected static async importPublicKey(
asnKey: core.asn1.EcPublicKey,
algorithm: EcKeyImportParams,
extractable: boolean,
keyUsages: KeyUsage[]
) {
const keyInfo = new core.asn1.PublicKeyInfo();
keyInfo.publicKeyAlgorithm.algorithm = "1.2.840.10045.2.1";
const namedCurve = getOidByNamedCurve(algorithm.namedCurve);
keyInfo.publicKeyAlgorithm.parameters = AsnSerializer.serialize(new core.asn1.ObjectIdentifier(namedCurve));
keyInfo.publicKeyAlgorithm.parameters = AsnSerializer.serialize(
new core.asn1.ObjectIdentifier(namedCurve)
);
keyInfo.publicKey = asnKey.value;
const key = new EcPublicKey();
@ -217,5 +331,4 @@ export class EcCrypto {
return curve;
}
}
}

View File

@ -1,22 +1,26 @@
import * as core from "webcrypto-core";
import { CryptoKey } from "../../keys";
import { setCryptoKey, getCryptoKey } from "../storage";
import { EcCrypto } from "./crypto";
import { EcPrivateKey } from "./private_key";
import { EcPublicKey } from "./public_key";
import { CryptoKey } from "../../keys";
import { setCryptoKey, getCryptoKey } from "../storage";
export class EcdhProvider extends core.EcdhProvider {
public override namedCurves = core.EcCurves.names;
public namedCurves = core.EcCurves.names;
public async onGenerateKey(algorithm: EcKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<core.CryptoKeyPair> {
public async onGenerateKey(
algorithm: EcKeyGenParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKeyPair> {
const keys = await EcCrypto.generateKey(
{
...algorithm,
name: this.name,
},
extractable,
keyUsages);
keyUsages
);
return {
privateKey: setCryptoKey(keys.privateKey as CryptoKey),
@ -24,26 +28,53 @@ export class EcdhProvider extends core.EcdhProvider {
};
}
public async onExportKey(format: KeyFormat, key: CryptoKey): Promise<JsonWebKey | ArrayBuffer> {
public async onExportKey(
format: KeyFormat,
key: CryptoKey
): Promise<JsonWebKey | ArrayBuffer> {
return EcCrypto.exportKey(format, getCryptoKey(key));
}
public async onImportKey(format: KeyFormat, keyData: JsonWebKey | ArrayBuffer, algorithm: EcKeyImportParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<core.CryptoKey> {
const key = await EcCrypto.importKey(format, keyData, {...algorithm, name: this.name}, extractable, keyUsages);
public async onImportKey(
format: KeyFormat,
keyData: JsonWebKey | ArrayBuffer,
algorithm: EcKeyImportParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<core.CryptoKey> {
const key = await EcCrypto.importKey(
format,
keyData,
{ ...algorithm, name: this.name },
extractable,
keyUsages
);
return setCryptoKey(key);
}
public checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
public override checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
super.checkCryptoKey(key, keyUsage);
const internalKey = getCryptoKey(key);
if (!(internalKey instanceof EcPrivateKey || internalKey instanceof EcPublicKey)) {
if (
!(
internalKey instanceof EcPrivateKey ||
internalKey instanceof EcPublicKey
)
) {
throw new TypeError("key: Is not EC CryptoKey");
}
}
public async onDeriveBits(algorithm: EcdhKeyDeriveParams, baseKey: CryptoKey, length: number): Promise<ArrayBuffer> {
const bits = await EcCrypto.deriveBits({...algorithm, public: getCryptoKey(algorithm.public)}, getCryptoKey(baseKey), length);
public async onDeriveBits(
algorithm: EcdhKeyDeriveParams,
baseKey: CryptoKey,
length: number
): Promise<ArrayBuffer> {
const bits = await EcCrypto.deriveBits(
{ ...algorithm, public: getCryptoKey(algorithm.public) },
getCryptoKey(baseKey),
length
);
return bits;
}
}

View File

@ -1,21 +1,37 @@
import * as core from "webcrypto-core";
import { setCryptoKey, getCryptoKey } from "../storage";
import { EcCrypto } from "./crypto";
import { EcPrivateKey } from "./private_key";
import { EcPublicKey } from "./public_key";
import { setCryptoKey, getCryptoKey } from "../storage";
export class EcdsaProvider extends core.EcdsaProvider {
public override namedCurves = core.EcCurves.names;
public 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<core.CryptoKeyPair> {
public async onGenerateKey(
algorithm: EcKeyGenParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKeyPair> {
const keys = await EcCrypto.generateKey(
{
...algorithm,
name: this.name,
},
extractable,
keyUsages);
keyUsages
);
return {
privateKey: setCryptoKey(keys.privateKey as EcPrivateKey),
@ -23,29 +39,66 @@ export class EcdsaProvider extends core.EcdsaProvider {
};
}
public async onSign(algorithm: EcdsaParams, key: EcPrivateKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return EcCrypto.sign(algorithm, getCryptoKey(key) as EcPrivateKey, new Uint8Array(data));
public async onSign(
algorithm: EcdsaParams,
key: EcPrivateKey,
data: ArrayBuffer
): Promise<ArrayBuffer> {
return EcCrypto.sign(
algorithm,
getCryptoKey(key) as EcPrivateKey,
new Uint8Array(data)
);
}
public async onVerify(algorithm: EcdsaParams, key: EcPublicKey, signature: ArrayBuffer, data: ArrayBuffer): Promise<boolean> {
return EcCrypto.verify(algorithm, getCryptoKey(key) as EcPublicKey, new Uint8Array(signature), new Uint8Array(data));
public async onVerify(
algorithm: EcdsaParams,
key: EcPublicKey,
signature: ArrayBuffer,
data: ArrayBuffer
): Promise<boolean> {
return EcCrypto.verify(
algorithm,
getCryptoKey(key) as EcPublicKey,
new Uint8Array(signature),
new Uint8Array(data)
);
}
public async onExportKey(format: KeyFormat, key: CryptoKey): Promise<JsonWebKey | ArrayBuffer> {
public async onExportKey(
format: KeyFormat,
key: CryptoKey
): Promise<JsonWebKey | ArrayBuffer> {
return EcCrypto.exportKey(format, getCryptoKey(key));
}
public async onImportKey(format: KeyFormat, keyData: JsonWebKey | ArrayBuffer, algorithm: EcKeyImportParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
const key = await EcCrypto.importKey(format, keyData, { ...algorithm, name: this.name }, extractable, keyUsages);
public async onImportKey(
format: KeyFormat,
keyData: JsonWebKey | ArrayBuffer,
algorithm: EcKeyImportParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKey> {
const key = await EcCrypto.importKey(
format,
keyData,
{ ...algorithm, name: this.name },
extractable,
keyUsages
);
return setCryptoKey(key);
}
public checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
public override checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
super.checkCryptoKey(key, keyUsage);
const internalKey = getCryptoKey(key);
if (!(internalKey instanceof EcPrivateKey || internalKey instanceof EcPublicKey)) {
if (
!(
internalKey instanceof EcPrivateKey ||
internalKey instanceof EcPublicKey
)
) {
throw new TypeError("key: Is not EC CryptoKey");
}
}
}

View File

@ -15,40 +15,42 @@ const namedOIDs: { [key: string]: string } = {
"K-256": "1.3.132.0.10",
// brainpool
"brainpoolP160r1": "1.3.36.3.3.2.8.1.1.1",
brainpoolP160r1: "1.3.36.3.3.2.8.1.1.1",
"1.3.36.3.3.2.8.1.1.1": "brainpoolP160r1",
"brainpoolP160t1": "1.3.36.3.3.2.8.1.1.2",
brainpoolP160t1: "1.3.36.3.3.2.8.1.1.2",
"1.3.36.3.3.2.8.1.1.2": "brainpoolP160t1",
"brainpoolP192r1": "1.3.36.3.3.2.8.1.1.3",
brainpoolP192r1: "1.3.36.3.3.2.8.1.1.3",
"1.3.36.3.3.2.8.1.1.3": "brainpoolP192r1",
"brainpoolP192t1": "1.3.36.3.3.2.8.1.1.4",
brainpoolP192t1: "1.3.36.3.3.2.8.1.1.4",
"1.3.36.3.3.2.8.1.1.4": "brainpoolP192t1",
"brainpoolP224r1": "1.3.36.3.3.2.8.1.1.5",
brainpoolP224r1: "1.3.36.3.3.2.8.1.1.5",
"1.3.36.3.3.2.8.1.1.5": "brainpoolP224r1",
"brainpoolP224t1": "1.3.36.3.3.2.8.1.1.6",
brainpoolP224t1: "1.3.36.3.3.2.8.1.1.6",
"1.3.36.3.3.2.8.1.1.6": "brainpoolP224t1",
"brainpoolP256r1": "1.3.36.3.3.2.8.1.1.7",
brainpoolP256r1: "1.3.36.3.3.2.8.1.1.7",
"1.3.36.3.3.2.8.1.1.7": "brainpoolP256r1",
"brainpoolP256t1": "1.3.36.3.3.2.8.1.1.8",
brainpoolP256t1: "1.3.36.3.3.2.8.1.1.8",
"1.3.36.3.3.2.8.1.1.8": "brainpoolP256t1",
"brainpoolP320r1": "1.3.36.3.3.2.8.1.1.9",
brainpoolP320r1: "1.3.36.3.3.2.8.1.1.9",
"1.3.36.3.3.2.8.1.1.9": "brainpoolP320r1",
"brainpoolP320t1": "1.3.36.3.3.2.8.1.1.10",
brainpoolP320t1: "1.3.36.3.3.2.8.1.1.10",
"1.3.36.3.3.2.8.1.1.10": "brainpoolP320t1",
"brainpoolP384r1": "1.3.36.3.3.2.8.1.1.11",
brainpoolP384r1: "1.3.36.3.3.2.8.1.1.11",
"1.3.36.3.3.2.8.1.1.11": "brainpoolP384r1",
"brainpoolP384t1": "1.3.36.3.3.2.8.1.1.12",
brainpoolP384t1: "1.3.36.3.3.2.8.1.1.12",
"1.3.36.3.3.2.8.1.1.12": "brainpoolP384t1",
"brainpoolP512r1": "1.3.36.3.3.2.8.1.1.13",
brainpoolP512r1: "1.3.36.3.3.2.8.1.1.13",
"1.3.36.3.3.2.8.1.1.13": "brainpoolP512r1",
"brainpoolP512t1": "1.3.36.3.3.2.8.1.1.14",
brainpoolP512t1: "1.3.36.3.3.2.8.1.1.14",
"1.3.36.3.3.2.8.1.1.14": "brainpoolP512t1",
};
export function getNamedCurveByOid(oid: string) {
const namedCurve = namedOIDs[oid];
if (!namedCurve) {
throw new core.OperationError(`Cannot convert OID(${oid}) to WebCrypto named curve`);
throw new core.OperationError(
`Cannot convert OID(${oid}) to WebCrypto named curve`
);
}
return namedCurve;
}
@ -56,7 +58,9 @@ export function getNamedCurveByOid(oid: string) {
export function getOidByNamedCurve(namedCurve: string) {
const oid = namedOIDs[namedCurve];
if (!oid) {
throw new core.OperationError(`Cannot convert WebCrypto named curve '${namedCurve}' to OID`);
throw new core.OperationError(
`Cannot convert WebCrypto named curve '${namedCurve}' to OID`
);
}
return oid;
}

View File

@ -1,12 +1,17 @@
import { Buffer } from "buffer";
import { AsnParser, AsnSerializer } from "@peculiar/asn1-schema";
import { IJsonConvertible, JsonParser, JsonSerializer } from "@peculiar/json-schema";
import {
IJsonConvertible,
JsonParser,
JsonSerializer,
} from "@peculiar/json-schema";
import * as core from "webcrypto-core";
import { AsymmetricKey } from "../../keys";
import { getOidByNamedCurve } from "./helper";
import { AsymmetricKey } from "../../keys";
export class EcPrivateKey extends AsymmetricKey implements IJsonConvertible {
public readonly type: "private" = "private";
public algorithm!: EcKeyAlgorithm;
public readonly type = "private" as const;
public override algorithm!: EcKeyAlgorithm;
public getKey() {
const keyInfo = AsnParser.parse(this.data, core.asn1.PrivateKeyInfo);
@ -28,20 +33,23 @@ export class EcPrivateKey extends AsymmetricKey implements IJsonConvertible {
public fromJSON(json: JsonWebKey) {
if (!json.crv) {
throw new core.OperationError(`Cannot get named curve from JWK. Property 'crv' is required`);
throw new core.OperationError(
`Cannot get named curve from JWK. Property 'crv' is required`
);
}
const keyInfo = new core.asn1.PrivateKeyInfo();
keyInfo.privateKeyAlgorithm.algorithm = "1.2.840.10045.2.1";
keyInfo.privateKeyAlgorithm.parameters = AsnSerializer.serialize(
new core.asn1.ObjectIdentifier(getOidByNamedCurve(json.crv)),
new core.asn1.ObjectIdentifier(getOidByNamedCurve(json.crv))
);
const key = JsonParser.fromJSON(json, { targetSchema: core.asn1.EcPrivateKey });
const key = JsonParser.fromJSON(json, {
targetSchema: core.asn1.EcPrivateKey,
});
keyInfo.privateKey = AsnSerializer.serialize(key);
this.data = Buffer.from(AsnSerializer.serialize(keyInfo));
return this;
}
}

View File

@ -1,13 +1,17 @@
import { Buffer } from "buffer";
import { AsnParser, AsnSerializer } from "@peculiar/asn1-schema";
import { IJsonConvertible, JsonParser, JsonSerializer } from "@peculiar/json-schema";
import {
IJsonConvertible,
JsonParser,
JsonSerializer,
} from "@peculiar/json-schema";
import * as core from "webcrypto-core";
import { AsymmetricKey } from "../../keys/asymmetric";
import { getOidByNamedCurve } from "./helper";
import { AsymmetricKey } from "../../keys/asymmetric";
export class EcPublicKey extends AsymmetricKey implements IJsonConvertible {
public readonly type: "public" = "public";
public algorithm!: EcKeyAlgorithm;
public readonly type = "public" as const;
public override algorithm!: EcKeyAlgorithm;
public getKey() {
const keyInfo = AsnParser.parse(this.data, core.asn1.PublicKeyInfo);
@ -29,17 +33,21 @@ export class EcPublicKey extends AsymmetricKey implements IJsonConvertible {
public fromJSON(json: JsonWebKey) {
if (!json.crv) {
throw new core.OperationError(`Cannot get named curve from JWK. Property 'crv' is required`);
throw new core.OperationError(
`Cannot get named curve from JWK. Property 'crv' is required`
);
}
const key = JsonParser.fromJSON(json, { targetSchema: core.asn1.EcPublicKey });
const key = JsonParser.fromJSON(json, {
targetSchema: core.asn1.EcPublicKey,
});
const keyInfo = new core.asn1.PublicKeyInfo();
keyInfo.publicKeyAlgorithm.algorithm = "1.2.840.10045.2.1";
keyInfo.publicKeyAlgorithm.parameters = AsnSerializer.serialize(
new core.asn1.ObjectIdentifier(getOidByNamedCurve(json.crv)),
new core.asn1.ObjectIdentifier(getOidByNamedCurve(json.crv))
);
keyInfo.publicKey = AsnSerializer.toASN(key).valueHex;
keyInfo.publicKey = (AsnSerializer.toASN(key) as any).valueHex;
this.data = Buffer.from(AsnSerializer.serialize(keyInfo));

View File

@ -1,27 +1,35 @@
import { Buffer } from "buffer";
import crypto from "crypto";
import { AsnParser } from "@peculiar/asn1-schema";
import { JsonParser, JsonSerializer } from "@peculiar/json-schema";
import { Convert } from "pvtsutils";
import * as core from "webcrypto-core";
import { CryptoKey } from "../../keys";
import { EdPrivateKey } from "./private_key";
import { EdPublicKey } from "./public_key";
import { CryptoKey } from "../../keys";
export class EdCrypto {
public static publicKeyUsages = ["verify"];
public static privateKeyUsages = ["sign", "deriveKey", "deriveBits"];
public static async generateKey(algorithm: EcKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<core.CryptoKeyPair> {
public static async generateKey(
algorithm: EcKeyGenParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKeyPair> {
const privateKey = new EdPrivateKey();
privateKey.algorithm = algorithm;
privateKey.extractable = extractable;
privateKey.usages = keyUsages.filter((usage) => this.privateKeyUsages.indexOf(usage) !== -1);
privateKey.usages = keyUsages.filter(
(usage) => this.privateKeyUsages.indexOf(usage) !== -1
);
const publicKey = new EdPublicKey();
publicKey.algorithm = algorithm;
publicKey.extractable = true;
publicKey.usages = keyUsages.filter((usage) => this.publicKeyUsages.indexOf(usage) !== -1);
publicKey.usages = keyUsages.filter(
(usage) => this.publicKeyUsages.indexOf(usage) !== -1
);
const type = algorithm.namedCurve.toLowerCase() as "x448"; // "x448" | "ed448" | "x25519" | "ed25519"
const keys = crypto.generateKeyPairSync(type, {
@ -46,9 +54,15 @@ export class EdCrypto {
return res;
}
public static async sign(algorithm: Algorithm, key: EdPrivateKey, data: Uint8Array): Promise<ArrayBuffer> {
public static async sign(
algorithm: Algorithm,
key: EdPrivateKey,
data: Uint8Array
): Promise<ArrayBuffer> {
if (!key.pem) {
key.pem = `-----BEGIN PRIVATE KEY-----\n${key.data.toString("base64")}\n-----END PRIVATE KEY-----`;
key.pem = `-----BEGIN PRIVATE KEY-----\n${key.data.toString(
"base64"
)}\n-----END PRIVATE KEY-----`;
}
const options = {
key: key.pem,
@ -58,18 +72,34 @@ export class EdCrypto {
return core.BufferSourceConverter.toArrayBuffer(signature);
}
public static async verify(algorithm: EcdsaParams, key: EdPublicKey, signature: Uint8Array, data: Uint8Array): Promise<boolean> {
public static async verify(
algorithm: EcdsaParams,
key: EdPublicKey,
signature: Uint8Array,
data: Uint8Array
): Promise<boolean> {
if (!key.pem) {
key.pem = `-----BEGIN PUBLIC KEY-----\n${key.data.toString("base64")}\n-----END PUBLIC KEY-----`;
key.pem = `-----BEGIN PUBLIC KEY-----\n${key.data.toString(
"base64"
)}\n-----END PUBLIC KEY-----`;
}
const options = {
key: key.pem,
};
const ok = crypto.verify(null, Buffer.from(data), options, Buffer.from(signature));
const ok = crypto.verify(
null,
Buffer.from(data),
options,
Buffer.from(signature)
);
return ok;
}
public static async deriveBits(algorithm: EcdhKeyDeriveParams, baseKey: CryptoKey, length: number): Promise<ArrayBuffer> {
public static async deriveBits(
algorithm: EcdhKeyDeriveParams,
baseKey: CryptoKey,
length: number
): Promise<ArrayBuffer> {
const publicKey = crypto.createPublicKey({
key: (algorithm.public as CryptoKey).data,
format: "der",
@ -88,7 +118,10 @@ export class EdCrypto {
return new Uint8Array(bits).buffer.slice(0, length >> 3);
}
public static async exportKey(format: KeyFormat, key: CryptoKey): Promise<JsonWebKey | ArrayBuffer> {
public static async exportKey(
format: KeyFormat,
key: CryptoKey
): Promise<JsonWebKey | ArrayBuffer> {
switch (format.toLowerCase()) {
case "jwk":
return JsonSerializer.toJSON(key);
@ -96,60 +129,114 @@ export class EdCrypto {
case "spki":
return new Uint8Array(key.data).buffer;
case "raw": {
const publicKeyInfo = AsnParser.parse(key.data, core.asn1.PublicKeyInfo);
const publicKeyInfo = AsnParser.parse(
key.data,
core.asn1.PublicKeyInfo
);
return publicKeyInfo.publicKey;
}
default:
throw new core.OperationError("format: Must be 'jwk', 'raw', pkcs8' or 'spki'");
throw new core.OperationError(
"format: Must be 'jwk', 'raw', pkcs8' or 'spki'"
);
}
}
public static async importKey(format: KeyFormat, keyData: JsonWebKey | ArrayBuffer, algorithm: EcKeyImportParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
public static async importKey(
format: KeyFormat,
keyData: JsonWebKey | ArrayBuffer,
algorithm: EcKeyImportParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKey> {
switch (format.toLowerCase()) {
case "jwk": {
const jwk = keyData as JsonWebKey;
if (jwk.d) {
const asnKey = JsonParser.fromJSON(keyData, { targetSchema: core.asn1.CurvePrivateKey });
return this.importPrivateKey(asnKey, algorithm, extractable, keyUsages);
const asnKey = JsonParser.fromJSON(keyData, {
targetSchema: core.asn1.CurvePrivateKey,
});
return this.importPrivateKey(
asnKey,
algorithm,
extractable,
keyUsages
);
} else {
if (!jwk.x) {
throw new TypeError("keyData: Cannot get required 'x' filed");
}
return this.importPublicKey(Convert.FromBase64Url(jwk.x), algorithm, extractable, keyUsages);
return this.importPublicKey(
Convert.FromBase64Url(jwk.x),
algorithm,
extractable,
keyUsages
);
}
}
case "raw": {
return this.importPublicKey(keyData as ArrayBuffer, algorithm, extractable, keyUsages);
return this.importPublicKey(
keyData as ArrayBuffer,
algorithm,
extractable,
keyUsages
);
}
case "spki": {
const keyInfo = AsnParser.parse(new Uint8Array(keyData as ArrayBuffer), core.asn1.PublicKeyInfo);
return this.importPublicKey(keyInfo.publicKey, algorithm, extractable, keyUsages);
const keyInfo = AsnParser.parse(
new Uint8Array(keyData as ArrayBuffer),
core.asn1.PublicKeyInfo
);
return this.importPublicKey(
keyInfo.publicKey,
algorithm,
extractable,
keyUsages
);
}
case "pkcs8": {
const keyInfo = AsnParser.parse(new Uint8Array(keyData as ArrayBuffer), core.asn1.PrivateKeyInfo);
const asnKey = AsnParser.parse(keyInfo.privateKey, core.asn1.CurvePrivateKey);
const keyInfo = AsnParser.parse(
new Uint8Array(keyData as ArrayBuffer),
core.asn1.PrivateKeyInfo
);
const asnKey = AsnParser.parse(
keyInfo.privateKey,
core.asn1.CurvePrivateKey
);
return this.importPrivateKey(asnKey, algorithm, extractable, keyUsages);
}
default:
throw new core.OperationError("format: Must be 'jwk', 'raw', 'pkcs8' or 'spki'");
throw new core.OperationError(
"format: Must be 'jwk', 'raw', 'pkcs8' or 'spki'"
);
}
}
protected static importPrivateKey(asnKey: core.asn1.CurvePrivateKey, algorithm: EcKeyImportParams, extractable: boolean, keyUsages: KeyUsage[]) {
protected static importPrivateKey(
asnKey: core.asn1.CurvePrivateKey,
algorithm: EcKeyImportParams,
extractable: boolean,
keyUsages: KeyUsage[]
) {
const key = new EdPrivateKey();
key.fromJSON({
crv: algorithm.namedCurve,
d: Convert.ToBase64Url(asnKey.d),
});
key.algorithm = Object.assign({}, algorithm) as EcKeyAlgorithm;
key.extractable = extractable;
key.usages = keyUsages;
return key;
}
protected static async importPublicKey(asnKey: ArrayBuffer, algorithm: EcKeyImportParams, extractable: boolean, keyUsages: KeyUsage[]) {
protected static async importPublicKey(
asnKey: ArrayBuffer,
algorithm: EcKeyImportParams,
extractable: boolean,
keyUsages: KeyUsage[]
) {
const key = new EdPublicKey();
key.fromJSON({
crv: algorithm.namedCurve,
@ -162,5 +249,4 @@ export class EdCrypto {
return key;
}
}

View File

@ -1,18 +1,22 @@
import * as core from "webcrypto-core";
import { EdCrypto } from "./crypto";
import { CryptoKey } from "../../keys";
import { getCryptoKey, setCryptoKey } from "../storage";
import { EdCrypto } from "./crypto";
export class EcdhEsProvider extends core.EcdhEsProvider {
public async onGenerateKey(algorithm: EcKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<core.CryptoKeyPair> {
public async onGenerateKey(
algorithm: EcKeyGenParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKeyPair> {
const keys = await EdCrypto.generateKey(
{
name: this.name,
namedCurve: algorithm.namedCurve.toUpperCase(),
},
extractable,
keyUsages);
keyUsages
);
return {
privateKey: setCryptoKey(keys.privateKey as CryptoKey),
@ -20,18 +24,40 @@ export class EcdhEsProvider extends core.EcdhEsProvider {
};
}
public async onDeriveBits(algorithm: EcdhKeyDeriveParams, baseKey: core.CryptoKey, length: number): Promise<ArrayBuffer> {
const bits = await EdCrypto.deriveBits({...algorithm, public: getCryptoKey(algorithm.public)}, getCryptoKey(baseKey), length);
public async onDeriveBits(
algorithm: EcdhKeyDeriveParams,
baseKey: core.CryptoKey,
length: number
): Promise<ArrayBuffer> {
const bits = await EdCrypto.deriveBits(
{ ...algorithm, public: getCryptoKey(algorithm.public) },
getCryptoKey(baseKey),
length
);
return bits;
}
public async onExportKey(format: KeyFormat, key: CryptoKey): Promise<ArrayBuffer | JsonWebKey> {
public async onExportKey(
format: KeyFormat,
key: CryptoKey
): Promise<ArrayBuffer | JsonWebKey> {
return EdCrypto.exportKey(format, getCryptoKey(key));
}
public async onImportKey(format: KeyFormat, keyData: ArrayBuffer | JsonWebKey, algorithm: EcKeyImportParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<core.CryptoKey> {
const key = await EdCrypto.importKey(format, keyData, { ...algorithm, name: this.name }, extractable, keyUsages);
public async onImportKey(
format: KeyFormat,
keyData: ArrayBuffer | JsonWebKey,
algorithm: EcKeyImportParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<core.CryptoKey> {
const key = await EdCrypto.importKey(
format,
keyData,
{ ...algorithm, name: this.name },
extractable,
keyUsages
);
return setCryptoKey(key);
}
}
}

View File

@ -1,20 +1,24 @@
import * as core from "webcrypto-core";
import { CryptoKey } from "../../keys";
import { getCryptoKey, setCryptoKey } from "../storage";
import { EdCrypto } from "./crypto";
import { EdPrivateKey } from "./private_key";
import { EdPublicKey } from "./public_key";
import { CryptoKey } from "../../keys";
import { getCryptoKey, setCryptoKey } from "../storage";
export class EdDsaProvider extends core.EdDsaProvider {
public async onGenerateKey(algorithm: EcKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<core.CryptoKeyPair> {
public async onGenerateKey(
algorithm: EcKeyGenParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKeyPair> {
const keys = await EdCrypto.generateKey(
{
name: this.name,
namedCurve: algorithm.namedCurve.replace(/^ed/i, "Ed"),
},
extractable,
keyUsages);
keyUsages
);
return {
privateKey: setCryptoKey(keys.privateKey as CryptoKey),
@ -22,21 +26,53 @@ export class EdDsaProvider extends core.EdDsaProvider {
};
}
public async onSign(algorithm: EcdsaParams, key: CryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return EdCrypto.sign(algorithm, getCryptoKey(key) as EdPrivateKey, new Uint8Array(data));
public async onSign(
algorithm: EcdsaParams,
key: CryptoKey,
data: ArrayBuffer
): Promise<ArrayBuffer> {
return EdCrypto.sign(
algorithm,
getCryptoKey(key) as EdPrivateKey,
new Uint8Array(data)
);
}
public async onVerify(algorithm: EcdsaParams, key: CryptoKey, signature: ArrayBuffer, data: ArrayBuffer): Promise<boolean> {
return EdCrypto.verify(algorithm, getCryptoKey(key) as EdPublicKey, new Uint8Array(signature), new Uint8Array(data));
public async onVerify(
algorithm: EcdsaParams,
key: CryptoKey,
signature: ArrayBuffer,
data: ArrayBuffer
): Promise<boolean> {
return EdCrypto.verify(
algorithm,
getCryptoKey(key) as EdPublicKey,
new Uint8Array(signature),
new Uint8Array(data)
);
}
public async onExportKey(format: KeyFormat, key: CryptoKey): Promise<ArrayBuffer | JsonWebKey> {
public async onExportKey(
format: KeyFormat,
key: CryptoKey
): Promise<ArrayBuffer | JsonWebKey> {
return EdCrypto.exportKey(format, getCryptoKey(key));
}
public async onImportKey(format: KeyFormat, keyData: ArrayBuffer | JsonWebKey, algorithm: EcKeyImportParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<core.CryptoKey> {
const key = await EdCrypto.importKey(format, keyData, { ...algorithm, name: this.name }, extractable, keyUsages);
public async onImportKey(
format: KeyFormat,
keyData: ArrayBuffer | JsonWebKey,
algorithm: EcKeyImportParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<core.CryptoKey> {
const key = await EdCrypto.importKey(
format,
keyData,
{ ...algorithm, name: this.name },
extractable,
keyUsages
);
return setCryptoKey(key);
}
}
}

View File

@ -3,22 +3,24 @@ import * as core from "webcrypto-core";
const edOIDs: { [key: string]: string } = {
// Ed448
[core.asn1.idEd448]: "Ed448",
"ed448": core.asn1.idEd448,
ed448: core.asn1.idEd448,
// X448
[core.asn1.idX448]: "X448",
"x448": core.asn1.idX448,
x448: core.asn1.idX448,
// Ed25519
[core.asn1.idEd25519]: "Ed25519",
"ed25519": core.asn1.idEd25519,
ed25519: core.asn1.idEd25519,
// X25519
[core.asn1.idX25519]: "X25519",
"x25519": core.asn1.idX25519,
x25519: core.asn1.idX25519,
};
export function getNamedCurveByOid(oid: string) {
const namedCurve = edOIDs[oid];
if (!namedCurve) {
throw new core.OperationError(`Cannot convert OID(${oid}) to WebCrypto named curve`);
throw new core.OperationError(
`Cannot convert OID(${oid}) to WebCrypto named curve`
);
}
return namedCurve;
}
@ -26,7 +28,9 @@ export function getNamedCurveByOid(oid: string) {
export function getOidByNamedCurve(namedCurve: string) {
const oid = edOIDs[namedCurve.toLowerCase()];
if (!oid) {
throw new core.OperationError(`Cannot convert WebCrypto named curve '${namedCurve}' to OID`);
throw new core.OperationError(
`Cannot convert WebCrypto named curve '${namedCurve}' to OID`
);
}
return oid;
}

View File

@ -1,12 +1,17 @@
import { Buffer } from "buffer";
import { AsnParser, AsnSerializer } from "@peculiar/asn1-schema";
import { IJsonConvertible, JsonParser, JsonSerializer } from "@peculiar/json-schema";
import {
IJsonConvertible,
JsonParser,
JsonSerializer,
} from "@peculiar/json-schema";
import * as core from "webcrypto-core";
import { AsymmetricKey } from "../../keys";
import { getOidByNamedCurve } from "./helper";
import { AsymmetricKey } from "../../keys";
export class EdPrivateKey extends AsymmetricKey implements IJsonConvertible {
public readonly type: "private" = "private";
public algorithm!: EcKeyAlgorithm;
public readonly type = "private" as const;
public override algorithm!: EcKeyAlgorithm;
public getKey() {
const keyInfo = AsnParser.parse(this.data, core.asn1.PrivateKeyInfo);
@ -28,17 +33,20 @@ export class EdPrivateKey extends AsymmetricKey implements IJsonConvertible {
public fromJSON(json: JsonWebKey) {
if (!json.crv) {
throw new core.OperationError(`Cannot get named curve from JWK. Property 'crv' is required`);
throw new core.OperationError(
`Cannot get named curve from JWK. Property 'crv' is required`
);
}
const keyInfo = new core.asn1.PrivateKeyInfo();
keyInfo.privateKeyAlgorithm.algorithm = getOidByNamedCurve(json.crv);
const key = JsonParser.fromJSON(json, { targetSchema: core.asn1.CurvePrivateKey });
const key = JsonParser.fromJSON(json, {
targetSchema: core.asn1.CurvePrivateKey,
});
keyInfo.privateKey = AsnSerializer.serialize(key);
this.data = Buffer.from(AsnSerializer.serialize(keyInfo));
return this;
}
}

View File

@ -1,14 +1,14 @@
import { Buffer } from "buffer";
import { AsnParser, AsnSerializer } from "@peculiar/asn1-schema";
import { IJsonConvertible } from "@peculiar/json-schema";
import { Convert } from "pvtsutils";
import * as core from "webcrypto-core";
import { AsymmetricKey } from "../../keys/asymmetric";
import { getOidByNamedCurve } from "./helper";
import { AsymmetricKey } from "../../keys/asymmetric";
export class EdPublicKey extends AsymmetricKey implements IJsonConvertible {
public readonly type: "public" = "public";
public algorithm!: EcKeyAlgorithm;
public readonly type = "public" as const;
public override algorithm!: EcKeyAlgorithm;
public getKey() {
const keyInfo = AsnParser.parse(this.data, core.asn1.PublicKeyInfo);
@ -26,16 +26,20 @@ export class EdPublicKey extends AsymmetricKey implements IJsonConvertible {
};
return Object.assign(json, {
x: Convert.ToBase64Url(key)
x: Convert.ToBase64Url(key),
});
}
public fromJSON(json: JsonWebKey) {
if (!json.crv) {
throw new core.OperationError(`Cannot get named curve from JWK. Property 'crv' is required`);
throw new core.OperationError(
`Cannot get named curve from JWK. Property 'crv' is required`
);
}
if (!json.x) {
throw new core.OperationError(`Cannot get property from JWK. Property 'x' is required`);
throw new core.OperationError(
`Cannot get property from JWK. Property 'x' is required`
);
}
const keyInfo = new core.asn1.PublicKeyInfo();

View File

@ -1,14 +1,20 @@
import { Buffer } from "buffer";
import crypto from "crypto";
import * as core from "webcrypto-core";
import { BufferSourceConverter, CryptoKey } from "webcrypto-core";
import { setCryptoKey, getCryptoKey } from "../storage";
import { HkdfCryptoKey } from "./key";
import { setCryptoKey, getCryptoKey } from "../storage";
export class HkdfProvider extends core.HkdfProvider {
public async onImportKey(format: KeyFormat, keyData: ArrayBuffer, algorithm: HmacImportParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
public async onImportKey(
format: KeyFormat,
keyData: ArrayBuffer,
algorithm: HmacImportParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKey> {
if (format.toLowerCase() !== "raw") {
throw new core.OperationError("Operation not supported");
throw new core.OperationError("Operation not supported");
}
const key: HkdfCryptoKey = new HkdfCryptoKey();
@ -19,35 +25,40 @@ export class HkdfProvider extends core.HkdfProvider {
return setCryptoKey(key);
}
public async onDeriveBits(params: HkdfParams, baseKey: HkdfCryptoKey, length: number): Promise<ArrayBuffer> {
public async onDeriveBits(
params: HkdfParams,
baseKey: HkdfCryptoKey,
length: number
): Promise<ArrayBuffer> {
const hash = (params.hash as Algorithm).name.replace("-", "");
const hashLength = crypto.createHash(hash).digest().length;
const byteLength = length / 8;
const info = BufferSourceConverter.toUint8Array(params.info);
const PRK = crypto.createHmac(hash, BufferSourceConverter.toUint8Array(params.salt))
.update(BufferSourceConverter.toUint8Array(getCryptoKey(baseKey).data))
.digest();
const PRK = crypto
.createHmac(hash, BufferSourceConverter.toUint8Array(params.salt))
.update(BufferSourceConverter.toUint8Array(getCryptoKey(baseKey).data))
.digest();
const blocks = [Buffer.alloc(0)];
const blockCount = Math.ceil(byteLength / hashLength) + 1; // Includes empty buffer
for (let i = 1; i < blockCount; ++i) {
blocks.push(
crypto.createHmac(hash, PRK)
.update(Buffer.concat([blocks[i - 1], info, Buffer.from([i])]))
.digest(),
crypto
.createHmac(hash, PRK)
.update(Buffer.concat([blocks[i - 1], info, Buffer.from([i])]))
.digest()
);
}
return Buffer.concat(blocks).slice(0, byteLength);
}
public checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
public override checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
super.checkCryptoKey(key, keyUsage);
if (!(getCryptoKey(key) instanceof HkdfCryptoKey)) {
throw new TypeError("key: Is not HKDF CryptoKey");
}
}
}

View File

@ -1,8 +1,7 @@
import { CryptoKey } from "../../keys";
export class HkdfCryptoKey extends CryptoKey {
public override data!: Buffer;
public data!: Buffer;
public algorithm!: KeyAlgorithm;
public override algorithm!: KeyAlgorithm;
}

View File

@ -1,16 +1,25 @@
import { Buffer } from "buffer";
import crypto from "crypto";
import { JsonParser, JsonSerializer } from "@peculiar/json-schema";
import * as core from "webcrypto-core";
import { setCryptoKey, getCryptoKey } from "../storage";
import { HmacCryptoKey } from "./key";
import { ShaCrypto } from "../sha";
import { setCryptoKey, getCryptoKey } from "../storage";
export class HmacProvider extends core.HmacProvider {
public async onGenerateKey(algorithm: HmacKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
const length = (algorithm.length || this.getDefaultLength((algorithm.hash as Algorithm).name)) >> 3 << 3;
public async onGenerateKey(
algorithm: HmacKeyGenParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKey> {
const length =
((algorithm.length ||
this.getDefaultLength((algorithm.hash as Algorithm).name)) >>
3) <<
3;
const key = new HmacCryptoKey();
key.algorithm = {
...algorithm as any,
...(algorithm as any),
length,
name: this.name,
};
@ -21,23 +30,42 @@ export class HmacProvider extends core.HmacProvider {
return setCryptoKey(key);
}
public 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)
.update(Buffer.from(data)).digest();
public override async onSign(
algorithm: Algorithm,
key: HmacCryptoKey,
data: ArrayBuffer
): Promise<ArrayBuffer> {
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 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)
.update(Buffer.from(data)).digest();
public override async onVerify(
algorithm: Algorithm,
key: HmacCryptoKey,
signature: ArrayBuffer,
data: ArrayBuffer
): Promise<boolean> {
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;
}
public async onImportKey(format: KeyFormat, keyData: JsonWebKey | ArrayBuffer, algorithm: HmacImportParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
public async onImportKey(
format: KeyFormat,
keyData: JsonWebKey | ArrayBuffer,
algorithm: HmacImportParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKey> {
let key: HmacCryptoKey;
switch (format.toLowerCase()) {
@ -63,7 +91,10 @@ export class HmacProvider extends core.HmacProvider {
return setCryptoKey(key);
}
public async onExportKey(format: KeyFormat, key: HmacCryptoKey): Promise<JsonWebKey | ArrayBuffer> {
public async onExportKey(
format: KeyFormat,
key: HmacCryptoKey
): Promise<JsonWebKey | ArrayBuffer> {
switch (format.toLowerCase()) {
case "jwk":
return JsonSerializer.toJSON(getCryptoKey(key));
@ -74,11 +105,10 @@ export class HmacProvider extends core.HmacProvider {
}
}
public checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
public override checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
super.checkCryptoKey(key, keyUsage);
if (!(getCryptoKey(key) instanceof HmacCryptoKey)) {
throw new TypeError("key: Is not HMAC CryptoKey");
}
}
}

View File

@ -3,11 +3,10 @@ import { JsonBase64UrlConverter } from "../../converters";
import { CryptoKey } from "../../keys";
export class HmacCryptoKey extends CryptoKey {
@JsonProp({ name: "k", converter: JsonBase64UrlConverter })
public data!: Buffer;
public override data!: Buffer;
public algorithm!: HmacKeyAlgorithm;
public override algorithm!: HmacKeyAlgorithm;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
@ -16,8 +15,7 @@ export class HmacCryptoKey extends CryptoKey {
return `HS${hash.replace("SHA-", "")}`;
}
protected set alg(value: string) {
protected override set alg(value: string) {
// nothing, cause set is needed for json-schema, but is not used by module
}
}

View File

@ -7,3 +7,4 @@ export * from "./sha";
export * from "./pbkdf";
export * from "./hmac";
export * from "./hkdf";
export * from "./shake";

View File

@ -1,4 +1,3 @@
import { CryptoKey } from "../../keys";
export class PbkdfCryptoKey extends CryptoKey {
}
export class PbkdfCryptoKey extends CryptoKey {}

View File

@ -1,25 +1,42 @@
import { Buffer } from "buffer";
import crypto from "crypto";
import * as core from "webcrypto-core";
import { setCryptoKey, getCryptoKey } from "../storage";
import { PbkdfCryptoKey } from "./key";
import { setCryptoKey, getCryptoKey } from "../storage";
export class Pbkdf2Provider extends core.Pbkdf2Provider {
public async onDeriveBits(algorithm: Pbkdf2Params, baseKey: PbkdfCryptoKey, length: number): Promise<ArrayBuffer> {
public async onDeriveBits(
algorithm: Pbkdf2Params,
baseKey: PbkdfCryptoKey,
length: number
): Promise<ArrayBuffer> {
return new Promise<ArrayBuffer>((resolve, reject) => {
const salt = core.BufferSourceConverter.toArrayBuffer(algorithm.salt);
const hash = (algorithm.hash as Algorithm).name.replace("-", "");
crypto.pbkdf2(getCryptoKey(baseKey).data, Buffer.from(salt), algorithm.iterations, length >> 3, hash, (err, derivedBits) => {
if (err) {
reject(err);
} else {
resolve(new Uint8Array(derivedBits).buffer);
crypto.pbkdf2(
getCryptoKey(baseKey).data,
Buffer.from(salt),
algorithm.iterations,
length >> 3,
hash,
(err, derivedBits) => {
if (err) {
reject(err);
} else {
resolve(new Uint8Array(derivedBits).buffer);
}
}
});
);
});
}
public async onImportKey(format: KeyFormat, keyData: JsonWebKey | ArrayBuffer, algorithm: Algorithm, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
public async onImportKey(
format: KeyFormat,
keyData: JsonWebKey | ArrayBuffer,
algorithm: Algorithm,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKey> {
if (format === "raw") {
const key = new PbkdfCryptoKey();
key.data = Buffer.from(keyData as ArrayBuffer);
@ -31,11 +48,10 @@ export class Pbkdf2Provider extends core.Pbkdf2Provider {
throw new core.OperationError("format: Must be 'raw'");
}
public checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
public override checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
super.checkCryptoKey(key, keyUsage);
if (!(getCryptoKey(key) instanceof PbkdfCryptoKey)) {
throw new TypeError("key: Is not PBKDF CryptoKey");
}
}
}

View File

@ -1,10 +1,11 @@
import { Buffer } from "buffer";
import crypto from "crypto";
import { AsnParser, AsnSerializer } from "@peculiar/asn1-schema";
import { JsonParser, JsonSerializer } from "@peculiar/json-schema";
import * as core from "webcrypto-core";
import { CryptoKey } from "../../keys";
import { RsaPrivateKey } from "./private_key";
import { RsaPublicKey } from "./public_key";
import { CryptoKey } from "../../keys";
interface INodeCryptoSignOptions {
key: string;
@ -14,20 +15,27 @@ interface INodeCryptoSignOptions {
}
export class RsaCrypto {
public static publicKeyUsages = ["verify", "encrypt", "wrapKey"];
public static privateKeyUsages = ["sign", "decrypt", "unwrapKey"];
public static async generateKey(algorithm: RsaHashedKeyGenParams | RsaKeyGenParams, extractable: boolean, keyUsages: string[]): Promise<core.CryptoKeyPair> {
public static async generateKey(
algorithm: RsaHashedKeyGenParams | RsaKeyGenParams,
extractable: boolean,
keyUsages: string[]
): Promise<CryptoKeyPair> {
const privateKey = new RsaPrivateKey();
privateKey.algorithm = algorithm as RsaHashedKeyAlgorithm;
privateKey.extractable = extractable;
privateKey.usages = keyUsages.filter((usage) => this.privateKeyUsages.indexOf(usage) !== -1) as KeyUsage[];
privateKey.usages = keyUsages.filter(
(usage) => this.privateKeyUsages.indexOf(usage) !== -1
) as KeyUsage[];
const publicKey = new RsaPublicKey();
publicKey.algorithm = algorithm as RsaHashedKeyAlgorithm;
publicKey.extractable = true;
publicKey.usages = keyUsages.filter((usage) => this.publicKeyUsages.indexOf(usage) !== -1) as KeyUsage[];
publicKey.usages = keyUsages.filter(
(usage) => this.publicKeyUsages.indexOf(usage) !== -1
) as KeyUsage[];
const publicExponent = Buffer.concat([
Buffer.alloc(4 - algorithm.publicExponent.byteLength, 0),
@ -58,7 +66,10 @@ export class RsaCrypto {
return res;
}
public static async exportKey(format: KeyFormat, key: CryptoKey): Promise<JsonWebKey | ArrayBuffer> {
public static async exportKey(
format: KeyFormat,
key: CryptoKey
): Promise<JsonWebKey | ArrayBuffer> {
switch (format.toLowerCase()) {
case "jwk":
return JsonSerializer.toJSON(key);
@ -66,38 +77,78 @@ export class RsaCrypto {
case "spki":
return new Uint8Array(key.data).buffer;
default:
throw new core.OperationError("format: Must be 'jwk', 'pkcs8' or 'spki'");
throw new core.OperationError(
"format: Must be 'jwk', 'pkcs8' or 'spki'"
);
}
}
public static async importKey(format: KeyFormat, keyData: JsonWebKey | ArrayBuffer, algorithm: RsaHashedImportParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
public static async importKey(
format: KeyFormat,
keyData: JsonWebKey | ArrayBuffer,
algorithm: RsaHashedImportParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKey> {
switch (format.toLowerCase()) {
case "jwk": {
const jwk = keyData as JsonWebKey;
if (jwk.d) {
const asnKey = JsonParser.fromJSON(keyData, { targetSchema: core.asn1.RsaPrivateKey });
return this.importPrivateKey(asnKey, algorithm, extractable, keyUsages);
const asnKey = JsonParser.fromJSON(keyData, {
targetSchema: core.asn1.RsaPrivateKey,
});
return this.importPrivateKey(
asnKey,
algorithm,
extractable,
keyUsages
);
} else {
const asnKey = JsonParser.fromJSON(keyData, { targetSchema: core.asn1.RsaPublicKey });
return this.importPublicKey(asnKey, algorithm, extractable, keyUsages);
const asnKey = JsonParser.fromJSON(keyData, {
targetSchema: core.asn1.RsaPublicKey,
});
return this.importPublicKey(
asnKey,
algorithm,
extractable,
keyUsages
);
}
}
case "spki": {
const keyInfo = AsnParser.parse(new Uint8Array(keyData as ArrayBuffer), core.asn1.PublicKeyInfo);
const asnKey = AsnParser.parse(keyInfo.publicKey, core.asn1.RsaPublicKey);
const keyInfo = AsnParser.parse(
new Uint8Array(keyData as ArrayBuffer),
core.asn1.PublicKeyInfo
);
const asnKey = AsnParser.parse(
keyInfo.publicKey,
core.asn1.RsaPublicKey
);
return this.importPublicKey(asnKey, algorithm, extractable, keyUsages);
}
case "pkcs8": {
const keyInfo = AsnParser.parse(new Uint8Array(keyData as ArrayBuffer), core.asn1.PrivateKeyInfo);
const asnKey = AsnParser.parse(keyInfo.privateKey, core.asn1.RsaPrivateKey);
const keyInfo = AsnParser.parse(
new Uint8Array(keyData as ArrayBuffer),
core.asn1.PrivateKeyInfo
);
const asnKey = AsnParser.parse(
keyInfo.privateKey,
core.asn1.RsaPrivateKey
);
return this.importPrivateKey(asnKey, algorithm, extractable, keyUsages);
}
default:
throw new core.OperationError("format: Must be 'jwk', 'pkcs8' or 'spki'");
throw new core.OperationError(
"format: Must be 'jwk', 'pkcs8' or 'spki'"
);
}
}
public static async sign(algorithm: Algorithm, key: RsaPrivateKey, data: Uint8Array): Promise<ArrayBuffer> {
public static async sign(
algorithm: Algorithm,
key: RsaPrivateKey,
data: Uint8Array
): Promise<ArrayBuffer> {
switch (algorithm.name.toUpperCase()) {
case "RSA-PSS":
case "RSASSA-PKCS1-V1_5":
@ -107,7 +158,12 @@ export class RsaCrypto {
}
}
public static async verify(algorithm: Algorithm, key: RsaPublicKey, signature: Uint8Array, data: Uint8Array): Promise<boolean> {
public static async verify(
algorithm: Algorithm,
key: RsaPublicKey,
signature: Uint8Array,
data: Uint8Array
): Promise<boolean> {
switch (algorithm.name.toUpperCase()) {
case "RSA-PSS":
case "RSASSA-PKCS1-V1_5":
@ -117,7 +173,11 @@ export class RsaCrypto {
}
}
public static async encrypt(algorithm: RsaOaepParams, key: RsaPublicKey, data: Uint8Array): Promise<ArrayBuffer> {
public static async encrypt(
algorithm: RsaOaepParams,
key: RsaPublicKey,
data: Uint8Array
): Promise<ArrayBuffer> {
switch (algorithm.name.toUpperCase()) {
case "RSA-OAEP":
return this.encryptOAEP(algorithm, key, data);
@ -126,7 +186,11 @@ export class RsaCrypto {
}
}
public static async decrypt(algorithm: RsaOaepParams, key: RsaPrivateKey, data: Uint8Array): Promise<ArrayBuffer> {
public static async decrypt(
algorithm: RsaOaepParams,
key: RsaPrivateKey,
data: Uint8Array
): Promise<ArrayBuffer> {
switch (algorithm.name.toUpperCase()) {
case "RSA-OAEP":
return this.decryptOAEP(algorithm, key, data);
@ -135,7 +199,12 @@ export class RsaCrypto {
}
}
protected static importPrivateKey(asnKey: core.asn1.RsaPrivateKey, algorithm: RsaHashedImportParams, extractable: boolean, keyUsages: KeyUsage[]) {
protected static importPrivateKey(
asnKey: core.asn1.RsaPrivateKey,
algorithm: RsaHashedImportParams,
extractable: boolean,
keyUsages: KeyUsage[]
) {
const keyInfo = new core.asn1.PrivateKeyInfo();
keyInfo.privateKeyAlgorithm.algorithm = "1.2.840.113549.1.1.1";
keyInfo.privateKeyAlgorithm.parameters = null;
@ -153,7 +222,12 @@ export class RsaCrypto {
return key;
}
protected static importPublicKey(asnKey: core.asn1.RsaPublicKey, algorithm: RsaHashedImportParams, extractable: boolean, keyUsages: KeyUsage[]) {
protected static importPublicKey(
asnKey: core.asn1.RsaPublicKey,
algorithm: RsaHashedImportParams,
extractable: boolean,
keyUsages: KeyUsage[]
) {
const keyInfo = new core.asn1.PublicKeyInfo();
keyInfo.publicKeyAlgorithm.algorithm = "1.2.840.113549.1.1.1";
keyInfo.publicKeyAlgorithm.parameters = null;
@ -181,18 +255,30 @@ 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");
}
}
protected static signRsa(algorithm: Algorithm, key: RsaPrivateKey, data: Uint8Array) {
protected static signRsa(
algorithm: Algorithm,
key: RsaPrivateKey,
data: Uint8Array
) {
const cryptoAlg = this.getCryptoAlgorithm(key.algorithm);
const signer = crypto.createSign(cryptoAlg);
signer.update(Buffer.from(data));
if (!key.pem) {
key.pem = `-----BEGIN PRIVATE KEY-----\n${key.data.toString("base64")}\n-----END PRIVATE KEY-----`;
key.pem = `-----BEGIN PRIVATE KEY-----\n${key.data.toString(
"base64"
)}\n-----END PRIVATE KEY-----`;
}
const options: INodeCryptoSignOptions = {
key: key.pem,
@ -206,13 +292,20 @@ export class RsaCrypto {
return new Uint8Array(signature).buffer;
}
protected static verifySSA(algorithm: Algorithm, key: RsaPublicKey, data: Uint8Array, signature: Uint8Array) {
protected static verifySSA(
algorithm: Algorithm,
key: RsaPublicKey,
data: Uint8Array,
signature: Uint8Array
) {
const cryptoAlg = this.getCryptoAlgorithm(key.algorithm);
const signer = crypto.createVerify(cryptoAlg);
signer.update(Buffer.from(data));
if (!key.pem) {
key.pem = `-----BEGIN PUBLIC KEY-----\n${key.data.toString("base64")}\n-----END PUBLIC KEY-----`;
key.pem = `-----BEGIN PUBLIC KEY-----\n${key.data.toString(
"base64"
)}\n-----END PUBLIC KEY-----`;
}
const options: INodeCryptoSignOptions = {
key: key.pem,
@ -226,9 +319,15 @@ export class RsaCrypto {
return ok;
}
protected static encryptOAEP(algorithm: RsaOaepParams, key: RsaPublicKey, data: Uint8Array) {
protected static encryptOAEP(
algorithm: RsaOaepParams,
key: RsaPublicKey,
data: Uint8Array
) {
const options: crypto.RsaPublicKey = {
key: `-----BEGIN PUBLIC KEY-----\n${key.data.toString("base64")}\n-----END PUBLIC KEY-----`,
key: `-----BEGIN PUBLIC KEY-----\n${key.data.toString(
"base64"
)}\n-----END PUBLIC KEY-----`,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
};
if (algorithm.label) {
@ -238,9 +337,15 @@ export class RsaCrypto {
return new Uint8Array(crypto.publicEncrypt(options, data)).buffer;
}
protected static decryptOAEP(algorithm: RsaOaepParams, key: RsaPrivateKey, data: Uint8Array) {
protected static decryptOAEP(
algorithm: RsaOaepParams,
key: RsaPrivateKey,
data: Uint8Array
) {
const options: crypto.RsaPrivateKey = {
key: `-----BEGIN PRIVATE KEY-----\n${key.data.toString("base64")}\n-----END PRIVATE KEY-----`,
key: `-----BEGIN PRIVATE KEY-----\n${key.data.toString(
"base64"
)}\n-----END PRIVATE KEY-----`,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
};
if (algorithm.label) {
@ -249,5 +354,4 @@ export class RsaCrypto {
return new Uint8Array(crypto.privateDecrypt(options, data)).buffer;
}
}

View File

@ -1,12 +1,13 @@
import { Buffer } from "buffer";
import { AsnParser, AsnSerializer } from "@peculiar/asn1-schema";
import { JsonParser, JsonSerializer } from "@peculiar/json-schema";
import * as core from "webcrypto-core";
import { AsymmetricKey } from "../../keys";
import { getJwkAlgorithm } from "./helper";
import { AsymmetricKey } from "../../keys";
export class RsaPrivateKey extends AsymmetricKey {
public readonly type: "private" = "private";
public algorithm!: RsaHashedKeyAlgorithm;
public readonly type = "private" as const;
public override algorithm!: RsaHashedKeyAlgorithm;
public getKey() {
const keyInfo = AsnParser.parse(this.data, core.asn1.PrivateKeyInfo);
@ -27,7 +28,9 @@ export class RsaPrivateKey extends AsymmetricKey {
}
public fromJSON(json: JsonWebKey) {
const key = JsonParser.fromJSON(json, { targetSchema: core.asn1.RsaPrivateKey });
const key = JsonParser.fromJSON(json, {
targetSchema: core.asn1.RsaPrivateKey,
});
const keyInfo = new core.asn1.PrivateKeyInfo();
keyInfo.privateKeyAlgorithm.algorithm = "1.2.840.113549.1.1.1";
@ -36,5 +39,4 @@ export class RsaPrivateKey extends AsymmetricKey {
this.data = Buffer.from(AsnSerializer.serialize(keyInfo));
}
}

View File

@ -1,12 +1,13 @@
import { Buffer } from "buffer";
import { AsnParser, AsnSerializer } from "@peculiar/asn1-schema";
import { JsonParser, JsonSerializer } from "@peculiar/json-schema";
import * as core from "webcrypto-core";
import { AsymmetricKey } from "../../keys/asymmetric";
import { getJwkAlgorithm } from "./helper";
import { AsymmetricKey } from "../../keys/asymmetric";
export class RsaPublicKey extends AsymmetricKey {
public readonly type: "public" = "public";
public algorithm!: RsaHashedKeyAlgorithm;
public readonly type = "public" as const;
public override algorithm!: RsaHashedKeyAlgorithm;
public getKey() {
const keyInfo = AsnParser.parse(this.data, core.asn1.PublicKeyInfo);
@ -27,7 +28,9 @@ export class RsaPublicKey extends AsymmetricKey {
}
public fromJSON(json: JsonWebKey) {
const key = JsonParser.fromJSON(json, { targetSchema: core.asn1.RsaPublicKey });
const key = JsonParser.fromJSON(json, {
targetSchema: core.asn1.RsaPublicKey,
});
const keyInfo = new core.asn1.PublicKeyInfo();
keyInfo.publicKeyAlgorithm.algorithm = "1.2.840.113549.1.1.1";

View File

@ -1,27 +1,31 @@
import * as crypto from "crypto";
import { Convert } from "pvtsutils";
import * as core from "webcrypto-core";
import { setCryptoKey, getCryptoKey } from "../storage";
import { RsaCrypto } from "./crypto";
import { RsaPrivateKey } from "./private_key";
import { RsaPublicKey } from "./public_key";
import { setCryptoKey, getCryptoKey } from "../storage";
export class RsaEsProvider extends core.ProviderCrypto {
public name = "RSAES-PKCS1-v1_5";
public usages = {
publicKey: ["encrypt", "wrapKey"] as core.KeyUsages,
privateKey: ["decrypt", "unwrapKey"] as core.KeyUsages,
};
public async onGenerateKey(algorithm: RsaKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<core.CryptoKeyPair> {
public override async onGenerateKey(
algorithm: RsaKeyGenParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKeyPair> {
const keys = await RsaCrypto.generateKey(
{
...algorithm,
name: this.name,
},
extractable,
keyUsages);
keyUsages
);
return {
privateKey: setCryptoKey(keys.privateKey as RsaPrivateKey),
@ -29,10 +33,15 @@ export class RsaEsProvider extends core.ProviderCrypto {
};
}
public checkGenerateKeyParams(algorithm: RsaKeyGenParams) {
public override checkGenerateKeyParams(algorithm: RsaKeyGenParams) {
// public exponent
this.checkRequiredProperty(algorithm, "publicExponent");
if (!(algorithm.publicExponent && algorithm.publicExponent instanceof Uint8Array)) {
if (
!(
algorithm.publicExponent &&
algorithm.publicExponent instanceof Uint8Array
)
) {
throw new TypeError("publicExponent: Missing or not a Uint8Array");
}
const publicExponent = Convert.ToBase64(algorithm.publicExponent);
@ -52,31 +61,59 @@ export class RsaEsProvider extends core.ProviderCrypto {
}
}
public async onEncrypt(algorithm: Algorithm, key: RsaPublicKey, data: ArrayBuffer): Promise<ArrayBuffer> {
public override async onEncrypt(
algorithm: Algorithm,
key: RsaPublicKey,
data: ArrayBuffer
): Promise<ArrayBuffer> {
const options = this.toCryptoOptions(key);
const enc = crypto.publicEncrypt(options, new Uint8Array(data));
return new Uint8Array(enc).buffer;
}
public async onDecrypt(algorithm: Algorithm, key: RsaPrivateKey, data: ArrayBuffer): Promise<ArrayBuffer> {
public override async onDecrypt(
algorithm: Algorithm,
key: RsaPrivateKey,
data: ArrayBuffer
): Promise<ArrayBuffer> {
const options = this.toCryptoOptions(key);
const dec = crypto.privateDecrypt(options, new Uint8Array(data));
return new Uint8Array(dec).buffer;
}
public async onExportKey(format: KeyFormat, key: CryptoKey): Promise<JsonWebKey | ArrayBuffer> {
public override async onExportKey(
format: KeyFormat,
key: CryptoKey
): Promise<JsonWebKey | ArrayBuffer> {
return RsaCrypto.exportKey(format, getCryptoKey(key));
}
public async onImportKey(format: KeyFormat, keyData: JsonWebKey | ArrayBuffer, algorithm: RsaHashedImportParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
const key = await RsaCrypto.importKey(format, keyData, { ...algorithm, name: this.name }, extractable, keyUsages);
public override async onImportKey(
format: KeyFormat,
keyData: JsonWebKey | ArrayBuffer,
algorithm: RsaHashedImportParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKey> {
const key = await RsaCrypto.importKey(
format,
keyData,
{ ...algorithm, name: this.name },
extractable,
keyUsages
);
return setCryptoKey(key);
}
public checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
public override checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
super.checkCryptoKey(key, keyUsage);
const internalKey = getCryptoKey(key);
if (!(internalKey instanceof RsaPrivateKey || internalKey instanceof RsaPublicKey)) {
if (
!(
internalKey instanceof RsaPrivateKey ||
internalKey instanceof RsaPublicKey
)
) {
throw new TypeError("key: Is not RSA CryptoKey");
}
}
@ -86,7 +123,9 @@ export class RsaEsProvider extends core.ProviderCrypto {
private toCryptoOptions(key: RsaPrivateKey | RsaPublicKey) {
const type = key.type.toUpperCase();
return {
key: `-----BEGIN ${type} KEY-----\n${getCryptoKey(key).data.toString("base64")}\n-----END ${type} KEY-----`,
key: `-----BEGIN ${type} KEY-----\n${getCryptoKey(key).data.toString(
"base64"
)}\n-----END ${type} KEY-----`,
padding: crypto.constants.RSA_PKCS1_PADDING,
};
}

View File

@ -1,10 +1,11 @@
import { Buffer } from "buffer";
import crypto from "crypto";
import * as core from "webcrypto-core";
import { ShaCrypto } from "../sha/crypto";
import { setCryptoKey, getCryptoKey } from "../storage";
import { RsaCrypto } from "./crypto";
import { RsaPrivateKey } from "./private_key";
import { RsaPublicKey } from "./public_key";
import { ShaCrypto } from "../sha/crypto";
import { setCryptoKey, getCryptoKey } from "../storage";
/**
* Source code for decrypt, encrypt, mgf1 functions is from asmcrypto module
@ -14,23 +15,31 @@ import { RsaPublicKey } from "./public_key";
*/
export class RsaOaepProvider extends core.RsaOaepProvider {
public async onGenerateKey(algorithm: RsaHashedKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<core.CryptoKeyPair> {
public async onGenerateKey(
algorithm: RsaHashedKeyGenParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKeyPair> {
const keys = await RsaCrypto.generateKey(
{
...algorithm,
name: this.name,
},
extractable,
keyUsages);
keyUsages
);
return {
privateKey: setCryptoKey(keys.privateKey as RsaPrivateKey),
publicKey: setCryptoKey(keys.publicKey as RsaPublicKey),
};
return {
privateKey: setCryptoKey(keys.privateKey as RsaPrivateKey),
publicKey: setCryptoKey(keys.publicKey as RsaPublicKey),
};
}
public async onEncrypt(algorithm: RsaOaepParams, key: RsaPublicKey, data: ArrayBuffer): Promise<ArrayBuffer> {
public async onEncrypt(
algorithm: RsaOaepParams,
key: RsaPublicKey,
data: ArrayBuffer
): Promise<ArrayBuffer> {
const internalKey = getCryptoKey(key) as RsaPublicKey;
const dataView = new Uint8Array(data);
const keySize = Math.ceil(internalKey.algorithm.modulusLength >> 3);
@ -48,37 +57,59 @@ export class RsaOaepProvider extends core.RsaOaepProvider {
dataBlock.set(dataView, hashSize + psLength + 1);
const labelHash = crypto.createHash(internalKey.algorithm.hash.name.replace("-", ""))
.update(core.BufferSourceConverter.toUint8Array(algorithm.label || new Uint8Array(0)))
const labelHash = crypto
.createHash(internalKey.algorithm.hash.name.replace("-", ""))
.update(
core.BufferSourceConverter.toUint8Array(
algorithm.label || new Uint8Array(0)
)
)
.digest();
dataBlock.set(labelHash, 0);
dataBlock[hashSize + psLength] = 1;
crypto.randomFillSync(seed);
const dataBlockMask = this.mgf1(internalKey.algorithm.hash, seed, dataBlock.length);
const dataBlockMask = this.mgf1(
internalKey.algorithm.hash,
seed,
dataBlock.length
);
for (let i = 0; i < dataBlock.length; i++) {
dataBlock[i] ^= dataBlockMask[i];
}
const seedMask = this.mgf1(internalKey.algorithm.hash, dataBlock, seed.length);
const seedMask = this.mgf1(
internalKey.algorithm.hash,
dataBlock,
seed.length
);
for (let i = 0; i < seed.length; i++) {
seed[i] ^= seedMask[i];
}
if (!internalKey.pem) {
internalKey.pem = `-----BEGIN PUBLIC KEY-----\n${internalKey.data.toString("base64")}\n-----END PUBLIC KEY-----`;
internalKey.pem = `-----BEGIN PUBLIC KEY-----\n${internalKey.data.toString(
"base64"
)}\n-----END PUBLIC KEY-----`;
}
const pkcs0 = crypto.publicEncrypt({
key: internalKey.pem,
padding: crypto.constants.RSA_NO_PADDING,
}, Buffer.from(message));
const pkcs0 = crypto.publicEncrypt(
{
key: internalKey.pem,
padding: crypto.constants.RSA_NO_PADDING,
},
Buffer.from(message)
);
return new Uint8Array(pkcs0).buffer;
}
public async onDecrypt(algorithm: RsaOaepParams, key: RsaPrivateKey, data: ArrayBuffer): Promise<ArrayBuffer> {
public async onDecrypt(
algorithm: RsaOaepParams,
key: RsaPrivateKey,
data: ArrayBuffer
): Promise<ArrayBuffer> {
const internalKey = getCryptoKey(key) as RsaPrivateKey;
const keySize = Math.ceil(internalKey.algorithm.modulusLength >> 3);
const hashSize = ShaCrypto.size(internalKey.algorithm.hash) >> 3;
@ -89,13 +120,18 @@ export class RsaOaepProvider extends core.RsaOaepProvider {
}
if (!internalKey.pem) {
internalKey.pem = `-----BEGIN PRIVATE KEY-----\n${internalKey.data.toString("base64")}\n-----END PRIVATE KEY-----`;
internalKey.pem = `-----BEGIN PRIVATE KEY-----\n${internalKey.data.toString(
"base64"
)}\n-----END PRIVATE KEY-----`;
}
let pkcs0 = crypto.privateDecrypt({
key: internalKey.pem,
padding: crypto.constants.RSA_NO_PADDING,
}, Buffer.from(data));
let pkcs0 = crypto.privateDecrypt(
{
key: internalKey.pem,
padding: crypto.constants.RSA_NO_PADDING,
},
Buffer.from(data)
);
const z = pkcs0[0];
const seed = pkcs0.subarray(1, hashSize + 1);
const dataBlock = pkcs0.subarray(hashSize + 1);
@ -104,18 +140,31 @@ export class RsaOaepProvider extends core.RsaOaepProvider {
throw new Error("Decryption failed");
}
const seedMask = this.mgf1(internalKey.algorithm.hash, dataBlock, seed.length);
const seedMask = this.mgf1(
internalKey.algorithm.hash,
dataBlock,
seed.length
);
for (let i = 0; i < seed.length; i++) {
seed[i] ^= seedMask[i];
}
const dataBlockMask = this.mgf1(internalKey.algorithm.hash, seed, dataBlock.length);
const dataBlockMask = this.mgf1(
internalKey.algorithm.hash,
seed,
dataBlock.length
);
for (let i = 0; i < dataBlock.length; i++) {
dataBlock[i] ^= dataBlockMask[i];
}
const labelHash = crypto.createHash(internalKey.algorithm.hash.name.replace("-", ""))
.update(core.BufferSourceConverter.toUint8Array(algorithm.label || new Uint8Array(0)))
const labelHash = crypto
.createHash(internalKey.algorithm.hash.name.replace("-", ""))
.update(
core.BufferSourceConverter.toUint8Array(
algorithm.label || new Uint8Array(0)
)
)
.digest();
for (let i = 0; i < hashSize; i++) {
if (labelHash[i] !== dataBlock[i]) {
@ -142,19 +191,39 @@ export class RsaOaepProvider extends core.RsaOaepProvider {
return new Uint8Array(pkcs0).buffer;
}
public async onExportKey(format: KeyFormat, key: CryptoKey): Promise<JsonWebKey | ArrayBuffer> {
public async onExportKey(
format: KeyFormat,
key: CryptoKey
): Promise<JsonWebKey | ArrayBuffer> {
return RsaCrypto.exportKey(format, getCryptoKey(key));
}
public async onImportKey(format: KeyFormat, keyData: JsonWebKey | ArrayBuffer, algorithm: RsaHashedImportParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
const key = await RsaCrypto.importKey(format, keyData, { ...algorithm, name: this.name }, extractable, keyUsages);
public async onImportKey(
format: KeyFormat,
keyData: JsonWebKey | ArrayBuffer,
algorithm: RsaHashedImportParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKey> {
const key = await RsaCrypto.importKey(
format,
keyData,
{ ...algorithm, name: this.name },
extractable,
keyUsages
);
return setCryptoKey(key);
}
public checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
public override checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
super.checkCryptoKey(key, keyUsage);
const internalKey = getCryptoKey(key);
if (!(internalKey instanceof RsaPrivateKey || internalKey instanceof RsaPublicKey)) {
if (
!(
internalKey instanceof RsaPrivateKey ||
internalKey instanceof RsaPublicKey
)
) {
throw new TypeError("key: Is not RSA CryptoKey");
}
}
@ -178,7 +247,8 @@ export class RsaOaepProvider extends core.RsaOaepProvider {
const submask = mask.subarray(i * hashSize);
let chunk = crypto.createHash(algorithm.name.replace("-", ""))
let chunk = crypto
.createHash(algorithm.name.replace("-", ""))
.update(seed)
.update(counter)
.digest() as Uint8Array;
@ -191,5 +261,4 @@ export class RsaOaepProvider extends core.RsaOaepProvider {
return mask;
}
}

View File

@ -1,49 +1,102 @@
import * as core from "webcrypto-core";
import { setCryptoKey, getCryptoKey } from "../storage";
import { RsaCrypto } from "./crypto";
import { RsaPrivateKey } from "./private_key";
import { RsaPublicKey } from "./public_key";
import { setCryptoKey, getCryptoKey } from "../storage";
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<core.CryptoKeyPair> {
public async onGenerateKey(
algorithm: RsaHashedKeyGenParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKeyPair> {
const keys = await RsaCrypto.generateKey(
{
...algorithm,
name: this.name,
},
extractable,
keyUsages);
keyUsages
);
return {
privateKey: setCryptoKey(keys.privateKey as RsaPrivateKey),
publicKey: setCryptoKey(keys.publicKey as RsaPublicKey),
};
return {
privateKey: setCryptoKey(keys.privateKey as RsaPrivateKey),
publicKey: setCryptoKey(keys.publicKey as RsaPublicKey),
};
}
public async onSign(algorithm: RsaPssParams, key: RsaPrivateKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return RsaCrypto.sign(algorithm, getCryptoKey(key) as RsaPrivateKey, new Uint8Array(data));
public async onSign(
algorithm: RsaPssParams,
key: RsaPrivateKey,
data: ArrayBuffer
): Promise<ArrayBuffer> {
return RsaCrypto.sign(
algorithm,
getCryptoKey(key) as RsaPrivateKey,
new Uint8Array(data)
);
}
public async onVerify(algorithm: RsaPssParams, key: RsaPublicKey, signature: ArrayBuffer, data: ArrayBuffer): Promise<boolean> {
return RsaCrypto.verify(algorithm, getCryptoKey(key) as RsaPublicKey, new Uint8Array(signature), new Uint8Array(data));
public async onVerify(
algorithm: RsaPssParams,
key: RsaPublicKey,
signature: ArrayBuffer,
data: ArrayBuffer
): Promise<boolean> {
return RsaCrypto.verify(
algorithm,
getCryptoKey(key) as RsaPublicKey,
new Uint8Array(signature),
new Uint8Array(data)
);
}
public async onExportKey(format: KeyFormat, key: CryptoKey): Promise<JsonWebKey | ArrayBuffer> {
public async onExportKey(
format: KeyFormat,
key: CryptoKey
): Promise<JsonWebKey | ArrayBuffer> {
return RsaCrypto.exportKey(format, getCryptoKey(key));
}
public async onImportKey(format: KeyFormat, keyData: JsonWebKey | ArrayBuffer, algorithm: RsaHashedImportParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
const key = await RsaCrypto.importKey(format, keyData, {...algorithm, name: this.name}, extractable, keyUsages);
public async onImportKey(
format: KeyFormat,
keyData: JsonWebKey | ArrayBuffer,
algorithm: RsaHashedImportParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKey> {
const key = await RsaCrypto.importKey(
format,
keyData,
{ ...algorithm, name: this.name },
extractable,
keyUsages
);
return setCryptoKey(key);
}
public checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
public override checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
super.checkCryptoKey(key, keyUsage);
const internalKey = getCryptoKey(key);
if (!(internalKey instanceof RsaPrivateKey || internalKey instanceof RsaPublicKey)) {
if (
!(
internalKey instanceof RsaPrivateKey ||
internalKey instanceof RsaPublicKey
)
) {
throw new TypeError("key: Is not RSA CryptoKey");
}
}
}

View File

@ -1,49 +1,102 @@
import * as core from "webcrypto-core";
import { setCryptoKey, getCryptoKey } from "../storage";
import { RsaCrypto } from "./crypto";
import { RsaPrivateKey } from "./private_key";
import { RsaPublicKey } from "./public_key";
import { setCryptoKey, getCryptoKey } from "../storage";
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<core.CryptoKeyPair> {
public async onGenerateKey(
algorithm: RsaHashedKeyGenParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<globalThis.CryptoKeyPair> {
const keys = await RsaCrypto.generateKey(
{
...algorithm,
name: this.name,
},
extractable,
keyUsages);
keyUsages
);
return {
privateKey: setCryptoKey(keys.privateKey as RsaPrivateKey),
publicKey: setCryptoKey(keys.publicKey as RsaPublicKey),
};
return {
privateKey: setCryptoKey(keys.privateKey as RsaPrivateKey),
publicKey: setCryptoKey(keys.publicKey as RsaPublicKey),
};
}
public async onSign(algorithm: Algorithm, key: RsaPrivateKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return RsaCrypto.sign(algorithm, getCryptoKey(key) as RsaPrivateKey, new Uint8Array(data));
public async onSign(
algorithm: Algorithm,
key: RsaPrivateKey,
data: ArrayBuffer
): Promise<ArrayBuffer> {
return RsaCrypto.sign(
algorithm,
getCryptoKey(key) as RsaPrivateKey,
new Uint8Array(data)
);
}
public async onVerify(algorithm: Algorithm, key: RsaPublicKey, signature: ArrayBuffer, data: ArrayBuffer): Promise<boolean> {
return RsaCrypto.verify(algorithm, getCryptoKey(key) as RsaPublicKey, new Uint8Array(signature), new Uint8Array(data));
public async onVerify(
algorithm: Algorithm,
key: RsaPublicKey,
signature: ArrayBuffer,
data: ArrayBuffer
): Promise<boolean> {
return RsaCrypto.verify(
algorithm,
getCryptoKey(key) as RsaPublicKey,
new Uint8Array(signature),
new Uint8Array(data)
);
}
public async onExportKey(format: KeyFormat, key: CryptoKey): Promise<JsonWebKey | ArrayBuffer> {
public async onExportKey(
format: KeyFormat,
key: CryptoKey
): Promise<JsonWebKey | ArrayBuffer> {
return RsaCrypto.exportKey(format, getCryptoKey(key));
}
public async onImportKey(format: KeyFormat, keyData: JsonWebKey | ArrayBuffer, algorithm: RsaHashedImportParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
const key = await RsaCrypto.importKey(format, keyData, {...algorithm, name: this.name}, extractable, keyUsages);
public async onImportKey(
format: KeyFormat,
keyData: JsonWebKey | ArrayBuffer,
algorithm: RsaHashedImportParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKey> {
const key = await RsaCrypto.importKey(
format,
keyData,
{ ...algorithm, name: this.name },
extractable,
keyUsages
);
return setCryptoKey(key);
}
public checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
public override checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
super.checkCryptoKey(key, keyUsage);
const internalKey = getCryptoKey(key);
if (!(internalKey instanceof RsaPrivateKey || internalKey instanceof RsaPublicKey)) {
if (
!(
internalKey instanceof RsaPrivateKey ||
internalKey instanceof RsaPublicKey
)
) {
throw new TypeError("key: Is not RSA CryptoKey");
}
}
}

View File

@ -1,7 +1,7 @@
import { Buffer } from "buffer";
import crypto from "crypto";
export class ShaCrypto {
/**
* Returns size of the hash algorithm in bits
* @param algorithm Hash algorithm
@ -12,20 +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");
}
}
public static digest(algorithm: Algorithm, data: ArrayBuffer) {
const hash = crypto.createHash(algorithm.name.replace("-", ""))
.update(Buffer.from(data)).digest();
return new Uint8Array(hash).buffer;
/**
* 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 hashAlg = this.getAlgorithmName(algorithm);
const hash = crypto.createHash(hashAlg).update(Buffer.from(data)).digest();
return new Uint8Array(hash).buffer;
}
}

View File

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

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

@ -0,0 +1,14 @@
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);
}
}

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

@ -0,0 +1,14 @@
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);
}
}

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

@ -0,0 +1,14 @@
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

@ -5,8 +5,10 @@ export class Sha1Provider extends core.ProviderCrypto {
public name = "SHA-1";
public usages = [];
public async onDigest(algorithm: Algorithm, data: ArrayBuffer): Promise<ArrayBuffer> {
public override async onDigest(
algorithm: Algorithm,
data: ArrayBuffer
): Promise<ArrayBuffer> {
return ShaCrypto.digest(algorithm, data);
}
}

View File

@ -5,8 +5,10 @@ export class Sha256Provider extends core.ProviderCrypto {
public name = "SHA-256";
public usages = [];
public async onDigest(algorithm: Algorithm, data: ArrayBuffer): Promise<ArrayBuffer> {
public override async onDigest(
algorithm: Algorithm,
data: ArrayBuffer
): Promise<ArrayBuffer> {
return ShaCrypto.digest(algorithm, data);
}
}

View File

@ -5,8 +5,10 @@ export class Sha384Provider extends core.ProviderCrypto {
public name = "SHA-384";
public usages = [];
public async onDigest(algorithm: Algorithm, data: ArrayBuffer): Promise<ArrayBuffer> {
public override async onDigest(
algorithm: Algorithm,
data: ArrayBuffer
): Promise<ArrayBuffer> {
return ShaCrypto.digest(algorithm, data);
}
}

View File

@ -5,8 +5,10 @@ export class Sha512Provider extends core.ProviderCrypto {
public name = "SHA-512";
public usages = [];
public async onDigest(algorithm: Algorithm, data: ArrayBuffer): Promise<ArrayBuffer> {
public override async onDigest(
algorithm: Algorithm,
data: ArrayBuffer
): Promise<ArrayBuffer> {
return ShaCrypto.digest(algorithm, data);
}
}

19
src/mechs/shake/crypto.ts Normal file
View File

@ -0,0 +1,19 @@
import { Buffer } from "buffer";
import crypto from "crypto";
import * as core from "webcrypto-core";
export class ShakeCrypto {
public static digest(
algorithm: Required<core.ShakeParams>,
data: ArrayBuffer
) {
const hash = crypto
.createHash(algorithm.name.toLowerCase(), {
outputLength: algorithm.length,
})
.update(Buffer.from(data))
.digest();
return new Uint8Array(hash).buffer;
}
}

3
src/mechs/shake/index.ts Normal file
View File

@ -0,0 +1,3 @@
export * from "./crypto";
export * from "./shake128";
export * from "./shake256";

View File

@ -0,0 +1,11 @@
import * as core from "webcrypto-core";
import { ShakeCrypto } from "./crypto";
export class Shake128Provider extends core.Shake128Provider {
public override async onDigest(
algorithm: Required<core.ShakeParams>,
data: ArrayBuffer
): Promise<ArrayBuffer> {
return ShakeCrypto.digest(algorithm, data);
}
}

View File

@ -0,0 +1,11 @@
import * as core from "webcrypto-core";
import { ShakeCrypto } from "./crypto";
export class Shake256Provider extends core.Shake256Provider {
public override async onDigest(
algorithm: Required<core.ShakeParams>,
data: ArrayBuffer
): Promise<ArrayBuffer> {
return ShakeCrypto.digest(algorithm, data);
}
}

View File

@ -12,7 +12,12 @@ export function getCryptoKey(key: core.CryptoKey) {
}
export function setCryptoKey(value: InternalCryptoKey) {
const key = core.CryptoKey.create(value.algorithm, value.type, value.extractable, value.usages);
const key = core.CryptoKey.create(
value.algorithm,
value.type,
value.extractable,
value.usages
);
Object.freeze(key);
keyStorage.set(key, value);

View File

@ -1,16 +1,35 @@
import * as crypto from "crypto";
import * as process from "process";
import * as core from "webcrypto-core";
import {
AesCbcProvider, AesCmacProvider, AesCtrProvider, AesEcbProvider, AesGcmProvider, AesKwProvider,
AesCbcProvider,
AesCmacProvider,
AesCtrProvider,
AesEcbProvider,
AesGcmProvider,
AesKwProvider,
DesCbcProvider,
DesEde3CbcProvider, EcdhProvider,
EcdsaProvider, HkdfProvider,
DesEde3CbcProvider,
EcdhProvider,
EcdsaProvider,
HkdfProvider,
EdDsaProvider,
EcdhEsProvider,
HmacProvider,
Pbkdf2Provider,
RsaEsProvider, RsaOaepProvider, RsaPssProvider, RsaSsaProvider,
Sha1Provider, Sha256Provider, Sha384Provider, Sha512Provider,
RsaEsProvider,
RsaOaepProvider,
RsaPssProvider,
RsaSsaProvider,
Sha1Provider,
Sha256Provider,
Sha384Provider,
Sha512Provider,
Shake128Provider,
Shake256Provider,
Sha3256Provider,
Sha3384Provider,
Sha3512Provider,
} from "./mechs";
export class SubtleCrypto extends core.SubtleCrypto {
@ -27,7 +46,10 @@ export class SubtleCrypto extends core.SubtleCrypto {
//#endregion
//#region DES
this.providers.set(new DesCbcProvider());
const ciphers = crypto.getCiphers();
if (ciphers.includes("des-cbc")) {
this.providers.set(new DesCbcProvider());
}
this.providers.set(new DesEde3CbcProvider());
//#endregion
@ -63,6 +85,24 @@ export class SubtleCrypto extends core.SubtleCrypto {
//#endregion
const nodeMajorVersion = /^v(\d+)/.exec(process.version)?.[1];
if (nodeMajorVersion && parseInt(nodeMajorVersion, 10) >= 12) {
//#region SHAKE
this.providers.set(new Shake128Provider());
this.providers.set(new Shake256Provider());
//#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());

View File

@ -1,5 +1,6 @@
import assert from "assert";
import process from "process";
import assert from "node:assert";
import nodeCrypto from "node:crypto";
import process from "node:process";
import { WebcryptoTest } from "@peculiar/webcrypto-test";
import { Convert } from "pvtsutils";
import * as core from "webcrypto-core";
@ -10,7 +11,11 @@ const nodeMajorVersion = parseInt(/^v(\d+)/.exec(process.version)![1], 10);
const crypto = new Crypto();
WebcryptoTest.check(crypto as any, {});
const ciphers = nodeCrypto.getCiphers();
WebcryptoTest.check(crypto as any, {
DESCBC: !ciphers.includes("des-cbc"),
});
context("Crypto", () => {
context("getRandomValues", () => {
@ -60,7 +65,7 @@ context("Crypto", () => {
hash: "SHA-256",
info: new Uint8Array([1, 2, 3, 4, 5]),
salt: new Uint8Array([1, 2, 3, 4, 5]),
} as globalThis.HkdfParams,
} as HkdfParams,
hkdf,
{
name: "HMAC",
@ -75,6 +80,18 @@ context("Crypto", () => {
context("generateKey", () => {
it("RSA 3072bits", async () => {
const alg: globalThis.RsaHashedKeyGenParams = {
name: "RSASSA-PKCS1-v1_5",
hash: "SHA-256",
publicExponent: new Uint8Array([1, 0, 1]),
modulusLength: 3072,
};
const keys = await crypto.subtle.generateKey(alg, false, ["sign", "verify"]);
assert.strictEqual((keys.privateKey.algorithm as RsaHashedKeyAlgorithm).modulusLength, 3072);
});
it("Ed25519", async () => {
const keys = await crypto.subtle.generateKey({ name: "eddsa", namedCurve: "ed25519" } as globalThis.EcKeyGenParams, false, ["sign", "verify"]);
@ -214,4 +231,77 @@ context("Crypto", () => {
assert.strictEqual(hmacKey.algorithm.name, "HMAC");
});
context("shake digest", () => {
const data = Buffer.from("test data");
context("shake128", () => {
it("default", async () => {
const hash = await crypto.subtle.digest("shake128", data);
assert.strictEqual(Buffer.from(hash).toString("hex"), "ae3bdcf04986a8e7ddd99ac948254693");
});
it("128 byte length", async () => {
const hash = await crypto.subtle.digest({ name: "shake128", length: 128 } as core.ShakeParams, data);
assert.strictEqual(Buffer.from(hash).toString("hex"), "ae3bdcf04986a8e7ddd99ac948254693fc32ca6ce3ed278c0c54127f072ba21e977d76aa76cab8f85f61c3e1fb7dab42c6b96d39f96fbd8cdcba7121e28cc97bb51f277a00398f99a9e6f11d027473cbffb3ac4ce444e2e8284caeca4e62f725d340fa3519eec7ca3eb4188607c26b0ecdf3750beba8882d6f2b734960cca914");
});
});
context("shake128", () => {
it("default", async () => {
const hash = await crypto.subtle.digest("shake256", data);
assert.strictEqual(Buffer.from(hash).toString("hex"), "be15253026b9a85e01ae54b1939284e8e514fbdad2a3bd5c1c0f437e60548e26");
});
it("256 byte length", async () => {
const hash = await crypto.subtle.digest({ name: "shake256", length: 256 } as core.ShakeParams, data);
assert.strictEqual(Buffer.from(hash).toString("hex"), "be15253026b9a85e01ae54b1939284e8e514fbdad2a3bd5c1c0f437e60548e262dd68c2a2f932847f9610eeb51f8ba1a180ca878c788e900d899538d45c9c4a6f1bf10d8502a7ccbd9fd540bd856591000700e10130673ef970ffb788afe08426648a216d032733b71e85f128f1ed9e4c8bd910b5000e8c381afb45735680eaf7cb5bf1ae4265ee0822dfe6a9426ff21e309398df57cbf5861f5947f3d261e2d4517ff0d1be988e7014a09c4312d37010cf0e47468c1cf832e6a61e9d9fe3b67e6ab265cb6d95ad7a1f863d71e0e6ed5cd17d568b86e99d84bdb970a580f551017b501ae6761d2d6de76a64385dc10f27d18c2564a6bfbfb1e3f335010bebdf8");
});
});
});
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");
});
});
context("ECDH deriveBits with null", () => {
it("P-256", async () => {
const keyPair = await crypto.subtle.generateKey({ name: "ECDH", namedCurve: "P-256" }, false, ["deriveBits"]);
const bits = await crypto.subtle.deriveBits({ name: keyPair.publicKey.algorithm.name, public: keyPair.publicKey } as globalThis.EcdhKeyDeriveParams, keyPair.privateKey, <number><unknown>null);
assert.equal(bits.byteLength, 32);
});
it("P-384", async () => {
const keyPair = await crypto.subtle.generateKey({ name: "ECDH", namedCurve: "P-384" }, false, ["deriveBits"]);
const bits = await crypto.subtle.deriveBits({ name: keyPair.publicKey.algorithm.name, public: keyPair.publicKey } as globalThis.EcdhKeyDeriveParams, keyPair.privateKey, <number><unknown>null);
assert.equal(bits.byteLength, 48);
});
it("P-521", async () => {
const keyPair = await crypto.subtle.generateKey({ name: "ECDH", namedCurve: "P-521" }, false, ["deriveBits"]);
const bits = await crypto.subtle.deriveBits({ name: keyPair.publicKey.algorithm.name, public: keyPair.publicKey } as globalThis.EcdhKeyDeriveParams, keyPair.privateKey, <number><unknown>null);
assert.equal(bits.byteLength, 66);
});
});
});

View File

@ -1,59 +1,19 @@
{
"compilerOptions": {
/* Basic Options */
"target": "es2018", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "./", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
"removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
"importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
"experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
}
"target": "es2019",
"module": "commonjs",
"lib": [
"dom"
],
"removeComments": true,
"importHelpers": true,
"strict": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"noImplicitOverride": true
},
"exclude": [
"build/**/*",
"index.d.ts"
]
}

2070
yarn.lock Normal file

File diff suppressed because it is too large Load Diff