@pagopa/io-react-native-wallet 1.5.0 → 1.6.1
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/06-obtain-credential.js +5 -1
- package/lib/commonjs/credential/issuance/06-obtain-credential.js.map +1 -1
- package/lib/commonjs/credential/issuance/07-verify-and-parse-credential.js +33 -21
- package/lib/commonjs/credential/issuance/07-verify-and-parse-credential.js.map +1 -1
- package/lib/commonjs/credential/presentation/07-evaluate-input-descriptor.js +192 -58
- package/lib/commonjs/credential/presentation/07-evaluate-input-descriptor.js.map +1 -1
- package/lib/commonjs/credential/presentation/08-send-authorization-response.js +45 -18
- package/lib/commonjs/credential/presentation/08-send-authorization-response.js.map +1 -1
- package/lib/commonjs/credential/presentation/types.js +1 -1
- package/lib/commonjs/credential/presentation/types.js.map +1 -1
- package/lib/commonjs/entity/trust/chain.js.map +1 -1
- package/lib/commonjs/mdoc/index.js +45 -13
- package/lib/commonjs/mdoc/index.js.map +1 -1
- package/lib/commonjs/utils/crypto.js +70 -4
- package/lib/commonjs/utils/crypto.js.map +1 -1
- package/lib/commonjs/utils/string.js +4 -4
- package/lib/commonjs/utils/string.js.map +1 -1
- package/lib/module/credential/issuance/06-obtain-credential.js +5 -1
- package/lib/module/credential/issuance/06-obtain-credential.js.map +1 -1
- package/lib/module/credential/issuance/07-verify-and-parse-credential.js +33 -21
- package/lib/module/credential/issuance/07-verify-and-parse-credential.js.map +1 -1
- package/lib/module/credential/presentation/07-evaluate-input-descriptor.js +186 -55
- package/lib/module/credential/presentation/07-evaluate-input-descriptor.js.map +1 -1
- package/lib/module/credential/presentation/08-send-authorization-response.js +45 -18
- package/lib/module/credential/presentation/08-send-authorization-response.js.map +1 -1
- package/lib/module/credential/presentation/types.js +1 -1
- package/lib/module/credential/presentation/types.js.map +1 -1
- package/lib/module/entity/trust/chain.js.map +1 -1
- package/lib/module/mdoc/index.js +43 -12
- package/lib/module/mdoc/index.js.map +1 -1
- package/lib/module/utils/crypto.js +67 -2
- package/lib/module/utils/crypto.js.map +1 -1
- package/lib/module/utils/string.js +4 -4
- package/lib/module/utils/string.js.map +1 -1
- 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 +1 -1
- package/lib/typescript/credential/issuance/07-verify-and-parse-credential.d.ts.map +1 -1
- package/lib/typescript/credential/presentation/07-evaluate-input-descriptor.d.ts +49 -13
- package/lib/typescript/credential/presentation/07-evaluate-input-descriptor.d.ts.map +1 -1
- package/lib/typescript/credential/presentation/08-send-authorization-response.d.ts +6 -2
- package/lib/typescript/credential/presentation/08-send-authorization-response.d.ts.map +1 -1
- package/lib/typescript/credential/presentation/types.d.ts +10 -7
- package/lib/typescript/credential/presentation/types.d.ts.map +1 -1
- package/lib/typescript/entity/trust/chain.d.ts.map +1 -1
- package/lib/typescript/mdoc/index.d.ts +6 -2
- package/lib/typescript/mdoc/index.d.ts.map +1 -1
- package/lib/typescript/utils/crypto.d.ts +8 -0
- package/lib/typescript/utils/crypto.d.ts.map +1 -1
- package/lib/typescript/utils/errors.d.ts.map +1 -1
- package/lib/typescript/utils/misc.d.ts.map +1 -1
- package/lib/typescript/utils/string.d.ts +3 -3
- package/lib/typescript/utils/string.d.ts.map +1 -1
- package/package.json +14 -12
- package/src/credential/issuance/06-obtain-credential.ts +3 -1
- package/src/credential/issuance/07-verify-and-parse-credential.ts +37 -16
- package/src/credential/presentation/07-evaluate-input-descriptor.ts +278 -97
- package/src/credential/presentation/08-send-authorization-response.ts +50 -27
- package/src/credential/presentation/types.ts +9 -6
- package/src/entity/trust/chain.ts +14 -10
- package/src/mdoc/index.ts +72 -15
- package/src/utils/crypto.ts +61 -2
- package/src/utils/errors.ts +2 -2
- package/src/utils/misc.ts +2 -2
- package/src/utils/string.ts +4 -4
@@ -9,17 +9,20 @@ import { JWKS } from "../../utils/jwk";
|
|
9
9
|
export type Presentation = [
|
10
10
|
/* verified credential token */ string,
|
11
11
|
/* claims */ string[],
|
12
|
-
/* the context for the key associated to the credential */ CryptoContext
|
12
|
+
/* the context for the key associated to the credential */ CryptoContext,
|
13
13
|
];
|
14
14
|
|
15
15
|
/**
|
16
16
|
* A object that associate the information needed to multiple remote presentation
|
17
17
|
*/
|
18
18
|
export type RemotePresentation = {
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
presentations: {
|
20
|
+
requestedClaims: string[];
|
21
|
+
inputDescriptor: InputDescriptor;
|
22
|
+
format: string;
|
23
|
+
vpToken: string;
|
24
|
+
}[];
|
25
|
+
generatedNonce?: string /* nonce generated by app, used in mdoc presentation */;
|
23
26
|
};
|
24
27
|
|
25
28
|
const Fields = z.object({
|
@@ -82,7 +85,7 @@ export const RequestObject = z.object({
|
|
82
85
|
iss: z.string().optional(), //optional by RFC 7519, mandatory for Potential
|
83
86
|
iat: UnixTime.optional(),
|
84
87
|
exp: UnixTime.optional(),
|
85
|
-
state: z.string(),
|
88
|
+
state: z.string().optional(),
|
86
89
|
nonce: z.string(),
|
87
90
|
response_uri: z.string(),
|
88
91
|
response_type: z.literal("vp_token"),
|
@@ -70,8 +70,8 @@ export async function validateTrustChain(
|
|
70
70
|
elementIndex === 0
|
71
71
|
? FirstElementShape
|
72
72
|
: elementIndex === chain.length - 1
|
73
|
-
|
74
|
-
|
73
|
+
? LastElementShape
|
74
|
+
: MiddleElementShape;
|
75
75
|
|
76
76
|
// select the kid from the current index
|
77
77
|
const selectKid = (currentIndex: number): string => {
|
@@ -136,15 +136,19 @@ export function renewTrustChain(
|
|
136
136
|
ec.success
|
137
137
|
? getSignedEntityConfiguration(ec.data.payload.iss, { appFetch })
|
138
138
|
: es.success
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
139
|
+
? getSignedEntityStatement(
|
140
|
+
es.data.payload.iss,
|
141
|
+
es.data.payload.sub,
|
142
|
+
{
|
143
|
+
appFetch,
|
144
|
+
}
|
145
|
+
)
|
146
|
+
: // if the element fail to parse in both EntityStatement and EntityConfiguration, raise an error
|
147
|
+
Promise.reject(
|
148
|
+
new IoWalletError(
|
149
|
+
`Cannot renew trust chain because the element #${i} failed to be parsed.`
|
150
|
+
)
|
146
151
|
)
|
147
|
-
)
|
148
152
|
)
|
149
153
|
);
|
150
154
|
}
|
package/src/mdoc/index.ts
CHANGED
@@ -1,28 +1,85 @@
|
|
1
|
-
import { CBOR } from "@pagopa/io-react-native-cbor";
|
1
|
+
import { CBOR, COSE, ISO18013 } from "@pagopa/io-react-native-cbor";
|
2
2
|
import type { JWK } from "../utils/jwk";
|
3
|
+
import type { PublicKey } from "@pagopa/io-react-native-crypto";
|
4
|
+
import { b64utob64 } from "jsrsasign";
|
5
|
+
import {
|
6
|
+
convertCertToPem,
|
7
|
+
getSigningJwk,
|
8
|
+
parsePublicKey,
|
9
|
+
} from "../utils/crypto";
|
10
|
+
import { type Presentation } from "../credential/presentation/types";
|
11
|
+
import { base64ToBase64Url } from "../utils/string";
|
3
12
|
|
4
13
|
export const verify = async (
|
5
14
|
token: string,
|
6
|
-
|
7
|
-
): Promise<{
|
15
|
+
_: JWK | JWK[]
|
16
|
+
): Promise<{ issuerSigned: CBOR.IssuerSigned }> => {
|
8
17
|
// get decoded data
|
9
|
-
const
|
10
|
-
if (!
|
11
|
-
throw new Error("Invalid mDoc");
|
12
|
-
}
|
13
|
-
const mDoc = documents.documents[0];
|
14
|
-
if (!mDoc) {
|
18
|
+
const issuerSigned = await CBOR.decodeIssuerSigned(token);
|
19
|
+
if (!issuerSigned) {
|
15
20
|
throw new Error("Invalid mDoc");
|
16
21
|
}
|
17
22
|
|
18
|
-
const
|
19
|
-
|
20
|
-
|
21
|
-
|
23
|
+
const cert = issuerSigned.issuerAuth.unprotectedHeader[0]?.keyId;
|
24
|
+
if (!cert) throw new Error("Certificate not present in credential");
|
25
|
+
|
26
|
+
const pemcert = convertCertToPem(b64utob64(cert));
|
27
|
+
const publickey = parsePublicKey(pemcert);
|
28
|
+
if (!publickey) throw new Error("Certificate not present in credential");
|
29
|
+
|
30
|
+
const jwk = getSigningJwk(publickey);
|
31
|
+
|
32
|
+
jwk.x = b64utob64(jwk.x!);
|
33
|
+
jwk.y = b64utob64(jwk.y!);
|
34
|
+
|
35
|
+
const signatureCorrect = await COSE.verify(
|
36
|
+
b64utob64(issuerSigned.issuerAuth.rawValue!),
|
37
|
+
jwk as PublicKey
|
38
|
+
).catch(() => false);
|
39
|
+
if (!signatureCorrect) throw new Error("Invalid mDoc signature");
|
40
|
+
|
41
|
+
return { issuerSigned };
|
42
|
+
};
|
43
|
+
|
44
|
+
export const prepareVpTokenMdoc = async (
|
45
|
+
requestNonce: string,
|
46
|
+
generatedNonce: string,
|
47
|
+
clientId: string,
|
48
|
+
responseUri: string,
|
49
|
+
docType: string,
|
50
|
+
keyTag: string,
|
51
|
+
[verifiableCredential, requestedClaims, _]: Presentation
|
52
|
+
): Promise<{
|
53
|
+
vp_token: string;
|
54
|
+
}> => {
|
55
|
+
/* verifiableCredential is a IssuerSigned structure */
|
56
|
+
const documents = [
|
57
|
+
{
|
58
|
+
issuerSignedContent: verifiableCredential,
|
59
|
+
alias: keyTag,
|
60
|
+
docType,
|
61
|
+
},
|
62
|
+
];
|
63
|
+
|
64
|
+
/* we map each requested claim as for ex. { "org.iso.18013.5.1.mDL" { <claim-name>: true, ... }} for selective disclosure */
|
65
|
+
const fieldRequestedAndAccepted = JSON.stringify({
|
66
|
+
[docType]: requestedClaims.reduce((acc, item) => {
|
67
|
+
return { ...acc, [item]: true };
|
68
|
+
}, {}),
|
69
|
+
});
|
22
70
|
|
23
|
-
|
71
|
+
/* clientId,responseUri,requestNonce are retrieved by Auth Request Object */
|
72
|
+
/* create DeviceResponse as { documents: { docType, issuerSigned, deviceSigned }, version, status } */
|
73
|
+
const vp_token = await ISO18013.generateOID4VPDeviceResponse(
|
74
|
+
clientId,
|
75
|
+
responseUri,
|
76
|
+
requestNonce,
|
77
|
+
generatedNonce,
|
78
|
+
documents,
|
79
|
+
fieldRequestedAndAccepted
|
80
|
+
);
|
24
81
|
|
25
82
|
return {
|
26
|
-
|
83
|
+
vp_token: base64ToBase64Url(vp_token),
|
27
84
|
};
|
28
85
|
};
|
package/src/utils/crypto.ts
CHANGED
@@ -3,12 +3,14 @@ import {
|
|
3
3
|
sign,
|
4
4
|
generate,
|
5
5
|
deleteKey,
|
6
|
+
type PublicKey,
|
6
7
|
} from "@pagopa/io-react-native-crypto";
|
7
8
|
import uuid from "react-native-uuid";
|
8
9
|
import { thumbprint, type CryptoContext } from "@pagopa/io-react-native-jwt";
|
9
|
-
import { fixBase64EncodingOnKey } from "./jwk";
|
10
10
|
import { X509, KEYUTIL, RSAKey, KJUR } from "jsrsasign";
|
11
11
|
import { JWK } from "./jwk";
|
12
|
+
import { removePadding } from "@pagopa/io-react-native-jwt";
|
13
|
+
import { Buffer } from "buffer";
|
12
14
|
|
13
15
|
/**
|
14
16
|
* Create a CryptoContext bound to a key pair.
|
@@ -26,7 +28,7 @@ export const createCryptoContextFor = (keytag: string): CryptoContext => {
|
|
26
28
|
*/
|
27
29
|
async getPublicKey() {
|
28
30
|
return getPublicKey(keytag)
|
29
|
-
.then(
|
31
|
+
.then(fixBase64WithLeadingZero)
|
30
32
|
.then(async (jwk) => ({
|
31
33
|
...jwk,
|
32
34
|
// Keys in the TEE are not stored with their KID, which is supposed to be assigned when they are included in JWK sets.
|
@@ -48,6 +50,45 @@ export const createCryptoContextFor = (keytag: string): CryptoContext => {
|
|
48
50
|
};
|
49
51
|
};
|
50
52
|
|
53
|
+
/**
|
54
|
+
* This function takes a JSON Web Key (JWK) and returns a new JWK with its base64-url properties (x, y, e, n) processed.
|
55
|
+
* Each property is passed through the `removeLeadingZeroAndParseb64u` function if it exists, which fixes any unwanted leading zeros.
|
56
|
+
*
|
57
|
+
* @param key - The input JSON Web Key that may contain properties with potential leading zero issues.
|
58
|
+
* @returns A new JSON Web Key with the processed properties.
|
59
|
+
*/
|
60
|
+
const fixBase64WithLeadingZero = (key: JWK): JWK => {
|
61
|
+
const { x, y, e, n, ...pk } = key;
|
62
|
+
|
63
|
+
return {
|
64
|
+
...pk,
|
65
|
+
...(x ? { x: removeLeadingZeroAndParseb64u(x) } : {}),
|
66
|
+
...(y ? { y: removeLeadingZeroAndParseb64u(y) } : {}),
|
67
|
+
...(e ? { e: removeLeadingZeroAndParseb64u(e) } : {}),
|
68
|
+
...(n ? { n: removeLeadingZeroAndParseb64u(n) } : {}),
|
69
|
+
};
|
70
|
+
};
|
71
|
+
|
72
|
+
/**
|
73
|
+
* This function processes a base64-encoded string to remove any unwanted leading zeros.
|
74
|
+
* It converts the input base64 string into a buffer, then to a hex string, checks for a leading "00",
|
75
|
+
* and removes it if present. The result is then converted back to a base64-url.
|
76
|
+
*
|
77
|
+
* @param input - The base64 encoded string to process.
|
78
|
+
* @returns A new base64-url encoded string with any leading zero removed.
|
79
|
+
*/
|
80
|
+
const removeLeadingZeroAndParseb64u = (input: string): string => {
|
81
|
+
// Decode base64 input into a Buffer
|
82
|
+
const buffer = Buffer.from(input, "base64");
|
83
|
+
const hex = buffer.toString("hex");
|
84
|
+
// If the hex string starts with "00", remove the first two characters
|
85
|
+
const fixedHex = hex.startsWith("00") ? hex.slice(2) : hex;
|
86
|
+
const newBuffer = Buffer.from(fixedHex, "hex");
|
87
|
+
|
88
|
+
// removePadding convert base64 string to base64-url
|
89
|
+
return removePadding(newBuffer.toString("base64"));
|
90
|
+
};
|
91
|
+
|
51
92
|
/**
|
52
93
|
* Executes the input function injecting an ephemeral crypto context.
|
53
94
|
* An ephemeral crypto context is a context which is bound to a key
|
@@ -106,3 +147,21 @@ export const getSigningJwk = (publicKey: RSAKey | KJUR.crypto.ECDSA): JWK => ({
|
|
106
147
|
...JWK.parse(KEYUTIL.getJWKFromKey(publicKey)),
|
107
148
|
use: "sig",
|
108
149
|
});
|
150
|
+
|
151
|
+
/**
|
152
|
+
* This function takes two {@link PublicKey} and evaluates and compares their thumbprints
|
153
|
+
* @param key1 The first key
|
154
|
+
* @param key2 The second key
|
155
|
+
* @returns true if the keys' thumbprints are equal, false otherwise
|
156
|
+
*/
|
157
|
+
export const compareKeysByThumbprint = async (
|
158
|
+
key1: PublicKey,
|
159
|
+
key2: PublicKey
|
160
|
+
) => {
|
161
|
+
//Parallel for optimization
|
162
|
+
const [thumbprint1, thumbprint2] = await Promise.all([
|
163
|
+
thumbprint(key1),
|
164
|
+
thumbprint(key2),
|
165
|
+
]);
|
166
|
+
return thumbprint1 === thumbprint2;
|
167
|
+
};
|
package/src/utils/errors.ts
CHANGED
@@ -189,8 +189,8 @@ export class ResponseErrorBuilder<T extends typeof UnexpectedStatusCodeError> {
|
|
189
189
|
type ErrorCodeMap<T> = T extends typeof IssuerResponseError
|
190
190
|
? IssuerResponseErrorCode
|
191
191
|
: T extends typeof WalletProviderResponseError
|
192
|
-
|
193
|
-
|
192
|
+
? WalletProviderResponseErrorCode
|
193
|
+
: never;
|
194
194
|
|
195
195
|
type ErrorCase<T> = {
|
196
196
|
code: ErrorCodeMap<T>;
|
package/src/utils/misc.ts
CHANGED
@@ -37,8 +37,8 @@ export const parseRawHttpResponse = <T extends Record<string, unknown>>(
|
|
37
37
|
export type Out<FN> = FN extends (...args: any[]) => Promise<any>
|
38
38
|
? Awaited<ReturnType<FN>>
|
39
39
|
: FN extends (...args: any[]) => any
|
40
|
-
|
41
|
-
|
40
|
+
? ReturnType<FN>
|
41
|
+
: never;
|
42
42
|
|
43
43
|
/**
|
44
44
|
* TODO [SIW-1310]: replace this function with a cryptographically secure one.
|
package/src/utils/string.ts
CHANGED
@@ -45,11 +45,11 @@ export const obfuscateString = (
|
|
45
45
|
};
|
46
46
|
|
47
47
|
/**
|
48
|
-
* Converts a
|
48
|
+
* Converts a base64 string to a Base64 URL-encoded string.
|
49
49
|
*
|
50
|
-
* @param byteString - The input string in
|
50
|
+
* @param byteString - The input string in base64 format.
|
51
51
|
* @returns The Base64 URL-encoded string.
|
52
52
|
*/
|
53
|
-
export const base64ToBase64Url = (
|
54
|
-
return
|
53
|
+
export const base64ToBase64Url = (base64: string): string => {
|
54
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/[=]+$/, "");
|
55
55
|
};
|