Skip to content

Commit

Permalink
Merge pull request eu-digital-green-certificates#26 from eu-digital-g…
Browse files Browse the repository at this point in the history
…reen-certificates/feature/Download-Keys

Feature/download keys
  • Loading branch information
ascheibal authored Apr 28, 2021
2 parents e5ba427 + 3fb64e8 commit 8bf0fca
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 21 deletions.
20 changes: 15 additions & 5 deletions DGCAVerifier/Models/HCert.swift
Original file line number Diff line number Diff line change
Expand Up @@ -114,16 +114,24 @@ struct HCert {
}
#endif
print(header)
// print(body)
#if DEBUG
print(body)
#endif
return true
}

init?(from cborData: Data) {
let headerStr = CBOR.header(from: cborData)?.toString() ?? "{}"
let bodyStr = CBOR.payload(from: cborData)?.toString() ?? "{}"
rawData = cborData
guard
let headerStr = CBOR.header(from: cborData)?.toString(),
let bodyStr = CBOR.payload(from: cborData)?.toString(),
let kid = CBOR.kid(from: cborData)
else {
return nil
}
kidStr = KID.string(from: kid)
header = JSON(parseJSON: headerStr)
var body = JSON(parseJSON: bodyStr)
// print(body)
if body[ClaimKey.HCERT.rawValue].exists() {
body = body[ClaimKey.HCERT.rawValue]
}
Expand Down Expand Up @@ -158,6 +166,8 @@ struct HCert {
return info
}

var rawData: Data
var kidStr: String
var header: JSON
var body: JSON

Expand Down Expand Up @@ -224,7 +234,7 @@ struct HCert {
return .test
}
var isValid: Bool {
return Int.random(in: 0...9) < 5
return COSE.verify(rawData, with: LocalData.sharedInstance.encodedPublicKeys[kidStr] ?? "")
}
var validity: HCertValidity {
return isValid ? .valid : .invalid
Expand Down
28 changes: 26 additions & 2 deletions DGCAVerifier/Models/LocalData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,41 @@ struct LocalData: Codable {

var encodedPublicKeys = [String: String]()
var resumeToken: String?
var lastFetch_: Date?
var lastFetch: Date {
get {
lastFetch_ ?? .init(timeIntervalSince1970: 0)
}
set(v) {
lastFetch_ = v
}
}

static func add(encodedPublicKey: String) {
mutating func add(encodedPublicKey: String) {
let kid = KID.from(encodedPublicKey)
let kidStr = KID.string(from: kid)

sharedInstance.encodedPublicKeys[kidStr] = encodedPublicKey
encodedPublicKeys[kidStr] = encodedPublicKey
}

static func set(resumeToken: String) {
sharedInstance.resumeToken = resumeToken
}

public func save() {
Self.storage.save(self)
}

static let storage = SecureStorage<LocalData>()

static func initialize(completion: @escaping () -> Void) {
storage.loadOverride(fallback: LocalData.sharedInstance) { success in
guard let result = success else {
return
}
print("loaded \(result.encodedPublicKeys.count) certs from ", LocalData.storage.path ?? .init(fileURLWithPath: "/"))
LocalData.sharedInstance = result
completion()
}
}
}
78 changes: 74 additions & 4 deletions DGCAVerifier/Services/GatewayConnection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,25 @@

import Foundation
import Alamofire
import SwiftyJSON

struct GatewayConnection {
static let serverURI = "https://dgca-verifier-service.cfapps.eu10.hana.ondemand.com/"
static let updateEndpoint = "signercertificateUpdate"
static let statusEndpoint = "signercertificateStatus"

public static func fetchCert(resume resumeToken: String? = nil) {
AF.request(serverURI + updateEndpoint).response {
public static func certUpdate(resume resumeToken: String? = nil, completion: ((String?, String?) -> Void)?) {
var headers = [String: String]()
if let token = resumeToken {
headers["x-resume-token"] = token
}
AF.request(serverURI + updateEndpoint, method: .get, parameters: nil, encoding: URLEncoding(), headers: .init(headers), interceptor: nil, requestModifier: nil).response {
if
let status = $0.response?.statusCode,
status == 204 {
completion?(nil, nil)
return
}
guard
case let .success(result) = $0.result,
let response = result,
Expand All @@ -51,8 +62,67 @@ struct GatewayConnection {
if kidStr != responseKid {
return
}
LocalData.add(encodedPublicKey: responseStr)
LocalData.set(resumeToken: newResumeToken)
completion?(responseStr, newResumeToken)
}
}
public static func certStatus(resume resumeToken: String? = nil, completion: (([String]) -> Void)?) {
AF.request(serverURI + statusEndpoint).response {
guard
case let .success(result) = $0.result,
let response = result,
let responseStr = String(data: response, encoding: .utf8),
let json = JSON(parseJSON: responseStr).array
else {
return
}
let kids = json.compactMap { $0.string }
completion?(kids)
}
}

static var timer: Timer?

public static func initialize() {
timer?.invalidate()
timer = Timer.scheduledTimer(withTimeInterval: 60.0, repeats: true) {
_ in trigger()
}
timer?.tolerance = 1.0
trigger()
}

static func trigger() {
guard LocalData.sharedInstance.lastFetch.timeIntervalSinceNow < -24 * 60 * 60 else {
return
}
update()
}

static func update() {
certUpdate(resume: LocalData.sharedInstance.resumeToken) { encodedCert, token in
LocalData.sharedInstance.lastFetch = Date()
guard let encodedCert = encodedCert else {
status()
return
}
LocalData.sharedInstance.add(encodedPublicKey: encodedCert)
LocalData.sharedInstance.resumeToken = token
update()
}
}

static func status() {
certStatus { validKids in
var invalid = [String]()
for key in LocalData.sharedInstance.encodedPublicKeys.keys {
if !validKids.contains(key) {
invalid.append(key)
}
}
for key in invalid {
LocalData.sharedInstance.encodedPublicKeys.removeValue(forKey: key)
}
LocalData.sharedInstance.save()
}
}
}
5 changes: 3 additions & 2 deletions DGCAVerifier/Services/SecureStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ struct SecureStorage<T: Codable> {
completion?(false)
return
}
completion?(write(data: encrypted, signature: signature))
let success = write(data: encrypted, signature: signature)
completion?(success)
}
}

Expand All @@ -112,7 +113,7 @@ struct SecureStorage<T: Codable> {

func read() -> (Data, Data)? {
guard
let rawData = try? Data(contentsOf: path),
let rawData = try? Data(contentsOf: path, options: [.uncached]),
let result = try? JSONDecoder().decode(SecureDB.self, from: rawData)
else {
return nil
Expand Down
13 changes: 5 additions & 8 deletions DGCAVerifier/ViewControllers/Home.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,12 @@ import Foundation
import UIKit

class HomeVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

LocalData.storage.loadOverride(fallback: LocalData.sharedInstance) { [weak self] success in
guard let result = success else {
return
}
LocalData.sharedInstance = result
DispatchQueue.main.async {
GatewayConnection.timer?.invalidate()
LocalData.initialize {
DispatchQueue.main.async { [weak self] in
self?.performSegue(withIdentifier: "scanner", sender: self)
}
}
Expand Down
1 change: 1 addition & 0 deletions DGCAVerifier/ViewControllers/Scan.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class ScanVC: UIViewController {
checkPermissions()
setupCameraLiveView()
#endif
GatewayConnection.initialize()
}

override func viewWillDisappear(_ animated: Bool) {
Expand Down

0 comments on commit 8bf0fca

Please sign in to comment.