/*
* Copyright (c) 2017 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
#import "OTBottledPeer.h"
#if OCTAGON
#import <SecurityFoundation/SFEncryptionOperation.h>
#import <SecurityFoundation/SFSigningOperation.h>
#import <SecurityFoundation/SFDigestOperation.h>
#import <SecurityFoundation/SFKey.h>
#import <SecurityFoundation/SFKey_Private.h>
#import <Security/SecKeyPriv.h>
#import <corecrypto/cchkdf.h>
#import <corecrypto/ccsha2.h>
#import <corecrypto/ccec.h>
#import <utilities/debugging.h>
#import <CommonCrypto/CommonRandomSPI.h>
#import "OTBottle.h"
#import "OTBottleContents.h"
#import "OTDefines.h"
#import "OTPrivateKey.h"
#import "OTPrivateKey+SF.h"
#import "OTAuthenticatedCiphertext.h"
#import "OTAuthenticatedCiphertext+SF.h"
#import "SFPublicKey+SPKI.h"
@interface OTBottledPeer ()
@property (nonatomic, strong) NSString* peerID;
@property (nonatomic, strong) NSString* spID;
@property (nonatomic, strong) SFECKeyPair* peerSigningKey;
@property (nonatomic, strong) SFECKeyPair* peerEncryptionKey;
@property (nonatomic, strong) NSData* data;
@end
@implementation OTBottledPeer
+ (SFAuthenticatedEncryptionOperation *) encryptionOperation
{
SFAESKeySpecifier *keySpecifier = [[SFAESKeySpecifier alloc] initWithBitSize:SFAESKeyBitSize256];
return [[SFAuthenticatedEncryptionOperation alloc] initWithKeySpecifier:keySpecifier];
}
// Given a peer's details including private key material, and
// the keys generated from the escrow secret, encrypt the peer private keys,
// make a bottled peer object and serialize it into data.
- (nullable instancetype) initWithPeerID:(NSString * _Nullable)peerID
spID:(NSString * _Nullable)spID
peerSigningKey:(SFECKeyPair *)peerSigningKey
peerEncryptionKey:(SFECKeyPair *)peerEncryptionKey
escrowKeys:(OTEscrowKeys *)escrowKeys
error:(NSError**)error
{
self = [super init];
if (self) {
// Serialize the peer private keys into "contents"
OTBottleContents *contentsObj = [[OTBottleContents alloc] init];
contentsObj.peerSigningPrivKey = [OTPrivateKey fromECKeyPair:peerSigningKey];
contentsObj.peerEncryptionPrivKey = [OTPrivateKey fromECKeyPair:peerEncryptionKey];
NSData *clearContentsData = contentsObj.data;
// Encrypt the contents
SFAuthenticatedEncryptionOperation *op = [OTBottledPeer encryptionOperation];
SFAuthenticatedCiphertext* cipher = [op encrypt:clearContentsData withKey:escrowKeys.symmetricKey error:error];
if (!cipher) {
return nil;
}
// Serialize the whole thing
OTBottle *obj = [[OTBottle alloc] init];
obj.peerID = peerID;
obj.spID = spID;
obj.escrowedSigningSPKI = [escrowKeys.signingKey.publicKey asSPKI];
obj.escrowedEncryptionSPKI = [escrowKeys.encryptionKey.publicKey asSPKI];
obj.peerSigningSPKI = [peerSigningKey.publicKey asSPKI];
obj.peerEncryptionSPKI = [peerEncryptionKey.publicKey asSPKI];
obj.contents = [OTAuthenticatedCiphertext fromSFAuthenticatedCiphertext:cipher];
_peerID = [peerID copy];
_spID = [spID copy];
_peerSigningKey = peerSigningKey;
_peerEncryptionKey = peerEncryptionKey;
_data = obj.data;
}
return self;
}
// Deserialize a bottle and decrypt the contents (peer keys)
// using the keys generated from the escrow secret.
- (nullable instancetype) initWithData:(NSData *)data
escrowKeys:(OTEscrowKeys *)escrowKeys
error:(NSError**)error
{
self = [super init];
if (self) {
NSError* localError =nil;
// Deserialize the whole thing
OTBottle *obj = [[OTBottle alloc] initWithData:data];
if (!obj) {
secerror("octagon: failed to deserialize data into OTBottle");
if(error){
*error = [NSError errorWithDomain:octagonErrorDomain code:OTErrorDeserializationFailure userInfo:@{NSLocalizedDescriptionKey: @"Failed to deserialize bottle peer"}];
}
return nil;
}
// Decrypt contents
SFAuthenticatedEncryptionOperation *op = [OTBottledPeer encryptionOperation];
SFAuthenticatedCiphertext* ciphertext = [obj.contents asSFAuthenticatedCiphertext];
NSData* clearContentsData = [op decrypt:ciphertext withKey:escrowKeys.symmetricKey error:&localError];
if (!clearContentsData || clearContentsData.length == 0) {
secerror("octagon: could not decrypt bottle contents: if(error){
*error = localError;
}
return nil;
}
// Deserialize contents into private peer keys
OTBottleContents *contentsObj = [[OTBottleContents alloc] initWithData:clearContentsData];
if (!contentsObj) {
secerror("octagon: could not deserialize bottle contents");
if(error){
*error = [NSError errorWithDomain:octagonErrorDomain code:OTErrorDeserializationFailure userInfo:@{NSLocalizedDescriptionKey: @"Failed to deserialize bottle contents"}];
}
return nil;
}
_peerID = obj.peerID;
_spID = obj.spID;
_peerSigningKey = [contentsObj.peerSigningPrivKey asECKeyPair];
_peerEncryptionKey = [contentsObj.peerEncryptionPrivKey asECKeyPair];
if (!_peerSigningKey || !_peerEncryptionKey) {
secerror("octagon: could not get private EC keys from bottle contents");
if(error){
*error = [NSError errorWithDomain:octagonErrorDomain code:OTErrorPrivateKeyFailure userInfo:@{NSLocalizedDescriptionKey: @"Failed to instantiate octagon peer keys"}];
}
return nil;
}
_data = [data copy];
SFECPublicKey *peerSigningPubKey = [SFECPublicKey fromSPKI:obj.peerSigningSPKI];
SFECPublicKey *peerEncryptionPubKey = [SFECPublicKey fromSPKI:obj.peerEncryptionSPKI];
// Check the private keys match the public keys
if (![_peerSigningKey.publicKey isEqual:peerSigningPubKey]) {
secerror("octagon: public and private peer signing keys do not match");
if(error){
*error = [NSError errorWithDomain:octagonErrorDomain code:OTErrorPrivateKeyFailure userInfo:@{NSLocalizedDescriptionKey: @"public and private peer signing keys do not match"}];
}
return nil;
}
if (![_peerEncryptionKey.publicKey isEqual:peerEncryptionPubKey]) {
secerror("octagon: public and private peer encryption keys do not match");
if(error){
*error = [NSError errorWithDomain:octagonErrorDomain code:OTErrorPrivateKeyFailure userInfo:@{NSLocalizedDescriptionKey: @"public and private peer encryption keys do not match"}];
}
return nil;
}
}
return self;
}
@end
#endif