Skip to content

Commit

Permalink
Merge pull request #244 from DP-3T/feature/travel
Browse files Browse the repository at this point in the history
Add parameter for international key exchange
  • Loading branch information
ubfelix authored Mar 4, 2021
2 parents e1b1ba2 + b8bf03b commit ccb29b9
Show file tree
Hide file tree
Showing 10 changed files with 90 additions and 21 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
# Changelog for DP3T-SDK iOS

## Next version
- Add support for international key exchange with parameter 'federationGateway'

## Version 2.1.0 (21.12.2020)
- Add support for iOS 12.5
- Add support for iOS 12.5
- Fix timeshift detection

## Version 2.0.0 (29.10.2020)
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,17 @@ Included in this repository is a Calibration App that can run, debug and test th
### Initialization
Name | Description | Function Name
---- | ----------- | -------------
init | Initializes the SDK and configures it | `initialize(applicationDescriptor:urlSession:backgroundHandler)`
init | Initializes the SDK and configures it | `initialize(applicationDescriptor:urlSession:backgroundHandler:federationGateway)`

### Methods
Name | Description | Function Name
### Methods & Properties
Name | Description | Function/Variable Name
---- | ----------- | -------------
startTracing | Starts EN tracing | `func startTracing(completionHandler:)`
stopTracing | Stops EN tracing | `func stopTracing(completionHandler:)`
sync | Pro-actively triggers sync with backend to refresh exposed list | `func sync(callback:)`
status | Returns a TracingState-Object describing the current state. This contains:<br/>- `numberOfHandshakes` : `Int` <br /> - `trackingState` : `TrackingState` <br /> - `lastSync` : `Date` <br /> - `infectionStatus`:`InfectionStatus`<br /> - `backgroundRefreshState`:`UIBackgroundRefreshStatus ` | `func status(callback:)`
iWasExposed | This method must be called upon positive test. | `func iWasExposed(onset:authentication:isFakeRequest:callback:)`
iWasExposed | This method must be called upon positive test. | `func iWasExposed(onset:authentication:isFakeRequest:callback:)`
federationGateway | Specifies whether keys should be exchanged with other compatible countries. Possible values are 'yes', 'no', 'unspecified' (default) | `var federationGateway`
reset | Removes all SDK related data | `func reset()`


Expand Down
35 changes: 30 additions & 5 deletions Sources/DP3TSDK/DP3TSDK.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,11 @@ class DP3TSDK {
/// - applicationDescriptor: information about the backend to use
/// - urlSession: the url session to use for networking (app can set it to enable certificate pinning)
/// - backgroundHandler: handler which gets called on background execution
/// - federationGateway: specifies whether keys should be shared with other countries
convenience init(applicationDescriptor: ApplicationDescriptor,
urlSession: URLSession,
backgroundHandler: DP3TBackgroundHandler?) {
backgroundHandler: DP3TBackgroundHandler?,
federationGateway: FederationGateway) {
// reset keychain on first launch
let defaults = Default.shared
if defaults.isFirstLaunch {
Expand All @@ -78,26 +80,28 @@ class DP3TSDK {
defaults.reset()
}

defaults.federationGateway = federationGateway

let exposureDayStorage = ExposureDayStorage()

let manager = ENManager()
let tracer = ExposureNotificationTracer(manager: manager)
let matcher = ExposureNotificationMatcher(manager: manager, exposureDayStorage: exposureDayStorage)
let diagnosisKeysProvider: DiagnosisKeysProvider = manager

let service = ExposeeServiceClient(descriptor: applicationDescriptor, urlSession: urlSession)
let serviceClient = ExposeeServiceClient(descriptor: applicationDescriptor, urlSession: urlSession, federationGateway: federationGateway)

let synchronizer = KnownCasesSynchronizer(matcher: matcher, service: service, descriptor: applicationDescriptor)
let synchronizer = KnownCasesSynchronizer(matcher: matcher, service: serviceClient, descriptor: applicationDescriptor)

let backgroundTaskManager = DP3TBackgroundTaskManager(handler: backgroundHandler, keyProvider: manager, serviceClient: service, tracer: tracer, manager: manager)
let backgroundTaskManager = DP3TBackgroundTaskManager(handler: backgroundHandler, keyProvider: manager, serviceClient: serviceClient, tracer: tracer, manager: manager)

self.init(applicationDescriptor: applicationDescriptor,
urlSession: urlSession,
tracer: tracer,
matcher: matcher,
diagnosisKeysProvider: diagnosisKeysProvider,
exposureDayStorage: exposureDayStorage,
service: service,
service: serviceClient,
synchronizer: synchronizer,
backgroundTaskManager: backgroundTaskManager,
defaults: defaults)
Expand Down Expand Up @@ -222,6 +226,16 @@ class DP3TSDK {
return state
}

var federationGateway: FederationGateway {
get {
return defaults.federationGateway
}
set {
defaults.federationGateway = newValue
service.federationGateway = newValue
}
}

/// tell the SDK that the user was exposed
/// This will stop tracing
/// - Parameters:
Expand Down Expand Up @@ -275,7 +289,18 @@ class DP3TSDK {

mutableKeys.append(contentsOf: self.diagnosisKeysProvider.getFakeKeys(count: fakeKeyCount, startingFrom: startingFrom))

let withFederationGateway: Bool?
switch self.federationGateway {
case .yes:
withFederationGateway = true
case .no:
withFederationGateway = false
case .unspecified:
withFederationGateway = nil
}

let model = ExposeeListModel(gaenKeys: mutableKeys,
withFederationGateway: withFederationGateway,
fake: isFakeRequest)

self.service.addExposeeList(model, authentication: authentication) { [weak self] result in
Expand Down
7 changes: 5 additions & 2 deletions Sources/DP3TSDK/DP3TTracing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,18 @@ public enum DP3TTracing {
/// - enviroment: enviroment to use
/// - urlSession: the url session to use for networking (can used to enable certificate pinning)
/// - backgroundHandler: a delegate to perform background tasks
/// - federationGateway: specifies whether keys should be shared with other countries
@available(iOS 12.5, *)
public static func initialize(with applicationDescriptor: ApplicationDescriptor,
urlSession: URLSession = .shared,
backgroundHandler: DP3TBackgroundHandler? = nil) {
backgroundHandler: DP3TBackgroundHandler? = nil,
federationGateway: FederationGateway = .unspecified) {
precondition(Self.isOSCompatible, "Operating System is not compatible")
precondition(instance == nil, "DP3TSDK already initialized")
instance = DP3TSDK(applicationDescriptor: applicationDescriptor,
urlSession: urlSession,
backgroundHandler: backgroundHandler)
backgroundHandler: backgroundHandler,
federationGateway: federationGateway)
}

@available(iOS 12.5, *)
Expand Down
8 changes: 6 additions & 2 deletions Sources/DP3TSDK/Models/ExposeeListModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,21 @@ struct ExposeeListModel: Encodable {
/// Diagnosis keys
let gaenKeys: [CodableDiagnosisKey]

let withFederationGateway: Bool?

let fake: Bool

func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
// Encode key
try container.encode(gaenKeys, forKey: .gaenKeys)

if let withFederationGateway = withFederationGateway {
try container.encode(withFederationGateway ? 1 : 0, forKey: .withFederationGateway)
}
try container.encode(fake ? 1 : 0, forKey: .fake)
}

enum CodingKeys: CodingKey {
case gaenKeys, fake
case gaenKeys, withFederationGateway, fake
}
}
22 changes: 18 additions & 4 deletions Sources/DP3TSDK/Networking/Endpoints.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,29 @@ struct ExposeeEndpoint {
/// Get the URL for the exposed people endpoint for a given lastKeyBundleTag
/// - Parameters:
/// - lastKeyBundleTag: last published key tag if one is stored
func getExposee(lastKeyBundleTag: String?) -> URL {
func getExposee(lastKeyBundleTag: String?, federationGateway: FederationGateway) -> URL {
let url = baseURLVersionned.appendingPathComponent("gaen")
.appendingPathComponent("exposed")

var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false)

var queryItems: [URLQueryItem] = []

if let lastKeyBundleTag = lastKeyBundleTag {
urlComponents?.queryItems = [
URLQueryItem(name: "lastKeyBundleTag", value: lastKeyBundleTag)
]
queryItems.append(URLQueryItem(name: "lastKeyBundleTag", value: lastKeyBundleTag))
}

switch federationGateway {
case .yes:
queryItems.append(URLQueryItem(name: "withFederationGateway", value: "true"))
case .no:
queryItems.append(URLQueryItem(name: "withFederationGateway", value: "false"))
case .unspecified:
break
}

if !queryItems.isEmpty {
urlComponents?.queryItems = queryItems
}

guard let finalUrl = urlComponents?.url else {
Expand Down
9 changes: 7 additions & 2 deletions Sources/DP3TSDK/Networking/ExposeeServiceClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ protocol ExposeeServiceClientProtocol: class {

var descriptor: ApplicationDescriptor { get }

var federationGateway: FederationGateway { get set }

/// Get all exposee for a known lastKeyBundleTag
/// - Parameters:
/// - since: last published key tag if one is stored
Expand Down Expand Up @@ -52,6 +54,8 @@ class ExposeeServiceClient: ExposeeServiceClientProtocol {

private let jwtVerifier: DP3TJWTVerifier?

var federationGateway: FederationGateway

private let log = Logger(ExposeeServiceClient.self, category: "exposeeServiceClient")

/// The user agent to send with the requests
Expand All @@ -66,7 +70,7 @@ class ExposeeServiceClient: ExposeeServiceClientProtocol {

/// Initialize the client with a descriptor
/// - Parameter descriptor: The descriptor to use
public init(descriptor: ApplicationDescriptor, urlSession: URLSession = .shared, urlCache: URLCache = .shared) {
public init(descriptor: ApplicationDescriptor, urlSession: URLSession = .shared, urlCache: URLCache = .shared, federationGateway: FederationGateway = .unspecified) {
self.descriptor = descriptor
self.urlSession = urlSession
self.urlCache = urlCache
Expand All @@ -77,6 +81,7 @@ class ExposeeServiceClient: ExposeeServiceClientProtocol {
} else {
jwtVerifier = nil
}
self.federationGateway = federationGateway
}
func detectTimeshift(response: HTTPURLResponse) -> DP3TNetworkingError? {
guard let date = response.date else { return nil }
Expand All @@ -102,7 +107,7 @@ class ExposeeServiceClient: ExposeeServiceClientProtocol {
/// - returns: array of objects or nil if they were already cached
func getExposee(lastKeyBundleTag: String?, completion: @escaping (Result<ExposeeSuccess, DP3TNetworkingError>) -> Void) -> URLSessionDataTask {
log.log("getExposeeSynchronously for lastPublishedKeyTag %{public}@", lastKeyBundleTag ?? "nil")
let url: URL = exposeeEndpoint.getExposee(lastKeyBundleTag: lastKeyBundleTag)
let url: URL = exposeeEndpoint.getExposee(lastKeyBundleTag: lastKeyBundleTag, federationGateway: federationGateway)

var request = URLRequest(url: url, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 60.0)
request.setValue("application/zip", forHTTPHeaderField: "Accept")
Expand Down
11 changes: 11 additions & 0 deletions Sources/DP3TSDK/Storage/Defaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@

import Foundation

public enum FederationGateway: Int, Codable {
case yes
case no
case unspecified
}

protocol DefaultStorage {
/// stores if this is the first launch of the SDK
var isFirstLaunch: Bool { get set }
Expand All @@ -27,6 +33,8 @@ protocol DefaultStorage {

var exposureDetectionDates: [Date] { get set }

var federationGateway: FederationGateway { get set }

func reset()
}

Expand All @@ -53,6 +61,9 @@ class Default: DefaultStorage {
@Persisted(userDefaultsKey: "org.dpppt.exposureDetectionDates", defaultValue: [])
var exposureDetectionDates: [Date]

@Persisted(userDefaultsKey: "org.dpppt.federationGateway", defaultValue: .unspecified)
var federationGateway: FederationGateway

/// Parameters
private func saveParameters(_ parameters: DP3TParameters) {

Expand Down
2 changes: 2 additions & 0 deletions Tests/DP3TSDKTests/Mocks/MockDefaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ class MockDefaults: DefaultStorage {

var didMarkAsInfected: Bool = false

var federationGateway: FederationGateway = .unspecified

func reset() {
exposureDetectionDates = []
lastKeyBundleTag = nil
Expand Down
3 changes: 2 additions & 1 deletion Tests/DP3TSDKTests/Mocks/MockService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@ import Foundation

class MockService: ExposeeServiceClientProtocol {


static var descriptor: ApplicationDescriptor = .init(appId: "org.dpppt", bucketBaseUrl: URL(string: "https://bucket.dpppt.org")!, reportBaseUrl: URL(string: "https://report.bucket.dpppt.org")!)

var descriptor: ApplicationDescriptor {
Self.descriptor
}

var federationGateway: FederationGateway = .unspecified

var requests: [String?] = []
let session = MockSession(data: "Data".data(using: .utf8), urlResponse: nil, error: nil)
let queue = DispatchQueue(label: "synchronous")
Expand Down

0 comments on commit ccb29b9

Please sign in to comment.