X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_transform/lib/Digest.cpp diff --git a/Security/libsecurity_transform/lib/Digest.cpp b/Security/libsecurity_transform/lib/Digest.cpp new file mode 100644 index 00000000..20c9abd4 --- /dev/null +++ b/Security/libsecurity_transform/lib/Digest.cpp @@ -0,0 +1,592 @@ +#include "Digest.h" +#include "SecDigestTransform.h" +#include "Utilities.h" + +Digest::Digest(CFStringRef digestType, size_t digestLength) : mDigestType(digestType), mDigestLength(digestLength) +{ + // we don't need to retain the type here because it's a constant +} + + + +Digest::~Digest() +{ +} + +int Digest::LengthForType(CFStringRef type) +{ + if (!CFStringCompare(kSecDigestSHA1, type, kCFCompareAnchored)) { + return CC_SHA1_DIGEST_LENGTH; + } else if (!CFStringCompare(kSecDigestSHA2, type, kCFCompareAnchored)) { + // XXX SHA2 comes in multiple length flavors, our buest guess is "the big one" + return CC_SHA256_DIGEST_LENGTH; + } else if (!CFStringCompare(kSecDigestMD2, type, kCFCompareAnchored)) { + return CC_MD2_DIGEST_LENGTH; + } else if (!CFStringCompare(kSecDigestMD4, type, kCFCompareAnchored)) { + return CC_MD4_DIGEST_LENGTH; + } else if (!CFStringCompare(kSecDigestMD5, type, kCFCompareAnchored)) { + return CC_MD5_DIGEST_LENGTH; + } else { + return transforms_assume(type == CFSTR("A supported digest type")); + } +} + +CFDictionaryRef Digest::CopyState() +{ + CFMutableDictionaryRef dr = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionaryAddValue(dr, kSecDigestTypeAttribute, mDigestType); + + CFIndex size = mDigestLength; + CFNumberRef nr = CFNumberCreate(NULL, kCFNumberCFIndexType, &size); + CFDictionaryAddValue(dr, kSecDigestLengthAttribute, nr); + CFRelease(nr); + + return dr; +} + + + +MD2Digest::MD2Digest() : Digest(kSecDigestMD2, CC_MD2_DIGEST_LENGTH) +{ + CC_MD2_Init(&mContext); +} + + + +void MD2Digest::Update(const void* buffer, size_t length) +{ + CC_MD2_Update(&mContext, buffer, (CC_LONG)length); +} + + + +size_t MD2Digest::DigestLength() +{ + return CC_MD2_DIGEST_LENGTH; +} + + + +const void* MD2Digest::Finalize() +{ + CC_MD2_Final(mDigestBuffer, &mContext); + return mDigestBuffer; +} + + + +MD4Digest::MD4Digest() : Digest(kSecDigestMD4, CC_MD4_DIGEST_LENGTH) +{ + CC_MD4_Init(&mContext); +} + + + +void MD4Digest::Update(const void* buffer, size_t length) +{ + CC_MD4_Update(&mContext, buffer, (CC_LONG)length); +} + + + +size_t MD4Digest::DigestLength() +{ + return CC_MD4_DIGEST_LENGTH; +} + + + +const void* MD4Digest::Finalize() +{ + CC_MD4_Final(mDigestBuffer, &mContext); + return mDigestBuffer; +} + + + + + + +MD5Digest::MD5Digest() : Digest(kSecDigestMD5, CC_MD5_DIGEST_LENGTH) +{ + CC_MD5_Init(&mContext); +} + + + +void MD5Digest::Update(const void* buffer, size_t length) +{ + CC_MD5_Update(&mContext, buffer, (CC_LONG)length); +} + + + +size_t MD5Digest::DigestLength() +{ + return CC_MD5_DIGEST_LENGTH; +} + + + +const void* MD5Digest::Finalize() +{ + CC_MD5_Final(mDigestBuffer, &mContext); + return mDigestBuffer; +} + + + +SHA1Digest::SHA1Digest() : Digest(kSecDigestSHA1, CC_SHA1_DIGEST_LENGTH) +{ + CC_SHA1_Init(&mContext); +} + + + +void SHA1Digest::Update(const void* buffer, size_t length) +{ + CC_SHA1_Update(&mContext, buffer, (CC_LONG)length); +} + + + +size_t SHA1Digest::DigestLength() +{ + return CC_SHA1_DIGEST_LENGTH; +} + + + +const void* SHA1Digest::Finalize() +{ + CC_SHA1_Final(mDigestBuffer, &mContext); + return mDigestBuffer; +} + + + +SHA256Digest::SHA256Digest() : Digest(kSecDigestSHA2, CC_SHA256_DIGEST_LENGTH) +{ + CC_SHA256_Init(&mContext); +} + + + +void SHA256Digest::Update(const void* buffer, size_t length) +{ + CC_SHA256_Update(&mContext, buffer, (CC_LONG)length); +} + + + +size_t SHA256Digest::DigestLength() +{ + return CC_SHA256_DIGEST_LENGTH; +} + + + +const void* SHA256Digest::Finalize() +{ + CC_SHA256_Final(mDigestBuffer, &mContext); + return mDigestBuffer; +} + + + +SHA224Digest::SHA224Digest() : Digest(kSecDigestSHA2, CC_SHA224_DIGEST_LENGTH) +{ + CC_SHA224_Init(&mContext); +} + + + +void SHA224Digest::Update(const void* buffer, size_t length) +{ + CC_SHA224_Update(&mContext, buffer, (CC_LONG)length); +} + + + +size_t SHA224Digest::DigestLength() +{ + return CC_SHA224_DIGEST_LENGTH; +} + + + +const void* SHA224Digest::Finalize() +{ + CC_SHA224_Final(mDigestBuffer, &mContext); + return mDigestBuffer; +} + + + +SHA512Digest::SHA512Digest() : Digest(kSecDigestSHA2, CC_SHA512_DIGEST_LENGTH) +{ + CC_SHA512_Init(&mContext); +} + + + +void SHA512Digest::Update(const void* buffer, size_t length) +{ + CC_SHA512_Update(&mContext, buffer, (CC_LONG)length); +} + + + +size_t SHA512Digest::DigestLength() +{ + return CC_SHA512_DIGEST_LENGTH; +} + + + +const void* SHA512Digest::Finalize() +{ + CC_SHA512_Final(mDigestBuffer, &mContext); + return mDigestBuffer; +} + + + +SHA384Digest::SHA384Digest() : Digest(kSecDigestSHA2, CC_SHA384_DIGEST_LENGTH) +{ + CC_SHA384_Init(&mContext); +} + + + +void SHA384Digest::Update(const void* buffer, size_t length) +{ + CC_SHA384_Update(&mContext, buffer, (CC_LONG)length); +} + + + +size_t SHA384Digest::DigestLength() +{ + return CC_SHA384_DIGEST_LENGTH; +} + + + +const void* SHA384Digest::Finalize() +{ + CC_SHA384_Final(mDigestBuffer, &mContext); + return mDigestBuffer; +} + + + +DigestTransform::DigestTransform() : Transform(CFSTR("Digest Transform")) +{ + mDigest = NULL; +} + + +CFErrorRef DigestTransform::Setup(CFTypeRef dt, CFIndex length) +{ + if (dt == NULL) + { + dt = kSecDigestSHA2; + length = 512; + } + transforms_assume_zero(mDigest); + + CFStringRef digestType = (CFStringRef) dt; + + // figure out what kind of digest is being requested + if (CFStringCompare(digestType, kSecDigestMD2, 0) == kCFCompareEqualTo) + { + mDigest = new MD2Digest(); + } + else if (CFStringCompare(digestType, kSecDigestMD4, 0) == kCFCompareEqualTo) + { + mDigest = new MD4Digest(); + } + else if (CFStringCompare(digestType, kSecDigestMD5, 0) == kCFCompareEqualTo) + { + mDigest = new MD5Digest(); + } + else if (CFStringCompare(digestType, kSecDigestSHA1, 0) == kCFCompareEqualTo) + { + mDigest = new SHA1Digest(); + } + else if (CFStringCompare(digestType, kSecDigestSHA2, 0) == kCFCompareEqualTo) + { + switch (length) + { + case 224: + mDigest = new SHA224Digest(); + break; + + case 256: + mDigest = new SHA256Digest(); + break; + + case 384: + mDigest = new SHA384Digest(); + break; + + case 0: + case 512: + mDigest = new SHA512Digest(); + break; + + default: + { + CFErrorRef result = CreateSecTransformErrorRef(kSecTransformErrorInvalidLength, "%d is an invalid digest size (use 224, 256, 384, 512, or 0).", length); + return result; + } + } + } + else + { + if (CFStringCompare(digestType, kSecDigestHMACMD5, 0) == kCFCompareEqualTo) + { + mDigest = new Hmac(this, digestType, kCCHmacAlgMD5, CC_MD5_DIGEST_LENGTH); + } + else if (CFStringCompare(digestType, kSecDigestHMACSHA1, 0) == kCFCompareEqualTo) + { + mDigest = new Hmac(this, digestType, kCCHmacAlgSHA1, CC_SHA1_DIGEST_LENGTH); + } + else if (CFStringCompare(digestType, kSecDigestHMACSHA2, 0) == kCFCompareEqualTo) + { + switch (length) + { + case 224: + { + mDigest = new Hmac(this, digestType, kCCHmacAlgSHA224, CC_SHA224_DIGEST_LENGTH); + } + break; + + case 256: + { + mDigest = new Hmac(this, digestType, kCCHmacAlgSHA256, CC_SHA256_DIGEST_LENGTH); + } + break; + + case 384: + { + mDigest = new Hmac(this, digestType, kCCHmacAlgSHA384, CC_SHA384_DIGEST_LENGTH); + } + break; + + case 0: + case 512: + { + mDigest = new Hmac(this, digestType, kCCHmacAlgSHA512, CC_SHA512_DIGEST_LENGTH); + } + break; + + default: + { + CFErrorRef result = CreateSecTransformErrorRef(kSecTransformErrorInvalidLength, "%d is an invalid digest size (use 224, 256, 384, 512, or 0).", length); + return result; + } + } + } + else + { + CFErrorRef result = CreateSecTransformErrorRef(kSecTransformErrorInvalidAlgorithm, "%@ is not a supported digest algorithm (use kSecDigestSHA2, kSecDigestMD2, kSecDigestMD5, kSecDigestSHA or kSecDigestSHA2", digestType); + return result; + } + } + + long lengthInt = mDigest->DigestLength(); + CFNumberRef lengthNumber = CFNumberCreate(NULL, kCFNumberLongType, &lengthInt); + SendAttribute(kSecDigestLengthAttribute, lengthNumber); + CFRelease(lengthNumber); + return NULL; +} + + + +static CFStringRef gDigestTransformName = CFSTR("SecDigestTransform"); + +CFTypeRef DigestTransform::Make() +{ + DigestTransform* dt = new DigestTransform(); + return CoreFoundationHolder::MakeHolder(gDigestTransformName, dt); +} + + + +DigestTransform::~DigestTransform() +{ + if (mDigest != NULL) + { + delete mDigest; + } +} + + + +void DigestTransform::AttributeChanged(CFStringRef name, CFTypeRef value) +{ + if (CFStringCompare(name, kSecTransformInputAttributeName, 0) == kCFCompareEqualTo) + { + if (value == NULL) + { + // we are done, finalize the digest and send the result forward + const void* result = mDigest->Finalize(); + + // send the result + CFDataRef resultRef = CFDataCreate(NULL, (UInt8*) result, mDigest->DigestLength()); + SendAttribute(kSecTransformOutputAttributeName, resultRef); + CFRelease(resultRef); + + // send the EOS + SendAttribute(kSecTransformOutputAttributeName, NULL); + } + else + { + // if we got an error, just pass it on + CFTypeID valueType = CFGetTypeID(value); + if (valueType == CFErrorGetTypeID()) + { + SendAttribute(kSecTransformOutputAttributeName, value); + } + else if (valueType != CFDataGetTypeID()) + { + CFStringRef idType = CFCopyTypeIDDescription(valueType); + CFErrorRef result = CreateSecTransformErrorRef(kSecTransformErrorInvalidType, "value is not a CFDataRef -- it's a %@ instead", idType); + CFRelease(idType); + SetAttributeNoCallback(kSecTransformOutputAttributeName, result); + } + else + { + CFDataRef valueRef = (CFDataRef) value; + mDigest->Update(CFDataGetBytePtr(valueRef), CFDataGetLength(valueRef)); + } + } + } +} + + + +class DigestTransformFactory : public TransformFactory +{ +public: + DigestTransformFactory(); + virtual CFTypeRef Make(); +}; + + +DigestTransformFactory::DigestTransformFactory() : TransformFactory(gDigestTransformName, true) +{ +} + + + +CFTypeRef DigestTransformFactory::Make() +{ + return DigestTransform::Make(); +} + + + +TransformFactory* DigestTransform::MakeTransformFactory() +{ + return new DigestTransformFactory(); +} + + + +CFDictionaryRef DigestTransform::CopyState() +{ + return mDigest->CopyState(); +} + + + +void DigestTransform::RestoreState(CFDictionaryRef state) +{ + if (mDigest != NULL) + { + delete mDigest; + } + + // get the type and state from the dictionary + CFStringRef type = (CFStringRef) CFDictionaryGetValue(state, kSecDigestTypeAttribute); + CFNumberRef size = (CFNumberRef) CFDictionaryGetValue(state, kSecDigestLengthAttribute); + CFIndex realSize; + CFNumberGetValue(size, kCFNumberCFIndexType, &realSize); + + Setup(type, realSize); +} + + + +CFTypeID DigestTransform::GetCFTypeID() +{ + return CoreFoundationObject::FindObjectType(gDigestTransformName); +} + + + +Hmac::Hmac(Transform* parentTransform, CFStringRef digestType, CCHmacAlgorithm alg, size_t length) : + Digest(digestType, length), mInitialized(false), mParentTransform(parentTransform), mAlg(alg) +{ +} + + + +Hmac::~Hmac() +{ + free(mDigestBuffer); +} + + + +void Hmac::Initialize() +{ + // initialize + const UInt8* data = NULL; + size_t dataLength = 0; + + CFDataRef key = (CFDataRef) mParentTransform->GetAttribute(kSecDigestHMACKeyAttribute); + if (key) + { + data = CFDataGetBytePtr(key); + dataLength = CFDataGetLength(key); + } + + CCHmacInit(&mHMACContext, mAlg, data, dataLength); + + // make room to hold the result + mDigestBuffer = (UInt8*) malloc(mDigestLength); + + mInitialized = true; +} + + + +void Hmac::Update(const void* buffer, size_t length) +{ + if (!mInitialized) + { + Initialize(); + } + + CCHmacUpdate(&mHMACContext, buffer, length); +} + + + +size_t Hmac::DigestLength() +{ + return mDigestLength; +} + + + +const void* Hmac::Finalize() +{ + CCHmacFinal(&mHMACContext, mDigestBuffer); + return mDigestBuffer; +} +