X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/ce3c8656732c924baf7e88df75eab50891bdc471..fa7225c82381bac4432a6edf16f53b5370238d85:/OSX/sec/Security/SecSignatureVerificationSupport.c diff --git a/OSX/sec/Security/SecSignatureVerificationSupport.c b/OSX/sec/Security/SecSignatureVerificationSupport.c new file mode 100644 index 00000000..2e29fa3c --- /dev/null +++ b/OSX/sec/Security/SecSignatureVerificationSupport.c @@ -0,0 +1,119 @@ +// +// SecSignatureVerificationSupport.c +// sec +// + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +static const uint8_t *sec_decode_forced_uint(cc_size n, + cc_unit *r, const uint8_t *der, const uint8_t *der_end) +{ + size_t len; + der = ccder_decode_tl(CCDER_INTEGER, &len, der, der_end); + if (der && ccn_read_uint(n, r, len, der) >= 0) { + return der + len; + } + return NULL; +} + +static CFErrorRef +SecCreateSignatureVerificationError(OSStatus errorCode, CFStringRef descriptionString) +{ + const CFStringRef defaultDescription = CFSTR("Error verifying signature."); + const void* keys[1] = { kCFErrorDescriptionKey }; + const void* values[2] = { (descriptionString) ? descriptionString : defaultDescription }; + return CFErrorCreateWithUserInfoKeysAndValues(kCFAllocatorDefault, + kCFErrorDomainOSStatus, errorCode, keys, values, 1); +} + +static void +SecRecreateSignatureWithAlgId(SecKeyRef publicKey, const SecAsn1AlgId *publicKeyAlgId, + const uint8_t *oldSignature, size_t oldSignatureSize, + uint8_t **newSignature, size_t *newSignatureSize) +{ + if (!publicKey || !publicKeyAlgId || + kSecECDSAAlgorithmID != SecKeyGetAlgorithmId(publicKey)) { + // ECDSA SHA-256 is the only type of signature currently supported by this function + return; + } + + cc_size n = ccec_cp_n(ccec_cp_256()); + cc_unit r[n], s[n]; + + const uint8_t *oldSignatureEnd = oldSignature + oldSignatureSize; + + oldSignature = ccder_decode_sequence_tl(&oldSignatureEnd, oldSignature, oldSignatureEnd); + oldSignature = sec_decode_forced_uint(n, r, oldSignature, oldSignatureEnd); + oldSignature = sec_decode_forced_uint(n, s, oldSignature, oldSignatureEnd); + if (!oldSignature || !(oldSignatureEnd == oldSignature)) { + // failed to decode the old signature successfully + *newSignature = NULL; + return; + } + + const uint8_t *outputPointer = *newSignature; + uint8_t *outputEndPointer = *newSignature + *newSignatureSize; + + *newSignature = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, + outputEndPointer, outputPointer, + ccder_encode_integer(n, r, outputPointer, ccder_encode_integer(n, s, outputPointer, outputEndPointer))); + long newSigSize = outputEndPointer - *newSignature; + *newSignatureSize = (newSigSize >= 0) ? (size_t)newSigSize : 0; +} + +bool SecVerifySignatureWithPublicKey(SecKeyRef publicKey, const SecAsn1AlgId *publicKeyAlgId, + const uint8_t *dataToHash, size_t amountToHash, + const uint8_t *signatureStart, size_t signatureSize, + CFErrorRef *error) +{ + OSStatus errorCode = errSecParam; + require(signatureSize > 0, fail); + + errorCode = SecKeyDigestAndVerify(publicKey, publicKeyAlgId, + dataToHash, amountToHash, + (uint8_t*)signatureStart, signatureSize); + require_noerr(errorCode, fail); + return true; + +fail: + ; // Semicolon works around compiler issue that won't recognize a declaration directly after a label + + // fallback to potentially fix signatures with missing zero-byte padding. + // worst-case is that both integers get zero-padded, plus size of each integer and sequence size increases by 1 + size_t replacementSignatureLen = signatureSize + 5; + uint8_t *replacementSignature = malloc(replacementSignatureLen); + require_quiet(replacementSignature, fail2); + + uint8_t *replacementSignaturePtr = replacementSignature; + SecRecreateSignatureWithAlgId(publicKey, publicKeyAlgId, signatureStart, signatureSize, &replacementSignaturePtr, &replacementSignatureLen); + require_quiet(replacementSignaturePtr, fail2); + + require_noerr_quiet(SecKeyDigestAndVerify(publicKey, publicKeyAlgId, dataToHash, amountToHash, replacementSignaturePtr, replacementSignatureLen), fail2); + + free(replacementSignature); + return true; + +fail2: + if (replacementSignature) { + free(replacementSignature); + } + if (error) { + *error = SecCreateSignatureVerificationError(errorCode, CFSTR("Unable to verify signature")); + } + return false; +}