#include "mDNSEmbeddedAPI.h"
#include <CommonCrypto/CommonDigest.h> // For Hash algorithms SHA1 etc.
#include <dispatch/dispatch.h> // For Base32/Base64 encoding/decoding
#include <dispatch/private.h> // dispatch_data_create_with_transform
#include "CryptoAlg.h"
#include "CryptoSupport.h"
#include "dnssec.h"
#if TARGET_OS_IPHONE
#include "SecRSAKey.h" // For RSA_SHA1 etc. verification
#endif
typedef struct
{
#if DISPATCH_API_VERSION >= 20111008
dispatch_data_t encData;
dispatch_data_t encMap;
dispatch_data_t encNULL;
#endif
}encContext;
mDNSlocal mStatus enc_create(AlgContext *ctx)
{
encContext *ptr;
switch (ctx->alg)
{
case ENC_BASE32:
case ENC_BASE64:
ptr = (encContext *)mDNSPlatformMemAllocate(sizeof(encContext));
if (!ptr) return mStatus_NoMemoryErr;
break;
default:
LogMsg("enc_create: Unsupported algorithm %d", ctx->alg);
return mStatus_BadParamErr;
}
#if DISPATCH_API_VERSION >= 20111008
ptr->encData = NULL;
ptr->encMap = NULL;
ptr->encNULL = dispatch_data_create("", 1, dispatch_get_global_queue(0, 0), ^{});
if (!ptr->encNULL)
{
mDNSPlatformMemFree(ptr);
return mStatus_NoMemoryErr;
}
#endif
ctx->context = ptr;
return mStatus_NoError;
}
mDNSlocal mStatus enc_destroy(AlgContext *ctx)
{
encContext *ptr = (encContext *)ctx->context;
#if DISPATCH_API_VERSION >= 20111008
if (ptr->encData) dispatch_release(ptr->encData);
if (ptr->encMap) dispatch_release(ptr->encMap);
if (ptr->encNULL) dispatch_release(ptr->encNULL);
#endif
mDNSPlatformMemFree(ptr);
return mStatus_NoError;
}
mDNSlocal mStatus enc_add(AlgContext *ctx, void *data, mDNSu32 len)
{
switch (ctx->alg)
{
case ENC_BASE32:
case ENC_BASE64:
{
#if DISPATCH_API_VERSION >= 20111008
encContext *ptr = (encContext *)ctx->context;
dispatch_data_t src_data = dispatch_data_create(data, len, dispatch_get_global_queue(0, 0), ^{});
if (!src_data)
{
LogMsg("enc_add: dispatch_data_create src failed");
return mStatus_BadParamErr;
}
dispatch_data_t dest_data = dispatch_data_create_with_transform(src_data, DISPATCH_DATA_FORMAT_TYPE_NONE,
(ctx->alg == ENC_BASE32 ? DISPATCH_DATA_FORMAT_TYPE_BASE32 : DISPATCH_DATA_FORMAT_TYPE_BASE64));
dispatch_release(src_data);
if (!dest_data)
{
LogMsg("enc_add: dispatch_data_create dst failed");
return mStatus_BadParamErr;
}
ptr->encData = dest_data;
#else
(void)data;
(void)len;
#endif
return mStatus_NoError;
}
default:
LogMsg("enc_add: Unsupported algorithm %d", ctx->alg);
return mStatus_BadParamErr;
}
}
mDNSlocal mDNSu8* enc_encode(AlgContext *ctx)
{
const void *result = NULL;
switch (ctx->alg)
{
case ENC_BASE32:
case ENC_BASE64:
{
#if DISPATCH_API_VERSION >= 20111008
encContext *ptr = (encContext *)ctx->context;
size_t size;
dispatch_data_t dest_data = ptr->encData;
dispatch_data_t data = dispatch_data_create_concat(dest_data, ptr->encNULL);
if (!data)
{
LogMsg("enc_encode: cannot concatenate");
return NULL;
}
dispatch_data_t map = dispatch_data_create_map(data, &result, &size);
if (!map)
{
LogMsg("enc_encode: cannot create map %d", ctx->alg);
return NULL;
}
dispatch_release(dest_data);
ptr->encData = data;
ptr->encMap = map;
#endif
return (mDNSu8 *)result;
}
default:
LogMsg("enc_encode: Unsupported algorithm %d", ctx->alg);
return mDNSNULL;
}
}
mDNSlocal mStatus sha_create(AlgContext *ctx)
{
mDNSu8 *ptr;
switch (ctx->alg)
{
case SHA1_DIGEST_TYPE:
ptr = mDNSPlatformMemAllocate(sizeof(CC_SHA1_CTX));
if (!ptr) return mStatus_NoMemoryErr;
CC_SHA1_Init((CC_SHA1_CTX *)ptr);
break;
case SHA256_DIGEST_TYPE:
ptr = mDNSPlatformMemAllocate(sizeof(CC_SHA256_CTX));
if (!ptr) return mStatus_NoMemoryErr;
CC_SHA256_Init((CC_SHA256_CTX *)ptr);
break;
default:
LogMsg("sha_create: Unsupported algorithm %d", ctx->alg);
return mStatus_BadParamErr;
}
ctx->context = ptr;
return mStatus_NoError;
}
mDNSlocal mStatus sha_destroy(AlgContext *ctx)
{
mDNSPlatformMemFree(ctx->context);
return mStatus_NoError;
}
mDNSlocal mDNSu32 sha_len(AlgContext *ctx)
{
switch (ctx->alg)
{
case SHA1_DIGEST_TYPE:
return CC_SHA1_DIGEST_LENGTH;
case SHA256_DIGEST_TYPE:
return CC_SHA256_DIGEST_LENGTH;
default:
LogMsg("sha_len: Unsupported algorithm %d", ctx->alg);
return mStatus_BadParamErr;
}
}
mDNSlocal mStatus sha_add(AlgContext *ctx, void *data, mDNSu32 len)
{
switch (ctx->alg)
{
case SHA1_DIGEST_TYPE:
CC_SHA1_Update((CC_SHA1_CTX *)ctx->context, data, len);
break;
case SHA256_DIGEST_TYPE:
CC_SHA256_Update((CC_SHA256_CTX *)ctx->context, data, len);
break;
default:
LogMsg("sha_add: Unsupported algorithm %d", ctx->alg);
return mStatus_BadParamErr;
}
return mStatus_NoError;
}
mDNSlocal mStatus sha_verify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *digestIn, mDNSu32 dlen)
{
mDNSu8 digest[CC_SHA512_DIGEST_LENGTH];
mDNSu32 digestLen;
(void) key; (void)keylen; switch (ctx->alg)
{
case SHA1_DIGEST_TYPE:
digestLen = CC_SHA1_DIGEST_LENGTH;
CC_SHA1_Final(digest, (CC_SHA1_CTX *)ctx->context);
break;
case SHA256_DIGEST_TYPE:
digestLen = CC_SHA256_DIGEST_LENGTH;
CC_SHA256_Final(digest, (CC_SHA256_CTX *)ctx->context);
break;
default:
LogMsg("sha_verify: Unsupported algorithm %d", ctx->alg);
return mStatus_BadParamErr;
}
if (dlen != digestLen)
{
LogMsg("sha_verify(Alg %d): digest len mismatch len %u, expected %u", ctx->alg, (unsigned int)dlen, (unsigned int)digestLen);
return mStatus_BadParamErr;
}
if (!memcmp(digest, digestIn, digestLen))
return mStatus_NoError;
else
return mStatus_NoAuth;
}
mDNSlocal mStatus rsa_sha_create(AlgContext *ctx)
{
mDNSu8 *ptr;
switch (ctx->alg)
{
case CRYPTO_RSA_NSEC3_SHA1:
case CRYPTO_RSA_SHA1:
ptr = mDNSPlatformMemAllocate(sizeof(CC_SHA1_CTX));
if (!ptr) return mStatus_NoMemoryErr;
CC_SHA1_Init((CC_SHA1_CTX *)ptr);
break;
case CRYPTO_RSA_SHA256:
ptr = mDNSPlatformMemAllocate(sizeof(CC_SHA256_CTX));
if (!ptr) return mStatus_NoMemoryErr;
CC_SHA256_Init((CC_SHA256_CTX *)ptr);
break;
case CRYPTO_RSA_SHA512:
ptr = mDNSPlatformMemAllocate(sizeof(CC_SHA512_CTX));
if (!ptr) return mStatus_NoMemoryErr;
CC_SHA512_Init((CC_SHA512_CTX *)ptr);
break;
default:
LogMsg("rsa_sha_create: Unsupported algorithm %d", ctx->alg);
return mStatus_BadParamErr;
}
ctx->context = ptr;
return mStatus_NoError;
}
mDNSlocal mStatus rsa_sha_destroy(AlgContext *ctx)
{
mDNSPlatformMemFree(ctx->context);
return mStatus_NoError;
}
mDNSlocal mDNSu32 rsa_sha_len(AlgContext *ctx)
{
switch (ctx->alg)
{
case CRYPTO_RSA_NSEC3_SHA1:
case CRYPTO_RSA_SHA1:
return CC_SHA1_DIGEST_LENGTH;
case CRYPTO_RSA_SHA256:
return CC_SHA256_DIGEST_LENGTH;
case CRYPTO_RSA_SHA512:
return CC_SHA512_DIGEST_LENGTH;
default:
LogMsg("rsa_sha_len: Unsupported algorithm %d", ctx->alg);
return mStatus_BadParamErr;
}
}
mDNSlocal mStatus rsa_sha_add(AlgContext *ctx, void *data, mDNSu32 len)
{
switch (ctx->alg)
{
case CRYPTO_RSA_NSEC3_SHA1:
case CRYPTO_RSA_SHA1:
CC_SHA1_Update((CC_SHA1_CTX *)ctx->context, data, len);
break;
case CRYPTO_RSA_SHA256:
CC_SHA256_Update((CC_SHA256_CTX *)ctx->context, data, len);
break;
case CRYPTO_RSA_SHA512:
CC_SHA512_Update((CC_SHA512_CTX *)ctx->context, data, len);
break;
default:
LogMsg("rsa_sha_add: Unsupported algorithm %d", ctx->alg);
return mStatus_BadParamErr;
}
return mStatus_NoError;
}
#if TARGET_OS_IPHONE
mDNSlocal SecKeyRef rfc3110_import(const mDNSu8 *data, const mDNSu32 len)
{
static const int max_key_bytes = 4096 / 8; static const int max_exp_bytes = 3; static const int asn1_cmd_bytes = 3; static const int asn1_max_len_bytes = 3 * 3; unsigned char asn1[max_key_bytes + 1 + max_exp_bytes + asn1_cmd_bytes + asn1_max_len_bytes]; const mDNSu8 *modulus;
unsigned int modulus_length;
unsigned int exp_length;
mDNSu32 index = 0;
mDNSu32 asn1_length = 0;
unsigned int i;
if (!data)
return NULL;
if (len < 1)
return NULL;
exp_length = data[0];
if (len < 1+exp_length)
return NULL;
modulus_length = len - 1 - exp_length;
if (modulus_length > 512)
return NULL;
if (modulus_length < 1)
return NULL;
++modulus_length;
modulus = &data[1+exp_length];
asn1_length = modulus_length + exp_length + 2 + 2;
if (modulus_length > 0xFF)
asn1_length += 2;
else if (modulus_length >= 128)
++asn1_length;
asn1[index++] = 0x30;
if (asn1_length < 128)
{
asn1[index++] = asn1_length & 0xFF;
}
else
{
asn1[index++] = (0x80 | ((asn1_length & 0xFF00) ? 2 : 1));
if (asn1_length & 0xFF00)
asn1[index++] = (asn1_length & 0xFF00) >> 8;
asn1[index++] = asn1_length & 0xFF;
}
asn1[index++] = 0x02;
if (modulus_length < 128)
{
asn1[index++] = asn1_length & 0xFF;
}
else
{
asn1[index++] = 0x80 | ((modulus_length & 0xFF00) ? 2 : 1);
if (modulus_length & 0xFF00)
asn1[index++] = (modulus_length & 0xFF00) >> 8;
asn1[index++] = modulus_length & 0xFF;
}
asn1[index++] = 0x00;
memcpy(&asn1[index], (void *)modulus, modulus_length-1);
index += modulus_length-1;
asn1[index++] = 0x02;
asn1[index++] = exp_length & 0xFF;
for (i = 1; i <= exp_length; i++)
asn1[index++] = data[i];
return SecKeyCreateRSAPublicKey(NULL, asn1, index, kSecKeyEncodingPkcs1);
}
mDNSlocal mStatus rsa_sha_verify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen)
{
SecKeyRef keyref;
OSStatus result;
mDNSu8 digest[CC_SHA512_DIGEST_LENGTH];
int digestlen;
switch (ctx->alg)
{
case CRYPTO_RSA_NSEC3_SHA1:
case CRYPTO_RSA_SHA1:
digestlen = CC_SHA1_DIGEST_LENGTH;
CC_SHA1_Final(digest, (CC_SHA1_CTX *)ctx->context);
break;
case CRYPTO_RSA_SHA256:
digestlen = CC_SHA256_DIGEST_LENGTH;
CC_SHA256_Final(digest, (CC_SHA256_CTX *)ctx->context);
break;
case CRYPTO_RSA_SHA512:
digestlen = CC_SHA512_DIGEST_LENGTH;
CC_SHA512_Final(digest, (CC_SHA512_CTX *)ctx->context);
break;
default:
LogMsg("rsa_sha_verify: Unsupported algorithm %d", ctx->alg);
return mStatus_BadParamErr;
}
keyref = rfc3110_import(key, keylen);
if (!key)
{
LogMsg("rsa_sha_verify: Error decoding rfc3110 key data");
return mStatus_NoMemoryErr;
}
result = SecKeyRawVerify(keyref, kSecPaddingPKCS1SHA1, digest, digestlen, signature, siglen);
LogMsg("rsa_sha_verify: result: %s (%ld)", result == noErr ? "PASS" : "FAIL", result);
if (result == noErr)
return mStatus_NoError;
else
return mStatus_BadParamErr;
}
#else
mDNSlocal mStatus rsa_sha_verify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen)
{
(void)ctx;
(void)key;
(void)keylen;
(void)signature;
(void)siglen;
return mStatus_NoError;
}
#endif
AlgFuncs sha_funcs = {sha_create, sha_destroy, sha_len, sha_add, sha_verify, mDNSNULL};
AlgFuncs rsa_sha_funcs = {rsa_sha_create, rsa_sha_destroy, rsa_sha_len, rsa_sha_add, rsa_sha_verify, mDNSNULL};
AlgFuncs enc_funcs = {enc_create, enc_destroy, mDNSNULL, enc_add, mDNSNULL, enc_encode};
mDNSexport mStatus DNSSECCryptoInit(mDNS *const m)
{
mStatus result;
result = DigestAlgInit(SHA1_DIGEST_TYPE, &sha_funcs);
if (result != mStatus_NoError)
return result;
result = DigestAlgInit(SHA256_DIGEST_TYPE, &sha_funcs);
if (result != mStatus_NoError)
return result;
result = CryptoAlgInit(CRYPTO_RSA_SHA1, &rsa_sha_funcs);
if (result != mStatus_NoError)
return result;
result = CryptoAlgInit(CRYPTO_RSA_NSEC3_SHA1, &rsa_sha_funcs);
if (result != mStatus_NoError)
return result;
result = CryptoAlgInit(CRYPTO_RSA_SHA256, &rsa_sha_funcs);
if (result != mStatus_NoError)
return result;
result = CryptoAlgInit(CRYPTO_RSA_SHA512, &rsa_sha_funcs);
if (result != mStatus_NoError)
return result;
result = EncAlgInit(ENC_BASE32, &enc_funcs);
if (result != mStatus_NoError)
return result;
result = EncAlgInit(ENC_BASE64, &enc_funcs);
if (result != mStatus_NoError)
return result;
m->TrustAnchors = mDNSNULL;
m->notifyToken = 0;
return mStatus_NoError;
}