/ keychain / SecureObjectSync / SOSRingConcordanceTrust.c
SOSRingConcordanceTrust.c
  1  //
  2  //  SOSRingConcordanceTrust.c
  3  //  sec
  4  //
  5  //  Created by Richard Murphy on 3/15/15.
  6  //
  7  //
  8  
  9  #include <AssertMacros.h>
 10  
 11  #include "keychain/SecureObjectSync/SOSInternal.h"
 12  #include "keychain/SecureObjectSync/SOSPeer.h"
 13  #include "keychain/SecureObjectSync/SOSPeerInfoInternal.h"
 14  #include "keychain/SecureObjectSync/SOSPeerInfoCollections.h"
 15  #include "keychain/SecureObjectSync/SOSCircle.h"
 16  #include <Security/SecFramework.h>
 17  
 18  #include <Security/SecKey.h>
 19  #include <Security/SecKeyPriv.h>
 20  #include <CoreFoundation/CoreFoundation.h>
 21  
 22  #include <utilities/SecCFWrappers.h>
 23  
 24  //#include "ckdUtilities.h"
 25  
 26  #include <corecrypto/ccder.h>
 27  #include <corecrypto/ccdigest.h>
 28  #include <corecrypto/ccsha2.h>
 29  
 30  
 31  #include <utilities/der_plist.h>
 32  #include <utilities/der_plist_internal.h>
 33  #include <corecrypto/ccder.h>
 34  #include <utilities/der_date.h>
 35  
 36  #include <stdlib.h>
 37  
 38  #include "SOSRing.h"
 39  #include "SOSRingUtils.h"
 40  
 41  static inline CFDictionaryRef SOSPeerInfoDictionaryCreate(CFSetRef peers) {
 42      size_t n = CFSetGetCount(peers);
 43      SOSPeerInfoRef  peerInfos[n];
 44      CFStringRef     peerIDs[n];
 45      CFSetGetValues(peers, (const void **) peerInfos);
 46      for(size_t i = 0; i < n; i++) peerIDs[i] = SOSPeerInfoGetPeerID(peerInfos[i]);
 47      return CFDictionaryCreate(NULL, (const void **)peerIDs, (const void **)peerInfos, n, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
 48  }
 49  
 50  static inline SOSConcordanceStatus CheckPeerStatus(CFStringRef peerID, SOSPeerInfoRef peer, SOSRingRef ring, SecKeyRef userPub, CFErrorRef *error) {
 51      SOSConcordanceStatus result = kSOSConcordanceNoPeer;
 52      SecKeyRef pubKey = NULL;
 53  
 54      require_action_quiet(peer, exit, result = kSOSConcordanceNoPeer);
 55      pubKey = SOSPeerInfoCopyPubKey(peer, error);
 56      require_quiet(pubKey, exit);
 57      require_action_quiet(SOSRingHasPeerID(ring, peerID), exit, result = kSOSConcordanceNoPeer);
 58      require_action_quiet(SOSPeerInfoApplicationVerify(peer, userPub, NULL), exit, result = kSOSConcordanceNoPeer);
 59      require_action_quiet(SOSRingVerifySignatureExists(ring, pubKey, error), exit, result = kSOSConcordanceNoPeerSig);
 60      require_action_quiet(SOSRingVerify(ring, pubKey, error), exit, result = kSOSConcordanceBadPeerSig);
 61  
 62      result = kSOSConcordanceTrusted;
 63  
 64  exit:
 65      CFReleaseNull(pubKey);
 66      return result;
 67  }
 68  
 69  static inline SOSConcordanceStatus CombineStatus(SOSConcordanceStatus status1, SOSConcordanceStatus status2)
 70  {
 71      if (status1 == kSOSConcordanceTrusted || status2 == kSOSConcordanceTrusted)
 72          return kSOSConcordanceTrusted;
 73  
 74      if (status1 == kSOSConcordanceBadPeerSig || status2 == kSOSConcordanceBadPeerSig)
 75          return kSOSConcordanceBadPeerSig;
 76  
 77      if (status1 == kSOSConcordanceNoPeerSig || status2 == kSOSConcordanceNoPeerSig)
 78          return kSOSConcordanceNoPeerSig;
 79  
 80      return status1;
 81  }
 82  
 83  SOSConcordanceStatus GetSignersStatus(CFSetRef peers, SOSRingRef signersRing, SOSRingRef statusRing,
 84                                                      SecKeyRef userPubkey, CFStringRef excludePeerID, CFErrorRef *error) {
 85      CFDictionaryRef ringPeerInfos = SOSPeerInfoDictionaryCreate(peers);
 86      __block SOSConcordanceStatus status = kSOSConcordanceNoPeer;
 87      SOSRingForEachPeerID(signersRing, ^(CFStringRef peerID) {
 88          SOSPeerInfoRef pi = (SOSPeerInfoRef) CFDictionaryGetValue(ringPeerInfos, peerID);
 89          SOSConcordanceStatus peerStatus = CheckPeerStatus(peerID, pi, statusRing, userPubkey, error);
 90  
 91          secnotice("ring", "concordance-signer-status: %@ -> %d", peerID, peerStatus);
 92  
 93          if (peerStatus == kSOSConcordanceNoPeerSig &&
 94              (CFEqualSafe(SOSPeerInfoGetPeerID(pi), excludePeerID) || SOSPeerInfoIsCloudIdentity(pi)))
 95              peerStatus = kSOSConcordanceNoPeer;
 96  
 97          status = CombineStatus(status, peerStatus);
 98      });
 99  
100      return status;
101  }
102  
103  
104  SOSConcordanceStatus GetSignersStatus_Transitive(CFSetRef peers, SOSRingRef signersRing, SOSRingRef statusRing,
105                                                   SecKeyRef userPubkey, CFStringRef excludePeerID, CFErrorRef *error) {
106      __block SOSConcordanceStatus status = kSOSConcordanceNoPeer;
107      
108      CFSetForEach(peers, ^(const void *value) {
109          SOSPeerInfoRef pi = (SOSPeerInfoRef) value;
110          CFStringRef peerID = SOSPeerInfoGetPeerID(pi);
111          if(SOSRingHasPeerWithID(statusRing, peerID, NULL)) {
112              SOSConcordanceStatus peerStatus = CheckPeerStatus(peerID, pi, statusRing, userPubkey, error);
113              
114              if (peerStatus == kSOSConcordanceNoPeerSig &&
115                  (CFEqualSafe(SOSPeerInfoGetPeerID(pi), excludePeerID) || SOSPeerInfoIsCloudIdentity(pi)))
116                  peerStatus = kSOSConcordanceNoPeer;
117              
118              status = CombineStatus(status, peerStatus);
119          }
120      });
121      
122      return status;
123  }
124  
125  
126  SOSConcordanceStatus SOSRingUserKeyConcordanceTrust(SOSFullPeerInfoRef me, CFSetRef peers, SOSRingRef knownRing, SOSRingRef proposedRing,
127                                                      SecKeyRef knownPubkey, SecKeyRef userPubkey,
128                                                      CFStringRef excludePeerID, CFErrorRef *error) {
129      if(userPubkey == NULL) {
130          SOSCreateError(kSOSErrorPublicKeyAbsent, CFSTR("Concordance with no public key"), NULL, error);
131          return kSOSConcordanceNoUserKey;
132      }
133  
134      if (SOSRingIsEmpty_Internal(proposedRing)) {
135          return kSOSConcordanceTrusted;
136      }
137  
138      if(!SOSRingVerifySignatureExists(proposedRing, userPubkey, error)) {
139          SOSCreateError(kSOSErrorBadSignature, CFSTR("No public signature"), (error != NULL) ? *error : NULL, error);
140          return kSOSConcordanceNoUserSig;
141      }
142  
143      if(!SOSRingVerify(proposedRing, userPubkey, error)) {
144          SOSCreateError(kSOSErrorBadSignature, CFSTR("Bad public signature"), (error != NULL) ? *error : NULL, error);
145          return kSOSConcordanceBadUserSig;
146      }
147  
148      if (SOSRingIsEmpty_Internal(knownRing) || SOSRingIsOffering_Internal(proposedRing)) {
149          return GetSignersStatus(peers, proposedRing, proposedRing, userPubkey, NULL, error);
150      }
151  
152      if(SOSRingIsOlderGeneration(proposedRing, knownRing)) {
153          SOSCreateError(kSOSErrorReplay, CFSTR("Bad generation"), NULL, error);
154          return kSOSConcordanceGenOld;
155      }
156  
157      if(knownPubkey == NULL) knownPubkey = userPubkey;
158      if(!SOSRingVerify(knownRing, knownPubkey, error)) knownPubkey = userPubkey;
159      return GetSignersStatus(peers, knownRing, proposedRing, knownPubkey, CFSTR("novalue"), error);
160  }
161  
162  
163  SOSConcordanceStatus SOSRingPeerKeyConcordanceTrust(SOSFullPeerInfoRef me, CFSetRef peers, SOSRingRef knownRing, SOSRingRef proposedRing,
164                                                      __unused SecKeyRef knownPubkey, SecKeyRef userPubkey,
165                                                      CFStringRef excludePeerID, CFErrorRef *error) {
166      if(userPubkey == NULL) {
167          SOSCreateError(kSOSErrorPublicKeyAbsent, CFSTR("Concordance with no public key - need to validate application"), NULL, error);
168          return kSOSConcordanceNoUserKey;
169      }
170  
171      if (SOSRingIsEmpty_Internal(proposedRing)) {
172          secnotice("ring", "ring empty -> trusted");
173          return kSOSConcordanceTrusted;
174      }
175  
176      if (SOSRingIsEmpty_Internal(knownRing) || SOSRingIsOffering_Internal(proposedRing)) {
177          return GetSignersStatus(peers, proposedRing, proposedRing, userPubkey, NULL, error);
178      }
179  
180      if(SOSRingIsOlderGeneration(proposedRing, knownRing)) {
181          SOSCreateError(kSOSErrorReplay, CFSTR("Bad generation"), NULL, error);
182          return kSOSConcordanceGenOld;
183      }
184      return GetSignersStatus(peers, knownRing, proposedRing, userPubkey, excludePeerID, error);
185  }