@pagopa/io-react-native-wallet 2.0.0-next.1 → 2.0.0-next.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/credential/issuance/02-evaluate-issuer-trust.js +2 -2
- package/lib/commonjs/credential/issuance/02-evaluate-issuer-trust.js.map +1 -1
- package/lib/commonjs/credential/issuance/03-start-user-authorization.js +38 -24
- package/lib/commonjs/credential/issuance/03-start-user-authorization.js.map +1 -1
- package/lib/commonjs/credential/issuance/05-authorize-access.js +6 -10
- package/lib/commonjs/credential/issuance/05-authorize-access.js.map +1 -1
- package/lib/commonjs/credential/issuance/06-obtain-credential.js +43 -11
- package/lib/commonjs/credential/issuance/06-obtain-credential.js.map +1 -1
- package/lib/commonjs/credential/issuance/07-verify-and-parse-credential.js +51 -48
- package/lib/commonjs/credential/issuance/07-verify-and-parse-credential.js.map +1 -1
- package/lib/commonjs/credential/issuance/README.md +34 -13
- package/lib/commonjs/credential/issuance/const.js +1 -1
- package/lib/commonjs/credential/issuance/types.js +16 -10
- package/lib/commonjs/credential/issuance/types.js.map +1 -1
- package/lib/commonjs/credential/presentation/02-evaluate-rp-trust.js +2 -2
- package/lib/commonjs/credential/presentation/02-evaluate-rp-trust.js.map +1 -1
- package/lib/commonjs/credential/presentation/05-verify-request-object.js.map +1 -1
- package/lib/commonjs/credential/presentation/07-evaluate-dcql-query.js +4 -4
- package/lib/commonjs/credential/presentation/07-evaluate-input-descriptor.js +3 -3
- package/lib/commonjs/credential/presentation/08-send-authorization-response.js.map +1 -1
- package/lib/commonjs/credential/status/README.md +0 -1
- package/lib/commonjs/sd-jwt/__test__/index.test.js +11 -15
- package/lib/commonjs/sd-jwt/__test__/index.test.js.map +1 -1
- package/lib/commonjs/sd-jwt/__test__/types.test.js +5 -2
- package/lib/commonjs/sd-jwt/__test__/types.test.js.map +1 -1
- package/lib/commonjs/sd-jwt/__test__/utils.test.js +37 -0
- package/lib/commonjs/sd-jwt/__test__/utils.test.js.map +1 -0
- package/lib/commonjs/sd-jwt/index.js +20 -0
- package/lib/commonjs/sd-jwt/index.js.map +1 -1
- package/lib/commonjs/sd-jwt/types.js +51 -4
- package/lib/commonjs/sd-jwt/types.js.map +1 -1
- package/lib/commonjs/sd-jwt/utils.js +64 -0
- package/lib/commonjs/sd-jwt/utils.js.map +1 -0
- package/lib/commonjs/trust/build-chain.js +252 -0
- package/lib/commonjs/trust/build-chain.js.map +1 -0
- package/lib/commonjs/trust/index.js +11 -282
- package/lib/commonjs/trust/index.js.map +1 -1
- package/lib/commonjs/trust/types.js +18 -13
- package/lib/commonjs/trust/types.js.map +1 -1
- package/lib/commonjs/trust/{chain.js → verify-chain.js} +40 -5
- package/lib/commonjs/trust/verify-chain.js.map +1 -0
- package/lib/commonjs/utils/errors.js.map +1 -1
- package/lib/commonjs/utils/par.js +32 -22
- package/lib/commonjs/utils/par.js.map +1 -1
- package/lib/commonjs/utils/pop.js +1 -1
- package/lib/commonjs/utils/pop.js.map +1 -1
- package/lib/commonjs/wallet-instance-attestation/types.js +5 -1
- package/lib/commonjs/wallet-instance-attestation/types.js.map +1 -1
- package/lib/module/credential/issuance/02-evaluate-issuer-trust.js +1 -1
- package/lib/module/credential/issuance/02-evaluate-issuer-trust.js.map +1 -1
- package/lib/module/credential/issuance/03-start-user-authorization.js +38 -24
- package/lib/module/credential/issuance/03-start-user-authorization.js.map +1 -1
- package/lib/module/credential/issuance/05-authorize-access.js +6 -10
- package/lib/module/credential/issuance/05-authorize-access.js.map +1 -1
- package/lib/module/credential/issuance/06-obtain-credential.js +44 -12
- package/lib/module/credential/issuance/06-obtain-credential.js.map +1 -1
- package/lib/module/credential/issuance/07-verify-and-parse-credential.js +51 -48
- package/lib/module/credential/issuance/07-verify-and-parse-credential.js.map +1 -1
- package/lib/module/credential/issuance/README.md +34 -13
- package/lib/module/credential/issuance/const.js +1 -1
- package/lib/module/credential/issuance/types.js +12 -8
- package/lib/module/credential/issuance/types.js.map +1 -1
- package/lib/module/credential/presentation/02-evaluate-rp-trust.js +1 -1
- package/lib/module/credential/presentation/02-evaluate-rp-trust.js.map +1 -1
- package/lib/module/credential/presentation/05-verify-request-object.js.map +1 -1
- package/lib/module/credential/presentation/07-evaluate-dcql-query.js +4 -4
- package/lib/module/credential/presentation/07-evaluate-input-descriptor.js +3 -3
- package/lib/module/credential/presentation/08-send-authorization-response.js +1 -1
- package/lib/module/credential/presentation/08-send-authorization-response.js.map +1 -1
- package/lib/module/credential/status/README.md +0 -1
- package/lib/module/sd-jwt/__test__/index.test.js +11 -16
- package/lib/module/sd-jwt/__test__/index.test.js.map +1 -1
- package/lib/module/sd-jwt/__test__/types.test.js +5 -2
- package/lib/module/sd-jwt/__test__/types.test.js.map +1 -1
- package/lib/module/sd-jwt/__test__/utils.test.js +35 -0
- package/lib/module/sd-jwt/__test__/utils.test.js.map +1 -0
- package/lib/module/sd-jwt/index.js +1 -0
- package/lib/module/sd-jwt/index.js.map +1 -1
- package/lib/module/sd-jwt/types.js +50 -3
- package/lib/module/sd-jwt/types.js.map +1 -1
- package/lib/module/sd-jwt/utils.js +57 -0
- package/lib/module/sd-jwt/utils.js.map +1 -0
- package/lib/module/trust/build-chain.js +235 -0
- package/lib/module/trust/build-chain.js.map +1 -0
- package/lib/module/trust/index.js +5 -268
- package/lib/module/trust/index.js.map +1 -1
- package/lib/module/trust/types.js +18 -13
- package/lib/module/trust/types.js.map +1 -1
- package/lib/module/trust/{chain.js → verify-chain.js} +36 -2
- package/lib/module/trust/verify-chain.js.map +1 -0
- package/lib/module/utils/errors.js +1 -1
- package/lib/module/utils/errors.js.map +1 -1
- package/lib/module/utils/par.js +29 -20
- package/lib/module/utils/par.js.map +1 -1
- package/lib/module/utils/pop.js +1 -1
- package/lib/module/utils/pop.js.map +1 -1
- package/lib/module/wallet-instance-attestation/types.js +5 -1
- package/lib/module/wallet-instance-attestation/types.js.map +1 -1
- package/lib/typescript/client/generated/wallet-provider.d.ts +12 -12
- package/lib/typescript/credential/issuance/01-start-flow.d.ts +2 -2
- package/lib/typescript/credential/issuance/01-start-flow.d.ts.map +1 -1
- package/lib/typescript/credential/issuance/02-evaluate-issuer-trust.d.ts.map +1 -1
- package/lib/typescript/credential/issuance/03-start-user-authorization.d.ts +7 -6
- package/lib/typescript/credential/issuance/03-start-user-authorization.d.ts.map +1 -1
- package/lib/typescript/credential/issuance/05-authorize-access.d.ts.map +1 -1
- package/lib/typescript/credential/issuance/06-obtain-credential.d.ts +10 -5
- package/lib/typescript/credential/issuance/06-obtain-credential.d.ts.map +1 -1
- package/lib/typescript/credential/issuance/07-verify-and-parse-credential.d.ts +3 -2
- package/lib/typescript/credential/issuance/07-verify-and-parse-credential.d.ts.map +1 -1
- package/lib/typescript/credential/issuance/const.d.ts +1 -1
- package/lib/typescript/credential/issuance/types.d.ts +46 -26
- package/lib/typescript/credential/issuance/types.d.ts.map +1 -1
- package/lib/typescript/credential/presentation/02-evaluate-rp-trust.d.ts.map +1 -1
- package/lib/typescript/credential/presentation/05-verify-request-object.d.ts +1 -1
- package/lib/typescript/credential/presentation/05-verify-request-object.d.ts.map +1 -1
- package/lib/typescript/credential/presentation/08-send-authorization-response.d.ts +2 -2
- package/lib/typescript/credential/presentation/08-send-authorization-response.d.ts.map +1 -1
- package/lib/typescript/credential/presentation/types.d.ts +4 -4
- package/lib/typescript/pid/sd-jwt/types.d.ts +7 -7
- package/lib/typescript/sd-jwt/__test__/utils.test.d.ts +2 -0
- package/lib/typescript/sd-jwt/__test__/utils.test.d.ts.map +1 -0
- package/lib/typescript/sd-jwt/index.d.ts +21 -8
- package/lib/typescript/sd-jwt/index.d.ts.map +1 -1
- package/lib/typescript/sd-jwt/types.d.ts +194 -12
- package/lib/typescript/sd-jwt/types.d.ts.map +1 -1
- package/lib/typescript/sd-jwt/utils.d.ts +18 -0
- package/lib/typescript/sd-jwt/utils.d.ts.map +1 -0
- package/lib/typescript/trust/build-chain.d.ts +1300 -0
- package/lib/typescript/trust/build-chain.d.ts.map +1 -0
- package/lib/typescript/trust/index.d.ts +5 -1301
- package/lib/typescript/trust/index.d.ts.map +1 -1
- package/lib/typescript/trust/types.d.ts +788 -624
- package/lib/typescript/trust/types.d.ts.map +1 -1
- package/lib/typescript/trust/{chain.d.ts → verify-chain.d.ts} +17 -1
- package/lib/typescript/trust/verify-chain.d.ts.map +1 -0
- package/lib/typescript/utils/errors.d.ts +2 -2
- package/lib/typescript/utils/errors.d.ts.map +1 -1
- package/lib/typescript/utils/par.d.ts +29 -13
- package/lib/typescript/utils/par.d.ts.map +1 -1
- package/lib/typescript/wallet-instance-attestation/types.d.ts +9 -9
- package/lib/typescript/wallet-instance-attestation/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/credential/issuance/01-start-flow.ts +2 -2
- package/src/credential/issuance/02-evaluate-issuer-trust.ts +1 -1
- package/src/credential/issuance/03-start-user-authorization.ts +57 -38
- package/src/credential/issuance/05-authorize-access.ts +5 -11
- package/src/credential/issuance/06-obtain-credential.ts +53 -23
- package/src/credential/issuance/07-verify-and-parse-credential.ts +54 -62
- package/src/credential/issuance/README.md +34 -13
- package/src/credential/issuance/const.ts +1 -1
- package/src/credential/issuance/types.ts +18 -8
- package/src/credential/presentation/02-evaluate-rp-trust.ts +1 -1
- package/src/credential/presentation/05-verify-request-object.ts +1 -1
- package/src/credential/presentation/07-evaluate-dcql-query.ts +4 -4
- package/src/credential/presentation/07-evaluate-input-descriptor.ts +3 -3
- package/src/credential/presentation/08-send-authorization-response.ts +4 -4
- package/src/credential/status/README.md +0 -1
- package/src/sd-jwt/__test__/index.test.ts +8 -29
- package/src/sd-jwt/__test__/types.test.ts +6 -2
- package/src/sd-jwt/__test__/utils.test.ts +37 -0
- package/src/sd-jwt/index.ts +2 -0
- package/src/sd-jwt/types.ts +49 -2
- package/src/sd-jwt/utils.ts +73 -0
- package/src/trust/build-chain.ts +395 -0
- package/src/trust/index.ts +5 -442
- package/src/trust/types.ts +23 -17
- package/src/trust/{chain.ts → verify-chain.ts} +41 -1
- package/src/utils/errors.ts +4 -4
- package/src/utils/par.ts +37 -21
- package/src/utils/pop.ts +1 -1
- package/src/wallet-instance-attestation/types.ts +3 -1
- package/lib/commonjs/trust/chain.js.map +0 -1
- package/lib/module/trust/chain.js.map +0 -1
- package/lib/typescript/trust/chain.d.ts.map +0 -1
@@ -3,39 +3,14 @@ import { decode, disclose } from "../index";
|
|
3
3
|
|
4
4
|
import { encodeBase64, decodeBase64 } from "@pagopa/io-react-native-jwt";
|
5
5
|
import { SdJwt4VC } from "../types";
|
6
|
+
import { pid } from "../__mocks__/sd-jwt";
|
6
7
|
|
7
|
-
|
8
|
-
// but adapted to adhere to format declared in https://italia.github.io/eudi-wallet-it-docs/versione-corrente/en/pid-eaa-data-model.html#id2
|
9
|
-
// In short, the token is a Frankenstein composed as follows:
|
10
|
-
// - the header is taken from the italian specification, with kid and alg valued according to the signing keys
|
11
|
-
// - disclosures are taken from the SD-JWT-4-VC standard
|
12
|
-
// - payload is taken from the italian specification, but _sd are compiled with:
|
13
|
-
// - "address" is used as verification._sd
|
14
|
-
// - all others disclosures are in claims._sd
|
15
|
-
const token =
|
16
|
-
"eyJraWQiOiItRl82VWdhOG4zVmVnalkyVTdZVUhLMXpMb2FELU5QVGM2M1JNSVNuTGF3IiwidHlwIjoidmMrc2Qtand0IiwiYWxnIjoiRVMyNTYifQ.eyJfc2QiOlsiMHExRDVKbWF2NnBRYUVoX0pfRmN2X3VOTk1RSWdDeWhRT3hxbFk0bDNxVSIsIktDSi1BVk52ODhkLXhqNnNVSUFPSnhGbmJVaDNySFhES2tJSDFsRnFiUnMiLCJNOWxvOVl4RE5JWHJBcTJxV2VpQ0E0MHpwSl96WWZGZFJfNEFFQUxjUnRVIiwiY3pnalVrMG5xUkNzd1NoQ2hDamRTNkExLXY0N2RfcVRDU0ZJdklIaE1vSSIsIm5HblFyN2NsbTN0ZlRwOHlqTF91SHJEU090elIyUFZiOFM3R2VMZEFxQlEiLCJ4TklWd2xwU3NhWjhDSlNmMGd6NXhfNzVWUldXYzZWMW1scGVqZENycVVzIl0sInN1YiI6IjIxNmY4OTQ2LTllY2ItNDgxOS05MzA5LWMwNzZmMzRhN2UxMSIsIl9zZF9hbGciOiJzaGEtMjU2IiwidmN0IjoiUGVyc29uSWRlbnRpZmljYXRpb25EYXRhIiwiaXNzIjoiaHR0cHM6Ly9wcmUuZWlkLndhbGxldC5pcHpzLml0IiwiY25mIjp7Imp3ayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2Iiwia2lkIjoiUnYzVy1FaUtwdkJUeWs1eVp4dnJldi03TURCNlNselVDQm9fQ1FqamRkVSIsIngiOiIwV294N1F0eVBxQnlnMzVNSF9YeUNjbmQ1TGUtSm0wQVhIbFVnREJBMDNZIiwieSI6ImVFaFZ2ZzFKUHFOZDNEVFNhNG1HREdCbHdZNk5QLUVaYkxiTkZYU1h3SWcifX0sImV4cCI6MTc1MTU0NjU3Niwic3RhdHVzIjp7InN0YXR1c19hdHRlc3RhdGlvbiI6eyJjcmVkZW50aWFsX2hhc2hfYWxnIjoic2hhLTI1NiJ9fX0.qXHA2oqr8trX4fGxpxpUft2GX380TM3pzfo1MYAsDjUC8HsODA-4rdRWAvDe2zYP57x4tJU7eiABkd1Kmln9yQ~WyJrSkRFUDhFYU5URU1CRE9aelp6VDR3IiwidW5pcXVlX2lkIiwiVElOSVQtTFZMREFBODVUNTBHNzAyQiJd~WyJ6SUF5VUZ2UGZJcEUxekJxeEk1aGFRIiwiYmlydGhfZGF0ZSIsIjE5ODUtMTItMTAiXQ~WyJHcjNSM3MyOTBPa1FVbS1ORlR1OTZBIiwidGF4X2lkX2NvZGUiLCJUSU5JVC1MVkxEQUE4NVQ1MEc3MDJCIl0~WyJHeE9SYWxNQWVsZlowZWRGSmpqWVV3IiwiZ2l2ZW5fbmFtZSIsIkFkYSJd~WyJfdlY1UklrbDBJT0VYS290czlrdDF3IiwiZmFtaWx5X25hbWUiLCJMb3ZlbGFjZSJd~WyJDajV0Y2NSNzJKd3J6ZTJUVzRhLXdnIiwiaWF0IiwxNzIwMDEwNTc1XQ";
|
17
|
-
|
18
|
-
const unsigned =
|
19
|
-
"eyJraWQiOiItRl82VWdhOG4zVmVnalkyVTdZVUhLMXpMb2FELU5QVGM2M1JNSVNuTGF3IiwidHlwIjoidmMrc2Qtand0IiwiYWxnIjoiRVMyNTYifQ.eyJfc2QiOlsiMHExRDVKbWF2NnBRYUVoX0pfRmN2X3VOTk1RSWdDeWhRT3hxbFk0bDNxVSIsIktDSi1BVk52ODhkLXhqNnNVSUFPSnhGbmJVaDNySFhES2tJSDFsRnFiUnMiLCJNOWxvOVl4RE5JWHJBcTJxV2VpQ0E0MHpwSl96WWZGZFJfNEFFQUxjUnRVIiwiY3pnalVrMG5xUkNzd1NoQ2hDamRTNkExLXY0N2RfcVRDU0ZJdklIaE1vSSIsIm5HblFyN2NsbTN0ZlRwOHlqTF91SHJEU090elIyUFZiOFM3R2VMZEFxQlEiLCJ4TklWd2xwU3NhWjhDSlNmMGd6NXhfNzVWUldXYzZWMW1scGVqZENycVVzIl0sInN1YiI6IjIxNmY4OTQ2LTllY2ItNDgxOS05MzA5LWMwNzZmMzRhN2UxMSIsIl9zZF9hbGciOiJzaGEtMjU2IiwidmN0IjoiUGVyc29uSWRlbnRpZmljYXRpb25EYXRhIiwiaXNzIjoiaHR0cHM6Ly9wcmUuZWlkLndhbGxldC5pcHpzLml0IiwiY25mIjp7Imp3ayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2Iiwia2lkIjoiUnYzVy1FaUtwdkJUeWs1eVp4dnJldi03TURCNlNselVDQm9fQ1FqamRkVSIsIngiOiIwV294N1F0eVBxQnlnMzVNSF9YeUNjbmQ1TGUtSm0wQVhIbFVnREJBMDNZIiwieSI6ImVFaFZ2ZzFKUHFOZDNEVFNhNG1HREdCbHdZNk5QLUVaYkxiTkZYU1h3SWcifX0sImV4cCI6MTc1MTU0NjU3Niwic3RhdHVzIjp7InN0YXR1c19hdHRlc3RhdGlvbiI6eyJjcmVkZW50aWFsX2hhc2hfYWxnIjoic2hhLTI1NiJ9fX0";
|
20
|
-
|
21
|
-
const signature =
|
22
|
-
"qXHA2oqr8trX4fGxpxpUft2GX380TM3pzfo1MYAsDjUC8HsODA-4rdRWAvDe2zYP57x4tJU7eiABkd1Kmln9yQ";
|
23
|
-
|
24
|
-
const signed = `${unsigned}.${signature}`;
|
25
|
-
|
26
|
-
const tokenizedDisclosures = [
|
27
|
-
"WyJrSkRFUDhFYU5URU1CRE9aelp6VDR3IiwidW5pcXVlX2lkIiwiVElOSVQtTFZMREFBODVUNTBHNzAyQiJd",
|
28
|
-
"WyJ6SUF5VUZ2UGZJcEUxekJxeEk1aGFRIiwiYmlydGhfZGF0ZSIsIjE5ODUtMTItMTAiXQ",
|
29
|
-
"WyJHcjNSM3MyOTBPa1FVbS1ORlR1OTZBIiwidGF4X2lkX2NvZGUiLCJUSU5JVC1MVkxEQUE4NVQ1MEc3MDJCIl0",
|
30
|
-
"WyJHeE9SYWxNQWVsZlowZWRGSmpqWVV3IiwiZ2l2ZW5fbmFtZSIsIkFkYSJd",
|
31
|
-
"WyJfdlY1UklrbDBJT0VYS290czlrdDF3IiwiZmFtaWx5X25hbWUiLCJMb3ZlbGFjZSJd",
|
32
|
-
"WyJDajV0Y2NSNzJKd3J6ZTJUVzRhLXdnIiwiaWF0IiwxNzIwMDEwNTc1XQ",
|
33
|
-
];
|
8
|
+
const { token, signed, tokenizedDisclosures } = pid;
|
34
9
|
|
35
10
|
const sdJwt = {
|
36
11
|
header: {
|
37
12
|
kid: "-F_6Uga8n3VegjY2U7YUHK1zLoaD-NPTc63RMISnLaw",
|
38
|
-
typ: "
|
13
|
+
typ: "dc+sd-jwt",
|
39
14
|
alg: "ES256",
|
40
15
|
},
|
41
16
|
payload: {
|
@@ -50,7 +25,11 @@ const sdJwt = {
|
|
50
25
|
sub: "216f8946-9ecb-4819-9309-c076f34a7e11",
|
51
26
|
_sd_alg: "sha-256",
|
52
27
|
vct: "PersonIdentificationData",
|
28
|
+
"vct#integrity":
|
29
|
+
"13e25888ac7b8a3a6d61440da787fccc81654e61085732bcacd89b36aec32675",
|
53
30
|
iss: "https://pre.eid.wallet.ipzs.it",
|
31
|
+
issuing_country: "IT",
|
32
|
+
issuing_authority: "Istituto Poligrafico e Zecca dello Stato",
|
54
33
|
cnf: {
|
55
34
|
jwk: {
|
56
35
|
kty: "EC",
|
@@ -62,7 +41,7 @@ const sdJwt = {
|
|
62
41
|
},
|
63
42
|
exp: 1751546576,
|
64
43
|
status: {
|
65
|
-
|
44
|
+
status_assertion: {
|
66
45
|
credential_hash_alg: "sha-256",
|
67
46
|
},
|
68
47
|
},
|
@@ -5,7 +5,7 @@ describe("SdJwt4VC", () => {
|
|
5
5
|
// example provided at https://italia.github.io/eidas-it-wallet-docs/en/pid-data-model.html
|
6
6
|
const token = {
|
7
7
|
header: {
|
8
|
-
typ: "
|
8
|
+
typ: "dc+sd-jwt",
|
9
9
|
alg: "RS512",
|
10
10
|
kid: "dB67gL7ck3TFiIAf7N6_7SHvqk0MDYMEQcoGGlkUAAw",
|
11
11
|
},
|
@@ -21,7 +21,11 @@ describe("SdJwt4VC", () => {
|
|
21
21
|
sub: "216f8946-9ecb-4819-9309-c076f34a7e11",
|
22
22
|
_sd_alg: "sha-256",
|
23
23
|
vct: "PersonIdentificationData",
|
24
|
+
"vct#integrity":
|
25
|
+
"13e25888ac7b8a3a6d61440da787fccc81654e61085732bcacd89b36aec32675",
|
24
26
|
iss: "https://pidprovider.example.com",
|
27
|
+
issuing_country: "IT",
|
28
|
+
issuing_authority: "Istituto Poligrafico e Zecca dello Stato",
|
25
29
|
cnf: {
|
26
30
|
jwk: {
|
27
31
|
kty: "EC",
|
@@ -33,7 +37,7 @@ describe("SdJwt4VC", () => {
|
|
33
37
|
},
|
34
38
|
exp: 1751107255,
|
35
39
|
status: {
|
36
|
-
|
40
|
+
status_assertion: {
|
37
41
|
credential_hash_alg: "sha-256",
|
38
42
|
},
|
39
43
|
},
|
@@ -0,0 +1,37 @@
|
|
1
|
+
import { getVerification } from "..";
|
2
|
+
import { pid } from "../__mocks__/sd-jwt";
|
3
|
+
|
4
|
+
const { signed, token } = pid;
|
5
|
+
|
6
|
+
describe("SD-JWT getVerification", () => {
|
7
|
+
it("extracts the verification claims correctly", () => {
|
8
|
+
const disclosure =
|
9
|
+
"WyJxTGxVdkNKY3hwX3d4MVY5dHFPbFFRIiwidmVyaWZpY2F0aW9uIix7ImV2aWRlbmNlIjpbeyJhdHRlc3RhdGlvbiI6eyJkYXRlX29mX2lzc3VhbmNlIjoiMjAyNS0wNi0yMyIsInZvdWNoZXIiOnsib3JnYW5pemF0aW9uIjoiTWluaXN0ZXJvIGRlbGwnSW50ZXJubyJ9LCJ0eXBlIjoiZGlnaXRhbF9hdHRlc3RhdGlvbiIsInJlZmVyZW5jZV9udW1iZXIiOiIxMjM0NTY3ODkifSwidGltZSI6IjIwMjUtMDYtMjNUMTM6MTQ6MjVaIiwidHlwZSI6InZvdWNoIn1dLCJ0cnVzdF9mcmFtZXdvcmsiOiJpdF9jaWUiLCJhc3N1cmFuY2VfbGV2ZWwiOiJoaWdoIn1d";
|
10
|
+
expect(getVerification(`${signed}~${disclosure}`)).toEqual({
|
11
|
+
evidence: [
|
12
|
+
{
|
13
|
+
attestation: {
|
14
|
+
date_of_issuance: "2025-06-23",
|
15
|
+
voucher: { organization: "Ministero dell'Interno" },
|
16
|
+
type: "digital_attestation",
|
17
|
+
reference_number: "123456789",
|
18
|
+
},
|
19
|
+
time: "2025-06-23T13:14:25Z",
|
20
|
+
type: "vouch",
|
21
|
+
},
|
22
|
+
],
|
23
|
+
trust_framework: "it_cie",
|
24
|
+
assurance_level: "high",
|
25
|
+
});
|
26
|
+
});
|
27
|
+
|
28
|
+
it("returns undefined when the verification claim is not found", () => {
|
29
|
+
expect(getVerification(token)).toBeUndefined();
|
30
|
+
});
|
31
|
+
|
32
|
+
it("throws when the verification claim is invalid", () => {
|
33
|
+
const disclosure =
|
34
|
+
"WyJxTGxVdkNKY3hwX3d4MVY5dHFPbFFRIiwidmVyaWZpY2F0aW9uIix7InRydXN0X2ZyYW1ld29yayI6ICJpdF9jaWUiLCJhc3N1cmFuY2VfbGV2ZWwiOiAic3Vic3RhbnRpYWwifV0";
|
35
|
+
expect(() => getVerification(`${signed}~${disclosure}`)).toThrow();
|
36
|
+
});
|
37
|
+
});
|
package/src/sd-jwt/index.ts
CHANGED
@@ -10,6 +10,8 @@ import * as Errors from "./errors";
|
|
10
10
|
import { Base64 } from "js-base64";
|
11
11
|
import { type Presentation } from "../credential/presentation/types";
|
12
12
|
|
13
|
+
export * from "./utils";
|
14
|
+
|
13
15
|
const decodeDisclosure = (encoded: string): DisclosureWithEncoded => {
|
14
16
|
const utf8String = Base64.decode(encoded); // Decode Base64 into UTF-8 string
|
15
17
|
const decoded = Disclosure.parse(JSON.parse(utf8String));
|
package/src/sd-jwt/types.ts
CHANGED
@@ -36,7 +36,7 @@ export type DisclosureWithEncoded = {
|
|
36
36
|
export type SdJwt4VC = z.infer<typeof SdJwt4VC>;
|
37
37
|
export const SdJwt4VC = z.object({
|
38
38
|
header: z.object({
|
39
|
-
typ: z.literal("
|
39
|
+
typ: z.literal("dc+sd-jwt"),
|
40
40
|
alg: z.string(),
|
41
41
|
kid: z.string().optional(),
|
42
42
|
}),
|
@@ -48,7 +48,7 @@ export const SdJwt4VC = z.object({
|
|
48
48
|
exp: UnixTime,
|
49
49
|
_sd_alg: z.literal("sha-256"),
|
50
50
|
status: z.object({
|
51
|
-
|
51
|
+
status_assertion: z.object({
|
52
52
|
credential_hash_alg: z.literal("sha-256"),
|
53
53
|
}),
|
54
54
|
}),
|
@@ -56,7 +56,54 @@ export const SdJwt4VC = z.object({
|
|
56
56
|
jwk: JWK,
|
57
57
|
}),
|
58
58
|
vct: z.string(),
|
59
|
+
"vct#integrity": z.string(),
|
60
|
+
issuing_authority: z.string(),
|
61
|
+
issuing_country: z.string(),
|
59
62
|
}),
|
60
63
|
ObfuscatedDisclosures
|
61
64
|
),
|
62
65
|
});
|
66
|
+
|
67
|
+
/**
|
68
|
+
* Object containing User authentication and User data verification information.
|
69
|
+
* Useful to extract the assurance level to determine L2/L3 authentication.
|
70
|
+
*/
|
71
|
+
export type Verification = z.infer<typeof Verification>;
|
72
|
+
export const Verification = z.object({
|
73
|
+
trust_framework: z.string(),
|
74
|
+
assurance_level: z.string(),
|
75
|
+
evidence: z.array(
|
76
|
+
z.object({
|
77
|
+
type: z.literal("vouch"),
|
78
|
+
time: z.string(),
|
79
|
+
attestation: z.object({
|
80
|
+
type: z.literal("digital_attestation"),
|
81
|
+
reference_number: z.string(),
|
82
|
+
date_of_issuance: z.string(),
|
83
|
+
voucher: z.object({ organization: z.string() }),
|
84
|
+
}),
|
85
|
+
})
|
86
|
+
),
|
87
|
+
});
|
88
|
+
|
89
|
+
/**
|
90
|
+
* Metadata for a digital credential. This information is retrieved from the URL defined in the `vct` claim.
|
91
|
+
*
|
92
|
+
* @see https://italia.github.io/eid-wallet-it-docs/v0.9.1/en/pid-eaa-data-model.html#digital-credential-metadata-type
|
93
|
+
*/
|
94
|
+
export type TypeMetadata = z.infer<typeof TypeMetadata>;
|
95
|
+
export const TypeMetadata = z.object({
|
96
|
+
name: z.string(),
|
97
|
+
description: z.string(),
|
98
|
+
data_source: z.object({
|
99
|
+
trust_framework: z.string(),
|
100
|
+
authentic_source: z.object({
|
101
|
+
organization_name: z.string(),
|
102
|
+
organization_code: z.string(),
|
103
|
+
contacts: z.array(z.string()),
|
104
|
+
homepage_uri: z.string().url(),
|
105
|
+
logo_uri: z.string().url(),
|
106
|
+
}),
|
107
|
+
}),
|
108
|
+
// TODO: add more fields
|
109
|
+
});
|
@@ -0,0 +1,73 @@
|
|
1
|
+
import { sha256ToBase64 } from "@pagopa/io-react-native-jwt";
|
2
|
+
import { hasStatusOrThrow } from "../utils/misc";
|
3
|
+
import { TypeMetadata, Verification } from "./types";
|
4
|
+
import {
|
5
|
+
IoWalletError,
|
6
|
+
IssuerResponseError,
|
7
|
+
ValidationFailed,
|
8
|
+
} from "../utils/errors";
|
9
|
+
import { decode } from ".";
|
10
|
+
import { getValueFromDisclosures } from "./converters";
|
11
|
+
|
12
|
+
/**
|
13
|
+
* Retrieve the Type Metadata for a credential and verify its integrity.
|
14
|
+
* @param vct The VCT as a valid HTTPS url
|
15
|
+
* @param vctIntegrity The integrity hash
|
16
|
+
* @param context.appFetch (optional) fetch api implementation. Default: built-in fetch
|
17
|
+
* @returns The credential metadata {@link TypeMetadata}
|
18
|
+
*/
|
19
|
+
export const fetchTypeMetadata = async (
|
20
|
+
vct: string,
|
21
|
+
vctIntegrity: string,
|
22
|
+
context: {
|
23
|
+
appFetch?: GlobalFetch["fetch"];
|
24
|
+
} = {}
|
25
|
+
): Promise<TypeMetadata> => {
|
26
|
+
const { appFetch = fetch } = context;
|
27
|
+
const { origin, pathname } = new URL(vct);
|
28
|
+
|
29
|
+
const metadata = await appFetch(`${origin}/.well-known/vct${pathname}`, {
|
30
|
+
headers: {
|
31
|
+
"Content-Type": "application/json",
|
32
|
+
},
|
33
|
+
})
|
34
|
+
.then(hasStatusOrThrow(200, IssuerResponseError))
|
35
|
+
.then((res) => res.json())
|
36
|
+
.then(TypeMetadata.parse);
|
37
|
+
|
38
|
+
const [alg, hash] = vctIntegrity.split(/-(.*)/s);
|
39
|
+
|
40
|
+
if (alg !== "sha256") {
|
41
|
+
throw new IoWalletError(`${alg} algorithm is not supported`);
|
42
|
+
}
|
43
|
+
|
44
|
+
// TODO: [SIW-2264] check if the hash is correctly calculated
|
45
|
+
const metadataHash = await sha256ToBase64(JSON.stringify(metadata));
|
46
|
+
|
47
|
+
if (metadataHash !== hash) {
|
48
|
+
throw new ValidationFailed({
|
49
|
+
message: "Unable to verify VCT integrity",
|
50
|
+
reason: "vct#integrity does not match the metadata hash",
|
51
|
+
});
|
52
|
+
}
|
53
|
+
|
54
|
+
return metadata;
|
55
|
+
};
|
56
|
+
|
57
|
+
/**
|
58
|
+
* Extract and validate the `verification` claim from disclosures.
|
59
|
+
* @param credentialSdJwt The raw credential SD-JWT
|
60
|
+
* @returns The verification claim or undefined if it wasn't found
|
61
|
+
*/
|
62
|
+
export const getVerification = (
|
63
|
+
credentialSdJwt: string
|
64
|
+
): Verification | undefined => {
|
65
|
+
const { disclosures } = decode(credentialSdJwt);
|
66
|
+
const verificationDisclosure = getValueFromDisclosures(
|
67
|
+
disclosures.map((d) => d.decoded),
|
68
|
+
"verification"
|
69
|
+
);
|
70
|
+
return verificationDisclosure
|
71
|
+
? Verification.parse(verificationDisclosure)
|
72
|
+
: undefined;
|
73
|
+
};
|
@@ -0,0 +1,395 @@
|
|
1
|
+
import type { JWK } from "../utils/jwk";
|
2
|
+
import {
|
3
|
+
BuildTrustChainError,
|
4
|
+
FederationListParseError,
|
5
|
+
MissingFederationFetchEndpointError,
|
6
|
+
RelyingPartyNotAuthorizedError,
|
7
|
+
TrustAnchorKidMissingError,
|
8
|
+
} from "./errors";
|
9
|
+
import { decode, verify } from "./utils";
|
10
|
+
import {
|
11
|
+
CredentialIssuerEntityConfiguration,
|
12
|
+
EntityConfiguration,
|
13
|
+
EntityStatement,
|
14
|
+
FederationListResponse,
|
15
|
+
RelyingPartyEntityConfiguration,
|
16
|
+
TrustAnchorEntityConfiguration,
|
17
|
+
WalletProviderEntityConfiguration,
|
18
|
+
} from "./types";
|
19
|
+
import { hasStatusOrThrow } from "../utils/misc";
|
20
|
+
import { decode as decodeJwt } from "@pagopa/io-react-native-jwt";
|
21
|
+
|
22
|
+
/**
|
23
|
+
* Fetch and parse the entity configuration document for a given federation entity.
|
24
|
+
* This is an inner method to serve public interfaces.
|
25
|
+
*
|
26
|
+
* To add another entity configuration type (example: Foo entity type):
|
27
|
+
* - create its zod schema and type by inherit from the base type (example: FooEntityConfiguration = BaseEntityConfiguration.and(...))
|
28
|
+
* - add such type to EntityConfiguration union
|
29
|
+
* - add an overload to this function
|
30
|
+
* - create a public function which use such type (example: getFooEntityConfiguration = (url, options) => Promise<FooEntityConfiguration>)
|
31
|
+
*
|
32
|
+
* @param entityBaseUrl The base url of the entity.
|
33
|
+
* @param schema The expected schema of the entity configuration, according to the kind of entity we are fetching from.
|
34
|
+
* @param options An optional object with additional options.
|
35
|
+
* @param options.appFetch An optional instance of the http client to be used.
|
36
|
+
* @returns The parsed entity configuration object
|
37
|
+
* @throws {IoWalletError} If the http request fails
|
38
|
+
* @throws Parse error if the document is not in the expected shape.
|
39
|
+
*/
|
40
|
+
async function fetchAndParseEntityConfiguration(
|
41
|
+
entityBaseUrl: string,
|
42
|
+
schema: typeof WalletProviderEntityConfiguration,
|
43
|
+
options?: {
|
44
|
+
appFetch?: GlobalFetch["fetch"];
|
45
|
+
}
|
46
|
+
): Promise<WalletProviderEntityConfiguration>;
|
47
|
+
async function fetchAndParseEntityConfiguration(
|
48
|
+
entityBaseUrl: string,
|
49
|
+
schema: typeof RelyingPartyEntityConfiguration,
|
50
|
+
options?: {
|
51
|
+
appFetch?: GlobalFetch["fetch"];
|
52
|
+
}
|
53
|
+
): Promise<RelyingPartyEntityConfiguration>;
|
54
|
+
async function fetchAndParseEntityConfiguration(
|
55
|
+
entityBaseUrl: string,
|
56
|
+
schema: typeof TrustAnchorEntityConfiguration,
|
57
|
+
options?: {
|
58
|
+
appFetch?: GlobalFetch["fetch"];
|
59
|
+
}
|
60
|
+
): Promise<TrustAnchorEntityConfiguration>;
|
61
|
+
async function fetchAndParseEntityConfiguration(
|
62
|
+
entityBaseUrl: string,
|
63
|
+
schema: typeof CredentialIssuerEntityConfiguration,
|
64
|
+
options?: {
|
65
|
+
appFetch?: GlobalFetch["fetch"];
|
66
|
+
}
|
67
|
+
): Promise<CredentialIssuerEntityConfiguration>;
|
68
|
+
async function fetchAndParseEntityConfiguration(
|
69
|
+
entityBaseUrl: string,
|
70
|
+
schema: typeof EntityConfiguration,
|
71
|
+
options?: {
|
72
|
+
appFetch?: GlobalFetch["fetch"];
|
73
|
+
}
|
74
|
+
): Promise<EntityConfiguration>;
|
75
|
+
async function fetchAndParseEntityConfiguration(
|
76
|
+
entityBaseUrl: string,
|
77
|
+
schema: /* FIXME: why is it different from "typeof EntityConfiguration"? */
|
78
|
+
| typeof CredentialIssuerEntityConfiguration
|
79
|
+
| typeof WalletProviderEntityConfiguration
|
80
|
+
| typeof RelyingPartyEntityConfiguration
|
81
|
+
| typeof TrustAnchorEntityConfiguration
|
82
|
+
| typeof EntityConfiguration,
|
83
|
+
{
|
84
|
+
appFetch = fetch,
|
85
|
+
}: {
|
86
|
+
appFetch?: GlobalFetch["fetch"];
|
87
|
+
} = {}
|
88
|
+
) {
|
89
|
+
const responseText = await getSignedEntityConfiguration(entityBaseUrl, {
|
90
|
+
appFetch,
|
91
|
+
});
|
92
|
+
|
93
|
+
const responseJwt = decodeJwt(responseText);
|
94
|
+
return schema.parse({
|
95
|
+
header: responseJwt.protectedHeader,
|
96
|
+
payload: responseJwt.payload,
|
97
|
+
});
|
98
|
+
}
|
99
|
+
|
100
|
+
export const getWalletProviderEntityConfiguration = (
|
101
|
+
entityBaseUrl: Parameters<typeof fetchAndParseEntityConfiguration>[0],
|
102
|
+
options?: Parameters<typeof fetchAndParseEntityConfiguration>[2]
|
103
|
+
) =>
|
104
|
+
fetchAndParseEntityConfiguration(
|
105
|
+
entityBaseUrl,
|
106
|
+
WalletProviderEntityConfiguration,
|
107
|
+
options
|
108
|
+
);
|
109
|
+
|
110
|
+
export const getCredentialIssuerEntityConfiguration = (
|
111
|
+
entityBaseUrl: Parameters<typeof fetchAndParseEntityConfiguration>[0],
|
112
|
+
options?: Parameters<typeof fetchAndParseEntityConfiguration>[2]
|
113
|
+
) =>
|
114
|
+
fetchAndParseEntityConfiguration(
|
115
|
+
entityBaseUrl,
|
116
|
+
CredentialIssuerEntityConfiguration,
|
117
|
+
options
|
118
|
+
);
|
119
|
+
|
120
|
+
export const getTrustAnchorEntityConfiguration = (
|
121
|
+
entityBaseUrl: Parameters<typeof fetchAndParseEntityConfiguration>[0],
|
122
|
+
options?: Parameters<typeof fetchAndParseEntityConfiguration>[2]
|
123
|
+
) =>
|
124
|
+
fetchAndParseEntityConfiguration(
|
125
|
+
entityBaseUrl,
|
126
|
+
TrustAnchorEntityConfiguration,
|
127
|
+
options
|
128
|
+
);
|
129
|
+
|
130
|
+
export const getRelyingPartyEntityConfiguration = (
|
131
|
+
entityBaseUrl: Parameters<typeof fetchAndParseEntityConfiguration>[0],
|
132
|
+
options?: Parameters<typeof fetchAndParseEntityConfiguration>[2]
|
133
|
+
) =>
|
134
|
+
fetchAndParseEntityConfiguration(
|
135
|
+
entityBaseUrl,
|
136
|
+
RelyingPartyEntityConfiguration,
|
137
|
+
options
|
138
|
+
);
|
139
|
+
|
140
|
+
export const getEntityConfiguration = (
|
141
|
+
entityBaseUrl: Parameters<typeof fetchAndParseEntityConfiguration>[0],
|
142
|
+
options?: Parameters<typeof fetchAndParseEntityConfiguration>[2]
|
143
|
+
) =>
|
144
|
+
fetchAndParseEntityConfiguration(entityBaseUrl, EntityConfiguration, options);
|
145
|
+
|
146
|
+
/**
|
147
|
+
* Fetch and parse the entity statement document for a given federation entity.
|
148
|
+
*
|
149
|
+
* @param accreditationBodyBaseUrl The base url of the accreditation body which holds and signs the required entity statement
|
150
|
+
* @param subordinatedEntityBaseUrl The url that identifies the subordinate entity
|
151
|
+
* @param appFetch An optional instance of the http client to be used.
|
152
|
+
* @returns The parsed entity configuration object
|
153
|
+
* @throws {IoWalletError} If the http request fails
|
154
|
+
*/
|
155
|
+
export async function getEntityStatement(
|
156
|
+
accreditationBodyBaseUrl: string,
|
157
|
+
subordinatedEntityBaseUrl: string,
|
158
|
+
{
|
159
|
+
appFetch = fetch,
|
160
|
+
}: {
|
161
|
+
appFetch?: GlobalFetch["fetch"];
|
162
|
+
} = {}
|
163
|
+
) {
|
164
|
+
const responseText = await getSignedEntityStatement(
|
165
|
+
accreditationBodyBaseUrl,
|
166
|
+
subordinatedEntityBaseUrl,
|
167
|
+
{
|
168
|
+
appFetch,
|
169
|
+
}
|
170
|
+
);
|
171
|
+
|
172
|
+
const responseJwt = decodeJwt(responseText);
|
173
|
+
return EntityStatement.parse({
|
174
|
+
header: responseJwt.protectedHeader,
|
175
|
+
payload: responseJwt.payload,
|
176
|
+
});
|
177
|
+
}
|
178
|
+
|
179
|
+
/**
|
180
|
+
* Fetch the signed entity configuration token for an entity
|
181
|
+
*
|
182
|
+
* @param entityBaseUrl The url of the entity to fetch
|
183
|
+
* @param appFetch (optional) fetch api implementation
|
184
|
+
* @returns The signed Entity Configuration token
|
185
|
+
*/
|
186
|
+
export async function getSignedEntityConfiguration(
|
187
|
+
entityBaseUrl: string,
|
188
|
+
{
|
189
|
+
appFetch = fetch,
|
190
|
+
}: {
|
191
|
+
appFetch?: GlobalFetch["fetch"];
|
192
|
+
} = {}
|
193
|
+
): Promise<string> {
|
194
|
+
const wellKnownUrl = `${entityBaseUrl}/.well-known/openid-federation`;
|
195
|
+
|
196
|
+
return await appFetch(wellKnownUrl, {
|
197
|
+
method: "GET",
|
198
|
+
})
|
199
|
+
.then(hasStatusOrThrow(200))
|
200
|
+
.then((res) => res.text());
|
201
|
+
}
|
202
|
+
|
203
|
+
/**
|
204
|
+
* Fetch the entity statement document for a given federation entity.
|
205
|
+
*
|
206
|
+
* @param federationFetchEndpoint The exact endpoint provided by the parent EC's metadata.
|
207
|
+
* @param subordinatedEntityBaseUrl The url that identifies the subordinate entity.
|
208
|
+
* @param appFetch An optional instance of the http client to be used.
|
209
|
+
* @returns The signed entity statement token.
|
210
|
+
* @throws {IoWalletError} If the http request fails.
|
211
|
+
*/
|
212
|
+
export async function getSignedEntityStatement(
|
213
|
+
federationFetchEndpoint: string,
|
214
|
+
subordinatedEntityBaseUrl: string,
|
215
|
+
{
|
216
|
+
appFetch = fetch,
|
217
|
+
}: {
|
218
|
+
appFetch?: GlobalFetch["fetch"];
|
219
|
+
} = {}
|
220
|
+
) {
|
221
|
+
const url = new URL(federationFetchEndpoint);
|
222
|
+
url.searchParams.set("sub", subordinatedEntityBaseUrl);
|
223
|
+
|
224
|
+
return await appFetch(url.toString(), {
|
225
|
+
method: "GET",
|
226
|
+
})
|
227
|
+
.then(hasStatusOrThrow(200))
|
228
|
+
.then((res) => res.text());
|
229
|
+
}
|
230
|
+
|
231
|
+
/**
|
232
|
+
* Fetch the federation list document from a given endpoint.
|
233
|
+
*
|
234
|
+
* @param federationListEndpoint The URL of the federation list endpoint.
|
235
|
+
* @param appFetch An optional instance of the http client to be used.
|
236
|
+
* @returns The federation list as an array of strings.
|
237
|
+
* @throws {IoWalletError} If the HTTP request fails.
|
238
|
+
* @throws {FederationError} If the result is not in the expected format.
|
239
|
+
*/
|
240
|
+
export async function getFederationList(
|
241
|
+
federationListEndpoint: string,
|
242
|
+
{
|
243
|
+
appFetch = fetch,
|
244
|
+
}: {
|
245
|
+
appFetch?: GlobalFetch["fetch"];
|
246
|
+
} = {}
|
247
|
+
): Promise<string[]> {
|
248
|
+
return await appFetch(federationListEndpoint, {
|
249
|
+
method: "GET",
|
250
|
+
})
|
251
|
+
.then(hasStatusOrThrow(200))
|
252
|
+
.then((res) => res.json())
|
253
|
+
.then((json) => {
|
254
|
+
const result = FederationListResponse.safeParse(json);
|
255
|
+
if (!result.success) {
|
256
|
+
throw new FederationListParseError(
|
257
|
+
`Invalid federation list format received from ${federationListEndpoint}. Error: ${result.error.message}`,
|
258
|
+
{ url: federationListEndpoint, parseError: result.error.toString() }
|
259
|
+
);
|
260
|
+
}
|
261
|
+
return result.data;
|
262
|
+
});
|
263
|
+
}
|
264
|
+
|
265
|
+
/**
|
266
|
+
* Build a not-verified trust chain for a given Relying Party (RP) entity.
|
267
|
+
*
|
268
|
+
* @param relyingPartyEntityBaseUrl The base URL of the RP entity
|
269
|
+
* @param trustAnchorKey The public key of the Trust Anchor (TA) entity
|
270
|
+
* @param appFetch An optional instance of the http client to be used.
|
271
|
+
* @returns A list of signed tokens that represent the trust chain, in the order of the chain (from the RP to the Trust Anchor)
|
272
|
+
* @throws {FederationError} When an element of the chain fails to parse or other build steps fail.
|
273
|
+
*/
|
274
|
+
export async function buildTrustChain(
|
275
|
+
relyingPartyEntityBaseUrl: string,
|
276
|
+
trustAnchorKey: JWK,
|
277
|
+
appFetch: GlobalFetch["fetch"] = fetch
|
278
|
+
): Promise<string[]> {
|
279
|
+
// 1: Recursively gather the trust chain from the RP up to the Trust Anchor
|
280
|
+
const trustChain = await gatherTrustChain(
|
281
|
+
relyingPartyEntityBaseUrl,
|
282
|
+
appFetch
|
283
|
+
);
|
284
|
+
|
285
|
+
// 2: Trust Anchor signature verification
|
286
|
+
const trustAnchorJwt = trustChain[trustChain.length - 1];
|
287
|
+
if (!trustAnchorJwt) {
|
288
|
+
throw new BuildTrustChainError(
|
289
|
+
"Cannot verify trust anchor: missing entity configuration in gathered chain.",
|
290
|
+
{ relyingPartyUrl: relyingPartyEntityBaseUrl }
|
291
|
+
);
|
292
|
+
}
|
293
|
+
|
294
|
+
if (!trustAnchorKey.kid) {
|
295
|
+
throw new TrustAnchorKidMissingError();
|
296
|
+
}
|
297
|
+
|
298
|
+
await verify(trustAnchorJwt, trustAnchorKey.kid, [trustAnchorKey]);
|
299
|
+
|
300
|
+
// 3: Check the federation list
|
301
|
+
const trustAnchorConfig = EntityConfiguration.parse(decode(trustAnchorJwt));
|
302
|
+
const federationListEndpoint =
|
303
|
+
trustAnchorConfig.payload.metadata.federation_entity
|
304
|
+
.federation_list_endpoint;
|
305
|
+
|
306
|
+
if (federationListEndpoint) {
|
307
|
+
const federationList = await getFederationList(federationListEndpoint, {
|
308
|
+
appFetch,
|
309
|
+
});
|
310
|
+
|
311
|
+
if (!federationList.includes(relyingPartyEntityBaseUrl)) {
|
312
|
+
throw new RelyingPartyNotAuthorizedError(
|
313
|
+
"Relying Party entity base URL is not authorized by the Trust Anchor's federation list.",
|
314
|
+
{ relyingPartyUrl: relyingPartyEntityBaseUrl, federationListEndpoint }
|
315
|
+
);
|
316
|
+
}
|
317
|
+
}
|
318
|
+
|
319
|
+
return trustChain;
|
320
|
+
}
|
321
|
+
|
322
|
+
/**
|
323
|
+
* Recursively gather the trust chain for an entity and all its superiors.
|
324
|
+
* @param entityBaseUrl The base URL of the entity for which to gather the chain.
|
325
|
+
* @param appFetch An optional instance of the http client to be used.
|
326
|
+
* @param isLeaf Whether the current entity is the leaf of the chain.
|
327
|
+
* @returns A full ordered list of JWTs (ECs and ESs) forming the trust chain.
|
328
|
+
* @throws {FederationError} If any of the fetched documents fail to parse or other errors occur during the gathering process.
|
329
|
+
*/
|
330
|
+
async function gatherTrustChain(
|
331
|
+
entityBaseUrl: string,
|
332
|
+
appFetch: GlobalFetch["fetch"],
|
333
|
+
isLeaf: boolean = true
|
334
|
+
): Promise<string[]> {
|
335
|
+
const chain: string[] = [];
|
336
|
+
|
337
|
+
// Fetch self-signed EC (only needed for the leaf)
|
338
|
+
const entityECJwt = await getSignedEntityConfiguration(entityBaseUrl, {
|
339
|
+
appFetch,
|
340
|
+
});
|
341
|
+
const entityEC = EntityConfiguration.parse(decode(entityECJwt));
|
342
|
+
|
343
|
+
if (isLeaf) {
|
344
|
+
// Only push EC for the leaf
|
345
|
+
chain.push(entityECJwt);
|
346
|
+
}
|
347
|
+
|
348
|
+
// Find authority_hints (parent, if any)
|
349
|
+
const authorityHints = entityEC.payload.authority_hints ?? [];
|
350
|
+
if (authorityHints.length === 0) {
|
351
|
+
// This is the Trust Anchor (no parent)
|
352
|
+
if (!isLeaf) {
|
353
|
+
chain.push(entityECJwt);
|
354
|
+
}
|
355
|
+
return chain;
|
356
|
+
}
|
357
|
+
|
358
|
+
const parentEntityBaseUrl = authorityHints[0]!;
|
359
|
+
|
360
|
+
// Fetch parent EC
|
361
|
+
const parentECJwt = await getSignedEntityConfiguration(parentEntityBaseUrl, {
|
362
|
+
appFetch,
|
363
|
+
});
|
364
|
+
const parentEC = EntityConfiguration.parse(decode(parentECJwt));
|
365
|
+
|
366
|
+
// Fetch ES
|
367
|
+
const federationFetchEndpoint =
|
368
|
+
parentEC.payload.metadata.federation_entity.federation_fetch_endpoint;
|
369
|
+
if (!federationFetchEndpoint) {
|
370
|
+
throw new MissingFederationFetchEndpointError(
|
371
|
+
`Missing federation_fetch_endpoint in parent's (${parentEntityBaseUrl}) configuration when gathering chain for ${entityBaseUrl}.`,
|
372
|
+
{ entityBaseUrl, missingInEntityUrl: parentEntityBaseUrl }
|
373
|
+
);
|
374
|
+
}
|
375
|
+
|
376
|
+
const entityStatementJwt = await getSignedEntityStatement(
|
377
|
+
federationFetchEndpoint,
|
378
|
+
entityBaseUrl,
|
379
|
+
{ appFetch }
|
380
|
+
);
|
381
|
+
// Validate the ES
|
382
|
+
EntityStatement.parse(decode(entityStatementJwt));
|
383
|
+
|
384
|
+
// Push this ES into the chain
|
385
|
+
chain.push(entityStatementJwt);
|
386
|
+
|
387
|
+
// Recurse into the parent
|
388
|
+
const parentChain = await gatherTrustChain(
|
389
|
+
parentEntityBaseUrl,
|
390
|
+
appFetch,
|
391
|
+
false
|
392
|
+
);
|
393
|
+
|
394
|
+
return chain.concat(parentChain);
|
395
|
+
}
|