Skip to content

Commit

Permalink
Merge pull request #731 from minvws/bugfix/2.2.2-clock_deviation_revi…
Browse files Browse the repository at this point in the history
…sion

Hotfix: clock deviation
  • Loading branch information
iandundas authored Aug 12, 2021
2 parents 227bc0b + 4ad3a0d commit c9584ef
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class ClockDeviationManagerTests: XCTestCase {

sut = ClockDeviationManager(
remoteConfigManager: remoteConfigManagerSpy,
currentSystemUptime: { 1 * hour },
currentSystemUptime: { 1 * 3600 },
now: { now }
)
}
Expand Down
2 changes: 1 addition & 1 deletion Localizations/CoronaCheck holder/en/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@
"holder.dashboard.empty.domestic.title" = "Find your Dutch certificate here later";
"holder.dashboard.empty.domestic.message" = "<p>You can add a certificate if you're vaccinated, if you've had corona or by taking a test at an <a href=\"https://coronacheck.nl/en/testafspraak-in-app.html\">affiliated test location</a>. The app generates a certificate for the Netherlands and one for other countries.</p>";
"holder.dashboard.empty.international.title" = "Find your international certificate here later";
"holder.dashboard.empty.international.message" = "<p>Are you abroad, or planning to cross the border? Then always use your international certificate. Please check before leaving which certificate you need.</p><br /><p><a href=\"https://www.netherlandsworldwide.nl/travelling-outside-the-netherlands\">Which certificate is valid where I’m heading?</a></p>";
"holder.dashboard.empty.international.message" = "<p>Are you abroad, or planning to cross the border? Then always use your international certificate. Please check before leaving which certificate you need.</p><p><a href=\"https://www.netherlandsworldwide.nl/travelling-outside-the-netherlands\">Which certificate is valid where I’m heading?</a></p>";
/* MARK: - Holder Enlarged */
"holder.enlarged.screenshot.title" = "A screenshot is not a valid entrance ticket.";
"holder.enlarged.screenshot.message" = "A screenshot causes issues when scanning. You can only use the QR code in the app as a valid entrance ticket.";
Expand Down
4 changes: 2 additions & 2 deletions Localizations/CoronaCheck holder/nl/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@
/* MARK: - Holder Dashboard */
"holder.dashboard.title" = "Mijn bewijzen";
"holder.dashboard.intro.domestic" = "Laat de QR-code en je identiteitsbewijs zien bij de ingang van de locatie die je bezoekt.";
"holder.dashboard.intro.international" = "<p>Ben je in het buitenland of ga je de grens over? Gebruik dan altijd de internationale QR-code hieronder. Controleer voor vertrek welk bewijs je nodig hebt.</p><br /><p><a href=\"https://www.nederlandwereldwijd.nl/reizen/reisadviezen\">Is mijn bewijs geldig op mijn bestemming?</a></p>";
"holder.dashboard.intro.international" = "<p>Ben je in het buitenland of ga je de grens over? Gebruik dan altijd de internationale QR-code hieronder. Controleer voor vertrek welk bewijs je nodig hebt.</p><p><a href=\"https://www.nederlandwereldwijd.nl/reizen/reisadviezen\">Is mijn bewijs geldig op mijn bestemming?</a></p>";
"holder.dashboard.qr.expired" = "Je QR-code is verlopen.";
"holder.dashboard.qr.hour" = "uur";
"holder.dashboard.qr.minute" = "min";
Expand Down Expand Up @@ -349,7 +349,7 @@
"holder.dashboard.empty.domestic.title" = "Hier komt jouw Nederlandse bewijs";
"holder.dashboard.empty.domestic.message" = "<p>Je kunt een bewijs toevoegen als je bent gevaccineerd of hersteld. Of als je bent getest bij een <a href=\"https://coronacheck.nl/nl/testafspraak-in-app\">aangesloten testlocatie</a>. De app maakt een Nederlands en een internationaal bewijs.</p>";
"holder.dashboard.empty.international.title" = "Hier komt jouw internationale bewijs";
"holder.dashboard.empty.international.message" = "<p>Ben je in het buitenland of ga je de grens over? Gebruik dan altijd jouw internationale bewijs. Controleer voor vertrek welk bewijs je nodig hebt.</p><br /><p><a href=\"https://www.nederlandwereldwijd.nl/reizen/reisadviezen\">Welk bewijs is geldig op mijn bestemming?</a></p>";
"holder.dashboard.empty.international.message" = "<p>Ben je in het buitenland of ga je de grens over? Gebruik dan altijd jouw internationale bewijs. Controleer voor vertrek welk bewijs je nodig hebt.</p><p><a href=\"https://www.nederlandwereldwijd.nl/reizen/reisadviezen\">Welk bewijs is geldig op mijn bestemming?</a></p>";
/* MARK: - Holder Enlarged */
"holder.enlarged.screenshot.title" = "Een schermafbeelding is geen geldig toegangsbewijs";
"holder.enlarged.screenshot.message" = "Een schermafbeelding zorgt voor problemen bij het scannen. Alleen de QR-code in de app is een geldig toegangsbewijs.";
Expand Down
71 changes: 45 additions & 26 deletions Sources/CTR/Infrastructure/Services/ClockDeviationManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ protocol ClockDeviationManaging: AnyObject {
init()

func update(serverHeaderDate: String)
func update(serverResponseDateTime: Date, localResponseDatetime: Date, localResponseSystemUptime: TimeInterval)
func update(serverResponseDateTime: Date, localResponseDateTime: Date, localResponseSystemUptime: __darwin_time_t)

func appendDeviationChangeObserver(_ observer: @escaping (Bool) -> Void) -> ClockDeviationManager.ObserverToken
func removeDeviationChangeObserver(token: ClockDeviationManager.ObserverToken)
Expand All @@ -24,31 +24,33 @@ class ClockDeviationManager: ClockDeviationManaging, Logging {

var hasSignificantDeviation: Bool? {
guard let serverResponseDateTime = serverResponseDateTime,
let localResponseDatetime = localResponseDatetime,
let localResponseDatetime = localResponseDateTime,
let localResponseSystemUptime = localResponseSystemUptime,
let clockDeviationThresholdSeconds = clockDeviationThresholdSeconds
let clockDeviationThresholdSeconds = clockDeviationThresholdSeconds,
let systemUptime = currentSystemUptime()
else { return nil }

let result = ClockDeviationManager.hasSignificantDeviation(
serverResponseDateTime: serverResponseDateTime,
localResponseDatetime: localResponseDatetime,
clockDeviationThresholdSeconds: clockDeviationThresholdSeconds,
localSystemUptime: localResponseSystemUptime,
systemUptime: currentSystemUptime(),
now: now()
localResponseDateTime: localResponseDatetime,
localResponseSystemUptime: localResponseSystemUptime,
currentDate: now(),
currentSystemUptime: systemUptime,
clockDeviationThresholdSeconds: clockDeviationThresholdSeconds
)

return result
}

private var serverResponseDateTime: Date?
private var localResponseDatetime: Date?
private var localResponseSystemUptime: TimeInterval?
private var localResponseDateTime: Date?
private var localResponseSystemUptime: __darwin_time_t?

private var clockDeviationThresholdSeconds: Double? {
remoteConfigManager.getConfiguration().clockDeviationThresholdSeconds.map { Double($0) }
}
private let remoteConfigManager: RemoteConfigManaging
private let currentSystemUptime: () -> TimeInterval
private let currentSystemUptime: () -> __darwin_time_t?
private let now: () -> Date
private var deviationChangeObservers = [ObserverToken: (Bool) -> Void]()
private lazy var serverHeaderDateFormatter: DateFormatter = {
Expand All @@ -61,14 +63,14 @@ class ClockDeviationManager: ClockDeviationManaging, Logging {
required convenience init() {
self.init(
remoteConfigManager: Services.remoteConfigManager,
currentSystemUptime: { ProcessInfo.processInfo.systemUptime },
currentSystemUptime: { ClockDeviationManager.currentSystemUptime() },
now: { Date() }
)
}

required init(
remoteConfigManager: RemoteConfigManaging = Services.remoteConfigManager,
currentSystemUptime: @escaping () -> TimeInterval = { ProcessInfo.processInfo.systemUptime },
currentSystemUptime: @escaping () -> __darwin_time_t? = { ClockDeviationManager.currentSystemUptime() },
now: @escaping () -> Date = { Date() }
) {
self.remoteConfigManager = remoteConfigManager
Expand All @@ -92,18 +94,20 @@ class ClockDeviationManager: ClockDeviationManaging, Logging {
/// Update using the Server Response Header string
/// e.g. "Sat, 07 Aug 2021 12:12:57 GMT"
func update(serverHeaderDate: String) {
guard let serverDate = serverHeaderDateFormatter.date(from: serverHeaderDate) else { return }
guard let serverDate = serverHeaderDateFormatter.date(from: serverHeaderDate),
let systemUptime = currentSystemUptime()
else { return }

update(
serverResponseDateTime: serverDate,
localResponseDatetime: now(),
localResponseSystemUptime: currentSystemUptime()
localResponseDateTime: now(),
localResponseSystemUptime: systemUptime
)
}

func update(serverResponseDateTime: Date, localResponseDatetime: Date, localResponseSystemUptime: TimeInterval) {
func update(serverResponseDateTime: Date, localResponseDateTime: Date, localResponseSystemUptime: __darwin_time_t) {
self.serverResponseDateTime = serverResponseDateTime
self.localResponseDatetime = localResponseDatetime
self.localResponseDateTime = localResponseDateTime
self.localResponseSystemUptime = localResponseSystemUptime
notifyObservers()
}
Expand Down Expand Up @@ -138,19 +142,34 @@ class ClockDeviationManager: ClockDeviationManaging, Logging {
/// - now: the current local time
private static func hasSignificantDeviation(
serverResponseDateTime: Date,
localResponseDatetime: Date,
clockDeviationThresholdSeconds: Double,
localSystemUptime: TimeInterval,
systemUptime: TimeInterval,
now: Date = Date()
localResponseDateTime: Date,
localResponseSystemUptime: __darwin_time_t,
currentDate: Date = Date(),
currentSystemUptime: __darwin_time_t,
clockDeviationThresholdSeconds: Double
) -> Bool {

let responseSystemStartDatetime = localResponseDatetime.timeIntervalSince1970 - localSystemUptime
let currentSystemStartDateTime = now.timeIntervalSince1970 - systemUptime
let responseSystemStartDatetime = localResponseDateTime.timeIntervalSince1970 - TimeInterval(localResponseSystemUptime)
let currentSystemStartDateTime = currentDate.timeIntervalSince1970 - TimeInterval(currentSystemUptime)
let systemUptimeDelta = currentSystemStartDateTime - responseSystemStartDatetime
let responseTimeDelta = localResponseDatetime.timeIntervalSince1970 - serverResponseDateTime.timeIntervalSince1970
let responseTimeDelta = localResponseDateTime.timeIntervalSince1970 - serverResponseDateTime.timeIntervalSince1970

let hasDeviation = abs(responseTimeDelta + systemUptimeDelta) >= clockDeviationThresholdSeconds
return hasDeviation
}

private static func currentSystemUptime() -> __darwin_time_t? {

var uptime = timespec()

// `CLOCK_MONOTONIC` represents the absolute elapsed wall-clock time since some arbitrary,
// fixed point in the past. It isn't affected by changes in the system time-of-day clock.

// Check response is 0 else there was an error:
guard clock_gettime(CLOCK_MONOTONIC, &uptime) == 0 else {
return nil
}

return uptime.tv_sec
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,8 @@ final class HolderDashboardViewModel: Logging {
myQRCards: [],
expiredGreenCards: [],
showCreateCard: true,
isRefreshingStrippen: false
isRefreshingStrippen: false,
deviceHasClockDeviation: Services.clockDeviationManager.hasSignificantDeviation ?? false
)

self.datasource.didUpdate = { [weak self] (qrCardDataItems: [MyQRCard], expiredGreenCards: [ExpiredQR]) in
Expand Down Expand Up @@ -294,7 +295,7 @@ final class HolderDashboardViewModel: Logging {
]
}

if state.deviceHasClockDeviation && validityRegion == .domestic && !regionFilteredMyQRCards.isEmpty {
if state.deviceHasClockDeviation && !allQRCards.isEmpty {
viewControllerCards += [
.deviceHasClockDeviation(message: L.holderDashboardClockDeviationDetectedMessage(), didTapMoreInfo: {
coordinatorDelegate.userWishesMoreInfoAboutClockDeviation()
Expand Down

0 comments on commit c9584ef

Please sign in to comment.