-
-
Notifications
You must be signed in to change notification settings - Fork 3.2k
/
Copy pathDeviceTransferService+URL.swift
88 lines (69 loc) · 3.62 KB
/
DeviceTransferService+URL.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import MultipeerConnectivity
import SignalServiceKit
extension DeviceTransferService {
private static let currentTransferVersion = 1
private static let versionKey = "version"
private static let peerIdKey = "peerId"
private static let certificateHashKey = "certificateHash"
private static let transferModeKey = "transferMode"
private enum Constants {
static let transferHost = "transfer"
}
func urlForTransfer(mode: TransferMode) throws -> URL {
guard let identity = identity else {
throw OWSAssertionError("unexpectedly missing identity")
}
var components = URLComponents()
components.scheme = UrlOpener.Constants.sgnlPrefix
components.host = Constants.transferHost
guard let base64CertificateHash = try identity.computeCertificateHash().base64EncodedString().encodeURIComponent else {
throw OWSAssertionError("failed to get base64 certificate hash")
}
guard let base64PeerId = try NSKeyedArchiver.archivedData(withRootObject: peerId, requiringSecureCoding: true).base64EncodedString().encodeURIComponent else {
throw OWSAssertionError("failed to get base64 peerId")
}
let queryItems = [
DeviceTransferService.versionKey: String(DeviceTransferService.currentTransferVersion),
DeviceTransferService.transferModeKey: mode.rawValue,
DeviceTransferService.certificateHashKey: base64CertificateHash,
DeviceTransferService.peerIdKey: base64PeerId
]
components.queryItems = queryItems.map { URLQueryItem(name: $0.key, value: $0.value) }
return components.url!
}
func parseTransferURL(_ url: URL) throws -> (peerId: MCPeerID, certificateHash: Data) {
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false), let queryItems = components.queryItems else {
throw OWSAssertionError("Invalid url")
}
let queryItemsDictionary = [String: String](uniqueKeysWithValues: queryItems.compactMap { item in
guard let value = item.value else { return nil }
return (item.name, value)
})
guard let version = queryItemsDictionary[DeviceTransferService.versionKey],
Int(version) == DeviceTransferService.currentTransferVersion else {
throw Error.unsupportedVersion
}
let currentMode: TransferMode = DependenciesBridge.shared.tsAccountManager
.registrationStateWithMaybeSneakyTransaction.isPrimaryDevice == true ? .primary : .linked
guard let rawMode = queryItemsDictionary[DeviceTransferService.transferModeKey],
rawMode == currentMode.rawValue else {
throw Error.modeMismatch
}
guard let base64CertificateHash = queryItemsDictionary[DeviceTransferService.certificateHashKey],
let uriDecodedHash = base64CertificateHash.removingPercentEncoding,
let certificateHash = Data(base64Encoded: uriDecodedHash) else {
throw OWSAssertionError("failed to decode certificate hash")
}
guard let base64PeerId = queryItemsDictionary[DeviceTransferService.peerIdKey],
let uriDecodedPeerId = base64PeerId.removingPercentEncoding,
let peerIdData = Data(base64Encoded: uriDecodedPeerId),
let peerId = try NSKeyedUnarchiver.unarchivedObject(ofClass: MCPeerID.self, from: peerIdData) else {
throw OWSAssertionError("failed to decode MCPeerId")
}
return (peerId, certificateHash)
}
}