CFDataRef SOSRecoveryKeyCopyKeyForAccount(CFAllocatorRef allocator, SOSAccountRef account, SOSRecoveryKeyBagRef recoveryKeyBag, CFErrorRef *error) { CFDataRef retval = NULL; require_action_quiet(recoveryKeyBag && recoveryKeyBag->recoveryKeyBag && recoveryKeyBag->accountDSID, errOut, SOSCreateError(kSOSErrorDecodeFailure, CFSTR("Null recoveryKeyBag Object"), NULL, error)); CFStringRef dsid = asString(SOSAccountGetValue(account, kSOSDSIDKey, NULL), error); require_action_quiet(dsid, errOut, SOSCreateError(kSOSErrorDecodeFailure, CFSTR("No DSID in Account"), NULL, error)); require_action_quiet(CFEqual(dsid, recoveryKeyBag->accountDSID), errOut, SOSCreateError(kSOSErrorDecodeFailure, CFSTR("Account/RecoveryKeybag DSID miss-match"), NULL, error)); retval = CFDataCreateCopy(allocator, recoveryKeyBag->recoveryKeyBag); errOut: return retval; }
bool SOSAccountSendIKSPSyncList(SOSAccountRef account, CFErrorRef *error){ bool result = true; __block CFErrorRef localError = NULL; __block CFMutableArrayRef ids = NULL; SOSCircleRef circle = NULL; require_action_quiet(SOSAccountIsInCircle(account, NULL), xit, SOSCreateError(kSOSErrorNoCircle, CFSTR("This device is not in circle"), NULL, &localError)); circle = SOSAccountGetCircle(account, error); ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); SOSCircleForEachValidPeer(circle, account->user_public, ^(SOSPeerInfoRef peer) { if (!SOSAccountIsThisPeerIDMe(account, SOSPeerInfoGetPeerID(peer))) { if(SOSPeerInfoShouldUseIDSTransport(SOSFullPeerInfoGetPeerInfo(account->my_identity), peer) && SOSPeerInfoShouldUseIDSMessageFragmentation(SOSFullPeerInfoGetPeerInfo(account->my_identity), peer) && !SOSPeerInfoShouldUseACKModel(SOSFullPeerInfoGetPeerInfo(account->my_identity), peer)){ SOSTransportMessageIDSSetFragmentationPreference(account->ids_message_transport, kCFBooleanTrue); CFStringRef deviceID = SOSPeerInfoCopyDeviceID(peer); if(deviceID != NULL){ CFArrayAppendValue(ids, deviceID); } CFReleaseNull(deviceID); } } });
bool SOSAccountPublishCloudParameters(SOSAccountRef account, CFErrorRef* error){ bool success = false; CFIndex cloud_der_len = der_sizeof_cloud_parameters( account->user_public, account->user_key_parameters, error); CFMutableDataRef cloudParameters = CFDataCreateMutableWithScratch(kCFAllocatorDefault, cloud_der_len); if (der_encode_cloud_parameters(account->user_public, account->user_key_parameters, error, CFDataGetMutableBytePtr(cloudParameters), CFDataGetMutablePastEndPtr(cloudParameters)) != NULL) { CFErrorRef changeError = NULL; if (SOSTrasnportKeyParameterPublishCloudParameters(account->key_transport, cloudParameters, error)) { success = true; } else { SOSCreateErrorWithFormat(kSOSErrorSendFailure, changeError, error, NULL, CFSTR("update parameters key failed [%@]"), cloudParameters); } CFReleaseSafe(changeError); } else { SOSCreateError(kSOSErrorEncodeFailure, CFSTR("Encoding parameters failed"), NULL, error); } CFReleaseNull(cloudParameters); return success; }
CFDataRef SOSUserKeyCreateGenerateParameters(CFErrorRef *error) { size_t saltlen = SALTMAX; uint8_t salt[saltlen]; size_t iterations = ITERATIONMIN; size_t keysize = 256; if(CCRandomCopyBytes(kCCRandomDefault, salt, sizeof(salt)) != kCCSuccess) { SOSCreateError(kSOSErrorProcessingFailure, CFSTR("CCRandomCopyBytes failed"), NULL, error); return NULL; } CFMutableDataRef result = CFDataCreateMutable(kCFAllocatorDefault, 0); CFDataSetLength(result, der_sizeof_pbkdf2_params(saltlen, salt, iterations, keysize)); uint8_t * encode = der_encode_pbkdf2_params(saltlen, salt, iterations, keysize, CFDataGetBytePtr(result), CFDataGetMutableBytePtr(result) + CFDataGetLength(result)); if (!encode) CFReleaseNull(result); if (result) { secnotice("keygen", "Created new parameters: iterations %zd, keysize %zd: %@", iterations, keysize, result); } return result; }
SOSRecoveryKeyBagRef SOSRecoveryKeyBagCreateForAccount(CFAllocatorRef allocator, SOSAccountRef account, CFDataRef pubData, CFErrorRef *error) { SOSRecoveryKeyBagRef retval = NULL; SOSGenCountRef gencount = NULL; require_action_quiet(account, errOut, SOSCreateError(kSOSErrorEncodeFailure, CFSTR("Null Account Object"), NULL, error)); CFStringRef dsid = asString(SOSAccountGetValue(account, kSOSDSIDKey, NULL), error); gencount = SOSGenerationCreate(); require_action_quiet(pubData && dsid && gencount, errOut, SOSCreateError(kSOSErrorEncodeFailure, CFSTR("Couldn't get recovery keybag components"), NULL, error)); retval = CFTypeAllocate(SOSRecoveryKeyBag, struct __OpaqueSOSRecoveryKeyBag, allocator); require_action_quiet(retval, errOut, SOSCreateError(kSOSErrorEncodeFailure, CFSTR("Couldn't get memory for recoveryKeyBag"), NULL, error)); retval->rkbVersion = CURRENT_RKB_VERSION; retval->accountDSID = CFStringCreateCopy(allocator, dsid); CFRetainAssign(retval->generation, gencount); retval->recoveryKeyBag = CFDataCreateCopy(allocator, pubData); errOut: CFReleaseNull(gencount); return retval; }
static bool sosAccountSetPreApprovedInfo(SOSAccountRef account, CFStringRef peerID, CFErrorRef *error) { bool retval = false; CFMutableSetRef preApprovedPeers = SOSAccountCopyPreApprovedHSA2Info(account); require_action_quiet(preApprovedPeers, errOut, SOSCreateError(kSOSErrorAllocationFailure, CFSTR("Can't Alloc Pre-Approved Peers Set"), NULL, error)); CFSetSetValue(preApprovedPeers, peerID); require(SOSAccountSetValue(account, kSOSHsaPreApprovedPeerKeyInfo, preApprovedPeers, error), errOut); retval = true; errOut: CFReleaseNull(preApprovedPeers); return retval; }
static bool SOSPeerInfoSign(SecKeyRef privKey, SOSPeerInfoRef peer, CFErrorRef *error) { bool status = false; const struct ccdigest_info *di = ccsha256_di(); uint8_t hbuf[di->output_size]; CFDataRef newSignature = NULL; require_action_quiet(SOSDescriptionHash(peer, di, hbuf, error), fail, SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to hash description for peer"), NULL, error)); newSignature = sosSignHash(privKey, di, hbuf); require_action_quiet(newSignature, fail, SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to sign peerinfo for peer"), NULL, error)); CFReleaseNull(peer->signature); peer->signature = newSignature; newSignature = NULL; status = true; fail: CFReleaseNull(newSignature); return status; }
size_t der_sizeof_RecoveryKeyBag(SOSRecoveryKeyBagRef RecoveryKeyBag, CFErrorRef *error) { size_t result = 0; if(RecoveryKeyBag && RecoveryKeyBag->recoveryKeyBag && RecoveryKeyBag->accountDSID && RecoveryKeyBag->generation) { size_t partSize = der_sizeof_string(RecoveryKeyBag->accountDSID, NULL); partSize += SOSGenCountGetDEREncodedSize(RecoveryKeyBag->generation, NULL); partSize += ccder_sizeof_uint64(RecoveryKeyBag->rkbVersion); partSize += der_sizeof_data(RecoveryKeyBag->recoveryKeyBag, NULL); result = ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, partSize); } else { SOSCreateError(kSOSErrorEncodeFailure, CFSTR("Null RecoveryKeyBag"), NULL, error); } return result; }
uint8_t* der_encode_RecoveryKeyBag(SOSRecoveryKeyBagRef RecoveryKeyBag, CFErrorRef *error, const uint8_t *der, uint8_t *der_end) { uint8_t *result = NULL; if (der_end == NULL) return der_end; if(RecoveryKeyBag && RecoveryKeyBag->recoveryKeyBag && RecoveryKeyBag->accountDSID && RecoveryKeyBag->generation) { der_end = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der, der_encode_string(RecoveryKeyBag->accountDSID, error, der, SOSGenCountEncodeToDER(RecoveryKeyBag->generation, error, der, ccder_encode_uint64(RecoveryKeyBag->rkbVersion, der, der_encode_data(RecoveryKeyBag->recoveryKeyBag, error, der, der_end))))); require_quiet(der_end == der, errOut); result = der_end; } else { SOSCreateError(kSOSErrorEncodeFailure, CFSTR("Null RecoveryKeyBag"), NULL, error); } errOut: return result; }
require_action_quiet(full_peer, fail, success = false); CFDictionaryAddValue(account->circle_identities, circleName, full_peer); CFReleaseNull(full_peer); } fail: CFReleaseNull(circle); } } } }); CFReleaseNull(array); require_quiet(success, fail); require_action_quiet(SOSAccountEnsureFactoryCircles(account), fail, SOSCreateError(kSOSErrorBadFormat, CFSTR("Cannot EnsureFactoryCircles"), (error != NULL) ? *error : NULL, error)); return account; fail: // Create a default error if we don't have one: SOSCreateError(kSOSErrorBadFormat, CFSTR("Bad Account DER"), NULL, error); CFReleaseNull(account); return NULL; } SOSAccountRef SOSAccountCreateFromDER_V2(CFAllocatorRef allocator, SOSDataSourceFactoryRef factory, CFErrorRef* error, const uint8_t** der_p, const uint8_t *der_end) {
SecKeyRef SOSUserKeygen(CFDataRef password, CFDataRef parameters, CFErrorRef *error) { size_t saltlen; const uint8_t *salt = NULL; size_t iterations = 0; size_t keysize = 0; const uint8_t *der = CFDataGetBytePtr(parameters); const uint8_t *der_end = der + CFDataGetLength(parameters); der = der_decode_pbkdf2_params(&saltlen, &salt, &iterations, &keysize, der, der_end); if (der == NULL) { SOSCreateErrorWithFormat(kSOSErrorDecodeFailure, NULL, error, NULL, CFSTR("Bad paramter encoding, got: %@"), parameters); return NULL; } if (keysize != 256) { SOSCreateErrorWithFormat(kSOSErrorUnsupported, NULL, error, NULL, CFSTR("Key size not supported, requested %zd."), keysize); return NULL; } if (saltlen < 4) { SOSCreateErrorWithFormat(kSOSErrorUnsupported, NULL, error, NULL, CFSTR("Salt length not supported, requested %zd."), saltlen); return NULL; } if (iterations < ITERATIONMIN) { SOSCreateErrorWithFormat(kSOSErrorUnsupported, NULL, error, NULL, CFSTR("Too few iterations, params suggested %zd."), iterations); return NULL; } const uint8_t *password_bytes = CFDataGetBytePtr(password); size_t password_length = CFDataGetLength(password); const size_t maxbytes = 128; ccec_const_cp_t cp = ccec_get_cp(keysize); struct ccrng_pbkdf2_prng_state pbkdf2_prng; ccec_full_ctx_decl_cp(cp, tmpkey); secnotice("keygen", "Generating key for: iterations %zd, keysize %zd: %@", iterations, keysize, parameters); if (ccrng_pbkdf2_prng_init(&pbkdf2_prng, maxbytes, password_length, password_bytes, saltlen, salt, iterations)) { SOSCreateError(kSOSErrorProcessingFailure, CFSTR("prng init failed"), NULL, error); return NULL; } if (ccec_generate_key(cp, (struct ccrng_state *)&pbkdf2_prng, tmpkey)) { SOSCreateError(kSOSErrorProcessingFailure, CFSTR("Keygen failed"), NULL, error); return NULL; } return ccec2SecKey(tmpkey); }
SOSCoderStatus SOSCoderUnwrap(SOSCoderRef coder, CFDataRef codedMessage, CFMutableDataRef *message, CFStringRef clientId, CFErrorRef *error) { if(codedMessage == NULL) return kSOSCoderDataReturned; if(coder->sessRef == NULL) return nullCoder(codedMessage, message); CFMutableStringRef action = CFStringCreateMutable(kCFAllocatorDefault, 0); /* This should be the "normal" case. We just use OTR to unwrap the received message. */ SOSCoderStatus result = kSOSCoderFailure; CFStringRef beginState = CFCopyDescription(coder->sessRef); enum SecOTRSMessageKind kind = SecOTRSGetMessageKind(coder->sessRef, codedMessage); switch (kind) { case kOTRNegotiationPacket: { /* If we're in here we haven't completed negotiating a session. Use SecOTRSProcessPacket() to go through the negotiation steps and immediately send a reply back if necessary using the sendBlock. This assumes the sendBlock is still available. */ CFMutableDataRef response = CFDataCreateMutable(kCFAllocatorDefault, 0); OSStatus ppstatus = errSecSuccess; if (response) { switch (ppstatus = SecOTRSProcessPacket(coder->sessRef, codedMessage, response)) { case errSecSuccess: if (CFDataGetLength(response) > 1) { CFStringAppendFormat(action, NULL, CFSTR("Sending OTR Response %s"), SecOTRPacketTypeString(response)); CFRetainAssign(coder->pendingResponse, response); result = kSOSCoderNegotiating; if (SecOTRSGetIsReadyForMessages(coder->sessRef)) { CFStringAppend(action, CFSTR(" begin waiting for data packet")); coder->waitingForDataPacket = true; } } else if(!SecOTRSGetIsReadyForMessages(coder->sessRef)) { CFStringAppend(action, CFSTR("stuck?")); result = kSOSCoderNegotiating; } else { CFStringAppend(action, CFSTR("completed negotiation")); result = kSOSCoderNegotiationCompleted; coder->waitingForDataPacket = false; } break; case errSecDecode: CFStringAppend(action, CFSTR("resending dh")); result = SOSCoderResendDH(coder, error); break; default: SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, (error != NULL) ? *error : NULL, error, NULL, CFSTR("%@ Cannot negotiate session (%ld)"), clientId, (long)ppstatus); result = kSOSCoderFailure; break; }; } else { SOSCreateErrorWithFormat(kSOSErrorAllocationFailure, (error != NULL) ? *error : NULL, error, NULL, CFSTR("%@ Cannot allocate CFData"), clientId); result = kSOSCoderFailure; } CFReleaseNull(response); break; } case kOTRDataPacket: if(!SecOTRSGetIsReadyForMessages(coder->sessRef)) { CFStringAppend(action, CFSTR("not ready, resending DH packet")); SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncFailed, 1); CFStringAppend(action, CFSTR("not ready for data; resending dh")); result = SOSCoderResendDH(coder, error); } else { if (coder->waitingForDataPacket) { CFStringAppend(action, CFSTR("got data packet we were waiting for ")); coder->waitingForDataPacket = false; } CFMutableDataRef exposed = CFDataCreateMutable(0, 0); OSStatus otrResult = SecOTRSVerifyAndExposeMessage(coder->sessRef, codedMessage, exposed); CFStringAppend(action, CFSTR("verify and expose message")); if (otrResult) { if (otrResult == errSecOTRTooOld) { CFStringAppend(action, CFSTR(" too old")); result = kSOSCoderStaleEvent; } else { SecError(otrResult, error, CFSTR("%@ Cannot expose message: %" PRIdOSStatus), clientId, otrResult); secerror("%@ Decode OTR Protected Packet: %@", clientId, error ? *error : NULL); result = kSOSCoderFailure; } } else { CFStringAppend(action, CFSTR("decoded OTR protected packet")); *message = exposed; exposed = NULL; result = kSOSCoderDataReturned; } CFReleaseNull(exposed); } break; default: secerror("%@ Unknown packet type: %@", clientId, codedMessage); SOSCreateError(kSOSErrorDecodeFailure, CFSTR("Unknown packet type"), (error != NULL) ? *error : NULL, error); result = kSOSCoderFailure; break; }; // Uber state log if (result == kSOSCoderFailure && error && *error) CFStringAppendFormat(action, NULL, CFSTR(" %@"), *error); secnotice("coder", "%@ %@ %s %@ %@ returned %s", clientId, beginState, SecOTRPacketTypeString(codedMessage), action, coder->sessRef, SOSCoderString(result)); CFReleaseSafe(beginState); CFRelease(action); return result; }
// Start OTR negotiation if we haven't already done so. SOSCoderStatus SOSCoderStart(SOSCoderRef coder, CFErrorRef *error) { CFMutableStringRef action = CFStringCreateMutable(kCFAllocatorDefault, 0); CFStringRef beginState = NULL; SOSCoderStatus result = kSOSCoderFailure; CFMutableDataRef startPacket = NULL; require_action_quiet(coder->sessRef, coderFailure, CFStringAppend(action, CFSTR("*** no otr session ***"))); beginState = CFCopyDescription(coder->sessRef); require_action_quiet(!coder->waitingForDataPacket, negotiatingOut, CFStringAppend(action, CFSTR("waiting for peer to send first data packet"))); require_action_quiet(!SecOTRSGetIsReadyForMessages(coder->sessRef), coderFailure, CFStringAppend(action, CFSTR("otr session ready")); result = kSOSCoderDataReturned); require_action_quiet(SecOTRSGetIsIdle(coder->sessRef), negotiatingOut, CFStringAppend(action, CFSTR("otr negotiating already"))); require_action_quiet(startPacket = CFDataCreateMutable(kCFAllocatorDefault, 0), coderFailure, SOSCreateError(kSOSErrorAllocationFailure, CFSTR("alloc failed"), NULL, error)); require_quiet(SOSOTRSAppendStartPacket(coder->sessRef, startPacket, error), coderFailure); CFRetainAssign(coder->pendingResponse, startPacket); negotiatingOut: result = kSOSCoderNegotiating; coderFailure: // Uber state log if (result == kSOSCoderFailure && error && *error) CFStringAppendFormat(action, NULL, CFSTR(" %@"), *error); secnotice("coder", "%@ %s %@ %@ returned %s", beginState, SecOTRPacketTypeString(startPacket), action, coder->sessRef, SOSCoderString(result)); CFReleaseNull(startPacket); CFReleaseSafe(beginState); CFRelease(action); return result; }