+static CFDataRef SOSPiggyBackBlobCopyEncodedData(SOSGenCountRef gencount, SecKeyRef pubKey, CFDataRef signature, CFAllocatorRef allocator, CFErrorRef *error)
+{
+ return CFDataCreateWithDER(kCFAllocatorDefault, SOSPiggyBackBlobGetDEREncodedSize(gencount, pubKey, signature, error), ^uint8_t*(size_t size, uint8_t *buffer) {
+ return SOSPiggyBackBlobEncodeToDER(gencount, pubKey, signature, error, buffer, (uint8_t *) buffer + size);
+ });
+}
+
+struct piggyBackBlob {
+ SOSGenCountRef gencount;
+ SecKeyRef pubKey;
+ CFDataRef signature;
+};
+
+static struct piggyBackBlob *SOSPiggyBackBlobCreateFromDER(CFAllocatorRef allocator, CFErrorRef *error,
+ const uint8_t** der_p, const uint8_t *der_end) {
+ const uint8_t *sequence_end;
+ struct piggyBackBlob *retval = NULL;
+ SOSGenCountRef gencount = NULL;
+ CFDataRef signature = NULL;
+ CFDataRef publicBytes = NULL;
+
+ *der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end);
+ require_action_quiet(sequence_end != NULL, errOut,
+ SOSCreateError(kSOSErrorBadFormat, CFSTR("Bad Blob DER"), (error != NULL) ? *error : NULL, error));
+ *der_p = der_decode_number(allocator, 0, &gencount, error, *der_p, sequence_end);
+ *der_p = der_decode_data_or_null(kCFAllocatorDefault, &publicBytes, error, *der_p, der_end);
+ *der_p = der_decode_data_or_null(kCFAllocatorDefault, &signature, error, *der_p, der_end);
+ require_action_quiet(*der_p && *der_p == der_end, errOut,
+ SOSCreateError(kSOSErrorBadFormat, CFSTR("Didn't consume all bytes for pbblob"), (error != NULL) ? *error : NULL, error));
+ retval = malloc(sizeof(struct piggyBackBlob));
+ retval->gencount = gencount;
+ retval->signature = signature;
+ retval->pubKey = SecKeyCreateFromPublicData(kCFAllocatorDefault, kSecECDSAAlgorithmID, publicBytes);
+
+errOut:
+ if(!retval) {
+ CFReleaseNull(gencount);
+ CFReleaseNull(publicBytes);
+ CFReleaseNull(signature);
+ }
+ return retval;
+}
+
+static struct piggyBackBlob *SOSPiggyBackBlobCreateFromData(CFAllocatorRef allocator, CFDataRef blobData, CFErrorRef *error)
+{
+ size_t size = CFDataGetLength(blobData);
+ const uint8_t *der = CFDataGetBytePtr(blobData);
+ struct piggyBackBlob *inflated = SOSPiggyBackBlobCreateFromDER(allocator, error, &der, der + size);
+ return inflated;
+}
+
+
+
+SOSPeerInfoRef SOSAccountCopyApplication(SOSAccountRef account, CFErrorRef* error) {
+ SOSPeerInfoRef applicant = NULL;
+ SecKeyRef userKey = SOSAccountGetPrivateCredential(account, error);
+ if(!userKey) return false;
+ require_quiet(SOSAccountEnsureFullPeerAvailable(account, error), errOut);
+ require(SOSFullPeerInfoPromoteToApplication(account->my_identity, userKey, error), errOut);
+ applicant = SOSPeerInfoCreateCopy(kCFAllocatorDefault, (SOSFullPeerInfoGetPeerInfo(account->my_identity)), error);
+errOut:
+ return applicant;
+}
+
+
+CFDataRef SOSAccountCopyCircleJoiningBlob(SOSAccountRef account, SOSPeerInfoRef applicant, CFErrorRef *error) {
+ SOSGenCountRef gencount = NULL;
+ CFDataRef signature = NULL;
+ SecKeyRef ourKey = NULL;
+
+ CFDataRef pbblob = NULL;
+
+ SecKeyRef userKey = SOSAccountGetTrustedPublicCredential(account, error);
+ require_quiet(userKey, errOut);
+
+ require_action_quiet(applicant, errOut, SOSCreateError(kSOSErrorProcessingFailure, CFSTR("No applicant provided"), (error != NULL) ? *error : NULL, error));
+ require_quiet(SOSPeerInfoApplicationVerify(applicant, userKey, error), errOut);
+
+ {
+ SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInfo(account);
+ ourKey = SOSFullPeerInfoCopyDeviceKey(fpi, error);
+ require_quiet(ourKey, errOut);
+ }
+
+ SOSCircleRef currentCircle = SOSAccountGetCircle(account, error);
+ require_quiet(currentCircle, errOut);
+
+ SOSCircleRef prunedCircle = SOSCircleCopyCircle(NULL, currentCircle, error);
+ require_quiet(prunedCircle, errOut);
+ require_quiet(SOSCirclePreGenerationSign(prunedCircle, userKey, error), errOut);
+
+ gencount = SOSGenerationIncrementAndCreate(SOSCircleGetGeneration(prunedCircle));
+
+ signature = SOSCircleCopyNextGenSignatureWithPeerAdded(prunedCircle, applicant, ourKey, error);
+ require_quiet(signature, errOut);
+
+ pbblob = SOSPiggyBackBlobCopyEncodedData(gencount, ourKey, signature, kCFAllocatorDefault, error);
+
+errOut:
+ CFReleaseNull(gencount);
+ CFReleaseNull(signature);
+ CFReleaseNull(ourKey);
+
+ return pbblob;
+}
+
+bool SOSAccountJoinWithCircleJoiningBlob(SOSAccountRef account, CFDataRef joiningBlob, CFErrorRef *error) {
+ bool retval = false;
+ SecKeyRef userKey = NULL;
+ struct piggyBackBlob *pbb = NULL;
+
+ userKey = SOSAccountGetPrivateCredential(account, error);
+ require_quiet(userKey, errOut);
+ pbb = SOSPiggyBackBlobCreateFromData(kCFAllocatorDefault, joiningBlob, error);
+ require_quiet(pbb, errOut);
+
+ SOSAccountSetValue(account, kSOSUnsyncedViewsKey, kCFBooleanTrue, NULL);
+
+ retval = SOSAccountModifyCircle(account, error, ^bool(SOSCircleRef copyOfCurrent) {
+ return SOSCircleAcceptPeerFromHSA2(copyOfCurrent, userKey,
+ pbb->gencount,
+ pbb->pubKey,
+ pbb->signature,
+ account->my_identity, error);;
+
+ });
+
+errOut:
+ if(pbb) {
+ CFReleaseNull(pbb->gencount);
+ CFReleaseNull(pbb->pubKey);
+ CFReleaseNull(pbb->signature);
+ free(pbb);
+ }
+ return retval;
+}
+
+static char boolToChars(bool val, char truechar, char falsechar) {
+ return val? truechar: falsechar;
+}
+
+#define ACCOUNTLOGSTATE "accountLogState"
+void SOSAccountLogState(SOSAccountRef account) {
+ bool hasPubKey = account->user_public != NULL;
+ bool pubTrusted = account->user_public_trusted;
+ bool hasPriv = account->_user_private != NULL;
+ SOSCCStatus stat = SOSAccountGetCircleStatus(account, NULL);
+ CFStringRef userPubKeyID = (account->user_public) ? SOSCopyIDOfKeyWithLength(account->user_public, 8, NULL):
+ CFStringCreateCopy(kCFAllocatorDefault, CFSTR("*No Key*"));
+
+ secnotice(ACCOUNTLOGSTATE, "Start");
+
+ secnotice(ACCOUNTLOGSTATE, "ACCOUNT: [keyStatus: %c%c%c hpub %@] [SOSCCStatus: %@]",
+ boolToChars(hasPubKey, 'U', 'u'), boolToChars(pubTrusted, 'T', 't'), boolToChars(hasPriv, 'I', 'i'),
+ userPubKeyID,
+ SOSAccountGetSOSCCStatusString(stat)
+ );
+ CFReleaseNull(userPubKeyID);
+ if(account->trusted_circle) SOSCircleLogState(ACCOUNTLOGSTATE, account->trusted_circle, account->user_public, SOSAccountGetMyPeerID(account));
+ else secnotice(ACCOUNTLOGSTATE, "ACCOUNT: No Circle");
+}
+
+void SOSAccountLogViewState(SOSAccountRef account) {
+ bool isInCircle = SOSAccountIsInCircle(account, NULL);
+ require_quiet(isInCircle, imOut);
+ SOSPeerInfoRef mpi = SOSAccountGetMyPeerInfo(account);
+ bool isInitialComplete = SOSAccountHasCompletedInitialSync(account);
+ bool isBackupComplete = SOSAccountHasCompletedRequiredBackupSync(account);
+
+ CFSetRef views = mpi ? SOSPeerInfoCopyEnabledViews(mpi) : NULL;
+ CFStringSetPerformWithDescription(views, ^(CFStringRef description) {
+ secnotice(ACCOUNTLOGSTATE, "Sync: %c%c PeerViews: %@",
+ boolToChars(isInitialComplete, 'I', 'i'),
+ boolToChars(isBackupComplete, 'B', 'b'),
+ description);
+ });
+ CFReleaseNull(views);
+ CFSetRef unsyncedViews = SOSAccountCopyOutstandingViews(account);
+ CFStringSetPerformWithDescription(views, ^(CFStringRef description) {
+ secnotice(ACCOUNTLOGSTATE, "outstanding views: %@", description);
+ });
+ CFReleaseNull(unsyncedViews);
+
+imOut:
+ secnotice(ACCOUNTLOGSTATE, "Finish");
+
+ return;
+}