@pagopa/io-react-native-wallet 0.23.0 → 0.24.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. package/README.md +1 -0
  2. package/lib/commonjs/credential/index.js +3 -1
  3. package/lib/commonjs/credential/index.js.map +1 -1
  4. package/lib/commonjs/credential/trustmark/README.md +62 -0
  5. package/lib/commonjs/credential/trustmark/get-credential-trustmark.js +72 -0
  6. package/lib/commonjs/credential/trustmark/get-credential-trustmark.js.map +1 -0
  7. package/lib/commonjs/credential/trustmark/index.js +13 -0
  8. package/lib/commonjs/credential/trustmark/index.js.map +1 -0
  9. package/lib/commonjs/utils/string.js +50 -0
  10. package/lib/commonjs/utils/string.js.map +1 -0
  11. package/lib/module/credential/index.js +2 -1
  12. package/lib/module/credential/index.js.map +1 -1
  13. package/lib/module/credential/trustmark/README.md +62 -0
  14. package/lib/module/credential/trustmark/get-credential-trustmark.js +63 -0
  15. package/lib/module/credential/trustmark/get-credential-trustmark.js.map +1 -0
  16. package/lib/module/credential/trustmark/index.js +3 -0
  17. package/lib/module/credential/trustmark/index.js.map +1 -0
  18. package/lib/module/utils/string.js +43 -0
  19. package/lib/module/utils/string.js.map +1 -0
  20. package/lib/typescript/credential/index.d.ts +2 -1
  21. package/lib/typescript/credential/index.d.ts.map +1 -1
  22. package/lib/typescript/credential/trustmark/get-credential-trustmark.d.ts +50 -0
  23. package/lib/typescript/credential/trustmark/get-credential-trustmark.d.ts.map +1 -0
  24. package/lib/typescript/credential/trustmark/index.d.ts +4 -0
  25. package/lib/typescript/credential/trustmark/index.d.ts.map +1 -0
  26. package/lib/typescript/utils/string.d.ts +17 -0
  27. package/lib/typescript/utils/string.d.ts.map +1 -0
  28. package/package.json +1 -1
  29. package/src/credential/index.ts +2 -1
  30. package/src/credential/trustmark/README.md +62 -0
  31. package/src/credential/trustmark/get-credential-trustmark.ts +111 -0
  32. package/src/credential/trustmark/index.ts +8 -0
  33. package/src/utils/string.ts +45 -0
package/README.md CHANGED
@@ -146,6 +146,7 @@ Different flows are provided to perform common operations. Each flow is a set of
146
146
  - [Issuance](./src/credential/issuance/README.md)
147
147
  - [Presentation](./src/credential/presentation/README.md) (TODO)
148
148
  - [Status](./src/credential/status/README.md)
149
+ - [Trustmark](./src/credential/trustmark/README.md)
149
150
 
150
151
  ### Example
151
152
 
@@ -3,13 +3,15 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.Status = exports.Presentation = exports.Issuance = void 0;
6
+ exports.Trustmark = exports.Status = exports.Presentation = exports.Issuance = void 0;
7
7
  var Issuance = _interopRequireWildcard(require("./issuance"));
8
8
  exports.Issuance = Issuance;
9
9
  var Presentation = _interopRequireWildcard(require("./presentation"));
10
10
  exports.Presentation = Presentation;
11
11
  var Status = _interopRequireWildcard(require("./status"));
12
12
  exports.Status = Status;
13
+ var Trustmark = _interopRequireWildcard(require("./trustmark"));
14
+ exports.Trustmark = Trustmark;
13
15
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
14
16
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
15
17
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["Issuance","_interopRequireWildcard","require","exports","Presentation","Status","_getRequireWildcardCache","nodeInterop","WeakMap","cacheBabelInterop","cacheNodeInterop","obj","__esModule","default","cache","has","get","newObj","hasPropertyDescriptor","Object","defineProperty","getOwnPropertyDescriptor","key","prototype","hasOwnProperty","call","desc","set"],"sourceRoot":"../../../src","sources":["credential/index.ts"],"mappings":";;;;;;AAAA,IAAAA,QAAA,GAAAC,uBAAA,CAAAC,OAAA;AAAuCC,OAAA,CAAAH,QAAA,GAAAA,QAAA;AACvC,IAAAI,YAAA,GAAAH,uBAAA,CAAAC,OAAA;AAA+CC,OAAA,CAAAC,YAAA,GAAAA,YAAA;AAC/C,IAAAC,MAAA,GAAAJ,uBAAA,CAAAC,OAAA;AAAmCC,OAAA,CAAAE,MAAA,GAAAA,MAAA;AAAA,SAAAC,yBAAAC,WAAA,eAAAC,OAAA,kCAAAC,iBAAA,OAAAD,OAAA,QAAAE,gBAAA,OAAAF,OAAA,YAAAF,wBAAA,YAAAA,CAAAC,WAAA,WAAAA,WAAA,GAAAG,gBAAA,GAAAD,iBAAA,KAAAF,WAAA;AAAA,SAAAN,wBAAAU,GAAA,EAAAJ,WAAA,SAAAA,WAAA,IAAAI,GAAA,IAAAA,GAAA,CAAAC,UAAA,WAAAD,GAAA,QAAAA,GAAA,oBAAAA,GAAA,wBAAAA,GAAA,4BAAAE,OAAA,EAAAF,GAAA,UAAAG,KAAA,GAAAR,wBAAA,CAAAC,WAAA,OAAAO,KAAA,IAAAA,KAAA,CAAAC,GAAA,CAAAJ,GAAA,YAAAG,KAAA,CAAAE,GAAA,CAAAL,GAAA,SAAAM,MAAA,WAAAC,qBAAA,GAAAC,MAAA,CAAAC,cAAA,IAAAD,MAAA,CAAAE,wBAAA,WAAAC,GAAA,IAAAX,GAAA,QAAAW,GAAA,kBAAAH,MAAA,CAAAI,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAd,GAAA,EAAAW,GAAA,SAAAI,IAAA,GAAAR,qBAAA,GAAAC,MAAA,CAAAE,wBAAA,CAAAV,GAAA,EAAAW,GAAA,cAAAI,IAAA,KAAAA,IAAA,CAAAV,GAAA,IAAAU,IAAA,CAAAC,GAAA,KAAAR,MAAA,CAAAC,cAAA,CAAAH,MAAA,EAAAK,GAAA,EAAAI,IAAA,YAAAT,MAAA,CAAAK,GAAA,IAAAX,GAAA,CAAAW,GAAA,SAAAL,MAAA,CAAAJ,OAAA,GAAAF,GAAA,MAAAG,KAAA,IAAAA,KAAA,CAAAa,GAAA,CAAAhB,GAAA,EAAAM,MAAA,YAAAA,MAAA"}
1
+ {"version":3,"names":["Issuance","_interopRequireWildcard","require","exports","Presentation","Status","Trustmark","_getRequireWildcardCache","nodeInterop","WeakMap","cacheBabelInterop","cacheNodeInterop","obj","__esModule","default","cache","has","get","newObj","hasPropertyDescriptor","Object","defineProperty","getOwnPropertyDescriptor","key","prototype","hasOwnProperty","call","desc","set"],"sourceRoot":"../../../src","sources":["credential/index.ts"],"mappings":";;;;;;AAAA,IAAAA,QAAA,GAAAC,uBAAA,CAAAC,OAAA;AAAuCC,OAAA,CAAAH,QAAA,GAAAA,QAAA;AACvC,IAAAI,YAAA,GAAAH,uBAAA,CAAAC,OAAA;AAA+CC,OAAA,CAAAC,YAAA,GAAAA,YAAA;AAC/C,IAAAC,MAAA,GAAAJ,uBAAA,CAAAC,OAAA;AAAmCC,OAAA,CAAAE,MAAA,GAAAA,MAAA;AACnC,IAAAC,SAAA,GAAAL,uBAAA,CAAAC,OAAA;AAAyCC,OAAA,CAAAG,SAAA,GAAAA,SAAA;AAAA,SAAAC,yBAAAC,WAAA,eAAAC,OAAA,kCAAAC,iBAAA,OAAAD,OAAA,QAAAE,gBAAA,OAAAF,OAAA,YAAAF,wBAAA,YAAAA,CAAAC,WAAA,WAAAA,WAAA,GAAAG,gBAAA,GAAAD,iBAAA,KAAAF,WAAA;AAAA,SAAAP,wBAAAW,GAAA,EAAAJ,WAAA,SAAAA,WAAA,IAAAI,GAAA,IAAAA,GAAA,CAAAC,UAAA,WAAAD,GAAA,QAAAA,GAAA,oBAAAA,GAAA,wBAAAA,GAAA,4BAAAE,OAAA,EAAAF,GAAA,UAAAG,KAAA,GAAAR,wBAAA,CAAAC,WAAA,OAAAO,KAAA,IAAAA,KAAA,CAAAC,GAAA,CAAAJ,GAAA,YAAAG,KAAA,CAAAE,GAAA,CAAAL,GAAA,SAAAM,MAAA,WAAAC,qBAAA,GAAAC,MAAA,CAAAC,cAAA,IAAAD,MAAA,CAAAE,wBAAA,WAAAC,GAAA,IAAAX,GAAA,QAAAW,GAAA,kBAAAH,MAAA,CAAAI,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAd,GAAA,EAAAW,GAAA,SAAAI,IAAA,GAAAR,qBAAA,GAAAC,MAAA,CAAAE,wBAAA,CAAAV,GAAA,EAAAW,GAAA,cAAAI,IAAA,KAAAA,IAAA,CAAAV,GAAA,IAAAU,IAAA,CAAAC,GAAA,KAAAR,MAAA,CAAAC,cAAA,CAAAH,MAAA,EAAAK,GAAA,EAAAI,IAAA,YAAAT,MAAA,CAAAK,GAAA,IAAAX,GAAA,CAAAW,GAAA,SAAAL,MAAA,CAAAJ,OAAA,GAAAF,GAAA,MAAAG,KAAA,IAAAA,KAAA,CAAAa,GAAA,CAAAhB,GAAA,EAAAM,MAAA,YAAAA,MAAA"}
@@ -0,0 +1,62 @@
1
+ # Credential Trustmark
2
+
3
+ A credential TrustMark is a signed JWT that verifies the authenticity of a credential issued by a trusted source. It serves as proof that a credential is valid and linked to a specific wallet instance.
4
+ The TrustMark is often presented as a QR code, containing cryptographic data to ensure it hasn't been tampered with. It includes fields like issuer, issuance and expiration timestamps, and credential-specific details. TrustMarks have a short validity period and are used to enhance security and prevent misuse, such as QR code swapping.
5
+
6
+ ### getCredentialTrustmark
7
+
8
+ A function that generates a signed JWT Trustmark to verify the authenticity of a digital credential. The Trustmark serves as a cryptographic proof linking a credential to a specific wallet instance, ensuring the credential's validity and preventing unauthorized modifications or misuse.
9
+
10
+ #### Signature
11
+
12
+ ```typescript
13
+ function getCredentialTrustmark({
14
+ walletInstanceAttestation: string,
15
+ wiaCryptoContext: CryptoContext,
16
+ credentialType: string,
17
+ docNumber?: string,
18
+ expirationTime?: number | string
19
+ }): Promise<{
20
+ jwt: string,
21
+ expirationTime: number
22
+ }>
23
+ ```
24
+
25
+ #### Parameters
26
+ | Parameter | Type | Required | Description |
27
+ |-----------|------|----------|-------------|
28
+ | walletInstanceAttestation | string | Yes | A base64-encoded string containing the Wallet Instance Attestation (WIA). This attestation proves the authenticity of the wallet instance. |
29
+ | wiaCryptoContext | CryptoContext | Yes | The cryptographic context associated with the wallet instance. Must contain the same key pair used to generate the WIA. |
30
+ | credentialType | string | Yes | Identifier for the type of credential (e.g., "MDL" for Mobile Driver's License). |
31
+ | docNumber | string | No | The document number of the credential. If provided, it will be obfuscated in the Trustmark for privacy. |
32
+ | expirationTime | number \| string | No | Specifies when the Trustmark expires. Can be either:<br>- A timestamp in seconds<br>- A time span string (e.g., "2m" for 2 minutes)<br>Default: "2m" |
33
+
34
+ #### Return Value
35
+
36
+ Returns a Promise that resolves to an object containing:
37
+ | Property | Type | Description |
38
+ |----------|------|-------------|
39
+ | jwt | string | The signed trustmark JWT string |
40
+ | expirationTime | number | The expiration timestamp of the JWT in seconds |
41
+
42
+ ## Example
43
+
44
+ ```typescript
45
+ // Required inputs
46
+ const walletInstanceAttestation = "base64AttestationString";
47
+ const credentialType = "MDL"; // Credential type (e.g., Mobile Driver's License)
48
+ const documentNumber = "AB123456"; // Optional document number
49
+ const cryptoContext = createCryptoContextFor("wiaKeyTag"); // Sample crypto context
50
+
51
+ // Generate the TrustMark JWT
52
+ const { jwt, expirationTime } = await getCredentialTrustmark({
53
+ walletInstanceAttestation: "eyJ0eXAi...", // WIA JWT
54
+ wiaCryptoContext: cryptoContext,
55
+ credentialType: "IdentityCard",
56
+ docNumber: "AB123456",
57
+ expirationTime: "5m", // 5 minutes
58
+ });
59
+
60
+ console.log("Generated TrustMark JWT:", jwt);
61
+ console.log("Expires at:", new Date(expirationTime * 1000));
62
+ ```
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getCredentialTrustmark = void 0;
7
+ var _ioReactNativeJwt = require("@pagopa/io-react-native-jwt");
8
+ var WalletInstanceAttestation = _interopRequireWildcard(require("../../wallet-instance-attestation"));
9
+ var _errors = require("../../utils/errors");
10
+ var _string = require("../../utils/string");
11
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
12
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
13
+ /**
14
+ * Generates a trustmark signed JWT, which is used to verify the authenticity of a credential.
15
+ * The public key used to sign the trustmark must the same used for the Wallet Instance Attestation.
16
+ *
17
+ * @param walletInstanceAttestation the Wallet Instance's attestation
18
+ * @param wiaCryptoContext The Wallet Instance's crypto context associated with the walletInstanceAttestation parameter
19
+ * @param credentialType The type of credential for which the trustmark is generated
20
+ * @param docNumber (Optional) Document number contained in the credential, if applicable
21
+ * @param expirationTime (Optional) Expiration time for the trustmark, default is 2 minutes.
22
+ * If a number is provided, it is interpreted as a timestamp in seconds.
23
+ * If a string is provided, it is interpreted as a time span and added to the current timestamp.
24
+ * @throws {IoWalletError} If the public key associated to the WIA is not the same for the CryptoContext
25
+ * @returns A promise containing the signed JWT and its expiration time in seconds
26
+ */
27
+ const getCredentialTrustmark = async _ref => {
28
+ let {
29
+ walletInstanceAttestation,
30
+ wiaCryptoContext,
31
+ credentialType,
32
+ docNumber,
33
+ expirationTime = "2m"
34
+ } = _ref;
35
+ /**
36
+ * Check that the public key used to sign the trustmark is the one used for the WIA
37
+ */
38
+ const holderBindingKey = await wiaCryptoContext.getPublicKey();
39
+ const decodedWia = WalletInstanceAttestation.decode(walletInstanceAttestation);
40
+
41
+ /**
42
+ * Verify holder binding by comparing thumbprints of the WIA and the CryptoContext key
43
+ */
44
+ const wiaThumbprint = await (0, _ioReactNativeJwt.thumbprint)(decodedWia.payload.cnf.jwk);
45
+ const cryptoContextThumbprint = await (0, _ioReactNativeJwt.thumbprint)(holderBindingKey);
46
+ if (wiaThumbprint !== cryptoContextThumbprint) {
47
+ throw new _errors.IoWalletError(`Failed to verify holder binding for status attestation, expected thumbprint: ${cryptoContextThumbprint}, got: ${wiaThumbprint}`);
48
+ }
49
+
50
+ /**
51
+ * Generate Trustmark signed JWT
52
+ */
53
+ const signedTrustmarkJwt = await new _ioReactNativeJwt.SignJWT(wiaCryptoContext).setProtectedHeader({
54
+ alg: "ES256"
55
+ }).setPayload({
56
+ iss: walletInstanceAttestation,
57
+ sub: credentialType,
58
+ /**
59
+ * If present, the document number is obfuscated before adding it to the payload
60
+ */
61
+ ...(docNumber ? {
62
+ subtyp: (0, _string.obfuscateString)(docNumber)
63
+ } : {})
64
+ }).setIssuedAt().setExpirationTime(expirationTime).sign();
65
+ const decodedTrustmark = (0, _ioReactNativeJwt.decode)(signedTrustmarkJwt);
66
+ return {
67
+ jwt: signedTrustmarkJwt,
68
+ expirationTime: decodedTrustmark.payload.exp ?? 0
69
+ };
70
+ };
71
+ exports.getCredentialTrustmark = getCredentialTrustmark;
72
+ //# sourceMappingURL=get-credential-trustmark.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_ioReactNativeJwt","require","WalletInstanceAttestation","_interopRequireWildcard","_errors","_string","_getRequireWildcardCache","nodeInterop","WeakMap","cacheBabelInterop","cacheNodeInterop","obj","__esModule","default","cache","has","get","newObj","hasPropertyDescriptor","Object","defineProperty","getOwnPropertyDescriptor","key","prototype","hasOwnProperty","call","desc","set","getCredentialTrustmark","_ref","walletInstanceAttestation","wiaCryptoContext","credentialType","docNumber","expirationTime","holderBindingKey","getPublicKey","decodedWia","decode","wiaThumbprint","thumbprint","payload","cnf","jwk","cryptoContextThumbprint","IoWalletError","signedTrustmarkJwt","SignJWT","setProtectedHeader","alg","setPayload","iss","sub","subtyp","obfuscateString","setIssuedAt","setExpirationTime","sign","decodedTrustmark","decodeJwt","jwt","exp","exports"],"sourceRoot":"../../../../src","sources":["credential/trustmark/get-credential-trustmark.ts"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "getCredentialTrustmark", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _getCredentialTrustmark.getCredentialTrustmark;
10
+ }
11
+ });
12
+ var _getCredentialTrustmark = require("./get-credential-trustmark");
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_getCredentialTrustmark","require"],"sourceRoot":"../../../../src","sources":["credential/trustmark/index.ts"],"mappings":";;;;;;;;;;;AAAA,IAAAA,uBAAA,GAAAC,OAAA"}
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.obfuscateString = void 0;
7
+ /**
8
+ * Randomly obfuscates characters in a string by replacing them with a specified character.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const value = "1234567890";
13
+ * const obfuscated = obfuscateString(value, 60, "*");
14
+ * // Could output: "12**5*78**"
15
+ * ```
16
+ *
17
+ * @param value - The input string to obfuscate
18
+ * @param percentage - Percentage of characters to obfuscate (0-100). Defaults to 60
19
+ * @param obfuscatedChar - Character used for obfuscation. Defaults to "*"
20
+ * @returns The obfuscated string with random characters replaced
21
+ */
22
+ const obfuscateString = function (value) {
23
+ let percentage = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 60;
24
+ let obfuscatedChar = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "*";
25
+ if (!value) {
26
+ return "";
27
+ }
28
+
29
+ // Ensure percentage is between 0 and 100
30
+ const safePercentage = Math.max(0, Math.min(100, percentage));
31
+
32
+ // Calculate number of characters to obfuscate
33
+ const charsToObfuscate = Math.floor(value.length * safePercentage / 100);
34
+
35
+ // Convert string to array for manipulation
36
+ const chars = value.split("");
37
+
38
+ // Get random positions to obfuscate
39
+ const positions = Array.from({
40
+ length: value.length
41
+ }, (_, i) => i).sort(() => Math.random() - 0.5).slice(0, charsToObfuscate);
42
+
43
+ // Replace characters at random positions
44
+ positions.forEach(pos => {
45
+ chars[pos] = obfuscatedChar;
46
+ });
47
+ return chars.join("");
48
+ };
49
+ exports.obfuscateString = obfuscateString;
50
+ //# sourceMappingURL=string.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["obfuscateString","value","percentage","arguments","length","undefined","obfuscatedChar","safePercentage","Math","max","min","charsToObfuscate","floor","chars","split","positions","Array","from","_","i","sort","random","slice","forEach","pos","join","exports"],"sourceRoot":"../../../src","sources":["utils/string.ts"],"mappings":";;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAMA,eAAe,GAAG,SAAAA,CAC7BC,KAAa,EAGF;EAAA,IAFXC,UAAkB,GAAAC,SAAA,CAAAC,MAAA,QAAAD,SAAA,QAAAE,SAAA,GAAAF,SAAA,MAAG,EAAE;EAAA,IACvBG,cAAsB,GAAAH,SAAA,CAAAC,MAAA,QAAAD,SAAA,QAAAE,SAAA,GAAAF,SAAA,MAAG,GAAG;EAE5B,IAAI,CAACF,KAAK,EAAE;IACV,OAAO,EAAE;EACX;;EAEA;EACA,MAAMM,cAAc,GAAGC,IAAI,CAACC,GAAG,CAAC,CAAC,EAAED,IAAI,CAACE,GAAG,CAAC,GAAG,EAAER,UAAU,CAAC,CAAC;;EAE7D;EACA,MAAMS,gBAAgB,GAAGH,IAAI,CAACI,KAAK,CAAEX,KAAK,CAACG,MAAM,GAAGG,cAAc,GAAI,GAAG,CAAC;;EAE1E;EACA,MAAMM,KAAK,GAAGZ,KAAK,CAACa,KAAK,CAAC,EAAE,CAAC;;EAE7B;EACA,MAAMC,SAAS,GAAGC,KAAK,CAACC,IAAI,CAAC;IAAEb,MAAM,EAAEH,KAAK,CAACG;EAAO,CAAC,EAAE,CAACc,CAAC,EAAEC,CAAC,KAAKA,CAAC,CAAC,CAChEC,IAAI,CAAC,MAAMZ,IAAI,CAACa,MAAM,CAAC,CAAC,GAAG,GAAG,CAAC,CAC/BC,KAAK,CAAC,CAAC,EAAEX,gBAAgB,CAAC;;EAE7B;EACAI,SAAS,CAACQ,OAAO,CAAEC,GAAG,IAAK;IACzBX,KAAK,CAACW,GAAG,CAAC,GAAGlB,cAAc;EAC7B,CAAC,CAAC;EAEF,OAAOO,KAAK,CAACY,IAAI,CAAC,EAAE,CAAC;AACvB,CAAC;AAACC,OAAA,CAAA1B,eAAA,GAAAA,eAAA"}
@@ -1,5 +1,6 @@
1
1
  import * as Issuance from "./issuance";
2
2
  import * as Presentation from "./presentation";
3
3
  import * as Status from "./status";
4
- export { Issuance, Presentation, Status };
4
+ import * as Trustmark from "./trustmark";
5
+ export { Issuance, Presentation, Status, Trustmark };
5
6
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["Issuance","Presentation","Status"],"sourceRoot":"../../../src","sources":["credential/index.ts"],"mappings":"AAAA,OAAO,KAAKA,QAAQ,MAAM,YAAY;AACtC,OAAO,KAAKC,YAAY,MAAM,gBAAgB;AAC9C,OAAO,KAAKC,MAAM,MAAM,UAAU;AAElC,SAASF,QAAQ,EAAEC,YAAY,EAAEC,MAAM"}
1
+ {"version":3,"names":["Issuance","Presentation","Status","Trustmark"],"sourceRoot":"../../../src","sources":["credential/index.ts"],"mappings":"AAAA,OAAO,KAAKA,QAAQ,MAAM,YAAY;AACtC,OAAO,KAAKC,YAAY,MAAM,gBAAgB;AAC9C,OAAO,KAAKC,MAAM,MAAM,UAAU;AAClC,OAAO,KAAKC,SAAS,MAAM,aAAa;AAExC,SAASH,QAAQ,EAAEC,YAAY,EAAEC,MAAM,EAAEC,SAAS"}
@@ -0,0 +1,62 @@
1
+ # Credential Trustmark
2
+
3
+ A credential TrustMark is a signed JWT that verifies the authenticity of a credential issued by a trusted source. It serves as proof that a credential is valid and linked to a specific wallet instance.
4
+ The TrustMark is often presented as a QR code, containing cryptographic data to ensure it hasn't been tampered with. It includes fields like issuer, issuance and expiration timestamps, and credential-specific details. TrustMarks have a short validity period and are used to enhance security and prevent misuse, such as QR code swapping.
5
+
6
+ ### getCredentialTrustmark
7
+
8
+ A function that generates a signed JWT Trustmark to verify the authenticity of a digital credential. The Trustmark serves as a cryptographic proof linking a credential to a specific wallet instance, ensuring the credential's validity and preventing unauthorized modifications or misuse.
9
+
10
+ #### Signature
11
+
12
+ ```typescript
13
+ function getCredentialTrustmark({
14
+ walletInstanceAttestation: string,
15
+ wiaCryptoContext: CryptoContext,
16
+ credentialType: string,
17
+ docNumber?: string,
18
+ expirationTime?: number | string
19
+ }): Promise<{
20
+ jwt: string,
21
+ expirationTime: number
22
+ }>
23
+ ```
24
+
25
+ #### Parameters
26
+ | Parameter | Type | Required | Description |
27
+ |-----------|------|----------|-------------|
28
+ | walletInstanceAttestation | string | Yes | A base64-encoded string containing the Wallet Instance Attestation (WIA). This attestation proves the authenticity of the wallet instance. |
29
+ | wiaCryptoContext | CryptoContext | Yes | The cryptographic context associated with the wallet instance. Must contain the same key pair used to generate the WIA. |
30
+ | credentialType | string | Yes | Identifier for the type of credential (e.g., "MDL" for Mobile Driver's License). |
31
+ | docNumber | string | No | The document number of the credential. If provided, it will be obfuscated in the Trustmark for privacy. |
32
+ | expirationTime | number \| string | No | Specifies when the Trustmark expires. Can be either:<br>- A timestamp in seconds<br>- A time span string (e.g., "2m" for 2 minutes)<br>Default: "2m" |
33
+
34
+ #### Return Value
35
+
36
+ Returns a Promise that resolves to an object containing:
37
+ | Property | Type | Description |
38
+ |----------|------|-------------|
39
+ | jwt | string | The signed trustmark JWT string |
40
+ | expirationTime | number | The expiration timestamp of the JWT in seconds |
41
+
42
+ ## Example
43
+
44
+ ```typescript
45
+ // Required inputs
46
+ const walletInstanceAttestation = "base64AttestationString";
47
+ const credentialType = "MDL"; // Credential type (e.g., Mobile Driver's License)
48
+ const documentNumber = "AB123456"; // Optional document number
49
+ const cryptoContext = createCryptoContextFor("wiaKeyTag"); // Sample crypto context
50
+
51
+ // Generate the TrustMark JWT
52
+ const { jwt, expirationTime } = await getCredentialTrustmark({
53
+ walletInstanceAttestation: "eyJ0eXAi...", // WIA JWT
54
+ wiaCryptoContext: cryptoContext,
55
+ credentialType: "IdentityCard",
56
+ docNumber: "AB123456",
57
+ expirationTime: "5m", // 5 minutes
58
+ });
59
+
60
+ console.log("Generated TrustMark JWT:", jwt);
61
+ console.log("Expires at:", new Date(expirationTime * 1000));
62
+ ```
@@ -0,0 +1,63 @@
1
+ import { SignJWT, thumbprint, decode as decodeJwt } from "@pagopa/io-react-native-jwt";
2
+ import * as WalletInstanceAttestation from "../../wallet-instance-attestation";
3
+ import { IoWalletError } from "../../utils/errors";
4
+ import { obfuscateString } from "../../utils/string";
5
+ /**
6
+ * Generates a trustmark signed JWT, which is used to verify the authenticity of a credential.
7
+ * The public key used to sign the trustmark must the same used for the Wallet Instance Attestation.
8
+ *
9
+ * @param walletInstanceAttestation the Wallet Instance's attestation
10
+ * @param wiaCryptoContext The Wallet Instance's crypto context associated with the walletInstanceAttestation parameter
11
+ * @param credentialType The type of credential for which the trustmark is generated
12
+ * @param docNumber (Optional) Document number contained in the credential, if applicable
13
+ * @param expirationTime (Optional) Expiration time for the trustmark, default is 2 minutes.
14
+ * If a number is provided, it is interpreted as a timestamp in seconds.
15
+ * If a string is provided, it is interpreted as a time span and added to the current timestamp.
16
+ * @throws {IoWalletError} If the public key associated to the WIA is not the same for the CryptoContext
17
+ * @returns A promise containing the signed JWT and its expiration time in seconds
18
+ */
19
+ export const getCredentialTrustmark = async _ref => {
20
+ let {
21
+ walletInstanceAttestation,
22
+ wiaCryptoContext,
23
+ credentialType,
24
+ docNumber,
25
+ expirationTime = "2m"
26
+ } = _ref;
27
+ /**
28
+ * Check that the public key used to sign the trustmark is the one used for the WIA
29
+ */
30
+ const holderBindingKey = await wiaCryptoContext.getPublicKey();
31
+ const decodedWia = WalletInstanceAttestation.decode(walletInstanceAttestation);
32
+
33
+ /**
34
+ * Verify holder binding by comparing thumbprints of the WIA and the CryptoContext key
35
+ */
36
+ const wiaThumbprint = await thumbprint(decodedWia.payload.cnf.jwk);
37
+ const cryptoContextThumbprint = await thumbprint(holderBindingKey);
38
+ if (wiaThumbprint !== cryptoContextThumbprint) {
39
+ throw new IoWalletError(`Failed to verify holder binding for status attestation, expected thumbprint: ${cryptoContextThumbprint}, got: ${wiaThumbprint}`);
40
+ }
41
+
42
+ /**
43
+ * Generate Trustmark signed JWT
44
+ */
45
+ const signedTrustmarkJwt = await new SignJWT(wiaCryptoContext).setProtectedHeader({
46
+ alg: "ES256"
47
+ }).setPayload({
48
+ iss: walletInstanceAttestation,
49
+ sub: credentialType,
50
+ /**
51
+ * If present, the document number is obfuscated before adding it to the payload
52
+ */
53
+ ...(docNumber ? {
54
+ subtyp: obfuscateString(docNumber)
55
+ } : {})
56
+ }).setIssuedAt().setExpirationTime(expirationTime).sign();
57
+ const decodedTrustmark = decodeJwt(signedTrustmarkJwt);
58
+ return {
59
+ jwt: signedTrustmarkJwt,
60
+ expirationTime: decodedTrustmark.payload.exp ?? 0
61
+ };
62
+ };
63
+ //# sourceMappingURL=get-credential-trustmark.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["SignJWT","thumbprint","decode","decodeJwt","WalletInstanceAttestation","IoWalletError","obfuscateString","getCredentialTrustmark","_ref","walletInstanceAttestation","wiaCryptoContext","credentialType","docNumber","expirationTime","holderBindingKey","getPublicKey","decodedWia","wiaThumbprint","payload","cnf","jwk","cryptoContextThumbprint","signedTrustmarkJwt","setProtectedHeader","alg","setPayload","iss","sub","subtyp","setIssuedAt","setExpirationTime","sign","decodedTrustmark","jwt","exp"],"sourceRoot":"../../../../src","sources":["credential/trustmark/get-credential-trustmark.ts"],"mappings":"AAAA,SACEA,OAAO,EACPC,UAAU,EAEVC,MAAM,IAAIC,SAAS,QACd,6BAA6B;AACpC,OAAO,KAAKC,yBAAyB,MAAM,mCAAmC;AAC9E,SAASC,aAAa,QAAQ,oBAAoB;AAClD,SAASC,eAAe,QAAQ,oBAAoB;AAoCpD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,sBAAiD,GAAG,MAAAC,IAAA,IAM3D;EAAA,IANkE;IACtEC,yBAAyB;IACzBC,gBAAgB;IAChBC,cAAc;IACdC,SAAS;IACTC,cAAc,GAAG;EACnB,CAAC,GAAAL,IAAA;EACC;AACF;AACA;EACE,MAAMM,gBAAgB,GAAG,MAAMJ,gBAAgB,CAACK,YAAY,CAAC,CAAC;EAC9D,MAAMC,UAAU,GAAGZ,yBAAyB,CAACF,MAAM,CACjDO,yBACF,CAAC;;EAED;AACF;AACA;EACE,MAAMQ,aAAa,GAAG,MAAMhB,UAAU,CAACe,UAAU,CAACE,OAAO,CAACC,GAAG,CAACC,GAAG,CAAC;EAClE,MAAMC,uBAAuB,GAAG,MAAMpB,UAAU,CAACa,gBAAgB,CAAC;EAElE,IAAIG,aAAa,KAAKI,uBAAuB,EAAE;IAC7C,MAAM,IAAIhB,aAAa,CACpB,gFAA+EgB,uBAAwB,UAASJ,aAAc,EACjI,CAAC;EACH;;EAEA;AACF;AACA;EACE,MAAMK,kBAAkB,GAAG,MAAM,IAAItB,OAAO,CAACU,gBAAgB,CAAC,CAC3Da,kBAAkB,CAAC;IAClBC,GAAG,EAAE;EACP,CAAC,CAAC,CACDC,UAAU,CAAC;IACVC,GAAG,EAAEjB,yBAAyB;IAC9BkB,GAAG,EAAEhB,cAAc;IACnB;AACN;AACA;IACM,IAAIC,SAAS,GAAG;MAAEgB,MAAM,EAAEtB,eAAe,CAACM,SAAS;IAAE,CAAC,GAAG,CAAC,CAAC;EAC7D,CAAC,CAAC,CACDiB,WAAW,CAAC,CAAC,CACbC,iBAAiB,CAACjB,cAAc,CAAC,CACjCkB,IAAI,CAAC,CAAC;EAET,MAAMC,gBAAgB,GAAG7B,SAAS,CAACmB,kBAAkB,CAAC;EAEtD,OAAO;IACLW,GAAG,EAAEX,kBAAkB;IACvBT,cAAc,EAAEmB,gBAAgB,CAACd,OAAO,CAACgB,GAAG,IAAI;EAClD,CAAC;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { getCredentialTrustmark } from "./get-credential-trustmark";
2
+ export { getCredentialTrustmark };
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["getCredentialTrustmark"],"sourceRoot":"../../../../src","sources":["credential/trustmark/index.ts"],"mappings":"AAAA,SAEEA,sBAAsB,QACjB,4BAA4B;AAEnC,SAASA,sBAAsB"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Randomly obfuscates characters in a string by replacing them with a specified character.
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * const value = "1234567890";
7
+ * const obfuscated = obfuscateString(value, 60, "*");
8
+ * // Could output: "12**5*78**"
9
+ * ```
10
+ *
11
+ * @param value - The input string to obfuscate
12
+ * @param percentage - Percentage of characters to obfuscate (0-100). Defaults to 60
13
+ * @param obfuscatedChar - Character used for obfuscation. Defaults to "*"
14
+ * @returns The obfuscated string with random characters replaced
15
+ */
16
+ export const obfuscateString = function (value) {
17
+ let percentage = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 60;
18
+ let obfuscatedChar = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "*";
19
+ if (!value) {
20
+ return "";
21
+ }
22
+
23
+ // Ensure percentage is between 0 and 100
24
+ const safePercentage = Math.max(0, Math.min(100, percentage));
25
+
26
+ // Calculate number of characters to obfuscate
27
+ const charsToObfuscate = Math.floor(value.length * safePercentage / 100);
28
+
29
+ // Convert string to array for manipulation
30
+ const chars = value.split("");
31
+
32
+ // Get random positions to obfuscate
33
+ const positions = Array.from({
34
+ length: value.length
35
+ }, (_, i) => i).sort(() => Math.random() - 0.5).slice(0, charsToObfuscate);
36
+
37
+ // Replace characters at random positions
38
+ positions.forEach(pos => {
39
+ chars[pos] = obfuscatedChar;
40
+ });
41
+ return chars.join("");
42
+ };
43
+ //# sourceMappingURL=string.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["obfuscateString","value","percentage","arguments","length","undefined","obfuscatedChar","safePercentage","Math","max","min","charsToObfuscate","floor","chars","split","positions","Array","from","_","i","sort","random","slice","forEach","pos","join"],"sourceRoot":"../../../src","sources":["utils/string.ts"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMA,eAAe,GAAG,SAAAA,CAC7BC,KAAa,EAGF;EAAA,IAFXC,UAAkB,GAAAC,SAAA,CAAAC,MAAA,QAAAD,SAAA,QAAAE,SAAA,GAAAF,SAAA,MAAG,EAAE;EAAA,IACvBG,cAAsB,GAAAH,SAAA,CAAAC,MAAA,QAAAD,SAAA,QAAAE,SAAA,GAAAF,SAAA,MAAG,GAAG;EAE5B,IAAI,CAACF,KAAK,EAAE;IACV,OAAO,EAAE;EACX;;EAEA;EACA,MAAMM,cAAc,GAAGC,IAAI,CAACC,GAAG,CAAC,CAAC,EAAED,IAAI,CAACE,GAAG,CAAC,GAAG,EAAER,UAAU,CAAC,CAAC;;EAE7D;EACA,MAAMS,gBAAgB,GAAGH,IAAI,CAACI,KAAK,CAAEX,KAAK,CAACG,MAAM,GAAGG,cAAc,GAAI,GAAG,CAAC;;EAE1E;EACA,MAAMM,KAAK,GAAGZ,KAAK,CAACa,KAAK,CAAC,EAAE,CAAC;;EAE7B;EACA,MAAMC,SAAS,GAAGC,KAAK,CAACC,IAAI,CAAC;IAAEb,MAAM,EAAEH,KAAK,CAACG;EAAO,CAAC,EAAE,CAACc,CAAC,EAAEC,CAAC,KAAKA,CAAC,CAAC,CAChEC,IAAI,CAAC,MAAMZ,IAAI,CAACa,MAAM,CAAC,CAAC,GAAG,GAAG,CAAC,CAC/BC,KAAK,CAAC,CAAC,EAAEX,gBAAgB,CAAC;;EAE7B;EACAI,SAAS,CAACQ,OAAO,CAAEC,GAAG,IAAK;IACzBX,KAAK,CAACW,GAAG,CAAC,GAAGlB,cAAc;EAC7B,CAAC,CAAC;EAEF,OAAOO,KAAK,CAACY,IAAI,CAAC,EAAE,CAAC;AACvB,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import * as Issuance from "./issuance";
2
2
  import * as Presentation from "./presentation";
3
3
  import * as Status from "./status";
4
- export { Issuance, Presentation, Status };
4
+ import * as Trustmark from "./trustmark";
5
+ export { Issuance, Presentation, Status, Trustmark };
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/credential/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,YAAY,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AAEnC,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/credential/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,YAAY,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AACnC,OAAO,KAAK,SAAS,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC"}
@@ -0,0 +1,50 @@
1
+ import { type CryptoContext } from "@pagopa/io-react-native-jwt";
2
+ export type GetCredentialTrustmarkJwt = (params: {
3
+ /**
4
+ * The Wallet Instance's attestation
5
+ */
6
+ walletInstanceAttestation: string;
7
+ /**
8
+ * The Wallet Instance's crypto context associated with the walletInstanceAttestation parameter
9
+ */
10
+ wiaCryptoContext: CryptoContext;
11
+ /**
12
+ * The type of credential for which the trustmark is generated
13
+ */
14
+ credentialType: string;
15
+ /**
16
+ * (Optional) Document number contained in the credential, if applicable
17
+ */
18
+ docNumber?: string;
19
+ /**
20
+ * (Optional) Expiration time for the trustmark, default is 2 minutes.
21
+ * If a number is provided, it is interpreted as a timestamp in seconds.
22
+ * If a string is provided, it is interpreted as a time span and added to the current timestamp.
23
+ */
24
+ expirationTime?: number | string;
25
+ }) => Promise<{
26
+ /**
27
+ * The signed JWT
28
+ */
29
+ jwt: string;
30
+ /**
31
+ * The expiration time of the JWT in seconds
32
+ */
33
+ expirationTime: number;
34
+ }>;
35
+ /**
36
+ * Generates a trustmark signed JWT, which is used to verify the authenticity of a credential.
37
+ * The public key used to sign the trustmark must the same used for the Wallet Instance Attestation.
38
+ *
39
+ * @param walletInstanceAttestation the Wallet Instance's attestation
40
+ * @param wiaCryptoContext The Wallet Instance's crypto context associated with the walletInstanceAttestation parameter
41
+ * @param credentialType The type of credential for which the trustmark is generated
42
+ * @param docNumber (Optional) Document number contained in the credential, if applicable
43
+ * @param expirationTime (Optional) Expiration time for the trustmark, default is 2 minutes.
44
+ * If a number is provided, it is interpreted as a timestamp in seconds.
45
+ * If a string is provided, it is interpreted as a time span and added to the current timestamp.
46
+ * @throws {IoWalletError} If the public key associated to the WIA is not the same for the CryptoContext
47
+ * @returns A promise containing the signed JWT and its expiration time in seconds
48
+ */
49
+ export declare const getCredentialTrustmark: GetCredentialTrustmarkJwt;
50
+ //# sourceMappingURL=get-credential-trustmark.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-credential-trustmark.d.ts","sourceRoot":"","sources":["../../../../src/credential/trustmark/get-credential-trustmark.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,aAAa,EAEnB,MAAM,6BAA6B,CAAC;AAKrC,MAAM,MAAM,yBAAyB,GAAG,CAAC,MAAM,EAAE;IAC/C;;OAEG;IACH,yBAAyB,EAAE,MAAM,CAAC;IAClC;;OAEG;IACH,gBAAgB,EAAE,aAAa,CAAC;IAChC;;OAEG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAClC,KAAK,OAAO,CAAC;IACZ;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;OAEG;IACH,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC,CAAC;AAEH;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,sBAAsB,EAAE,yBAoDpC,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { type GetCredentialTrustmarkJwt, getCredentialTrustmark } from "./get-credential-trustmark";
2
+ export { getCredentialTrustmark };
3
+ export type { GetCredentialTrustmarkJwt };
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/credential/trustmark/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,yBAAyB,EAC9B,sBAAsB,EACvB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EAAE,sBAAsB,EAAE,CAAC;AAElC,YAAY,EAAE,yBAAyB,EAAE,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Randomly obfuscates characters in a string by replacing them with a specified character.
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * const value = "1234567890";
7
+ * const obfuscated = obfuscateString(value, 60, "*");
8
+ * // Could output: "12**5*78**"
9
+ * ```
10
+ *
11
+ * @param value - The input string to obfuscate
12
+ * @param percentage - Percentage of characters to obfuscate (0-100). Defaults to 60
13
+ * @param obfuscatedChar - Character used for obfuscation. Defaults to "*"
14
+ * @returns The obfuscated string with random characters replaced
15
+ */
16
+ export declare const obfuscateString: (value: string, percentage?: number, obfuscatedChar?: string) => string;
17
+ //# sourceMappingURL=string.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"string.d.ts","sourceRoot":"","sources":["../../../src/utils/string.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,eAAe,UACnB,MAAM,eACD,MAAM,mBACF,MAAM,KACrB,MAyBF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pagopa/io-react-native-wallet",
3
- "version": "0.23.0",
3
+ "version": "0.24.0",
4
4
  "description": "Provide data structures, helpers and API for IO Wallet",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
@@ -1,5 +1,6 @@
1
1
  import * as Issuance from "./issuance";
2
2
  import * as Presentation from "./presentation";
3
3
  import * as Status from "./status";
4
+ import * as Trustmark from "./trustmark";
4
5
 
5
- export { Issuance, Presentation, Status };
6
+ export { Issuance, Presentation, Status, Trustmark };
@@ -0,0 +1,62 @@
1
+ # Credential Trustmark
2
+
3
+ A credential TrustMark is a signed JWT that verifies the authenticity of a credential issued by a trusted source. It serves as proof that a credential is valid and linked to a specific wallet instance.
4
+ The TrustMark is often presented as a QR code, containing cryptographic data to ensure it hasn't been tampered with. It includes fields like issuer, issuance and expiration timestamps, and credential-specific details. TrustMarks have a short validity period and are used to enhance security and prevent misuse, such as QR code swapping.
5
+
6
+ ### getCredentialTrustmark
7
+
8
+ A function that generates a signed JWT Trustmark to verify the authenticity of a digital credential. The Trustmark serves as a cryptographic proof linking a credential to a specific wallet instance, ensuring the credential's validity and preventing unauthorized modifications or misuse.
9
+
10
+ #### Signature
11
+
12
+ ```typescript
13
+ function getCredentialTrustmark({
14
+ walletInstanceAttestation: string,
15
+ wiaCryptoContext: CryptoContext,
16
+ credentialType: string,
17
+ docNumber?: string,
18
+ expirationTime?: number | string
19
+ }): Promise<{
20
+ jwt: string,
21
+ expirationTime: number
22
+ }>
23
+ ```
24
+
25
+ #### Parameters
26
+ | Parameter | Type | Required | Description |
27
+ |-----------|------|----------|-------------|
28
+ | walletInstanceAttestation | string | Yes | A base64-encoded string containing the Wallet Instance Attestation (WIA). This attestation proves the authenticity of the wallet instance. |
29
+ | wiaCryptoContext | CryptoContext | Yes | The cryptographic context associated with the wallet instance. Must contain the same key pair used to generate the WIA. |
30
+ | credentialType | string | Yes | Identifier for the type of credential (e.g., "MDL" for Mobile Driver's License). |
31
+ | docNumber | string | No | The document number of the credential. If provided, it will be obfuscated in the Trustmark for privacy. |
32
+ | expirationTime | number \| string | No | Specifies when the Trustmark expires. Can be either:<br>- A timestamp in seconds<br>- A time span string (e.g., "2m" for 2 minutes)<br>Default: "2m" |
33
+
34
+ #### Return Value
35
+
36
+ Returns a Promise that resolves to an object containing:
37
+ | Property | Type | Description |
38
+ |----------|------|-------------|
39
+ | jwt | string | The signed trustmark JWT string |
40
+ | expirationTime | number | The expiration timestamp of the JWT in seconds |
41
+
42
+ ## Example
43
+
44
+ ```typescript
45
+ // Required inputs
46
+ const walletInstanceAttestation = "base64AttestationString";
47
+ const credentialType = "MDL"; // Credential type (e.g., Mobile Driver's License)
48
+ const documentNumber = "AB123456"; // Optional document number
49
+ const cryptoContext = createCryptoContextFor("wiaKeyTag"); // Sample crypto context
50
+
51
+ // Generate the TrustMark JWT
52
+ const { jwt, expirationTime } = await getCredentialTrustmark({
53
+ walletInstanceAttestation: "eyJ0eXAi...", // WIA JWT
54
+ wiaCryptoContext: cryptoContext,
55
+ credentialType: "IdentityCard",
56
+ docNumber: "AB123456",
57
+ expirationTime: "5m", // 5 minutes
58
+ });
59
+
60
+ console.log("Generated TrustMark JWT:", jwt);
61
+ console.log("Expires at:", new Date(expirationTime * 1000));
62
+ ```
@@ -0,0 +1,111 @@
1
+ import {
2
+ SignJWT,
3
+ thumbprint,
4
+ type CryptoContext,
5
+ decode as decodeJwt,
6
+ } from "@pagopa/io-react-native-jwt";
7
+ import * as WalletInstanceAttestation from "../../wallet-instance-attestation";
8
+ import { IoWalletError } from "../../utils/errors";
9
+ import { obfuscateString } from "../../utils/string";
10
+
11
+ export type GetCredentialTrustmarkJwt = (params: {
12
+ /**
13
+ * The Wallet Instance's attestation
14
+ */
15
+ walletInstanceAttestation: string;
16
+ /**
17
+ * The Wallet Instance's crypto context associated with the walletInstanceAttestation parameter
18
+ */
19
+ wiaCryptoContext: CryptoContext;
20
+ /**
21
+ * The type of credential for which the trustmark is generated
22
+ */
23
+ credentialType: string;
24
+ /**
25
+ * (Optional) Document number contained in the credential, if applicable
26
+ */
27
+ docNumber?: string;
28
+ /**
29
+ * (Optional) Expiration time for the trustmark, default is 2 minutes.
30
+ * If a number is provided, it is interpreted as a timestamp in seconds.
31
+ * If a string is provided, it is interpreted as a time span and added to the current timestamp.
32
+ */
33
+ expirationTime?: number | string;
34
+ }) => Promise<{
35
+ /**
36
+ * The signed JWT
37
+ */
38
+ jwt: string;
39
+ /**
40
+ * The expiration time of the JWT in seconds
41
+ */
42
+ expirationTime: number;
43
+ }>;
44
+
45
+ /**
46
+ * Generates a trustmark signed JWT, which is used to verify the authenticity of a credential.
47
+ * The public key used to sign the trustmark must the same used for the Wallet Instance Attestation.
48
+ *
49
+ * @param walletInstanceAttestation the Wallet Instance's attestation
50
+ * @param wiaCryptoContext The Wallet Instance's crypto context associated with the walletInstanceAttestation parameter
51
+ * @param credentialType The type of credential for which the trustmark is generated
52
+ * @param docNumber (Optional) Document number contained in the credential, if applicable
53
+ * @param expirationTime (Optional) Expiration time for the trustmark, default is 2 minutes.
54
+ * If a number is provided, it is interpreted as a timestamp in seconds.
55
+ * If a string is provided, it is interpreted as a time span and added to the current timestamp.
56
+ * @throws {IoWalletError} If the public key associated to the WIA is not the same for the CryptoContext
57
+ * @returns A promise containing the signed JWT and its expiration time in seconds
58
+ */
59
+ export const getCredentialTrustmark: GetCredentialTrustmarkJwt = async ({
60
+ walletInstanceAttestation,
61
+ wiaCryptoContext,
62
+ credentialType,
63
+ docNumber,
64
+ expirationTime = "2m",
65
+ }) => {
66
+ /**
67
+ * Check that the public key used to sign the trustmark is the one used for the WIA
68
+ */
69
+ const holderBindingKey = await wiaCryptoContext.getPublicKey();
70
+ const decodedWia = WalletInstanceAttestation.decode(
71
+ walletInstanceAttestation
72
+ );
73
+
74
+ /**
75
+ * Verify holder binding by comparing thumbprints of the WIA and the CryptoContext key
76
+ */
77
+ const wiaThumbprint = await thumbprint(decodedWia.payload.cnf.jwk);
78
+ const cryptoContextThumbprint = await thumbprint(holderBindingKey);
79
+
80
+ if (wiaThumbprint !== cryptoContextThumbprint) {
81
+ throw new IoWalletError(
82
+ `Failed to verify holder binding for status attestation, expected thumbprint: ${cryptoContextThumbprint}, got: ${wiaThumbprint}`
83
+ );
84
+ }
85
+
86
+ /**
87
+ * Generate Trustmark signed JWT
88
+ */
89
+ const signedTrustmarkJwt = await new SignJWT(wiaCryptoContext)
90
+ .setProtectedHeader({
91
+ alg: "ES256",
92
+ })
93
+ .setPayload({
94
+ iss: walletInstanceAttestation,
95
+ sub: credentialType,
96
+ /**
97
+ * If present, the document number is obfuscated before adding it to the payload
98
+ */
99
+ ...(docNumber ? { subtyp: obfuscateString(docNumber) } : {}),
100
+ })
101
+ .setIssuedAt()
102
+ .setExpirationTime(expirationTime)
103
+ .sign();
104
+
105
+ const decodedTrustmark = decodeJwt(signedTrustmarkJwt);
106
+
107
+ return {
108
+ jwt: signedTrustmarkJwt,
109
+ expirationTime: decodedTrustmark.payload.exp ?? 0,
110
+ };
111
+ };
@@ -0,0 +1,8 @@
1
+ import {
2
+ type GetCredentialTrustmarkJwt,
3
+ getCredentialTrustmark,
4
+ } from "./get-credential-trustmark";
5
+
6
+ export { getCredentialTrustmark };
7
+
8
+ export type { GetCredentialTrustmarkJwt };
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Randomly obfuscates characters in a string by replacing them with a specified character.
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * const value = "1234567890";
7
+ * const obfuscated = obfuscateString(value, 60, "*");
8
+ * // Could output: "12**5*78**"
9
+ * ```
10
+ *
11
+ * @param value - The input string to obfuscate
12
+ * @param percentage - Percentage of characters to obfuscate (0-100). Defaults to 60
13
+ * @param obfuscatedChar - Character used for obfuscation. Defaults to "*"
14
+ * @returns The obfuscated string with random characters replaced
15
+ */
16
+ export const obfuscateString = (
17
+ value: string,
18
+ percentage: number = 60,
19
+ obfuscatedChar: string = "*"
20
+ ): string => {
21
+ if (!value) {
22
+ return "";
23
+ }
24
+
25
+ // Ensure percentage is between 0 and 100
26
+ const safePercentage = Math.max(0, Math.min(100, percentage));
27
+
28
+ // Calculate number of characters to obfuscate
29
+ const charsToObfuscate = Math.floor((value.length * safePercentage) / 100);
30
+
31
+ // Convert string to array for manipulation
32
+ const chars = value.split("");
33
+
34
+ // Get random positions to obfuscate
35
+ const positions = Array.from({ length: value.length }, (_, i) => i)
36
+ .sort(() => Math.random() - 0.5)
37
+ .slice(0, charsToObfuscate);
38
+
39
+ // Replace characters at random positions
40
+ positions.forEach((pos) => {
41
+ chars[pos] = obfuscatedChar;
42
+ });
43
+
44
+ return chars.join("");
45
+ };