]> git.saurik.com Git - apple/mdnsresponder.git/blobdiff - mDNSCore/DNSDigest.c
mDNSResponder-258.21.tar.gz
[apple/mdnsresponder.git] / mDNSCore / DNSDigest.c
index 370e70643667434e6d467a79c8556936ea4b1e2a..d3d0a5cdf7cfeef9776b518a5cc202e4cacb90c0 100644 (file)
@@ -1,59 +1,26 @@
-/*
+/* -*- Mode: C; tab-width: 4 -*-
+ *
  * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
  *
- * @APPLE_LICENSE_HEADER_START@
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  * 
- * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
+ *     http://www.apache.org/licenses/LICENSE-2.0
  * 
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
  * limitations under the License.
- * 
- * @APPLE_LICENSE_HEADER_END@
-
-    Change History (most recent first):
-
-$Log: DNSDigest.c,v $
-Revision 1.6  2004/06/02 00:17:46  ksekar
-Referenced original OpenSSL license headers in source file description.
-
-Revision 1.5  2004/05/20 18:37:37  cheshire
-Fix compiler warnings
-
-Revision 1.4  2004/04/22 20:28:20  cheshire
-Use existing facility of PutResourceRecordTTL() to update count field for us
-
-Revision 1.3  2004/04/22 03:05:28  cheshire
-kDNSClass_ANY should be kDNSQClass_ANY
-
-Revision 1.2  2004/04/15 00:51:28  bradley
-Minor tweaks for Windows and C++ builds. Added casts for signed/unsigned integers and 64-bit pointers.
-Prefix some functions with mDNS to avoid conflicts. Disable benign warnings on Microsoft compilers.
-
-Revision 1.1  2004/04/14 23:09:28  ksekar
-Support for TSIG signed dynamic updates.
-
-
-
-*/
+ */
 
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-#include "mDNSClientAPI.h"
+#include "mDNSEmbeddedAPI.h"
 #include "DNSCommon.h"
 
 // Disable certain benign warnings with Microsoft compilers
@@ -64,6 +31,22 @@ extern "C" {
        #pragma warning(disable:4127)
 #endif
 
+
+ // ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - Byte Swapping Functions
+#endif
+
+mDNSlocal mDNSu16 NToH16(mDNSu8 * bytes)
+       {
+       return (mDNSu16)((mDNSu16)bytes[0] << 8 | (mDNSu16)bytes[1]);
+       }
+
+mDNSlocal mDNSu32 NToH32(mDNSu8 * bytes)
+       {
+       return (mDNSu32)((mDNSu32) bytes[0] << 24 | (mDNSu32) bytes[1] << 16 | (mDNSu32) bytes[2] << 8 | (mDNSu32)bytes[3]);
+       }
+
  // ***************************************************************************
 #if COMPILER_LIKES_PRAGMA_MARK
 #pragma mark - MD5 Hash Functions
@@ -81,7 +64,7 @@ extern "C" {
  * Note: machine archetecure specific conditionals from the original sources are turned off, but are left in the code
  * to aid in platform-specific optimizations and debugging.
  * Sources originally distributed under the following license headers:
- * CommonDigest.c - APSL
+ * CommonDigest.h - APSL
  * 
  * md32_Common.h
  * ====================================================================
@@ -437,7 +420,7 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num);
 #   define ROTATE(a,n) (unsigned MD32_REG_T)__rlwinm((int)a,n,0,31)
 #  elif defined(__MC68K__)
     /* Motorola specific tweak. <appro@fy.chalmers.se> */
-#   define ROTATE(a,n) ( n<24 ? __rol(a,n) : __ror(a,32-n) )
+#   define ROTATE(a,n) (n<24 ? __rol(a,n) : __ror(a,32-n))
 #  else
 #   define ROTATE(a,n) __rol(a,n)
 #  endif
@@ -449,12 +432,17 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num);
    *
    *                                   <appro@fy.chalmers.se>
    */
+  /*
+   * LLVM is more strict about compatibility of types between input & output constraints,
+   * but we want these to be rotations of 32 bits, not 64, so we explicitly drop the
+   * most significant bytes by casting to an unsigned int.
+   */
 #  if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__)
 #   define ROTATE(a,n) ({ register unsigned int ret;   \
                                asm (                   \
                                "roll %1,%0"            \
                                : "=r"(ret)             \
-                               : "I"(n), "0"(a)        \
+                               : "I"(n), "0"((unsigned int)a)  \
                                : "cc");                \
                           ret;                         \
                        })
@@ -661,7 +649,7 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num);
  * Time for some action:-)
  */
 
-int HASH_UPDATE (HASH_CTX *c, const void *data_, mDNSu32 len)
+int HASH_UPDATE (HASH_CTX *c, const void *data_, unsigned long len)
        {
        const unsigned char *data=(const unsigned char *)data_;
        register HASH_LONG * p;
@@ -744,7 +732,7 @@ int HASH_UPDATE (HASH_CTX *c, const void *data_, mDNSu32 len)
 #if !defined(HASH_BLOCK_DATA_ORDER)
                        while (sw--)
                                {
-                               memcpy (p=c->data,data,HASH_CBLOCK);
+                               mDNSPlatformMemCopy(p=c->data,data,HASH_CBLOCK);
                                HASH_BLOCK_DATA_ORDER_ALIGNED(c,p,1);
                                data+=HASH_CBLOCK;
                                len-=HASH_CBLOCK;
@@ -787,7 +775,7 @@ void HASH_TRANSFORM (HASH_CTX *c, const unsigned char *data)
        else
 #if !defined(HASH_BLOCK_DATA_ORDER)
                {
-               memcpy (c->data,data,HASH_CBLOCK);
+               mDNSPlatformMemCopy(c->data,data,HASH_CBLOCK);
                HASH_BLOCK_DATA_ORDER_ALIGNED (c,c->data,1);
                }
 #endif
@@ -1141,20 +1129,18 @@ void md5_block_data_order (MD5_CTX *c, const void *data_, int num)
 #endif
 
 
-
  // ***************************************************************************
 #if COMPILER_LIKES_PRAGMA_MARK
 #pragma mark - base64 -> binary conversion
 #endif
 
-static const char Base64[] =
-       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 static const char Pad64 = '=';
 
 
 #define mDNSisspace(x) (x == '\t' || x == '\n' || x == '\v' || x == '\f' || x == '\r' || x == ' ')
 
-static const char *mDNSstrchr(const char *s, int c)
+mDNSlocal const char *mDNSstrchr(const char *s, int c)
        {
        while (1)
                {
@@ -1170,7 +1156,7 @@ static const char *mDNSstrchr(const char *s, int c)
 // it returns the number of data bytes stored at the target, or -1 on error.
 // adapted from BIND sources
 
-mDNSexport mDNSs32 DNSDigest_Base64ToBin(const char *src, mDNSu8 *target, mDNSu32 targsize)
+mDNSlocal mDNSs32 DNSDigest_Base64ToBin(const char *src, mDNSu8 *target, mDNSu32 targsize)
        {
        int tarindex, state, ch;
        const char *pos;
@@ -1297,13 +1283,10 @@ mDNSexport mDNSs32 DNSDigest_Base64ToBin(const char *src, mDNSu8 *target, mDNSu3
 #define HMAC_OPAD   0x5c
 #define MD5_LEN     16
 
-static domainname HMAC_MD5_AlgName = { { '\010', 'h', 'm', 'a', 'c', '-', 'm', 'd', '5',
-                                                                                '\007', 's', 'i', 'g', '-', 'a', 'l', 'g',
-                                                                                '\003', 'r', 'e', 'g',
-                                                                                '\003', 'i', 'n', 't',
-                                                                                '\0' } };
+#define HMAC_MD5_AlgName (*(const domainname*) "\010" "hmac-md5" "\007" "sig-alg" "\003" "reg" "\003" "int")
+
 // Adapted from Appendix, RFC 2104
-mDNSexport void DNSDigest_ConstructHMACKey(uDNS_AuthInfo *info, mDNSu8 *key, mDNSu32 len)              
+mDNSlocal void DNSDigest_ConstructHMACKey(DomainAuthInfo *info, const mDNSu8 *key, mDNSu32 len)                
        {
        MD5_CTX k;
        mDNSu8 buf[MD5_LEN];
@@ -1320,24 +1303,33 @@ mDNSexport void DNSDigest_ConstructHMACKey(uDNS_AuthInfo *info, mDNSu8 *key, mDN
                }
 
        // store key in pads
-       mDNSPlatformMemZero(info->key.ipad, HMAC_LEN);
-       mDNSPlatformMemZero(info->key.opad, HMAC_LEN);
-       mDNSPlatformMemCopy(key, info->key.ipad, len);
-       mDNSPlatformMemCopy(key, info->key.opad, len);
+       mDNSPlatformMemZero(info->keydata_ipad, HMAC_LEN);
+       mDNSPlatformMemZero(info->keydata_opad, HMAC_LEN);
+       mDNSPlatformMemCopy(info->keydata_ipad, key, len);
+       mDNSPlatformMemCopy(info->keydata_opad, key, len);
 
        // XOR key with ipad and opad values
        for (i = 0; i < HMAC_LEN; i++)
                {
-               info->key.ipad[i] ^= HMAC_IPAD;
-               info->key.opad[i] ^= HMAC_OPAD;
+               info->keydata_ipad[i] ^= HMAC_IPAD;
+               info->keydata_opad[i] ^= HMAC_OPAD;
                }
 
        }
 
-mDNSexport mDNSu8 *DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, mDNSu16 *numAdditionals, uDNS_AuthInfo *info)
+mDNSexport mDNSs32 DNSDigest_ConstructHMACKeyfromBase64(DomainAuthInfo *info, const char *b64key)
+       {
+       mDNSu8 keybuf[1024];
+       mDNSs32 keylen = DNSDigest_Base64ToBin(b64key, keybuf, sizeof(keybuf));
+       if (keylen < 0) return(keylen);
+       DNSDigest_ConstructHMACKey(info, keybuf, (mDNSu32)keylen);
+       return(keylen);
+       }
+
+mDNSexport void DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, DomainAuthInfo *info, mDNSu16 tcode)
        {
        AuthRecord tsig;
-       mDNSu8 *countPtr, *rdata;
+       mDNSu8  *rdata, *const countPtr = (mDNSu8 *)&msg->h.numAdditionals;     // Get existing numAdditionals value
        mDNSu32 utc32;
        mDNSu8 utc48[6];
        mDNSu8 digest[MD5_LEN];
@@ -1345,20 +1337,19 @@ mDNSexport mDNSu8 *DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, mDNSu16
        mDNSu32 len;
        mDNSOpaque16 buf;
        MD5_CTX c;
+       mDNSu16 numAdditionals = (mDNSu16)((mDNSu16)countPtr[0] << 8 | countPtr[1]);
        
        // Init MD5 context, digest inner key pad and message
     MD5_Init(&c);
-    MD5_Update(&c, info->key.ipad, HMAC_LEN);
+    MD5_Update(&c, info->keydata_ipad, HMAC_LEN);
        MD5_Update(&c, (mDNSu8 *)msg, (unsigned long)(*end - (mDNSu8 *)msg));
           
        // Construct TSIG RR, digesting variables as apporpriate
-       mDNSPlatformMemZero(&tsig, sizeof(AuthRecord)); 
        mDNS_SetupResourceRecord(&tsig, mDNSNULL, 0, kDNSType_TSIG, 0, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
-       rdata = tsig.resrec.rdata->u.data;
 
        // key name
-       mDNSPlatformStrCopy(info->keyname.c, tsig.resrec.name.c);
-       MD5_Update(&c, info->keyname.c, mDNSPlatformStrLen(info->keyname.c)+1);
+       AssignDomainName(&tsig.namestorage, &info->keyname);
+       MD5_Update(&c, info->keyname.c, DomainNameLength(&info->keyname));
 
        // class
        tsig.resrec.rrclass = kDNSQClass_ANY;
@@ -1370,35 +1361,37 @@ mDNSexport mDNSu8 *DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, mDNSu16
        MD5_Update(&c, (mDNSu8 *)&tsig.resrec.rroriginalttl, sizeof(tsig.resrec.rroriginalttl));
        
        // alg name
-       mDNSPlatformStrCopy(HMAC_MD5_AlgName.c, rdata);     
-       len = mDNSPlatformStrLen(HMAC_MD5_AlgName.c) + 1;
-       rdata += len;
+       AssignDomainName(&tsig.resrec.rdata->u.name, &HMAC_MD5_AlgName);
+       len = DomainNameLength(&HMAC_MD5_AlgName);
+       rdata = tsig.resrec.rdata->u.data + len;
        MD5_Update(&c, HMAC_MD5_AlgName.c, len);
 
        // time
        // get UTC (universal time), convert to 48-bit unsigned in network byte order
        utc32 = (mDNSu32)mDNSPlatformUTC();
-       if (utc32 == (unsigned)-1) { LogMsg("ERROR: DNSDigest_SignMessage - mDNSPlatformUTC returned bad time -1"); return mDNSNULL; }
+       if (utc32 == (unsigned)-1) { LogMsg("ERROR: DNSDigest_SignMessage - mDNSPlatformUTC returned bad time -1"); *end = mDNSNULL; }
        utc48[0] = 0;
        utc48[1] = 0;
        utc48[2] = (mDNSu8)((utc32 >> 24) & 0xff);
        utc48[3] = (mDNSu8)((utc32 >> 16) & 0xff);
-       utc48[4] = (mDNSu8)((utc32 >> 8)  & 0xff);
+       utc48[4] = (mDNSu8)((utc32 >>  8) & 0xff);
        utc48[5] = (mDNSu8)( utc32        & 0xff);
 
-       mDNSPlatformMemCopy(utc48, rdata, 6);
+       mDNSPlatformMemCopy(rdata, utc48, 6);
        rdata += 6;                     
        MD5_Update(&c, utc48, 6);
 
-       // fudge
-       buf = mDNSOpaque16fromIntVal(300);     // 300 sec is fudge recommended in RFC 2485
-       ((mDNSOpaque16 *)rdata)->NotAnInteger = buf.NotAnInteger;
+       // 300 sec is fudge recommended in RFC 2485
+       rdata[0] = (mDNSu8)((300 >> 8)  & 0xff);
+       rdata[1] = (mDNSu8)( 300        & 0xff);
+       MD5_Update(&c, rdata, sizeof(mDNSOpaque16));
        rdata += sizeof(mDNSOpaque16);
-       MD5_Update(&c, buf.b, sizeof(mDNSOpaque16));
 
-       // digest error and other data len (both zero) - we'll add them to the rdata later
-       buf.NotAnInteger = 0;
+       // digest error (tcode) and other data len (zero) - we'll add them to the rdata later
+       buf.b[0] = (mDNSu8)((tcode >> 8) & 0xff);
+       buf.b[1] = (mDNSu8)( tcode       & 0xff);
        MD5_Update(&c, buf.b, sizeof(mDNSOpaque16));  // error
+       buf.NotAnInteger = 0;
        MD5_Update(&c, buf.b, sizeof(mDNSOpaque16));  // other data len
 
        // finish the message & tsig var hash
@@ -1406,34 +1399,178 @@ mDNSexport mDNSu8 *DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, mDNSu16
        
        // perform outer MD5 (outer key pad, inner digest)
        MD5_Init(&c);
-       MD5_Update(&c, info->key.opad, HMAC_LEN);
+       MD5_Update(&c, info->keydata_opad, HMAC_LEN);
        MD5_Update(&c, digest, MD5_LEN);
        MD5_Final(digest, &c);
 
        // set remaining rdata fields
-       *(mDNSOpaque16 *)rdata = mDNSOpaque16fromIntVal(MD5_LEN);             // MAC size       
+       rdata[0] = (mDNSu8)((MD5_LEN >> 8)  & 0xff);
+       rdata[1] = (mDNSu8)( MD5_LEN        & 0xff);
        rdata += sizeof(mDNSOpaque16);
-       mDNSPlatformMemCopy(digest, rdata, MD5_LEN);                          // MAC
+       mDNSPlatformMemCopy(rdata, digest, MD5_LEN);                          // MAC
        rdata += MD5_LEN;
-       ((mDNSOpaque16 *)rdata)->NotAnInteger = msg->h.id.NotAnInteger;       // original ID
-       rdata += sizeof(mDNSOpaque16);
-       ((mDNSOpaque16 *)rdata)->NotAnInteger = 0;                            // no error
-       rdata += sizeof(mDNSOpaque16);
-       ((mDNSOpaque16 *)rdata)->NotAnInteger = 0;                            // other data len
-       rdata += sizeof(mDNSOpaque16);
+       rdata[0] = msg->h.id.b[0];                                            // original ID
+       rdata[1] = msg->h.id.b[1];
+       rdata[2] = (mDNSu8)((tcode >> 8) & 0xff);
+       rdata[3] = (mDNSu8)( tcode       & 0xff);
+       rdata[4] = 0;                                                         // other data len
+       rdata[5] = 0;
+       rdata += 6;
        
        tsig.resrec.rdlength = (mDNSu16)(rdata - tsig.resrec.rdata->u.data);
-       *end = PutResourceRecordTTL(msg, ptr, numAdditionals, &tsig.resrec, 0);
-       if (!*end) { LogMsg("ERROR: DNSDigest_SignMessage - could not put TSIG"); return mDNSNULL; }
-
-       // update num additionals
-       countPtr = (mDNSu8 *)&msg->h.numAdditionals;  // increment (network-byte ordered) header value
-       *countPtr++ = (mDNSu8)(*numAdditionals >> 8);
-       *countPtr++ = (mDNSu8)(*numAdditionals &  0xFF);
+       *end = PutResourceRecordTTLJumbo(msg, ptr, &numAdditionals, &tsig.resrec, 0);
+       if (!*end) { LogMsg("ERROR: DNSDigest_SignMessage - could not put TSIG"); *end = mDNSNULL; return; }
 
-       return *end;
+       // Write back updated numAdditionals value
+       countPtr[0] = (mDNSu8)(numAdditionals >> 8);
+       countPtr[1] = (mDNSu8)(numAdditionals &  0xFF);
        }
+
+mDNSexport mDNSBool DNSDigest_VerifyMessage(DNSMessage *msg, mDNSu8 *end, LargeCacheRecord * lcr, DomainAuthInfo *info, mDNSu16 * rcode, mDNSu16 * tcode)
+       {
+       mDNSu8                  *       ptr = (mDNSu8*) &lcr->r.resrec.rdata->u.data;
+       mDNSs32                         now;
+       mDNSs32                         then;
+       mDNSu8                          thisDigest[MD5_LEN];
+       mDNSu8                          thatDigest[MD5_LEN];
+       mDNSu32                         macsize;
+       mDNSOpaque16            buf;
+       mDNSu8                          utc48[6];
+       mDNSs32                         delta;
+       mDNSu16                         fudge;
+       domainname              *       algo;
+       MD5_CTX                         c;
+       mDNSBool                        ok = mDNSfalse;
+
+       // We only support HMAC-MD5 for now
+
+       algo = (domainname*) ptr;
+
+       if (!SameDomainName(algo, &HMAC_MD5_AlgName))
+               {
+               LogMsg("ERROR: DNSDigest_VerifyMessage - TSIG algorithm not supported: %##s", algo->c);
+               *rcode = kDNSFlag1_RC_NotAuth;
+               *tcode = TSIG_ErrBadKey;
+               ok = mDNSfalse;
+               goto exit;
+               }
+
+       ptr += DomainNameLength(algo);
+
+       // Check the times
+
+       now = mDNSPlatformUTC();
+       if (now == -1)
+               {
+               LogMsg("ERROR: DNSDigest_VerifyMessage - mDNSPlatformUTC returned bad time -1");
+               *rcode = kDNSFlag1_RC_NotAuth;
+               *tcode = TSIG_ErrBadTime;
+               ok = mDNSfalse;
+               goto exit;
+               }
+
+       // Get the 48 bit time field, skipping over the first word
+
+       utc48[0] = *ptr++;
+       utc48[1] = *ptr++;
+       utc48[2] = *ptr++;
+       utc48[3] = *ptr++;
+       utc48[4] = *ptr++;
+       utc48[5] = *ptr++;
+
+       then  = (mDNSs32)NToH32(utc48 + sizeof(mDNSu16));
+
+       fudge = NToH16(ptr);
+
+       ptr += sizeof(mDNSu16);
+
+       delta = (now > then) ? now - then : then - now;
+
+       if (delta > fudge)
+               {
+               LogMsg("ERROR: DNSDigest_VerifyMessage - time skew > %d", fudge);
+               *rcode = kDNSFlag1_RC_NotAuth;
+               *tcode = TSIG_ErrBadTime;
+               ok = mDNSfalse;
+               goto exit;
+               }
+
+       // MAC size
+
+       macsize = (mDNSu32) NToH16(ptr);
+       
+       ptr += sizeof(mDNSu16);
+
+       // MAC
+
+       mDNSPlatformMemCopy(thatDigest, ptr, MD5_LEN);
+
+       // Init MD5 context, digest inner key pad and message
+
+       MD5_Init(&c);
+       MD5_Update(&c, info->keydata_ipad, HMAC_LEN);
+       MD5_Update(&c, (mDNSu8*) msg, (unsigned long)(end - (mDNSu8*) msg));
+          
+       // Key name
+
+       MD5_Update(&c, lcr->r.resrec.name->c, DomainNameLength(lcr->r.resrec.name));
+
+       // Class name
+
+       buf = mDNSOpaque16fromIntVal(lcr->r.resrec.rrclass);
+       MD5_Update(&c, buf.b, sizeof(mDNSOpaque16));
+
+       // TTL
+
+       MD5_Update(&c, (mDNSu8*) &lcr->r.resrec.rroriginalttl, sizeof(lcr->r.resrec.rroriginalttl));
+       
+       // Algorithm
+       MD5_Update(&c, algo->c, DomainNameLength(algo));
+
+       // Time
+
+       MD5_Update(&c, utc48, 6);
+
+       // Fudge
+
+       buf = mDNSOpaque16fromIntVal(fudge);
+       MD5_Update(&c, buf.b, sizeof(mDNSOpaque16));
+
+       // Digest error and other data len (both zero) - we'll add them to the rdata later
+
+       buf.NotAnInteger = 0;
+       MD5_Update(&c, buf.b, sizeof(mDNSOpaque16));  // error
+       MD5_Update(&c, buf.b, sizeof(mDNSOpaque16));  // other data len
+
+       // Finish the message & tsig var hash
+
+    MD5_Final(thisDigest, &c);
        
+       // perform outer MD5 (outer key pad, inner digest)
+
+       MD5_Init(&c);
+       MD5_Update(&c, info->keydata_opad, HMAC_LEN);
+       MD5_Update(&c, thisDigest, MD5_LEN);
+       MD5_Final(thisDigest, &c);
+
+       if (!mDNSPlatformMemSame(thisDigest, thatDigest, MD5_LEN))
+               {
+               LogMsg("ERROR: DNSDigest_VerifyMessage - bad signature");
+               *rcode = kDNSFlag1_RC_NotAuth;
+               *tcode = TSIG_ErrBadSig;
+               ok = mDNSfalse;
+               goto exit;
+               }
+
+       // set remaining rdata fields
+       ok = mDNStrue;
+
+exit:
+
+       return ok;
+       }
+
 
 #ifdef __cplusplus
 }