Setup + ASN.1 utilities (PublicKey bundle)
This commit is contained in:
parent
f942cf2d41
commit
e5ba3d4b7e
43
package-lock.json
generated
43
package-lock.json
generated
@ -13,11 +13,14 @@
|
|||||||
"@alexaltea/capstone-js": "^3.0.5",
|
"@alexaltea/capstone-js": "^3.0.5",
|
||||||
"@astronautlabs/amf": "^0.0.6",
|
"@astronautlabs/amf": "^0.0.6",
|
||||||
"@blu3r4y/lzma": "^2.3.3",
|
"@blu3r4y/lzma": "^2.3.3",
|
||||||
|
"@noble/curves": "^2.2.0",
|
||||||
"@noble/hashes": "2.2.0",
|
"@noble/hashes": "2.2.0",
|
||||||
|
"@peculiar/x509": "^1.14.3",
|
||||||
"@wavesenterprise/crypto-gost-js": "^2.1.0-RC1",
|
"@wavesenterprise/crypto-gost-js": "^2.1.0-RC1",
|
||||||
"@xmldom/xmldom": "^0.8.13",
|
"@xmldom/xmldom": "^0.8.13",
|
||||||
"argon2-browser": "^1.18.0",
|
"argon2-browser": "^1.18.0",
|
||||||
"arrive": "^2.5.3",
|
"arrive": "^2.5.3",
|
||||||
|
"asn1js": "^3.0.10",
|
||||||
"assert": "^2.1.0",
|
"assert": "^2.1.0",
|
||||||
"avsc": "^5.7.9",
|
"avsc": "^5.7.9",
|
||||||
"bcryptjs": "^3.0.3",
|
"bcryptjs": "^3.0.3",
|
||||||
@ -4237,6 +4240,21 @@
|
|||||||
"archiver": "^5.3.1"
|
"archiver": "^5.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@noble/curves": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-T/BoHgFXirb0ENSPBquzX0rcjXeM6Lo892a2jlYJkqk83LqZx0l1Of7DzlKJ6jkpvMrkHSnAcgb5JegL8SeIkQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@noble/hashes": "2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20.19.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@noble/hashes": {
|
"node_modules/@noble/hashes": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.2.0.tgz",
|
||||||
@ -4253,7 +4271,6 @@
|
|||||||
"version": "2.6.1",
|
"version": "2.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.6.1.tgz",
|
||||||
"integrity": "sha512-vdG4fBF6Lkirkcl53q6eOdn3XYKt+kJTG59edgRZORlg/3atWWEReRCx5rYE1ZzTTX6vLK5zDMjHh7vbrcXGtw==",
|
"integrity": "sha512-vdG4fBF6Lkirkcl53q6eOdn3XYKt+kJTG59edgRZORlg/3atWWEReRCx5rYE1ZzTTX6vLK5zDMjHh7vbrcXGtw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@peculiar/asn1-schema": "^2.6.0",
|
"@peculiar/asn1-schema": "^2.6.0",
|
||||||
@ -4267,7 +4284,6 @@
|
|||||||
"version": "2.6.1",
|
"version": "2.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.6.1.tgz",
|
||||||
"integrity": "sha512-WRWnKfIocHyzFYQTka8O/tXCiBquAPSrRjXbOkHbO4qdmS6loffCEGs+rby6WxxGdJCuunnhS2duHURhjyio6w==",
|
"integrity": "sha512-WRWnKfIocHyzFYQTka8O/tXCiBquAPSrRjXbOkHbO4qdmS6loffCEGs+rby6WxxGdJCuunnhS2duHURhjyio6w==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@peculiar/asn1-schema": "^2.6.0",
|
"@peculiar/asn1-schema": "^2.6.0",
|
||||||
@ -4280,7 +4296,6 @@
|
|||||||
"version": "2.6.1",
|
"version": "2.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.6.1.tgz",
|
||||||
"integrity": "sha512-+Vqw8WFxrtDIN5ehUdvlN2m73exS2JVG0UAyfVB31gIfor3zWEAQPD+K9ydCxaj3MLen9k0JhKpu9LqviuCE1g==",
|
"integrity": "sha512-+Vqw8WFxrtDIN5ehUdvlN2m73exS2JVG0UAyfVB31gIfor3zWEAQPD+K9ydCxaj3MLen9k0JhKpu9LqviuCE1g==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@peculiar/asn1-schema": "^2.6.0",
|
"@peculiar/asn1-schema": "^2.6.0",
|
||||||
@ -4293,7 +4308,6 @@
|
|||||||
"version": "2.6.1",
|
"version": "2.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.6.1.tgz",
|
||||||
"integrity": "sha512-nB5jVQy3MAAWvq0KY0R2JUZG8bO/bTLpnwyOzXyEh/e54ynGTatAR+csOnXkkVD9AFZ2uL8Z7EV918+qB1qDvw==",
|
"integrity": "sha512-nB5jVQy3MAAWvq0KY0R2JUZG8bO/bTLpnwyOzXyEh/e54ynGTatAR+csOnXkkVD9AFZ2uL8Z7EV918+qB1qDvw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@peculiar/asn1-cms": "^2.6.1",
|
"@peculiar/asn1-cms": "^2.6.1",
|
||||||
@ -4308,7 +4322,6 @@
|
|||||||
"version": "2.6.1",
|
"version": "2.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.6.1.tgz",
|
||||||
"integrity": "sha512-JB5iQ9Izn5yGMw3ZG4Nw3Xn/hb/G38GYF3lf7WmJb8JZUydhVGEjK/ZlFSWhnlB7K/4oqEs8HnfFIKklhR58Tw==",
|
"integrity": "sha512-JB5iQ9Izn5yGMw3ZG4Nw3Xn/hb/G38GYF3lf7WmJb8JZUydhVGEjK/ZlFSWhnlB7K/4oqEs8HnfFIKklhR58Tw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@peculiar/asn1-schema": "^2.6.0",
|
"@peculiar/asn1-schema": "^2.6.0",
|
||||||
@ -4321,7 +4334,6 @@
|
|||||||
"version": "2.6.1",
|
"version": "2.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.6.1.tgz",
|
||||||
"integrity": "sha512-5EV8nZoMSxeWmcxWmmcolg22ojZRgJg+Y9MX2fnE2bGRo5KQLqV5IL9kdSQDZxlHz95tHvIq9F//bvL1OeNILw==",
|
"integrity": "sha512-5EV8nZoMSxeWmcxWmmcolg22ojZRgJg+Y9MX2fnE2bGRo5KQLqV5IL9kdSQDZxlHz95tHvIq9F//bvL1OeNILw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@peculiar/asn1-cms": "^2.6.1",
|
"@peculiar/asn1-cms": "^2.6.1",
|
||||||
@ -4338,7 +4350,6 @@
|
|||||||
"version": "2.6.1",
|
"version": "2.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.6.1.tgz",
|
||||||
"integrity": "sha512-1nVMEh46SElUt5CB3RUTV4EG/z7iYc7EoaDY5ECwganibQPkZ/Y2eMsTKB/LeyrUJ+W/tKoD9WUqIy8vB+CEdA==",
|
"integrity": "sha512-1nVMEh46SElUt5CB3RUTV4EG/z7iYc7EoaDY5ECwganibQPkZ/Y2eMsTKB/LeyrUJ+W/tKoD9WUqIy8vB+CEdA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@peculiar/asn1-schema": "^2.6.0",
|
"@peculiar/asn1-schema": "^2.6.0",
|
||||||
@ -4351,7 +4362,6 @@
|
|||||||
"version": "2.6.0",
|
"version": "2.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.6.0.tgz",
|
||||||
"integrity": "sha512-xNLYLBFTBKkCzEZIw842BxytQQATQv+lDTCEMZ8C196iJcJJMBUZxrhSTxLaohMyKK8QlzRNTRkUmanucnDSqg==",
|
"integrity": "sha512-xNLYLBFTBKkCzEZIw842BxytQQATQv+lDTCEMZ8C196iJcJJMBUZxrhSTxLaohMyKK8QlzRNTRkUmanucnDSqg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"asn1js": "^3.0.6",
|
"asn1js": "^3.0.6",
|
||||||
@ -4363,7 +4373,6 @@
|
|||||||
"version": "2.6.1",
|
"version": "2.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.6.1.tgz",
|
||||||
"integrity": "sha512-O9jT5F1A2+t3r7C4VT7LYGXqkGLK7Kj1xFpz7U0isPrubwU5PbDoyYtx6MiGst29yq7pXN5vZbQFKRCP+lLZlA==",
|
"integrity": "sha512-O9jT5F1A2+t3r7C4VT7LYGXqkGLK7Kj1xFpz7U0isPrubwU5PbDoyYtx6MiGst29yq7pXN5vZbQFKRCP+lLZlA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@peculiar/asn1-schema": "^2.6.0",
|
"@peculiar/asn1-schema": "^2.6.0",
|
||||||
@ -4376,7 +4385,6 @@
|
|||||||
"version": "2.6.1",
|
"version": "2.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.6.1.tgz",
|
||||||
"integrity": "sha512-tlW6cxoHwgcQghnJwv3YS+9OO1737zgPogZ+CgWRUK4roEwIPzRH4JEiG770xe5HX2ATfCpmX60gurfWIF9dcQ==",
|
"integrity": "sha512-tlW6cxoHwgcQghnJwv3YS+9OO1737zgPogZ+CgWRUK4roEwIPzRH4JEiG770xe5HX2ATfCpmX60gurfWIF9dcQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@peculiar/asn1-schema": "^2.6.0",
|
"@peculiar/asn1-schema": "^2.6.0",
|
||||||
@ -4389,7 +4397,6 @@
|
|||||||
"version": "1.14.3",
|
"version": "1.14.3",
|
||||||
"resolved": "https://registry.npmjs.org/@peculiar/x509/-/x509-1.14.3.tgz",
|
"resolved": "https://registry.npmjs.org/@peculiar/x509/-/x509-1.14.3.tgz",
|
||||||
"integrity": "sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==",
|
"integrity": "sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@peculiar/asn1-cms": "^2.6.0",
|
"@peculiar/asn1-cms": "^2.6.0",
|
||||||
@ -5489,14 +5496,13 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/asn1js": {
|
"node_modules/asn1js": {
|
||||||
"version": "3.0.7",
|
"version": "3.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.10.tgz",
|
||||||
"integrity": "sha512-uLvq6KJu04qoQM6gvBfKFjlh6Gl0vOKQuR5cJMDHQkmwfMOQeN3F3SHCv9SNYSL+CRoHvOGFfllDlVz03GQjvQ==",
|
"integrity": "sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg==",
|
||||||
"dev": true,
|
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pvtsutils": "^1.3.6",
|
"pvtsutils": "^1.3.6",
|
||||||
"pvutils": "^1.1.3",
|
"pvutils": "^1.1.5",
|
||||||
"tslib": "^2.8.1"
|
"tslib": "^2.8.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -15323,7 +15329,6 @@
|
|||||||
"version": "1.3.6",
|
"version": "1.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz",
|
||||||
"integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==",
|
"integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.8.1"
|
"tslib": "^2.8.1"
|
||||||
@ -15333,7 +15338,6 @@
|
|||||||
"version": "1.1.5",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.5.tgz",
|
||||||
"integrity": "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==",
|
"integrity": "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.0.0"
|
"node": ">=16.0.0"
|
||||||
@ -17418,14 +17422,12 @@
|
|||||||
"version": "2.8.1",
|
"version": "2.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||||
"dev": true,
|
|
||||||
"license": "0BSD"
|
"license": "0BSD"
|
||||||
},
|
},
|
||||||
"node_modules/tsyringe": {
|
"node_modules/tsyringe": {
|
||||||
"version": "4.10.0",
|
"version": "4.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.10.0.tgz",
|
||||||
"integrity": "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==",
|
"integrity": "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^1.9.3"
|
"tslib": "^1.9.3"
|
||||||
@ -17438,7 +17440,6 @@
|
|||||||
"version": "1.14.1",
|
"version": "1.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
||||||
"dev": true,
|
|
||||||
"license": "0BSD"
|
"license": "0BSD"
|
||||||
},
|
},
|
||||||
"node_modules/tweetnacl": {
|
"node_modules/tweetnacl": {
|
||||||
|
|||||||
@ -96,11 +96,14 @@
|
|||||||
"@alexaltea/capstone-js": "^3.0.5",
|
"@alexaltea/capstone-js": "^3.0.5",
|
||||||
"@astronautlabs/amf": "^0.0.6",
|
"@astronautlabs/amf": "^0.0.6",
|
||||||
"@blu3r4y/lzma": "^2.3.3",
|
"@blu3r4y/lzma": "^2.3.3",
|
||||||
|
"@noble/curves": "^2.2.0",
|
||||||
"@noble/hashes": "2.2.0",
|
"@noble/hashes": "2.2.0",
|
||||||
|
"@peculiar/x509": "^1.14.3",
|
||||||
"@wavesenterprise/crypto-gost-js": "^2.1.0-RC1",
|
"@wavesenterprise/crypto-gost-js": "^2.1.0-RC1",
|
||||||
"@xmldom/xmldom": "^0.8.13",
|
"@xmldom/xmldom": "^0.8.13",
|
||||||
"argon2-browser": "^1.18.0",
|
"argon2-browser": "^1.18.0",
|
||||||
"arrive": "^2.5.3",
|
"arrive": "^2.5.3",
|
||||||
|
"asn1js": "^3.0.10",
|
||||||
"assert": "^2.1.0",
|
"assert": "^2.1.0",
|
||||||
"avsc": "^5.7.9",
|
"avsc": "^5.7.9",
|
||||||
"bcryptjs": "^3.0.3",
|
"bcryptjs": "^3.0.3",
|
||||||
|
|||||||
@ -4,14 +4,16 @@
|
|||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
- [ ] PR 1 — Setup + ASN.1 utilities
|
- [x] PR 1 — Setup + ASN.1 utilities
|
||||||
- [ ] PR 2 — SM2 rewrite
|
- [ ] PR 2 — SM2 rewrite
|
||||||
- [ ] PR 3 — ECDSA primitives
|
- [ ] PR 3 — ECDSA primitives
|
||||||
- [ ] PR 4 — PEM/JWK conversion + key extraction
|
- [ ] PR 4 — PEM/JWK conversion + key extraction
|
||||||
- [ ] PR 5 — X.509 / CSR / CRL parsing
|
- [ ] PR 5 — X.509 / CSR / CRL parsing
|
||||||
- [ ] PR 6 — Removal
|
- [ ] PR 6 — Removal
|
||||||
|
|
||||||
_Notes for next session:_ (none yet)
|
_Notes for next session:_
|
||||||
|
- **PR 2 blocker:** `@noble/curves` v2 dropped the `/sm2` subpath. v2 exposes only `nist`, `secp256k1`, `bls12-381`, `bn254`, `ed25519`, `ed448` and the `abstract/*` primitives. Before starting PR 2, either pin `@noble/curves` to v1 (which still ships sm2 — but check what other v1→v2 API gaps that introduces) or build SM2 on top of the abstract Weierstrass primitive in `@noble/curves/abstract/weierstrass.js` (curve parameters published in GM/T 0003-2012).
|
||||||
|
- **PR 5 blocker:** `@peculiar/x509` v2 needs a `reflect-metadata` polyfill at every entry point — PR 1 pinned to `^1.14.3` to avoid that. Stay on v1 unless the polyfill cost gets resolved.
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
@ -236,6 +238,14 @@ After **PR 6:**
|
|||||||
|
|
||||||
Record deviations from the original plan here, newest at the top. One bullet per change: what changed, why, and which PR.
|
Record deviations from the original plan here, newest at the top. One bullet per change: what changed, why, and which PR.
|
||||||
|
|
||||||
|
### PR 1 — 2026-05-17
|
||||||
|
- Pinned `@peculiar/x509` to `^1.14.3` instead of the latest (`2.x`). v2 hard-requires a `reflect-metadata` import at every entry point and the plan didn't budget for polyfilling every webpack chunk. Sticking with v1 keeps the bundle changes scoped to this PR.
|
||||||
|
- `@noble/curves` installed at `^2.2.0`. v2 no longer exports an `/sm2` subpath — see PR 2 note in "Notes for next session" above.
|
||||||
|
- `derToPem` was deliberately made lenient (whitespace stripped, odd length left-padded with `0`, non-hex chars treated as nibble `0`) to preserve the recipe-API tests that piped non-hex output from `To Morse Code` through `Hex to PEM`. The actual base64 emitted now follows standard byte-pair semantics, not jsrsasign's quirky `hex2b64` (3-hex-chars-→-2-base64-chars) layout — so the expected outputs in `tests/node/tests/nodeApi.mjs` for those recipe-format tests were regenerated.
|
||||||
|
- `dumpAsn1Hex` returns a plain `ASN.1 parse error: …` string when asn1js can't make sense of the input, rather than throwing. jsrsasign produced a best-effort `UNKNOWN(<tag>) <bytes>` dump in this case; replicating that on asn1js would be a meaningful chunk of code and the operation has no automated-fixture exposure of the difference, so we accepted the drift.
|
||||||
|
- PEM line endings switched from `\r\n` to `\n` (per the cross-PR convention in [AGENTS.md](AGENTS.md)). Updated the `Hex to PEM` and `Parse ASN.1 hex string` golden tests in `tests/node/tests/operations.mjs`.
|
||||||
|
- New per-op fixture file [tests/operations/tests/ASN1.mjs](tests/operations/tests/ASN1.mjs) covers OID round-trips, the multi-byte OID arc edge case, PEM line wrapping, and basic ASN.1 dumps. Wired in via `tests/operations/index.mjs`.
|
||||||
|
|
||||||
<!-- e.g.
|
<!-- e.g.
|
||||||
### PR 2 — 2026-05-20
|
### PR 2 — 2026-05-20
|
||||||
- Added `@noble/hashes` peer-dep bump (was on ^1.x, needed ^2 for the `legacy` subpath). No code impact.
|
- Added `@noble/hashes` peer-dep bump (was on ^1.x, needed ^2 for the `legacy` subpath). No code impact.
|
||||||
|
|||||||
338
src/core/lib/Asn1.mjs
Normal file
338
src/core/lib/Asn1.mjs
Normal file
@ -0,0 +1,338 @@
|
|||||||
|
/**
|
||||||
|
* ASN.1 / OID / PEM helpers.
|
||||||
|
*
|
||||||
|
* Replacements for the small jsrsasign utilities used by the
|
||||||
|
* HexToObjectIdentifier, ObjectIdentifierToHex, HexToPEM and
|
||||||
|
* ParseASN1HexString operations.
|
||||||
|
*
|
||||||
|
* @author n1474335 [n1474335@gmail.com]
|
||||||
|
* @copyright Crown Copyright 2016
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { fromBER } from "asn1js";
|
||||||
|
import OperationError from "../errors/OperationError.mjs";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a BER-encoded OID (as a hex string) to its dotted-decimal form.
|
||||||
|
*
|
||||||
|
* @param {string} hex
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function oidHexToInt(hex) {
|
||||||
|
const cleaned = hex.replace(/\s/g, "").toLowerCase();
|
||||||
|
if (cleaned.length === 0 || cleaned.length % 2 !== 0 || !/^[0-9a-f]+$/.test(cleaned)) {
|
||||||
|
throw new OperationError("Invalid hex string");
|
||||||
|
}
|
||||||
|
|
||||||
|
const bytes = new Uint8Array(cleaned.length / 2);
|
||||||
|
for (let i = 0; i < bytes.length; i++) {
|
||||||
|
bytes[i] = parseInt(cleaned.substr(i * 2, 2), 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
const varints = [];
|
||||||
|
let value = 0n;
|
||||||
|
for (const b of bytes) {
|
||||||
|
value = (value << 7n) | BigInt(b & 0x7f);
|
||||||
|
if ((b & 0x80) === 0) {
|
||||||
|
varints.push(value);
|
||||||
|
value = 0n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (value !== 0n) {
|
||||||
|
throw new OperationError("Malformed OID: ends with continuation byte");
|
||||||
|
}
|
||||||
|
if (varints.length === 0) {
|
||||||
|
throw new OperationError("Empty OID");
|
||||||
|
}
|
||||||
|
|
||||||
|
const arcs = [];
|
||||||
|
const first = varints[0];
|
||||||
|
if (first < 40n) {
|
||||||
|
arcs.push("0", first.toString());
|
||||||
|
} else if (first < 80n) {
|
||||||
|
arcs.push("1", (first - 40n).toString());
|
||||||
|
} else {
|
||||||
|
arcs.push("2", (first - 80n).toString());
|
||||||
|
}
|
||||||
|
for (let i = 1; i < varints.length; i++) {
|
||||||
|
arcs.push(varints[i].toString());
|
||||||
|
}
|
||||||
|
return arcs.join(".");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a dotted-decimal OID to its BER hex encoding.
|
||||||
|
*
|
||||||
|
* @param {string} oid
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function oidIntToHex(oid) {
|
||||||
|
if (typeof oid !== "string" || oid.length === 0) {
|
||||||
|
throw new OperationError("Empty OID");
|
||||||
|
}
|
||||||
|
const arcs = oid.split(".").map(a => {
|
||||||
|
if (!/^\d+$/.test(a)) {
|
||||||
|
throw new OperationError(`Invalid OID arc: ${a}`);
|
||||||
|
}
|
||||||
|
return BigInt(a);
|
||||||
|
});
|
||||||
|
if (arcs.length < 2) {
|
||||||
|
throw new OperationError("OID must have at least two arcs");
|
||||||
|
}
|
||||||
|
if (arcs[0] > 2n) {
|
||||||
|
throw new OperationError("First arc must be 0, 1 or 2");
|
||||||
|
}
|
||||||
|
if (arcs[0] < 2n && arcs[1] > 39n) {
|
||||||
|
throw new OperationError("Second arc must be ≤ 39 when first arc is 0 or 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
const values = [arcs[0] * 40n + arcs[1], ...arcs.slice(2)];
|
||||||
|
const out = [];
|
||||||
|
for (const v of values) {
|
||||||
|
if (v === 0n) {
|
||||||
|
out.push(0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const parts = [];
|
||||||
|
let n = v;
|
||||||
|
while (n > 0n) {
|
||||||
|
parts.unshift(Number(n & 0x7fn));
|
||||||
|
n >>= 7n;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < parts.length - 1; i++) parts[i] |= 0x80;
|
||||||
|
out.push(...parts);
|
||||||
|
}
|
||||||
|
return out.map(b => b.toString(16).padStart(2, "0")).join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap a hex-encoded DER blob in a PEM envelope.
|
||||||
|
*
|
||||||
|
* Uses LF line endings only (the old jsrsasign output used CRLF).
|
||||||
|
*
|
||||||
|
* Input parsing is intentionally lenient to match the previous jsrsasign
|
||||||
|
* behaviour: whitespace is stripped, an odd-length string is left-padded
|
||||||
|
* with a zero, and characters that are not hex digits are treated as the
|
||||||
|
* nibble `0`. This keeps the operation usable as a generic byte-emitter
|
||||||
|
* inside larger recipes where the upstream stage may not produce strict
|
||||||
|
* hex.
|
||||||
|
*
|
||||||
|
* @param {string} hex
|
||||||
|
* @param {string} label
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function derToPem(hex, label) {
|
||||||
|
let cleaned = hex.replace(/\s/g, "");
|
||||||
|
if (cleaned.length % 2 !== 0) cleaned = "0" + cleaned;
|
||||||
|
|
||||||
|
const bytes = new Uint8Array(cleaned.length / 2);
|
||||||
|
for (let i = 0; i < bytes.length; i++) {
|
||||||
|
const v = parseInt(cleaned.substr(i * 2, 2), 16);
|
||||||
|
bytes[i] = Number.isNaN(v) ? 0 : v;
|
||||||
|
}
|
||||||
|
|
||||||
|
let b64;
|
||||||
|
if (typeof Buffer !== "undefined") {
|
||||||
|
b64 = Buffer.from(bytes).toString("base64");
|
||||||
|
} else {
|
||||||
|
let bin = "";
|
||||||
|
for (const b of bytes) bin += String.fromCharCode(b);
|
||||||
|
b64 = btoa(bin);
|
||||||
|
}
|
||||||
|
|
||||||
|
const lines = b64.length === 0 ? [""] : b64.match(/.{1,64}/g);
|
||||||
|
return `-----BEGIN ${label}-----\n${lines.join("\n")}\n-----END ${label}-----\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Walk an asn1js parse tree and produce an indented dump similar to the
|
||||||
|
* one jsrsasign's ASN1HEX.dump produced.
|
||||||
|
*
|
||||||
|
* @param {string} hex
|
||||||
|
* @param {Object} [options]
|
||||||
|
* @param {number} [options.truncate=32] - max bytes of an OCTET/BIT/printable value shown before truncating
|
||||||
|
* @param {number} [options.startIndex=0] - hex-character offset to start parsing at
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function dumpAsn1Hex(hex, options = {}) {
|
||||||
|
const truncate = options.truncate ?? 32;
|
||||||
|
const startIndex = options.startIndex ?? 0;
|
||||||
|
|
||||||
|
let cleaned = hex.replace(/\s/g, "").toLowerCase();
|
||||||
|
if (cleaned.length % 2 !== 0) cleaned = "0" + cleaned;
|
||||||
|
const slice = cleaned.slice(startIndex);
|
||||||
|
const bytes = new Uint8Array(slice.length / 2);
|
||||||
|
for (let i = 0; i < bytes.length; i++) {
|
||||||
|
const v = parseInt(slice.substr(i * 2, 2), 16);
|
||||||
|
bytes[i] = Number.isNaN(v) ? 0 : v;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = fromBER(bytes.buffer);
|
||||||
|
const lines = [];
|
||||||
|
if (result.offset === -1 || !result.result || (result.result.error && !result.result.idBlock)) {
|
||||||
|
return `ASN.1 parse error: ${(result.result && result.result.error) || "unknown"}`;
|
||||||
|
}
|
||||||
|
formatAsn1Node(result.result, 0, truncate, lines);
|
||||||
|
if (result.offset === -1 && result.result && result.result.error) {
|
||||||
|
lines.push(`(parse warning: ${result.result.error})`);
|
||||||
|
}
|
||||||
|
return lines.join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively format one asn1js node.
|
||||||
|
*
|
||||||
|
* @param {Object} node
|
||||||
|
* @param {number} depth
|
||||||
|
* @param {number} truncate
|
||||||
|
* @param {string[]} out
|
||||||
|
*/
|
||||||
|
function formatAsn1Node(node, depth, truncate, out) {
|
||||||
|
const pad = " ".repeat(depth);
|
||||||
|
const idBlock = node.idBlock || {};
|
||||||
|
const valueBlock = node.valueBlock || {};
|
||||||
|
const tagClass = idBlock.tagClass;
|
||||||
|
const tagNumber = idBlock.tagNumber;
|
||||||
|
const isConstructed = !!idBlock.isConstructed;
|
||||||
|
|
||||||
|
if (tagClass !== 1) {
|
||||||
|
const className = ["", "UNIVERSAL", "APPLICATION", "CONTEXT", "PRIVATE"][tagClass] || "UNKNOWN";
|
||||||
|
const label = `[${className} ${tagNumber}]${isConstructed ? " (constructed)" : ""}`;
|
||||||
|
if (isConstructed && Array.isArray(valueBlock.value)) {
|
||||||
|
out.push(`${pad}${label}`);
|
||||||
|
for (const child of valueBlock.value) formatAsn1Node(child, depth + 1, truncate, out);
|
||||||
|
} else {
|
||||||
|
out.push(`${pad}${label} ${truncateHex(extractHex(valueBlock), truncate)}`);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ctorName = node.constructor && node.constructor.name;
|
||||||
|
switch (ctorName) {
|
||||||
|
case "Sequence":
|
||||||
|
out.push(`${pad}SEQUENCE`);
|
||||||
|
for (const child of valueBlock.value || []) formatAsn1Node(child, depth + 1, truncate, out);
|
||||||
|
return;
|
||||||
|
case "Set":
|
||||||
|
out.push(`${pad}SET`);
|
||||||
|
for (const child of valueBlock.value || []) formatAsn1Node(child, depth + 1, truncate, out);
|
||||||
|
return;
|
||||||
|
case "Null":
|
||||||
|
out.push(`${pad}NULL`);
|
||||||
|
return;
|
||||||
|
case "Boolean":
|
||||||
|
out.push(`${pad}BOOLEAN ${valueBlock.value ? "TRUE" : "FALSE"}`);
|
||||||
|
return;
|
||||||
|
case "Integer":
|
||||||
|
out.push(`${pad}INTEGER ${formatIntegerValue(valueBlock)}`);
|
||||||
|
return;
|
||||||
|
case "ObjectIdentifier":
|
||||||
|
out.push(`${pad}ObjectIdentifier ${valueBlock.toJSON().value}`);
|
||||||
|
return;
|
||||||
|
case "OctetString":
|
||||||
|
out.push(`${pad}OCTET STRING ${truncateHex(extractHex(valueBlock), truncate)}`);
|
||||||
|
return;
|
||||||
|
case "BitString":
|
||||||
|
out.push(`${pad}BIT STRING ${truncateHex(extractHex(valueBlock), truncate)}`);
|
||||||
|
return;
|
||||||
|
case "Utf8String":
|
||||||
|
case "PrintableString":
|
||||||
|
case "Ia5String":
|
||||||
|
case "IA5String":
|
||||||
|
case "VisibleString":
|
||||||
|
case "TeletexString":
|
||||||
|
case "UniversalString":
|
||||||
|
case "BmpString":
|
||||||
|
case "BMPString":
|
||||||
|
case "NumericString":
|
||||||
|
case "GeneralString":
|
||||||
|
case "CharacterString":
|
||||||
|
case "GraphicString":
|
||||||
|
case "VideotexString":
|
||||||
|
out.push(`${pad}${ctorName} "${truncateText(valueBlock.value || "", truncate)}"`);
|
||||||
|
return;
|
||||||
|
case "UTCTime":
|
||||||
|
case "GeneralizedTime":
|
||||||
|
out.push(`${pad}${ctorName} ${valueBlock.toString ? node.toString() : truncateHex(extractHex(valueBlock), truncate)}`);
|
||||||
|
return;
|
||||||
|
default: {
|
||||||
|
const label = ctorName || `UNIVERSAL ${tagNumber}`;
|
||||||
|
if (isConstructed && Array.isArray(valueBlock.value)) {
|
||||||
|
out.push(`${pad}${label}`);
|
||||||
|
for (const child of valueBlock.value) formatAsn1Node(child, depth + 1, truncate, out);
|
||||||
|
} else {
|
||||||
|
out.push(`${pad}${label} ${truncateHex(extractHex(valueBlock), truncate)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract the hex representation of an asn1js value block.
|
||||||
|
*
|
||||||
|
* @param {Object} valueBlock
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function extractHex(valueBlock) {
|
||||||
|
if (!valueBlock) return "";
|
||||||
|
const view = valueBlock.valueHexView;
|
||||||
|
if (view && view.length) return bufToHex(view);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a Uint8Array view to a lowercase hex string.
|
||||||
|
*
|
||||||
|
* @param {Uint8Array} buf
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function bufToHex(buf) {
|
||||||
|
let out = "";
|
||||||
|
for (const b of buf) out += b.toString(16).padStart(2, "0");
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Truncate hex string to at most `truncate` bytes, appending an ellipsis marker.
|
||||||
|
*
|
||||||
|
* @param {string} hex
|
||||||
|
* @param {number} truncate
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function truncateHex(hex, truncate) {
|
||||||
|
const maxChars = truncate * 2;
|
||||||
|
if (maxChars > 0 && hex.length > maxChars) {
|
||||||
|
return `${hex.slice(0, maxChars)}... (${hex.length / 2} bytes)`;
|
||||||
|
}
|
||||||
|
return hex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Truncate a text string to at most `truncate` characters.
|
||||||
|
*
|
||||||
|
* @param {string} text
|
||||||
|
* @param {number} truncate
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function truncateText(text, truncate) {
|
||||||
|
if (truncate > 0 && text.length > truncate) {
|
||||||
|
return `${text.slice(0, truncate)}... (${text.length} chars)`;
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format an Integer's value. asn1js exposes a small int as `valueDec`; for
|
||||||
|
* arbitrary-length ints we fall back to the raw hex view.
|
||||||
|
*
|
||||||
|
* @param {Object} valueBlock
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function formatIntegerValue(valueBlock) {
|
||||||
|
if (valueBlock.isHexOnly) return extractHex(valueBlock);
|
||||||
|
if (typeof valueBlock.valueDec === "number" && Number.isFinite(valueBlock.valueDec)) {
|
||||||
|
return valueBlock.valueDec.toString();
|
||||||
|
}
|
||||||
|
return extractHex(valueBlock);
|
||||||
|
}
|
||||||
@ -4,8 +4,8 @@
|
|||||||
* @license Apache-2.0
|
* @license Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import r from "jsrsasign";
|
|
||||||
import Operation from "../Operation.mjs";
|
import Operation from "../Operation.mjs";
|
||||||
|
import { oidHexToInt } from "../lib/Asn1.mjs";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hex to Object Identifier operation
|
* Hex to Object Identifier operation
|
||||||
@ -33,7 +33,7 @@ class HexToObjectIdentifier extends Operation {
|
|||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
run(input, args) {
|
run(input, args) {
|
||||||
return r.KJUR.asn1.ASN1Util.oidHexToInt(input.replace(/\s/g, ""));
|
return oidHexToInt(input.replace(/\s/g, ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,8 +4,8 @@
|
|||||||
* @license Apache-2.0
|
* @license Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import r from "jsrsasign";
|
|
||||||
import Operation from "../Operation.mjs";
|
import Operation from "../Operation.mjs";
|
||||||
|
import { derToPem } from "../lib/Asn1.mjs";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hex to PEM operation
|
* Hex to PEM operation
|
||||||
@ -39,7 +39,7 @@ class HexToPEM extends Operation {
|
|||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
run(input, args) {
|
run(input, args) {
|
||||||
return r.KJUR.asn1.ASN1Util.getPEMStringFromHex(input.replace(/\s/g, ""), args[0]);
|
return derToPem(input.replace(/\s/g, ""), args[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,8 +4,8 @@
|
|||||||
* @license Apache-2.0
|
* @license Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import r from "jsrsasign";
|
|
||||||
import Operation from "../Operation.mjs";
|
import Operation from "../Operation.mjs";
|
||||||
|
import { oidIntToHex } from "../lib/Asn1.mjs";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Object Identifier to Hex operation
|
* Object Identifier to Hex operation
|
||||||
@ -33,7 +33,7 @@ class ObjectIdentifierToHex extends Operation {
|
|||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
run(input, args) {
|
run(input, args) {
|
||||||
return r.KJUR.asn1.ASN1Util.oidIntToHex(input);
|
return oidIntToHex(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,8 +4,8 @@
|
|||||||
* @license Apache-2.0
|
* @license Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import r from "jsrsasign";
|
|
||||||
import Operation from "../Operation.mjs";
|
import Operation from "../Operation.mjs";
|
||||||
|
import { dumpAsn1Hex } from "../lib/Asn1.mjs";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse ASN.1 hex string operation
|
* Parse ASN.1 hex string operation
|
||||||
@ -45,9 +45,10 @@ class ParseASN1HexString extends Operation {
|
|||||||
*/
|
*/
|
||||||
run(input, args) {
|
run(input, args) {
|
||||||
const [index, truncateLen] = args;
|
const [index, truncateLen] = args;
|
||||||
return r.ASN1HEX.dump(input.replace(/\s/g, "").toLowerCase(), {
|
return dumpAsn1Hex(input.replace(/\s/g, "").toLowerCase(), {
|
||||||
"ommit_long_octet": truncateLen
|
truncate: truncateLen,
|
||||||
}, index);
|
startIndex: index,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -319,7 +319,7 @@ TestRegister.addApiTests([
|
|||||||
|
|
||||||
it("chef.bake: should take compact JSON format from Chef Website as recipe", async () => {
|
it("chef.bake: should take compact JSON format from Chef Website as recipe", async () => {
|
||||||
const result = await chef.bake("some input", [{"op": "To Morse Code", "args": ["Dash/Dot", "Backslash", "Comma"]}, {"op": "Hex to PEM", "args": ["SOMETHING"]}, {"op": "To Snake case", "args": [false]}]);
|
const result = await chef.bake("some input", [{"op": "To Morse Code", "args": ["Dash/Dot", "Backslash", "Comma"]}, {"op": "Hex to PEM", "args": ["SOMETHING"]}, {"op": "To Snake case", "args": [false]}]);
|
||||||
assert.strictEqual(result.toString(), "begin_something_anananaaaaak_da_aaak_da_aaaaananaaaaaaan_da_aaaaaaanan_da_aaak_end_something");
|
assert.strictEqual(result.toString(), "begin_something_dqanaaaa_cg_aka_ao_a_2_g_da_aaaaaaana_no_adqana_ao_a_cg_aaaaana_ao_a_2_g_a_end_something");
|
||||||
}),
|
}),
|
||||||
|
|
||||||
it("chef.bake: should accept Clean JSON format from Chef website as recipe", async () => {
|
it("chef.bake: should accept Clean JSON format from Chef website as recipe", async () => {
|
||||||
@ -331,7 +331,7 @@ TestRegister.addApiTests([
|
|||||||
{ "op": "To Snake case",
|
{ "op": "To Snake case",
|
||||||
"args": [false] }
|
"args": [false] }
|
||||||
]);
|
]);
|
||||||
assert.strictEqual(result.toString(), "begin_something_anananaaaaak_da_aaak_da_aaaaananaaaaaaan_da_aaaaaaanan_da_aaak_end_something");
|
assert.strictEqual(result.toString(), "begin_something_dqanaaaa_cg_aka_ao_a_2_g_da_aaaaaaana_no_adqana_ao_a_cg_aaaaana_ao_a_2_g_a_end_something");
|
||||||
}),
|
}),
|
||||||
|
|
||||||
it("chef.bake: should accept Clean JSON format from Chef website - args optional", async () => {
|
it("chef.bake: should accept Clean JSON format from Chef website - args optional", async () => {
|
||||||
@ -342,7 +342,7 @@ TestRegister.addApiTests([
|
|||||||
{ "op": "To Snake case",
|
{ "op": "To Snake case",
|
||||||
"args": [false] }
|
"args": [false] }
|
||||||
]);
|
]);
|
||||||
assert.strictEqual(result.toString(), "begin_something_aaaaaaaaaaaaaa_end_something");
|
assert.strictEqual(result.toString(), "begin_something_aaaaaaaaaaaaaaa_end_something");
|
||||||
}),
|
}),
|
||||||
|
|
||||||
it("chef.bake: should accept operation names from Chef Website which contain forward slash", async () => {
|
it("chef.bake: should accept operation names from Chef Website which contain forward slash", async () => {
|
||||||
@ -366,7 +366,7 @@ TestRegister.addApiTests([
|
|||||||
{ "op": "Parse ASN.1 hex string",
|
{ "op": "Parse ASN.1 hex string",
|
||||||
"args": [0, 32] }
|
"args": [0, 32] }
|
||||||
]);
|
]);
|
||||||
assert.strictEqual(result.toString(), `SEQUENCE\n INTEGER 05\n IA5String 'Anybody there?'\n`);
|
assert.strictEqual(result.toString(), `SEQUENCE\n INTEGER 5\n IA5String "Anybody there?"`);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
it("Excluded operations: throw a sensible error when you try and call one", () => {
|
it("Excluded operations: throw a sensible error when you try and call one", () => {
|
||||||
|
|||||||
@ -612,9 +612,10 @@ Password: 282760`;
|
|||||||
|
|
||||||
it("Hex to PEM", () => {
|
it("Hex to PEM", () => {
|
||||||
const result = chef.hexToPEM(chef.toHex("Yada Yada"));
|
const result = chef.hexToPEM(chef.toHex("Yada Yada"));
|
||||||
const expected = `-----BEGIN CERTIFICATE-----\r
|
const expected = `-----BEGIN CERTIFICATE-----
|
||||||
WWFkYSBZYWRh\r
|
WWFkYSBZYWRh
|
||||||
-----END CERTIFICATE-----\r\n`;
|
-----END CERTIFICATE-----
|
||||||
|
`;
|
||||||
assert.strictEqual(result.toString(), expected);
|
assert.strictEqual(result.toString(), expected);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
@ -647,7 +648,13 @@ WWFkYSBZYWRh\r
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
it("Parse ASN.1 Hex string", () => {
|
it("Parse ASN.1 Hex string", () => {
|
||||||
assert.strictEqual(chef.parseASN1HexString(chef.toHex("Mouth-watering")).toString(), "UNKNOWN(77) 7574682d7761746572696e67\n");
|
// The bytes for "Mouth-watering" don't form a well-formed ASN.1 structure
|
||||||
|
// (tag 0x4d declares length 0x6f but only 12 bytes follow), so we report
|
||||||
|
// a parse error rather than the partial best-effort dump jsrsasign emitted.
|
||||||
|
assert.strictEqual(
|
||||||
|
chef.parseASN1HexString(chef.toHex("Mouth-watering")).toString(),
|
||||||
|
"ASN.1 parse error: End of input reached before message was fully decoded (inconsistent offset and length values)"
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
it("Parse DateTime", () => {
|
it("Parse DateTime", () => {
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import { setLongTestFailure, logTestReport } from "../lib/utils.mjs";
|
|||||||
import TestRegister from "../lib/TestRegister.mjs";
|
import TestRegister from "../lib/TestRegister.mjs";
|
||||||
import "./tests/A1Z26CipherDecode.mjs";
|
import "./tests/A1Z26CipherDecode.mjs";
|
||||||
import "./tests/AESKeyWrap.mjs";
|
import "./tests/AESKeyWrap.mjs";
|
||||||
|
import "./tests/ASN1.mjs";
|
||||||
import "./tests/AnalyseUUID.mjs";
|
import "./tests/AnalyseUUID.mjs";
|
||||||
import "./tests/AlternatingCaps.mjs";
|
import "./tests/AlternatingCaps.mjs";
|
||||||
import "./tests/AvroToJSON.mjs";
|
import "./tests/AvroToJSON.mjs";
|
||||||
|
|||||||
95
tests/operations/tests/ASN1.mjs
Normal file
95
tests/operations/tests/ASN1.mjs
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
/**
|
||||||
|
* ASN.1 / OID / PEM tests.
|
||||||
|
*
|
||||||
|
* Covers the four operations migrated from jsrsasign to the in-house
|
||||||
|
* Asn1.mjs helper:
|
||||||
|
* - Hex to Object Identifier
|
||||||
|
* - Object Identifier to Hex
|
||||||
|
* - Hex to PEM
|
||||||
|
* - Parse ASN.1 hex string
|
||||||
|
*
|
||||||
|
* @author n1474335 [n1474335@gmail.com]
|
||||||
|
* @copyright Crown Copyright 2026
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import TestRegister from "../../lib/TestRegister.mjs";
|
||||||
|
|
||||||
|
TestRegister.addTests([
|
||||||
|
{
|
||||||
|
name: "Hex to Object Identifier: rsaEncryption (1.2.840.113549.1.1.1)",
|
||||||
|
input: "2a864886f70d010101",
|
||||||
|
expectedOutput: "1.2.840.113549.1.1.1",
|
||||||
|
recipeConfig: [{ op: "Hex to Object Identifier", args: [] }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Hex to Object Identifier: commonName (2.5.4.3)",
|
||||||
|
input: "550403",
|
||||||
|
expectedOutput: "2.5.4.3",
|
||||||
|
recipeConfig: [{ op: "Hex to Object Identifier", args: [] }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Hex to Object Identifier: Microsoft CTL signing (1.3.6.1.4.1.311.2.1.4) — multi-byte arc",
|
||||||
|
input: "2b060104018237020104",
|
||||||
|
expectedOutput: "1.3.6.1.4.1.311.2.1.4",
|
||||||
|
recipeConfig: [{ op: "Hex to Object Identifier", args: [] }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Hex to Object Identifier: handles whitespace in input",
|
||||||
|
input: "55 04 03",
|
||||||
|
expectedOutput: "2.5.4.3",
|
||||||
|
recipeConfig: [{ op: "Hex to Object Identifier", args: [] }],
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "Object Identifier to Hex: rsaEncryption",
|
||||||
|
input: "1.2.840.113549.1.1.1",
|
||||||
|
expectedOutput: "2a864886f70d010101",
|
||||||
|
recipeConfig: [{ op: "Object Identifier to Hex", args: [] }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Object Identifier to Hex: commonName",
|
||||||
|
input: "2.5.4.3",
|
||||||
|
expectedOutput: "550403",
|
||||||
|
recipeConfig: [{ op: "Object Identifier to Hex", args: [] }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Object Identifier to Hex: Microsoft CTL signing (multi-byte arc)",
|
||||||
|
input: "1.3.6.1.4.1.311.2.1.4",
|
||||||
|
expectedOutput: "2b060104018237020104",
|
||||||
|
recipeConfig: [{ op: "Object Identifier to Hex", args: [] }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Object Identifier to Hex: 2.999 (multi-byte first combined arc)",
|
||||||
|
input: "2.999",
|
||||||
|
expectedOutput: "8837",
|
||||||
|
recipeConfig: [{ op: "Object Identifier to Hex", args: [] }],
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "Hex to PEM: short payload",
|
||||||
|
input: "48656c6c6f",
|
||||||
|
expectedOutput: "-----BEGIN CERTIFICATE-----\nSGVsbG8=\n-----END CERTIFICATE-----\n",
|
||||||
|
recipeConfig: [{ op: "Hex to PEM", args: ["CERTIFICATE"] }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Hex to PEM: wraps at 64 base64 characters",
|
||||||
|
// 60 bytes -> 80 base64 chars (no padding) -> wraps after 64
|
||||||
|
input: "00".repeat(60),
|
||||||
|
expectedOutput: "-----BEGIN PUBLIC KEY-----\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAA\n-----END PUBLIC KEY-----\n",
|
||||||
|
recipeConfig: [{ op: "Hex to PEM", args: ["PUBLIC KEY"] }],
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "Parse ASN.1 hex string: simple SEQUENCE of two INTEGERs",
|
||||||
|
input: "3006020101020102",
|
||||||
|
expectedOutput: "SEQUENCE\n INTEGER 1\n INTEGER 2",
|
||||||
|
recipeConfig: [{ op: "Parse ASN.1 hex string", args: [0, 32] }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Parse ASN.1 hex string: SEQUENCE { OID, NULL }",
|
||||||
|
input: "300d06092a864886f70d0107010500",
|
||||||
|
expectedOutput: "SEQUENCE\n ObjectIdentifier 1.2.840.113549.1.7.1\n NULL",
|
||||||
|
recipeConfig: [{ op: "Parse ASN.1 hex string", args: [0, 32] }],
|
||||||
|
},
|
||||||
|
]);
|
||||||
Loading…
x
Reference in New Issue
Block a user