]> git.saurik.com Git - apple/security.git/blob - keychain/TrustedPeersHelper/BottledPeer/BottledPeer.swift
Security-59754.41.1.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 var escrowKeys: EscrowKeys
29 var secret: Data
30 var peerID: String
31 var bottleID: String
32 var peerKeys: OctagonSelfPeerKeys
33
34 var signatureUsingEscrowKey: Data
35 var signatureUsingPeerKey: Data
36 var escrowSigningPublicKey: Data
37 var escrowSigningSPKI: Data
38 var peersigningSPKI: Data
39
40 var contents: Data
41
42 class func encryptionOperation() -> (_SFAuthenticatedEncryptionOperation) {
43 let keySpecifier = _SFAESKeySpecifier.init(bitSize: TPHObjectiveC.aes256BitSize())
44 return _SFAuthenticatedEncryptionOperation.init(keySpecifier: keySpecifier)
45 }
46
47 // Given a peer's details including private key material, and
48 // the keys generated from the escrow secret, encrypt the peer private keys,
49 // make a bottled peer object and serialize it into data.
50 init (peerID: String, bottleID: String, peerSigningKey: _SFECKeyPair, peerEncryptionKey: _SFECKeyPair, bottleSalt: String) throws {
51 let secret = try BottledPeer.makeMeSomeEntropy(requiredLength: Int(OTMasterSecretLength))
52
53 self.secret = secret
54 self.escrowKeys = try EscrowKeys(secret: secret, bottleSalt: bottleSalt)
55
56 // Serialize the peer private keys into "contents"
57 guard let contentsObj = OTBottleContents() else {
58 throw Error.OTErrorBottleCreation
59 }
60 guard let signingPK = OTPrivateKey() else {
61 throw Error.OTErrorPrivateKeyCreation
62 }
63 signingPK.keyType = OTPrivateKey_KeyType.EC_NIST_CURVES
64 signingPK.keyData = peerSigningKey.keyData
65
66 guard let encryptionPK = OTPrivateKey() else {
67 throw Error.OTErrorPrivateKeyCreation
68 }
69 encryptionPK.keyType = OTPrivateKey_KeyType.EC_NIST_CURVES
70 encryptionPK.keyData = peerEncryptionKey.keyData
71
72 contentsObj.peerSigningPrivKey = signingPK
73 contentsObj.peerEncryptionPrivKey = encryptionPK
74 guard let clearContentsData = contentsObj.data else {
75 throw Error.OTErrorBottleCreation
76 }
77
78 // Encrypt the contents
79 let op = BottledPeer.encryptionOperation()
80 let cipher = try op.encrypt(clearContentsData, with: escrowKeys.symmetricKey)
81
82 let escrowSigningECPubKey: _SFECPublicKey = (escrowKeys.signingKey.publicKey as! _SFECPublicKey)
83 let escrowEncryptionECPubKey: _SFECPublicKey = escrowKeys.encryptionKey.publicKey as! _SFECPublicKey
84
85 let peerSigningECPublicKey: _SFECPublicKey = peerSigningKey.publicKey as! _SFECPublicKey
86 let peerEncryptionECPublicKey: _SFECPublicKey = peerEncryptionKey.publicKey as! _SFECPublicKey
87
88 // Serialize the whole thing
89 guard let obj = OTBottle() else {
90 throw Error.OTErrorBottleCreation
91 }
92 obj.peerID = peerID
93 obj.bottleID = bottleID
94 obj.escrowedSigningSPKI = escrowSigningECPubKey.encodeSubjectPublicKeyInfo()
95 obj.escrowedEncryptionSPKI = escrowEncryptionECPubKey.encodeSubjectPublicKeyInfo()
96 obj.peerSigningSPKI = peerSigningECPublicKey.encodeSubjectPublicKeyInfo()
97 obj.peerEncryptionSPKI = peerEncryptionECPublicKey.encodeSubjectPublicKeyInfo()
98
99 guard let authObj = OTAuthenticatedCiphertext() else {
100 throw Error.OTErrorAuthCipherTextCreation
101 }
102 authObj.ciphertext = cipher.ciphertext
103 authObj.authenticationCode = cipher.authenticationCode
104 authObj.initializationVector = cipher.initializationVector
105
106 obj.contents = authObj
107
108 self.peerID = peerID
109 self.bottleID = bottleID
110
111 try self.peerKeys = OctagonSelfPeerKeys(peerID: peerID, signingKey: peerSigningKey, encryptionKey: peerEncryptionKey)
112 self.contents = obj.data
113
114 let escrowedSigningECPublicKey = escrowKeys.signingKey.publicKey as! _SFECPublicKey
115
116 self.escrowSigningPublicKey = escrowedSigningECPublicKey.keyData
117 self.escrowSigningSPKI = escrowedSigningECPublicKey.encodeSubjectPublicKeyInfo()
118 self.peersigningSPKI = peerSigningECPublicKey.encodeSubjectPublicKeyInfo()
119
120 let xso = BottledPeer.signingOperation()
121
122 let signedByEscrowKey = try xso.sign(self.contents, with: escrowKeys.signingKey)
123 self.signatureUsingEscrowKey = signedByEscrowKey.signature
124
125 let signedByPeerKey = try xso.sign(self.contents, with: peerSigningKey)
126 self.signatureUsingPeerKey = signedByPeerKey.signature
127 }
128
129 // Deserialize a bottle (data) and decrypt the contents (peer keys)
130 // using the keys generated from the escrow secret, and signatures from signing keys
131 init (contents: Data, secret: Data, bottleSalt: String, signatureUsingEscrow: Data, signatureUsingPeerKey: Data) throws {
132 self.secret = secret
133 self.escrowKeys = try EscrowKeys(secret: self.secret, bottleSalt: bottleSalt)
134
135 guard let escrowSigningECKey: _SFECPublicKey = escrowKeys.signingKey.publicKey() as? _SFECPublicKey else {
136 os_log("escrow key not an SFECPublicKey?", log: tplogDebug, type: .default)
137 throw Error.OTErrorBottleCreation
138 }
139 self.escrowSigningSPKI = escrowSigningECKey.encodeSubjectPublicKeyInfo()
140
141 // Deserialize the whole thing
142 guard let obj = OTBottle(data: contents) else {
143 os_log("Unable to deserialize bottle", log: tplogDebug, type: .default)
144 throw Error.OTErrorDeserializationFailure
145 }
146
147 // First, the easy check: did the entropy create the keys that are supposed to be in the bottle?
148 guard obj.escrowedSigningSPKI == self.escrowSigningSPKI else {
149 os_log("Bottled SPKI does not match re-created SPKI", log: tplogDebug, type: .default)
150 throw Error.OTErrorEntropyKeyMismatch
151 }
152
153 // Second, does the signature verify on the given data?
154 let xso = BottledPeer.signingOperation()
155
156 let escrowSigned = _SFSignedData.init(data: contents, signature: signatureUsingEscrow)
157 try xso.verify(escrowSigned, with: escrowSigningECKey)
158
159 // Now, decrypt contents
160 let op = BottledPeer.encryptionOperation()
161 let ac: OTAuthenticatedCiphertext = obj.contents as OTAuthenticatedCiphertext
162
163 let ciphertext = _SFAuthenticatedCiphertext.init(ciphertext: ac.ciphertext, authenticationCode: ac.authenticationCode, initializationVector: ac.initializationVector)
164
165 let clearContentsData = try op.decrypt(ciphertext, with: escrowKeys.symmetricKey)
166 if clearContentsData.isEmpty {
167 throw Error.OTErrorDecryptionFailure
168 }
169
170 // Deserialize contents into private peer keys
171 guard let contentsObj = OTBottleContents(data: clearContentsData) else {
172 throw Error.OTErrorDeserializationFailure
173 }
174
175 self.peerID = obj.peerID
176 self.bottleID = obj.bottleID
177
178 try self.peerKeys = OctagonSelfPeerKeys(peerID: peerID,
179 signingKey: try contentsObj.peerSigningPrivKey.asECKeyPair(),
180 encryptionKey: try contentsObj.peerEncryptionPrivKey.asECKeyPair())
181 self.contents = contents
182
183 self.peersigningSPKI = obj.peerSigningSPKI
184
185 let peerSigningPubKey = _SFECPublicKey(subjectPublicKeyInfo: obj.peerSigningSPKI)
186 let peerEncryptionPubKey = _SFECPublicKey(subjectPublicKeyInfo: obj.peerEncryptionSPKI)
187
188 // Check the private keys match the public keys
189 if self.peerKeys.signingKey.publicKey != peerSigningPubKey {
190 throw Error.OTErrorKeyMismatch
191 }
192 if self.peerKeys.encryptionKey.publicKey != peerEncryptionPubKey {
193 throw Error.OTErrorKeyMismatch
194 }
195
196 self.escrowSigningSPKI = escrowSigningECKey.encodeSubjectPublicKeyInfo()
197
198 self.signatureUsingPeerKey = signatureUsingPeerKey
199 self.signatureUsingEscrowKey = signatureUsingEscrow
200
201 self.escrowSigningPublicKey = escrowSigningECKey.keyData
202
203 let peerSigned = _SFSignedData.init(data: self.contents, signature: signatureUsingPeerKey)
204 guard let peerPublicKey = self.peerKeys.publicSigningKey else {
205 throw Error.OTErrorKeyMismatch
206 }
207 try xso.verify(peerSigned, with: peerPublicKey)
208 }
209
210 func escrowSigningPublicKeyHash() -> String {
211 return TPHObjectiveC.digest(usingSha384: self.escrowSigningPublicKey)
212 }
213
214 class func signingOperation() -> (_SFEC_X962SigningOperation) {
215 let keySpecifier = _SFECKeySpecifier.init(curve: SFEllipticCurve.nistp384)
216 let digestOperation = _SFSHA384DigestOperation.init()
217 return _SFEC_X962SigningOperation.init(keySpecifier: keySpecifier, digestOperation: digestOperation)
218 }
219
220 class func verifyBottleSignature(data: Data, signature: Data, pubKey: _SFECPublicKey) throws -> (Bool) {
221 let xso = BottledPeer.signingOperation()
222 let peerSigned = _SFSignedData.init(data: data, signature: signature)
223 try xso.verify(peerSigned, with: pubKey)
224 return true
225 }
226
227 class func makeMeSomeEntropy(requiredLength: Int) throws -> Data {
228 let bytesPointer = UnsafeMutableRawPointer.allocate(byteCount: requiredLength, alignment: 1)
229
230 if SecRandomCopyBytes(kSecRandomDefault, requiredLength, bytesPointer) != 0 {
231 throw Error.OTErrorEntropyCreation
232 }
233 return Data(bytes: bytesPointer, count: requiredLength)
234 }
235 }
236
237 extension BottledPeer {
238 enum Error: Swift.Error {
239 case OTErrorDeserializationFailure
240 case OTErrorDecryptionFailure
241 case OTErrorKeyInstantiation
242 case OTErrorKeyMismatch
243 case OTErrorBottleCreation
244 case OTErrorAuthCipherTextCreation
245 case OTErrorPrivateKeyCreation
246 case OTErrorEscrowKeyCreation
247 case OTErrorEntropyCreation
248 case OTErrorEntropyKeyMismatch
249 }
250 }
251
252 extension BottledPeer.Error: LocalizedError {
253 var errorDescription: String? {
254 switch self {
255 case .OTErrorDeserializationFailure:
256 return "Failed to deserialize bottle peer"
257 case .OTErrorDecryptionFailure:
258 return "could not decrypt bottle contents"
259 case .OTErrorKeyInstantiation:
260 return "Failed to instantiate octagon peer keys"
261 case .OTErrorKeyMismatch:
262 return "public and private peer signing keys do not match"
263 case .OTErrorBottleCreation:
264 return "failed to create bottle"
265 case .OTErrorAuthCipherTextCreation:
266 return "failed to create authenticated ciphertext"
267 case .OTErrorPrivateKeyCreation:
268 return "failed to create private key"
269 case .OTErrorEscrowKeyCreation:
270 return "failed to create escrow keys"
271 case .OTErrorEntropyCreation:
272 return "failed to create entropy"
273 case .OTErrorEntropyKeyMismatch:
274 return "keys generated by the entropy+salt do not match the bottle contents"
275 }
276 }
277 }