/ OSX / sec / Security / SecSignatureVerificationSupport.c
SecSignatureVerificationSupport.c
  1  //
  2  //  SecSignatureVerificationSupport.c
  3  //  sec
  4  //
  5  
  6  #include <TargetConditionals.h>
  7  #include <AssertMacros.h>
  8  #include <Security/SecSignatureVerificationSupport.h>
  9  
 10  #include <CoreFoundation/CFString.h>
 11  #include <utilities/SecCFError.h>
 12  #include <utilities/SecCFWrappers.h>
 13  
 14  #include <Security/SecBasePriv.h>
 15  #include <Security/SecKey.h>
 16  #include <Security/SecKeyPriv.h>
 17  #include <Security/SecECKeyPriv.h>
 18  
 19  #include <corecrypto/ccn.h>
 20  #include <corecrypto/ccec.h>
 21  #include <corecrypto/ccder.h>
 22  
 23  static const uint8_t *sec_decode_forced_uint(cc_size n,
 24      cc_unit *r, const uint8_t *der, const uint8_t *der_end)
 25  {
 26      size_t len;
 27      der = ccder_decode_tl(CCDER_INTEGER, &len, der, der_end);
 28      if (der && ccn_read_uint(n, r, len, der) >= 0) {
 29          return der + len;
 30      }
 31      return NULL;
 32  }
 33  
 34  static CFErrorRef
 35  SecCreateSignatureVerificationError(OSStatus errorCode, CFStringRef descriptionString)
 36  {
 37      const CFStringRef defaultDescription = CFSTR("Error verifying signature.");
 38      const void* keys[1] = { kCFErrorDescriptionKey };
 39      const void* values[2] = { (descriptionString) ? descriptionString : defaultDescription };
 40      return CFErrorCreateWithUserInfoKeysAndValues(kCFAllocatorDefault,
 41          kCFErrorDomainOSStatus, errorCode, keys, values, 1);
 42  }
 43  
 44  #pragma clang diagnostic push
 45  #pragma clang diagnostic ignored "-Wdeprecated-declarations"
 46  static void
 47  SecRecreateSignatureWithAlgId(SecKeyRef publicKey, const SecAsn1AlgId *publicKeyAlgId,
 48      const uint8_t *oldSignature, size_t oldSignatureSize,
 49      uint8_t **newSignature, size_t *newSignatureSize)
 50  #pragma clang diagnostic pop
 51  {
 52      if (!publicKey || !publicKeyAlgId ||
 53          kSecECDSAAlgorithmID != SecKeyGetAlgorithmId(publicKey)) {
 54          // ECDSA SHA-256 is the only type of signature currently supported by this function
 55          return;
 56      }
 57  
 58      cc_size n = ccec_cp_n(ccec_cp_256());
 59      cc_unit r[n], s[n];
 60  
 61      const uint8_t *oldSignatureEnd = oldSignature + oldSignatureSize;
 62  
 63      oldSignature = ccder_decode_sequence_tl(&oldSignatureEnd, oldSignature, oldSignatureEnd);
 64      oldSignature = sec_decode_forced_uint(n, r, oldSignature, oldSignatureEnd);
 65      oldSignature = sec_decode_forced_uint(n, s, oldSignature, oldSignatureEnd);
 66      if (!oldSignature || !(oldSignatureEnd == oldSignature)) {
 67          // failed to decode the old signature successfully
 68          *newSignature = NULL;
 69          return;
 70      }
 71  
 72      const uint8_t *outputPointer = *newSignature;
 73      uint8_t *outputEndPointer = *newSignature + *newSignatureSize;
 74  
 75      *newSignature = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE,
 76          outputEndPointer, outputPointer,
 77          ccder_encode_integer(n, r, outputPointer, ccder_encode_integer(n, s, outputPointer, outputEndPointer)));
 78      long newSigSize = outputEndPointer - *newSignature;
 79      *newSignatureSize = (newSigSize >= 0) ? (size_t)newSigSize : 0;
 80  }
 81  
 82  #pragma clang diagnostic push
 83  #pragma clang diagnostic ignored "-Wdeprecated-declarations"
 84  bool SecVerifySignatureWithPublicKey(SecKeyRef publicKey, const SecAsn1AlgId *publicKeyAlgId,
 85                                       const uint8_t *dataToHash, size_t amountToHash,
 86                                       const uint8_t *signatureStart, size_t signatureSize,
 87                                       CFErrorRef *error)
 88  #pragma clang diagnostic pop
 89  {
 90      OSStatus errorCode = errSecParam;
 91      require(signatureSize > 0, fail);
 92  
 93      errorCode = SecKeyDigestAndVerify(publicKey, publicKeyAlgId,
 94                                        dataToHash, amountToHash,
 95                                        (uint8_t*)signatureStart, signatureSize);
 96      require_noerr_quiet(errorCode, fail);
 97      return true;
 98  
 99  fail:
100      ; // Semicolon works around compiler issue that won't recognize a declaration directly after a label
101  
102      // fallback to potentially fix signatures with missing zero-byte padding.
103      // worst-case is that both integers get zero-padded, plus size of each integer and sequence size increases by 1
104      size_t replacementSignatureLen = signatureSize + 5;
105      uint8_t *replacementSignature = malloc(replacementSignatureLen);
106      require_quiet(replacementSignature, fail2);
107  
108      uint8_t *replacementSignaturePtr = replacementSignature;
109      SecRecreateSignatureWithAlgId(publicKey, publicKeyAlgId, signatureStart, signatureSize, &replacementSignaturePtr, &replacementSignatureLen);
110      require_quiet(replacementSignaturePtr, fail2);
111  
112      require_noerr_quiet(SecKeyDigestAndVerify(publicKey, publicKeyAlgId, dataToHash, amountToHash, replacementSignaturePtr, replacementSignatureLen), fail2);
113  
114      free(replacementSignature);
115      return true;
116  
117  fail2:
118      if (replacementSignature) {
119          free(replacementSignature);
120      }
121      if (error) {
122          *error = SecCreateSignatureVerificationError(errorCode, CFSTR("Unable to verify signature"));
123      }
124      return false;
125  }