SOSAccountPersistence.m   [plain text]

/*
* Copyright (c) 2013-2014 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@
*/

#include <stdio.h>
#include <stdlib.h>
#include <AssertMacros.h>
#include "SOSViews.h"

#include <utilities/SecCFWrappers.h>
#include <utilities/SecNSAdditions.h>

#include <utilities/SecCoreCrypto.h>
#include <utilities/SecBuffer.h>

#include "keychain/SecureObjectSync/SOSKVSKeys.h"
#include "keychain/SecureObjectSync/SOSPeerInfoDER.h"

#include "keychain/SecureObjectSync/SOSTransport.h"

#include "keychain/SecureObjectSync/SOSPeerInfoCollections.h"
#include <os/state_private.h>

#include "keychain/SecureObjectSync/SOSAccountPriv.h"
#import "keychain/SecureObjectSync/SOSAccountTrust.h"
#import "keychain/SecureObjectSync/SOSCircleDer.h"
#import "keychain/SecureObjectSync/SOSAccountTrustClassic.h"

@implementation SOSAccount (Persistence)

static SOSAccount* SOSAccountCreateFromRemainingDER_v6(CFAllocatorRef allocator,
SOSDataSourceFactoryRef factory,
CFErrorRef* error,
const uint8_t** der_p, const uint8_t *der_end)
{
SOSAccount* result = NULL;
SOSAccount* account = NULL;

CFArrayRef array = NULL;
CFDictionaryRef retiredPeers = NULL;

CFStringRef circle_name = factory->copy_name(factory);

{
CFDictionaryRef decoded_gestalt = NULL;
*der_p = der_decode_dictionary(kCFAllocatorDefault, &decoded_gestalt, error,
*der_p, der_end);

if (*der_p == 0)
return NULL;

account = SOSAccountCreate(allocator, decoded_gestalt, factory);

CFReleaseNull(decoded_gestalt);
}
SOSAccountTrustClassic *trust = account.trust;

*der_p = der_decode_array(kCFAllocatorDefault, &array, error, *der_p, der_end);

uint64_t tmp_departure_code = kSOSNeverAppliedToCircle;
*der_p = ccder_decode_uint64(&tmp_departure_code, *der_p, der_end);
[trust setDepartureCode:(enum DepartureReason)tmp_departure_code];

bool userPublicTrusted;
*der_p = ccder_decode_bool(&userPublicTrusted, *der_p, der_end);
account.accountKeyIsTrusted = userPublicTrusted;

SecKeyRef userPublic = NULL;
SecKeyRef previousUserPublic = NULL;
*der_p = der_decode_public_bytes(kCFAllocatorDefault, kSecECDSAAlgorithmID, &userPublic, error, *der_p, der_end);
*der_p = der_decode_public_bytes(kCFAllocatorDefault, kSecECDSAAlgorithmID, &previousUserPublic, error, *der_p, der_end);
account.accountKey = userPublic;
account.previousAccountKey = previousUserPublic;
CFReleaseNull(userPublic);
CFReleaseNull(previousUserPublic);

{
CFDataRef parms = NULL;
*der_p = der_decode_data_or_null(kCFAllocatorDefault, &parms, error, *der_p, der_end);
[account setAccountKeyDerivationParameters:(__bridge_transfer NSData*) parms];
}

*der_p = der_decode_dictionary(kCFAllocatorDefault, (CFDictionaryRef *) &retiredPeers, error, *der_p, der_end);

if(*der_p != der_end) {
*der_p = NULL;
return result;
}

{
CFMutableSetRef retirees = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);

CFDictionaryForEach(retiredPeers, ^(const void *key, const void *value) {
if (isData(value)) {
SOSPeerInfoRef retiree = SOSPeerInfoCreateFromData(kCFAllocatorDefault, NULL, (CFDataRef) value);

CFSetAddValue(retirees, retiree);

CFReleaseNull(retiree);
}
});

[trust setRetirees:(__bridge NSMutableSet *)retirees];
CFReleaseNull(retirees);
}

if(!array || !*der_p)
return result;

CFArrayForEach(array, ^(const void *value) {
CFDataRef circleData = NULL;
CFDataRef fullPeerInfoData = NULL;

if (isData(value)) {
circleData = (CFDataRef) value;
} else if (isArray(value)) {
CFArrayRef pair = (CFArrayRef) value;

CFTypeRef circleObject = CFArrayGetValueAtIndex(pair, 0);
CFTypeRef fullPeerInfoObject = CFArrayGetValueAtIndex(pair, 1);

if (CFArrayGetCount(pair) == 2 && isData(circleObject) && isData(fullPeerInfoObject)) {
circleData = (CFDataRef) circleObject;
fullPeerInfoData = (CFDataRef) fullPeerInfoObject;
}
}

if (circleData) {
SOSCircleRef circle = SOSCircleCreateFromData(kCFAllocatorDefault, circleData, error);
require_quiet(circle && CFEqualSafe(circle_name, SOSCircleGetName(circle)), fail);
[trust setTrustedCircle:circle];
CFReleaseNull(circle);

if(fullPeerInfoData) {
SOSFullPeerInfoRef identity = SOSFullPeerInfoCreateFromData(kCFAllocatorDefault, fullPeerInfoData, NULL);
trust.fullPeerInfo = identity;
CFReleaseNull(identity);
}

fail:
CFReleaseNull(circle);
}
});
CFReleaseNull(array);
require_action_quiet([account ensureFactoryCircles], fail,
SOSCreateError(kSOSErrorBadFormat, CFSTR("Cannot EnsureFactoryCircles"), (error != NULL) ? *error : NULL, error));
result = account;

fail:

return result;
}

static const uint8_t* der_decode_data_optional(CFAllocatorRef allocator,
CFDataRef* data, CFErrorRef *error,
const uint8_t* der, const uint8_t *der_end)
{
const uint8_t *dt_end = der_decode_data(allocator, data, NULL, der, der_end);

return dt_end ? dt_end : der;
}

static SOSAccount* SOSAccountCreateFromRemainingDER_v7(CFAllocatorRef allocator,
SOSDataSourceFactoryRef factory,
CFErrorRef* error,
const uint8_t** der_p, const uint8_t *der_end)
{
SOSAccount* account = NULL;
SOSAccount* result = NULL;

{
CFDictionaryRef decoded_gestalt = NULL;
*der_p = der_decode_dictionary(kCFAllocatorDefault, &decoded_gestalt, error,
*der_p, der_end);

if (*der_p == 0)
return NULL;

account = SOSAccountCreate(kCFAllocatorDefault, decoded_gestalt, factory);
CFReleaseNull(decoded_gestalt);
}

enum DepartureReason departure_code = 0;
SOSAccountTrustClassic *trust = account.trust;

{
SOSCircleRef circle = SOSCircleCreateFromDER(kCFAllocatorDefault, error, der_p, der_end);
[trust setTrustedCircle:circle];
CFReleaseNull(circle);
}

{
SOSFullPeerInfoRef identity = NULL;
*der_p = der_decode_fullpeer_or_null(kCFAllocatorDefault, &identity, error, *der_p, der_end);
trust.fullPeerInfo = identity;
CFReleaseNull(identity);
}

uint64_t tmp_departure_code = kSOSNeverAppliedToCircle;
*der_p = ccder_decode_uint64(&tmp_departure_code, *der_p, der_end);
bool userPublicTrusted;

*der_p = ccder_decode_bool(&userPublicTrusted, *der_p, der_end);
account.accountKeyIsTrusted = userPublicTrusted;

{
SecKeyRef userPublic = NULL;
*der_p = der_decode_public_bytes(kCFAllocatorDefault, kSecECDSAAlgorithmID, &userPublic, error, *der_p, der_end);
account.accountKey = userPublic;
CFReleaseNull(userPublic);
}

{
SecKeyRef previousUserPublic = NULL;
*der_p = der_decode_public_bytes(kCFAllocatorDefault, kSecECDSAAlgorithmID, &previousUserPublic, error, *der_p, der_end);
account.previousAccountKey = previousUserPublic;
CFReleaseNull(previousUserPublic);
}

{
CFDataRef parms = NULL;
*der_p = der_decode_data_or_null(kCFAllocatorDefault, &parms, error, *der_p, der_end);
account.accountKeyDerivationParameters = (__bridge_transfer NSData*)parms;
}

{
CFSetRef retirees = SOSPeerInfoSetCreateFromArrayDER(kCFAllocatorDefault, &kSOSPeerSetCallbacks, error, der_p, der_end);
[trust setRetirees:(__bridge NSMutableSet *)retirees];
}

{
CFDataRef bKey = NULL;
*der_p = der_decode_data_optional(kCFAllocatorDefault, &bKey, error, *der_p, der_end);
if(bKey != NULL)
[account setBackup_key:(__bridge_transfer NSData *)bKey];
}

departure_code = (enum DepartureReason) tmp_departure_code;

[trust setDepartureCode:departure_code];
require_action_quiet(*der_p && *der_p == der_end, fail,
SOSCreateError(kSOSErrorBadFormat, CFSTR("Didn't consume all bytes v7"), (error != NULL) ? *error : NULL, error));

result = account;

fail:
return result;
}

static SOSAccount* SOSAccountCreateFromRemainingDER_v8(CFAllocatorRef allocator,
SOSDataSourceFactoryRef factory,
CFErrorRef* error,
const uint8_t** der_p, const uint8_t *der_end)
{
SOSAccount* account = NULL;
SOSAccount* result = NULL;

{
CFDictionaryRef decoded_gestalt = NULL;
*der_p = der_decode_dictionary(kCFAllocatorDefault, &decoded_gestalt, error,
*der_p, der_end);

if (*der_p == 0) {
CFReleaseNull(decoded_gestalt);
return NULL;
}

account = SOSAccountCreate(kCFAllocatorDefault, decoded_gestalt, factory);
CFReleaseNull(decoded_gestalt);
}

SOSAccountTrustClassic *trust = account.trust;

{
SOSCircleRef circle = SOSCircleCreateFromDER(kCFAllocatorDefault, error, der_p, der_end);
[trust setTrustedCircle:circle];
CFReleaseNull(circle);
}

{
SOSFullPeerInfoRef identity = NULL;
*der_p = der_decode_fullpeer_or_null(kCFAllocatorDefault, &identity, error, *der_p, der_end);
trust.fullPeerInfo = identity;
CFReleaseNull(identity);
}

uint64_t tmp_departure_code = kSOSNeverAppliedToCircle;
*der_p = ccder_decode_uint64(&tmp_departure_code, *der_p, der_end);
[trust setDepartureCode:(enum DepartureReason) tmp_departure_code];

bool userPublicTrusted;
*der_p = ccder_decode_bool(&userPublicTrusted, *der_p, der_end);
account.accountKeyIsTrusted = userPublicTrusted;

{
SecKeyRef userPublic = NULL;
*der_p = der_decode_public_bytes(kCFAllocatorDefault, kSecECDSAAlgorithmID, &userPublic, error, *der_p, der_end);
account.accountKey = userPublic;
CFReleaseNull(userPublic);
}

{
SecKeyRef previousUserPublic = NULL;
*der_p = der_decode_public_bytes(kCFAllocatorDefault, kSecECDSAAlgorithmID, &previousUserPublic, error, *der_p, der_end);
account.previousAccountKey = previousUserPublic;
CFReleaseNull(previousUserPublic);
}

{
CFDataRef parms;
*der_p = der_decode_data_or_null(kCFAllocatorDefault, &parms, error, *der_p, der_end);
account.accountKeyDerivationParameters = (__bridge_transfer NSData*)parms;
parms = NULL;
}

{
CFMutableSetRef retirees = SOSPeerInfoSetCreateFromArrayDER(kCFAllocatorDefault, &kSOSPeerSetCallbacks, error, der_p, der_end);
[trust setRetirees:(__bridge NSMutableSet *)retirees];
CFReleaseNull(retirees);
}

CFDataRef bKey = NULL;
*der_p = der_decode_data_optional(kCFAllocatorDefault, &bKey, error, *der_p, der_end);
{
CFDictionaryRef expansion = NULL;
*der_p = der_decode_dictionary(kCFAllocatorDefault, &expansion, error,
*der_p, der_end);

if (*der_p == 0) {
CFReleaseNull(bKey);
CFReleaseNull(expansion);
return NULL;
}

if(expansion) {
[trust setExpansion:(__bridge NSMutableDictionary *)(expansion)];
}
CFReleaseNull(expansion);
}
if(bKey) {
[account setBackup_key:[[NSData alloc] initWithData:(__bridge NSData * _Nonnull)(bKey)]];
}
CFReleaseNull(bKey);

require_action_quiet(*der_p && *der_p == der_end, fail,
SOSCreateError(kSOSErrorBadFormat, CFSTR("Didn't consume all bytes v7"), (error != NULL) ? *error : NULL, error));

result = account;

fail:
return result;
}

//
// Version History for Account
//
// 1-5 - InnsbruckTaos/Cab; Never supported even for upgrading.
// 6 - First version used in the field.
// 7 - One Circle version
// 8 - Adding expansion dictionary
//

#define CURRENT_ACCOUNT_PERSISTENT_VERSION 8

static SOSAccount* SOSAccountCreateFromDER(CFAllocatorRef allocator,
SOSDataSourceFactoryRef factory,
CFErrorRef* error,
const uint8_t** der_p, const uint8_t *der_end)
{
SOSAccount* account = NULL;
uint64_t version = 0;

SOSFullPeerInfoRef identity = NULL;

const uint8_t *sequence_end;
*der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end);
*der_p = ccder_decode_uint64(&version, *der_p, sequence_end);
if (*der_p == NULL) {
SOSCreateError(kSOSErrorBadFormat, CFSTR("Version parsing failed"), (error != NULL) ? *error : NULL, error);
return nil;
}

switch (version) {
case CURRENT_ACCOUNT_PERSISTENT_VERSION:
account = SOSAccountCreateFromRemainingDER_v8(allocator, factory, error, der_p, sequence_end);
break;

case 7:
account = SOSAccountCreateFromRemainingDER_v7(allocator, factory, error, der_p, sequence_end);
break;

case 6:
account = SOSAccountCreateFromRemainingDER_v6(allocator, factory, error, der_p, sequence_end);
break;

default:
SOSCreateErrorWithFormat(kSOSErrorBadFormat, (error != NULL) ? *error : NULL, error,
NULL, CFSTR("Unsupported version (%llu)"), version);
break;
}

if (!account) {
// Error should have been filled in above.
return nil;
}

if (*der_p != sequence_end) {
SOSCreateError(kSOSErrorBadFormat, CFSTR("Extra data at the end of saved acount"), (error != NULL) ? *error : NULL, error);
return nil;
}

identity = account.fullPeerInfo;
/* I may not always have an identity, but when I do, it has a private key */
if(identity) {
if(!(SOSFullPeerInfoPrivKeyExists(identity)))
{
SOSUnregisterTransportKeyParameter(account.key_transport);
SOSUnregisterTransportCircle((SOSCircleStorageTransport*)account.circle_transport);
SOSUnregisterTransportMessage(account.kvs_message_transport);
secnotice("account", "No private key associated with my_identity, resetting");
return nil;
} else {
SecKeyRef ppk = SOSFullPeerInfoCopyPubKey(identity, NULL);
account.peerPublicKey = ppk;
CFReleaseNull(ppk);
}
}

if (![account ensureFactoryCircles]) {
SOSCreateError(kSOSErrorBadFormat, CFSTR("Cannot EnsureFactoryCircles"), (error != NULL) ? *error : NULL, error);
return nil;
}

SOSPeerInfoRef oldPI = CFRetainSafe(account.peerInfo);
if (oldPI && [account isInCircle: nil] == kSOSCCInCircle) {
SOSAccountCheckForAlwaysOnViews(account);
}
CFReleaseNull(oldPI);

[account performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
SOSAccountEnsureRecoveryRing(account);

secnotice("circleop", "Setting account.key_interests_need_updating to true in SOSAccountCreateFromDER");
account.key_interests_need_updating = true;
}];

SOSAccountEnsureUUID(account);

return account;
}

+(instancetype) accountFromDER: (const uint8_t**) der
end: (const uint8_t*) der_end
factory: (SOSDataSourceFactoryRef) factory
error: (NSError**) error {
CFErrorRef failure = NULL;
SOSAccount* result = SOSAccountCreateFromDER(kCFAllocatorDefault, factory, &failure, der, der_end);

if (result == nil) {
if (error) {
*error = (__bridge_transfer NSError*) failure;
failure = NULL;
}
}
CFReleaseNull(failure);

return result;
}

+(instancetype) accountFromData: (NSData*) data
factory: (SOSDataSourceFactoryRef) factory
error: (NSError**) error {
size_t size = [data length];
const uint8_t *der = [data bytes];
return [self accountFromDER: &der end: der+size factory: factory error: error];
}

CFMutableSetRef SOSPeerInfoSetCreateFromArrayDER(CFAllocatorRef allocator, const CFSetCallBacks *callbacks, CFErrorRef* error,
const uint8_t** der_p, const uint8_t *der_end);
size_t SOSPeerInfoSetGetDEREncodedArraySize(CFSetRef pia, CFErrorRef *error);
uint8_t* SOSPeerInfoSetEncodeToArrayDER(CFSetRef pia, CFErrorRef* error, const uint8_t* der, uint8_t* der_end);

/************************/

- (NSData*) encodedData: (NSError* __autoreleasing *) error {
NSUInteger expected = [self.trust getDEREncodedSize: self err:error];
if (expected == 0) return nil;

return [NSMutableData dataWithSpace:expected
DEREncode:^uint8_t *(size_t size, uint8_t *buffer) {
return [self.trust encodeToDER:self
err:error
start:buffer
end:buffer + size];
}];
}

@end