@@ -3,12 +3,13 @@ import { FiscalCode, NonEmptyString } from "@pagopa/ts-commons/lib/strings";
33import { decode as cborDecode } from "cbor-x" ;
44import { createPublicKey } from "crypto" ;
55import { X509Certificate } from "crypto" ;
6+ import * as A from "fp-ts/Array" ;
67import * as E from "fp-ts/Either" ;
78import { pipe } from "fp-ts/function" ;
89import * as J from "fp-ts/Json" ;
10+ import { sequenceS } from "fp-ts/lib/Apply" ;
911import * as O from "fp-ts/Option" ;
1012import * as RA from "fp-ts/ReadonlyArray" ;
11- import * as A from "fp-ts/Array" ;
1213import * as TE from "fp-ts/TaskEither" ;
1314import { JwkPublicKey } from "io-wallet-common/jwk" ;
1415import { 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" ;
2932import {
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
211256export { ValidatedAttestation } ;
0 commit comments