pull in did:peer verification (from endorser-ch)
This commit is contained in:
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
### Added
|
### Added
|
||||||
- Replacement of an existing file
|
- Replacement of an existing file
|
||||||
- Local resolver for did:ethr
|
- Local resolver for did:ethr
|
||||||
|
- Validation of did:peer JWANT
|
||||||
- Testing for file deletion
|
- Testing for file deletion
|
||||||
### Fixed
|
### Fixed
|
||||||
- Incorrect check for others who recorded same image
|
- Incorrect check for others who recorded same image
|
||||||
|
|||||||
@@ -6,8 +6,12 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.614.0",
|
"@aws-sdk/client-s3": "^3.614.0",
|
||||||
"@aws-sdk/lib-storage": "^3.614.0",
|
"@aws-sdk/lib-storage": "^3.614.0",
|
||||||
|
"@peculiar/asn1-ecc": "^2.3.8",
|
||||||
|
"@peculiar/asn1-schema": "^2.3.8",
|
||||||
|
"base64url": "^3.0.1",
|
||||||
|
"cbor-x": "^1.5.9",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"did-jwt": "^8.0.4",
|
"did-jwt": "^7.4.7",
|
||||||
"did-resolver": "^4.1.0",
|
"did-resolver": "^4.1.0",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"express": "^4.19.2",
|
"express": "^4.19.2",
|
||||||
|
|||||||
162
pnpm-lock.yaml
generated
162
pnpm-lock.yaml
generated
@@ -11,12 +11,24 @@ dependencies:
|
|||||||
'@aws-sdk/lib-storage':
|
'@aws-sdk/lib-storage':
|
||||||
specifier: ^3.614.0
|
specifier: ^3.614.0
|
||||||
version: 3.614.0(@aws-sdk/client-s3@3.614.0)
|
version: 3.614.0(@aws-sdk/client-s3@3.614.0)
|
||||||
|
'@peculiar/asn1-ecc':
|
||||||
|
specifier: ^2.3.8
|
||||||
|
version: 2.3.8
|
||||||
|
'@peculiar/asn1-schema':
|
||||||
|
specifier: ^2.3.8
|
||||||
|
version: 2.3.8
|
||||||
|
base64url:
|
||||||
|
specifier: ^3.0.1
|
||||||
|
version: 3.0.1
|
||||||
|
cbor-x:
|
||||||
|
specifier: ^1.5.9
|
||||||
|
version: 1.5.9
|
||||||
cors:
|
cors:
|
||||||
specifier: ^2.8.5
|
specifier: ^2.8.5
|
||||||
version: 2.8.5
|
version: 2.8.5
|
||||||
did-jwt:
|
did-jwt:
|
||||||
specifier: ^8.0.4
|
specifier: ^7.4.7
|
||||||
version: 8.0.4
|
version: 7.4.7
|
||||||
did-resolver:
|
did-resolver:
|
||||||
specifier: ^4.1.0
|
specifier: ^4.1.0
|
||||||
version: 4.1.0
|
version: 4.1.0
|
||||||
@@ -689,6 +701,54 @@ packages:
|
|||||||
tslib: 2.6.3
|
tslib: 2.6.3
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@cbor-extract/cbor-extract-darwin-arm64@2.2.0:
|
||||||
|
resolution: {integrity: sha512-P7swiOAdF7aSi0H+tHtHtr6zrpF3aAq/W9FXx5HektRvLTM2O89xCyXF3pk7pLc7QpaY7AoaE8UowVf9QBdh3w==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: false
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@cbor-extract/cbor-extract-darwin-x64@2.2.0:
|
||||||
|
resolution: {integrity: sha512-1liF6fgowph0JxBbYnAS7ZlqNYLf000Qnj4KjqPNW4GViKrEql2MgZnAsExhY9LSy8dnvA4C0qHEBgPrll0z0w==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: false
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@cbor-extract/cbor-extract-linux-arm64@2.2.0:
|
||||||
|
resolution: {integrity: sha512-rQvhNmDuhjTVXSPFLolmQ47/ydGOFXtbR7+wgkSY0bdOxCFept1hvg59uiLPT2fVDuJFuEy16EImo5tE2x3RsQ==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: false
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@cbor-extract/cbor-extract-linux-arm@2.2.0:
|
||||||
|
resolution: {integrity: sha512-QeBcBXk964zOytiedMPQNZr7sg0TNavZeuUCD6ON4vEOU/25+pLhNN6EDIKJ9VLTKaZ7K7EaAriyYQ1NQ05s/Q==}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: false
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@cbor-extract/cbor-extract-linux-x64@2.2.0:
|
||||||
|
resolution: {integrity: sha512-cWLAWtT3kNLHSvP4RKDzSTX9o0wvQEEAj4SKvhWuOVZxiDAeQazr9A+PSiRILK1VYMLeDml89ohxCnUNQNQNCw==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: false
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@cbor-extract/cbor-extract-win32-x64@2.2.0:
|
||||||
|
resolution: {integrity: sha512-l2M+Z8DO2vbvADOBNLbbh9y5ST1RY5sqkWOg/58GkUPBYou/cuNZ68SGQ644f1CvZ8kcOxyZtw06+dxWHIoN/w==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [win32]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: false
|
||||||
|
optional: true
|
||||||
|
|
||||||
/@cspotcode/source-map-support@0.8.1:
|
/@cspotcode/source-map-support@0.8.1:
|
||||||
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
|
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@@ -722,8 +782,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw==}
|
resolution: {integrity: sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@noble/ciphers@0.5.3:
|
/@noble/ciphers@0.4.1:
|
||||||
resolution: {integrity: sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w==}
|
resolution: {integrity: sha512-QCOA9cgf3Rc33owG0AYBB9wszz+Ul2kramWN8tXG44Gyciud/tbkEqvxRF/IpqQaBpRBNi9f4jdNxqB2CQCIXg==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@noble/curves@1.4.2:
|
/@noble/curves@1.4.2:
|
||||||
@@ -757,6 +817,33 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
/@peculiar/asn1-ecc@2.3.8:
|
||||||
|
resolution: {integrity: sha512-Ah/Q15y3A/CtxbPibiLM/LKcMbnLTdUdLHUgdpB5f60sSvGkXzxJCu5ezGTFHogZXWNX3KSmYqilCrfdmBc6pQ==}
|
||||||
|
dependencies:
|
||||||
|
'@peculiar/asn1-schema': 2.3.8
|
||||||
|
'@peculiar/asn1-x509': 2.3.8
|
||||||
|
asn1js: 3.0.5
|
||||||
|
tslib: 2.6.3
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@peculiar/asn1-schema@2.3.8:
|
||||||
|
resolution: {integrity: sha512-ULB1XqHKx1WBU/tTFIA+uARuRoBVZ4pNdOA878RDrRbBfBGcSzi5HBkdScC6ZbHn8z7L8gmKCgPC1LHRrP46tA==}
|
||||||
|
dependencies:
|
||||||
|
asn1js: 3.0.5
|
||||||
|
pvtsutils: 1.3.5
|
||||||
|
tslib: 2.6.3
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@peculiar/asn1-x509@2.3.8:
|
||||||
|
resolution: {integrity: sha512-voKxGfDU1c6r9mKiN5ZUsZWh3Dy1BABvTM3cimf0tztNwyMJPhiXY94eRTgsMQe6ViLfT6EoXxkWVzcm3mFAFw==}
|
||||||
|
dependencies:
|
||||||
|
'@peculiar/asn1-schema': 2.3.8
|
||||||
|
asn1js: 3.0.5
|
||||||
|
ipaddr.js: 2.2.0
|
||||||
|
pvtsutils: 1.3.5
|
||||||
|
tslib: 2.6.3
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@scure/base@1.1.7:
|
/@scure/base@1.1.7:
|
||||||
resolution: {integrity: sha512-PPNYBslrLNNUQ/Yad37MHYsNQtK67EhWb6WtSvNLLPo7SdVZgkUjD6Dg+5On7zNwmskf8OX7I7Nx5oN+MIWE0g==}
|
resolution: {integrity: sha512-PPNYBslrLNNUQ/Yad37MHYsNQtK67EhWb6WtSvNLLPo7SdVZgkUjD6Dg+5On7zNwmskf8OX7I7Nx5oN+MIWE0g==}
|
||||||
dev: false
|
dev: false
|
||||||
@@ -1384,6 +1471,15 @@ packages:
|
|||||||
safer-buffer: 2.1.2
|
safer-buffer: 2.1.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/asn1js@3.0.5:
|
||||||
|
resolution: {integrity: sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==}
|
||||||
|
engines: {node: '>=12.0.0'}
|
||||||
|
dependencies:
|
||||||
|
pvtsutils: 1.3.5
|
||||||
|
pvutils: 1.1.3
|
||||||
|
tslib: 2.6.3
|
||||||
|
dev: false
|
||||||
|
|
||||||
/assert-plus@1.0.0:
|
/assert-plus@1.0.0:
|
||||||
resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==}
|
resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==}
|
||||||
engines: {node: '>=0.8'}
|
engines: {node: '>=0.8'}
|
||||||
@@ -1409,6 +1505,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/base64url@3.0.1:
|
||||||
|
resolution: {integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==}
|
||||||
|
engines: {node: '>=6.0.0'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/bcrypt-pbkdf@1.0.2:
|
/bcrypt-pbkdf@1.0.2:
|
||||||
resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==}
|
resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -1556,6 +1657,28 @@ packages:
|
|||||||
resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==}
|
resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/cbor-extract@2.2.0:
|
||||||
|
resolution: {integrity: sha512-Ig1zM66BjLfTXpNgKpvBePq271BPOvu8MR0Jl080yG7Jsl+wAZunfrwiwA+9ruzm/WEdIV5QF/bjDZTqyAIVHA==}
|
||||||
|
hasBin: true
|
||||||
|
requiresBuild: true
|
||||||
|
dependencies:
|
||||||
|
node-gyp-build-optional-packages: 5.1.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@cbor-extract/cbor-extract-darwin-arm64': 2.2.0
|
||||||
|
'@cbor-extract/cbor-extract-darwin-x64': 2.2.0
|
||||||
|
'@cbor-extract/cbor-extract-linux-arm': 2.2.0
|
||||||
|
'@cbor-extract/cbor-extract-linux-arm64': 2.2.0
|
||||||
|
'@cbor-extract/cbor-extract-linux-x64': 2.2.0
|
||||||
|
'@cbor-extract/cbor-extract-win32-x64': 2.2.0
|
||||||
|
dev: false
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/cbor-x@1.5.9:
|
||||||
|
resolution: {integrity: sha512-OEI5rEu3MeR0WWNUXuIGkxmbXVhABP+VtgAXzm48c9ulkrsvxshjjk94XSOGphyAKeNGLPfAxxzEtgQ6rEVpYQ==}
|
||||||
|
optionalDependencies:
|
||||||
|
cbor-extract: 2.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/chokidar@3.6.0:
|
/chokidar@3.6.0:
|
||||||
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
|
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
|
||||||
engines: {node: '>= 8.10.0'}
|
engines: {node: '>= 8.10.0'}
|
||||||
@@ -1736,10 +1859,10 @@ packages:
|
|||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/did-jwt@8.0.4:
|
/did-jwt@7.4.7:
|
||||||
resolution: {integrity: sha512-KPtG7H+8GgKGMiDqFvOdNy5BBN3hpA+8xV7VygEnpst5oPIqjvcH3rTtnPF55a8bOxIzE2PudKGIXIQhekv7WA==}
|
resolution: {integrity: sha512-Apz7nIfIHSKWIMaEP5L/K8xkwByvjezjTG0xiqwKdnNj1x8M0+Yasury5Dm/KPltxi2PlGfRPf3IejRKZrT8mQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@noble/ciphers': 0.5.3
|
'@noble/ciphers': 0.4.1
|
||||||
'@noble/curves': 1.4.2
|
'@noble/curves': 1.4.2
|
||||||
'@noble/hashes': 1.4.0
|
'@noble/hashes': 1.4.0
|
||||||
'@scure/base': 1.1.7
|
'@scure/base': 1.1.7
|
||||||
@@ -2279,6 +2402,11 @@ packages:
|
|||||||
engines: {node: '>= 0.10'}
|
engines: {node: '>= 0.10'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/ipaddr.js@2.2.0:
|
||||||
|
resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/is-binary-path@2.1.0:
|
/is-binary-path@2.1.0:
|
||||||
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
|
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -2605,6 +2733,15 @@ packages:
|
|||||||
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
|
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/node-gyp-build-optional-packages@5.1.1:
|
||||||
|
resolution: {integrity: sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw==}
|
||||||
|
hasBin: true
|
||||||
|
requiresBuild: true
|
||||||
|
dependencies:
|
||||||
|
detect-libc: 2.0.3
|
||||||
|
dev: false
|
||||||
|
optional: true
|
||||||
|
|
||||||
/node-gyp@8.4.1:
|
/node-gyp@8.4.1:
|
||||||
resolution: {integrity: sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==}
|
resolution: {integrity: sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==}
|
||||||
engines: {node: '>= 10.12.0'}
|
engines: {node: '>= 10.12.0'}
|
||||||
@@ -2810,6 +2947,17 @@ packages:
|
|||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/pvtsutils@1.3.5:
|
||||||
|
resolution: {integrity: sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA==}
|
||||||
|
dependencies:
|
||||||
|
tslib: 2.6.3
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/pvutils@1.1.3:
|
||||||
|
resolution: {integrity: sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==}
|
||||||
|
engines: {node: '>=6.0.0'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/qs@6.11.0:
|
/qs@6.11.0:
|
||||||
resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==}
|
resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==}
|
||||||
engines: {node: '>=0.6'}
|
engines: {node: '>=0.6'}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
//@ts-nocheck
|
//@ts-nocheck
|
||||||
|
|
||||||
const { DeleteObjectCommand, PutObjectCommand, S3Client } = require('@aws-sdk/client-s3');
|
const { DeleteObjectCommand, PutObjectCommand, S3Client } = require('@aws-sdk/client-s3');
|
||||||
const cors = require('cors');
|
const cors = require('cors');
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
@@ -11,7 +12,7 @@ const multer = require('multer');
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const sqlite3 = require('sqlite3').verbose();
|
const sqlite3 = require('sqlite3').verbose();
|
||||||
|
|
||||||
const { didEthLocalResolver } = require("./vc/did-eth-local-resolver");
|
import { decodeAndVerifyJwt } from "./vc";
|
||||||
|
|
||||||
require('dotenv').config()
|
require('dotenv').config()
|
||||||
|
|
||||||
@@ -24,8 +25,6 @@ const dbFile = process.env.SQLITE_FILE || './image-db.sqlite';
|
|||||||
const bucketName = process.env.S3_BUCKET_NAME || 'gifts-image-test';
|
const bucketName = process.env.S3_BUCKET_NAME || 'gifts-image-test';
|
||||||
const imageServer = process.env.DOWNLOAD_IMAGE_SERVER || 'test-image.timesafari.app';
|
const imageServer = process.env.DOWNLOAD_IMAGE_SERVER || 'test-image.timesafari.app';
|
||||||
|
|
||||||
const resolver = new Resolver({ 'ethr': didEthLocalResolver });
|
|
||||||
|
|
||||||
// Open a connection to the SQLite database
|
// Open a connection to the SQLite database
|
||||||
const db = new sqlite3.Database(dbFile, (err) => {
|
const db = new sqlite3.Database(dbFile, (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -502,7 +501,7 @@ async function decodeJwt(req, res) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
const jwt = auth.substring('Bearer '.length);
|
const jwt = auth.substring('Bearer '.length);
|
||||||
const verified = await didJwt.verifyJWT(jwt, { resolver });
|
const verified = await decodeAndVerifyJwt(jwt);
|
||||||
if (!verified.verified) {
|
if (!verified.verified) {
|
||||||
const errorTime = new Date().toISOString();
|
const errorTime = new Date().toISOString();
|
||||||
console.error(errorTime, 'Got invalid JWT in Authorization header:', verified);
|
console.error(errorTime, 'Got invalid JWT in Authorization header:', verified);
|
||||||
|
|||||||
8
src/vc/did-eth-local-resolver.ts
Normal file
8
src/vc/did-eth-local-resolver.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import {DIDResolutionResult} from "did-resolver";
|
||||||
|
|
||||||
|
declare module './did-eth-local-resolver.js' {
|
||||||
|
const value: {
|
||||||
|
didEthLocalResolver: (jwt: string) => Promise<DIDResolutionResult>;
|
||||||
|
};
|
||||||
|
export default value;
|
||||||
|
}
|
||||||
139
src/vc/didPeer.ts
Normal file
139
src/vc/didPeer.ts
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
import { AsnParser } from "@peculiar/asn1-schema";
|
||||||
|
import { ECDSASigValue } from "@peculiar/asn1-ecc";
|
||||||
|
import crypto from "crypto";
|
||||||
|
import { decode as cborDecode } from "cbor-x";
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* similar code is in crowd-funder-for-time-pwa libs/crypto/vc/passkeyDidPeer.ts verifyJwtWebCrypto
|
||||||
|
*
|
||||||
|
* @returns {Promise<boolean>}
|
||||||
|
*/
|
||||||
|
export async function verifyPeerSignature(
|
||||||
|
payloadBytes: Uint8Array,
|
||||||
|
publicKeyBytes: Uint8Array,
|
||||||
|
signatureBytes: Uint8Array
|
||||||
|
) {
|
||||||
|
// this simple approach doesn't work
|
||||||
|
//const verify = crypto.createVerify('sha256')
|
||||||
|
//verify.update(preimage)
|
||||||
|
//const result = verify.verify(publicKey, signature)
|
||||||
|
|
||||||
|
const finalSignatureBuffer = unwrapEC2Signature(signatureBytes);
|
||||||
|
const verifyAlgorithm = {
|
||||||
|
name: "ECDSA",
|
||||||
|
hash: { name: "SHA-256" },
|
||||||
|
};
|
||||||
|
const publicKeyJwk = cborToKeys(publicKeyBytes).publicKeyJwk;
|
||||||
|
const keyAlgorithm = {
|
||||||
|
name: "ECDSA",
|
||||||
|
namedCurve: publicKeyJwk.crv,
|
||||||
|
};
|
||||||
|
const publicKeyCryptoKey = await crypto.subtle.importKey(
|
||||||
|
"jwk",
|
||||||
|
publicKeyJwk,
|
||||||
|
keyAlgorithm,
|
||||||
|
false,
|
||||||
|
["verify"],
|
||||||
|
);
|
||||||
|
const verified = await crypto.subtle.verify(
|
||||||
|
verifyAlgorithm,
|
||||||
|
publicKeyCryptoKey,
|
||||||
|
finalSignatureBuffer,
|
||||||
|
payloadBytes,
|
||||||
|
);
|
||||||
|
return verified;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cborToKeys(publicKeyBytes: Uint8Array) {
|
||||||
|
const jwkObj = cborDecode(publicKeyBytes);
|
||||||
|
if (
|
||||||
|
jwkObj[1] != 2 || // kty "EC"
|
||||||
|
jwkObj[3] != -7 || // alg "ES256"
|
||||||
|
jwkObj[-1] != 1 || // crv "P-256"
|
||||||
|
jwkObj[-2].length != 32 || // x
|
||||||
|
jwkObj[-3].length != 32 // y
|
||||||
|
) {
|
||||||
|
throw new Error("Unable to extract key.");
|
||||||
|
}
|
||||||
|
const publicKeyJwk = {
|
||||||
|
alg: "ES256",
|
||||||
|
crv: "P-256",
|
||||||
|
kty: "EC",
|
||||||
|
x: arrayToBase64Url(jwkObj[-2]),
|
||||||
|
y: arrayToBase64Url(jwkObj[-3]),
|
||||||
|
};
|
||||||
|
const publicKeyBuffer = Buffer.concat([
|
||||||
|
Buffer.from(jwkObj[-2]),
|
||||||
|
Buffer.from(jwkObj[-3]),
|
||||||
|
]);
|
||||||
|
return { publicKeyJwk, publicKeyBuffer };
|
||||||
|
}
|
||||||
|
|
||||||
|
function toBase64Url(anythingB64: string) {
|
||||||
|
return anythingB64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function arrayToBase64Url(anything: Uint8Array) {
|
||||||
|
return toBase64Url(Buffer.from(anything).toString("base64"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In WebAuthn, EC2 signatures are wrapped in ASN.1 structure so we need to peel r and s apart.
|
||||||
|
*
|
||||||
|
* See https://www.w3.org/TR/webauthn-2/#sctn-signature-attestation-types
|
||||||
|
*
|
||||||
|
* @return Uint8Array of the signature inside
|
||||||
|
*/
|
||||||
|
function unwrapEC2Signature(signature: Uint8Array) {
|
||||||
|
const parsedSignature = AsnParser.parse(signature, ECDSASigValue);
|
||||||
|
let rBytes = new Uint8Array(parsedSignature.r);
|
||||||
|
let sBytes = new Uint8Array(parsedSignature.s);
|
||||||
|
|
||||||
|
if (shouldRemoveLeadingZero(rBytes)) {
|
||||||
|
rBytes = rBytes.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldRemoveLeadingZero(sBytes)) {
|
||||||
|
sBytes = sBytes.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const finalSignature = isoUint8ArrayConcat([rBytes, sBytes]);
|
||||||
|
|
||||||
|
return finalSignature;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the DER-specific `00` byte at the start of an ECDSA signature byte sequence
|
||||||
|
* should be removed based on the following logic:
|
||||||
|
*
|
||||||
|
* "If the leading byte is 0x0, and the high order bit on the second byte is not set to 0,
|
||||||
|
* then remove the leading 0x0 byte"
|
||||||
|
*
|
||||||
|
* @return true if leading zero should be removed
|
||||||
|
*/
|
||||||
|
function shouldRemoveLeadingZero(bytes: Uint8Array) {
|
||||||
|
return bytes[0] === 0x0 && (bytes[1] & (1 << 7)) !== 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// from https://github.com/MasterKale/SimpleWebAuthn/blob/master/packages/server/src/helpers/iso/isoUint8Array.ts#L49
|
||||||
|
/**
|
||||||
|
* Combine multiple Uint8Arrays into a single Uint8Array
|
||||||
|
*
|
||||||
|
* @param arrays - Uint8Array[]
|
||||||
|
* @return Uint8Array
|
||||||
|
*/
|
||||||
|
function isoUint8ArrayConcat(arrays: Uint8Array[]) {
|
||||||
|
let pointer = 0;
|
||||||
|
const totalLength = arrays.reduce((prev, curr) => prev + curr.length, 0);
|
||||||
|
|
||||||
|
const toReturn = new Uint8Array(totalLength);
|
||||||
|
|
||||||
|
arrays.forEach((arr) => {
|
||||||
|
toReturn.set(arr, pointer);
|
||||||
|
pointer += arr.length;
|
||||||
|
});
|
||||||
|
|
||||||
|
return toReturn;
|
||||||
|
}
|
||||||
@@ -5,3 +5,84 @@
|
|||||||
* Other projects: endorser-ch, crowd-funder-for-time-pwa
|
* Other projects: endorser-ch, crowd-funder-for-time-pwa
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import base64url from "base64url";
|
||||||
|
import didJwt from "did-jwt";
|
||||||
|
import {Resolver} from "did-resolver";
|
||||||
|
|
||||||
|
import ethResolver from "./did-eth-local-resolver";
|
||||||
|
import {verifyJwt as peerVerifyJwt} from "./passkeyDidPeer";
|
||||||
|
|
||||||
|
|
||||||
|
export const TEST_BYPASS_ENV_VALUE = "test-local";
|
||||||
|
export const ETHR_DID_PREFIX = 'did:ethr:'
|
||||||
|
export const PEER_DID_PREFIX = 'did:peer:'
|
||||||
|
export const JWT_VERIFY_FAILED_CODE = "JWT_VERIFY_FAILED_CODE"
|
||||||
|
export const UNSUPPORTED_DID_METHOD_CODE = "UNSUPPORTED_DID_METHOD"
|
||||||
|
|
||||||
|
const resolver = new Resolver({
|
||||||
|
'ethr': ethResolver.didEthLocalResolver
|
||||||
|
});
|
||||||
|
|
||||||
|
// return Promise of at least { issuer, payload, verified boolean }
|
||||||
|
// ... and also if successfully verified by did-jwt (not JWANT): data, doc, signature, signer
|
||||||
|
export async function decodeAndVerifyJwt(jwt: string) {
|
||||||
|
const pieces = jwt.split('.')
|
||||||
|
const header = JSON.parse(base64url.decode(pieces[0]))
|
||||||
|
const payload = JSON.parse(base64url.decode(pieces[1]))
|
||||||
|
const issuerDid = payload.iss
|
||||||
|
if (!issuerDid) {
|
||||||
|
return Promise.reject({
|
||||||
|
clientError: {
|
||||||
|
message: `Missing "iss" field in JWT.`,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (issuerDid && issuerDid.startsWith(ETHR_DID_PREFIX) && process.env.NODE_ENV === TEST_BYPASS_ENV_VALUE) {
|
||||||
|
// Error of "Cannot read property 'toString' of undefined" usually means the JWT is malformed
|
||||||
|
// eg. no "." separators.
|
||||||
|
let nowEpoch = Math.floor(new Date().getTime() / 1000)
|
||||||
|
if (payload.exp < nowEpoch) {
|
||||||
|
console.log("JWT with exp " + payload.exp
|
||||||
|
+ " has expired but we're in test mode so we'll use a new time."
|
||||||
|
)
|
||||||
|
payload.exp = nowEpoch + 100
|
||||||
|
}
|
||||||
|
return { issuer: issuerDid, payload, verified: true } // other elements will = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
if (issuerDid.startsWith(ETHR_DID_PREFIX)) {
|
||||||
|
try {
|
||||||
|
let verified = await didJwt.verifyJWT(jwt, {resolver})
|
||||||
|
return verified
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
return Promise.reject({
|
||||||
|
clientError: {
|
||||||
|
message: `JWT failed verification: ` + e,
|
||||||
|
code: JWT_VERIFY_FAILED_CODE
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (issuerDid.startsWith(PEER_DID_PREFIX) && header.typ === "JWANT") {
|
||||||
|
const { claimPayload, verified } = await peerVerifyJwt(payload, issuerDid, pieces[2])
|
||||||
|
return { issuer: issuerDid, payload: claimPayload, verified: verified }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (issuerDid.startsWith(PEER_DID_PREFIX)) {
|
||||||
|
return Promise.reject({
|
||||||
|
clientError: {
|
||||||
|
message: `JWT with a PEER DID currently only supported with typ == JWANT. Contact us us for JWT suport since it should be straightforward.`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject({
|
||||||
|
clientError: {
|
||||||
|
message: `Unsupported DID method ${issuerDid}`,
|
||||||
|
code: UNSUPPORTED_DID_METHOD_CODE
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
104
src/vc/passkeyDidPeer.ts
Normal file
104
src/vc/passkeyDidPeer.ts
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
import crypto from "crypto";
|
||||||
|
import didJwt from "did-jwt";
|
||||||
|
|
||||||
|
import {PEER_DID_PREFIX, TEST_BYPASS_ENV_VALUE} from "./index";
|
||||||
|
import {verifyPeerSignature} from "./didPeer";
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param payload
|
||||||
|
* @param issuerDid
|
||||||
|
* @param signatureString
|
||||||
|
* @returns {Promise<{claimPayload: string, verified: boolean}>}
|
||||||
|
*/
|
||||||
|
export async function verifyJwt(payload: any, issuerDid: any, signatureString: any) {
|
||||||
|
if (!payload.iss) {
|
||||||
|
return Promise.reject({
|
||||||
|
clientError: {
|
||||||
|
message: `JWT is missing an "iss" field.`,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
let nowEpoch = Math.floor(new Date().getTime() / 1000)
|
||||||
|
if (!payload.exp) {
|
||||||
|
return Promise.reject({
|
||||||
|
clientError: {
|
||||||
|
message: `JWT with is missing an "exp" field.`,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (payload.exp < nowEpoch && process.env.NODE_ENV !== TEST_BYPASS_ENV_VALUE) {
|
||||||
|
return Promise.reject({
|
||||||
|
clientError: {
|
||||||
|
message: `JWT with exp ${payload.exp} has expired.`,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const authData: string = payload.AuthenticationDataB64URL
|
||||||
|
const clientData: string = payload.ClientDataJSONB64URL
|
||||||
|
if (!authData || !clientData) {
|
||||||
|
return Promise.reject({
|
||||||
|
clientError: {
|
||||||
|
message: `JWT with typ == JWANT requires AuthenticationData and ClientDataJSON.`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const decodedAuthDataBuff = Buffer.from(authData, 'base64url')
|
||||||
|
const decodedClientData = Buffer.from(clientData, 'base64url')
|
||||||
|
|
||||||
|
let claimPayload = JSON.parse(decodedClientData.toString())
|
||||||
|
if (claimPayload.challenge) {
|
||||||
|
claimPayload = JSON.parse(Buffer.from(claimPayload.challenge, "base64url").toString())
|
||||||
|
if (!claimPayload.exp) {
|
||||||
|
claimPayload.exp = payload.exp
|
||||||
|
}
|
||||||
|
if (!claimPayload.iat) {
|
||||||
|
claimPayload.iat = payload.iat
|
||||||
|
}
|
||||||
|
if (!claimPayload.iss) {
|
||||||
|
claimPayload.iss = payload.iss
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!claimPayload.exp) {
|
||||||
|
return Promise.reject({
|
||||||
|
clientError: {
|
||||||
|
message: `JWT client data challenge is missing an "exp" field.`,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (claimPayload.exp < nowEpoch && process.env.NODE_ENV !== TEST_BYPASS_ENV_VALUE) {
|
||||||
|
return Promise.reject({
|
||||||
|
clientError: {
|
||||||
|
message: `JWT client data challenge exp time is past.`,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (claimPayload.exp !== payload.exp) {
|
||||||
|
return Promise.reject({
|
||||||
|
clientError: {
|
||||||
|
message: `JWT client data challenge "exp" field doesn't match the outside payload "exp".`,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (claimPayload.iss !== payload.iss) {
|
||||||
|
return Promise.reject({
|
||||||
|
clientError: {
|
||||||
|
message: `JWT client data challenge "iss" field doesn't match the outside payload "iss".`,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const hashedClientDataBuff = crypto.createHash("sha256")
|
||||||
|
.update(decodedClientData)
|
||||||
|
.digest()
|
||||||
|
const preimage = new Uint8Array(Buffer.concat([decodedAuthDataBuff, hashedClientDataBuff]))
|
||||||
|
const PEER_DID_MULTIBASE_PREFIX = PEER_DID_PREFIX + "0"
|
||||||
|
// Uint8Array
|
||||||
|
const publicKey = didJwt.multibaseToBytes(issuerDid.substring(PEER_DID_MULTIBASE_PREFIX.length));
|
||||||
|
const signature = new Uint8Array(Buffer.from(signatureString, 'base64url'))
|
||||||
|
const verified = await verifyPeerSignature(preimage, publicKey, signature)
|
||||||
|
return { claimPayload, verified }
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user