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

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.
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
+ };