Skip to content

Commit d832b71

Browse files
committed
updated
1 parent c0f0749 commit d832b71

File tree

3 files changed

+119
-108
lines changed

3 files changed

+119
-108
lines changed

apps/io-wallet-user-func/src/infra/mobile-attestation-service/android/index.ts

Lines changed: 14 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { NonEmptyString } from "@pagopa/ts-commons/lib/strings";
33
import { X509Certificate } from "crypto";
44
import * as E from "fp-ts/Either";
55
import { flow, pipe } from "fp-ts/function";
6-
import * as J from "fp-ts/Json";
76
import * as TE from "fp-ts/TaskEither";
87
import * as t from "io-ts";
98
import { AndroidDeviceDetails } from "io-wallet-common/device-details";
@@ -64,40 +63,25 @@ export const validateAndroidAssertion = (
6463
hardwareKey: JwkPublicKey,
6564
bundleIdentifiers: string[],
6665
androidPlayStoreCertificateHash: string,
67-
googleAppCredentialsEncoded: string,
66+
googleAppCredentials: GoogleAppCredentials,
6867
androidPlayIntegrityUrl: string,
6968
allowDevelopmentEnvironment: boolean,
7069
) =>
7170
pipe(
72-
E.tryCatch(
73-
() => Buffer.from(googleAppCredentialsEncoded, "base64").toString(),
74-
E.toError,
75-
),
76-
E.chain(J.parse),
77-
E.mapLeft(
71+
TE.tryCatch(
7872
() =>
79-
new AndroidAssertionError(
80-
"Unable to parse Google App Credentials string",
81-
),
82-
),
83-
E.chainW(parse(GoogleAppCredentials, "Invalid Google App Credentials")),
84-
TE.fromEither,
85-
TE.chain((googleAppCredentials) =>
86-
TE.tryCatch(
87-
() =>
88-
verifyAssertion({
89-
allowDevelopmentEnvironment,
90-
androidPlayIntegrityUrl,
91-
androidPlayStoreCertificateHash,
92-
bundleIdentifiers,
93-
clientData,
94-
googleAppCredentials,
95-
hardwareKey,
96-
hardwareSignature,
97-
integrityAssertion,
98-
}),
99-
E.toError,
100-
),
73+
verifyAssertion({
74+
allowDevelopmentEnvironment,
75+
androidPlayIntegrityUrl,
76+
androidPlayStoreCertificateHash,
77+
bundleIdentifiers,
78+
clientData,
79+
googleAppCredentials,
80+
hardwareKey,
81+
hardwareSignature,
82+
integrityAssertion,
83+
}),
84+
E.toError,
10185
),
10286
TE.chain((assertionValidationResult) =>
10387
assertionValidationResult.success

apps/io-wallet-user-func/src/infra/mobile-attestation-service/index.ts

Lines changed: 92 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@ import { FiscalCode, NonEmptyString } from "@pagopa/ts-commons/lib/strings";
33
import { decode as cborDecode } from "cbor-x";
44
import { createPublicKey } from "crypto";
55
import { X509Certificate } from "crypto";
6+
import * as A from "fp-ts/Array";
67
import * as E from "fp-ts/Either";
78
import { pipe } from "fp-ts/function";
89
import * as J from "fp-ts/Json";
10+
import { sequenceS } from "fp-ts/lib/Apply";
911
import * as O from "fp-ts/Option";
1012
import * as RA from "fp-ts/ReadonlyArray";
11-
import * as A from "fp-ts/Array";
1213
import * as TE from "fp-ts/TaskEither";
1314
import { JwkPublicKey } from "io-wallet-common/jwk";
1415
import { calculateJwkThumbprint } from "jose";
@@ -26,7 +27,10 @@ import {
2627
validateAndroidAssertion,
2728
validateAndroidAttestation,
2829
} from "./android";
30+
import { GoogleAppCredentials } from "./android/assertion";
31+
import { AndroidAssertionError } from "./errors";
2932
import {
33+
iOsAssertion,
3034
iOsAttestation,
3135
validateiOSAssertion,
3236
validateiOSAttestation,
@@ -88,20 +92,14 @@ export class MobileAttestationService implements AttestationService {
8892
),
8993
TE.chainW((clientData) =>
9094
pipe(
91-
E.tryCatch(
92-
() => cborDecode(Buffer.from(integrityAssertion, "base64")),
93-
E.toError,
94-
),
95-
E.chainW((decoded) =>
96-
iOsAttestation.decode(decoded)._tag === "Right"
97-
? E.right(decoded)
98-
: E.left(new Error("Not a valid iOS assertion")),
99-
),
95+
this.parseIosAssertion({
96+
hardwareSignature,
97+
integrityAssertion,
98+
}),
10099
TE.fromEither,
101-
TE.chainW(() =>
100+
TE.chainW((decodedAssertion) =>
102101
validateiOSAssertion(
103-
integrityAssertion,
104-
hardwareSignature,
102+
decodedAssertion,
105103
clientData,
106104
hardwareKey,
107105
signCount,
@@ -111,46 +109,30 @@ export class MobileAttestationService implements AttestationService {
111109
),
112110
),
113111
TE.orElseW(() =>
114-
validateAndroidAssertion(
115-
integrityAssertion,
116-
hardwareSignature,
117-
clientData,
118-
hardwareKey,
119-
this.#configuration.androidBundleIdentifiers,
120-
this.#configuration.androidPlayStoreCertificateHash,
121-
this.#configuration.googleAppCredentialsEncoded,
122-
this.#configuration.androidPlayIntegrityUrl,
123-
this.allowDevelopmentEnvironmentForUser(user),
112+
pipe(
113+
this.decodeGoogleAppCredentials(
114+
this.#configuration.googleAppCredentialsEncoded,
115+
),
116+
TE.fromEither,
117+
TE.chain((googleAppCredentials) =>
118+
validateAndroidAssertion(
119+
integrityAssertion,
120+
hardwareSignature,
121+
clientData,
122+
hardwareKey,
123+
this.#configuration.androidBundleIdentifiers,
124+
this.#configuration.androidPlayStoreCertificateHash,
125+
googleAppCredentials,
126+
this.#configuration.androidPlayIntegrityUrl,
127+
this.allowDevelopmentEnvironmentForUser(user),
128+
),
129+
),
124130
),
125131
),
126132
),
127133
),
128134
);
129135

130-
private parseIosAttestation = (data: Buffer) =>
131-
pipe(
132-
E.tryCatch(() => cborDecode(data), E.toError),
133-
E.chainW(
134-
parse(
135-
iOsAttestation,
136-
"[iOS Attestation] attestation format is invalid",
137-
),
138-
),
139-
);
140-
141-
private parseAndroidAttestation = (data: Buffer) =>
142-
pipe(
143-
data.toString("utf-8").split(","),
144-
A.map((b64) =>
145-
E.tryCatch(
146-
() => new X509Certificate(base64ToPem(b64)),
147-
() => new Error("Not a valid Android attestation (X509 parse failed)")
148-
)
149-
),
150-
A.sequence(E.Applicative),
151-
);
152-
153-
154136
validateAttestation = (
155137
attestation: NonEmptyString,
156138
nonce: NonEmptyString,
@@ -206,6 +188,69 @@ export class MobileAttestationService implements AttestationService {
206188
),
207189
),
208190
);
191+
192+
private decodeGoogleAppCredentials = (googleAppCredentialsEncoded: string) =>
193+
pipe(
194+
E.tryCatch(
195+
() => Buffer.from(googleAppCredentialsEncoded, "base64").toString(),
196+
E.toError,
197+
),
198+
E.chain(J.parse),
199+
E.mapLeft(
200+
() =>
201+
new AndroidAssertionError(
202+
"Unable to parse Google App Credentials string",
203+
),
204+
),
205+
E.chainW(parse(GoogleAppCredentials, "Invalid Google App Credentials")),
206+
);
207+
208+
private parseAndroidAttestation = (data: Buffer) =>
209+
pipe(
210+
data.toString("utf-8").split(","),
211+
A.map((b64) =>
212+
E.tryCatch(
213+
() => new X509Certificate(base64ToPem(b64)),
214+
() =>
215+
new Error("Not a valid Android attestation (X509 parse failed)"),
216+
),
217+
),
218+
A.sequence(E.Applicative),
219+
);
220+
221+
private parseIosAssertion = ({
222+
hardwareSignature,
223+
integrityAssertion,
224+
}: {
225+
hardwareSignature: NonEmptyString;
226+
integrityAssertion: NonEmptyString;
227+
}) =>
228+
pipe(
229+
sequenceS(E.Applicative)({
230+
authenticatorData: E.tryCatch(
231+
() => Buffer.from(integrityAssertion, "base64"),
232+
E.toError,
233+
),
234+
signature: E.tryCatch(
235+
() => Buffer.from(hardwareSignature, "base64"),
236+
E.toError,
237+
),
238+
}),
239+
E.chainW(
240+
parse(iOsAssertion, "[iOS Assertion] assertion format is invalid"),
241+
),
242+
);
243+
244+
private parseIosAttestation = (data: Buffer) =>
245+
pipe(
246+
E.tryCatch(() => cborDecode(data), E.toError),
247+
E.chainW(
248+
parse(
249+
iOsAttestation,
250+
"[iOS Attestation] attestation format is invalid",
251+
),
252+
),
253+
);
209254
}
210255

211256
export { ValidatedAttestation };

apps/io-wallet-user-func/src/infra/mobile-attestation-service/ios/index.ts

Lines changed: 13 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { parse } from "@pagopa/handler-kit";
22
import { NonEmptyString } from "@pagopa/ts-commons/lib/strings";
33
import * as E from "fp-ts/Either";
44
import { pipe } from "fp-ts/function";
5-
import { sequenceS } from "fp-ts/lib/Apply";
65
import * as TE from "fp-ts/TaskEither";
76
import * as t from "io-ts";
87
import { JwkPublicKey } from "io-wallet-common/jwk";
@@ -77,8 +76,7 @@ export const iOsAssertion = t.type({
7776
export type iOsAssertion = t.TypeOf<typeof iOsAssertion>;
7877

7978
export const validateiOSAssertion = (
80-
integrityAssertion: NonEmptyString,
81-
hardwareSignature: NonEmptyString,
79+
decodedAssertion: t.TypeOf<typeof iOsAssertion>,
8280
clientData: string,
8381
hardwareKey: JwkPublicKey,
8482
signCount: number,
@@ -87,34 +85,18 @@ export const validateiOSAssertion = (
8785
skipSignatureValidation: boolean,
8886
) =>
8987
pipe(
90-
sequenceS(E.Applicative)({
91-
authenticatorData: E.tryCatch(
92-
() => Buffer.from(integrityAssertion, "base64"),
93-
E.toError,
94-
),
95-
signature: E.tryCatch(
96-
() => Buffer.from(hardwareSignature, "base64"),
97-
E.toError,
98-
),
99-
}),
100-
E.chainW(
101-
parse(iOsAssertion, "[iOS Assertion] assertion format is invalid"),
102-
),
103-
TE.fromEither,
104-
TE.chain((decodedAssertion) =>
105-
TE.tryCatch(
106-
() =>
107-
verifyAssertion({
108-
bundleIdentifiers,
109-
clientData,
110-
decodedAssertion,
111-
hardwareKey,
112-
signCount,
113-
skipSignatureValidation,
114-
teamIdentifier,
115-
}),
116-
E.toError,
117-
),
88+
TE.tryCatch(
89+
() =>
90+
verifyAssertion({
91+
bundleIdentifiers,
92+
clientData,
93+
decodedAssertion,
94+
hardwareKey,
95+
signCount,
96+
skipSignatureValidation,
97+
teamIdentifier,
98+
}),
99+
E.toError,
118100
),
119101
TE.chain((assertionValidationResult) =>
120102
assertionValidationResult.success

0 commit comments

Comments
 (0)