Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add parameter for international key exchange #244

Merged
merged 5 commits into from
Mar 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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