From 9b73ddaa98fd977855485c093788b8795944719d Mon Sep 17 00:00:00 2001 From: Ivan Lele <110336129+ivanlele@users.noreply.github.com> Date: Fri, 21 Jun 2024 22:43:12 +0700 Subject: [PATCH] upgrade points security level (#66) * upgrade points security level * handle the points reverve conflict http status code --- Rarime.xcodeproj/project.pbxproj | 8 +-- .../xcshareddata/swiftpm/Package.resolved | 4 +- Rarime/Code/Managers/UserManager.swift | 24 ++++++++- Rarime/Code/Models/Points.swift | 29 +++++++++-- .../Views/ReserveTokensView.swift | 52 ++++++++++++++++--- .../Views/WaitlistPassportView.swift | 13 ++++- Rarime/Resources/Localizable.xcstrings | 3 ++ 7 files changed, 115 insertions(+), 18 deletions(-) diff --git a/Rarime.xcodeproj/project.pbxproj b/Rarime.xcodeproj/project.pbxproj index 5d64c368..cfae50d7 100644 --- a/Rarime.xcodeproj/project.pbxproj +++ b/Rarime.xcodeproj/project.pbxproj @@ -148,6 +148,7 @@ CE19CB472BE39DA400B26DF7 /* RarimoUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE19CB462BE39DA400B26DF7 /* RarimoUtils.swift */; }; CE19CB492BE3AD4200B26DF7 /* LockScreenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE19CB482BE3AD4200B26DF7 /* LockScreenView.swift */; }; CE1FA56A2C1708F100B2C191 /* Points.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1FA5692C1708F100B2C191 /* Points.swift */; }; + CE2FB5582C25C632002B1F98 /* Identity.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE2FB5572C25C632002B1F98 /* Identity.xcframework */; }; CE31E5462C11F4180039CAA5 /* GetStartedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE31E5452C11F4180039CAA5 /* GetStartedView.swift */; }; CE31E54A2C11FE150039CAA5 /* ImportIdentityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE31E5492C11FE150039CAA5 /* ImportIdentityView.swift */; }; CE32CF4F2BAC97A800E507B6 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE32CF4E2BAC97A800E507B6 /* MainView.swift */; }; @@ -155,7 +156,6 @@ CE3C6BA42C2310190055A20A /* libwitnesscalc_registerIdentityUniversalRSA4096.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CE3C6BA22C2310190055A20A /* libwitnesscalc_registerIdentityUniversalRSA4096.a */; }; CE3C6BA52C2310190055A20A /* libwitnesscalc_registerIdentityUniversalRSA2048.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CE3C6BA32C2310190055A20A /* libwitnesscalc_registerIdentityUniversalRSA2048.a */; }; CE3E0D3B2C2025CE0039DD50 /* AFError+RetriveOpenApiHttpCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE3E0D3A2C2025CE0039DD50 /* AFError+RetriveOpenApiHttpCode.swift */; }; - CE4119A22C24AC5F00BB26D7 /* Identity.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE4119A12C24AC5F00BB26D7 /* Identity.xcframework */; }; CE51E2E52C21790700043D9A /* CircuitDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE51E2E42C21790700043D9A /* CircuitDataManager.swift */; }; CE51E2FB2C21CA0B00043D9A /* StateKeeperContract.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE51E2FA2C21CA0B00043D9A /* StateKeeperContract.swift */; }; CE61E8832C24240000940129 /* Semaphore in Frameworks */ = {isa = PBXBuildFile; productRef = CE61E8822C24240000940129 /* Semaphore */; }; @@ -342,13 +342,13 @@ CE19CB462BE39DA400B26DF7 /* RarimoUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RarimoUtils.swift; sourceTree = ""; }; CE19CB482BE3AD4200B26DF7 /* LockScreenView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockScreenView.swift; sourceTree = ""; }; CE1FA5692C1708F100B2C191 /* Points.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Points.swift; sourceTree = ""; }; + CE2FB5572C25C632002B1F98 /* Identity.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = Identity.xcframework; sourceTree = ""; }; CE31E5452C11F4180039CAA5 /* GetStartedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetStartedView.swift; sourceTree = ""; }; CE31E5492C11FE150039CAA5 /* ImportIdentityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportIdentityView.swift; sourceTree = ""; }; CE32CF4E2BAC97A800E507B6 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = ""; }; CE3C6BA22C2310190055A20A /* libwitnesscalc_registerIdentityUniversalRSA4096.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libwitnesscalc_registerIdentityUniversalRSA4096.a; sourceTree = ""; }; CE3C6BA32C2310190055A20A /* libwitnesscalc_registerIdentityUniversalRSA2048.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libwitnesscalc_registerIdentityUniversalRSA2048.a; sourceTree = ""; }; CE3E0D3A2C2025CE0039DD50 /* AFError+RetriveOpenApiHttpCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AFError+RetriveOpenApiHttpCode.swift"; sourceTree = ""; }; - CE4119A12C24AC5F00BB26D7 /* Identity.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = Identity.xcframework; sourceTree = ""; }; CE51E2E42C21790700043D9A /* CircuitDataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircuitDataManager.swift; sourceTree = ""; }; CE51E2EC2C21A58200043D9A /* witnesscalc_registerIdentityUniversalRSA4096.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = witnesscalc_registerIdentityUniversalRSA4096.h; sourceTree = ""; }; CE51E2ED2C21A58200043D9A /* witnesscalc_registerIdentityUniversalRSA2048.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = witnesscalc_registerIdentityUniversalRSA2048.h; sourceTree = ""; }; @@ -422,7 +422,7 @@ CE3C6BA52C2310190055A20A /* libwitnesscalc_registerIdentityUniversalRSA2048.a in Frameworks */, 56E2D9322BD01E71006E725D /* NFCPassportReader in Frameworks */, CE3C6BA42C2310190055A20A /* libwitnesscalc_registerIdentityUniversalRSA4096.a in Frameworks */, - CE4119A22C24AC5F00BB26D7 /* Identity.xcframework in Frameworks */, + CE2FB5582C25C632002B1F98 /* Identity.xcframework in Frameworks */, 56933E082BBC09990068C894 /* QKMRZScanner in Frameworks */, CE8FEB5C2C1AFC3F0008381A /* libwitnesscalc_auth.a in Frameworks */, CE68BE472BD9B6EA00D92EBB /* Web3ContractABI in Frameworks */, @@ -1010,7 +1010,7 @@ CEC562122BD92804002D4954 /* Frameworks */ = { isa = PBXGroup; children = ( - CE4119A12C24AC5F00BB26D7 /* Identity.xcframework */, + CE2FB5572C25C632002B1F98 /* Identity.xcframework */, CE8FEB5B2C1AFC3F0008381A /* libwitnesscalc_auth.a */, CEC561FF2BD923B3002D4954 /* bridge.h */, CEC562022BD923B4002D4954 /* libfq.a */, diff --git a/Rarime.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Rarime.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index c5f94aca..5792f3f1 100644 --- a/Rarime.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Rarime.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -176,8 +176,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-http-types", "state" : { - "revision" : "9bee2fdb79cc740081abd8ebd80738063d632286", - "version" : "1.1.0" + "revision" : "1ddbea1ee34354a6a2532c60f98501c35ae8edfa", + "version" : "1.2.0" } }, { diff --git a/Rarime/Code/Managers/UserManager.swift b/Rarime/Code/Managers/UserManager.swift index 187b8d07..65666e86 100644 --- a/Rarime/Code/Managers/UserManager.swift +++ b/Rarime/Code/Managers/UserManager.swift @@ -445,8 +445,30 @@ class UserManager: ObservableObject { func reserveTokens(_ jwt: JWT, _ registerProof: ZkProof, _ passport: Passport) async throws { let queryProof = try await generatePointsProof(registerProof, passport) + var calculateAnonymousIDError: NSError? + let anonymousID = IdentityCalculateAnonymousID(passport.dg1, Points.PointsEventId, &calculateAnonymousIDError) + if let calculateAnonymousIDError { + throw calculateAnonymousIDError + } + + var error: NSError? + let hmacMessage = IdentityCalculateHmacMessage(jwt.payload.sub, passport.nationality, anonymousID, &error) + if let error { + throw error + } + + let key = Data(hex: ConfigManager.shared.api.joinRewardsKey) ?? Data() + + let hmacSingature = HMACUtils.hmacSha256(hmacMessage ?? Data(), key) + let points = Points(ConfigManager.shared.api.pointsServiceURL) - let _ = try await points.verifyPassport(jwt, queryProof) + let _ = try await points.verifyPassport( + jwt, + queryProof, + hmacSingature.hex, + passport.nationality, + anonymousID?.hex ?? "" + ) LoggerUtil.common.info("Passport verified, token reserved") diff --git a/Rarime/Code/Models/Points.swift b/Rarime/Code/Models/Points.swift index 4cbe29d1..e9430d6b 100644 --- a/Rarime/Code/Models/Points.swift +++ b/Rarime/Code/Models/Points.swift @@ -122,11 +122,18 @@ class Points { return response } - func verifyPassport(_ jwt: JWT, _ zkProof: ZkProof) async throws -> VerifyPassportResponse { + func verifyPassport( + _ jwt: JWT, + _ zkProof: ZkProof, + _ signature: String, + _ country: String, + _ anonymousId: String + ) async throws -> VerifyPassportResponse { let nullifier = jwt.payload.sub let headers = HTTPHeaders( [ + HTTPHeader(name: "Signature", value: signature), HTTPHeader(name: "Authorization", value: "Bearer \(jwt.raw)") ] ) @@ -138,6 +145,8 @@ class Points { id: nullifier, type: "verify_passport", attributes: VerifyPassportRequestAttributes( + anonymousId: anonymousId, + country: country, proof: zkProof ) ) @@ -286,7 +295,7 @@ class Points { return response } - func joinRewardsProgram(_ jwt: JWT, _ country: String, _ signature: String) async throws -> VerifyPassportResponse { + func joinRewardsProgram(_ jwt: JWT, _ country: String, _ signature: String, _ anonymousId: String) async throws -> VerifyPassportResponse { let headers = HTTPHeaders( [ HTTPHeader(name: "Signature", value: signature), @@ -299,8 +308,9 @@ class Points { let requestPayload = JoinRewardsProgramRequest( data: JoinRewardsProgramRequestData( id: jwt.payload.sub, - type: "join_program", + type: "verify_passport", attributes: JoinRewardsProgramRequestAttributes( + anonymousId: anonymousId, country: country ) ) @@ -462,7 +472,14 @@ struct VerifyPassportRequestData: Codable { } struct VerifyPassportRequestAttributes: Codable { + let anonymousId: String + let country: String let proof: ZkProof + + enum CodingKeys: String, CodingKey { + case anonymousId = "anonymous_id" + case country, proof + } } struct WithdrawalHistoryResponse: Codable { @@ -751,7 +768,13 @@ struct JoinRewardsProgramRequestData: Codable { } struct JoinRewardsProgramRequestAttributes: Codable { + let anonymousId: String let country: String + + enum CodingKeys: String, CodingKey { + case anonymousId = "anonymous_id" + case country = "country" + } } enum EventNames: String, Codable { diff --git a/Rarime/Code/Modules/ScanPassport/Views/ReserveTokensView.swift b/Rarime/Code/Modules/ScanPassport/Views/ReserveTokensView.swift index 43c6cb42..0ca8d059 100644 --- a/Rarime/Code/Modules/ScanPassport/Views/ReserveTokensView.swift +++ b/Rarime/Code/Modules/ScanPassport/Views/ReserveTokensView.swift @@ -1,4 +1,5 @@ import SwiftUI +import Alamofire struct ReserveTokensView: View { @EnvironmentObject private var decentralizedAuthManager: DecentralizedAuthManager @@ -11,6 +12,16 @@ struct ReserveTokensView: View { @State private var isReserving: Bool @State private var termsChecked: Bool + + @State private var isAlreadyReserved = false + + var reverveButtonText: LocalizedStringResource { + if isAlreadyReserved { + return "Close" + } else { + return isReserving ? "Reserving..." : "Reserve" + } + } init( showTerms: Bool = false, @@ -53,10 +64,25 @@ struct ReserveTokensView: View { FeedbackGenerator.shared.notify(.success) onFinish(true) } catch { - LoggerUtil.passport.error("Error while reserving tokens: \(error.localizedDescription, privacy: .public)") - FeedbackGenerator.shared.notify(.error) - onFinish(false) - AlertManager.shared.emitError(.serviceDown(nil)) + do { + guard let error = error as? AFError else { throw error } + + let openApiHttpCode = try error.retriveOpenApiHttpCode() + + if openApiHttpCode == HTTPStatusCode.conflict.rawValue { + isAlreadyReserved = true + isReserving = false + + return + } + + throw error + } catch { + LoggerUtil.passport.error("Error while reserving tokens: \(error.localizedDescription, privacy: .public)") + FeedbackGenerator.shared.notify(.error) + onFinish(false) + AlertManager.shared.emitError(.serviceDown(nil)) + } } } @@ -112,11 +138,25 @@ struct ReserveTokensView: View { AirdropCheckboxView(checked: $termsChecked) } AppButton( - text: isReserving ? "Reserving..." : "Reserve", - action: { Task { await reserveTokens() } } + text: reverveButtonText, + action: { + if isAlreadyReserved { + onFinish(false) + + return + } + + Task { await reserveTokens() } + } ) .disabled(isReserving || !termsChecked) .controlSize(.large) + if isAlreadyReserved { + Text("You have already reserved tokens") + .body3() + .foregroundStyle(.red) + .opacity(0.8) + } } .padding(.horizontal, 20) .padding(.top, 12) diff --git a/Rarime/Code/Modules/ScanPassport/Views/WaitlistPassportView.swift b/Rarime/Code/Modules/ScanPassport/Views/WaitlistPassportView.swift index 74d0adf0..e5beb139 100644 --- a/Rarime/Code/Modules/ScanPassport/Views/WaitlistPassportView.swift +++ b/Rarime/Code/Modules/ScanPassport/Views/WaitlistPassportView.swift @@ -90,8 +90,16 @@ struct WaitlistPassportView: View { let country = passportViewModel.passport?.nationality ?? "" + let dg1 = passportViewModel.passport?.dg1 ?? Data() + + var calculateAnonymousIDError: NSError? + let anonymousID = IdentityCalculateAnonymousID(dg1, Points.PointsEventId, &calculateAnonymousIDError) + if let calculateAnonymousIDError { + throw calculateAnonymousIDError + } + var error: NSError? - let hmacMessage = IdentityCalculateHmacMessage(accessJwt.payload.sub, country, &error) + let hmacMessage = IdentityCalculateHmacMessage(accessJwt.payload.sub, country, anonymousID, &error) if let error { throw error } @@ -104,7 +112,8 @@ struct WaitlistPassportView: View { let _ = try await points.joinRewardsProgram( accessJwt, country, - hmacSingature.hex + hmacSingature.hex, + anonymousID?.hex ?? "" ) LoggerUtil.common.info("User joined program") diff --git a/Rarime/Resources/Localizable.xcstrings b/Rarime/Resources/Localizable.xcstrings index 08f90993..d30f3dda 100644 --- a/Rarime/Resources/Localizable.xcstrings +++ b/Rarime/Resources/Localizable.xcstrings @@ -2776,6 +2776,9 @@ }, "You entered wrong passcode.\nLoading time: %@" : { + }, + "You have already reserved tokens" : { + }, "You must be invited or receive a code from social channels" : {