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 {
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
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
41 public var contents: Data
43 public class func encryptionOperation() -> (_SFAuthenticatedEncryptionOperation) {
44 let keySpecifier = _SFAESKeySpecifier.init(bitSize: TPHObjectiveC.aes256BitSize())
45 return _SFAuthenticatedEncryptionOperation.init(keySpecifier: keySpecifier)
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 {
53 let secret = try BottledPeer.makeMeSomeEntropy(requiredLength: Int(OTMasterSecretLength))
56 self.escrowKeys = try EscrowKeys(secret: secret, bottleSalt: bottleSalt)
58 // Serialize the peer private keys into "contents"
59 guard let contentsObj = OTBottleContents() else {
60 throw Error.OTErrorBottleCreation
62 guard let signingPK = OTPrivateKey() else {
63 throw Error.OTErrorPrivateKeyCreation
65 signingPK.keyType = OTPrivateKey_KeyType.EC_NIST_CURVES
66 signingPK.keyData = peerSigningKey.keyData
68 guard let encryptionPK = OTPrivateKey() else {
69 throw Error.OTErrorPrivateKeyCreation
71 encryptionPK.keyType = OTPrivateKey_KeyType.EC_NIST_CURVES
72 encryptionPK.keyData = peerEncryptionKey.keyData
74 contentsObj.peerSigningPrivKey = signingPK
75 contentsObj.peerEncryptionPrivKey = encryptionPK
76 guard let clearContentsData = contentsObj.data else {
77 throw Error.OTErrorBottleCreation
80 // Encrypt the contents
81 let op = BottledPeer.encryptionOperation()
82 let cipher = try op.encrypt(clearContentsData, with: escrowKeys.symmetricKey)
84 let escrowSigningECPubKey: _SFECPublicKey = (escrowKeys.signingKey.publicKey as! _SFECPublicKey)
85 let escrowEncryptionECPubKey: _SFECPublicKey = escrowKeys.encryptionKey.publicKey as! _SFECPublicKey
87 let peerSigningECPublicKey: _SFECPublicKey = peerSigningKey.publicKey as! _SFECPublicKey
88 let peerEncryptionECPublicKey: _SFECPublicKey = peerEncryptionKey.publicKey as! _SFECPublicKey
90 // Serialize the whole thing
91 guard let obj = OTBottle() else {
92 throw Error.OTErrorBottleCreation
95 obj.bottleID = bottleID
96 obj.escrowedSigningSPKI = escrowSigningECPubKey.encodeSubjectPublicKeyInfo()
97 obj.escrowedEncryptionSPKI = escrowEncryptionECPubKey.encodeSubjectPublicKeyInfo()
98 obj.peerSigningSPKI = peerSigningECPublicKey.encodeSubjectPublicKeyInfo()
99 obj.peerEncryptionSPKI = peerEncryptionECPublicKey.encodeSubjectPublicKeyInfo()
101 guard let authObj = OTAuthenticatedCiphertext() else {
102 throw Error.OTErrorAuthCipherTextCreation
104 authObj.ciphertext = cipher.ciphertext
105 authObj.authenticationCode = cipher.authenticationCode
106 authObj.initializationVector = cipher.initializationVector
108 obj.contents = authObj
111 self.bottleID = bottleID
113 try self.peerKeys = OctagonSelfPeerKeys(peerID: peerID, signingKey: peerSigningKey, encryptionKey: peerEncryptionKey)
114 self.contents = obj.data
116 let escrowedSigningECPublicKey = escrowKeys.signingKey.publicKey as! _SFECPublicKey
118 self.escrowSigningPublicKey = escrowedSigningECPublicKey.keyData
119 self.escrowSigningSPKI = escrowedSigningECPublicKey.encodeSubjectPublicKeyInfo()
120 self.peersigningSPKI = peerSigningECPublicKey.encodeSubjectPublicKeyInfo()
122 let xso = BottledPeer.signingOperation()
124 let signedByEscrowKey = try xso.sign(self.contents, with: escrowKeys.signingKey)
125 self.signatureUsingEscrowKey = signedByEscrowKey.signature
127 let signedByPeerKey = try xso.sign(self.contents, with: peerSigningKey)
128 self.signatureUsingPeerKey = signedByPeerKey.signature
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 {
136 self.escrowKeys = try EscrowKeys(secret: self.secret, bottleSalt: bottleSalt)
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
142 self.escrowSigningSPKI = escrowSigningECKey.encodeSubjectPublicKeyInfo()
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
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
156 // Second, does the signature verify on the given data?
157 let xso = BottledPeer.signingOperation()
159 let escrowSigned = _SFSignedData.init(data: contents, signature: signatureUsingEscrow)
160 try xso.verify(escrowSigned, with: escrowSigningECKey)
162 // Now, decrypt contents
163 let op = BottledPeer.encryptionOperation()
164 let ac: OTAuthenticatedCiphertext = obj.contents as OTAuthenticatedCiphertext
166 let ciphertext = _SFAuthenticatedCiphertext.init(ciphertext: ac.ciphertext, authenticationCode: ac.authenticationCode, initializationVector: ac.initializationVector)
168 let clearContentsData = try op.decrypt(ciphertext, with: escrowKeys.symmetricKey)
169 if clearContentsData.count == 0 {
170 throw Error.OTErrorDecryptionFailure
173 // Deserialize contents into private peer keys
174 guard let contentsObj = OTBottleContents(data: clearContentsData) else {
175 throw Error.OTErrorDeserializationFailure
178 self.peerID = obj.peerID
179 self.bottleID = obj.bottleID
181 try self.peerKeys = OctagonSelfPeerKeys(peerID: peerID,
182 signingKey: try contentsObj.peerSigningPrivKey.asECKeyPair(),
183 encryptionKey: try contentsObj.peerEncryptionPrivKey.asECKeyPair())
184 self.contents = contents
186 self.peersigningSPKI = obj.peerSigningSPKI
188 let peerSigningPubKey = _SFECPublicKey(subjectPublicKeyInfo: obj.peerSigningSPKI)
189 let peerEncryptionPubKey = _SFECPublicKey(subjectPublicKeyInfo: obj.peerEncryptionSPKI)
191 // Check the private keys match the public keys
192 if self.peerKeys.signingKey.publicKey != peerSigningPubKey {
193 throw Error.OTErrorKeyMismatch
195 if self.peerKeys.encryptionKey.publicKey != peerEncryptionPubKey {
196 throw Error.OTErrorKeyMismatch
199 self.escrowSigningSPKI = escrowSigningECKey.encodeSubjectPublicKeyInfo()
201 self.signatureUsingPeerKey = signatureUsingPeerKey
202 self.signatureUsingEscrowKey = signatureUsingEscrow
204 self.escrowSigningPublicKey = escrowSigningECKey.keyData
206 let peerSigned = _SFSignedData.init(data: self.contents, signature: signatureUsingPeerKey)
207 guard let peerPublicKey = self.peerKeys.publicSigningKey else {
208 throw Error.OTErrorKeyMismatch
210 try xso.verify(peerSigned, with: peerPublicKey)
213 public func escrowSigningPublicKeyHash() -> String {
214 return TPHObjectiveC.digest(usingSha384: self.escrowSigningPublicKey)
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)
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)
230 public class func makeMeSomeEntropy(requiredLength: Int) throws -> Data {
231 let bytesPointer = UnsafeMutableRawPointer.allocate(byteCount: requiredLength, alignment: 1)
233 if SecRandomCopyBytes(kSecRandomDefault, requiredLength, bytesPointer) != 0 {
234 throw Error.OTErrorEntropyCreation
236 return Data(bytes: bytesPointer, count: requiredLength)
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
255 extension BottledPeer.Error: LocalizedError {
256 public var errorDescription: String? {
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"