/ keychain / SecureObjectSync / SOSCoder.c
SOSCoder.c
  1  /*
  2   * Copyright (c) 2013-2014 Apple Inc. All Rights Reserved.
  3   *
  4   * @APPLE_LICENSE_HEADER_START@
  5   * 
  6   * This file contains Original Code and/or Modifications of Original Code
  7   * as defined in and that are subject to the Apple Public Source License
  8   * Version 2.0 (the 'License'). You may not use this file except in
  9   * compliance with the License. Please obtain a copy of the License at
 10   * http://www.opensource.apple.com/apsl/ and read it before using this
 11   * file.
 12   * 
 13   * The Original Code and all software distributed under the License are
 14   * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 15   * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 16   * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 17   * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 18   * Please see the License for the specific language governing rights and
 19   * limitations under the License.
 20   * 
 21   * @APPLE_LICENSE_HEADER_END@
 22   */
 23  
 24  #include <stdlib.h>
 25  
 26  #include <CoreFoundation/CFBase.h>
 27  #include <CoreFoundation/CFError.h>
 28  
 29  #include <Security/SecBasePriv.h>
 30  #include <Security/SecOTR.h>
 31  #include <Security/SecOTRSession.h>
 32  #include <Security/SecOTRSessionPriv.h>
 33  #include "keychain/SecureObjectSync/SOSInternal.h"
 34  #include "keychain/SecureObjectSync/SOSFullPeerInfo.h"
 35  #include <Security/SecureObjectSync/SOSPeerInfo.h>
 36  #include "keychain/SecureObjectSync/SOSPeer.h"
 37  #include "keychain/SecureObjectSync/SOSCoder.h"
 38  
 39  #include <utilities/SecCFRelease.h>
 40  #include <utilities/SecCFWrappers.h>
 41  #include <utilities/SecIOFormat.h>
 42  #include <utilities/SecCFError.h>
 43  #include <utilities/SecCoreCrypto.h>
 44  #include <utilities/debugging.h>
 45  
 46  #include <utilities/der_plist.h>
 47  #include <utilities/der_plist_internal.h>
 48  
 49  #include <corecrypto/ccder.h>
 50  #include <utilities/iCloudKeychainTrace.h>
 51  
 52  #include "AssertMacros.h"
 53  
 54  struct __OpaqueSOSCoder {
 55      CFRuntimeBase _base;
 56  
 57      CFStringRef peer_id;
 58      SecOTRSessionRef sessRef;
 59      bool waitingForDataPacket;
 60      CFDataRef pendingResponse;
 61  
 62      CFDataRef hashOfLastReceived;
 63      bool      lastReceivedWasOld;
 64  };
 65  
 66  #define lastReceived_di ccsha1_di
 67  
 68  CFGiblisWithCompareFor(SOSCoder)
 69  
 70  static CFStringRef SOSCoderCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
 71      SOSCoderRef coder = (SOSCoderRef)cf;
 72      if(coder){
 73          __block CFStringRef desc = NULL;
 74          CFDataPerformWithHexString(coder->hashOfLastReceived, ^(CFStringRef dataString) {
 75              desc = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("<Coder %@ %@ %s%s>"),
 76                                              coder->sessRef,
 77                                              dataString,
 78                                              coder->waitingForDataPacket ? "W" : "w",
 79                                              coder->lastReceivedWasOld ? "O" : "o"
 80                                              );
 81  
 82          });
 83          return desc;
 84      }
 85      else
 86          return CFSTR("NULL");
 87  }
 88  
 89  static Boolean SOSCoderCompare(CFTypeRef cfA, CFTypeRef cfB) {
 90      SOSCoderRef coderA = (SOSCoderRef)cfA, coderB = (SOSCoderRef)cfB;
 91      // Use mainly to see if peerB is actually this device (peerA)
 92      return CFStringCompare(coderA->peer_id, coderB->peer_id, 0) == kCFCompareEqualTo;
 93  }
 94  
 95  
 96  static const char *SOSCoderString(SOSCoderStatus coderStatus) {
 97      switch (coderStatus) {
 98          case kSOSCoderDataReturned: return "DataReturned";
 99          case kSOSCoderNegotiating: return "Negotiating";
100          case kSOSCoderNegotiationCompleted: return "NegotiationCompleted";
101          case kSOSCoderFailure: return "Failure";
102          case kSOSCoderStaleEvent: return "StaleEvent";
103          case kSOSCoderTooNew: return "TooNew";
104          default: return "StatusUnknown";
105      }
106  }
107  
108  static CFMutableDataRef sessSerializedCreate(SOSCoderRef coder, CFErrorRef *error) {
109      CFMutableDataRef otr_state = NULL;
110          
111      if(!coder || !coder->sessRef) {
112          SOSCreateErrorWithFormat(kSOSErrorUnexpectedType, NULL, error, 0, CFSTR("No session reference."));
113          return NULL;
114      }
115      
116      if ((otr_state = CFDataCreateMutable(NULL, 0)) == NULL) {
117          SOSCreateErrorWithFormat(kSOSErrorAllocationFailure, NULL, error, 0, CFSTR("Mutable Data allocation failed."));
118          return NULL;
119      }
120      
121      if (errSecSuccess != SecOTRSAppendSerialization(coder->sessRef, otr_state)) {
122          SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, NULL, error, 0, CFSTR("Append Serialization failed."));
123          CFReleaseSafe(otr_state);
124          return NULL;
125      }
126      
127      return otr_state;
128  
129  }
130  
131  static size_t der_sizeof_optional_data(CFDataRef data) {
132      return data ? der_sizeof_data(data, NULL) : 0;
133  }
134  
135  static uint8_t* der_encode_optional_data(CFDataRef data, CFErrorRef *error, const uint8_t* der, uint8_t* der_end) {
136      return data ? der_encode_data(data, error, der, der_end) : der_end;
137  }
138  
139  
140  
141  static size_t SOSCoderGetDEREncodedSize(SOSCoderRef coder, CFErrorRef *error) {
142      size_t encoded_size = 0;
143      CFMutableDataRef otr_state = sessSerializedCreate(coder, error);
144  
145      if (otr_state) {
146          size_t data_size = der_sizeof_data(otr_state, error);
147          size_t waiting_size = ccder_sizeof_bool(coder->waitingForDataPacket, error);
148          size_t pending_size = der_sizeof_optional_data(coder->pendingResponse);
149          
150          if ((data_size != 0) && (waiting_size != 0))
151          {
152              encoded_size = ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, data_size + waiting_size + pending_size);
153          }
154          CFReleaseSafe(otr_state);
155      }
156      return encoded_size;
157  }
158  
159  
160  static uint8_t* SOSCoderEncodeToDER(SOSCoderRef coder, CFErrorRef* error, const uint8_t* der, uint8_t* der_end) {
161      if(!der_end) return NULL;
162      uint8_t* result = NULL;
163      CFMutableDataRef otr_state = sessSerializedCreate(coder, error);
164      
165      if(otr_state) {
166          result = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
167                                               der_encode_data(otr_state, error, der,
168                                               ccder_encode_bool(coder->waitingForDataPacket, der,
169                                               der_encode_optional_data(coder->pendingResponse, error, der, der_end))));
170          CFReleaseSafe(otr_state);
171      }
172      return result;
173  }
174  
175  bool SOSCoderIsCoderInAwaitingState(SOSCoderRef coder){
176      return SecOTRSessionIsSessionInAwaitingState(coder->sessRef);
177  }
178  
179  CFDataRef SOSCoderCopyDER(SOSCoderRef coder, CFErrorRef* error) {
180      CFMutableDataRef encoded = NULL;
181      size_t encoded_size = SOSCoderGetDEREncodedSize(coder, error);
182      
183      if (encoded_size > 0) {
184          encoded = CFDataCreateMutable(NULL, encoded_size);
185          if (encoded) {
186              CFDataSetLength(encoded, encoded_size);
187              uint8_t * der = CFDataGetMutableBytePtr(encoded);
188              uint8_t * der_end = der + encoded_size;
189              if (!SOSCoderEncodeToDER(coder, error, der, der_end)) {
190                  CFReleaseNull(encoded);
191                  encoded = NULL;
192              }
193          }
194      }
195      return encoded;
196  }
197  
198  static SOSCoderRef SOSCoderCreate_internal() {
199      SOSCoderRef p = CFTypeAllocate(SOSCoder, struct __OpaqueSOSCoder, kCFAllocatorDefault);
200  
201      p->peer_id = NULL;
202      p->sessRef = NULL;
203      p->pendingResponse = NULL;
204      p->waitingForDataPacket = false;
205  
206      p->hashOfLastReceived = NULL;
207      p->lastReceivedWasOld = false;
208  
209      return p;
210  
211  }
212  
213  // 0 - Type not understood
214  // 1 - OCTET_STRING, just stored the data for OTR
215  // 2 - SEQUENCE with no version value
216  // 3 - SEQUENCE with version value we pull out of the CCDER_INTEGER
217  
218  typedef enum coderExportFormatVersion {
219      kNotUnderstood = 0,
220      kCoderAsOTRDataOnly = 1,
221      kCoderAsSequence = 2,
222      kCoderAsVersionedSequence = 3,
223  
224      kCurrentCoderExportVersion = kCoderAsVersionedSequence
225  } CoderExportFormatVersion;
226  
227  static uint64_t SOSCoderGetExportedVersion(const uint8_t *der, const uint8_t *der_end) {
228      ccder_tag tag;
229      uint64_t result = kNotUnderstood;
230      require(ccder_decode_tag(&tag, der, der_end),xit);
231      switch (tag) {
232          case CCDER_OCTET_STRING: // TODO: this code is safe to delete?
233              result = kCoderAsOTRDataOnly;
234              break;
235  
236          case CCDER_CONSTRUCTED_SEQUENCE:
237          {
238              const uint8_t *sequence_end = NULL;
239              der = ccder_decode_sequence_tl(&sequence_end, der, der_end);
240              ccder_tag firstSequenceTag;
241              require(ccder_decode_tag(&firstSequenceTag, der, der_end),xit);
242  
243              switch (firstSequenceTag) {
244                  case CCDER_OCTET_STRING:
245                      result = kCoderAsSequence;
246                      break;
247                  case CCDER_INTEGER:
248                      der = ccder_decode_uint64(NULL, der, sequence_end);
249                      if (der == NULL) {
250                          result = kNotUnderstood;
251                      } else {
252                          result = kCoderAsVersionedSequence;
253                      }
254                      break;
255              }
256          }
257      }
258  xit:
259      return result;
260  
261  }
262  
263  SOSCoderRef SOSCoderCreateFromData(CFDataRef exportedData, CFErrorRef *error) {
264      // TODO: fill in errors for all failure cases
265      //require_action_quiet(coder, xit, SOSCreateError(kSOSErrorSendFailure, CFSTR("No coder for peer"), NULL, error));
266  
267      SOSCoderRef p = SOSCoderCreate_internal();
268      
269      const uint8_t *der = CFDataGetBytePtr(exportedData);
270      const uint8_t *der_end = der + CFDataGetLength(exportedData);
271      
272      CFDataRef otr_data = NULL;
273  
274      switch (SOSCoderGetExportedVersion(der, der_end)) {
275          case kCoderAsOTRDataOnly:
276              der = der_decode_data(kCFAllocatorDefault, &otr_data, error, der, der_end);
277              p->waitingForDataPacket = false;
278              break;
279  
280          case kCoderAsSequence:
281          {
282              const uint8_t *sequence_end = NULL;
283              der = ccder_decode_sequence_tl(&sequence_end, der, der_end);
284  
285              require_action_quiet(sequence_end == der_end, fail, SecCFDERCreateError(kSOSErrorDecodeFailure, CFSTR("Extra data in SOS coder"), NULL, error));
286  
287              der = der_decode_data(kCFAllocatorDefault, &otr_data, error, der, sequence_end);
288              der = ccder_decode_bool(&p->waitingForDataPacket, der, sequence_end);
289              if (der != sequence_end) { // optionally a pending response
290                  der = der_decode_data(kCFAllocatorDefault, &p->pendingResponse, error, der, sequence_end);
291              }
292          }
293              break;
294  
295          case kCoderAsVersionedSequence:
296          {
297              const uint8_t *sequence_end = NULL;
298              der = ccder_decode_sequence_tl(&sequence_end, der, der_end);
299  
300              require_action_quiet(sequence_end == der_end, fail, SecCFDERCreateError(kSOSErrorDecodeFailure, CFSTR("Extra data in SOS coder"), NULL, error));
301  
302              uint64_t version;
303              der = ccder_decode_uint64(&version, der, sequence_end);
304              if (version != kCoderAsVersionedSequence) {
305                  SOSErrorCreate(kSOSErrorDecodeFailure, error, NULL, CFSTR("Unsupported Sequence Version: %lld"), version);
306                  goto fail;
307              }
308  
309              der = der_decode_data(kCFAllocatorDefault, &otr_data, error, der, sequence_end);
310              der = ccder_decode_bool(&p->waitingForDataPacket, der, sequence_end);
311              der = ccder_decode_bool(&p->lastReceivedWasOld, der, sequence_end);
312              der = der_decode_data(kCFAllocatorDefault, &p->hashOfLastReceived, error, der, sequence_end);
313              if (der != sequence_end) { // optionally a pending response
314                  der = der_decode_data(kCFAllocatorDefault, &p->pendingResponse, error, der, sequence_end);
315              }
316          }
317              break;
318  
319          default:
320              SOSErrorCreate(kSOSErrorDecodeFailure, error, NULL, CFSTR("Unsupported SOS Coder DER"));
321              goto fail;
322      }
323  
324      require(der, fail);
325      
326      p->sessRef = SecOTRSessionCreateFromData(NULL, otr_data);
327      require(p->sessRef, fail);
328  
329      if (p->hashOfLastReceived == NULL)
330          p->hashOfLastReceived = CFDataCreateMutableWithScratch(kCFAllocatorDefault, lastReceived_di()->output_size);
331  
332      CFReleaseSafe(otr_data);
333      return p;
334          
335  fail:
336      CFReleaseNull(p);
337      CFReleaseSafe(otr_data);
338      return NULL;
339  }
340  
341  
342  SOSCoderRef SOSCoderCreate(SOSPeerInfoRef peerInfo, SOSFullPeerInfoRef myPeerInfo, CFBooleanRef useCompact, CFErrorRef *error) {
343      CFAllocatorRef allocator = CFGetAllocator(peerInfo);
344      
345      SOSCoderRef coder = SOSCoderCreate_internal();
346  
347      CFErrorRef localError = NULL;
348  
349      SecOTRFullIdentityRef myRef = NULL;
350      SecOTRPublicIdentityRef peerRef = NULL;
351      SecKeyRef privateKey = NULL;
352      SecKeyRef publicKey = NULL;
353  
354      if (myPeerInfo && peerInfo) {
355          privateKey = SOSFullPeerInfoCopyDeviceKey(myPeerInfo, &localError);
356          require_quiet(privateKey, errOut);
357  
358          myRef = SecOTRFullIdentityCreateFromSecKeyRefSOS(allocator, privateKey, &localError);
359          require_quiet(myRef, errOut);
360          
361          CFReleaseNull(privateKey);
362      
363          publicKey = SOSPeerInfoCopyPubKey(peerInfo, &localError);
364          require(publicKey, errOut);
365          
366          peerRef = SecOTRPublicIdentityCreateFromSecKeyRef(allocator, publicKey, &localError);
367          require_quiet(peerRef, errOut);
368          
369          if(useCompact == kCFBooleanTrue)
370              coder->sessRef = SecOTRSessionCreateFromIDAndFlags(allocator, myRef, peerRef, kSecOTRUseAppleCustomMessageFormat);
371          
372          else
373              coder->sessRef = SecOTRSessionCreateFromID(allocator, myRef, peerRef);
374  
375          require(coder->sessRef, errOut);
376          
377          coder->waitingForDataPacket = false;
378          coder->pendingResponse = NULL;
379  
380          CFReleaseNull(publicKey);
381          CFReleaseNull(privateKey);
382          CFReleaseNull(myRef);
383          CFReleaseNull(peerRef);
384      } else {
385          secnotice("coder", "NULL Coder requested, no transport security");
386      }
387  
388      coder->hashOfLastReceived = CFDataCreateMutableWithScratch(kCFAllocatorDefault, lastReceived_di()->output_size);
389      coder->lastReceivedWasOld = false;
390  
391      SOSCoderStart(coder, NULL);
392  
393      return coder;
394  
395  errOut:
396      secerror("Coder create failed: %@\n", localError ? localError : (CFTypeRef)CFSTR("No local error in SOSCoderCreate"));
397      secerror("Coder create failed: %@\n", error ? *error : (CFTypeRef)CFSTR("WTF NULL?"));
398      CFReleaseNull(myRef);
399      CFReleaseNull(peerRef);
400      CFReleaseNull(publicKey);
401      CFReleaseNull(privateKey);
402  
403      CFReleaseNull(coder);
404      return NULL;
405  }
406  
407  static void SOSCoderDestroy(CFTypeRef cf)
408  {
409      SOSCoderRef coder = (SOSCoderRef) cf;
410      if (coder) {
411          CFReleaseNull(coder->sessRef);
412          CFReleaseNull(coder->pendingResponse);
413          CFReleaseNull(coder->hashOfLastReceived);
414      }
415  }
416  
417  void SOSCoderReset(SOSCoderRef coder)
418  {
419      SecOTRSessionReset(coder->sessRef);
420      coder->waitingForDataPacket = false;
421      CFReleaseNull(coder->pendingResponse);
422  
423      coder->lastReceivedWasOld = false;
424      CFReleaseNull(coder->hashOfLastReceived);
425      coder->hashOfLastReceived = CFDataCreateMutableWithScratch(kCFAllocatorDefault, lastReceived_di()->output_size);
426  }
427  
428  bool SOSCoderIsFor(SOSCoderRef coder, SOSPeerInfoRef peerInfo, SOSFullPeerInfoRef myPeerInfo) {
429      SecKeyRef theirPublicKey = NULL;
430      SecKeyRef myPublicKey = NULL;
431      bool isForThisPair = false;
432      CFErrorRef localError = NULL;
433  
434      myPublicKey = SOSPeerInfoCopyPubKey(SOSFullPeerInfoGetPeerInfo(myPeerInfo), &localError);
435      require(myPublicKey, errOut);
436  
437      theirPublicKey = SOSPeerInfoCopyPubKey(peerInfo, &localError);
438      require(theirPublicKey, errOut);
439  
440      isForThisPair = SecOTRSIsForKeys(coder->sessRef, myPublicKey, theirPublicKey);
441  
442  errOut:
443      if (localError) {
444          secerror("SOSCoderIsFor failed: %@\n", localError ? localError : (CFTypeRef)CFSTR("No local error in SOSCoderCreate"));
445      }
446  
447      CFReleaseNull(myPublicKey);
448      CFReleaseNull(theirPublicKey);
449      CFReleaseNull(localError);
450      return isForThisPair;
451  }
452  
453  CFDataRef SOSCoderCopyPendingResponse(SOSCoderRef coder)
454  {
455      return coder->pendingResponse ? CFDataCreateCopy(kCFAllocatorDefault, coder->pendingResponse) : NULL;
456  }
457  
458  void SOSCoderConsumeResponse(SOSCoderRef coder)
459  {
460      CFReleaseNull(coder->pendingResponse);
461  }
462  
463  static bool SOSOTRSAppendStartPacket(SecOTRSessionRef session, CFMutableDataRef appendPacket, CFErrorRef *error) {
464      OSStatus otrStatus = SecOTRSAppendStartPacket(session, appendPacket);
465      if (otrStatus != errSecSuccess) {
466          SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, (error != NULL) ? *error : NULL, error, NULL, CFSTR("append start packet returned: %" PRIdOSStatus), otrStatus);
467      }
468      return otrStatus == errSecSuccess;
469  }
470  
471  // Start OTR negotiation if we haven't already done so.
472  SOSCoderStatus
473  SOSCoderStart(SOSCoderRef coder, CFErrorRef *error) {
474      CFMutableStringRef action = CFStringCreateMutable(kCFAllocatorDefault, 0);
475      CFStringRef beginState = NULL;
476      SOSCoderStatus result = kSOSCoderFailure;
477      CFMutableDataRef startPacket = NULL;
478  
479      require_action_quiet(coder->sessRef, coderFailure, CFStringAppend(action, CFSTR("*** no otr session ***")));
480      beginState = CFCopyDescription(coder->sessRef);
481      require_action_quiet(!coder->waitingForDataPacket, negotiatingOut, CFStringAppend(action, CFSTR("waiting for peer to send first data packet")));
482      require_action_quiet(!SecOTRSGetIsReadyForMessages(coder->sessRef), coderFailure, CFStringAppend(action, CFSTR("otr session ready"));
483                           result = kSOSCoderDataReturned);
484      require_action_quiet(SecOTRSGetIsIdle(coder->sessRef), negotiatingOut, CFStringAppend(action, CFSTR("otr negotiating already")));
485      require_action_quiet(startPacket = CFDataCreateMutable(kCFAllocatorDefault, 0), coderFailure, SOSCreateError(kSOSErrorAllocationFailure, CFSTR("alloc failed"), NULL, error));
486      require_quiet(SOSOTRSAppendStartPacket(coder->sessRef, startPacket, error), coderFailure);
487      CFRetainAssign(coder->pendingResponse, startPacket);
488  
489  negotiatingOut:
490      result = kSOSCoderNegotiating;
491  coderFailure:
492      // Uber state log
493      if (result == kSOSCoderFailure && error && *error)
494          CFStringAppendFormat(action, NULL, CFSTR(" %@"), *error);
495      secinfo("coder", "%@ %s %@ %@ returned %s", beginState,
496                SecOTRPacketTypeString(startPacket), action, coder->sessRef, SOSCoderString(result));
497      CFReleaseNull(startPacket);
498      CFReleaseSafe(beginState);
499      CFRelease(action);
500  
501      return result;
502  
503  }
504  
505  SOSCoderStatus
506  SOSCoderResendDH(SOSCoderRef coder, CFErrorRef *error) {
507      if(coder->sessRef == NULL) return kSOSCoderDataReturned;
508      CFMutableDataRef startPacket = CFDataCreateMutable(kCFAllocatorDefault, 0);
509      SOSCoderStatus result = kSOSCoderFailure;
510      require_noerr_quiet(SecOTRSAppendRestartPacket(coder->sessRef, startPacket), exit);
511      secnotice("coder", "Resending OTR Start %@", startPacket);
512      CFRetainAssign(coder->pendingResponse, startPacket);
513      result = kSOSCoderNegotiating;
514  exit:
515      CFReleaseNull(startPacket);
516      return result;
517  }
518  
519  
520  static SOSCoderStatus nullCoder(CFDataRef from, CFMutableDataRef *to) {
521      *to = CFDataCreateMutableCopy(NULL, CFDataGetLength(from), from);
522      return kSOSCoderDataReturned;
523  }
524  
525  SOSCoderStatus SOSCoderUnwrap(SOSCoderRef coder, CFDataRef codedMessage, CFMutableDataRef *message,
526                                CFStringRef clientId, CFErrorRef *error) {
527      if(codedMessage == NULL) return kSOSCoderDataReturned;
528      if(coder->sessRef == NULL) return nullCoder(codedMessage, message);
529      CFMutableStringRef action = CFStringCreateMutable(kCFAllocatorDefault, 0);
530      /* This should be the "normal" case.  We just use OTR to unwrap the received message. */
531      SOSCoderStatus result = kSOSCoderFailure;
532  
533      CFStringRef beginState = CFCopyDescription(coder->sessRef);
534      enum SecOTRSMessageKind kind = SecOTRSGetMessageKind(coder->sessRef, codedMessage);
535  
536  
537      switch (kind) {
538          case kOTRNegotiationPacket: {
539              /* If we're in here we haven't completed negotiating a session.  Use SecOTRSProcessPacket() to go through
540               the negotiation steps and immediately send a reply back if necessary using the sendBlock.  This
541               assumes the sendBlock is still available.
542               */
543              CFMutableDataRef response = CFDataCreateMutable(kCFAllocatorDefault, 0);
544              OSStatus ppstatus = errSecSuccess;
545              if (response) {
546                  switch (ppstatus = SecOTRSProcessPacket(coder->sessRef, codedMessage, response)) {
547                      case errSecSuccess:
548                          if (CFDataGetLength(response) > 1) {
549                              CFStringAppendFormat(action, NULL, CFSTR("Sending OTR Response %s"), SecOTRPacketTypeString(response));
550                              CFRetainAssign(coder->pendingResponse, response);
551                              result = kSOSCoderNegotiating;
552                              if (SecOTRSGetIsReadyForMessages(coder->sessRef)) {
553                                  CFStringAppend(action, CFSTR(" begin waiting for data packet"));
554                                  coder->waitingForDataPacket = true;
555                              }
556                          } else if(!SecOTRSGetIsReadyForMessages(coder->sessRef)) {
557                              CFStringAppend(action, CFSTR("stuck?"));
558                              result = kSOSCoderNegotiating;
559                          } else {
560                              CFStringAppend(action, CFSTR("completed negotiation"));
561                              result = kSOSCoderNegotiationCompleted;
562                              coder->waitingForDataPacket = false;
563                          }
564                          break;
565                      case errSecDecode:
566                          CFStringAppend(action, CFSTR("resending dh"));
567                          result = SOSCoderResendDH(coder, error);
568                          break;
569                      default:
570                          SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, (error != NULL) ? *error : NULL, error, NULL, CFSTR("%@ Cannot negotiate session (%ld)"), clientId, (long)ppstatus);
571                          result = kSOSCoderFailure;
572                          break;
573                  };
574              } else {
575                  SOSCreateErrorWithFormat(kSOSErrorAllocationFailure, (error != NULL) ? *error : NULL, error, NULL, CFSTR("%@ Cannot allocate CFData"), clientId);
576                  result = kSOSCoderFailure;
577              }
578              
579              CFReleaseNull(response);
580              
581              break;
582          }
583  
584          case kOTRDataPacket:
585          {
586              CFDataRef previousMessageHash = coder->hashOfLastReceived;
587              coder->hashOfLastReceived = CFDataCreateWithHash(kCFAllocatorDefault, lastReceived_di(), CFDataGetBytePtr(codedMessage), CFDataGetLength(codedMessage));
588              bool lastWasOld = coder->lastReceivedWasOld;
589              coder->lastReceivedWasOld = false;
590  
591              if(!SecOTRSGetIsReadyForMessages(coder->sessRef)) {
592                  CFStringAppend(action, CFSTR("not ready for data; resending DH packet"));
593                  result = SOSCoderResendDH(coder, error);
594              } else {
595                  if (coder->waitingForDataPacket) {
596                      CFStringAppend(action, CFSTR("got data packet we were waiting for "));
597                      coder->waitingForDataPacket = false;
598                  }
599                  CFMutableDataRef exposed = CFDataCreateMutable(0, 0);
600                  OSStatus otrResult = SecOTRSVerifyAndExposeMessage(coder->sessRef, codedMessage, exposed);
601                  CFStringAppend(action, CFSTR("verify and expose message"));
602                  switch(otrResult) {
603                      case errSecSuccess:
604                          CFStringAppend(action, CFSTR("decoded OTR protected packet"));
605                          CFTransferRetained(*message, exposed);
606                          result = kSOSCoderDataReturned;
607                          break;
608                      case errSecOTRTooOld:
609                          if (CFEqualSafe(previousMessageHash, coder->hashOfLastReceived)) {
610                              CFStringAppend(action, CFSTR(" repeated"));
611                              result = kSOSCoderStaleEvent;
612                          } else {
613                              coder->lastReceivedWasOld = true;
614                              if (lastWasOld) {
615                                  CFStringAppend(action, CFSTR(" too old, repeated renegotiating"));
616                                  // Fail so we will renegotiate
617                                  result = kSOSCoderFailure;
618                              } else {
619                                  CFStringAppend(action, CFSTR(" too old, forcing message"));
620                                  // Force message send.
621                                  result = kSOSCoderForceMessage;
622                              }
623                          }
624                          break;
625                      case errSecOTRIDTooNew:
626                          CFStringAppend(action, CFSTR(" too new"));
627                          result = kSOSCoderTooNew;
628                          break;
629                      default:
630                          SecError(otrResult, error, CFSTR("%@ Cannot expose message: %" PRIdOSStatus), clientId, otrResult);
631                          secerror("%@ Decode OTR Protected Packet: %@", clientId, error ? *error : NULL);
632                          result = kSOSCoderFailure;
633                          break;
634                  }
635  
636                  CFReleaseNull(exposed);
637              }
638              CFReleaseNull(previousMessageHash);
639          }
640              break;
641  
642          default:
643              secerror("%@ Unknown packet type: %@", clientId, codedMessage);
644              SOSCreateError(kSOSErrorDecodeFailure, CFSTR("Unknown packet type"), (error != NULL) ? *error : NULL, error);
645              result = kSOSCoderFailure;
646              break;
647      };
648  
649      // Uber state log
650      if (result == kSOSCoderFailure && error && *error)
651          CFStringAppendFormat(action, NULL, CFSTR(" %@"), *error);
652      secnotice("coder", "%@ %@ %s %@ %@ returned %s", clientId, beginState,
653                SecOTRPacketTypeString(codedMessage), action, coder->sessRef, SOSCoderString(result));
654      CFReleaseSafe(beginState);
655      CFRelease(action);
656  
657      return result;
658  }
659  
660  
661  SOSCoderStatus SOSCoderWrap(SOSCoderRef coder, CFDataRef message, CFMutableDataRef *codedMessage, CFStringRef clientId, CFErrorRef *error) {
662      CFMutableStringRef action = CFStringCreateMutable(kCFAllocatorDefault, 0);
663      SOSCoderStatus result = kSOSCoderDataReturned;
664      CFStringRef beginState = NULL;
665      CFMutableDataRef encoded = NULL;
666      OSStatus otrStatus = 0;
667  
668      require_action_quiet(coder->sessRef, errOut,
669                           CFStringAppend(action, CFSTR("*** using null coder ***"));
670                           result = nullCoder(message, codedMessage));
671      beginState = CFCopyDescription(coder->sessRef);
672      require_action_quiet(SecOTRSGetIsReadyForMessages(coder->sessRef), errOut,
673                           CFStringAppend(action, CFSTR("not ready"));
674                           result = kSOSCoderNegotiating);
675      require_action_quiet(!coder->waitingForDataPacket, errOut,
676                           CFStringAppend(action, CFSTR("waiting for peer to send data packet first"));
677                           result = kSOSCoderNegotiating);
678      require_action_quiet(encoded = CFDataCreateMutable(kCFAllocatorDefault, 0), errOut,
679                           SOSCreateErrorWithFormat(kSOSErrorAllocationFailure, NULL, error, NULL, CFSTR("%@ alloc failed"), clientId);
680                           result = kSOSCoderFailure);
681      require_noerr_action_quiet(otrStatus = SecOTRSSignAndProtectMessage(coder->sessRef, message, encoded), errOut,
682                                 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, (error != NULL) ? *error : NULL, error, NULL, CFSTR("%@ cannot protect message: %" PRIdOSStatus), clientId, otrStatus);
683                                 CFReleaseNull(encoded);
684                                 result = kSOSCoderFailure);
685      *codedMessage = encoded;
686  
687  errOut:
688      // Uber state log
689      if (result == kSOSCoderFailure && error && *error)
690          CFStringAppendFormat(action, NULL, CFSTR(" %@"), *error);
691      secinfo("coder", "%@ %@ %s %@ %@ returned %s", clientId, beginState,
692                SecOTRPacketTypeString(encoded), action, coder->sessRef, SOSCoderString(result));
693      CFReleaseSafe(beginState);
694      CFRelease(action);
695  
696      return result;
697  }
698  
699  bool SOSCoderCanWrap(SOSCoderRef coder) {
700      return coder->sessRef && SecOTRSGetIsReadyForMessages(coder->sessRef) && !coder->waitingForDataPacket;
701  }