Merge pull request #25 from PeculiarVentures/eddsa
Support EdDSA and ECDH-ES mechanisms
This commit is contained in:
commit
99273f0e99
|
@ -49,6 +49,8 @@ npm install @peculiar/webcrypto
|
||||||
| AES-KW | X | | X | | | X | |
|
| AES-KW | X | | X | | | X | |
|
||||||
| ECDSA<sup>1</sup> | X | | X | X | | | |
|
| ECDSA<sup>1</sup> | X | | X | X | | | |
|
||||||
| ECDH<sup>1</sup> | X | | X | | | | X |
|
| ECDH<sup>1</sup> | X | | X | | | | X |
|
||||||
|
| EdDSA<sup>2,3</sup> | X | | X | X | | | |
|
||||||
|
| ECDH-ES<sup>2,4</sup> | X | | X | | | | X |
|
||||||
| HKDF | | | X | | | | X |
|
| HKDF | | | X | | | | X |
|
||||||
| PBKDF2 | | | X | | | | X |
|
| PBKDF2 | | | X | | | | X |
|
||||||
| DES-CBC<sup>2</sup>| X | | X | | X | X | |
|
| DES-CBC<sup>2</sup>| X | | X | | X | X | |
|
||||||
|
@ -58,6 +60,10 @@ npm install @peculiar/webcrypto
|
||||||
|
|
||||||
<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.
|
<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.
|
||||||
|
|
||||||
|
<sup>3</sup> Mechanism supports extended list of named curves `Ed25519`, and `Ed448`
|
||||||
|
|
||||||
|
<sup>4</sup> Mechanism supports extended list of named curves `X25519`, and `X448`
|
||||||
|
|
||||||
## Using
|
## Using
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
|
@ -254,9 +254,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@eslint/eslintrc": {
|
"@eslint/eslintrc": {
|
||||||
"version": "0.2.2",
|
"version": "0.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz",
|
||||||
"integrity": "sha512-EfB5OHNYp1F4px/LI/FEnGylop7nOqkQ1LRzCM0KccA2U8tvV8w01KBv37LbO7nW4H+YhKyo2LcJhRwjjV17QQ==",
|
"integrity": "sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"ajv": "^6.12.4",
|
"ajv": "^6.12.4",
|
||||||
|
@ -266,7 +266,7 @@
|
||||||
"ignore": "^4.0.6",
|
"ignore": "^4.0.6",
|
||||||
"import-fresh": "^3.2.1",
|
"import-fresh": "^3.2.1",
|
||||||
"js-yaml": "^3.13.1",
|
"js-yaml": "^3.13.1",
|
||||||
"lodash": "^4.17.19",
|
"lodash": "^4.17.20",
|
||||||
"minimatch": "^3.0.4",
|
"minimatch": "^3.0.4",
|
||||||
"strip-json-comments": "^3.1.1"
|
"strip-json-comments": "^3.1.1"
|
||||||
},
|
},
|
||||||
|
@ -433,9 +433,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/json-schema": {
|
"@types/json-schema": {
|
||||||
"version": "7.0.6",
|
"version": "7.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz",
|
||||||
"integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==",
|
"integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/json5": {
|
"@types/json5": {
|
||||||
|
@ -451,76 +451,77 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "12.19.12",
|
"version": "14.14.22",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.12.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.22.tgz",
|
||||||
"integrity": "sha512-UwfL2uIU9arX/+/PRcIkT08/iBadGN2z6ExOROA2Dh5mAuWTBj6iJbQX4nekiV5H8cTrEG569LeX+HRco9Cbxw==",
|
"integrity": "sha512-g+f/qj/cNcqKkc3tFqlXOYjrmZA+jNBiDzbP3kH+B+otKFqAdPgVTGP1IeKRdMml/aE69as5S4FqtxAbl+LaMw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@typescript-eslint/eslint-plugin": {
|
"@typescript-eslint/eslint-plugin": {
|
||||||
"version": "4.12.0",
|
"version": "4.14.2",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.14.2.tgz",
|
||||||
"integrity": "sha512-wHKj6q8s70sO5i39H2g1gtpCXCvjVszzj6FFygneNFyIAxRvNSVz9GML7XpqrB9t7hNutXw+MHnLN/Ih6uyB8Q==",
|
"integrity": "sha512-uMGfG7GFYK/nYutK/iqYJv6K/Xuog/vrRRZX9aEP4Zv1jsYXuvFUMDFLhUnc8WFv3D2R5QhNQL3VYKmvLS5zsQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/experimental-utils": "4.12.0",
|
"@typescript-eslint/experimental-utils": "4.14.2",
|
||||||
"@typescript-eslint/scope-manager": "4.12.0",
|
"@typescript-eslint/scope-manager": "4.14.2",
|
||||||
"debug": "^4.1.1",
|
"debug": "^4.1.1",
|
||||||
"functional-red-black-tree": "^1.0.1",
|
"functional-red-black-tree": "^1.0.1",
|
||||||
|
"lodash": "^4.17.15",
|
||||||
"regexpp": "^3.0.0",
|
"regexpp": "^3.0.0",
|
||||||
"semver": "^7.3.2",
|
"semver": "^7.3.2",
|
||||||
"tsutils": "^3.17.1"
|
"tsutils": "^3.17.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/experimental-utils": {
|
"@typescript-eslint/experimental-utils": {
|
||||||
"version": "4.12.0",
|
"version": "4.14.2",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.14.2.tgz",
|
||||||
"integrity": "sha512-MpXZXUAvHt99c9ScXijx7i061o5HEjXltO+sbYfZAAHxv3XankQkPaNi5myy0Yh0Tyea3Hdq1pi7Vsh0GJb0fA==",
|
"integrity": "sha512-mV9pmET4C2y2WlyHmD+Iun8SAEqkLahHGBkGqDVslHkmoj3VnxnGP4ANlwuxxfq1BsKdl/MPieDbohCEQgKrwA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/json-schema": "^7.0.3",
|
"@types/json-schema": "^7.0.3",
|
||||||
"@typescript-eslint/scope-manager": "4.12.0",
|
"@typescript-eslint/scope-manager": "4.14.2",
|
||||||
"@typescript-eslint/types": "4.12.0",
|
"@typescript-eslint/types": "4.14.2",
|
||||||
"@typescript-eslint/typescript-estree": "4.12.0",
|
"@typescript-eslint/typescript-estree": "4.14.2",
|
||||||
"eslint-scope": "^5.0.0",
|
"eslint-scope": "^5.0.0",
|
||||||
"eslint-utils": "^2.0.0"
|
"eslint-utils": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/parser": {
|
"@typescript-eslint/parser": {
|
||||||
"version": "4.12.0",
|
"version": "4.14.2",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.14.2.tgz",
|
||||||
"integrity": "sha512-9XxVADAo9vlfjfoxnjboBTxYOiNY93/QuvcPgsiKvHxW6tOZx1W4TvkIQ2jB3k5M0pbFP5FlXihLK49TjZXhuQ==",
|
"integrity": "sha512-ipqSP6EuUsMu3E10EZIApOJgWSpcNXeKZaFeNKQyzqxnQl8eQCbV+TSNsl+s2GViX2d18m1rq3CWgnpOxDPgHg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/scope-manager": "4.12.0",
|
"@typescript-eslint/scope-manager": "4.14.2",
|
||||||
"@typescript-eslint/types": "4.12.0",
|
"@typescript-eslint/types": "4.14.2",
|
||||||
"@typescript-eslint/typescript-estree": "4.12.0",
|
"@typescript-eslint/typescript-estree": "4.14.2",
|
||||||
"debug": "^4.1.1"
|
"debug": "^4.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/scope-manager": {
|
"@typescript-eslint/scope-manager": {
|
||||||
"version": "4.12.0",
|
"version": "4.14.2",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.14.2.tgz",
|
||||||
"integrity": "sha512-QVf9oCSVLte/8jvOsxmgBdOaoe2J0wtEmBr13Yz0rkBNkl5D8bfnf6G4Vhox9qqMIoG7QQoVwd2eG9DM/ge4Qg==",
|
"integrity": "sha512-cuV9wMrzKm6yIuV48aTPfIeqErt5xceTheAgk70N1V4/2Ecj+fhl34iro/vIssJlb7XtzcaD07hWk7Jk0nKghg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/types": "4.12.0",
|
"@typescript-eslint/types": "4.14.2",
|
||||||
"@typescript-eslint/visitor-keys": "4.12.0"
|
"@typescript-eslint/visitor-keys": "4.14.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/types": {
|
"@typescript-eslint/types": {
|
||||||
"version": "4.12.0",
|
"version": "4.14.2",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.14.2.tgz",
|
||||||
"integrity": "sha512-N2RhGeheVLGtyy+CxRmxdsniB7sMSCfsnbh8K/+RUIXYYq3Ub5+sukRCjVE80QerrUBvuEvs4fDhz5AW/pcL6g==",
|
"integrity": "sha512-LltxawRW6wXy4Gck6ZKlBD05tCHQUj4KLn4iR69IyRiDHX3d3NCAhO+ix5OR2Q+q9bjCrHE/HKt+riZkd1At8Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@typescript-eslint/typescript-estree": {
|
"@typescript-eslint/typescript-estree": {
|
||||||
"version": "4.12.0",
|
"version": "4.14.2",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.14.2.tgz",
|
||||||
"integrity": "sha512-gZkFcmmp/CnzqD2RKMich2/FjBTsYopjiwJCroxqHZIY11IIoN0l5lKqcgoAPKHt33H2mAkSfvzj8i44Jm7F4w==",
|
"integrity": "sha512-ESiFl8afXxt1dNj8ENEZT12p+jl9PqRur+Y19m0Z/SPikGL6rqq4e7Me60SU9a2M28uz48/8yct97VQYaGl0Vg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/types": "4.12.0",
|
"@typescript-eslint/types": "4.14.2",
|
||||||
"@typescript-eslint/visitor-keys": "4.12.0",
|
"@typescript-eslint/visitor-keys": "4.14.2",
|
||||||
"debug": "^4.1.1",
|
"debug": "^4.1.1",
|
||||||
"globby": "^11.0.1",
|
"globby": "^11.0.1",
|
||||||
"is-glob": "^4.0.1",
|
"is-glob": "^4.0.1",
|
||||||
|
@ -530,12 +531,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/visitor-keys": {
|
"@typescript-eslint/visitor-keys": {
|
||||||
"version": "4.12.0",
|
"version": "4.14.2",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.14.2.tgz",
|
||||||
"integrity": "sha512-hVpsLARbDh4B9TKYz5cLbcdMIOAoBYgFPCSP9FFS/liSF+b33gVNq8JHY3QGhHNVz85hObvL7BEYLlgx553WCw==",
|
"integrity": "sha512-KBB+xLBxnBdTENs/rUgeUKO0UkPBRs2vD09oMRRIkj5BEN8PX1ToXV532desXfpQnZsYTyLLviS7JrPhdL154w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/types": "4.12.0",
|
"@typescript-eslint/types": "4.14.2",
|
||||||
"eslint-visitor-keys": "^2.0.0"
|
"eslint-visitor-keys": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1169,13 +1170,13 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"eslint": {
|
"eslint": {
|
||||||
"version": "7.17.0",
|
"version": "7.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.19.0.tgz",
|
||||||
"integrity": "sha512-zJk08MiBgwuGoxes5sSQhOtibZ75pz0J35XTRlZOk9xMffhpA9BTbQZxoXZzOl5zMbleShbGwtw+1kGferfFwQ==",
|
"integrity": "sha512-CGlMgJY56JZ9ZSYhJuhow61lMPPjUzWmChFya71Z/jilVos7mR/jPgaEfVGgMBY5DshbKdG8Ezb8FDCHcoMEMg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/code-frame": "^7.0.0",
|
"@babel/code-frame": "^7.0.0",
|
||||||
"@eslint/eslintrc": "^0.2.2",
|
"@eslint/eslintrc": "^0.3.0",
|
||||||
"ajv": "^6.10.0",
|
"ajv": "^6.10.0",
|
||||||
"chalk": "^4.0.0",
|
"chalk": "^4.0.0",
|
||||||
"cross-spawn": "^7.0.2",
|
"cross-spawn": "^7.0.2",
|
||||||
|
@ -1199,7 +1200,7 @@
|
||||||
"js-yaml": "^3.13.1",
|
"js-yaml": "^3.13.1",
|
||||||
"json-stable-stringify-without-jsonify": "^1.0.1",
|
"json-stable-stringify-without-jsonify": "^1.0.1",
|
||||||
"levn": "^0.4.1",
|
"levn": "^0.4.1",
|
||||||
"lodash": "^4.17.19",
|
"lodash": "^4.17.20",
|
||||||
"minimatch": "^3.0.4",
|
"minimatch": "^3.0.4",
|
||||||
"natural-compare": "^1.4.0",
|
"natural-compare": "^1.4.0",
|
||||||
"optionator": "^0.9.1",
|
"optionator": "^0.9.1",
|
||||||
|
@ -1452,9 +1453,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"fast-glob": {
|
"fast-glob": {
|
||||||
"version": "3.2.4",
|
"version": "3.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz",
|
||||||
"integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==",
|
"integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@nodelib/fs.stat": "^2.0.2",
|
"@nodelib/fs.stat": "^2.0.2",
|
||||||
|
@ -1478,9 +1479,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"fastq": {
|
"fastq": {
|
||||||
"version": "1.10.0",
|
"version": "1.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.10.1.tgz",
|
||||||
"integrity": "sha512-NL2Qc5L3iQEsyYzweq7qfgy5OtXCmGzGvhElGEd/SoFWEMOEczNh5s5ocaF01HDetxz+p8ecjNPA6cZxxIHmzA==",
|
"integrity": "sha512-AWuv6Ery3pM+dY7LYS8YIaCiQvUaos9OB1RyNgaOWnaX+Tik7Onvcsf8x8c+YtDeT0maYLniBip2hox5KtEXXA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"reusify": "^1.0.4"
|
"reusify": "^1.0.4"
|
||||||
|
@ -1601,9 +1602,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flatted": {
|
"flatted": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz",
|
||||||
"integrity": "sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA==",
|
"integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"foreground-child": {
|
"foreground-child": {
|
||||||
|
@ -2949,11 +2950,11 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"pvtsutils": {
|
"pvtsutils": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.1.2.tgz",
|
||||||
"integrity": "sha512-Evbhe6L4Sxwu4SPLQ4LQZhgfWDQO3qa1lju9jM5cxsQp8vE10VipcSmo7hiJW48TmiHgVLgDtC2TL6/+ND+IVg==",
|
"integrity": "sha512-Yfm9Dsk1zfEpOWCaJaHfqtNXAFWNNHMFSCLN6jTnhuCCBCC2nqge4sAgo7UrkRBoAAYIL8TN/6LlLoNfZD/b5A==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"tslib": "^2.0.3"
|
"tslib": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"pvutils": {
|
"pvutils": {
|
||||||
|
@ -3110,12 +3111,21 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rollup": {
|
"rollup": {
|
||||||
"version": "2.36.1",
|
"version": "2.38.4",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.36.1.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.38.4.tgz",
|
||||||
"integrity": "sha512-eAfqho8dyzuVvrGqpR0ITgEdq0zG2QJeWYh+HeuTbpcaXk8vNFc48B7bJa1xYosTCKx0CuW+447oQOW8HgBIZQ==",
|
"integrity": "sha512-B0LcJhjiwKkTl79aGVF/u5KdzsH8IylVfV56Ut6c9ouWLJcUK17T83aZBetNYSnZtXf2OHD4+2PbmRW+Fp5ulg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"fsevents": "~2.1.2"
|
"fsevents": "~2.3.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"fsevents": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-YR47Eg4hChJGAB1O3yEAOkGO+rlzutoICGqGo9EZ4lKWokzZRSyIW1QmTzqjtw8MJdj9srP869CuWw/hyzSiBw==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rollup-plugin-typescript2": {
|
"rollup-plugin-typescript2": {
|
||||||
|
@ -3444,9 +3454,9 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ajv": {
|
"ajv": {
|
||||||
"version": "7.0.3",
|
"version": "7.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-7.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-7.0.4.tgz",
|
||||||
"integrity": "sha512-R50QRlXSxqXcQP5SvKUrw8VZeypvo12i2IX0EeR5PiZ7bEKeHWgzgo264LDadUsCU42lTJVhFikTqJwNeH34gQ==",
|
"integrity": "sha512-xzzzaqgEQfmuhbhAoqjJ8T/1okb6gAzXn/eQRNpAN1AEUoHJTNF9xCDRTtf/s3SKldtZfa+RJeTs+BQq+eZ/sw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"fast-deep-equal": "^3.1.1",
|
"fast-deep-equal": "^3.1.1",
|
||||||
|
@ -3560,9 +3570,9 @@
|
||||||
"integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A=="
|
"integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A=="
|
||||||
},
|
},
|
||||||
"tsutils": {
|
"tsutils": {
|
||||||
"version": "3.19.1",
|
"version": "3.20.0",
|
||||||
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.19.1.tgz",
|
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.20.0.tgz",
|
||||||
"integrity": "sha512-GEdoBf5XI324lu7ycad7s6laADfnAqCw6wLGI+knxvw9vsIYBaJfYdmeCEG3FMMUiSm3OGgNb+m6utsWf5h9Vw==",
|
"integrity": "sha512-RYbuQuvkhuqVeXweWT3tJLKOEJ/UUw9GjNEZGWdrLLlM+611o1gwLHBpxoFJKKl25fLprp2eVthtKs5JOrNeXg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"tslib": "^1.8.1"
|
"tslib": "^1.8.1"
|
||||||
|
@ -3670,25 +3680,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"webcrypto-core": {
|
"webcrypto-core": {
|
||||||
"version": "1.1.10",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.1.10.tgz",
|
"resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.2.0.tgz",
|
||||||
"integrity": "sha512-9q47cYIi1NFGBg0C4FTJGy9zWwFvrowJ28V3Faxs9Hu7TAEFbrfxOIy7fsniSLsfjZqK/VMeiVUHoWWuxtiA8A==",
|
"integrity": "sha512-p76Z/YLuE4CHCRdc49FB/ETaM4bzM3roqWNJeGs+QNY1fOTzKTOVnhmudW1fuO+5EZg6/4LG9NJ6gaAyxTk9XQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@peculiar/asn1-schema": "^2.0.27",
|
"@peculiar/asn1-schema": "^2.0.27",
|
||||||
"@peculiar/json-schema": "^1.1.12",
|
"@peculiar/json-schema": "^1.1.12",
|
||||||
"asn1js": "^2.0.26",
|
"asn1js": "^2.0.26",
|
||||||
"pvtsutils": "^1.1.2",
|
"pvtsutils": "^1.1.2",
|
||||||
"tslib": "^2.1.0"
|
"tslib": "^2.1.0"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"pvtsutils": {
|
|
||||||
"version": "1.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.1.2.tgz",
|
|
||||||
"integrity": "sha512-Yfm9Dsk1zfEpOWCaJaHfqtNXAFWNNHMFSCLN6jTnhuCCBCC2nqge4sAgo7UrkRBoAAYIL8TN/6LlLoNfZD/b5A==",
|
|
||||||
"requires": {
|
|
||||||
"tslib": "^2.1.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"which": {
|
"which": {
|
||||||
|
|
21
package.json
21
package.json
|
@ -42,7 +42,12 @@
|
||||||
"aes",
|
"aes",
|
||||||
"des",
|
"des",
|
||||||
"hmac",
|
"hmac",
|
||||||
"pbkdf2"
|
"pbkdf2",
|
||||||
|
"eddsa",
|
||||||
|
"x25519",
|
||||||
|
"ed25519",
|
||||||
|
"x448",
|
||||||
|
"ed448"
|
||||||
],
|
],
|
||||||
"author": "PeculiarVentures",
|
"author": "PeculiarVentures",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
|
@ -57,16 +62,16 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@peculiar/webcrypto-test": "^1.0.7",
|
"@peculiar/webcrypto-test": "^1.0.7",
|
||||||
"@types/mocha": "^8.2.0",
|
"@types/mocha": "^8.2.0",
|
||||||
"@types/node": "^12.19.12",
|
"@types/node": "^14.14.22",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.12.0",
|
"@typescript-eslint/eslint-plugin": "^4.14.2",
|
||||||
"@typescript-eslint/parser": "^4.12.0",
|
"@typescript-eslint/parser": "^4.14.2",
|
||||||
"coveralls": "^3.1.0",
|
"coveralls": "^3.1.0",
|
||||||
"eslint": "^7.17.0",
|
"eslint": "^7.19.0",
|
||||||
"eslint-plugin-import": "^2.22.1",
|
"eslint-plugin-import": "^2.22.1",
|
||||||
"mocha": "^8.2.1",
|
"mocha": "^8.2.1",
|
||||||
"nyc": "^15.1.0",
|
"nyc": "^15.1.0",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"rollup": "^2.36.1",
|
"rollup": "^2.38.4",
|
||||||
"rollup-plugin-typescript2": "^0.29.0",
|
"rollup-plugin-typescript2": "^0.29.0",
|
||||||
"ts-node": "^9.1.1",
|
"ts-node": "^9.1.1",
|
||||||
"typescript": "^4.1.3"
|
"typescript": "^4.1.3"
|
||||||
|
@ -74,9 +79,9 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@peculiar/asn1-schema": "^2.0.27",
|
"@peculiar/asn1-schema": "^2.0.27",
|
||||||
"@peculiar/json-schema": "^1.1.12",
|
"@peculiar/json-schema": "^1.1.12",
|
||||||
"pvtsutils": "^1.1.1",
|
"pvtsutils": "^1.1.2",
|
||||||
"tslib": "^2.1.0",
|
"tslib": "^2.1.0",
|
||||||
"webcrypto-core": "^1.1.10"
|
"webcrypto-core": "^1.2.0"
|
||||||
},
|
},
|
||||||
"nyc": {
|
"nyc": {
|
||||||
"extension": [
|
"extension": [
|
||||||
|
|
|
@ -0,0 +1,166 @@
|
||||||
|
import crypto from "crypto";
|
||||||
|
import { AsnParser } from "@peculiar/asn1-schema";
|
||||||
|
import { JsonParser, JsonSerializer } from "@peculiar/json-schema";
|
||||||
|
import { Convert } from "pvtsutils";
|
||||||
|
import * as core from "webcrypto-core";
|
||||||
|
import { CryptoKey } from "../../keys";
|
||||||
|
import { EdPrivateKey } from "./private_key";
|
||||||
|
import { EdPublicKey } from "./public_key";
|
||||||
|
|
||||||
|
export class EdCrypto {
|
||||||
|
|
||||||
|
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 EdPrivateKey();
|
||||||
|
privateKey.algorithm = algorithm;
|
||||||
|
privateKey.extractable = extractable;
|
||||||
|
privateKey.usages = keyUsages.filter((usage) => this.privateKeyUsages.indexOf(usage) !== -1);
|
||||||
|
|
||||||
|
const publicKey = new EdPublicKey();
|
||||||
|
publicKey.algorithm = algorithm;
|
||||||
|
publicKey.extractable = true;
|
||||||
|
publicKey.usages = keyUsages.filter((usage) => this.publicKeyUsages.indexOf(usage) !== -1);
|
||||||
|
|
||||||
|
const type = algorithm.namedCurve.toLowerCase() as "x448"; // "x448" | "ed448" | "x25519" | "ed25519"
|
||||||
|
const keys = crypto.generateKeyPairSync(type, {
|
||||||
|
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 sign(algorithm: Algorithm, key: EdPrivateKey, data: Uint8Array): Promise<ArrayBuffer> {
|
||||||
|
if (!key.pem) {
|
||||||
|
key.pem = `-----BEGIN PRIVATE KEY-----\n${key.data.toString("base64")}\n-----END PRIVATE KEY-----`;
|
||||||
|
}
|
||||||
|
const options = {
|
||||||
|
key: key.pem,
|
||||||
|
};
|
||||||
|
const signature = crypto.sign(null, Buffer.from(data), options);
|
||||||
|
|
||||||
|
return core.BufferSourceConverter.toArrayBuffer(signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async verify(algorithm: EcdsaParams, key: EdPublicKey, signature: Uint8Array, data: Uint8Array): Promise<boolean> {
|
||||||
|
if (!key.pem) {
|
||||||
|
key.pem = `-----BEGIN PUBLIC KEY-----\n${key.data.toString("base64")}\n-----END PUBLIC KEY-----`;
|
||||||
|
}
|
||||||
|
const options = {
|
||||||
|
key: key.pem,
|
||||||
|
};
|
||||||
|
const ok = crypto.verify(null, Buffer.from(data), options, Buffer.from(signature));
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async deriveBits(algorithm: EcdhKeyDeriveParams, baseKey: CryptoKey, length: number): Promise<ArrayBuffer> {
|
||||||
|
const publicKey = crypto.createPublicKey({
|
||||||
|
key: (algorithm.public as CryptoKey).data,
|
||||||
|
format: "der",
|
||||||
|
type: "spki",
|
||||||
|
});
|
||||||
|
const privateKey = crypto.createPrivateKey({
|
||||||
|
key: baseKey.data,
|
||||||
|
format: "der",
|
||||||
|
type: "pkcs8",
|
||||||
|
});
|
||||||
|
const bits = crypto.diffieHellman({
|
||||||
|
publicKey,
|
||||||
|
privateKey,
|
||||||
|
});
|
||||||
|
|
||||||
|
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, core.asn1.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, { targetSchema: core.asn1.CurvePrivateKey });
|
||||||
|
return this.importPrivateKey(asnKey, algorithm, extractable, keyUsages);
|
||||||
|
} else {
|
||||||
|
if (!jwk.x) {
|
||||||
|
throw new TypeError("keyData: Cannot get required 'x' filed");
|
||||||
|
}
|
||||||
|
return this.importPublicKey(Convert.FromBase64Url(jwk.x), algorithm, extractable, keyUsages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "raw": {
|
||||||
|
return this.importPublicKey(keyData as ArrayBuffer, algorithm, extractable, keyUsages);
|
||||||
|
}
|
||||||
|
case "spki": {
|
||||||
|
const keyInfo = AsnParser.parse(new Uint8Array(keyData as ArrayBuffer), core.asn1.PublicKeyInfo);
|
||||||
|
return this.importPublicKey(keyInfo.publicKey, algorithm, extractable, keyUsages);
|
||||||
|
}
|
||||||
|
case "pkcs8": {
|
||||||
|
const keyInfo = AsnParser.parse(new Uint8Array(keyData as ArrayBuffer), core.asn1.PrivateKeyInfo);
|
||||||
|
const asnKey = AsnParser.parse(keyInfo.privateKey, core.asn1.CurvePrivateKey);
|
||||||
|
return this.importPrivateKey(asnKey, algorithm, extractable, keyUsages);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new core.OperationError("format: Must be 'jwk', 'raw', 'pkcs8' or 'spki'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static importPrivateKey(asnKey: core.asn1.CurvePrivateKey, algorithm: EcKeyImportParams, extractable: boolean, keyUsages: KeyUsage[]) {
|
||||||
|
const key = new EdPrivateKey();
|
||||||
|
key.fromJSON({
|
||||||
|
crv: algorithm.namedCurve,
|
||||||
|
d: Convert.ToBase64Url(asnKey.d),
|
||||||
|
});
|
||||||
|
|
||||||
|
key.algorithm = Object.assign({}, algorithm) as EcKeyAlgorithm;
|
||||||
|
key.extractable = extractable;
|
||||||
|
key.usages = keyUsages;
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static async importPublicKey(asnKey: ArrayBuffer, algorithm: EcKeyImportParams, extractable: boolean, keyUsages: KeyUsage[]) {
|
||||||
|
const key = new EdPublicKey();
|
||||||
|
key.fromJSON({
|
||||||
|
crv: algorithm.namedCurve,
|
||||||
|
x: Convert.ToBase64Url(asnKey),
|
||||||
|
});
|
||||||
|
|
||||||
|
key.algorithm = Object.assign({}, algorithm) as EcKeyAlgorithm;
|
||||||
|
key.extractable = extractable;
|
||||||
|
key.usages = keyUsages;
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
import * as core from "webcrypto-core";
|
||||||
|
import { CryptoKey } from "../../keys";
|
||||||
|
import { getCryptoKey, setCryptoKey } from "../storage";
|
||||||
|
import { EdCrypto } from "./crypto";
|
||||||
|
|
||||||
|
export class EcdhEsProvider extends core.EcdhEsProvider {
|
||||||
|
|
||||||
|
public async onGenerateKey(algorithm: EcKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKeyPair> {
|
||||||
|
const keys = await EdCrypto.generateKey(
|
||||||
|
{
|
||||||
|
name: this.name,
|
||||||
|
namedCurve: algorithm.namedCurve.toUpperCase(),
|
||||||
|
},
|
||||||
|
extractable,
|
||||||
|
keyUsages);
|
||||||
|
|
||||||
|
return {
|
||||||
|
privateKey: setCryptoKey(keys.privateKey as CryptoKey),
|
||||||
|
publicKey: setCryptoKey(keys.publicKey as CryptoKey),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onDeriveBits(algorithm: EcdhKeyDeriveParams, baseKey: core.CryptoKey, length: number): Promise<ArrayBuffer> {
|
||||||
|
const bits = await EdCrypto.deriveBits({...algorithm, public: getCryptoKey(algorithm.public)}, getCryptoKey(baseKey), length);
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onExportKey(format: KeyFormat, key: CryptoKey): Promise<ArrayBuffer | JsonWebKey> {
|
||||||
|
return EdCrypto.exportKey(format, getCryptoKey(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onImportKey(format: KeyFormat, keyData: ArrayBuffer | JsonWebKey, algorithm: EcKeyImportParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<core.CryptoKey> {
|
||||||
|
const key = await EdCrypto.importKey(format, keyData, { ...algorithm, name: this.name }, extractable, keyUsages);
|
||||||
|
return setCryptoKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
import * as core from "webcrypto-core";
|
||||||
|
import { CryptoKey } from "../../keys";
|
||||||
|
import { getCryptoKey, setCryptoKey } from "../storage";
|
||||||
|
import { EdCrypto } from "./crypto";
|
||||||
|
import { EdPrivateKey } from "./private_key";
|
||||||
|
import { EdPublicKey } from "./public_key";
|
||||||
|
|
||||||
|
export class EdDsaProvider extends core.EdDsaProvider {
|
||||||
|
|
||||||
|
public async onGenerateKey(algorithm: EcKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKeyPair> {
|
||||||
|
const keys = await EdCrypto.generateKey(
|
||||||
|
{
|
||||||
|
name: this.name,
|
||||||
|
namedCurve: algorithm.namedCurve.replace(/^ed/i, "Ed"),
|
||||||
|
},
|
||||||
|
extractable,
|
||||||
|
keyUsages);
|
||||||
|
|
||||||
|
return {
|
||||||
|
privateKey: setCryptoKey(keys.privateKey as CryptoKey),
|
||||||
|
publicKey: setCryptoKey(keys.publicKey as CryptoKey),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onSign(algorithm: EcdsaParams, key: CryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
|
||||||
|
return EdCrypto.sign(algorithm, getCryptoKey(key) as EdPrivateKey, new Uint8Array(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onVerify(algorithm: EcdsaParams, key: CryptoKey, signature: ArrayBuffer, data: ArrayBuffer): Promise<boolean> {
|
||||||
|
return EdCrypto.verify(algorithm, getCryptoKey(key) as EdPublicKey, new Uint8Array(signature), new Uint8Array(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onExportKey(format: KeyFormat, key: CryptoKey): Promise<ArrayBuffer | JsonWebKey> {
|
||||||
|
return EdCrypto.exportKey(format, getCryptoKey(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onImportKey(format: KeyFormat, keyData: ArrayBuffer | JsonWebKey, algorithm: EcKeyImportParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<core.CryptoKey> {
|
||||||
|
const key = await EdCrypto.importKey(format, keyData, { ...algorithm, name: this.name }, extractable, keyUsages);
|
||||||
|
return setCryptoKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
import * as core from "webcrypto-core";
|
||||||
|
|
||||||
|
const edOIDs: { [key: string]: string } = {
|
||||||
|
// Ed448
|
||||||
|
[core.asn1.idEd448]: "Ed448",
|
||||||
|
"ed448": core.asn1.idEd448,
|
||||||
|
// X448
|
||||||
|
[core.asn1.idX448]: "X448",
|
||||||
|
"x448": core.asn1.idX448,
|
||||||
|
// Ed25519
|
||||||
|
[core.asn1.idEd25519]: "Ed25519",
|
||||||
|
"ed25519": core.asn1.idEd25519,
|
||||||
|
// X25519
|
||||||
|
[core.asn1.idX25519]: "X25519",
|
||||||
|
"x25519": core.asn1.idX25519,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getNamedCurveByOid(oid: string) {
|
||||||
|
const namedCurve = edOIDs[oid];
|
||||||
|
if (!namedCurve) {
|
||||||
|
throw new core.OperationError(`Cannot convert OID(${oid}) to WebCrypto named curve`);
|
||||||
|
}
|
||||||
|
return namedCurve;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getOidByNamedCurve(namedCurve: string) {
|
||||||
|
const oid = edOIDs[namedCurve.toLowerCase()];
|
||||||
|
if (!oid) {
|
||||||
|
throw new core.OperationError(`Cannot convert WebCrypto named curve '${namedCurve}' to OID`);
|
||||||
|
}
|
||||||
|
return oid;
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
export * from "./eddsa";
|
||||||
|
export * from "./ecdh_es";
|
||||||
|
export * from "./private_key";
|
||||||
|
export * from "./public_key";
|
|
@ -0,0 +1,44 @@
|
||||||
|
import { AsnParser, AsnSerializer } from "@peculiar/asn1-schema";
|
||||||
|
import { IJsonConvertible, JsonParser, JsonSerializer } from "@peculiar/json-schema";
|
||||||
|
import * as core from "webcrypto-core";
|
||||||
|
import { AsymmetricKey } from "../../keys";
|
||||||
|
import { getOidByNamedCurve } from "./helper";
|
||||||
|
|
||||||
|
export class EdPrivateKey extends AsymmetricKey implements IJsonConvertible {
|
||||||
|
public readonly type: "private" = "private";
|
||||||
|
public algorithm!: EcKeyAlgorithm;
|
||||||
|
|
||||||
|
public getKey() {
|
||||||
|
const keyInfo = AsnParser.parse(this.data, core.asn1.PrivateKeyInfo);
|
||||||
|
return AsnParser.parse(keyInfo.privateKey, core.asn1.CurvePrivateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public toJSON() {
|
||||||
|
const key = this.getKey();
|
||||||
|
|
||||||
|
const json: JsonWebKey = {
|
||||||
|
kty: "OKP",
|
||||||
|
crv: this.algorithm.namedCurve,
|
||||||
|
key_ops: this.usages,
|
||||||
|
ext: this.extractable,
|
||||||
|
};
|
||||||
|
|
||||||
|
return Object.assign(json, JsonSerializer.toJSON(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
public fromJSON(json: JsonWebKey) {
|
||||||
|
if (!json.crv) {
|
||||||
|
throw new core.OperationError(`Cannot get named curve from JWK. Property 'crv' is required`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const keyInfo = new core.asn1.PrivateKeyInfo();
|
||||||
|
keyInfo.privateKeyAlgorithm.algorithm = getOidByNamedCurve(json.crv);
|
||||||
|
const key = JsonParser.fromJSON(json, { targetSchema: core.asn1.CurvePrivateKey });
|
||||||
|
keyInfo.privateKey = AsnSerializer.serialize(key);
|
||||||
|
|
||||||
|
this.data = Buffer.from(AsnSerializer.serialize(keyInfo));
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { AsnParser, AsnSerializer } from "@peculiar/asn1-schema";
|
||||||
|
import { IJsonConvertible } from "@peculiar/json-schema";
|
||||||
|
import { Convert } from "pvtsutils";
|
||||||
|
import * as core from "webcrypto-core";
|
||||||
|
import { AsymmetricKey } from "../../keys/asymmetric";
|
||||||
|
import { getOidByNamedCurve } from "./helper";
|
||||||
|
|
||||||
|
export class EdPublicKey extends AsymmetricKey implements IJsonConvertible {
|
||||||
|
|
||||||
|
public readonly type: "public" = "public";
|
||||||
|
public algorithm!: EcKeyAlgorithm;
|
||||||
|
|
||||||
|
public getKey() {
|
||||||
|
const keyInfo = AsnParser.parse(this.data, core.asn1.PublicKeyInfo);
|
||||||
|
return keyInfo.publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public toJSON() {
|
||||||
|
const key = this.getKey();
|
||||||
|
|
||||||
|
const json: JsonWebKey = {
|
||||||
|
kty: "OKP",
|
||||||
|
crv: this.algorithm.namedCurve,
|
||||||
|
key_ops: this.usages,
|
||||||
|
ext: this.extractable,
|
||||||
|
};
|
||||||
|
|
||||||
|
return Object.assign(json, {
|
||||||
|
x: Convert.ToBase64Url(key)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public fromJSON(json: JsonWebKey) {
|
||||||
|
if (!json.crv) {
|
||||||
|
throw new core.OperationError(`Cannot get named curve from JWK. Property 'crv' is required`);
|
||||||
|
}
|
||||||
|
if (!json.x) {
|
||||||
|
throw new core.OperationError(`Cannot get property from JWK. Property 'x' is required`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const keyInfo = new core.asn1.PublicKeyInfo();
|
||||||
|
keyInfo.publicKeyAlgorithm.algorithm = getOidByNamedCurve(json.crv);
|
||||||
|
keyInfo.publicKey = Convert.FromBase64Url(json.x);
|
||||||
|
|
||||||
|
this.data = Buffer.from(AsnSerializer.serialize(keyInfo));
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ export * from "./aes";
|
||||||
export * from "./des";
|
export * from "./des";
|
||||||
export * from "./rsa";
|
export * from "./rsa";
|
||||||
export * from "./ec";
|
export * from "./ec";
|
||||||
|
export * from "./ed";
|
||||||
export * from "./sha";
|
export * from "./sha";
|
||||||
export * from "./pbkdf";
|
export * from "./pbkdf";
|
||||||
export * from "./hmac";
|
export * from "./hmac";
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
|
import * as process from "process";
|
||||||
import * as core from "webcrypto-core";
|
import * as core from "webcrypto-core";
|
||||||
import {
|
import {
|
||||||
AesCbcProvider, AesCmacProvider, AesCtrProvider, AesEcbProvider, AesGcmProvider, AesKwProvider,
|
AesCbcProvider, AesCmacProvider, AesCtrProvider, AesEcbProvider, AesGcmProvider, AesKwProvider,
|
||||||
DesCbcProvider,
|
DesCbcProvider,
|
||||||
DesEde3CbcProvider, EcdhProvider,
|
DesEde3CbcProvider, EcdhProvider,
|
||||||
EcdsaProvider, HkdfProvider,
|
EcdsaProvider, HkdfProvider,
|
||||||
|
EdDsaProvider,
|
||||||
|
EcdhEsProvider,
|
||||||
HmacProvider,
|
HmacProvider,
|
||||||
Pbkdf2Provider,
|
Pbkdf2Provider,
|
||||||
RsaEsProvider, RsaOaepProvider, RsaPssProvider, RsaSsaProvider,
|
RsaEsProvider, RsaOaepProvider, RsaPssProvider, RsaSsaProvider,
|
||||||
|
@ -58,5 +61,16 @@ export class SubtleCrypto extends core.SubtleCrypto {
|
||||||
//#region HKDF
|
//#region HKDF
|
||||||
this.providers.set(new HkdfProvider());
|
this.providers.set(new HkdfProvider());
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
const nodeMajorVersion = /^v(\d+)/.exec(process.version)?.[1];
|
||||||
|
if (nodeMajorVersion && parseInt(nodeMajorVersion, 10) >= 14) {
|
||||||
|
//#region EdDSA
|
||||||
|
this.providers.set(new EdDsaProvider());
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region ECDH-ES
|
||||||
|
this.providers.set(new EcdhEsProvider());
|
||||||
|
//#endregion
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
import assert from "assert";
|
import assert from "assert";
|
||||||
|
import process from "process";
|
||||||
import { WebcryptoTest } from "@peculiar/webcrypto-test";
|
import { WebcryptoTest } from "@peculiar/webcrypto-test";
|
||||||
import * as core from "webcrypto-core";
|
import * as core from "webcrypto-core";
|
||||||
import { Crypto } from "../src";
|
import { Crypto } from "../src";
|
||||||
|
|
||||||
|
const nodeMajorVersion = parseInt(/^v(\d+)/.exec(process.version)![1], 10);
|
||||||
|
|
||||||
const crypto = new Crypto();
|
const crypto = new Crypto();
|
||||||
|
|
||||||
WebcryptoTest.check(crypto as any, {});
|
WebcryptoTest.check(crypto as any, {});
|
||||||
|
@ -57,4 +60,63 @@ context("Crypto", () => {
|
||||||
assert.strictEqual((hmac.algorithm as globalThis.HmacKeyAlgorithm).length, 512);
|
assert.strictEqual((hmac.algorithm as globalThis.HmacKeyAlgorithm).length, 512);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
(nodeMajorVersion < 14 ? context.skip : context)("EdDSA", () => {
|
||||||
|
|
||||||
|
context("generateKey", () => {
|
||||||
|
|
||||||
|
it("Ed25519", async () => {
|
||||||
|
const keys = await crypto.subtle.generateKey({ name: "eddsa", namedCurve: "ed25519" } as globalThis.EcKeyGenParams, false, ["sign", "verify"]) as CryptoKeyPair;
|
||||||
|
assert.strictEqual(keys.privateKey.algorithm.name, "EdDSA");
|
||||||
|
assert.strictEqual((keys.privateKey.algorithm as EcKeyAlgorithm).namedCurve, "Ed25519");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Ed448", async () => {
|
||||||
|
const keys = await crypto.subtle.generateKey({ name: "eddsa", namedCurve: "ed448" } as globalThis.EcKeyGenParams, true, ["sign", "verify"]) as CryptoKeyPair;
|
||||||
|
assert.strictEqual(keys.privateKey.algorithm.name, "EdDSA");
|
||||||
|
assert.strictEqual((keys.privateKey.algorithm as EcKeyAlgorithm).namedCurve, "Ed448");
|
||||||
|
|
||||||
|
const data = await crypto.subtle.exportKey("jwk", keys.privateKey);
|
||||||
|
assert.strictEqual(data.kty, "OKP");
|
||||||
|
assert.strictEqual(data.crv, "Ed448");
|
||||||
|
assert.strictEqual(!!data.d, true);
|
||||||
|
const privateKey = await crypto.subtle.importKey("jwk", data, { name: "eddsa", namedCurve: "ed448" } as EcKeyImportParams, false, ["sign"]);
|
||||||
|
|
||||||
|
const message = Buffer.from("message");
|
||||||
|
const signature = await crypto.subtle.sign({ name: "EdDSA" }, privateKey, message);
|
||||||
|
const ok = await crypto.subtle.verify({ name: "EdDSA" }, keys.publicKey, signature, message);
|
||||||
|
assert.strictEqual(ok, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
(nodeMajorVersion < 14 ? context.skip : context)("ECDH-ES", () => {
|
||||||
|
|
||||||
|
context("generateKey", () => {
|
||||||
|
|
||||||
|
it("X25519", async () => {
|
||||||
|
const keys = await crypto.subtle.generateKey({ name: "ecdh-es", namedCurve: "x25519" } as globalThis.EcKeyGenParams, false, ["deriveBits", "deriveKey"]) as CryptoKeyPair;
|
||||||
|
assert.strictEqual(keys.privateKey.algorithm.name, "ECDH-ES");
|
||||||
|
assert.strictEqual((keys.privateKey.algorithm as EcKeyAlgorithm).namedCurve, "X25519");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("X448", async () => {
|
||||||
|
const keys = await crypto.subtle.generateKey({ name: "ecdh-es", namedCurve: "x448" } as globalThis.EcKeyGenParams, true, ["deriveBits", "deriveKey"]) as CryptoKeyPair;
|
||||||
|
assert.strictEqual(keys.privateKey.algorithm.name, "ECDH-ES");
|
||||||
|
assert.strictEqual((keys.privateKey.algorithm as EcKeyAlgorithm).namedCurve, "X448");
|
||||||
|
|
||||||
|
const bits = await crypto.subtle.deriveBits({ name: "ECDH-ES", public: keys.publicKey } as globalThis.EcdhKeyDeriveParams, keys.privateKey, 256);
|
||||||
|
assert.strictEqual(bits.byteLength, 32);
|
||||||
|
|
||||||
|
const data = await crypto.subtle.exportKey("jwk", keys.publicKey);
|
||||||
|
assert.strictEqual(data.kty, "OKP");
|
||||||
|
assert.strictEqual(data.crv, "X448");
|
||||||
|
assert.strictEqual(!!data.x, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
Reference in New Issue