|
7 | 7 | // SPDX-License-Identifier: AGPL-3.0-only |
8 | 8 | // |
9 | 9 |
|
10 | | -import Foundation |
11 | 10 | import CryptoKit |
| 11 | +import Foundation |
12 | 12 |
|
13 | 13 | struct DecryptReports { |
14 | 14 |
|
15 | | - /// Decrypt a find my report with the according key |
16 | | - /// - Parameters: |
17 | | - /// - report: An encrypted FindMy Report |
18 | | - /// - key: A FindMyKey |
19 | | - /// - Throws: Errors if the decryption fails |
20 | | - /// - Returns: An decrypted location report |
21 | | - static func decrypt(report: FindMyReport, with key: FindMyKey) throws -> FindMyLocationReport { |
22 | | - let payloadData = report.payload |
23 | | - let keyData = key.privateKey |
| 15 | + /// Decrypt a find my report with the according key |
| 16 | + /// - Parameters: |
| 17 | + /// - report: An encrypted FindMy Report |
| 18 | + /// - key: A FindMyKey |
| 19 | + /// - Throws: Errors if the decryption fails |
| 20 | + /// - Returns: An decrypted location report |
| 21 | + static func decrypt(report: FindMyReport, with key: FindMyKey) throws -> FindMyLocationReport { |
| 22 | + let payloadData = report.payload |
| 23 | + let keyData = key.privateKey |
| 24 | + |
| 25 | + let privateKey = keyData |
| 26 | + let ephemeralKey = payloadData.subdata(in: 5..<62) |
| 27 | + |
| 28 | + guard |
| 29 | + let sharedKey = BoringSSL.deriveSharedKey( |
| 30 | + fromPrivateKey: privateKey, |
| 31 | + andEphemeralKey: ephemeralKey) |
| 32 | + else { |
| 33 | + throw FindMyError.decryptionError(description: "Failed generating shared key") |
| 34 | + } |
24 | 35 |
|
25 | | - let privateKey = keyData |
26 | | - let ephemeralKey = payloadData.subdata(in: 5..<62) |
| 36 | + let derivedKey = self.kdf(fromSharedSecret: sharedKey, andEphemeralKey: ephemeralKey) |
27 | 37 |
|
28 | | - guard let sharedKey = BoringSSL.deriveSharedKey( |
29 | | - fromPrivateKey: privateKey, |
30 | | - andEphemeralKey: ephemeralKey) else { |
31 | | - throw FindMyError.decryptionError(description: "Failed generating shared key") |
32 | | - } |
| 38 | + print("Derived key \(derivedKey.base64EncodedString())") |
33 | 39 |
|
34 | | - let derivedKey = self.kdf(fromSharedSecret: sharedKey, andEphemeralKey: ephemeralKey) |
| 40 | + let encData = payloadData.subdata(in: 62..<72) |
| 41 | + let tag = payloadData.subdata(in: 72..<payloadData.endIndex) |
35 | 42 |
|
36 | | - print("Derived key \(derivedKey.base64EncodedString())") |
| 43 | + let decryptedContent = try self.decryptPayload( |
| 44 | + payload: encData, symmetricKey: derivedKey, tag: tag) |
| 45 | + let locationReport = self.decode(content: decryptedContent, report: report) |
| 46 | + print(locationReport) |
| 47 | + return locationReport |
| 48 | + } |
37 | 49 |
|
38 | | - let encData = payloadData.subdata(in: 62..<72) |
39 | | - let tag = payloadData.subdata(in: 72..<payloadData.endIndex) |
| 50 | + /// Decrypt the payload |
| 51 | + /// - Parameters: |
| 52 | + /// - payload: Encrypted payload part |
| 53 | + /// - symmetricKey: Symmetric key |
| 54 | + /// - tag: AES GCM tag |
| 55 | + /// - Throws: AES GCM error |
| 56 | + /// - Returns: Decrypted error |
| 57 | + static func decryptPayload(payload: Data, symmetricKey: Data, tag: Data) throws -> Data { |
| 58 | + let decryptionKey = symmetricKey.subdata(in: 0..<16) |
| 59 | + let iv = symmetricKey.subdata(in: 16..<symmetricKey.endIndex) |
40 | 60 |
|
41 | | - let decryptedContent = try self.decryptPayload(payload: encData, symmetricKey: derivedKey, tag: tag) |
42 | | - let locationReport = self.decode(content: decryptedContent, report: report) |
43 | | - print(locationReport) |
44 | | - return locationReport |
45 | | - } |
| 61 | + print("Decryption Key \(decryptionKey.base64EncodedString())") |
| 62 | + print("IV \(iv.base64EncodedString())") |
46 | 63 |
|
47 | | - /// Decrypt the payload |
48 | | - /// - Parameters: |
49 | | - /// - payload: Encrypted payload part |
50 | | - /// - symmetricKey: Symmetric key |
51 | | - /// - tag: AES GCM tag |
52 | | - /// - Throws: AES GCM error |
53 | | - /// - Returns: Decrypted error |
54 | | - static func decryptPayload(payload: Data, symmetricKey: Data, tag: Data) throws -> Data { |
55 | | - let decryptionKey = symmetricKey.subdata(in: 0..<16) |
56 | | - let iv = symmetricKey.subdata(in: 16..<symmetricKey.endIndex) |
57 | | - |
58 | | - print("Decryption Key \(decryptionKey.base64EncodedString())") |
59 | | - print("IV \(iv.base64EncodedString())") |
60 | | - |
61 | | - let sealedBox = try AES.GCM.SealedBox(nonce: AES.GCM.Nonce(data: iv), ciphertext: payload, tag: tag) |
62 | | - let symKey = SymmetricKey(data: decryptionKey) |
63 | | - let decrypted = try AES.GCM.open(sealedBox, using: symKey) |
64 | | - |
65 | | - return decrypted |
66 | | - } |
| 64 | + let sealedBox = try AES.GCM.SealedBox( |
| 65 | + nonce: AES.GCM.Nonce(data: iv), ciphertext: payload, tag: tag) |
| 66 | + let symKey = SymmetricKey(data: decryptionKey) |
| 67 | + let decrypted = try AES.GCM.open(sealedBox, using: symKey) |
67 | 68 |
|
68 | | - static func decode(content: Data, report: FindMyReport) -> FindMyLocationReport { |
69 | | - var longitude: Int32 = 0 |
70 | | - _ = withUnsafeMutableBytes(of: &longitude, {content.subdata(in: 4..<8).copyBytes(to: $0)}) |
71 | | - longitude = Int32(bigEndian: longitude) |
| 69 | + return decrypted |
| 70 | + } |
72 | 71 |
|
73 | | - var latitude: Int32 = 0 |
74 | | - _ = withUnsafeMutableBytes(of: &latitude, {content.subdata(in: 0..<4).copyBytes(to: $0)}) |
75 | | - latitude = Int32(bigEndian: latitude) |
| 72 | + static func decode(content: Data, report: FindMyReport) -> FindMyLocationReport { |
| 73 | + var longitude: Int32 = 0 |
| 74 | + _ = withUnsafeMutableBytes(of: &longitude, { content.subdata(in: 4..<8).copyBytes(to: $0) }) |
| 75 | + longitude = Int32(bigEndian: longitude) |
76 | 76 |
|
77 | | - var accuracy: UInt8 = 0 |
78 | | - _ = withUnsafeMutableBytes(of: &accuracy, {content.subdata(in: 8..<9).copyBytes(to: $0)}) |
| 77 | + var latitude: Int32 = 0 |
| 78 | + _ = withUnsafeMutableBytes(of: &latitude, { content.subdata(in: 0..<4).copyBytes(to: $0) }) |
| 79 | + latitude = Int32(bigEndian: latitude) |
79 | 80 |
|
80 | | - let latitudeDec = Double(latitude)/10000000.0 |
81 | | - let longitudeDec = Double(longitude)/10000000.0 |
| 81 | + var accuracy: UInt8 = 0 |
| 82 | + _ = withUnsafeMutableBytes(of: &accuracy, { content.subdata(in: 8..<9).copyBytes(to: $0) }) |
82 | 83 |
|
83 | | - return FindMyLocationReport(lat: latitudeDec, lng: longitudeDec, acc: accuracy, dP: report.datePublished, t: report.timestamp, c: report.confidence) |
84 | | - } |
| 84 | + let latitudeDec = Double(latitude) / 10000000.0 |
| 85 | + let longitudeDec = Double(longitude) / 10000000.0 |
85 | 86 |
|
86 | | - static func kdf(fromSharedSecret secret: Data, andEphemeralKey ephKey: Data) -> Data { |
| 87 | + return FindMyLocationReport( |
| 88 | + lat: latitudeDec, lng: longitudeDec, acc: accuracy, dP: report.datePublished, |
| 89 | + t: report.timestamp, c: report.confidence) |
| 90 | + } |
87 | 91 |
|
88 | | - var shaDigest = SHA256() |
89 | | - shaDigest.update(data: secret) |
90 | | - var counter: Int32 = 1 |
91 | | - let counterData = Data(Data(bytes: &counter, count: MemoryLayout.size(ofValue: counter)).reversed()) |
92 | | - shaDigest.update(data: counterData) |
93 | | - shaDigest.update(data: ephKey) |
| 92 | + static func kdf(fromSharedSecret secret: Data, andEphemeralKey ephKey: Data) -> Data { |
94 | 93 |
|
95 | | - let derivedKey = shaDigest.finalize() |
| 94 | + var shaDigest = SHA256() |
| 95 | + shaDigest.update(data: secret) |
| 96 | + var counter: Int32 = 1 |
| 97 | + let counterData = Data( |
| 98 | + Data(bytes: &counter, count: MemoryLayout.size(ofValue: counter)).reversed()) |
| 99 | + shaDigest.update(data: counterData) |
| 100 | + shaDigest.update(data: ephKey) |
96 | 101 |
|
97 | | - return Data(derivedKey) |
98 | | - } |
| 102 | + let derivedKey = shaDigest.finalize() |
| 103 | + |
| 104 | + return Data(derivedKey) |
| 105 | + } |
99 | 106 | } |
0 commit comments