Add files

This commit is contained in:
microshine 2019-01-25 13:43:13 +03:00
commit c0d66d0a4c
78 changed files with 5413 additions and 0 deletions

14
.gitignore vendored Normal file
View File

@ -0,0 +1,14 @@
/node_modules
# VS Code
/.vscode
# Coverage
/.nyc_output
/coverage
# Output
index.js
# MacOS
.DS_Store

9
.npmignore Normal file
View File

@ -0,0 +1,9 @@
.nyc_output
.vscode
coverage
node_modules
src
test
rollup.config.js
tsconfig.json
tslint.json

74
README.md Normal file
View File

@ -0,0 +1,74 @@
# node-webcrypto
[![License](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://raw.githubusercontent.com/PeculiarVentures/node-webcrypto-ossl/master/LICENSE)
[![Build Status](https://travis-ci.org/PeculiarVentures/node-webcrypto-ossl.svg?branch=master)](https://travis-ci.org/PeculiarVentures/node-webcrypto-ossl)
[![Coverage Status](https://coveralls.io/repos/github/PeculiarVentures/node-webcrypto-ossl/badge.svg?branch=master)](https://coveralls.io/github/PeculiarVentures/node-webcrypto-ossl?branch=master)
[![npm version](https://badge.fury.io/js/node-webcrypto-ossl.svg)](https://badge.fury.io/js/node-webcrypto-ossl)
[![NPM](https://nodei.co/npm/node-webcrypto-ossl.png)](https://nodei.co/npm/node-webcrypto-ossl/)
We wanted to be able to write Javascript that used crypto on both the client and the server but we did not want to rely on Javascript implementations of crypto. The only native cryptography available in browser is [Web Crypto](http://caniuse.com/#search=cryptography), this resulted in us creating a `@peculiar/webcrypto`.
## Table Of Contents
* [WARNING](#warning)
* [Installing](#installing)
* [Using](#using)
* [Examples](#examples)
* [Bug Reporting](#bug-reporting)
* [Related](#related)
## WARNING
**At this time this solution should be considered suitable for research and experimentation, further code and security review is needed before utilization in a production application.**
## Installing
```
npm install @peculiar/webcrypto
```
## Supported algorithms
| Algorithm name | generateKey | digest | export/import | sign/verify | encrypt/decrypt | wrapKey/unwrapKey | derive |
|-------------------|-------------|---------|---------------|-------------|-----------------|-------------------|---------|
| SHA-1 | | X | | | | | |
| SHA-256 | | X | | | | | |
| SHA-384 | | X | | | | | |
| SHA-512 | | X | | | | | |
| HMAC | X | | X | X | | | |
| RSASSA-PKCS1-v1_5 | X | | X | X | | | |
| RSA-PSS | X | | X | X | | | |
| AES-CBC | X | | X | | X | X | |
| AES-CTR | X | | X | | X | X | |
| AES-GCM | X | | X | | X | X | |
| ECDSA<sup>1</sup> | X | | X | X | | | |
| ECDH<sup>1</sup> | X | | X | | | | X |
| PBKDF2 | | | X | | | | X |
| DES-CBC<sup>2</sup>| X | | X | | X | X | |
| DES-EDE3-CBC<sup>2</sup>| X | | X | | X | X | |
<sup>1</sup> Mechanism supports extended list of named curves `P-256`, `P-384`, `P-521`, and `K-256`
<sup>2</sup> Mechanism is not defined by the WebCrypto specifications. Use of mechanism in a safe way is hard, it was added for the purpose of enabling interoperability with an existing system. We recommend against its use unless needed for interoperability.
## Using
```javascript
const { Crypto } = require("@peculiar/webcrypto");
const crypto = new Crypto();
```
## Examples
See [WebCrypto Docs](https://github.com/PeculiarVentures/webcrypto-docs/blob/master/README.md) for examples
## Bug Reporting
Please report bugs either as pull requests or as issues in the issue tracker. node-webcrypto-ossl has a full disclosure vulnerability policy. Please do NOT attempt to report any security vulnerability in this code privately to anybody.
## Related
- [node-webcrypto-ossl](https://github.com/PeculiarVentures/node-webcrypto-ossl)
- [node-webcrypto-p11](https://github.com/PeculiarVentures/node-webcrypto-p11)
- [webcrypto-liner](https://github.com/PeculiarVentures/webcrypto-liner)

693
package-lock.json generated Normal file
View File

@ -0,0 +1,693 @@
{
"name": "node-webcrypto",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@peculiar/asn1-schema": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-1.0.1.tgz",
"integrity": "sha512-uz7SW23x50yc7tkoPe5Si5uIkzF8Cbvo0auM+wI++SzGPO9jRckHn7rVMUI4R5jPBL7y2h3WDMiWR/Fz3Ui9sg==",
"requires": {
"asn1js": "^2.0.21",
"tslib": "^1.9.3"
}
},
"@peculiar/json-schema": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@peculiar/json-schema/-/json-schema-1.0.7.tgz",
"integrity": "sha512-6qc5ILb5kD70rAqqoAdmdv7Bxh4ENNUz6O8oTW1B922yXAf6yMIQP9nGqyle2j2VH5WFokciaNwZFsYazluKxQ==",
"requires": {
"tslib": "^1.9.3"
}
},
"@types/mocha": {
"version": "5.2.5",
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.5.tgz",
"integrity": "sha512-lAVp+Kj54ui/vLUFxsJTMtWvZraZxum3w3Nwkble2dNuV5VnPA+Mi2oGX9XYJAaIvZi3tn3cbjS/qcJXRb6Bww==",
"dev": true
},
"@types/node": {
"version": "10.12.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.10.tgz",
"integrity": "sha512-8xZEYckCbUVgK8Eg7lf5Iy4COKJ5uXlnIOnePN0WUwSQggy9tolM+tDJf7wMOnT/JT/W9xDYIaYggt3mRV2O5w==",
"dev": true
},
"arr-diff": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
"integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
"dev": true,
"requires": {
"arr-flatten": "^1.0.1"
}
},
"arr-flatten": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
"integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
"dev": true
},
"array-unique": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
"integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
"dev": true
},
"arrify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
"integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
"dev": true
},
"asn1js": {
"version": "2.0.21",
"resolved": "https://registry.npmjs.org/asn1js/-/asn1js-2.0.21.tgz",
"integrity": "sha512-yfRxOLyqiUvtn0pO7t//tdEet9ZWBWw5nrsHkdSfC5lLKOHYvhdPcULPD3KANSfGCOqHWXxndmOmXf0tZWjNPg==",
"requires": {
"pvutils": "^1.0.16"
}
},
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"braces": {
"version": "1.8.5",
"resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
"integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
"dev": true,
"requires": {
"expand-range": "^1.8.1",
"preserve": "^0.2.0",
"repeat-element": "^1.1.2"
}
},
"browser-stdout": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
"integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
"dev": true
},
"buffer-from": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
"dev": true
},
"commander": {
"version": "2.15.1",
"resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
"integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
"dev": true
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true
},
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
},
"diff": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
"dev": true
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"dev": true
},
"estree-walker": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.5.2.tgz",
"integrity": "sha512-XpCnW/AE10ws/kDAs37cngSkvgIR8aN3G0MS85m7dUpuK2EREo9VJ00uvw6Dg/hXEpfsE1I1TvJOJr+Z+TL+ig==",
"dev": true
},
"expand-brackets": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
"integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=",
"dev": true,
"requires": {
"is-posix-bracket": "^0.1.0"
}
},
"expand-range": {
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz",
"integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=",
"dev": true,
"requires": {
"fill-range": "^2.1.0"
}
},
"extglob": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
"integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=",
"dev": true,
"requires": {
"is-extglob": "^1.0.0"
}
},
"filename-regex": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz",
"integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=",
"dev": true
},
"fill-range": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz",
"integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==",
"dev": true,
"requires": {
"is-number": "^2.1.0",
"isobject": "^2.0.0",
"randomatic": "^3.0.0",
"repeat-element": "^1.1.2",
"repeat-string": "^1.5.2"
}
},
"for-in": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
"integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
"dev": true
},
"for-own": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz",
"integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=",
"dev": true,
"requires": {
"for-in": "^1.0.1"
}
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true
},
"glob": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"glob-base": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz",
"integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=",
"dev": true,
"requires": {
"glob-parent": "^2.0.0",
"is-glob": "^2.0.0"
}
},
"glob-parent": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
"integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
"dev": true,
"requires": {
"is-glob": "^2.0.0"
}
},
"growl": {
"version": "1.10.5",
"resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
"integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
"dev": true
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true
},
"he": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
"integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
"dev": true
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"dev": true,
"requires": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"dev": true
},
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"is-dotfile": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz",
"integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=",
"dev": true
},
"is-equal-shallow": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz",
"integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=",
"dev": true,
"requires": {
"is-primitive": "^2.0.0"
}
},
"is-extendable": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
"integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
"dev": true
},
"is-extglob": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
"integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
"dev": true
},
"is-glob": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
"integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
"dev": true,
"requires": {
"is-extglob": "^1.0.0"
}
},
"is-number": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz",
"integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=",
"dev": true,
"requires": {
"kind-of": "^3.0.2"
}
},
"is-posix-bracket": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz",
"integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=",
"dev": true
},
"is-primitive": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz",
"integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=",
"dev": true
},
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
"dev": true
},
"isobject": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
"integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
"dev": true,
"requires": {
"isarray": "1.0.0"
}
},
"kind-of": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
"integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
"dev": true,
"requires": {
"is-buffer": "^1.1.5"
}
},
"make-error": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz",
"integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==",
"dev": true
},
"math-random": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz",
"integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==",
"dev": true
},
"micromatch": {
"version": "2.3.11",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
"integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=",
"dev": true,
"requires": {
"arr-diff": "^2.0.0",
"array-unique": "^0.2.1",
"braces": "^1.8.2",
"expand-brackets": "^0.1.4",
"extglob": "^0.3.1",
"filename-regex": "^2.0.0",
"is-extglob": "^1.0.0",
"is-glob": "^2.0.1",
"kind-of": "^3.0.2",
"normalize-path": "^2.0.1",
"object.omit": "^2.0.0",
"parse-glob": "^3.0.4",
"regex-cache": "^0.4.2"
}
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "1.2.0",
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
},
"mkdirp": {
"version": "0.5.1",
"resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"dev": true,
"requires": {
"minimist": "0.0.8"
},
"dependencies": {
"minimist": {
"version": "0.0.8",
"resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"dev": true
}
}
},
"mocha": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz",
"integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==",
"dev": true,
"requires": {
"browser-stdout": "1.3.1",
"commander": "2.15.1",
"debug": "3.1.0",
"diff": "3.5.0",
"escape-string-regexp": "1.0.5",
"glob": "7.1.2",
"growl": "1.10.5",
"he": "1.1.1",
"minimatch": "3.0.4",
"mkdirp": "0.5.1",
"supports-color": "5.4.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
},
"normalize-path": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
"integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
"dev": true,
"requires": {
"remove-trailing-separator": "^1.0.1"
}
},
"object.omit": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz",
"integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=",
"dev": true,
"requires": {
"for-own": "^0.1.4",
"is-extendable": "^0.1.1"
}
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true,
"requires": {
"wrappy": "1"
}
},
"parse-glob": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz",
"integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=",
"dev": true,
"requires": {
"glob-base": "^0.3.0",
"is-dotfile": "^1.0.0",
"is-extglob": "^1.0.0",
"is-glob": "^2.0.0"
}
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true
},
"path-parse": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
"dev": true
},
"preserve": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz",
"integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=",
"dev": true
},
"pvtsutils": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.0.3.tgz",
"integrity": "sha512-tStF0bg63w6GwUsa3KUU6FmgFbON+CZJtcJJiwivuIw/+LFwAXRXZeqmt1aJq3czt3H5CCR916frulF81Asf6g==",
"requires": {
"@types/node": "^10.12.15",
"tslib": "^1.9.3"
},
"dependencies": {
"@types/node": {
"version": "10.12.15",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.15.tgz",
"integrity": "sha512-9kROxduaN98QghwwHmxXO2Xz3MaWf+I1sLVAA6KJDF5xix+IyXVhds0MAfdNwtcpSrzhaTsNB0/jnL86fgUhqA=="
}
}
},
"pvutils": {
"version": "1.0.16",
"resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.0.16.tgz",
"integrity": "sha512-+8C8cKbON+UH3GxGRdF0xHwiaVhq0+lsHcUgN/MZLCLtvcYhVP3fQPhrnkucK2v/RqRuq4pw1jjyF9PwW9f7mA=="
},
"randomatic": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz",
"integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==",
"dev": true,
"requires": {
"is-number": "^4.0.0",
"kind-of": "^6.0.0",
"math-random": "^1.0.1"
},
"dependencies": {
"is-number": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz",
"integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==",
"dev": true
},
"kind-of": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
"dev": true
}
}
},
"regex-cache": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz",
"integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==",
"dev": true,
"requires": {
"is-equal-shallow": "^0.1.3"
}
},
"remove-trailing-separator": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
"integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
"dev": true
},
"repeat-element": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
"integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==",
"dev": true
},
"repeat-string": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
"integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
"dev": true
},
"resolve": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.9.0.tgz",
"integrity": "sha512-TZNye00tI67lwYvzxCxHGjwTNlUV70io54/Ed4j6PscB8xVfuBJpRenI/o6dVk0cY0PYTY27AgCoGGxRnYuItQ==",
"dev": true,
"requires": {
"path-parse": "^1.0.6"
}
},
"rollup-plugin-typescript": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/rollup-plugin-typescript/-/rollup-plugin-typescript-1.0.0.tgz",
"integrity": "sha512-d2KDNMJXgaaB//dDGd/YmyMiopt1Pz965Iu3zmEoL08YqNcKRBz26uHqqc47rFGfrJV5kFqifC9IYlh6dpSCLg==",
"dev": true,
"requires": {
"resolve": "^1.8.1",
"rollup-pluginutils": "^2.3.1"
}
},
"rollup-pluginutils": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.3.3.tgz",
"integrity": "sha512-2XZwja7b6P5q4RZ5FhyX1+f46xi1Z3qBKigLRZ6VTZjwbN0K1IFGMlwm06Uu0Emcre2Z63l77nq/pzn+KxIEoA==",
"dev": true,
"requires": {
"estree-walker": "^0.5.2",
"micromatch": "^2.3.11"
}
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
},
"source-map-support": {
"version": "0.5.9",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz",
"integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==",
"dev": true,
"requires": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
},
"supports-color": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
"integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
},
"ts-node": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz",
"integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==",
"dev": true,
"requires": {
"arrify": "^1.0.0",
"buffer-from": "^1.1.0",
"diff": "^3.1.0",
"make-error": "^1.1.1",
"minimist": "^1.2.0",
"mkdirp": "^0.5.1",
"source-map-support": "^0.5.6",
"yn": "^2.0.0"
}
},
"tslib": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ=="
},
"typescript": {
"version": "3.1.6",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.6.tgz",
"integrity": "sha512-tDMYfVtvpb96msS1lDX9MEdHrW4yOuZ4Kdc4Him9oU796XldPYF/t2+uKoX0BBa0hXXwDlqYQbXY5Rzjzc5hBA==",
"dev": true
},
"webcrypto-core": {
"version": "0.1.25",
"resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-0.1.25.tgz",
"integrity": "sha512-BjpHEls+jFGwGngxbX7NR1Swnw3vTF0aAYAd42wulMdxd2MHXAgp1V3ZYlLALEvGu8do5HeZcCNW70JsnR5BFQ==",
"requires": {
"tslib": "^1.7.1"
}
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true
},
"yn": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz",
"integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=",
"dev": true
}
}
}

70
package.json Normal file
View File

@ -0,0 +1,70 @@
{
"name": "@peculiar/webcrypto",
"version": "1.0.0",
"description": "A WebCrypto Polyfill for NodeJS",
"repository": {
"type": "git",
"url": "https://github.com/PeculiarVentures/webcrypto.git"
},
"main": "index.js",
"scripts": {
"test": "mocha",
"build": "rollup -c",
"prepare": "npm run build",
"coverage": "nyc npm test",
"coveralls": "nyc report --reporter=text-lcov | coveralls"
},
"keywords": [
"webcrypto",
"crypto",
"sha",
"rsa",
"ec",
"aes",
"des",
"hmac",
"pbkdf2"
],
"author": "PeculiarVentures",
"contributors": [
"Miroshin Stepan<microshine@mail.ru>"
],
"license": "MIT",
"bugs": {
"url": "https://github.com/PeculiarVentures/webcrypto/issues"
},
"homepage": "https://github.com/PeculiarVentures/webcrypto#readme",
"banner": "// Copyright (c) 2019, Peculiar Ventures, All rights reserved.",
"devDependencies": {
"@types/mocha": "^5.2.5",
"@types/node": "^10.12.10",
"mocha": "^5.2.0",
"rollup-plugin-typescript": "^1.0.0",
"ts-node": "^7.0.1",
"typescript": "^3.1.6"
},
"dependencies": {
"@peculiar/asn1-schema": "^1.0.1",
"@peculiar/json-schema": "^1.0.7",
"asn1js": "^2.0.21",
"pvtsutils": "^1.0.3",
"tslib": "^1.9.3",
"webcrypto-core": "^0.1.25"
},
"nyc": {
"extension": [
".ts",
".tsx"
],
"include": [
"src/**/*.ts"
],
"exclude": [
"**/*.d.ts"
],
"reporter": [
"text-summary",
"html"
]
}
}

20
rollup.config.js Normal file
View File

@ -0,0 +1,20 @@
import typescript from "rollup-plugin-typescript";
const pkg = require("./package.json");
const banner = [pkg.banner || "", ""];
export default {
input: "src/index.ts",
plugins: [
typescript({ typescript: require("typescript"), target: "esnext", removeComments: true }),
],
external: Object.keys(pkg.dependencies),
output: [
{
banner: banner.join("\n"),
file: pkg.main,
format: "cjs",
}
]
};

View File

@ -0,0 +1,31 @@
import { AsnProp, AsnPropTypes } from "@peculiar/asn1-schema";
// RFC 5280
// https://tools.ietf.org/html/rfc5280#section-4.1.1.2
//
// AlgorithmIdentifier ::= SEQUENCE {
// algorithm OBJECT IDENTIFIER,
// parameters ANY DEFINED BY algorithm OPTIONAL }
// -- contains a value of the type
// -- registered for use with the
// -- algorithm object identifier value
export type ParametersType = ArrayBuffer | null;
export class AlgorithmIdentifier {
@AsnProp({
type: AsnPropTypes.ObjectIdentifier,
})
public algorithm!: string;
@AsnProp({
type: AsnPropTypes.Any,
optional: true,
})
public parameters?: ParametersType;
constructor(params?: Partial<AlgorithmIdentifier>) {
Object.assign(this, params);
}
}

57
src/asn/ec_private_key.ts Normal file
View File

@ -0,0 +1,57 @@
import { AsnIntegerConverter, AsnParser, AsnProp, AsnPropTypes, AsnSerializer, IAsnConverter } from "@peculiar/asn1-schema";
import { IJsonConvertible } from "@peculiar/json-schema";
import { Convert } from "pvtsutils";
import { EcPublicKey } from "./ec_public_key";
// tslint:disable-next-line: no-var-requires
const asn1 = require("asn1js");
// RFC 5915
// https://tools.ietf.org/html/rfc5915#section-3
//
// ECPrivateKey ::= SEQUENCE {
// version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
// privateKey OCTET STRING,
// parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
// publicKey [1] BIT STRING OPTIONAL
// }
export class EcPrivateKey implements IJsonConvertible {
@AsnProp({ type: AsnPropTypes.Integer, converter: AsnIntegerConverter })
public version = 1;
@AsnProp({ type: AsnPropTypes.OctetString })
public privateKey = new ArrayBuffer(0);
@AsnProp({ context: 0, type: AsnPropTypes.Any, optional: true })
public parameters?: ArrayBuffer;
@AsnProp({ context: 1, type: AsnPropTypes.BitString, optional: true })
public publicKey?: ArrayBuffer;
public fromJSON(json: any): this {
if (!("d" in json)) {
throw new Error("d: Missing required property");
}
this.privateKey = Convert.FromBase64Url(json.d);
if ("x" in json) {
const publicKey = new EcPublicKey();
publicKey.fromJSON(json);
this.publicKey = AsnSerializer.toASN(publicKey).valueBlock.valueHex;
}
return this;
}
public toJSON() {
const jwk: JsonWebKey = {};
jwk.d = Convert.ToBase64Url(this.privateKey);
if (this.publicKey) {
Object.assign(jwk, new EcPublicKey(this.publicKey).toJSON());
}
return jwk;
}
}

64
src/asn/ec_public_key.ts Normal file
View File

@ -0,0 +1,64 @@
import { AsnProp, AsnPropTypes, AsnType, AsnTypeTypes } from "@peculiar/asn1-schema";
import { IJsonConvertible } from "@peculiar/json-schema";
import { Convert } from "pvtsutils";
import * as core from "webcrypto-core";
// RFC 5480
// https://tools.ietf.org/html/rfc5480#section-2.2
//
// ECPoint ::= OCTET STRING
@AsnType({ type: AsnTypeTypes.Choice })
export class EcPublicKey implements IJsonConvertible {
@AsnProp({ type: AsnPropTypes.OctetString })
public value = new ArrayBuffer(0);
constructor(value?: ArrayBuffer) {
if (value) {
this.value = value;
}
}
public toJSON() {
let bytes = new Uint8Array(this.value);
if (bytes[0] !== 0x04) {
throw new core.CryptoError("Wrong ECPoint. Current version supports only Uncompressed (0x04) point");
}
bytes = new Uint8Array(this.value.slice(1));
const size = bytes.length / 2;
const offset = 0;
const json = {
x: Convert.ToBase64Url(bytes.buffer.slice(offset, offset + size)),
y: Convert.ToBase64Url(bytes.buffer.slice(offset + size, offset + size + size)),
};
return json;
}
public fromJSON(json: any): this {
if (!("x" in json)) {
throw new Error("x: Missing required property");
}
if (!("y" in json)) {
throw new Error("y: Missing required property");
}
const x = Convert.FromBase64Url(json.x);
const y = Convert.FromBase64Url(json.y);
const value = Buffer.concat([
new Uint8Array([0x04]), // uncompressed bit
new Uint8Array(x),
new Uint8Array(y),
]);
this.value = new Uint8Array(value).buffer;
return this;
}
}

39
src/asn/ec_signature.ts Normal file
View File

@ -0,0 +1,39 @@
import { AsnIntegerArrayBufferConverter, AsnProp, AsnPropTypes, IAsnConverter } from "@peculiar/asn1-schema";
// tslint:disable-next-line: no-var-requires
const asn1 = require("asn1js");
// RFC 3279
// https://tools.ietf.org/html/rfc3279#section-2.2.3
//
// ECDSA-Sig-Value ::= SEQUENCE {
// r INTEGER,
// s INTEGER
// }
export const AsnIntegerWithoutPaddingConverter: IAsnConverter<ArrayBuffer> = {
fromASN: (value: any) => {
const bytes = new Uint8Array(value.valueBlock.valueHex);
return (bytes[0] === 0)
? bytes.buffer.slice(1)
: bytes.buffer;
},
toASN: (value: ArrayBuffer) => {
const bytes = new Uint8Array(value);
if (bytes[0] > 128) {
const newValue = new Uint8Array(bytes.length + 1);
newValue.set(bytes, 1);
return new asn1.Integer({ valueHex: newValue });
}
return new asn1.Integer({ valueHex: value });
},
};
export class EcDsaSignature {
@AsnProp({ type: AsnPropTypes.Integer, converter: AsnIntegerWithoutPaddingConverter })
public r = new ArrayBuffer(0);
@AsnProp({ type: AsnPropTypes.Integer, converter: AsnIntegerWithoutPaddingConverter })
public s = new ArrayBuffer(0);
}

9
src/asn/index.ts Normal file
View File

@ -0,0 +1,9 @@
export * from "./object_identifier";
export * from "./algorithm_identifier";
export * from "./private_key_info";
export * from "./public_key_info";
export * from "./rsa_private_key";
export * from "./rsa_public_key";
export * from "./ec_private_key";
export * from "./ec_public_key";
export * from "./ec_signature";

View File

@ -0,0 +1,14 @@
import { AsnProp, AsnPropTypes, AsnType, AsnTypeTypes } from "@peculiar/asn1-schema";
@AsnType({ type: AsnTypeTypes.Choice })
export class ObjectIdentifier {
@AsnProp({type: AsnPropTypes.ObjectIdentifier})
public value!: string;
constructor(value?: string) {
if (value) {
this.value = value;
}
}
}

View File

@ -0,0 +1,35 @@
import { AsnProp, AsnPropTypes } from "@peculiar/asn1-schema";
import { AlgorithmIdentifier } from "./algorithm_identifier";
// RFC 5208
// https://tools.ietf.org/html/rfc5208#section-5
//
// PrivateKeyInfo ::= SEQUENCE {
// version Version,
// privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
// privateKey PrivateKey,
// attributes [0] IMPLICIT Attributes OPTIONAL }
//
// Version ::= INTEGER
//
// PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
//
// PrivateKey ::= OCTET STRING
//
// Attributes ::= SET OF Attribute
export class PrivateKeyInfo {
@AsnProp({ type: AsnPropTypes.Integer })
public version = 0;
@AsnProp({ type: AlgorithmIdentifier })
public privateKeyAlgorithm = new AlgorithmIdentifier();
@AsnProp({ type: AsnPropTypes.OctetString })
public privateKey = new ArrayBuffer(0);
@AsnProp({ type: AsnPropTypes.Any, optional: true })
public attributes?: ArrayBuffer;
}

View File

@ -0,0 +1,19 @@
import { AsnProp, AsnPropTypes } from "@peculiar/asn1-schema";
import { AlgorithmIdentifier } from "./algorithm_identifier";
// RFC 5280
// https://tools.ietf.org/html/rfc5280#section-4.1
//
// SubjectPublicKeyInfo ::= SEQUENCE {
// algorithm AlgorithmIdentifier,
// subjectPublicKey BIT STRING
export class PublicKeyInfo {
@AsnProp({ type: AlgorithmIdentifier })
public publicKeyAlgorithm = new AlgorithmIdentifier();
@AsnProp({ type: AsnPropTypes.BitString })
public publicKey = new ArrayBuffer(0);
}

View File

@ -0,0 +1,61 @@
import { AsnIntegerArrayBufferConverter, AsnIntegerConverter, AsnProp, AsnPropTypes } from "@peculiar/asn1-schema";
import { JsonProp } from "@peculiar/json-schema";
import { JsonBase64UrlArrayBufferConverter } from "../converters";
// RFC 3437
// https://tools.ietf.org/html/rfc3447#appendix-A.1.2
//
// RSAPrivateKey ::= SEQUENCE {
// version Version,
// modulus INTEGER, -- n
// publicExponent INTEGER, -- e
// privateExponent INTEGER, -- d
// prime1 INTEGER, -- p
// prime2 INTEGER, -- q
// exponent1 INTEGER, -- d mod (p-1)
// exponent2 INTEGER, -- d mod (q-1)
// coefficient INTEGER, -- (inverse of q) mod p
// otherPrimeInfos OtherPrimeInfos OPTIONAL
// }
export class RsaPrivateKey {
@AsnProp({ type: AsnPropTypes.Integer, converter: AsnIntegerConverter })
public version = 0;
@AsnProp({ type: AsnPropTypes.Integer, converter: AsnIntegerArrayBufferConverter })
@JsonProp({ name: "n", converter: JsonBase64UrlArrayBufferConverter })
public modulus = new ArrayBuffer(0);
@AsnProp({ type: AsnPropTypes.Integer, converter: AsnIntegerArrayBufferConverter })
@JsonProp({ name: "e", converter: JsonBase64UrlArrayBufferConverter })
public publicExponent = new ArrayBuffer(0);
@AsnProp({ type: AsnPropTypes.Integer, converter: AsnIntegerArrayBufferConverter })
@JsonProp({ name: "d", converter: JsonBase64UrlArrayBufferConverter })
public privateExponent = new ArrayBuffer(0);
@AsnProp({ type: AsnPropTypes.Integer, converter: AsnIntegerArrayBufferConverter })
@JsonProp({ name: "p", converter: JsonBase64UrlArrayBufferConverter })
public prime1 = new ArrayBuffer(0);
@AsnProp({ type: AsnPropTypes.Integer, converter: AsnIntegerArrayBufferConverter })
@JsonProp({ name: "q", converter: JsonBase64UrlArrayBufferConverter })
public prime2 = new ArrayBuffer(0);
@AsnProp({ type: AsnPropTypes.Integer, converter: AsnIntegerArrayBufferConverter })
@JsonProp({ name: "dp", converter: JsonBase64UrlArrayBufferConverter })
public exponent1 = new ArrayBuffer(0);
@AsnProp({ type: AsnPropTypes.Integer, converter: AsnIntegerArrayBufferConverter })
@JsonProp({ name: "dq", converter: JsonBase64UrlArrayBufferConverter })
public exponent2 = new ArrayBuffer(0);
@AsnProp({ type: AsnPropTypes.Integer, converter: AsnIntegerArrayBufferConverter })
@JsonProp({ name: "qi", converter: JsonBase64UrlArrayBufferConverter })
public coefficient = new ArrayBuffer(0);
@AsnProp({ type: AsnPropTypes.Any, optional: true })
public otherPrimeInfos?: ArrayBuffer;
}

23
src/asn/rsa_public_key.ts Normal file
View File

@ -0,0 +1,23 @@
import { AsnIntegerArrayBufferConverter, AsnIntegerConverter, AsnProp, AsnPropTypes } from "@peculiar/asn1-schema";
import { JsonProp } from "@peculiar/json-schema";
import { JsonBase64UrlArrayBufferConverter } from "../converters";
// RFC 3437
// https://tools.ietf.org/html/rfc3447#appendix-A.1.1
//
// RSAPublicKey ::= SEQUENCE {
// modulus INTEGER, -- n
// publicExponent INTEGER, -- e
// }
export class RsaPublicKey {
@AsnProp({ type: AsnPropTypes.Integer, converter: AsnIntegerArrayBufferConverter })
@JsonProp({ name: "n", converter: JsonBase64UrlArrayBufferConverter })
public modulus = new ArrayBuffer(0);
@AsnProp({ type: AsnPropTypes.Integer, converter: AsnIntegerArrayBufferConverter })
@JsonProp({ name: "e", converter: JsonBase64UrlArrayBufferConverter })
public publicExponent = new ArrayBuffer(0);
}

View File

@ -0,0 +1,12 @@
import { IJsonConverter } from "@peculiar/json-schema";
import { Convert } from "pvtsutils";
export const JsonBase64UrlConverter: IJsonConverter<Buffer, string> = {
fromJSON: (value: string) => Buffer.from(Convert.FromBase64Url(value)),
toJSON: (value: Buffer) => Convert.ToBase64Url(value),
};
export const JsonBase64UrlArrayBufferConverter: IJsonConverter<ArrayBuffer, string> = {
fromJSON: (value: string) => Convert.FromBase64Url(value),
toJSON: (value: ArrayBuffer) => Convert.ToBase64Url(new Uint8Array(value)),
};

1
src/converters/index.ts Normal file
View File

@ -0,0 +1 @@
export * from "./base64_url";

15
src/crypto.ts Normal file
View File

@ -0,0 +1,15 @@
import crypto from "crypto";
import * as core from "webcrypto-core";
import { SubtleCrypto } from "./subtle";
export class Crypto implements core.Crypto {
public subtle = new SubtleCrypto();
public getRandomValues<T extends ArrayBufferView>(array: T): T {
const buffer = Buffer.from(array.buffer);
crypto.randomFillSync(buffer);
return array;
}
}

1
src/index.ts Normal file
View File

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

7
src/keys/asymmetric.ts Normal file
View File

@ -0,0 +1,7 @@
import { CryptoKey } from "./key";
export abstract class AsymmetricKey extends CryptoKey {
public abstract type: "public" | "private";
}

3
src/keys/index.ts Normal file
View File

@ -0,0 +1,3 @@
export * from "./key";
export * from "./symmetric";
export * from "./asymmetric";

22
src/keys/key.ts Normal file
View File

@ -0,0 +1,22 @@
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: "" };
@JsonProp({ name: "ext", type: JsonPropTypes.Boolean, optional: true })
public extractable: boolean = false;
public type: KeyType = "secret";
@JsonProp({ name: "key_ops", type: JsonPropTypes.String, repeated: true, optional: true })
public usages: KeyUsage[] = [];
@JsonProp({ type: JsonPropTypes.String })
protected kty: string = "";
@JsonProp({ type: JsonPropTypes.String })
protected alg: string = "";
}

8
src/keys/symmetric.ts Normal file
View File

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

41
src/mechs/aes/aes_cbc.ts Normal file
View File

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

41
src/mechs/aes/aes_cmac.ts Normal file
View File

@ -0,0 +1,41 @@
import * as core from "webcrypto-core";
import { AesCrypto } from "./crypto";
import { AesCryptoKey } from "./key";
export class AesCmacProvider extends core.AesCmacProvider {
public async onGenerateKey(algorithm: AesKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKeyPair | CryptoKey> {
const key = await AesCrypto.generateKey(
{
name: this.name,
length: algorithm.length,
},
extractable,
keyUsages);
return key;
}
public async onSign(algorithm: AesCmacParams, key: AesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
throw new Error("CMAC sign method is not implemented");
}
public async onVerify(algorithm: AesCmacParams, key: AesCryptoKey, signature: ArrayBuffer, data: ArrayBuffer): Promise<boolean> {
throw new Error("CMAC verify method is not implemented");
}
public async onExportKey(format: KeyFormat, key: AesCryptoKey): Promise<JsonWebKey | ArrayBuffer> {
return AesCrypto.exportKey(format, key);
}
public async onImportKey(format: KeyFormat, keyData: JsonWebKey | ArrayBuffer, algorithm: Algorithm, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
return AesCrypto.importKey(format, keyData, { name: algorithm.name }, extractable, keyUsages);
}
public checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
super.checkCryptoKey(key, keyUsage);
if (!(key instanceof AesCryptoKey)) {
throw new TypeError("key: Is not a AesCryptoKey");
}
}
}

41
src/mechs/aes/aes_ctr.ts Normal file
View File

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

41
src/mechs/aes/aes_gcm.ts Normal file
View File

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

156
src/mechs/aes/crypto.ts Normal file
View File

@ -0,0 +1,156 @@
import { JsonParser, JsonSerializer } from "@peculiar/json-schema";
import crypto, { CipherGCM, DecipherGCM } from "crypto";
import * as core from "webcrypto-core";
import { CryptoKey } from "../../keys";
import { AesCryptoKey } from "./key";
const WRONG_KEY_TYPE = `Key must be instance of ${AesCryptoKey.name}`;
export class AesCrypto {
public static async generateKey(algorithm: AesKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<AesCryptoKey> {
const key = new AesCryptoKey();
key.algorithm = algorithm;
key.extractable = extractable;
key.usages = keyUsages;
key.data = crypto.randomBytes(algorithm.length >> 3);
return key;
}
public static async exportKey(format: string, key: AesCryptoKey): Promise<JsonWebKey | ArrayBuffer> {
if (!(key instanceof AesCryptoKey)) {
throw new Error("key: Is not AesCryptoKey");
}
switch (format.toLowerCase()) {
case "jwk":
return JsonSerializer.toJSON(key);
case "raw":
return new Uint8Array(key.data).buffer;
default:
throw new core.OperationError("format: Must be 'jwk' or 'raw'");
}
}
public static async importKey(format: string, keyData: JsonWebKey | ArrayBuffer, algorithm: any, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
let key: AesCryptoKey;
switch (format.toLowerCase()) {
case "jwk":
key = JsonParser.fromJSON(keyData, AesCryptoKey);
break;
case "raw":
key = new AesCryptoKey();
key.data = Buffer.from(keyData as ArrayBuffer);
break;
default:
throw new core.OperationError("format: Must be 'jwk' or 'raw'");
}
key.algorithm = algorithm;
key.algorithm.length = key.data.length << 3;
key.extractable = extractable;
key.usages = keyUsages;
// check key length
switch (key.algorithm.length) {
case 128:
case 192:
case 256:
break;
default:
throw new core.OperationError("keyData: Is wrong key length");
}
return key;
}
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));
case "AES-CTR":
return this.encryptAesCTR(algorithm as AesCtrParams, key, Buffer.from(data));
case "AES-GCM":
return this.encryptAesGCM(algorithm as AesGcmParams, 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> {
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));
case "AES-CTR":
return this.decryptAesCTR(algorithm as AesCtrParams, key, Buffer.from(data));
case "AES-GCM":
return this.decryptAesGCM(algorithm as AesGcmParams, 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));
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));
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));
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));
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
if (algorithm.additionalData) {
cipher.setAAD(Buffer.from(algorithm.additionalData as ArrayBuffer));
}
let enc = cipher.update(data);
enc = Buffer.concat([enc, cipher.final(), cipher.getAuthTag()]);
const res = new Uint8Array(enc).buffer;
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;
const tagLength = (algorithm.tagLength || 128) >> 3;
const enc = data.slice(0, data.length - tagLength);
const tag = data.slice(data.length - tagLength);
if (algorithm.additionalData) {
decipher.setAAD(Buffer.from(algorithm.additionalData as ArrayBuffer));
}
decipher.setAuthTag(tag);
let dec = decipher.update(enc);
dec = Buffer.concat([dec, decipher.final()]);
return new Uint8Array(dec).buffer;
}
}

5
src/mechs/aes/index.ts Normal file
View File

@ -0,0 +1,5 @@
export * from "./key";
export * from "./aes_cbc";
export * from "./aes_cmac";
export * from "./aes_ctr";
export * from "./aes_gcm";

30
src/mechs/aes/key.ts Normal file
View File

@ -0,0 +1,30 @@
import { JsonProp } from "@peculiar/json-schema";
import * as core from "webcrypto-core";
import { JsonBase64UrlConverter } from "../../converters";
import { SymmetricKey } from "../../keys";
export class AesCryptoKey extends SymmetricKey {
public algorithm!: AesKeyAlgorithm;
@JsonProp({name: "k", converter: JsonBase64UrlConverter})
public data!: Buffer;
public get alg() {
switch (this.algorithm.name.toUpperCase()) {
case "AES-CBC":
return `A${this.algorithm.length}CBC`;
case "AES-CTR":
return `A${this.algorithm.length}CTR`;
case "AES-GCM":
return `A${this.algorithm.length}GCM`;
default:
throw new core.AlgorithmError("Unsupported algorithm name");
}
}
public set alg(value: string) {
// nothing, cause set is needed for json-schema, but is not used by module
}
}

109
src/mechs/des/crypto.ts Normal file
View File

@ -0,0 +1,109 @@
import { JsonParser, JsonSerializer } from "@peculiar/json-schema";
import crypto from "crypto";
import * as core from "webcrypto-core";
import { DesParams } from "webcrypto-core";
import { CryptoKey } from "../../keys";
import { DesCryptoKey } from "./key";
export class DesCrypto {
public static async generateKey(algorithm: AesKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<DesCryptoKey> {
const key = new DesCryptoKey();
key.algorithm = algorithm;
key.extractable = extractable;
key.usages = keyUsages;
key.data = crypto.randomBytes(algorithm.length >> 3);
return key;
}
public static async exportKey(format: string, key: CryptoKey): Promise<JsonWebKey | ArrayBuffer> {
switch (format.toLowerCase()) {
case "jwk":
return JsonSerializer.toJSON(key);
case "raw":
return new Uint8Array(key.data).buffer;
default:
throw new core.OperationError("format: Must be 'jwk' or 'raw'");
}
}
public static async importKey(format: string, keyData: JsonWebKey | ArrayBuffer, algorithm: any, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
let key: DesCryptoKey;
switch (format.toLowerCase()) {
case "jwk":
key = JsonParser.fromJSON(keyData, DesCryptoKey);
break;
case "raw":
key = new DesCryptoKey();
key.data = Buffer.from(keyData as ArrayBuffer);
break;
default:
throw new core.OperationError("format: Must be 'jwk' or 'raw'");
}
key.algorithm = algorithm;
key.extractable = extractable;
key.usages = keyUsages;
return key;
}
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));
case "DES-EDE3-CBC":
return this.encryptDesEDE3CBC(algorithm, key, Buffer.from(data));
default:
throw new core.OperationError("algorithm: Is not recognized");
}
}
public static async decrypt(algorithm: DesParams, key: CryptoKey, data: Uint8Array): Promise<ArrayBuffer> {
if (!(key instanceof DesCryptoKey)) {
throw new Error("key: Is not DesCryptoKey");
}
switch (algorithm.name.toUpperCase()) {
case "DES-CBC":
return this.decryptDesCBC(algorithm, key, Buffer.from(data));
case "DES-EDE3-CBC":
return this.decryptDesEDE3CBC(algorithm, key, Buffer.from(data));
default:
throw new core.OperationError("algorithm: Is not recognized");
}
}
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));
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));
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));
let dec = decipher.update(data);
dec = Buffer.concat([dec, decipher.final()]);
return new Uint8Array(dec).buffer;
}
}

53
src/mechs/des/des_cbc.ts Normal file
View File

@ -0,0 +1,53 @@
import * as core from "webcrypto-core";
import { CryptoKey } from "../../keys";
import { DesCrypto } from "./crypto";
import { DesCryptoKey } from "./key";
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<CryptoKeyPair | CryptoKey> {
const key = await DesCrypto.generateKey(
{
name: this.name,
length: this.keySizeBits,
},
extractable,
keyUsages);
return key;
}
public async onEncrypt(algorithm: DesCbcParams, key: DesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return DesCrypto.encrypt(algorithm, key, new Uint8Array(data));
}
public async onDecrypt(algorithm: DesCbcParams, key: DesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return DesCrypto.decrypt(algorithm, key, new Uint8Array(data));
}
public async onExportKey(format: KeyFormat, key: CryptoKey): Promise<JsonWebKey | ArrayBuffer> {
return DesCrypto.exportKey(format, key);
}
public async onImportKey(format: KeyFormat, keyData: JsonWebKey | ArrayBuffer, algorithm: Algorithm, extractable: boolean, keyUsages: KeyUsage[]): Promise<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 key;
}
public checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
super.checkCryptoKey(key, keyUsage);
if (!(key instanceof DesCryptoKey)) {
throw new TypeError("key: Is not a DesCryptoKey");
}
}
}

View File

@ -0,0 +1,53 @@
import * as core from "webcrypto-core";
import { CryptoKey } from "../../keys";
import { DesCrypto } from "./crypto";
import { DesCryptoKey } from "./key";
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<CryptoKeyPair | CryptoKey> {
const key = await DesCrypto.generateKey(
{
name: this.name,
length: this.keySizeBits,
},
extractable,
keyUsages);
return key;
}
public async onEncrypt(algorithm: DesEde3CbcParams, key: DesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return DesCrypto.encrypt(algorithm, key, new Uint8Array(data));
}
public async onDecrypt(algorithm: DesEde3CbcParams, key: DesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return DesCrypto.decrypt(algorithm, key, new Uint8Array(data));
}
public async onExportKey(format: KeyFormat, key: CryptoKey): Promise<JsonWebKey | ArrayBuffer> {
return DesCrypto.exportKey(format, key);
}
public async onImportKey(format: KeyFormat, keyData: JsonWebKey | ArrayBuffer, algorithm: Algorithm, extractable: boolean, keyUsages: KeyUsage[]): Promise<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 key;
}
public checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
super.checkCryptoKey(key, keyUsage);
if (!(key instanceof DesCryptoKey)) {
throw new TypeError("key: Is not a DesCryptoKey");
}
}
}

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

@ -0,0 +1,3 @@
export * from "./key";
export * from "./des_cbc";
export * from "./des_ede3_cbc";

28
src/mechs/des/key.ts Normal file
View File

@ -0,0 +1,28 @@
import { JsonProp } from "@peculiar/json-schema";
import * as core from "webcrypto-core";
import { JsonBase64UrlConverter } from "../../converters";
import { SymmetricKey } from "../../keys";
export class DesCryptoKey extends SymmetricKey {
public algorithm!: core.DesKeyAlgorithm;
@JsonProp({name: "k", converter: JsonBase64UrlConverter})
public data!: Buffer;
public get alg() {
switch (this.algorithm.name.toUpperCase()) {
case "DES-CBC":
return `DES-CBC`;
case "DES-EDE3-CBC":
return `3DES-CBC`;
default:
throw new core.AlgorithmError("Unsupported algorithm name");
}
}
public set alg(value: string) {
// nothing, cause set is needed for json-schema, but is not used by module
}
}

195
src/mechs/ec/crypto.ts Normal file
View File

@ -0,0 +1,195 @@
import { AsnParser, AsnSerializer } from "@peculiar/asn1-schema";
import { JsonParser, JsonSerializer } from "@peculiar/json-schema";
import crypto from "crypto";
import * as core from "webcrypto-core";
import * as asn from "../../asn";
import { ObjectIdentifier } from "../../asn";
import { CryptoKey } from "../../keys";
import { getOidByNamedCurve } from "./helper";
import { EcPrivateKey } from "./private_key";
import { EcPublicKey } from "./public_key";
export class EcCrypto {
public static publicKeyUsages = ["verify"];
public static privateKeyUsages = ["sign", "deriveKey", "deriveBits"];
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);
const publicKey = new EcPublicKey();
publicKey.algorithm = algorithm;
publicKey.extractable = true;
publicKey.usages = keyUsages.filter((usage) => this.publicKeyUsages.indexOf(usage) !== -1);
// @ts-ignore NodeJs d.ts error
const keys = crypto.generateKeyPairSync("ec", {
namedCurve: this.getOpenSSLNamedCurve(algorithm.namedCurve),
publicKeyEncoding: {
format: "der",
type: "spki",
},
// @ts-ignore NodeJs d.ts error
privateKeyEncoding: {
format: "der",
type: "pkcs8",
},
});
privateKey.data = keys.privateKey;
publicKey.data = keys.publicKey;
const res: CryptoKeyPair = {
privateKey,
publicKey,
};
return res;
}
public static async sign(algorithm: EcdsaParams, key: EcPrivateKey, data: Uint8Array): Promise<ArrayBuffer> {
const cryptoAlg = (algorithm.hash as Algorithm).name.replace("-", "");
const signer = crypto.createSign(cryptoAlg);
signer.update(Buffer.from(data));
const options = {
key: `-----BEGIN PRIVATE KEY-----\n${key.data.toString("base64")}\n-----END PRIVATE KEY-----`,
};
const signature = signer.sign(options);
const ecSignature = AsnParser.parse(signature, asn.EcDsaSignature);
return new Uint8Array(Buffer.concat([
Buffer.from(ecSignature.r),
Buffer.from(ecSignature.s),
])).buffer;
}
public static async verify(algorithm: EcdsaParams, key: EcPublicKey, signature: Uint8Array, data: Uint8Array): Promise<boolean> {
const cryptoAlg = (algorithm.hash as Algorithm).name.replace("-", "");
const signer = crypto.createVerify(cryptoAlg);
signer.update(Buffer.from(data));
const options = {
key: `-----BEGIN PUBLIC KEY-----\n${key.data.toString("base64")}\n-----END PUBLIC KEY-----`,
};
const ecSignature = new asn.EcDsaSignature();
const size = signature.length / 2;
ecSignature.r = signature.buffer.slice(0, size);
ecSignature.s = signature.buffer.slice(size, size + size);
const ok = signer.verify(options, Buffer.from(AsnSerializer.serialize(ecSignature)));
return ok;
}
public static async deriveBits(algorithm: EcdhKeyDeriveParams, baseKey: CryptoKey, length: number): Promise<ArrayBuffer> {
const cryptoAlg = this.getOpenSSLNamedCurve((baseKey.algorithm as EcKeyAlgorithm).namedCurve);
const ecdh = crypto.createECDH(cryptoAlg);
const asnPrivateKey = AsnParser.parse(baseKey.data, asn.PrivateKeyInfo);
const asnEcPrivateKey = AsnParser.parse(asnPrivateKey.privateKey, asn.EcPrivateKey);
ecdh.setPrivateKey(Buffer.from(asnEcPrivateKey.privateKey));
const asnPublicKey = AsnParser.parse((algorithm.public as CryptoKey).data, asn.PublicKeyInfo);
const bits = ecdh.computeSecret(Buffer.from(asnPublicKey.publicKey));
return new Uint8Array(bits).buffer.slice(0, length >> 3);
}
public static async exportKey(format: KeyFormat, key: CryptoKey): Promise<JsonWebKey | ArrayBuffer> {
switch (format.toLowerCase()) {
case "jwk":
return JsonSerializer.toJSON(key);
case "pkcs8":
case "spki":
return new Uint8Array(key.data).buffer;
case "raw":
const publicKeyInfo = AsnParser.parse(key.data, asn.PublicKeyInfo);
return publicKeyInfo.publicKey;
default:
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> {
switch (format.toLowerCase()) {
case "jwk":
const jwk = keyData as JsonWebKey;
if (jwk.d) {
const asnKey = JsonParser.fromJSON(keyData, asn.EcPrivateKey);
return this.importPrivateKey(asnKey, algorithm, extractable, keyUsages);
} else {
const asnKey = JsonParser.fromJSON(keyData, asn.EcPublicKey);
return this.importPublicKey(asnKey, algorithm, extractable, keyUsages);
}
case "raw": {
const asnKey = new asn.EcPublicKey(keyData as ArrayBuffer);
return this.importPublicKey(asnKey, algorithm, extractable, keyUsages);
}
case "spki": {
const keyInfo = AsnParser.parse(new Uint8Array(keyData as ArrayBuffer), asn.PublicKeyInfo);
const asnKey = new asn.EcPublicKey(keyInfo.publicKey);
return this.importPublicKey(asnKey, algorithm, extractable, keyUsages);
}
case "pkcs8": {
const keyInfo = AsnParser.parse(new Uint8Array(keyData as ArrayBuffer), asn.PrivateKeyInfo);
const asnKey = AsnParser.parse(keyInfo.privateKey, asn.EcPrivateKey);
return this.importPrivateKey(asnKey, algorithm, extractable, keyUsages);
}
default:
throw new core.OperationError("format: Must be 'jwk', 'raw', 'pkcs8' or 'spki'");
}
}
protected static async importPrivateKey(asnKey: asn.EcPrivateKey, algorithm: EcKeyImportParams, extractable: boolean, keyUsages: KeyUsage[]) {
const keyInfo = new asn.PrivateKeyInfo();
keyInfo.privateKeyAlgorithm.algorithm = "1.2.840.10045.2.1";
keyInfo.privateKeyAlgorithm.parameters = AsnSerializer.serialize(new ObjectIdentifier(getOidByNamedCurve(algorithm.namedCurve)));
keyInfo.privateKey = AsnSerializer.serialize(asnKey);
const key = new EcPrivateKey();
key.data = Buffer.from(AsnSerializer.serialize(keyInfo));
key.algorithm = Object.assign({}, algorithm) as EcKeyAlgorithm;
key.extractable = extractable;
key.usages = keyUsages;
return key;
}
protected static async importPublicKey(asnKey: asn.EcPublicKey, algorithm: EcKeyImportParams, extractable: boolean, keyUsages: KeyUsage[]) {
const keyInfo = new asn.PublicKeyInfo();
keyInfo.publicKeyAlgorithm.algorithm = "1.2.840.10045.2.1";
keyInfo.publicKeyAlgorithm.parameters = AsnSerializer.serialize(new ObjectIdentifier(getOidByNamedCurve(algorithm.namedCurve)));
keyInfo.publicKey = asnKey.value;
const key = new EcPublicKey();
key.data = Buffer.from(AsnSerializer.serialize(keyInfo));
key.algorithm = Object.assign({}, algorithm) as EcKeyAlgorithm;
key.extractable = extractable;
key.usages = keyUsages;
return key;
}
private static getOpenSSLNamedCurve(curve: string) {
switch (curve.toUpperCase()) {
case "P-256":
return "prime256v1";
case "K-256":
return "secp256k1";
case "P-384":
return "secp384r1";
case "P-521":
return "secp521r1";
default:
throw new core.OperationError(`Cannot convert WebCrypto named curve to NodeJs. Unknown name '${curve}'`);
}
}
}

42
src/mechs/ec/ec_dh.ts Normal file
View File

@ -0,0 +1,42 @@
import * as core from "webcrypto-core";
import { CryptoKey } from "../../keys";
import { EcCrypto } from "./crypto";
import { EcPrivateKey } from "./private_key";
import { EcPublicKey } from "./public_key";
export class EcdhProvider extends core.EcdhProvider {
public async onGenerateKey(algorithm: EcKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKeyPair | CryptoKey> {
const key = await EcCrypto.generateKey(
{
...algorithm,
name: this.name,
},
extractable,
keyUsages);
return key;
}
public async onExportKey(format: KeyFormat, key: CryptoKey): Promise<JsonWebKey | ArrayBuffer> {
return EcCrypto.exportKey(format, 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);
return key;
}
public checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
super.checkCryptoKey(key, keyUsage);
if (!(key instanceof EcPrivateKey || key 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, baseKey, length);
return bits;
}
}

47
src/mechs/ec/ec_dsa.ts Normal file
View File

@ -0,0 +1,47 @@
import * as core from "webcrypto-core";
import { CryptoKey } from "../../keys";
import { EcCrypto } from "./crypto";
import { EcPrivateKey } from "./private_key";
import { EcPublicKey } from "./public_key";
export class EcdsaProvider extends core.EcdsaProvider {
public namedCurves = ["P-256", "P-384", "P-521", "K-256"];
public async onGenerateKey(algorithm: EcKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKeyPair | CryptoKey> {
const key = await EcCrypto.generateKey(
{
...algorithm,
name: this.name,
},
extractable,
keyUsages);
return key;
}
public async onSign(algorithm: EcdsaParams, key: EcPrivateKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return EcCrypto.sign(algorithm, key, new Uint8Array(data));
}
public async onVerify(algorithm: EcdsaParams, key: EcPublicKey, signature: ArrayBuffer, data: ArrayBuffer): Promise<boolean> {
return EcCrypto.verify(algorithm, key, new Uint8Array(signature), new Uint8Array(data));
}
public async onExportKey(format: KeyFormat, key: CryptoKey): Promise<JsonWebKey | ArrayBuffer> {
return EcCrypto.exportKey(format, 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);
return key;
}
public checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
super.checkCryptoKey(key, keyUsage);
if (!(key instanceof EcPrivateKey || key instanceof EcPublicKey)) {
throw new TypeError("key: Is not EC CryptoKey");
}
}
}

32
src/mechs/ec/helper.ts Normal file
View File

@ -0,0 +1,32 @@
import * as core from "webcrypto-core";
const namedOIDs: { [key: string]: string } = {
// P-256
"1.2.840.10045.3.1.7": "P-256",
"P-256": "1.2.840.10045.3.1.7",
// P-384
"1.3.132.0.34": "P-384",
"P-384": "1.3.132.0.34",
// P-521
"1.3.132.0.35": "P-521",
"P-521": "1.3.132.0.35",
// K-256
"1.3.132.0.10": "K-256",
"K-256": "1.3.132.0.10",
};
export function getNamedCurveByOid(oid: string) {
const namedCurve = namedOIDs[oid];
if (!namedCurve) {
throw new core.OperationError(`Cannot convert OID(${oid}) to WebCrypto named curve`);
}
return namedCurve;
}
export function getOidByNamedCurve(namedCurve: string) {
const oid = namedOIDs[namedCurve];
if (!oid) {
throw new core.OperationError(`Cannot convert WebCrypto named curve '${namedCurve}' to OID`);
}
return oid;
}

4
src/mechs/ec/index.ts Normal file
View File

@ -0,0 +1,4 @@
export * from "./private_key";
export * from "./public_key";
export * from "./ec_dsa";
export * from "./ec_dh";

View File

@ -0,0 +1,49 @@
import { AsnParser, AsnSerializer } from "@peculiar/asn1-schema";
import { IJsonConvertible, JsonParser, JsonSerializer } from "@peculiar/json-schema";
import * as core from "webcrypto-core";
import * as asn from "../../asn";
import { ObjectIdentifier } from "../../asn";
import { AsymmetricKey } from "../../keys";
import { getOidByNamedCurve } from "./helper";
export class EcPrivateKey extends AsymmetricKey implements IJsonConvertible {
public readonly type: "private" = "private";
public algorithm!: EcKeyAlgorithm;
public getKey() {
const keyInfo = AsnParser.parse(this.data, asn.PrivateKeyInfo);
return AsnParser.parse(keyInfo.privateKey, asn.EcPrivateKey);
}
public toJSON() {
const key = this.getKey();
const json: JsonWebKey = {
kty: "EC",
crv: this.algorithm.namedCurve,
key_ops: this.usages,
ext: this.extractable,
};
return Object.assign(json, JsonSerializer.toJSON(key));
}
public fromJSON(json: JsonWebKey) {
if (!json.alg) {
throw new core.OperationError(`Cannot get named curve from JWK. Property 'alg' is required`);
}
const keyInfo = new asn.PrivateKeyInfo();
keyInfo.privateKeyAlgorithm.algorithm = "1.2.840.10045.2.1";
keyInfo.privateKeyAlgorithm.parameters = AsnSerializer.serialize(
new ObjectIdentifier(getOidByNamedCurve(json.crv!)),
);
const key = JsonParser.fromJSON(json, asn.EcPrivateKey);
keyInfo.privateKey = AsnSerializer.serialize(key);
this.data = Buffer.from(AsnSerializer.serialize(keyInfo));
return this;
}
}

View File

@ -0,0 +1,45 @@
import { AsnParser, AsnSerializer } from "@peculiar/asn1-schema";
import { IJsonConvertible, JsonParser, JsonSerializer } from "@peculiar/json-schema";
import * as asn from "../../asn";
import { ObjectIdentifier } from "../../asn";
import { AsymmetricKey } from "../../keys/asymmetric";
import { getOidByNamedCurve } from "./helper";
export class EcPublicKey extends AsymmetricKey implements IJsonConvertible {
public readonly type: "public" = "public";
public algorithm!: EcKeyAlgorithm;
public getKey() {
const keyInfo = AsnParser.parse(this.data, asn.PublicKeyInfo);
return new asn.EcPublicKey(keyInfo.publicKey);
}
public toJSON() {
const key = this.getKey();
const json: JsonWebKey = {
kty: "EC",
crv: this.algorithm.namedCurve,
key_ops: this.usages,
ext: this.extractable,
};
return Object.assign(json, JsonSerializer.toJSON(key));
}
public fromJSON(json: JsonWebKey) {
const key = JsonParser.fromJSON(json, asn.EcPublicKey);
const keyInfo = new asn.PublicKeyInfo();
keyInfo.publicKeyAlgorithm.algorithm = "1.2.840.10045.2.1";
keyInfo.publicKeyAlgorithm.parameters = AsnSerializer.serialize(
new ObjectIdentifier(getOidByNamedCurve(json.crv!)),
);
keyInfo.publicKey = AsnSerializer.toASN(key).valueHex;
this.data = Buffer.from(AsnSerializer.serialize(keyInfo));
return this;
}
}

92
src/mechs/hmac/hmac.ts Normal file
View File

@ -0,0 +1,92 @@
import { JsonParser, JsonSerializer } from "@peculiar/json-schema";
import crypto from "crypto";
import * as core from "webcrypto-core";
import { HmacCryptoKey } from "./key";
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);
const key = new HmacCryptoKey();
key.algorithm = {
...algorithm as any,
name: this.name,
};
key.extractable = extractable;
key.usages = keyUsages;
key.data = crypto.randomBytes(length >> 3);
return key;
}
public async onSign(algorithm: Algorithm, key: HmacCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
const hash = key.algorithm.hash.name.replace("-", "");
const hmac = crypto.createHmac(hash, 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, 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> {
let key: HmacCryptoKey;
switch (format.toLowerCase()) {
case "jwk":
key = JsonParser.fromJSON(keyData, HmacCryptoKey);
break;
case "raw":
key = new HmacCryptoKey();
key.data = Buffer.from(keyData as ArrayBuffer);
break;
default:
throw new core.OperationError("format: Must be 'jwk' or 'raw'");
}
key.algorithm = {
hash: { name: (algorithm.hash as Algorithm).name },
name: this.name,
length: key.data.length << 3,
};
key.extractable = extractable;
key.usages = keyUsages;
// check key length
switch (key.algorithm.length) {
case 128:
case 192:
case 256:
break;
default:
throw new core.OperationError("keyData: Is wrong key length");
}
return key;
}
public async onExportKey(format: KeyFormat, key: HmacCryptoKey): Promise<JsonWebKey | ArrayBuffer> {
switch (format.toLowerCase()) {
case "jwk":
return JsonSerializer.toJSON(key);
case "raw":
return new Uint8Array(key.data).buffer;
default:
throw new core.OperationError("format: Must be 'jwk' or 'raw'");
}
}
public checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
super.checkCryptoKey(key, keyUsage);
if (!(key instanceof HmacCryptoKey)) {
throw new TypeError("key: Is not HMAC CryptoKey");
}
}
}

1
src/mechs/hmac/index.ts Normal file
View File

@ -0,0 +1 @@
export * from "./hmac";

22
src/mechs/hmac/key.ts Normal file
View File

@ -0,0 +1,22 @@
import { JsonProp } from "@peculiar/json-schema";
import * as core from "webcrypto-core";
import { JsonBase64UrlConverter } from "../../converters";
import { CryptoKey } from "../../keys";
export class HmacCryptoKey extends CryptoKey {
@JsonProp({ name: "k", converter: JsonBase64UrlConverter })
public data!: Buffer;
public algorithm!: HmacKeyAlgorithm;
protected get alg() {
const hash = this.algorithm.hash.name.toUpperCase();
return `HS${hash.replace("SHA-", "")}`;
}
protected set alg(value: string) {
// nothing, cause set is needed for json-schema, but is not used by module
}
}

7
src/mechs/index.ts Normal file
View File

@ -0,0 +1,7 @@
export * from "./aes";
export * from "./des";
export * from "./rsa";
export * from "./ec";
export * from "./sha";
export * from "./pbkdf";
export * from "./hmac";

1
src/mechs/pbkdf/index.ts Normal file
View File

@ -0,0 +1 @@
export * from "./pbkdf2";

5
src/mechs/pbkdf/key.ts Normal file
View File

@ -0,0 +1,5 @@
import * as core from "webcrypto-core";
import { CryptoKey } from "../../keys";
export class PbkdfCryptoKey extends CryptoKey {
}

40
src/mechs/pbkdf/pbkdf2.ts Normal file
View File

@ -0,0 +1,40 @@
import crypto from "crypto";
import * as core from "webcrypto-core";
import { PbkdfCryptoKey } from "./key";
export class Pbkdf2Provider extends core.Pbkdf2Provider {
public async onDeriveBits(algorithm: Pbkdf2Params, baseKey: PbkdfCryptoKey, length: number): Promise<ArrayBuffer> {
return new Promise<ArrayBuffer>((resolve, reject) => {
const salt = this.prepareData(algorithm.salt);
const hash = (algorithm.hash as Algorithm).name.replace("-", "");
crypto.pbkdf2(baseKey.data, Buffer.from(salt), algorithm.iterations, length >> 3, hash, (err, derivedBits) => {
if (err) {
reject(err);
} else {
resolve(new Uint8Array(derivedBits).buffer);
}
});
});
}
public async importKey(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);
key.algorithm = { name: this.name };
key.extractable = false;
key.usages = keyUsages;
return key;
}
throw new core.OperationError("format: Must be 'raw'");
}
public checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
super.checkCryptoKey(key, keyUsage);
if (!(key instanceof PbkdfCryptoKey)) {
throw new TypeError("key: Is not PBKDF CryptoKey");
}
}
}

252
src/mechs/rsa/crypto.ts Normal file
View File

@ -0,0 +1,252 @@
import { AsnParser, AsnSerializer } from "@peculiar/asn1-schema";
import { JsonParser, JsonSerializer } from "@peculiar/json-schema";
import crypto from "crypto";
import * as core from "webcrypto-core";
import * as asn from "../../asn";
import { CryptoKey } from "../../keys";
import { RsaPrivateKey } from "./private_key";
import { RsaPublicKey } from "./public_key";
interface INodeCryptoSignOptions {
key: string;
passphrase?: string;
padding?: number;
saltLength?: number;
}
export class RsaCrypto {
public static publicKeyUsages = ["verify", "encrypt", "wrapKey"];
public static privateKeyUsages = ["sign", "decrypt", "unwrapKey"];
public static async generateKey(algorithm: RsaHashedKeyGenParams, 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[];
const publicKey = new RsaPublicKey();
publicKey.algorithm = algorithm as RsaHashedKeyAlgorithm;
publicKey.extractable = true;
publicKey.usages = keyUsages.filter((usage) => this.publicKeyUsages.indexOf(usage) !== -1) as KeyUsage[];
const publicExponent = Buffer.concat([
Buffer.alloc(4 - algorithm.publicExponent.byteLength, 0),
Buffer.from(algorithm.publicExponent),
]).readInt32BE(0);
// @ts-ignore NodeJs d.ts error
const keys = crypto.generateKeyPairSync("rsa", {
modulusLength: algorithm.modulusLength,
publicExponent,
publicKeyEncoding: {
format: "der",
type: "spki",
},
privateKeyEncoding: {
format: "der",
type: "pkcs8",
},
});
privateKey.data = keys.privateKey;
publicKey.data = keys.publicKey;
const res: CryptoKeyPair = {
privateKey,
publicKey,
};
return res;
}
public static async exportKey(format: KeyFormat, key: CryptoKey): Promise<JsonWebKey | ArrayBuffer> {
switch (format.toLowerCase()) {
case "jwk":
return JsonSerializer.toJSON(key);
case "pkcs8":
case "spki":
return new Uint8Array(key.data).buffer;
default:
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> {
switch (format.toLowerCase()) {
case "jwk":
const jwk = keyData as JsonWebKey;
if (jwk.d) {
const asnKey = JsonParser.fromJSON(keyData, asn.RsaPrivateKey);
return this.importPrivateKey(asnKey, algorithm, extractable, keyUsages);
} else {
const asnKey = JsonParser.fromJSON(keyData, asn.RsaPublicKey);
return this.importPublicKey(asnKey, algorithm, extractable, keyUsages);
}
case "spki": {
const keyInfo = AsnParser.parse(new Uint8Array(keyData as ArrayBuffer), asn.PublicKeyInfo);
const asnKey = AsnParser.parse(keyInfo.publicKey, asn.RsaPublicKey);
return this.importPublicKey(asnKey, algorithm, extractable, keyUsages);
}
case "pkcs8": {
const keyInfo = AsnParser.parse(new Uint8Array(keyData as ArrayBuffer), asn.PrivateKeyInfo);
const asnKey = AsnParser.parse(keyInfo.privateKey, asn.RsaPrivateKey);
return this.importPrivateKey(asnKey, algorithm, extractable, keyUsages);
}
default:
throw new core.OperationError("format: Must be 'jwk', 'pkcs8' or 'spki'");
}
}
public static async sign(algorithm: Algorithm, key: RsaPrivateKey, data: Uint8Array): Promise<ArrayBuffer> {
switch (algorithm.name.toUpperCase()) {
case "RSA-PSS":
case "RSASSA-PKCS1-V1_5":
return this.signRsa(algorithm, key, data);
default:
throw new core.OperationError("algorithm: Is not recognized");
}
}
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":
return this.verifySSA(algorithm, key, data, signature);
default:
throw new core.OperationError("algorithm: Is not recognized");
}
}
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);
default:
throw new core.OperationError("algorithm: Is not recognized");
}
}
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);
default:
throw new core.OperationError("algorithm: Is not recognized");
}
}
protected static importPrivateKey(asnKey: asn.RsaPrivateKey, algorithm: RsaHashedImportParams, extractable: boolean, keyUsages: KeyUsage[]) {
const keyInfo = new asn.PrivateKeyInfo();
keyInfo.privateKeyAlgorithm.algorithm = "1.2.840.113549.1.1.1";
keyInfo.privateKeyAlgorithm.parameters = null;
keyInfo.privateKey = AsnSerializer.serialize(asnKey);
const key = new RsaPrivateKey();
key.data = Buffer.from(AsnSerializer.serialize(keyInfo));
key.algorithm.publicExponent = new Uint8Array(asnKey.publicExponent);
key.algorithm.modulusLength = asnKey.modulus.byteLength << 3;
key.algorithm = Object.assign({}, algorithm) as RsaHashedKeyAlgorithm;
key.extractable = extractable;
key.usages = keyUsages;
return key;
}
protected static importPublicKey(asnKey: asn.RsaPublicKey, algorithm: RsaHashedImportParams, extractable: boolean, keyUsages: KeyUsage[]) {
const keyInfo = new asn.PublicKeyInfo();
keyInfo.publicKeyAlgorithm.algorithm = "1.2.840.113549.1.1.1";
keyInfo.publicKeyAlgorithm.parameters = null;
keyInfo.publicKey = AsnSerializer.serialize(asnKey);
const key = new RsaPublicKey();
key.data = Buffer.from(AsnSerializer.serialize(keyInfo));
key.algorithm.publicExponent = new Uint8Array(asnKey.publicExponent);
key.algorithm.modulusLength = asnKey.modulus.byteLength << 3;
key.algorithm = Object.assign({}, algorithm) as RsaHashedKeyAlgorithm;
key.extractable = extractable;
key.usages = keyUsages;
return key;
}
protected static getCryptoAlgorithm(alg: RsaHashedKeyAlgorithm) {
switch (alg.hash.name.toUpperCase()) {
case "SHA-1":
return "RSA-SHA1";
case "SHA-256":
return "RSA-SHA256";
case "SHA-384":
return "RSA-SHA384";
case "SHA-512":
return "RSA-SHA512";
default:
throw new core.OperationError("algorithm.hash: Is not recognized");
}
}
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));
const options: INodeCryptoSignOptions = {
key: `-----BEGIN PRIVATE KEY-----\n${key.data.toString("base64")}\n-----END PRIVATE KEY-----`,
};
if (algorithm.name.toUpperCase() === "RSA-PSS") {
// @ts-ignore
options.padding = crypto.constants.RSA_PKCS1_PSS_PADDING;
options.saltLength = (algorithm as RsaPssParams).saltLength;
}
const signature = signer.sign(options);
return new Uint8Array(signature).buffer;
}
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));
const options: INodeCryptoSignOptions = {
key: `-----BEGIN PUBLIC KEY-----\n${key.data.toString("base64")}\n-----END PUBLIC KEY-----`,
};
if (algorithm.name.toUpperCase() === "RSA-PSS") {
// @ts-ignore
options.padding = crypto.constants.RSA_PKCS1_PSS_PADDING;
options.saltLength = (algorithm as RsaPssParams).saltLength;
}
const ok = signer.verify(options, signature);
return ok;
}
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-----`,
// @ts-ignore
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
};
if (algorithm.label) {
// nothing
}
return new Uint8Array(crypto.publicEncrypt(options, data)).buffer;
}
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-----`,
// @ts-ignore
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
};
if (algorithm.label) {
// nothing
}
return new Uint8Array(crypto.privateDecrypt(options, data)).buffer;
}
}

15
src/mechs/rsa/helper.ts Normal file
View File

@ -0,0 +1,15 @@
import * as core from "webcrypto-core";
export function getJwkAlgorithm(algorithm: RsaHashedKeyAlgorithm) {
switch (algorithm.name.toUpperCase()) {
case "RSA-OAEP":
const mdSize = /(\d+)$/.exec(algorithm.hash.name)![1];
return `RSA-OAEP${mdSize !== "1" ? `-${mdSize}` : ""}`;
case "RSASSA-PKCS1-V1_5":
return `RS${/(\d+)$/.exec(algorithm.hash.name)![1]}`;
case "RSA-PSS":
return `PS${/(\d+)$/.exec(algorithm.hash.name)![1]}`;
default:
throw new core.OperationError("algorithm: Is not recognized");
}
}

5
src/mechs/rsa/index.ts Normal file
View File

@ -0,0 +1,5 @@
export * from "./private_key";
export * from "./public_key";
export * from "./rsa_ssa";
export * from "./rsa_pss";
export * from "./rsa_oaep";

View File

@ -0,0 +1,40 @@
import { AsnParser, AsnSerializer } from "@peculiar/asn1-schema";
import { JsonParser, JsonSerializer } from "@peculiar/json-schema";
import * as asn from "../../asn";
import { AsymmetricKey } from "../../keys";
import { getJwkAlgorithm } from "./helper";
export class RsaPrivateKey extends AsymmetricKey {
public readonly type: "private" = "private";
public algorithm!: RsaHashedKeyAlgorithm;
public getKey() {
const keyInfo = AsnParser.parse(this.data, asn.PrivateKeyInfo);
return AsnParser.parse(keyInfo.privateKey, asn.RsaPrivateKey);
}
public toJSON() {
const key = this.getKey();
const json: JsonWebKey = {
kty: "RSA",
alg: getJwkAlgorithm(this.algorithm),
key_ops: this.usages,
ext: this.extractable,
};
return Object.assign(json, JsonSerializer.toJSON(key));
}
public fromJSON(json: JsonWebKey) {
const key = JsonParser.fromJSON(json, asn.RsaPrivateKey);
const keyInfo = new asn.PrivateKeyInfo();
keyInfo.privateKeyAlgorithm.algorithm = "1.2.840.113549.1.1.1";
keyInfo.privateKeyAlgorithm.parameters = null;
keyInfo.privateKey = AsnSerializer.serialize(key);
this.data = Buffer.from(AsnSerializer.serialize(keyInfo));
}
}

View File

@ -0,0 +1,39 @@
import { AsnParser, AsnSerializer } from "@peculiar/asn1-schema";
import { JsonParser, JsonSerializer } from "@peculiar/json-schema";
import * as asn from "../../asn";
import { AsymmetricKey } from "../../keys/asymmetric";
import { getJwkAlgorithm } from "./helper";
export class RsaPublicKey extends AsymmetricKey {
public readonly type: "public" = "public";
public algorithm!: RsaHashedKeyAlgorithm;
public getKey() {
const keyInfo = AsnParser.parse(this.data, asn.PublicKeyInfo);
return AsnParser.parse(keyInfo.publicKey, asn.RsaPublicKey);
}
public toJSON() {
const key = this.getKey();
const json: JsonWebKey = {
kty: "RSA",
alg: getJwkAlgorithm(this.algorithm),
key_ops: this.usages,
ext: this.extractable,
};
return Object.assign(json, JsonSerializer.toJSON(key));
}
public fromJSON(json: JsonWebKey) {
const key = JsonParser.fromJSON(json, asn.RsaPublicKey);
const keyInfo = new asn.PublicKeyInfo();
keyInfo.publicKeyAlgorithm.algorithm = "1.2.840.113549.1.1.1";
keyInfo.publicKeyAlgorithm.parameters = null;
keyInfo.publicKey = AsnSerializer.serialize(key);
this.data = Buffer.from(AsnSerializer.serialize(keyInfo));
}
}

45
src/mechs/rsa/rsa_oaep.ts Normal file
View File

@ -0,0 +1,45 @@
import * as core from "webcrypto-core";
import { CryptoKey } from "../../keys";
import { RsaCrypto } from "./crypto";
import { RsaPrivateKey } from "./private_key";
import { RsaPublicKey } from "./public_key";
export class RsaOaepProvider extends core.RsaOaepProvider {
public async onGenerateKey(algorithm: RsaHashedKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKeyPair | CryptoKey> {
const key = await RsaCrypto.generateKey(
{
...algorithm,
name: this.name,
},
extractable,
keyUsages);
return key;
}
public async onEncrypt(algorithm: RsaOaepParams, key: RsaPublicKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return RsaCrypto.encrypt(algorithm, key, new Uint8Array(data));
}
public async onDecrypt(algorithm: RsaOaepParams, key: RsaPrivateKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return RsaCrypto.decrypt(algorithm, key, new Uint8Array(data));
}
public async onExportKey(format: KeyFormat, key: CryptoKey): Promise<JsonWebKey | ArrayBuffer> {
return RsaCrypto.exportKey(format, 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);
return key;
}
public checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
super.checkCryptoKey(key, keyUsage);
if (!(key instanceof RsaPrivateKey || key instanceof RsaPublicKey)) {
throw new TypeError("key: Is not RSA CryptoKey");
}
}
}

45
src/mechs/rsa/rsa_pss.ts Normal file
View File

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

45
src/mechs/rsa/rsa_ssa.ts Normal file
View File

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

11
src/mechs/sha/crypto.ts Normal file
View File

@ -0,0 +1,11 @@
import crypto from "crypto";
export class ShaCrypto {
public static digest(algorithm: Algorithm, data: ArrayBuffer) {
const hash = crypto.createHash(algorithm.name.replace("-", ""))
.update(Buffer.from(data)).digest();
return new Uint8Array(hash).buffer;
}
}

4
src/mechs/sha/index.ts Normal file
View File

@ -0,0 +1,4 @@
export * from "./sha_1";
export * from "./sha_256";
export * from "./sha_384";
export * from "./sha_512";

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

@ -0,0 +1,12 @@
import * as core from "webcrypto-core";
import { ShaCrypto } from "./crypto";
export class Sha1Provider extends core.ProviderCrypto {
public name = "SHA-1";
public usages = [];
public async onDigest(algorithm: Algorithm, data: ArrayBuffer): Promise<ArrayBuffer> {
return ShaCrypto.digest(algorithm, data);
}
}

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

@ -0,0 +1,12 @@
import * as core from "webcrypto-core";
import { ShaCrypto } from "./crypto";
export class Sha256Provider extends core.ProviderCrypto {
public name = "SHA-256";
public usages = [];
public async onDigest(algorithm: Algorithm, data: ArrayBuffer): Promise<ArrayBuffer> {
return ShaCrypto.digest(algorithm, data);
}
}

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

@ -0,0 +1,12 @@
import * as core from "webcrypto-core";
import { ShaCrypto } from "./crypto";
export class Sha384Provider extends core.ProviderCrypto {
public name = "SHA-384";
public usages = [];
public async onDigest(algorithm: Algorithm, data: ArrayBuffer): Promise<ArrayBuffer> {
return ShaCrypto.digest(algorithm, data);
}
}

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

@ -0,0 +1,12 @@
import * as core from "webcrypto-core";
import { ShaCrypto } from "./crypto";
export class Sha512Provider extends core.ProviderCrypto {
public name = "SHA-512";
public usages = [];
public async onDigest(algorithm: Algorithm, data: ArrayBuffer): Promise<ArrayBuffer> {
return ShaCrypto.digest(algorithm, data);
}
}

53
src/subtle.ts Normal file
View File

@ -0,0 +1,53 @@
import * as core from "webcrypto-core";
import {
AesCbcProvider, AesCtrProvider, AesGcmProvider,
DesCbcProvider, DesEde3CbcProvider,
EcdhProvider, EcdsaProvider,
HmacProvider,
Pbkdf2Provider,
RsaOaepProvider, RsaPssProvider, RsaSsaProvider,
Sha1Provider, Sha256Provider, Sha384Provider, Sha512Provider,
} from "./mechs";
export class SubtleCrypto extends core.SubtleCrypto {
constructor() {
super();
//#region AES
this.providers.set(new AesCbcProvider());
this.providers.set(new AesCtrProvider());
this.providers.set(new AesGcmProvider());
//#endregion
//#region DES
this.providers.set(new DesCbcProvider());
this.providers.set(new DesEde3CbcProvider());
//#endregion
//#region RSA
this.providers.set(new RsaSsaProvider());
this.providers.set(new RsaPssProvider());
// this.providers.set(new RsaOaepProvider()); // TODO: Fix encrypt/decrypt
//#endregion
//#region EC
this.providers.set(new EcdsaProvider());
this.providers.set(new EcdhProvider());
//#endregion
//#region SHA
this.providers.set(new Sha1Provider());
this.providers.set(new Sha256Provider());
this.providers.set(new Sha384Provider());
this.providers.set(new Sha512Provider());
//#endregion
//#region PBKDF
this.providers.set(new Pbkdf2Provider());
//#endregion
//#region HMAC
this.providers.set(new HmacProvider());
//#endregion
}
}

533
test/aes.ts Normal file
View File

@ -0,0 +1,533 @@
import { Convert } from "pvtsutils";
import * as core from "webcrypto-core";
import { Crypto } from "../src";
import { testCrypto } from "./helper";
context("AES", () => {
const crypto = new Crypto();
testCrypto(crypto, [
//#region AES-CBC
{
name: "AES-128-CBC",
actions: {
generateKey: [
{
algorithm: { name: "AES-CBC", length: 128 } as AesKeyGenParams,
extractable: true,
keyUsages: ["encrypt", "decrypt"] as KeyUsage[],
},
],
encrypt: [
{
algorithm: {
name: "AES-CBC",
iv: Buffer.from("1234567890abcdef"),
} as AesCbcParams,
data: Buffer.from("test message"),
encData: Buffer.from("d5df3ea1598defe7446420802baef28e", "hex"),
key: {
format: "raw" as KeyFormat,
data: Buffer.from("1234567890abcdef"),
algorithm: { name: "AES-CBC" },
extractable: true,
keyUsages: ["encrypt", "decrypt"] as KeyUsage[],
},
},
],
import: [
{
name: "raw",
format: "raw" as KeyFormat,
data: Buffer.from("1234567890abcdef"),
algorithm: "AES-CBC",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] as KeyUsage[],
},
{
name: "wrong key size",
error: core.OperationError,
format: "raw" as KeyFormat,
data: Buffer.from("12345678"),
algorithm: "AES-CBC",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] as KeyUsage[],
},
{
name: "jwk",
format: "jwk" as KeyFormat,
data: {
kty: "oct",
alg: "A128CBC",
k: "MTIzNDU2Nzg5MGFiY2RlZg",
ext: true,
key_ops: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
algorithm: "AES-CBC",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] as KeyUsage[],
},
],
wrapKey: [
{
key: {
format: "raw",
algorithm: "AES-CBC",
data: Convert.FromBase64("AQIDBAUGBwgJAAECAwQFBg"),
extractable: true,
keyUsages: ["wrapKey", "unwrapKey"],
},
wKey: {
format: "raw" as KeyFormat,
data: Buffer.from("1234567890abcdef"),
algorithm: "AES-CBC",
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
algorithm: {
name: "AES-CBC",
iv: Buffer.from("1234567890abcdef"),
} as AesCbcParams,
},
],
},
},
{
name: "AES-192-CBC",
actions: {
generateKey: [
{
algorithm: { name: "AES-CBC", length: 192 } as AesKeyGenParams,
extractable: true,
keyUsages: ["encrypt", "decrypt"] as KeyUsage[],
},
],
encrypt: [
{
algorithm: {
name: "AES-CBC",
iv: Buffer.from("1234567890abcdef"),
} as AesCbcParams,
data: Buffer.from("test message"),
encData: Buffer.from("67d0b3022149829bf009ad4aff19963a", "hex"),
key: {
format: "raw" as KeyFormat,
data: Buffer.from("1234567890abcdef12345678"),
algorithm: { name: "AES-CBC" },
extractable: true,
keyUsages: ["encrypt", "decrypt"] as KeyUsage[],
},
},
],
import: [
{
name: "raw",
format: "raw" as KeyFormat,
data: Buffer.from("1234567890abcdef12345678"),
algorithm: "AES-CBC",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] as KeyUsage[],
},
{
name: "jwk",
format: "jwk",
data: {
kty: "oct",
alg: "A192CBC",
k: "MTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4",
ext: true,
key_ops: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
algorithm: "AES-CBC",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
],
},
},
{
name: "AES-256-CBC",
actions: {
generateKey: [
{
algorithm: { name: "AES-CBC", length: 256 } as AesKeyGenParams,
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
],
encrypt: [
{
algorithm: {
name: "AES-CBC",
iv: Buffer.from("1234567890abcdef"),
} as AesCbcParams,
data: Buffer.from("test message"),
encData: Buffer.from("d827c1c6aee9f0f552c62f30ddee83af", "hex"),
key: {
format: "raw",
data: Buffer.from("1234567890abcdef1234567809abcdef"),
algorithm: { name: "AES-CBC" },
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
},
],
import: [
{
name: "raw",
format: "raw",
data: Buffer.from("1234567890abcdef1234567890abcdef"),
algorithm: "AES-CBC",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
{
name: "jwk",
format: "jwk",
data: {
kty: "oct",
alg: "A256CBC",
k: "MTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4OTBhYmNkZWY",
ext: true,
key_ops: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
algorithm: "AES-CBC",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
],
},
},
//#endregion
//#region AES-CTR
{
name: "AES-128-CTR",
actions: {
generateKey: [
{
algorithm: { name: "AES-CTR", length: 128 } as AesKeyGenParams,
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
],
encrypt: [
{
algorithm: {
name: "AES-CTR",
counter: Buffer.from("1234567890abcdef"),
length: 128,
} as AesCtrParams,
data: Buffer.from("test message"),
encData: Buffer.from("e1d561c49ce4eb2f448f8a00", "hex"),
key: {
format: "raw",
data: Buffer.from("1234567890abcdef"),
algorithm: { name: "AES-CTR" },
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
},
],
import: [
{
name: "raw",
format: "raw",
data: Buffer.from("1234567890abcdef"),
algorithm: "AES-CTR",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
{
name: "jwk",
format: "jwk",
data: {
kty: "oct",
alg: "A128CTR",
k: "MTIzNDU2Nzg5MGFiY2RlZg",
ext: true,
key_ops: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
algorithm: "AES-CTR",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
],
},
},
{
name: "AES-192-CTR",
actions: {
generateKey: [
{
algorithm: { name: "AES-CTR", length: 192 } as AesKeyGenParams,
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
],
encrypt: [
{
algorithm: {
name: "AES-CTR",
counter: Buffer.from("1234567890abcdef"),
length: 128,
} as AesCtrParams,
data: Buffer.from("test message"),
encData: Buffer.from("55a00e2851f00aba53bbd02c", "hex"),
key: {
format: "raw",
data: Buffer.from("1234567890abcdef12345678"),
algorithm: { name: "AES-CTR" },
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
},
],
import: [
{
name: "raw",
format: "raw",
data: Buffer.from("1234567890abcdef12345678"),
algorithm: "AES-CTR",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
{
name: "jwk",
format: "jwk",
data: {
kty: "oct",
alg: "A192CTR",
k: "MTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4",
ext: true,
key_ops: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
algorithm: "AES-CTR",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
],
},
},
{
name: "AES-256-CTR",
actions: {
generateKey: [
{
algorithm: { name: "AES-CTR", length: 256 } as AesKeyGenParams,
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
],
encrypt: [
{
algorithm: {
name: "AES-CTR",
counter: Buffer.from("1234567890abcdef"),
length: 128,
} as AesCtrParams,
data: Buffer.from("test message"),
encData: Buffer.from("8208d011a20162c8af7a9ce5", "hex"),
key: {
format: "raw",
data: Buffer.from("1234567890abcdef1234567809abcdef"),
algorithm: { name: "AES-CTR" },
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
},
],
import: [
{
name: "raw",
format: "raw",
data: Buffer.from("1234567890abcdef1234567890abcdef"),
algorithm: "AES-CTR",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
{
name: "jwk",
format: "jwk",
data: {
kty: "oct",
alg: "A256CTR",
k: "MTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4OTBhYmNkZWY",
ext: true,
key_ops: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
algorithm: "AES-CTR",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
],
},
},
//#endregion
//#region AES-GCM
{
name: "AES-128-GCM",
actions: {
generateKey: [
{
algorithm: { name: "AES-GCM", length: 128 } as AesKeyGenParams,
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
],
encrypt: [
{
algorithm: {
name: "AES-GCM",
iv: Buffer.from("1234567890ab"),
} as AesGcmParams,
data: Buffer.from("test message"),
encData: Buffer.from("68d645649ddf8152a253304d698185072f28cdcf7644ac6064bcb240", "hex"),
key: {
format: "raw",
data: Buffer.from("1234567890abcdef"),
algorithm: { name: "AES-GCM" },
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
},
],
import: [
{
name: "raw",
format: "raw",
data: Buffer.from("1234567890abcdef"),
algorithm: "AES-GCM",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
{
name: "jwk",
format: "jwk",
data: {
kty: "oct",
alg: "A128GCM",
k: "MTIzNDU2Nzg5MGFiY2RlZg",
ext: true,
key_ops: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
algorithm: "AES-GCM",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
],
},
},
{
name: "AES-192-GCM",
actions: {
generateKey: [
{
algorithm: { name: "AES-GCM", length: 192 } as AesKeyGenParams,
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
],
encrypt: [
{
algorithm: {
name: "AES-GCM",
iv: Buffer.from("1234567890ab"),
} as AesGcmParams,
data: Buffer.from("test message"),
encData: Buffer.from("d8eab579ed2418f41ca9c4567226f54cb391d3ca2cb6819dace35691", "hex"),
key: {
format: "raw",
data: Buffer.from("1234567890abcdef12345678"),
algorithm: { name: "AES-GCM" },
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
},
],
import: [
{
name: "raw",
format: "raw",
data: Buffer.from("1234567890abcdef12345678"),
algorithm: "AES-GCM",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
{
name: "jwk",
format: "jwk",
data: {
kty: "oct",
alg: "A192GCM",
k: "MTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4",
ext: true,
key_ops: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
algorithm: "AES-GCM",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
],
},
},
{
name: "AES-256-GCM",
actions: {
generateKey: [
{
algorithm: { name: "AES-CTR", length: 256 } as AesKeyGenParams,
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
],
encrypt: [
{
algorithm: {
name: "AES-GCM",
iv: Buffer.from("1234567890ab"),
} as AesGcmParams,
data: Buffer.from("test message"),
encData: Buffer.from("f961f2aadbe689ffce86fcaf2619ab647950afcf19e55b71b857c79d", "hex"),
key: {
format: "raw",
data: Buffer.from("1234567890abcdef1234567809abcdef"),
algorithm: { name: "AES-GCM" },
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
},
],
import: [
{
name: "raw",
format: "raw",
data: Buffer.from("1234567890abcdef1234567890abcdef"),
algorithm: "AES-GCM",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
{
name: "jwk",
format: "jwk",
data: {
kty: "oct",
alg: "A256GCM",
k: "MTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4OTBhYmNkZWY",
ext: true,
key_ops: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
algorithm: "AES-GCM",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
],
},
},
//#endregion
]);
});

114
test/asn.ts Normal file
View File

@ -0,0 +1,114 @@
import { AsnParser, AsnSerializer } from "@peculiar/asn1-schema";
import { JsonParser, JsonSerializer } from "@peculiar/json-schema";
import assert from "assert";
import * as asn from "../src/asn";
context("ASN", () => {
context("RSA", () => {
context("PrivateKey", () => {
const bytes = Buffer.from("30820278020100300d06092a864886f70d0101010500048202623082025e02010002818100f73bb19904c72d3a3a837425d734190a26760151bdd6e15c948b21996e64a713dcd4cf7c0ac722e4ee589ed82ba9a8ab4f17b9283c5145422a02f35f140b586a8adfeb6f40bfc223fc1c777d692126f94f8abfac63bc67f593c8cd027326f70888fb9ac20d573cf65f90f3825156a64c34d964e0b7aba3fffe38b9cd773beea3020301000102818100b52ba83c42b5165e7a2c843b8a9521d83f50f02c8f59dcb17424f48d33c94c6a10c45dfb3f06a87a6c72c28c148af20fff189a5572c5f763c1d781b265c4de975bcfaa303e365e0e78784d64ea8345d34c988018c8868edef526da3cd9a458306a3c9f37545a645490bacf8dd040aa7aab52b0cd38c4744ddc556a8b6dbeeb51024100fc66e213e9785a33417dc830534644fcb85ebc70c3b4f6d9957637e0142879e489ec5a0fc38acaa579100d1fff585d2ad13a7f4280985e47534a36a369eed0c9024100fac1f2aa44b2dd000febaf75b9dd554068b0c6f15b41fada8089d7a52c447980498dcd95546454f0860b8a7747f40c0e7362f5a1a935a61327e4fb6298d2460b02410085dbf68882e89e45d2b4e7a7a1728201d9b1fc947d668b0828393336f6d9e4936d06595944e665c4ce68d1dd2769f0b75591858e7a6ed4895545e5a652f721e9024100a7a03339c346d6198e8afcf5f3d39383c4f837656c9dc4b5802ba52e53534aed893df3ea194a20c5d0b5b2505e9733e00d1e60193b6613a8c68879cbc560fb550240169d53d05b35edae1a5b48351b7281e12ef2f281c8026d64ed3d884249788830fc23fa45d73adb3224fa26f65e51b4838572583135de65fb13d360d9e3e19c74", "hex");
const json = {
n: "APc7sZkExy06OoN0Jdc0GQomdgFRvdbhXJSLIZluZKcT3NTPfArHIuTuWJ7YK6moq08XuSg8UUVCKgLzXxQLWGqK3-tvQL_CI_wcd31pISb5T4q_rGO8Z_WTyM0Ccyb3CIj7msINVzz2X5DzglFWpkw02WTgt6uj__44uc13O-6j",
e: "AQAB",
d: "ALUrqDxCtRZeeiyEO4qVIdg_UPAsj1ncsXQk9I0zyUxqEMRd-z8GqHpscsKMFIryD_8YmlVyxfdjwdeBsmXE3pdbz6owPjZeDnh4TWTqg0XTTJiAGMiGjt71Jto82aRYMGo8nzdUWmRUkLrPjdBAqnqrUrDNOMR0TdxVaottvutR",
p: "APxm4hPpeFozQX3IMFNGRPy4Xrxww7T22ZV2N-AUKHnkiexaD8OKyqV5EA0f_1hdKtE6f0KAmF5HU0o2o2nu0Mk",
q: "APrB8qpEst0AD-uvdbndVUBosMbxW0H62oCJ16UsRHmASY3NlVRkVPCGC4p3R_QMDnNi9aGpNaYTJ-T7YpjSRgs",
dp: "AIXb9oiC6J5F0rTnp6FyggHZsfyUfWaLCCg5Mzb22eSTbQZZWUTmZcTOaNHdJ2nwt1WRhY56btSJVUXlplL3Iek",
dq: "AKegMznDRtYZjor89fPTk4PE-DdlbJ3EtYArpS5TU0rtiT3z6hlKIMXQtbJQXpcz4A0eYBk7ZhOoxoh5y8Vg-1U",
qi: "Fp1T0Fs17a4aW0g1G3KB4S7y8oHIAm1k7T2IQkl4iDD8I_pF1zrbMiT6JvZeUbSDhXJYMTXeZfsT02DZ4-GcdA",
};
it("parse", () => {
const keyInfo = AsnParser.parse(bytes, asn.PrivateKeyInfo);
const key = AsnParser.parse(keyInfo.privateKey, asn.RsaPrivateKey);
const jsonKey = JsonSerializer.toJSON(key);
assert.deepEqual(jsonKey, json);
});
it("serialize", () => {
const key = JsonParser.fromJSON(json, asn.RsaPrivateKey);
const keyInfo = new asn.PrivateKeyInfo();
keyInfo.privateKeyAlgorithm.algorithm = "1.2.840.113549.1.1.1";
keyInfo.privateKeyAlgorithm.parameters = null;
keyInfo.privateKey = AsnSerializer.serialize(key);
const asnKeyInfo = Buffer.from(AsnSerializer.serialize(keyInfo));
assert.equal(asnKeyInfo.equals(bytes), true);
});
});
context("PublicKey", () => {
const bytes = Buffer.from("30819f300d06092a864886f70d010101050003818d0030818902818100f615b745314ffe4669255dfe68953184bb8e5db54eecd35b4c51ee899ce7e60aaf19cc765d924f94be93d6809ba506fab26b9f8ef0cf6ab2aec1942da222992f8dad2e621845f014f9e831a529665faf0a9b8ca97356a602ce8d17cd3469aafa2de82546773540fa480510d1906c78c87b81850c26fdaeccce37cd5fdeba7e050203010001", "hex");
const json = {
n: "APYVt0UxT_5GaSVd_miVMYS7jl21TuzTW0xR7omc5-YKrxnMdl2ST5S-k9aAm6UG-rJrn47wz2qyrsGULaIimS-NrS5iGEXwFPnoMaUpZl-vCpuMqXNWpgLOjRfNNGmq-i3oJUZ3NUD6SAUQ0ZBseMh7gYUMJv2uzM43zV_eun4F",
e: "AQAB",
};
it("parse", () => {
const keyInfo = AsnParser.parse(bytes, asn.PublicKeyInfo);
const key = AsnParser.parse(keyInfo.publicKey, asn.RsaPublicKey);
const jsonKey = JsonSerializer.toJSON(key);
assert.deepEqual(jsonKey, json);
});
it("serialize", () => {
const key = JsonParser.fromJSON(json, asn.RsaPublicKey);
const keyInfo = new asn.PublicKeyInfo();
keyInfo.publicKeyAlgorithm.algorithm = "1.2.840.113549.1.1.1";
keyInfo.publicKeyAlgorithm.parameters = null;
keyInfo.publicKey = AsnSerializer.serialize(key);
const asnKeyInfo = Buffer.from(AsnSerializer.serialize(keyInfo));
assert.equal(asnKeyInfo.equals(bytes), true);
});
});
});
context("EC", () => {
context("PrivateKey", () => {
const bytes = Buffer.from("308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b0201010420db0964fc2a963e9a2aef561f57db3556fa87e83ceb2e5f6dc84b00c18aa873e3a144034200043266c1386af7a0993b169393df1f7c4016e27fd48642e8d512c775b31c8f06722baef1310974a6c63aff2ef8832fba27f021f5ae2f2c6c2d56fde5be5ade78f5", "hex");
const json = {
d: "2wlk_CqWPpoq71YfV9s1VvqH6DzrLl9tyEsAwYqoc-M",
x: "MmbBOGr3oJk7FpOT3x98QBbif9SGQujVEsd1sxyPBnI",
y: "K67xMQl0psY6_y74gy-6J_Ah9a4vLGwtVv3lvlreePU",
};
it("parse", () => {
const keyInfo = AsnParser.parse(bytes, asn.PrivateKeyInfo);
const key = AsnParser.parse(keyInfo.privateKey, asn.EcPrivateKey);
const jsonKey = JsonSerializer.toJSON(key);
assert.deepEqual(jsonKey, json);
});
it("serialize", () => {
const keyInfo = new asn.PrivateKeyInfo();
keyInfo.privateKeyAlgorithm.algorithm = "1.2.840.10045.2.1";
keyInfo.privateKeyAlgorithm.parameters = AsnSerializer.serialize(
new asn.ObjectIdentifier("1.2.840.10045.3.1.7"),
);
const key = JsonParser.fromJSON(json, asn.EcPrivateKey);
keyInfo.privateKey = AsnSerializer.serialize(key);
const asnKeyInfo = Buffer.from(AsnSerializer.serialize(keyInfo));
assert.equal(asnKeyInfo.equals(bytes), true);
});
});
});
});

28
test/crypto.ts Normal file
View File

@ -0,0 +1,28 @@
import assert from "assert";
import { Crypto } from "../src";
context("Crypto", () => {
const crypto = new Crypto();
context("getRandomValues", () => {
it("Uint8Array", () => {
const array = new Uint8Array(5);
const array2 = crypto.getRandomValues(array);
assert.notEqual(Buffer.from(array).toString("hex"), "0000000000");
assert.equal(Buffer.from(array2).equals(array), true);
});
it("Uint16Array", () => {
const array = new Uint16Array(5);
const array2 = crypto.getRandomValues(array);
assert.notEqual(Buffer.from(array).toString("hex"), "00000000000000000000");
assert.equal(Buffer.from(array2).equals(Buffer.from(array)), true);
});
});
});

129
test/des.ts Normal file
View File

@ -0,0 +1,129 @@
import assert from "assert";
import * as core from "webcrypto-core";
import { Crypto } from "../src";
import { DesCbcParams } from "../src/mechs";
import { testCrypto } from "./helper";
context("DES", () => {
const crypto = new Crypto();
testCrypto(crypto, [
{
name: "DES-CBC",
actions: {
generateKey: [
{
algorithm: { name: "DES-CBC", length: 64 } as core.DesKeyGenParams,
extractable: false,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] as KeyUsage[],
},
],
encrypt: [
{
algorithm: {
name: "DES-CBC",
iv: Buffer.from("12345678"),
} as DesCbcParams,
data: Buffer.from("test message"),
encData: Buffer.from("3af3f901ff01fe0102dfbbf37d9bdb94", "hex"),
key: {
format: "raw" as KeyFormat,
algorithm: { name: "DES-CBC" },
extractable: true,
keyUsages: ["encrypt", "decrypt"] as KeyUsage[],
data: Buffer.from("12345678"),
},
},
],
import: [
{
name: "raw",
format: "raw" as KeyFormat,
data: Buffer.from("12345678"),
algorithm: "DES-CBC",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] as KeyUsage[],
},
{
name: "jwk",
format: "jwk" as KeyFormat,
data: {
kty: "oct",
alg: "DES-CBC",
k: "MTIzNDU2Nzg",
ext: true,
key_ops: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
algorithm: "DES-CBC",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] as KeyUsage[],
},
],
},
},
{
name: "DES-EDE3-CBC",
actions: {
generateKey: [
{
algorithm: { name: "DES-EDE3-CBC", length: 192 } as core.DesKeyGenParams,
extractable: false,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] as KeyUsage[],
},
],
encrypt: [
{
algorithm: {
name: "DES-EDE3-CBC",
iv: Buffer.from("12345678"),
} as DesCbcParams,
data: Buffer.from("test message"),
encData: Buffer.from("b9ef20e7db926490e4ff8680d99d2141", "hex"),
key: {
format: "raw" as KeyFormat,
algorithm: { name: "DES-EDE3-CBC" },
extractable: true,
keyUsages: ["encrypt", "decrypt"] as KeyUsage[],
data: Buffer.from("1234567890abcdef12345678"),
},
},
],
import: [
{
name: "raw",
format: "raw" as KeyFormat,
data: Buffer.from("1234567890abcdef12345678"),
algorithm: "DES-EDE3-CBC",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] as KeyUsage[],
},
{
name: "wrong key size",
error: core.OperationError,
format: "raw",
data: Buffer.from("12345678"),
algorithm: "DES-EDE3-CBC",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
{
name: "jwk",
format: "jwk",
data: {
kty: "oct",
alg: "3DES-CBC",
k: "MTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4",
ext: true,
key_ops: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
algorithm: "DES-EDE3-CBC",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
],
},
},
]);
});

539
test/ec.ts Normal file
View File

@ -0,0 +1,539 @@
import { Convert } from "pvtsutils";
import { Crypto } from "../src";
import { ITestGenerateKeyAction, testCrypto } from "./helper";
context("EC", () => {
const crypto = new Crypto();
testCrypto(crypto, [
{
name: "ECDSA",
actions: {
generateKey: ["P-256", "P-384", "P-521", "K-256"].map((namedCurve) => {
return {
name: namedCurve,
algorithm: {
name: "ECDSA",
namedCurve,
} as EcKeyGenParams,
extractable: false,
keyUsages: ["sign", "verify"],
} as ITestGenerateKeyAction;
}),
import: [
{
name: "JWK public key P-256",
format: "jwk" as KeyFormat,
data: {
crv: "P-256",
ext: true,
key_ops: ["verify"],
kty: "EC",
x: "dJ9C3NyXDa3fMeZ477NWdp9W6faytA7A_U1ub-tyRcs",
y: "aS0_VVe_SeIm8w5TBWjUEco7us6EJUMPKKJaIh36Lho",
},
algorithm: {
name: "ECDSA",
namedCurve: "P-256",
} as EcKeyImportParams,
extractable: true,
keyUsages: ["verify"],
},
{
name: "JWK public key P-384",
format: "jwk" as KeyFormat,
data: {
crv: "P-384",
ext: true,
key_ops: ["verify"],
kty: "EC",
x: "eHlLZ4jnt_Drs-qoVxK-SZZvhNhi34jLCgyaEZ9XI6bdlK3y1ettm8K5SnLtDhWO",
y: "qbr3pOOViYDQ2wWG-_9pwQ0S8cHV0LP-x9JO5dl-dsFYtbGix9YH7fRNOl8GkP-6",
},
algorithm: {
name: "ECDSA",
namedCurve: "P-384",
} as EcKeyImportParams,
extractable: true,
keyUsages: ["verify"],
},
{
name: "JWK public key P-521",
format: "jwk" as KeyFormat,
data: {
crv: "P-521",
ext: true,
key_ops: ["verify"],
kty: "EC",
x: "Adqn62IVQX8LIauAXrUtxH05DHlRygKcsP9qWAnd9tfJvpaG7bzIs16WMEUe1V-f4AxbQJceU4xCP8dJppK_fzdC",
y: "AEo3s1eExCOvpuBtBWnWlr7TuFhq_fMzqX9eqDHiy8qWl4I_koQtMePodrAc85mVrJAjvsa77Y3Ul3QtIWpXXBqa",
},
algorithm: {
name: "ECDSA",
namedCurve: "P-521",
} as EcKeyImportParams,
extractable: true,
keyUsages: ["verify"],
},
//#region SPKI
{
name: "SPKI P-256",
format: "spki" as KeyFormat,
data: Convert.FromBase64("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEoZMMqyfA16N6bvloFHmalk/SGMisr3zSXFZdR8F9UkaY7hF13hHiQtwp2YO+1zd7jwYi1Y7SMA9iUrC+ap2OCw=="),
algorithm: {
name: "ECDSA",
namedCurve: "P-256",
} as EcKeyImportParams,
extractable: true,
keyUsages: ["verify"],
},
{
name: "SPKI P-384",
format: "spki" as KeyFormat,
data: Convert.FromBase64("MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8Kf5Wv21nksy0LuMlkMZv9sxTVAmzNWt81b6MVlYuzxl9D2/obwoVp86pTe4BM79gWWj8pfLc1XrjaIyMSrV8+05IejRLB3i4c0KTGA6QARGm3/AOm0MbTt6kMQF7drL"),
algorithm: {
name: "ECDSA",
namedCurve: "P-384",
} as EcKeyImportParams,
extractable: true,
keyUsages: ["verify"],
},
{
name: "SPKI P-521",
format: "spki" as KeyFormat,
data: Convert.FromBase64("MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQB+/g37ii0T5iLHCAaXcYRRoNpT0LhfeAr88OwQY4cUpQm1S9lkR0EVUtyuYrYsMB8FarhAZYsLtOiyhjl/Y5f+lQAZ6veWILhbDcbrSNhTPSp3wamAm8QT3EjPUkJlYjHefuAUBIYS9pl5FWjK1pI9fkYe3bdAemkjP1ccHVzqZU9sjg="),
algorithm: {
name: "ECDSA",
namedCurve: "P-521",
} as EcKeyImportParams,
extractable: true,
keyUsages: ["verify"],
},
//#endregion
//#region RAW
{
name: "RAW P-256",
format: "raw" as KeyFormat,
data: Convert.FromBase64("BEehen4AavxgJkx5EPZpBeopzgZuY+1i3cMR9iYdZj+IY7/h98Q/GboC2BKS6lT0hEyt6y1DFFXj8ytuof4zXR4="),
algorithm: {
name: "ECDSA",
namedCurve: "P-256",
} as EcKeyImportParams,
extractable: true,
keyUsages: ["verify"],
},
{
name: "RAW P-384",
format: "raw" as KeyFormat,
data: Convert.FromBase64("BGYoCpP3Qv4o0s2GWg5xFnasdkI8h6K/LeBm4TV+9HCsqnoXFUJDM5SDeZ0rcCAUUuaPJVn5sedPEKEGW80zmLM1rBOG2RzaBq+uhEJkLpibongnzMZNX2LB58wGJ05f2g=="),
algorithm: {
name: "ECDSA",
namedCurve: "P-384",
} as EcKeyImportParams,
extractable: true,
keyUsages: ["verify"],
},
{
name: "RAW P-521",
format: "raw" as KeyFormat,
data: Convert.FromBase64("BABIiZ3f90HQsl4CYHt7Q1WnOIOs+dxeecfQrew/z+73yI/bUrMlmR3mOVARtvg7ZPX7h3lSSqzA1Vv6iv7bPYekcwDKQPeLJkem//H7zY8xtKY+YrYnLUVv6vPE9jyk2vYkj8QPxQRdeIT5bzY2BzTiTcLHDwi2+w2Eonkt7M+zb4G6xw=="),
algorithm: {
name: "ECDSA",
namedCurve: "P-521",
} as EcKeyImportParams,
extractable: true,
keyUsages: ["verify"],
},
//#endregion
//#region JWK Private key
{
name: "JWK private key P-256",
format: "jwk" as KeyFormat,
data: {
crv: "P-256",
d: "RIrfLaesGcEeNy7fOoVIkgMiImJOFw1Y44kdrtK_49I",
ext: true,
key_ops: ["sign"],
kty: "EC",
x: "wJls5KwIfRDxJEvyAlo3G84qNY0HjvsujyxDSMYAlm4",
y: "I61bQbFgnzfDom68P86kRo98fTrV_9HLeqa4gYnGOdw",
},
algorithm: {
name: "ECDSA",
namedCurve: "P-256",
} as EcKeyImportParams,
extractable: true,
keyUsages: ["sign"],
},
{
name: "JWK private key P-384",
format: "jwk" as KeyFormat,
data: {
crv: "P-384",
d: "4YQRcOD-4LMLEr-qsRhQ1oq8hfPKa66BfGVUv3LUlsf2OU3aFG5FxabG5xFUoAE2",
ext: true,
key_ops: ["sign"],
kty: "EC",
x: "XKewC5QCVW9w-SFyZd3z1vlmCqbYYuJmoGRzKtjwkpYQD_RhNAc3ck29d_t0QmaT",
y: "6oSrri3ry1_8c2NKM8aiaJcjwd146ITViezQ7-BpsE1-wDH18P1QkbmR3-Ho54We",
},
algorithm: {
name: "ECDSA",
namedCurve: "P-384",
} as EcKeyImportParams,
extractable: true,
keyUsages: ["sign"],
},
{
name: "JWK private key P-521",
format: "jwk" as KeyFormat,
data: {
crv: "P-521",
d: "AItxxufCXVzwPVePNe9Acy8HfbmYeUVkiEyFXdsYRnHxqgDpwucVnIJ44-ZWRpuWu5Ep5KVV3vY9Hp8nJfksi7z2",
ext: true,
key_ops: ["sign"],
kty: "EC",
x: "AJGuTezC-8F-d_0bBpS502OK0z63vo87Dw99a3NUm6gm5pQC1rwu7LcblGqFWOuFBZhsF8I6OFjYvsR-z3u7hhCA",
y: "AFQT8BB9hBf7UwwBUV4im8bFJ7_MD0qOZMVetmdbooMjfec1q3wU5cSoy4LvCnWAaFqu5havUxwnAUuPUWGG_InR",
},
algorithm: {
name: "ECDSA",
namedCurve: "P-521",
} as EcKeyImportParams,
extractable: true,
keyUsages: ["sign"],
},
//#endregion
//#region PKCS8
{
name: "PKCS8 P-256",
format: "pkcs8" as KeyFormat,
data: Convert.FromBase64("MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgiVEY5OFo3J7g1BnSw/WEWykY/alrhNmpEBLy/7cNnuGhRANCAAQ4SFnMDGYc5kWv7D0gtgUj/Bzbu0B6Bq6XK1vqOo//2m8FS1D4kYKV4KDfFRWehKEtrMBjjkW6OZcM/n0qZ6Uw"),
algorithm: {
name: "ECDSA",
namedCurve: "P-256",
} as EcKeyImportParams,
extractable: true,
keyUsages: ["sign"],
},
{
name: "PKCS8 P-384",
format: "pkcs8" as KeyFormat,
data: Convert.FromBase64("MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDCY18ajGPCgLv4aF1UkkohMEaB5MU1MyfkuFQSQVDYHLWFTn8f9czce7aTIDjkCx0OhZANiAAR1fni8TC1N1NdXvx25kJyK3y3rpVVaAmA44Wm9jIFseGmSzm/EgmKOFclSzQdEpSC6jxi3olIJ4iYetjl36Ygfwed/xqrsiV6BUb/ny2mimzk3r0M9H6yvbEVQFd7rEAA="),
algorithm: {
name: "ECDSA",
namedCurve: "P-384",
} as EcKeyImportParams,
extractable: true,
keyUsages: ["sign"],
},
{
name: "PKCS8 P-521",
format: "pkcs8" as KeyFormat,
data: Convert.FromBase64("MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIAbHGkGfik5q0l+ZMI70dbpTGWeKy1+c3mG98wHmnpU+d2bArcYDOXcoqg5Ic/pnmtHvxmk+El33u3XogGONKPlouhgYkDgYYABAH16CoJzEx+Oncpeam6ysUG17y9ttNm5Eg8WqD+BJkP9ju3R22I5PVyYYYZ3ICc1IyDGxFCS7leO1N7tqQLaLi8NAEFTkwCy1G6AAK7LbSa1hNC2fUAaC9L8QJNUNJpjgYiXPDmEnaRNT1XXL00Bjo5iMpE2Ddc/Kp6ktTAo2jOMnfmow=="),
algorithm: {
name: "ECDSA",
namedCurve: "P-521",
} as EcKeyImportParams,
extractable: true,
keyUsages: ["sign"],
},
//#endregion
],
sign: [
{
key: {
privateKey: {
format: "pkcs8" as KeyFormat,
data: Convert.FromBase64("MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgsY5TBHM+9mLXGpFaPmrigl6+jl0XWzazxu1lbwb5KRahRANCAATqDP2L/xxSOlckG+j6oPHfzBE4WpmjA/YE9sP2rXpXW1qe9I/GJ7wjlOTXpqHUxQeBbps8jSvV+A7DzQqzjOst"),
algorithm: {
name: "ECDSA",
namedCurve: "P-256",
} as EcKeyImportParams,
extractable: true,
keyUsages: ["sign"],
},
publicKey: {
format: "spki" as KeyFormat,
data: Convert.FromBase64("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE6gz9i/8cUjpXJBvo+qDx38wROFqZowP2BPbD9q16V1tanvSPxie8I5Tk16ah1MUHgW6bPI0r1fgOw80Ks4zrLQ=="),
algorithm: {
name: "ECDSA",
namedCurve: "P-256",
} as EcKeyImportParams,
extractable: true,
keyUsages: ["verify"],
},
},
data: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]),
signature: Convert.FromBase64("gsTh0IcWfzj3hjjourRgzTIsNa+wcDEDlKnkEA4Jv8ygLF2IDIOXpCD7ocCGo7xlSMGTme78CyrPqWGSz95mZg=="),
algorithm: {
name: "ECDSA",
hash: "SHA-256",
} as EcdsaParams,
},
{
key: {
privateKey: {
format: "pkcs8" as KeyFormat,
data: Convert.FromBase64("MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQg0h6+W+/4eFVP+i79hrzYeiEJ6UrveFYhuhoXRW+g/LGhRANCAASiJU6MaFN5fshUv6X5rCf/RjLQ0nAXj06gBdo3ruYiKZf8daAcYImniAq81PjF0j6eTwCy4bYbkyfBQtrtCTKR"),
algorithm: {
name: "ECDSA",
namedCurve: "K-256",
} as EcKeyImportParams,
extractable: true,
keyUsages: ["sign"],
},
publicKey: {
format: "spki" as KeyFormat,
data: Convert.FromBase64("MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEoiVOjGhTeX7IVL+l+awn/0Yy0NJwF49OoAXaN67mIimX/HWgHGCJp4gKvNT4xdI+nk8AsuG2G5MnwULa7QkykQ=="),
algorithm: {
name: "ECDSA",
namedCurve: "K-256",
} as EcKeyImportParams,
extractable: true,
keyUsages: ["verify"],
},
},
data: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]),
signature: Convert.FromBase64("lqUTZHqf9v9KcOCw5r5wR1sCt9RPA0ONVW6vqejpoALehd6vtAb+ybVrDEtyUDpBFw9UIRIW6GnXRrAz4KaO4Q=="),
algorithm: {
name: "ECDSA",
hash: "SHA-256",
} as EcdsaParams,
},
],
deriveBits: [
{
name: "P-256 128",
key: {
privateKey: {
format: "pkcs8" as KeyFormat,
data: Convert.FromBase64("MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgQA7bkTNYlIYVb9+DavBlJ3b08f0892or3XwfscA3tLGhRANCAARzsy+ZcbrNchF7SrpL0hYnGp6ICX77jXUrpMYkq0BuzfaPFWcu9YZH5ASUzQJGz9eCK3mDXEbLCuiHRw3dwkFs"),
algorithm: {
name: "ECDH",
namedCurve: "P-256",
} as EcKeyImportParams,
extractable: true,
keyUsages: ["deriveBits"],
},
publicKey: {
format: "spki" as KeyFormat,
data: Convert.FromBase64("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEc7MvmXG6zXIRe0q6S9IWJxqeiAl++411K6TGJKtAbs32jxVnLvWGR+QElM0CRs/Xgit5g1xGywroh0cN3cJBbA=="),
algorithm: {
name: "ECDH",
namedCurve: "P-256",
} as EcKeyImportParams,
extractable: true,
keyUsages: [],
},
},
data: Convert.FromBase64("Jlc1/Zqi/8mH1oQT8+YfCA=="),
algorithm: {
name: "ECDH",
},
length: 128,
},
{
name: "P-384 192",
key: {
privateKey: {
format: "pkcs8" as KeyFormat,
data: Convert.FromBase64("MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDAFOXcWxQ+YqPdUqc9Iar3ZDf012ZtQAFajBMApKpd2WPQccBmyPzvDZJSWKe3d5jShZANiAAQ4Z43bP7d5fUFIBorLA1pBFTwDLb6XA7J871VUwyu64q8L5qidV7iBZK3P+9m7eMMQWm0drWPvrEszE+4jEsS4HIbBeuduBU+6R46Orv+V6VXU1hAXKSdMFZOCzdbDFlE="),
algorithm: {
name: "ECDH",
namedCurve: "P-384",
} as EcKeyImportParams,
extractable: true,
keyUsages: ["deriveBits"],
},
publicKey: {
format: "spki" as KeyFormat,
data: Convert.FromBase64("MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEOGeN2z+3eX1BSAaKywNaQRU8Ay2+lwOyfO9VVMMruuKvC+aonVe4gWStz/vZu3jDEFptHa1j76xLMxPuIxLEuByGwXrnbgVPukeOjq7/lelV1NYQFyknTBWTgs3WwxZR"),
algorithm: {
name: "ECDH",
namedCurve: "P-384",
} as EcKeyImportParams,
extractable: true,
keyUsages: [],
},
},
data: Convert.FromBase64("2EKT/nmV68wIXFMZiCv4CyOEhWzpwdQ5"),
algorithm: {
name: "ECDH",
},
length: 192,
},
{
name: "P-521 256",
key: {
privateKey: {
format: "pkcs8" as KeyFormat,
data: Convert.FromBase64("MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIB6PyCXpJ4TWPpwlGAmayLz5ecYHT+1ilxD64HytpTaViUS72sEzG1JMApD31+STX0zeVcARfG+yh71dXLCTlqqHGhgYkDgYYABADgIblBbth8vnOZt/HLU9VdUJHmenwRRADVZWL+P5IeCDQs6B87API41R3+91xFDHnjst9VKksYl/NJIIfl6b9cmABO6z80mTz3+0klquIpSQLidK2aFaFbqiGnMdCO+AZfwxu2qBx+1f5MwbHXUW5HXsfmEvzBUC9xCQKLpQ8oZYBrSg=="),
algorithm: {
name: "ECDH",
namedCurve: "P-521",
} as EcKeyImportParams,
extractable: true,
keyUsages: ["deriveBits"],
},
publicKey: {
format: "spki" as KeyFormat,
data: Convert.FromBase64("MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQA4CG5QW7YfL5zmbfxy1PVXVCR5np8EUQA1WVi/j+SHgg0LOgfOwDyONUd/vdcRQx547LfVSpLGJfzSSCH5em/XJgATus/NJk89/tJJariKUkC4nStmhWhW6ohpzHQjvgGX8MbtqgcftX+TMGx11FuR17H5hL8wVAvcQkCi6UPKGWAa0o="),
algorithm: {
name: "ECDH",
namedCurve: "P-521",
} as EcKeyImportParams,
extractable: true,
keyUsages: [],
},
},
data: Convert.FromBase64("AS2ene28pmWYdJwW6dyTXUe1eq1p2i8QEIo/rXSiJRo="),
algorithm: {
name: "ECDH",
},
length: 256,
},
{
name: "K-256 128",
key: {
privateKey: {
format: "pkcs8" as KeyFormat,
data: Convert.FromBase64("MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQghgLhhrz/EYuB0G08/UoM5nV9jS7Pl/rtIcXeJkc2b3uhRANCAARgMfEiAPcF7pmEuLRGRRFXEKSwcJwqURKK/Pqo8MaqU0cl7eNQmLJ7mFpBtTDY8hr9xxJeIP9sI/u83A1F5ag7"),
algorithm: {
name: "ECDH",
namedCurve: "K-256",
} as EcKeyImportParams,
extractable: true,
keyUsages: ["deriveBits"],
},
publicKey: {
format: "spki" as KeyFormat,
data: Convert.FromBase64("MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEYDHxIgD3Be6ZhLi0RkURVxCksHCcKlESivz6qPDGqlNHJe3jUJiye5haQbUw2PIa/ccSXiD/bCP7vNwNReWoOw=="),
algorithm: {
name: "ECDH",
namedCurve: "K-256",
} as EcKeyImportParams,
extractable: true,
keyUsages: [],
},
},
data: Convert.FromBase64("3+2JX3D4/veBGJXnvU+aTg=="),
algorithm: {
name: "ECDH",
},
length: 128,
},
],
deriveKey: [
{
name: "P-256 128",
key: {
privateKey: {
format: "pkcs8" as KeyFormat,
data: Convert.FromBase64("MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgQA7bkTNYlIYVb9+DavBlJ3b08f0892or3XwfscA3tLGhRANCAARzsy+ZcbrNchF7SrpL0hYnGp6ICX77jXUrpMYkq0BuzfaPFWcu9YZH5ASUzQJGz9eCK3mDXEbLCuiHRw3dwkFs"),
algorithm: {
name: "ECDH",
namedCurve: "P-256",
} as EcKeyImportParams,
extractable: true,
keyUsages: ["deriveKey"],
},
publicKey: {
format: "spki" as KeyFormat,
data: Convert.FromBase64("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEc7MvmXG6zXIRe0q6S9IWJxqeiAl++411K6TGJKtAbs32jxVnLvWGR+QElM0CRs/Xgit5g1xGywroh0cN3cJBbA=="),
algorithm: {
name: "ECDH",
namedCurve: "P-256",
} as EcKeyImportParams,
extractable: true,
keyUsages: [],
},
},
algorithm: {
name: "ECDH",
},
derivedKeyType: {
name: "AES-CBC",
length: 128,
} as AesKeyAlgorithm,
keyUsages: ["encrypt", "decrypt"],
format: "raw",
keyData: Convert.FromBase64("Jlc1/Zqi/8mH1oQT8+YfCA=="),
},
{
name: "P-384 192",
key: {
privateKey: {
format: "pkcs8" as KeyFormat,
data: Convert.FromBase64("MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDAFOXcWxQ+YqPdUqc9Iar3ZDf012ZtQAFajBMApKpd2WPQccBmyPzvDZJSWKe3d5jShZANiAAQ4Z43bP7d5fUFIBorLA1pBFTwDLb6XA7J871VUwyu64q8L5qidV7iBZK3P+9m7eMMQWm0drWPvrEszE+4jEsS4HIbBeuduBU+6R46Orv+V6VXU1hAXKSdMFZOCzdbDFlE="),
algorithm: {
name: "ECDH",
namedCurve: "P-384",
} as EcKeyImportParams,
extractable: true,
keyUsages: ["deriveKey"],
},
publicKey: {
format: "spki" as KeyFormat,
data: Convert.FromBase64("MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEOGeN2z+3eX1BSAaKywNaQRU8Ay2+lwOyfO9VVMMruuKvC+aonVe4gWStz/vZu3jDEFptHa1j76xLMxPuIxLEuByGwXrnbgVPukeOjq7/lelV1NYQFyknTBWTgs3WwxZR"),
algorithm: {
name: "ECDH",
namedCurve: "P-384",
} as EcKeyImportParams,
extractable: true,
keyUsages: [],
},
},
algorithm: {
name: "ECDH",
},
derivedKeyType: {
name: "AES-GCM",
length: 192,
} as AesKeyAlgorithm,
keyUsages: ["encrypt", "decrypt"],
format: "raw",
keyData: Convert.FromBase64("2EKT/nmV68wIXFMZiCv4CyOEhWzpwdQ5"),
},
{
name: "P-521 256",
key: {
privateKey: {
format: "pkcs8" as KeyFormat,
data: Convert.FromBase64("MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIB6PyCXpJ4TWPpwlGAmayLz5ecYHT+1ilxD64HytpTaViUS72sEzG1JMApD31+STX0zeVcARfG+yh71dXLCTlqqHGhgYkDgYYABADgIblBbth8vnOZt/HLU9VdUJHmenwRRADVZWL+P5IeCDQs6B87API41R3+91xFDHnjst9VKksYl/NJIIfl6b9cmABO6z80mTz3+0klquIpSQLidK2aFaFbqiGnMdCO+AZfwxu2qBx+1f5MwbHXUW5HXsfmEvzBUC9xCQKLpQ8oZYBrSg=="),
algorithm: {
name: "ECDH",
namedCurve: "P-521",
} as EcKeyImportParams,
extractable: true,
keyUsages: ["deriveKey"],
},
publicKey: {
format: "spki" as KeyFormat,
data: Convert.FromBase64("MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQA4CG5QW7YfL5zmbfxy1PVXVCR5np8EUQA1WVi/j+SHgg0LOgfOwDyONUd/vdcRQx547LfVSpLGJfzSSCH5em/XJgATus/NJk89/tJJariKUkC4nStmhWhW6ohpzHQjvgGX8MbtqgcftX+TMGx11FuR17H5hL8wVAvcQkCi6UPKGWAa0o="),
algorithm: {
name: "ECDH",
namedCurve: "P-521",
} as EcKeyImportParams,
extractable: true,
keyUsages: [],
},
},
algorithm: {
name: "ECDH",
},
derivedKeyType: {
name: "AES-CTR",
length: 256,
} as AesKeyAlgorithm,
keyUsages: ["encrypt", "decrypt"],
format: "raw",
keyData: Convert.FromBase64("AS2ene28pmWYdJwW6dyTXUe1eq1p2i8QEIo/rXSiJRo="),
},
],
},
},
]);
});

356
test/helper.ts Normal file
View File

@ -0,0 +1,356 @@
import assert from "assert";
import { Convert } from "pvtsutils";
import { Crypto, CryptoKey } from "webcrypto-core";
/**
* Returns true if blobs from keys are equal
* @param a Crypto key
* @param b Crypto key
*/
export function isKeyEqual(a: CryptoKey, b: CryptoKey) {
if (a instanceof CryptoKey && b instanceof CryptoKey) {
return (a as any).data.equals((b as any).data);
}
return false;
}
export interface ITestAction {
name?: string;
only?: boolean;
skip?: boolean;
error?: any;
}
export interface ITestGenerateKeyAction extends ITestAction {
algorithm: Algorithm;
extractable: boolean;
keyUsages: KeyUsage[];
}
export interface IImportKeyParams {
format: KeyFormat;
data: JsonWebKey | BufferSource;
algorithm: AlgorithmIdentifier;
extractable: boolean;
keyUsages: KeyUsage[];
}
export interface IImportKeyPairParams {
privateKey: IImportKeyParams;
publicKey: IImportKeyParams;
}
export interface ITestEncryptAction extends ITestAction {
algorithm: Algorithm;
data: BufferSource;
encData: BufferSource;
key: IImportKeyParams | IImportKeyPairParams;
}
export interface ITestSignAction extends ITestAction {
algorithm: Algorithm;
data: BufferSource;
signature: BufferSource;
key: IImportKeyParams | IImportKeyPairParams;
}
export interface ITestDeriveBitsAction extends ITestAction {
algorithm: Algorithm;
key: IImportKeyParams | IImportKeyPairParams;
data: BufferSource;
length: number;
}
export interface ITestDeriveKeyAction extends ITestAction {
algorithm: Algorithm;
key: IImportKeyParams | IImportKeyPairParams;
derivedKeyType: Algorithm;
keyUsages: KeyUsage[];
format: KeyFormat;
keyData: BufferSource | JsonWebKey;
}
export interface ITestWrapKeyAction extends ITestAction {
key: IImportKeyParams | IImportKeyPairParams;
algorithm: Algorithm;
wKey: IImportKeyParams;
}
export interface ITestImportAction extends IImportKeyParams, ITestAction {
}
export interface ITestDigestAction extends ITestAction {
algorithm: AlgorithmIdentifier;
data: BufferSource;
hash: BufferSource;
}
export interface ITestActions {
generateKey?: ITestGenerateKeyAction[];
encrypt?: ITestEncryptAction[];
wrapKey?: ITestWrapKeyAction[];
sign?: ITestSignAction[];
import?: ITestImportAction[];
deriveBits?: ITestDeriveBitsAction[];
deriveKey?: ITestDeriveKeyAction[];
digest?: ITestDigestAction[];
}
export interface ITestParams {
name: string;
only?: boolean;
actions: ITestActions;
}
async function getKeys(crypto: Crypto, key: IImportKeyParams | IImportKeyPairParams) {
const keys = {} as CryptoKeyPair;
if ("privateKey" in key) {
keys.privateKey = await crypto.subtle.importKey(
key.privateKey.format,
key.privateKey.data,
key.privateKey.algorithm,
key.privateKey.extractable,
key.privateKey.keyUsages);
keys.publicKey = await crypto.subtle.importKey(
key.publicKey.format,
key.publicKey.data,
key.publicKey.algorithm,
key.publicKey.extractable,
key.publicKey.keyUsages);
} else {
keys.privateKey = keys.publicKey = await crypto.subtle.importKey(
key.format,
key.data,
key.algorithm,
key.extractable,
key.keyUsages);
}
return keys;
}
async function wrapTest(promise: () => Promise<void>, action: ITestAction, index: number) {
const test = action.skip
? it.skip
: action.only
? it.only
: it;
test(action.name || `#${index + 1}`, async () => {
if (action.error) {
await assert.rejects(promise(), action.error);
} else {
await promise();
}
});
}
export function testCrypto(crypto: Crypto, params: ITestParams[]) {
params.forEach((param) => {
context(param.name, () => {
//#region Generate key
if (param.actions.generateKey) {
context("Generate Key", () => {
param.actions.generateKey!.forEach((action, index) => {
wrapTest(async () => {
const algorithm = Object.assign({}, action.algorithm);
algorithm.name = algorithm.name.toLowerCase();
const key = await crypto.subtle.generateKey(
algorithm,
action.extractable,
action.keyUsages,
);
assert(key);
if (key instanceof CryptoKey) {
assert.equal(key.algorithm.name, action.algorithm.name, "Algorithm name MUST be equal to incoming algorithm and in the same case");
assert.equal(key.extractable, action.extractable);
assert.deepEqual(key.usages, action.keyUsages);
} else {
assert(key.privateKey);
assert.equal(key.privateKey.algorithm.name, action.algorithm.name, "Algorithm name MUST be equal to incoming algorithm and in the same case");
assert.equal(key.privateKey.extractable, action.extractable);
assert(key.publicKey);
assert.equal(key.publicKey.algorithm.name, action.algorithm.name, "Algorithm name MUST be equal to incoming algorithm and in the same case");
assert.equal(key.publicKey.extractable, true);
}
}, action, index);
});
});
}
//#endregion
//#region encrypt
if (param.actions.encrypt) {
context("Encrypt/Decrypt", () => {
param.actions.encrypt!.forEach((action, index) => {
wrapTest(async () => {
// import keys
const keys = await getKeys(crypto, action.key);
const encKey = keys.publicKey;
const decKey = keys.privateKey;
const algorithm = Object.assign({}, action.algorithm);
algorithm.name = algorithm.name.toLowerCase();
// encrypt
const enc = await crypto.subtle.encrypt(algorithm, encKey, action.data);
// decrypt
let dec = await crypto.subtle.decrypt(algorithm, decKey, enc);
assert.equal(Convert.ToHex(dec), Convert.ToHex(action.data));
dec = await crypto.subtle.decrypt(algorithm, decKey, action.encData);
assert.equal(Convert.ToHex(dec), Convert.ToHex(action.data));
}, action, index);
});
});
}
//#endregion
//#region Import/Export
if (param.actions.import) {
context("Import/Export", () => {
param.actions.import!.forEach((action, index) => {
wrapTest(async () => {
const importedKey = await crypto.subtle.importKey(
action.format,
action.data,
action.algorithm,
action.extractable,
action.keyUsages);
const exportedData = await crypto.subtle.exportKey(
action.format,
importedKey);
if (action.format === "jwk") {
assert.deepEqual(exportedData, action.data);
} else {
assert.equal(Buffer.from(exportedData as ArrayBuffer).toString("hex"), Buffer.from(action.data as ArrayBuffer).toString("hex"));
}
}, action, index);
});
});
}
//#endregion
//#region Sign/Verify
if (param.actions.sign) {
context("Sign/Verify", () => {
param.actions.sign!.forEach((action, index) => {
wrapTest(async () => {
// import keys
const keys = await getKeys(crypto, action.key);
const verifyKey = keys.publicKey;
const signKey = keys.privateKey;
const algorithm = Object.assign({}, action.algorithm);
algorithm.name = algorithm.name.toLowerCase();
// sign
const signature = await crypto.subtle.sign(algorithm, signKey, action.data);
// verify
let ok = await crypto.subtle.verify(algorithm, verifyKey, signature, action.data);
assert.equal(true, ok, "Cannot verify signature from Action data");
ok = await crypto.subtle.verify(algorithm, verifyKey, action.signature, action.data);
if (!ok) {
assert.equal(Convert.ToHex(signature), Convert.ToHex(action.signature));
}
assert.equal(true, ok);
}, action, index);
});
});
}
//#endregion
//#region Derive bits
if (param.actions.deriveBits) {
context("Derive bits", () => {
param.actions.deriveBits!.forEach((action, index) => {
wrapTest(async () => {
// import keys
const keys = await getKeys(crypto, action.key);
const algorithm = Object.assign({}, action.algorithm, { public: keys.publicKey }) as any;
algorithm.name = algorithm.name.toLowerCase();
// derive bits
const derivedBits = await crypto.subtle.deriveBits(algorithm, keys.privateKey, action.length);
assert.equal(Convert.ToHex(derivedBits), Convert.ToHex(action.data));
}, action, index);
});
});
}
//#endregion
//#region Derive key
if (param.actions.deriveKey) {
context("Derive key", () => {
param.actions.deriveKey!.forEach((action, index) => {
wrapTest(async () => {
// import keys
const keys = await getKeys(crypto, action.key);
const algorithm = Object.assign({}, action.algorithm, { public: keys.publicKey }) as any;
algorithm.name = algorithm.name.toLowerCase();
// derive key
const derivedKey = await crypto.subtle.deriveKey(algorithm, keys.privateKey, action.derivedKeyType, true, action.keyUsages);
const keyData = await crypto.subtle.exportKey(action.format, derivedKey);
if (action.format === "jwk") {
assert.deepEqual(keyData, action.keyData);
} else {
assert.equal(Convert.ToHex(keyData as ArrayBuffer), Convert.ToHex(action.keyData as ArrayBuffer));
}
}, action, index);
});
});
}
//#endregion
//#region Digest
if (param.actions.digest) {
context("Digest", () => {
param.actions.digest!.forEach((action, index) => {
wrapTest(async () => {
const hash = await crypto.subtle.digest(action.algorithm, action.data);
assert.equal(Convert.ToHex(hash), Convert.ToHex(action.hash));
}, action, index);
});
});
}
//#endregion
//#region Wrap/Unwrap key
if (param.actions.wrapKey) {
context("Wrap/Unwrap key", () => {
param.actions.wrapKey!.forEach((action, index) => {
wrapTest(async () => {
const wKey = (await getKeys(crypto, action.wKey)).privateKey;
const key = await getKeys(crypto, action.key);
const wrappedKey = await crypto.subtle.wrapKey(action.wKey.format, wKey, key.publicKey, action.algorithm);
const unwrappedKey = await crypto.subtle.unwrapKey(
action.wKey.format,
wrappedKey,
key.privateKey,
action.algorithm,
action.wKey.algorithm,
action.wKey.extractable,
action.wKey.keyUsages);
assert.deepEqual(unwrappedKey.algorithm, wKey.algorithm);
}, action, index);
});
});
}
//#endregion
});
});
}

139
test/hmac.ts Normal file
View File

@ -0,0 +1,139 @@
import { Convert } from "pvtsutils";
import { Crypto } from "../src";
import { ITestGenerateKeyAction, testCrypto } from "./helper";
context("HMAC", () => {
const crypto = new Crypto();
testCrypto(crypto, [
{
name: "HMAC",
actions: {
generateKey: [
{
name: "default length",
algorithm: {
name: "HMAC",
hash: "SHA-256",
} as HmacKeyGenParams,
extractable: true,
keyUsages: ["sign", "verify"],
},
...["SHA-1", "SHA-256", "SHA-384", "SHA-512"].map((hash) => {
return {
name: hash,
algorithm: {
name: "HMAC",
hash,
length: 128,
},
extractable: true,
keyUsages: ["sign", "verify"],
} as ITestGenerateKeyAction;
}),
],
sign: [
{
key: {
format: "raw",
data: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6]),
algorithm: {
name: "HMAC",
hash: "SHA-256",
length: 128,
} as HmacImportParams,
extractable: false,
keyUsages: ["sign", "verify"],
},
algorithm: { name: "HMAC" },
data: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]),
signature: Convert.FromBase64("9yMF9ReX1EhdBWTRjSR+AC21NA05H9W8vx0HZGVmgNc="),
},
],
import: [
{
name: "JWK SHA-1",
format: "jwk",
data: {
alg: "HS1",
ext: true,
k: "AQIDBAUGBwgJAAECAwQFBg",
key_ops: ["sign", "verify"],
kty: "oct",
},
algorithm: {
name: "HMAC",
hash: "SHA-1",
length: 128,
} as HmacImportParams,
extractable: true,
keyUsages: ["sign", "verify"],
},
{
name: "JWK SHA-256",
format: "jwk",
data: {
alg: "HS256",
ext: true,
k: "AQIDBAUGBwgJAAECAwQFBg",
key_ops: ["sign", "verify"],
kty: "oct",
},
algorithm: {
name: "HMAC",
hash: "SHA-256",
} as HmacImportParams,
extractable: true,
keyUsages: ["sign", "verify"],
},
{
name: "JWK SHA-384",
format: "jwk",
data: {
alg: "HS384",
ext: true,
k: "AQIDBAUGBwgJAAECAwQFBg",
key_ops: ["sign", "verify"],
kty: "oct",
},
algorithm: {
name: "HMAC",
hash: "SHA-384",
} as HmacImportParams,
extractable: true,
keyUsages: ["sign", "verify"],
},
{
name: "JWK SHA-512",
format: "jwk",
data: {
alg: "HS512",
ext: true,
k: "AQIDBAUGBwgJAAECAwQFBg",
key_ops: ["sign", "verify"],
kty: "oct",
},
algorithm: {
name: "HMAC",
hash: "SHA-512",
} as HmacImportParams,
extractable: true,
keyUsages: ["sign", "verify"],
},
{
name: "raw",
format: "raw",
data: Convert.FromBase64("AQIDBAUGBwgJAAECAwQFBg"),
algorithm: {
name: "HMAC",
hash: "SHA-512",
} as HmacImportParams,
extractable: true,
keyUsages: ["sign", "verify"],
},
],
},
},
]);
});

2
test/mocha.opts Normal file
View File

@ -0,0 +1,2 @@
--require ts-node/register
test/**/*.ts

63
test/pbkdf.ts Normal file
View File

@ -0,0 +1,63 @@
import { Convert } from "pvtsutils";
import { Crypto } from "../src";
import { testCrypto } from "./helper";
context("PBKDF", () => {
const crypto = new Crypto();
testCrypto(crypto, [
{
name: "PBKDF2",
actions: {
deriveBits: [
{
key: {
format: "raw",
data: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]),
algorithm: {
name: "PBKDF2",
},
extractable: false,
keyUsages: ["deriveBits"],
},
algorithm: {
name: "PBKDF2",
salt: new Uint8Array([1, 2, 3, 4]),
hash: "SHA-256",
iterations: 1000,
} as Pbkdf2Params,
data: Convert.FromBase64("3GK58/4RT+UPLooz5HT1MQ=="),
length: 128,
},
],
deriveKey: [
{
key: {
format: "raw",
data: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]),
algorithm: {
name: "PBKDF2",
},
extractable: false,
keyUsages: ["deriveKey"],
},
algorithm: {
name: "PBKDF2",
salt: new Uint8Array([1, 2, 3, 4]),
hash: "SHA-256",
iterations: 1000,
} as Pbkdf2Params,
derivedKeyType: {
name: "AES-CBC",
length: 128,
} as AesDerivedKeyParams,
keyUsages: ["encrypt"],
format: "raw",
keyData: Convert.FromBase64("3GK58/4RT+UPLooz5HT1MQ=="),
},
],
},
},
]);
});

308
test/rsa.ts Normal file
View File

@ -0,0 +1,308 @@
import { Convert } from "pvtsutils";
import { Crypto } from "../src";
import { ITestGenerateKeyAction, testCrypto } from "./helper";
context("RSA", () => {
const crypto = new Crypto();
testCrypto(crypto, [
// RSASSA-PKCS1-v1_5
{
name: "RSASSA-PKCS1-v1_5",
actions: {
generateKey: ["SHA-1", "SHA-256", "SHA-384", "SHA-512"].map((hash) => {
return {
name: hash,
algorithm: {
name: "RSASSA-PKCS1-v1_5",
hash,
publicExponent: new Uint8Array([1, 0, 1]),
modulusLength: 1024,
} as RsaHashedKeyGenParams,
extractable: false,
keyUsages: ["sign", "verify"],
} as ITestGenerateKeyAction;
}),
sign: [
{
algorithm: {
name: "RSASSA-PKCS1-v1_5",
},
data: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]),
signature: Convert.FromBase64("f8OvbYnwX5YPVPjWkOTalYTFJjS1Ks7iNmPdLEby/kK6BEGk5uPvY/ebcok6sTQpQXJXJFJbOcMrZftmJXpm1szcgOdNgVW6FDc3722a9Mzvk/YfvNUCQRNEMON9lYKdpOLSXAFpXR5ovZytbFQ2w2ztpKkJvNY2QZQlizcZKSg="),
key: {
publicKey: {
format: "jwk",
algorithm: { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
data: {
alg: "RS256",
e: "AQAB",
ext: true,
key_ops: ["verify"],
kty: "RSA",
n: "vqpvdxuyZ6rKYnWTj_ZzDBFZAAAlpe5hpoiYHqa2j5kK7v8U5EaPY2bLib9m4B40j-n3FV9xUCGiplWdqMJJKT-4PjGO5E3S4N9kjFhu57noYT7z7302J0sJXeoFbXxlgE-4G55Oxlm52ID2_RJesP5nzcGTriQwoRbrJP5OEt0",
},
extractable: true,
keyUsages: ["verify"],
},
privateKey: {
format: "jwk",
algorithm: { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
data: {
alg: "RS256",
d: "AkeIWJywp9OfYsj0ECsKmhDVBw55ZL_yU-rbIrashQ_31P6gsc_0I-SVN1rd8Hz79OJ_rTY8ZRBZ4PIyFdPoyvuo5apHdAHH6riJKxDHWPxhE-ReNVEPSTiF1ry8DSe5zC7w9BLnH_QM8bkN4cOnvgqrg7EbrGWomAGJVvoRwOM",
dp: "pOolqL7HwnmWLn7GDX8zGkm0Q1IAj-ouBL7ZZbaTm3wETLtwu-dGsQheEdzP_mfL_CTiCAwGuQBcSItimD0DdQ",
dq: "FTSY59AnkgmB7TsErWNBE3xlVB_pMpE2xWyCBCz96gyDOUOFDz8vlSV-clhjawJeRd1n30nZOPSBtOHozhwZmQ",
e: "AQAB",
ext: true,
key_ops: ["sign"],
kty: "RSA",
n: "vqpvdxuyZ6rKYnWTj_ZzDBFZAAAlpe5hpoiYHqa2j5kK7v8U5EaPY2bLib9m4B40j-n3FV9xUCGiplWdqMJJKT-4PjGO5E3S4N9kjFhu57noYT7z7302J0sJXeoFbXxlgE-4G55Oxlm52ID2_RJesP5nzcGTriQwoRbrJP5OEt0",
p: "6jFtmBJJQFIlQUXXZYIgvH70Y9a03oWKjNuF2veb5Zf09EtLNE86NpnIm463OnoHJPW0m8wHFXZZfcYVTIPR_w",
q: "0GttDMl1kIzSV2rNzGXpOS8tUqr5Lz0EtVZwIb9GJPMmJ0P3gZ801zEgZZ4-esU7cLUf-BSZEAmfnKA80G2jIw",
qi: "FByTxX4G2eXkk1xe0IuiEv7I5NS-CnFyp8iB4XLG0rabnfcIZFKpf__X0sNyVOAVo5-jJMuUYjCRTdaXNAWhkg",
},
extractable: true,
keyUsages: ["sign"],
},
},
},
],
import: [
{
name: "public key JWK",
format: "jwk" as KeyFormat,
algorithm: { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" } as Algorithm,
data: {
alg: "RS256",
e: "AQAB",
ext: true,
key_ops: ["verify"],
kty: "RSA",
n: "vqpvdxuyZ6rKYnWTj_ZzDBFZAAAlpe5hpoiYHqa2j5kK7v8U5EaPY2bLib9m4B40j-n3FV9xUCGiplWdqMJJKT-4PjGO5E3S4N9kjFhu57noYT7z7302J0sJXeoFbXxlgE-4G55Oxlm52ID2_RJesP5nzcGTriQwoRbrJP5OEt0",
},
extractable: true,
keyUsages: ["verify"],
},
{
name: "public key SPKI",
format: "spki" as KeyFormat,
algorithm: { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" } as Algorithm,
data: Convert.FromBase64("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+qm93G7JnqspidZOP9nMMEVkAACWl7mGmiJgepraPmQru/xTkRo9jZsuJv2bgHjSP6fcVX3FQIaKmVZ2owkkpP7g+MY7kTdLg32SMWG7nuehhPvPvfTYnSwld6gVtfGWAT7gbnk7GWbnYgPb9El6w/mfNwZOuJDChFusk/k4S3QIDAQAB"),
extractable: true,
keyUsages: ["verify"],
},
{
name: "private key JWK",
format: "jwk" as KeyFormat,
algorithm: { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" } as Algorithm,
data: {
alg: "RS256",
d: "AkeIWJywp9OfYsj0ECsKmhDVBw55ZL_yU-rbIrashQ_31P6gsc_0I-SVN1rd8Hz79OJ_rTY8ZRBZ4PIyFdPoyvuo5apHdAHH6riJKxDHWPxhE-ReNVEPSTiF1ry8DSe5zC7w9BLnH_QM8bkN4cOnvgqrg7EbrGWomAGJVvoRwOM",
dp: "pOolqL7HwnmWLn7GDX8zGkm0Q1IAj-ouBL7ZZbaTm3wETLtwu-dGsQheEdzP_mfL_CTiCAwGuQBcSItimD0DdQ",
dq: "FTSY59AnkgmB7TsErWNBE3xlVB_pMpE2xWyCBCz96gyDOUOFDz8vlSV-clhjawJeRd1n30nZOPSBtOHozhwZmQ",
e: "AQAB",
ext: true,
key_ops: ["sign"],
kty: "RSA",
n: "vqpvdxuyZ6rKYnWTj_ZzDBFZAAAlpe5hpoiYHqa2j5kK7v8U5EaPY2bLib9m4B40j-n3FV9xUCGiplWdqMJJKT-4PjGO5E3S4N9kjFhu57noYT7z7302J0sJXeoFbXxlgE-4G55Oxlm52ID2_RJesP5nzcGTriQwoRbrJP5OEt0",
p: "6jFtmBJJQFIlQUXXZYIgvH70Y9a03oWKjNuF2veb5Zf09EtLNE86NpnIm463OnoHJPW0m8wHFXZZfcYVTIPR_w",
q: "0GttDMl1kIzSV2rNzGXpOS8tUqr5Lz0EtVZwIb9GJPMmJ0P3gZ801zEgZZ4-esU7cLUf-BSZEAmfnKA80G2jIw",
qi: "FByTxX4G2eXkk1xe0IuiEv7I5NS-CnFyp8iB4XLG0rabnfcIZFKpf__X0sNyVOAVo5-jJMuUYjCRTdaXNAWhkg",
},
extractable: true,
keyUsages: ["sign"],
},
{
name: "private key pkcs8",
format: "pkcs8" as KeyFormat,
algorithm: { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" } as Algorithm,
data: Convert.FromBase64("MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAL6qb3cbsmeqymJ1k4/2cwwRWQAAJaXuYaaImB6mto+ZCu7/FORGj2Nmy4m/ZuAeNI/p9xVfcVAhoqZVnajCSSk/uD4xjuRN0uDfZIxYbue56GE+8+99NidLCV3qBW18ZYBPuBueTsZZudiA9v0SXrD+Z83Bk64kMKEW6yT+ThLdAgMBAAECgYACR4hYnLCn059iyPQQKwqaENUHDnlkv/JT6tsitqyFD/fU/qCxz/Qj5JU3Wt3wfPv04n+tNjxlEFng8jIV0+jK+6jlqkd0AcfquIkrEMdY/GET5F41UQ9JOIXWvLwNJ7nMLvD0Eucf9AzxuQ3hw6e+CquDsRusZaiYAYlW+hHA4wJBAOoxbZgSSUBSJUFF12WCILx+9GPWtN6Fiozbhdr3m+WX9PRLSzRPOjaZyJuOtzp6ByT1tJvMBxV2WX3GFUyD0f8CQQDQa20MyXWQjNJXas3MZek5Ly1SqvkvPQS1VnAhv0Yk8yYnQ/eBnzTXMSBlnj56xTtwtR/4FJkQCZ+coDzQbaMjAkEApOolqL7HwnmWLn7GDX8zGkm0Q1IAj+ouBL7ZZbaTm3wETLtwu+dGsQheEdzP/mfL/CTiCAwGuQBcSItimD0DdQJAFTSY59AnkgmB7TsErWNBE3xlVB/pMpE2xWyCBCz96gyDOUOFDz8vlSV+clhjawJeRd1n30nZOPSBtOHozhwZmQJAFByTxX4G2eXkk1xe0IuiEv7I5NS+CnFyp8iB4XLG0rabnfcIZFKpf//X0sNyVOAVo5+jJMuUYjCRTdaXNAWhkg=="),
extractable: true,
keyUsages: ["sign"],
},
],
},
},
// RSA-PSS
{
name: "RSA-PSS",
actions: {
generateKey: ["SHA-1", "SHA-256", "SHA-384", "SHA-512"].map((hash) => {
return {
name: hash,
algorithm: {
name: "RSA-PSS",
hash,
publicExponent: new Uint8Array([1, 0, 1]),
modulusLength: 1024,
} as RsaHashedKeyGenParams,
extractable: false,
keyUsages: ["sign", "verify"],
} as ITestGenerateKeyAction;
}),
sign: [
{
algorithm: {
name: "RSA-PSS",
saltLength: 64,
} as RsaPssParams,
data: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]),
signature: Convert.FromBase64("OYz/7fv71ELOs5kuz5IiYq1NsXuOazl22xqIFjiY++hYFzJMWaR+ZI0WPoMOifvb1PNKmdQ4dY+QbpYC1vdzlAKfkLe22l5htLyQaXzjD/yeMZYrL0KmrabC9ayL6bxrMW+ccePStkbrF1Jn0LT09l22aX/r1y3SPrl0b+zwo/Q="),
key: {
publicKey: {
format: "jwk",
algorithm: { name: "RSA-PSS", hash: "SHA-256" },
data: {
alg: "PS256",
e: "AQAB",
ext: true,
key_ops: ["verify"],
kty: "RSA",
n: "vqpvdxuyZ6rKYnWTj_ZzDBFZAAAlpe5hpoiYHqa2j5kK7v8U5EaPY2bLib9m4B40j-n3FV9xUCGiplWdqMJJKT-4PjGO5E3S4N9kjFhu57noYT7z7302J0sJXeoFbXxlgE-4G55Oxlm52ID2_RJesP5nzcGTriQwoRbrJP5OEt0",
},
extractable: true,
keyUsages: ["verify"],
},
privateKey: {
format: "jwk",
algorithm: { name: "RSA-PSS", hash: "SHA-256" },
data: {
alg: "PS256",
d: "AkeIWJywp9OfYsj0ECsKmhDVBw55ZL_yU-rbIrashQ_31P6gsc_0I-SVN1rd8Hz79OJ_rTY8ZRBZ4PIyFdPoyvuo5apHdAHH6riJKxDHWPxhE-ReNVEPSTiF1ry8DSe5zC7w9BLnH_QM8bkN4cOnvgqrg7EbrGWomAGJVvoRwOM",
dp: "pOolqL7HwnmWLn7GDX8zGkm0Q1IAj-ouBL7ZZbaTm3wETLtwu-dGsQheEdzP_mfL_CTiCAwGuQBcSItimD0DdQ",
dq: "FTSY59AnkgmB7TsErWNBE3xlVB_pMpE2xWyCBCz96gyDOUOFDz8vlSV-clhjawJeRd1n30nZOPSBtOHozhwZmQ",
e: "AQAB",
ext: true,
key_ops: ["sign"],
kty: "RSA",
n: "vqpvdxuyZ6rKYnWTj_ZzDBFZAAAlpe5hpoiYHqa2j5kK7v8U5EaPY2bLib9m4B40j-n3FV9xUCGiplWdqMJJKT-4PjGO5E3S4N9kjFhu57noYT7z7302J0sJXeoFbXxlgE-4G55Oxlm52ID2_RJesP5nzcGTriQwoRbrJP5OEt0",
p: "6jFtmBJJQFIlQUXXZYIgvH70Y9a03oWKjNuF2veb5Zf09EtLNE86NpnIm463OnoHJPW0m8wHFXZZfcYVTIPR_w",
q: "0GttDMl1kIzSV2rNzGXpOS8tUqr5Lz0EtVZwIb9GJPMmJ0P3gZ801zEgZZ4-esU7cLUf-BSZEAmfnKA80G2jIw",
qi: "FByTxX4G2eXkk1xe0IuiEv7I5NS-CnFyp8iB4XLG0rabnfcIZFKpf__X0sNyVOAVo5-jJMuUYjCRTdaXNAWhkg",
},
extractable: true,
keyUsages: ["sign"],
},
},
},
],
},
},
// RSA-OAEP
{
name: "RSA-OAEP",
actions: {
generateKey: ["SHA-1", "SHA-256", "SHA-384", "SHA-512"].map((hash) => {
return {
name: hash,
skip: true,
algorithm: {
name: "RSA-OAEP",
hash,
publicExponent: new Uint8Array([1, 0, 1]),
modulusLength: 1024,
} as RsaHashedKeyGenParams,
extractable: false,
keyUsages: ["encrypt", "decrypt"],
} as ITestGenerateKeyAction;
}),
encrypt: [
{
name: "with label",
skip: true,
algorithm: {
name: "RSA-OAEP",
label: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]),
} as RsaOaepParams,
data: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]),
encData: Convert.FromBase64("aHu8PBZuctYecfINKgUdB8gBoLyUUFxTZDTzTHUk9KKxtYywYml48HoijBG5DyaIWUUbOIdPgap9C8pFG2iYShQnE9Aj3gzKLHacBbFw1P79+Ei/Tm0j/THiXqCplBZC4dIp4jhTDepmdrlXZcY0slmjG+h8h8TpSmWKP3pEGGk="),
key: {
publicKey: {
format: "jwk",
algorithm: { name: "RSA-OAEP", hash: "SHA-256" },
data: {
alg: "RSA-OAEP-256",
e: "AQAB",
ext: true,
key_ops: ["encrypt"],
kty: "RSA",
n: "vqpvdxuyZ6rKYnWTj_ZzDBFZAAAlpe5hpoiYHqa2j5kK7v8U5EaPY2bLib9m4B40j-n3FV9xUCGiplWdqMJJKT-4PjGO5E3S4N9kjFhu57noYT7z7302J0sJXeoFbXxlgE-4G55Oxlm52ID2_RJesP5nzcGTriQwoRbrJP5OEt0",
},
extractable: true,
keyUsages: ["encrypt"],
},
privateKey: {
format: "jwk",
algorithm: { name: "RSA-OAEP", hash: "SHA-256" },
data: {
alg: "RSA-OAEP-256",
d: "AkeIWJywp9OfYsj0ECsKmhDVBw55ZL_yU-rbIrashQ_31P6gsc_0I-SVN1rd8Hz79OJ_rTY8ZRBZ4PIyFdPoyvuo5apHdAHH6riJKxDHWPxhE-ReNVEPSTiF1ry8DSe5zC7w9BLnH_QM8bkN4cOnvgqrg7EbrGWomAGJVvoRwOM",
dp: "pOolqL7HwnmWLn7GDX8zGkm0Q1IAj-ouBL7ZZbaTm3wETLtwu-dGsQheEdzP_mfL_CTiCAwGuQBcSItimD0DdQ",
dq: "FTSY59AnkgmB7TsErWNBE3xlVB_pMpE2xWyCBCz96gyDOUOFDz8vlSV-clhjawJeRd1n30nZOPSBtOHozhwZmQ",
e: "AQAB",
ext: true,
key_ops: ["decrypt"],
kty: "RSA",
n: "vqpvdxuyZ6rKYnWTj_ZzDBFZAAAlpe5hpoiYHqa2j5kK7v8U5EaPY2bLib9m4B40j-n3FV9xUCGiplWdqMJJKT-4PjGO5E3S4N9kjFhu57noYT7z7302J0sJXeoFbXxlgE-4G55Oxlm52ID2_RJesP5nzcGTriQwoRbrJP5OEt0",
p: "6jFtmBJJQFIlQUXXZYIgvH70Y9a03oWKjNuF2veb5Zf09EtLNE86NpnIm463OnoHJPW0m8wHFXZZfcYVTIPR_w",
q: "0GttDMl1kIzSV2rNzGXpOS8tUqr5Lz0EtVZwIb9GJPMmJ0P3gZ801zEgZZ4-esU7cLUf-BSZEAmfnKA80G2jIw",
qi: "FByTxX4G2eXkk1xe0IuiEv7I5NS-CnFyp8iB4XLG0rabnfcIZFKpf__X0sNyVOAVo5-jJMuUYjCRTdaXNAWhkg",
},
extractable: true,
keyUsages: ["decrypt"],
},
},
},
{
name: "without label",
skip: true,
algorithm: {
name: "RSA-OAEP",
} as RsaOaepParams,
data: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]),
encData: Convert.FromBase64("aHu8PBZuctYecfINKgUdB8gBoLyUUFxTZDTzTHUk9KKxtYywYml48HoijBG5DyaIWUUbOIdPgap9C8pFG2iYShQnE9Aj3gzKLHacBbFw1P79+Ei/Tm0j/THiXqCplBZC4dIp4jhTDepmdrlXZcY0slmjG+h8h8TpSmWKP3pEGGk="),
key: {
publicKey: {
format: "jwk",
algorithm: { name: "RSA-OAEP", hash: "SHA-256" },
data: {
alg: "RSA-OAEP-256",
e: "AQAB",
ext: true,
key_ops: ["encrypt"],
kty: "RSA",
n: "vqpvdxuyZ6rKYnWTj_ZzDBFZAAAlpe5hpoiYHqa2j5kK7v8U5EaPY2bLib9m4B40j-n3FV9xUCGiplWdqMJJKT-4PjGO5E3S4N9kjFhu57noYT7z7302J0sJXeoFbXxlgE-4G55Oxlm52ID2_RJesP5nzcGTriQwoRbrJP5OEt0",
},
extractable: true,
keyUsages: ["encrypt"],
},
privateKey: {
format: "jwk",
algorithm: { name: "RSA-OAEP", hash: "SHA-256" },
data: {
alg: "RSA-OAEP-256",
d: "AkeIWJywp9OfYsj0ECsKmhDVBw55ZL_yU-rbIrashQ_31P6gsc_0I-SVN1rd8Hz79OJ_rTY8ZRBZ4PIyFdPoyvuo5apHdAHH6riJKxDHWPxhE-ReNVEPSTiF1ry8DSe5zC7w9BLnH_QM8bkN4cOnvgqrg7EbrGWomAGJVvoRwOM",
dp: "pOolqL7HwnmWLn7GDX8zGkm0Q1IAj-ouBL7ZZbaTm3wETLtwu-dGsQheEdzP_mfL_CTiCAwGuQBcSItimD0DdQ",
dq: "FTSY59AnkgmB7TsErWNBE3xlVB_pMpE2xWyCBCz96gyDOUOFDz8vlSV-clhjawJeRd1n30nZOPSBtOHozhwZmQ",
e: "AQAB",
ext: true,
key_ops: ["decrypt"],
kty: "RSA",
n: "vqpvdxuyZ6rKYnWTj_ZzDBFZAAAlpe5hpoiYHqa2j5kK7v8U5EaPY2bLib9m4B40j-n3FV9xUCGiplWdqMJJKT-4PjGO5E3S4N9kjFhu57noYT7z7302J0sJXeoFbXxlgE-4G55Oxlm52ID2_RJesP5nzcGTriQwoRbrJP5OEt0",
p: "6jFtmBJJQFIlQUXXZYIgvH70Y9a03oWKjNuF2veb5Zf09EtLNE86NpnIm463OnoHJPW0m8wHFXZZfcYVTIPR_w",
q: "0GttDMl1kIzSV2rNzGXpOS8tUqr5Lz0EtVZwIb9GJPMmJ0P3gZ801zEgZZ4-esU7cLUf-BSZEAmfnKA80G2jIw",
qi: "FByTxX4G2eXkk1xe0IuiEv7I5NS-CnFyp8iB4XLG0rabnfcIZFKpf__X0sNyVOAVo5-jJMuUYjCRTdaXNAWhkg",
},
extractable: true,
keyUsages: ["decrypt"],
},
},
},
],
},
},
]);
});

44
test/sha.ts Normal file
View File

@ -0,0 +1,44 @@
import { Convert } from "pvtsutils";
import { Crypto } from "../src";
import { ITestGenerateKeyAction, testCrypto } from "./helper";
context("SHA", () => {
const crypto = new Crypto();
const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]);
testCrypto(crypto, [
{
name: "SHA",
actions: {
digest: [
{
name: "SHA-1",
algorithm: "SHA-1",
data,
hash: Convert.FromBase64("6JrVqWMcPv3e1+Psznm00P7c4b8="),
},
{
name: "SHA-256",
algorithm: "SHA-256",
data,
hash: Convert.FromBase64("monGjExeKLjEpVZ2c9Ri//UV20YRb5kAYk0JxHT1k/s="),
},
{
name: "SHA-384",
algorithm: "SHA-384",
data,
hash: Convert.FromBase64("E9WqubQC9JnxffIniWwf0soI91o5z0Kbvk+s/32Fi3z28kAh+Fcne7Hgy1nnW4rR"),
},
{
name: "SHA-512",
algorithm: "SHA-512",
data,
hash: Convert.FromBase64("OtPzaXlFDU9TNmJE7PEBD0+RIdaIgoX/FBBP1a3thdSKoXG/HjOhEmAvkrenCIsph4kBL7h7kFYyEkGhn7dOCw=="),
},
],
},
},
]);
});

59
tsconfig.json Normal file
View File

@ -0,0 +1,59 @@
{
"compilerOptions": {
/* Basic Options */
"target": "es6", /* 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. */
}
}

13
tslint.json Normal file
View File

@ -0,0 +1,13 @@
{
"defaultSeverity": "error",
"extends": [
"tslint:recommended"
],
"jsRules": {},
"rules": {
"max-line-length": false,
"no-bitwise": false,
"object-literal-sort-keys": false
},
"rulesDirectory": []
}