2 * Copyright (c) 2019 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 import SecurityFoundation
27 class BottledPeer: NSObject {
28 var escrowKeys: EscrowKeys
32 var peerKeys: OctagonSelfPeerKeys
34 var signatureUsingEscrowKey: Data
35 var signatureUsingPeerKey: Data
36 var escrowSigningPublicKey: Data
37 var escrowSigningSPKI: Data
38 var peersigningSPKI: Data
42 class func encryptionOperation() -> (_SFAuthenticatedEncryptionOperation) {
43 let keySpecifier = _SFAESKeySpecifier.init(bitSize: TPHObjectiveC.aes256BitSize())
44 return _SFAuthenticatedEncryptionOperation.init(keySpecifier: keySpecifier)
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))
54 self.escrowKeys = try EscrowKeys(secret: secret, bottleSalt: bottleSalt)
56 // Serialize the peer private keys into "contents"
57 guard let contentsObj = OTBottleContents() else {
58 throw Error.OTErrorBottleCreation
60 guard let signingPK = OTPrivateKey() else {
61 throw Error.OTErrorPrivateKeyCreation
63 signingPK.keyType = OTPrivateKey_KeyType.EC_NIST_CURVES
64 signingPK.keyData = peerSigningKey.keyData
66 guard let encryptionPK = OTPrivateKey() else {
67 throw Error.OTErrorPrivateKeyCreation
69 encryptionPK.keyType = OTPrivateKey_KeyType.EC_NIST_CURVES
70 encryptionPK.keyData = peerEncryptionKey.keyData
72 contentsObj.peerSigningPrivKey = signingPK
73 contentsObj.peerEncryptionPrivKey = encryptionPK
74 guard let clearContentsData = contentsObj.data else {
75 throw Error.OTErrorBottleCreation
78 // Encrypt the contents
79 let op = BottledPeer.encryptionOperation()
80 let cipher = try op.encrypt(clearContentsData, with: escrowKeys.symmetricKey)
82 let escrowSigningECPubKey: _SFECPublicKey = (escrowKeys.signingKey.publicKey as! _SFECPublicKey)
83 let escrowEncryptionECPubKey: _SFECPublicKey = escrowKeys.encryptionKey.publicKey as! _SFECPublicKey
85 let peerSigningECPublicKey: _SFECPublicKey = peerSigningKey.publicKey as! _SFECPublicKey
86 let peerEncryptionECPublicKey: _SFECPublicKey = peerEncryptionKey.publicKey as! _SFECPublicKey
88 // Serialize the whole thing
89 guard let obj = OTBottle() else {
90 throw Error.OTErrorBottleCreation
93 obj.bottleID = bottleID
94 obj.escrowedSigningSPKI = escrowSigningECPubKey.encodeSubjectPublicKeyInfo()
95 obj.escrowedEncryptionSPKI = escrowEncryptionECPubKey.encodeSubjectPublicKeyInfo()
96 obj.peerSigningSPKI = peerSigningECPublicKey.encodeSubjectPublicKeyInfo()
97 obj.peerEncryptionSPKI = peerEncryptionECPublicKey.encodeSubjectPublicKeyInfo()
99 guard let authObj = OTAuthenticatedCiphertext() else {
100 throw Error.OTErrorAuthCipherTextCreation
102 authObj.ciphertext = cipher.ciphertext
103 authObj.authenticationCode = cipher.authenticationCode
104 authObj.initializationVector = cipher.initializationVector
106 obj.contents = authObj
109 self.bottleID = bottleID
111 try self.peerKeys = OctagonSelfPeerKeys(peerID: peerID, signingKey: peerSigningKey, encryptionKey: peerEncryptionKey)
112 self.contents = obj.data
114 let escrowedSigningECPublicKey = escrowKeys.signingKey.publicKey as! _SFECPublicKey
116 self.escrowSigningPublicKey = escrowedSigningECPublicKey.keyData
117 self.escrowSigningSPKI = escrowedSigningECPublicKey.encodeSubjectPublicKeyInfo()
118 self.peersigningSPKI = peerSigningECPublicKey.encodeSubjectPublicKeyInfo()
120 let xso = BottledPeer.signingOperation()
122 let signedByEscrowKey = try xso.sign(self.contents, with: escrowKeys.signingKey)
123 self.signatureUsingEscrowKey = signedByEscrowKey.signature
125 let signedByPeerKey = try xso.sign(self.contents, with: peerSigningKey)
126 self.signatureUsingPeerKey = signedByPeerKey.signature
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 {
133 self.escrowKeys = try EscrowKeys(secret: self.secret, bottleSalt: bottleSalt)
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
139 self.escrowSigningSPKI = escrowSigningECKey.encodeSubjectPublicKeyInfo()
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
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
153 // Second, does the signature verify on the given data?
154 let xso = BottledPeer.signingOperation()
156 let escrowSigned = _SFSignedData.init(data: contents, signature: signatureUsingEscrow)
157 try xso.verify(escrowSigned, with: escrowSigningECKey)
159 // Now, decrypt contents
160 let op = BottledPeer.encryptionOperation()
161 let ac: OTAuthenticatedCiphertext = obj.contents as OTAuthenticatedCiphertext
163 let ciphertext = _SFAuthenticatedCiphertext.init(ciphertext: ac.ciphertext, authenticationCode: ac.authenticationCode, initializationVector: ac.initializationVector)
165 let clearContentsData = try op.decrypt(ciphertext, with: escrowKeys.symmetricKey)
166 if clearContentsData.isEmpty {
167 throw Error.OTErrorDecryptionFailure
170 // Deserialize contents into private peer keys
171 guard let contentsObj = OTBottleContents(data: clearContentsData) else {
172 throw Error.OTErrorDeserializationFailure
175 self.peerID = obj.peerID
176 self.bottleID = obj.bottleID
178 try self.peerKeys = OctagonSelfPeerKeys(peerID: peerID,
179 signingKey: try contentsObj.peerSigningPrivKey.asECKeyPair(),
180 encryptionKey: try contentsObj.peerEncryptionPrivKey.asECKeyPair())
181 self.contents = contents
183 self.peersigningSPKI = obj.peerSigningSPKI
185 let peerSigningPubKey = _SFECPublicKey(subjectPublicKeyInfo: obj.peerSigningSPKI)
186 let peerEncryptionPubKey = _SFECPublicKey(subjectPublicKeyInfo: obj.peerEncryptionSPKI)
188 // Check the private keys match the public keys
189 if self.peerKeys.signingKey.publicKey != peerSigningPubKey {
190 throw Error.OTErrorKeyMismatch
192 if self.peerKeys.encryptionKey.publicKey != peerEncryptionPubKey {
193 throw Error.OTErrorKeyMismatch
196 self.escrowSigningSPKI = escrowSigningECKey.encodeSubjectPublicKeyInfo()
198 self.signatureUsingPeerKey = signatureUsingPeerKey
199 self.signatureUsingEscrowKey = signatureUsingEscrow
201 self.escrowSigningPublicKey = escrowSigningECKey.keyData
203 let peerSigned = _SFSignedData.init(data: self.contents, signature: signatureUsingPeerKey)
204 guard let peerPublicKey = self.peerKeys.publicSigningKey else {
205 throw Error.OTErrorKeyMismatch
207 try xso.verify(peerSigned, with: peerPublicKey)
210 func escrowSigningPublicKeyHash() -> String {
211 return TPHObjectiveC.digest(usingSha384: self.escrowSigningPublicKey)
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)
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)
227 class func makeMeSomeEntropy(requiredLength: Int) throws -> Data {
228 let bytesPointer = UnsafeMutableRawPointer.allocate(byteCount: requiredLength, alignment: 1)
230 if SecRandomCopyBytes(kSecRandomDefault, requiredLength, bytesPointer) != 0 {
231 throw Error.OTErrorEntropyCreation
233 return Data(bytes: bytesPointer, count: requiredLength)
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
252 extension BottledPeer.Error: LocalizedError {
253 var errorDescription: String? {
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"