]> git.saurik.com Git - apple/security.git/blob - keychain/TrustedPeersHelper/BottledPeer/BottledPeer.swift
Security-59306.11.20.tar.gz
[apple/security.git] / keychain / TrustedPeersHelper / BottledPeer / BottledPeer.swift
1 /*
2 * Copyright (c) 2019 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 import Foundation
25 import SecurityFoundation
26
27 class BottledPeer: NSObject {
28
29 public var escrowKeys: EscrowKeys
30 public var secret: Data
31 public var peerID: String
32 public var bottleID: String
33 public var peerKeys: OctagonSelfPeerKeys
34
35 public var signatureUsingEscrowKey: Data
36 public var signatureUsingPeerKey: Data
37 public var escrowSigningPublicKey: Data
38 public var escrowSigningSPKI: Data
39 public var peersigningSPKI: Data
40
41 public var contents: Data
42
43 public class func encryptionOperation() -> (_SFAuthenticatedEncryptionOperation) {
44 let keySpecifier = _SFAESKeySpecifier.init(bitSize: TPHObjectiveC.aes256BitSize())
45 return _SFAuthenticatedEncryptionOperation.init(keySpecifier: keySpecifier)
46 }
47
48 // Given a peer's details including private key material, and
49 // the keys generated from the escrow secret, encrypt the peer private keys,
50 // make a bottled peer object and serialize it into data.
51 public init (peerID: String, bottleID: String, peerSigningKey: _SFECKeyPair, peerEncryptionKey: _SFECKeyPair, bottleSalt: String) throws {
52
53 let secret = try BottledPeer.makeMeSomeEntropy(requiredLength: Int(OTMasterSecretLength))
54
55 self.secret = secret
56 self.escrowKeys = try EscrowKeys(secret: secret, bottleSalt: bottleSalt)
57
58 // Serialize the peer private keys into "contents"
59 guard let contentsObj = OTBottleContents() else {
60 throw Error.OTErrorBottleCreation
61 }
62 guard let signingPK = OTPrivateKey() else {
63 throw Error.OTErrorPrivateKeyCreation
64 }
65 signingPK.keyType = OTPrivateKey_KeyType.EC_NIST_CURVES
66 signingPK.keyData = peerSigningKey.keyData
67
68 guard let encryptionPK = OTPrivateKey() else {
69 throw Error.OTErrorPrivateKeyCreation
70 }
71 encryptionPK.keyType = OTPrivateKey_KeyType.EC_NIST_CURVES
72 encryptionPK.keyData = peerEncryptionKey.keyData
73
74 contentsObj.peerSigningPrivKey = signingPK
75 contentsObj.peerEncryptionPrivKey = encryptionPK
76 guard let clearContentsData = contentsObj.data else {
77 throw Error.OTErrorBottleCreation
78 }
79
80 // Encrypt the contents
81 let op = BottledPeer.encryptionOperation()
82 let cipher = try op.encrypt(clearContentsData, with: escrowKeys.symmetricKey)
83
84 let escrowSigningECPubKey: _SFECPublicKey = (escrowKeys.signingKey.publicKey as! _SFECPublicKey)
85 let escrowEncryptionECPubKey: _SFECPublicKey = escrowKeys.encryptionKey.publicKey as! _SFECPublicKey
86
87 let peerSigningECPublicKey: _SFECPublicKey = peerSigningKey.publicKey as! _SFECPublicKey
88 let peerEncryptionECPublicKey: _SFECPublicKey = peerEncryptionKey.publicKey as! _SFECPublicKey
89
90 // Serialize the whole thing
91 guard let obj = OTBottle() else {
92 throw Error.OTErrorBottleCreation
93 }
94 obj.peerID = peerID
95 obj.bottleID = bottleID
96 obj.escrowedSigningSPKI = escrowSigningECPubKey.encodeSubjectPublicKeyInfo()
97 obj.escrowedEncryptionSPKI = escrowEncryptionECPubKey.encodeSubjectPublicKeyInfo()
98 obj.peerSigningSPKI = peerSigningECPublicKey.encodeSubjectPublicKeyInfo()
99 obj.peerEncryptionSPKI = peerEncryptionECPublicKey.encodeSubjectPublicKeyInfo()
100
101 guard let authObj = OTAuthenticatedCiphertext() else {
102 throw Error.OTErrorAuthCipherTextCreation
103 }
104 authObj.ciphertext = cipher.ciphertext
105 authObj.authenticationCode = cipher.authenticationCode
106 authObj.initializationVector = cipher.initializationVector
107
108 obj.contents = authObj
109
110 self.peerID = peerID
111 self.bottleID = bottleID
112
113 try self.peerKeys = OctagonSelfPeerKeys(peerID: peerID, signingKey: peerSigningKey, encryptionKey: peerEncryptionKey)
114 self.contents = obj.data
115
116 let escrowedSigningECPublicKey = escrowKeys.signingKey.publicKey as! _SFECPublicKey
117
118 self.escrowSigningPublicKey = escrowedSigningECPublicKey.keyData
119 self.escrowSigningSPKI = escrowedSigningECPublicKey.encodeSubjectPublicKeyInfo()
120 self.peersigningSPKI = peerSigningECPublicKey.encodeSubjectPublicKeyInfo()
121
122 let xso = BottledPeer.signingOperation()
123
124 let signedByEscrowKey = try xso.sign(self.contents, with: escrowKeys.signingKey)
125 self.signatureUsingEscrowKey = signedByEscrowKey.signature
126
127 let signedByPeerKey = try xso.sign(self.contents, with: peerSigningKey)
128 self.signatureUsingPeerKey = signedByPeerKey.signature
129 }
130
131 // Deserialize a bottle (data) and decrypt the contents (peer keys)
132 // using the keys generated from the escrow secret, and signatures from signing keys
133 public init (contents: Data, secret: Data, bottleSalt: String, signatureUsingEscrow: Data, signatureUsingPeerKey: Data) throws {
134
135 self.secret = secret
136 self.escrowKeys = try EscrowKeys(secret: self.secret, bottleSalt: bottleSalt)
137
138 guard let escrowSigningECKey: _SFECPublicKey = escrowKeys.signingKey.publicKey() as? _SFECPublicKey else {
139 os_log("escrow key not an SFECPublicKey?", log: tplogDebug, type: .default)
140 throw Error.OTErrorBottleCreation
141 }
142 self.escrowSigningSPKI = escrowSigningECKey.encodeSubjectPublicKeyInfo()
143
144 // Deserialize the whole thing
145 guard let obj = OTBottle(data: contents) else {
146 os_log("Unable to deserialize bottle", log: tplogDebug, type: .default)
147 throw Error.OTErrorDeserializationFailure
148 }
149
150 // First, the easy check: did the entropy create the keys that are supposed to be in the bottle?
151 guard obj.escrowedSigningSPKI == self.escrowSigningSPKI else {
152 os_log("Bottled SPKI does not match re-created SPKI", log: tplogDebug, type: .default)
153 throw Error.OTErrorEntropyKeyMismatch
154 }
155
156 // Second, does the signature verify on the given data?
157 let xso = BottledPeer.signingOperation()
158
159 let escrowSigned = _SFSignedData.init(data: contents, signature: signatureUsingEscrow)
160 try xso.verify(escrowSigned, with: escrowSigningECKey)
161
162 // Now, decrypt contents
163 let op = BottledPeer.encryptionOperation()
164 let ac: OTAuthenticatedCiphertext = obj.contents as OTAuthenticatedCiphertext
165
166 let ciphertext = _SFAuthenticatedCiphertext.init(ciphertext: ac.ciphertext, authenticationCode: ac.authenticationCode, initializationVector: ac.initializationVector)
167
168 let clearContentsData = try op.decrypt(ciphertext, with: escrowKeys.symmetricKey)
169 if clearContentsData.count == 0 {
170 throw Error.OTErrorDecryptionFailure
171 }
172
173 // Deserialize contents into private peer keys
174 guard let contentsObj = OTBottleContents(data: clearContentsData) else {
175 throw Error.OTErrorDeserializationFailure
176 }
177
178 self.peerID = obj.peerID
179 self.bottleID = obj.bottleID
180
181 try self.peerKeys = OctagonSelfPeerKeys(peerID: peerID,
182 signingKey: try contentsObj.peerSigningPrivKey.asECKeyPair(),
183 encryptionKey: try contentsObj.peerEncryptionPrivKey.asECKeyPair())
184 self.contents = contents
185
186 self.peersigningSPKI = obj.peerSigningSPKI
187
188 let peerSigningPubKey = _SFECPublicKey(subjectPublicKeyInfo: obj.peerSigningSPKI)
189 let peerEncryptionPubKey = _SFECPublicKey(subjectPublicKeyInfo: obj.peerEncryptionSPKI)
190
191 // Check the private keys match the public keys
192 if self.peerKeys.signingKey.publicKey != peerSigningPubKey {
193 throw Error.OTErrorKeyMismatch
194 }
195 if self.peerKeys.encryptionKey.publicKey != peerEncryptionPubKey {
196 throw Error.OTErrorKeyMismatch
197 }
198
199 self.escrowSigningSPKI = escrowSigningECKey.encodeSubjectPublicKeyInfo()
200
201 self.signatureUsingPeerKey = signatureUsingPeerKey
202 self.signatureUsingEscrowKey = signatureUsingEscrow
203
204 self.escrowSigningPublicKey = escrowSigningECKey.keyData
205
206 let peerSigned = _SFSignedData.init(data: self.contents, signature: signatureUsingPeerKey)
207 guard let peerPublicKey = self.peerKeys.publicSigningKey else {
208 throw Error.OTErrorKeyMismatch
209 }
210 try xso.verify(peerSigned, with: peerPublicKey)
211 }
212
213 public func escrowSigningPublicKeyHash() -> String {
214 return TPHObjectiveC.digest(usingSha384: self.escrowSigningPublicKey)
215 }
216
217 public class func signingOperation() -> (_SFEC_X962SigningOperation) {
218 let keySpecifier = _SFECKeySpecifier.init(curve: SFEllipticCurve.nistp384)
219 let digestOperation = _SFSHA384DigestOperation.init()
220 return _SFEC_X962SigningOperation.init(keySpecifier: keySpecifier, digestOperation: digestOperation)
221 }
222
223 public class func verifyBottleSignature(data: Data, signature: Data, pubKey: _SFECPublicKey) throws -> (Bool) {
224 let xso = BottledPeer.signingOperation()
225 let peerSigned = _SFSignedData.init(data: data, signature: signature)
226 try xso.verify(peerSigned, with: pubKey)
227 return true
228 }
229
230 public class func makeMeSomeEntropy(requiredLength: Int) throws -> Data {
231 let bytesPointer = UnsafeMutableRawPointer.allocate(byteCount: requiredLength, alignment: 1)
232
233 if SecRandomCopyBytes(kSecRandomDefault, requiredLength, bytesPointer) != 0 {
234 throw Error.OTErrorEntropyCreation
235 }
236 return Data(bytes: bytesPointer, count: requiredLength)
237 }
238 }
239
240 extension BottledPeer {
241 enum Error: Swift.Error {
242 case OTErrorDeserializationFailure
243 case OTErrorDecryptionFailure
244 case OTErrorKeyInstantiation
245 case OTErrorKeyMismatch
246 case OTErrorBottleCreation
247 case OTErrorAuthCipherTextCreation
248 case OTErrorPrivateKeyCreation
249 case OTErrorEscrowKeyCreation
250 case OTErrorEntropyCreation
251 case OTErrorEntropyKeyMismatch
252 }
253 }
254
255 extension BottledPeer.Error: LocalizedError {
256 public var errorDescription: String? {
257 switch self {
258 case .OTErrorDeserializationFailure:
259 return "Failed to deserialize bottle peer"
260 case .OTErrorDecryptionFailure:
261 return "could not decrypt bottle contents"
262 case .OTErrorKeyInstantiation:
263 return "Failed to instantiate octagon peer keys"
264 case .OTErrorKeyMismatch:
265 return "public and private peer signing keys do not match"
266 case .OTErrorBottleCreation:
267 return "failed to create bottle"
268 case .OTErrorAuthCipherTextCreation:
269 return "failed to create authenticated ciphertext"
270 case .OTErrorPrivateKeyCreation:
271 return "failed to create private key"
272 case .OTErrorEscrowKeyCreation:
273 return "failed to create escrow keys"
274 case .OTErrorEntropyCreation:
275 return "failed to create entropy"
276 case .OTErrorEntropyKeyMismatch:
277 return "keys generated by the entropy+salt do not match the bottle contents"
278 }
279 }
280 }