Skip to content

Commit caa83d2

Browse files
committed
Append date offset
1 parent df0b7aa commit caa83d2

File tree

4 files changed

+71
-14
lines changed

4 files changed

+71
-14
lines changed

README.md

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,11 +162,11 @@ func metadataOutput(_ output: AVCaptureMetadataOutput,
162162

163163
You can store a code with your preferred method: Core Data, Realm, JSON file, raw data.
164164

165-
You need to store 3 informations:
165+
You need to store at least 3 informations:
166166

167167
* Code identifier
168-
* Date of scan / manual input
169-
* A 128 bits key generated from code identifier and date _(called transportableKey)_
168+
* Date of scan / manual input (calculation is based on day + month + year)
169+
* A 128 bits key generated from code identifier, date offset _(if present)_ and date _(called transportableKey)_
170170

171171
So, once you received the code, you need to generate local content like this:
172172

@@ -177,6 +177,18 @@ let content = LocalContent(codeId: <# The ID from code #>, date: <# Date of scan
177177

178178
<# Store your data from content #>
179179

180+
```
181+
If you want to add an offset for each date (ie 1 = morning, 2 = afternoon), it's possible.
182+
183+
You just need to store date offset. Date offset will be used in key calculation.
184+
185+
```swift
186+
let content = LocalContent(codeId: <# The ID from code #>, date: <# Date of scan #>, dateOffset: <# Offset #>)
187+
188+
// Then, you can use `content.codeId`, `content.date`, `content.dateOffset` and `content.transportableKey`
189+
190+
<# Store your data from content #>
191+
180192
```
181193

182194
### Export data from local storage
@@ -186,7 +198,7 @@ Your data are stored locally and you want to share them with other users.
186198
First, rebuild your content:
187199

188200
```swift
189-
let localContent = LocalContent((codeId: <# UUID #>, date: <# Date #>, transportableKey: <# Data #>)
201+
let localContent = LocalContent(codeId: <# UUID #>, date: <# Date #>, transportableKey: <# Data #>)
190202
```
191203

192204
Then, create transportable content with data required for diagnosis:

Sources/QRContactTracingCore/Common/LocalContent.swift

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,35 +35,51 @@ public struct LocalContent: Identifiable {
3535
/// Date when code was entered
3636
public let date: Date
3737

38+
/// Date offset
39+
public let dateOffset: Int?
40+
3841
/// Transportable key calculated from code id + date
3942
public let transportableKey: Data
4043

4144
/// A date formatter used for encryption
45+
@inline(__always)
4246
fileprivate static let dateFormatter: DateFormatter = {
4347
let df = DateFormatter()
4448
df.dateFormat = "yyyyMMdd"
4549
return df
4650
}()
47-
4851
/// Initializer
4952
///
5053
/// - parameter codeId: Code ID (see `Code.id`)
5154
/// - parameter date: Date when code was entered (default: now)
52-
/// - parameter transportableKey: Transportable key calculated from code id + date
53-
public init(codeId: UUID, date: Date = Date(), transportableKey: Data? = nil) {
55+
/// - parameter dateOffset: Custom date offset to specify each part of the day is concerned (default: nil)
56+
/// - parameter transportableKey: Transportable key calculated from code id + date + date offset
57+
public init(codeId: UUID, date: Date = Date(), dateOffset: Int? = nil, transportableKey: Data? = nil) {
5458
self.codeId = codeId
5559
self.date = date
56-
self.transportableKey = transportableKey ?? Self.buildTransportableKey(id: codeId, date: date)
60+
self.dateOffset = dateOffset
61+
self.transportableKey = transportableKey ?? Self.buildTransportableKey(id: codeId, date: date, dateOffset: dateOffset)
5762
}
5863
}
5964

6065
extension LocalContent {
6166

67+
@inline(__always)
68+
private static func buildPassword(date: Date, dateOffset: Int? = nil) -> String {
69+
var password = dateFormatter.string(from: date)
70+
71+
if let dateOffset = dateOffset {
72+
password = password + String(dateOffset)
73+
}
74+
75+
return password
76+
}
77+
6278
/// Build transportable key using PBKDF2
6379
@inline(__always)
64-
internal static func buildTransportableKey(id: UUID, date: Date) -> Data {
80+
internal static func buildTransportableKey(id: UUID, date: Date, dateOffset: Int? = nil) -> Data {
6581
let length: Int = kCCKeySizeAES128
66-
let password = dateFormatter.string(from: date)
82+
let password = buildPassword(date: date, dateOffset: dateOffset)
6783
let rawPassword: [CChar] = password.cString(using: .utf8)!.dropLast()
6884
let rawSalt: [CUnsignedChar] = Array(hexadecimalSalt(id))
6985
.chunked(into: 2)

Sources/QRContactTracingCore/Common/TransportableContent.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,12 @@ public struct TransportableContent: Identifiable {
3535
/// - parameter clearData: Supplement data that need to be encrypted (stored in `encryptedData`)
3636
@inline(__always)
3737
public init?(localContent: LocalContent, clearData: Data) {
38-
let key = localContent.transportableKey
38+
let iv = localContent.transportableKey
3939

4040
let data = Self.crypt(
4141
input: clearData,
4242
key: localContent.key,
43-
iv: key,
43+
iv: iv,
4444
operation: CCOperation(kCCEncrypt)
4545
)
4646

@@ -49,7 +49,7 @@ public struct TransportableContent: Identifiable {
4949
}
5050

5151
self.encryptedData = encryptedData
52-
self.key = key
52+
self.key = iv
5353
}
5454

5555
/// Initializer from external data

Tests/QRContactTracingCoreTests/Common/LocalContentTests.swift

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ final class LocalContentTests: XCTestCase {
2222
XCTAssertEqual(scannedContent.codeId, code.id)
2323
XCTAssertEqual(scannedContent.codeId, identifier)
2424
XCTAssertEqual(scannedContent.date, date)
25+
XCTAssertNil(scannedContent.dateOffset)
2526

2627
let rawContent = LocalContent(codeId: identifier, date: date, transportableKey: key)
2728

@@ -30,11 +31,39 @@ final class LocalContentTests: XCTestCase {
3031
XCTAssertEqual(rawContent.key, code.id.data)
3132
XCTAssertEqual(rawContent.key, rawContent.codeId.data)
3233
XCTAssertEqual(rawContent.date, date)
34+
XCTAssertNil(rawContent.dateOffset)
3335
XCTAssertEqual(rawContent.transportableKey, key)
3436
XCTAssertEqual(rawContent.transportableKey, LocalContent.buildTransportableKey(id: rawContent.codeId, date: rawContent.date))
3537
}
3638

39+
func testInitializationWithOffset() {
40+
let identifier = UUID()
41+
let code = MyCode(id: identifier)
42+
let date = Date()
43+
let dateOffset = 10
44+
45+
let scannedContent = LocalContent(codeId: code.id, date: date, dateOffset: dateOffset)
46+
let key = scannedContent.transportableKey
47+
48+
XCTAssertEqual(scannedContent.codeId, code.id)
49+
XCTAssertEqual(scannedContent.codeId, identifier)
50+
XCTAssertEqual(scannedContent.date, date)
51+
XCTAssertEqual(scannedContent.dateOffset, dateOffset)
52+
53+
let rawContent = LocalContent(codeId: identifier, date: date, dateOffset: dateOffset, transportableKey: key)
54+
55+
XCTAssertEqual(rawContent.codeId, code.id)
56+
XCTAssertEqual(rawContent.codeId, identifier)
57+
XCTAssertEqual(rawContent.key, code.id.data)
58+
XCTAssertEqual(rawContent.key, rawContent.codeId.data)
59+
XCTAssertEqual(rawContent.date, date)
60+
XCTAssertEqual(rawContent.dateOffset, dateOffset)
61+
XCTAssertEqual(rawContent.transportableKey, key)
62+
XCTAssertEqual(rawContent.transportableKey, LocalContent.buildTransportableKey(id: rawContent.codeId, date: rawContent.date, dateOffset: dateOffset))
63+
}
64+
3765
static var allTests = [
38-
("testInitialization", testInitialization)
66+
("testInitialization", testInitialization),
67+
("testInitializationWithOffset", testInitializationWithOffset)
3968
]
4069
}

0 commit comments

Comments
 (0)