Darwin-Streaming-Server/CommonUtilitiesLib/md5digest.cpp

395 lines
13 KiB
C++
Raw Normal View History

/*
*
* @APPLE_LICENSE_HEADER_START@
*
* Copyright (c) 1999-2008 Apple Inc. All Rights Reserved.
*
* 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
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*
*/
/*
File: md5digest.cpp
Contains: Implements the function declared in md5digest.h
*/
#include "md5.h"
#include "md5digest.h"
#include "StrPtrLen.h"
#include <string.h>
#include "OSMemory.h"
static StrPtrLen sColon(":", 1);
static StrPtrLen sMD5Sess("md5-sess", 8);
static StrPtrLen sQopAuth("auth", 4);
static StrPtrLen sQopAuthInt("auth-int", 8);
// allocates memory for hashStr->Ptr
void HashToString(unsigned char aHash[kHashLen], StrPtrLen* hashStr){
UInt16 i;
UInt8 hexDigit;
// Allocating memory
char* str = NEW char[kHashHexLen+1];
str[kHashHexLen] = 0;
for(i = 0; i < kHashLen; i++) {
hexDigit = (aHash[i] >> 4) & 0xF;
str[i*2] = (hexDigit <= 9) ? (hexDigit + '0') : (hexDigit + 'a' - 10);
hexDigit = aHash[i] & 0xF;
str[i*2 + 1] = (hexDigit <= 9) ? (hexDigit + '0') : (hexDigit + 'a' - 10);
}
hashStr->Ptr = str;
hashStr->Len = kHashHexLen;
}
// allocates memory for hashA1Hex16Bit->Ptr
void CalcMD5HA1( StrPtrLen* userName,
StrPtrLen* realm,
StrPtrLen* userPassword,
StrPtrLen* hashA1Hex16Bit
)
{
// parameters must be valid pointers
// It is ok if parameter->Ptr is NULL as long as parameter->Len is 0
Assert(userName);
Assert(realm);
Assert(userPassword);
Assert(hashA1Hex16Bit);
Assert(hashA1Hex16Bit->Ptr == NULL); //This is the result. A Ptr here will be replaced. Value should be NULL.
MD5_CTX context;
unsigned char* aHash = NEW unsigned char[kHashLen];
// Calculate H(A1) for MD5
// where A1 for algorithm = "md5" or if nothing is specified is
// A1 = userName:realm:userPassword
MD5_Init(&context);
MD5_Update(&context, (unsigned char *)userName->Ptr, userName->Len);
MD5_Update(&context, (unsigned char *)sColon.Ptr, sColon.Len);
MD5_Update(&context, (unsigned char *)realm->Ptr, realm->Len);
MD5_Update(&context, (unsigned char *)sColon.Ptr, sColon.Len);
MD5_Update(&context, (unsigned char *)userPassword->Ptr, userPassword->Len);
MD5_Final(aHash, &context);
hashA1Hex16Bit->Ptr = (char *)aHash;
hashA1Hex16Bit->Len = kHashLen;
}
// allocates memory to hA1->Ptr
void CalcHA1( StrPtrLen* algorithm,
StrPtrLen* userName,
StrPtrLen* realm,
StrPtrLen* userPassword,
StrPtrLen* nonce,
StrPtrLen* cNonce,
StrPtrLen* hA1
)
{
// parameters must be valid pointers
// It is ok if parameter->Ptr is NULL as long as parameter->Len is 0
Assert(algorithm);
Assert(userName);
Assert(realm);
Assert(userPassword);
Assert(nonce);
Assert(cNonce);
Assert(hA1);
Assert(hA1->Ptr == NULL); //This is the result. A Ptr here will be replaced. Value should be NULL.
MD5_CTX context;
unsigned char aHash[kHashLen];
// Calculate H(A1)
// where A1 for algorithm = "md5" or if nothing is specified is
// A1 = userName:realm:userPassword
// and for algorithm = "md5-sess" is
// A1 = H(userName:realm:userPassword):nonce:cnonce
MD5_Init(&context);
MD5_Update(&context, (unsigned char *)userName->Ptr, userName->Len);
MD5_Update(&context, (unsigned char *)sColon.Ptr, sColon.Len);
MD5_Update(&context, (unsigned char *)realm->Ptr, realm->Len);
MD5_Update(&context, (unsigned char *)sColon.Ptr, sColon.Len);
MD5_Update(&context, (unsigned char *)userPassword->Ptr, userPassword->Len);
MD5_Final(aHash, &context);
if(algorithm->Equal(sMD5Sess)) {
MD5_Init(&context);
MD5_Update(&context, aHash, kHashLen);
MD5_Update(&context, (unsigned char *)sColon.Ptr, sColon.Len);
MD5_Update(&context, (unsigned char *)nonce->Ptr, nonce->Len);
MD5_Update(&context, (unsigned char *)sColon.Ptr, sColon.Len);
MD5_Update(&context, (unsigned char *)cNonce->Ptr, cNonce->Len);
MD5_Final(aHash, &context);
}
HashToString(aHash, hA1);
}
// allocates memory to hA1->Ptr
void CalcHA1Md5Sess(StrPtrLen* hashA1Hex16Bit, StrPtrLen* nonce, StrPtrLen* cNonce, StrPtrLen* hA1)
{
// parameters must be valid pointers
// It is ok if parameter->Ptr is NULL as long as parameter->Len is 0
Assert(hashA1Hex16Bit);
Assert(hashA1Hex16Bit->Len == kHashLen);
Assert(nonce);
Assert(cNonce);
Assert(hA1);
Assert(hA1->Ptr == NULL); //This is the result. A Ptr here will be replaced. Value should be NULL.
MD5_CTX context;
unsigned char aHash[kHashLen];
MD5_Init(&context);
MD5_Update(&context, (unsigned char *)hashA1Hex16Bit->Ptr, kHashLen);
MD5_Update(&context, (unsigned char *)sColon.Ptr, sColon.Len);
MD5_Update(&context, (unsigned char *)nonce->Ptr, nonce->Len);
MD5_Update(&context, (unsigned char *)sColon.Ptr, sColon.Len);
MD5_Update(&context, (unsigned char *)cNonce->Ptr, cNonce->Len);
MD5_Final(aHash, &context);
// allocates memory to hA1->Ptr
HashToString(aHash, hA1);
}
// allocates memory for requestDigest->Ptr
void CalcRequestDigest( StrPtrLen* hA1,
StrPtrLen* nonce,
StrPtrLen* nonceCount,
StrPtrLen* cNonce,
StrPtrLen* qop,
StrPtrLen* method,
StrPtrLen* digestUri,
StrPtrLen* hEntity,
StrPtrLen* requestDigest
)
{
// parameters must be valid pointers
// It is ok if parameter->Ptr is NULL as long as parameter->Len is 0
Assert(hA1);
Assert(nonce);
Assert(nonceCount);
Assert(cNonce);
Assert(qop);
Assert(method);
Assert(digestUri);
Assert(hEntity);
Assert(requestDigest);
Assert(requestDigest->Ptr == NULL); //This is the result. A Ptr here will be replaced. Value should be NULL.
unsigned char aHash[kHashLen], requestHash[kHashLen];
StrPtrLen hA2;
MD5_CTX context;
// H(data) = MD5(data)
// and KD(secret, data) = H(concat(secret, ":", data))
// Calculate H(A2)
// where A2 for qop="auth" or no qop is
// A2 = method:digestUri
// and for qop = "auth-int" is
// A2 = method:digestUri:H(entity-body)
MD5_Init(&context);
MD5_Update(&context, (unsigned char *)method->Ptr, method->Len);
MD5_Update(&context, (unsigned char *)sColon.Ptr, sColon.Len);
MD5_Update(&context, (unsigned char *)digestUri->Ptr, digestUri->Len);
if(qop->Equal(sQopAuthInt)) {
MD5_Update(&context, (unsigned char *)sColon.Ptr, sColon.Len);
MD5_Update(&context, (unsigned char *)hEntity->Ptr, hEntity->Len);
}
MD5_Final(aHash, &context);
// HashToString allocates memory for hA2...delete it after request-digest is created
HashToString(aHash, &hA2);
// Calculate request-digest
// where request-digest for qop="auth" or qop="auth-int" is
// request-digest = KD( H(A1), nonce:nonceCount:cNonce:qop:H(A2) )
// and if qop directive isn't present is
// request-digest = KD( H(A1), nonce:H(A2) )
MD5_Init(&context);
MD5_Update(&context, (unsigned char *)hA1->Ptr, hA1->Len);
MD5_Update(&context, (unsigned char *)sColon.Ptr, sColon.Len);
MD5_Update(&context, (unsigned char *)nonce->Ptr, nonce->Len);
MD5_Update(&context, (unsigned char *)sColon.Ptr, sColon.Len);
if(qop->Ptr != NULL) {
MD5_Update(&context, (unsigned char *)nonceCount->Ptr, nonceCount->Len);
MD5_Update(&context, (unsigned char *)sColon.Ptr, sColon.Len);
MD5_Update(&context, (unsigned char *)cNonce->Ptr, cNonce->Len);
MD5_Update(&context, (unsigned char *)sColon.Ptr, sColon.Len);
MD5_Update(&context, (unsigned char *)qop->Ptr, qop->Len);
MD5_Update(&context, (unsigned char *)sColon.Ptr, sColon.Len);
}
MD5_Update(&context, (unsigned char *)hA2.Ptr, hA2.Len);
MD5_Final(requestHash, &context);
HashToString(requestHash, requestDigest);
// Deleting memory allocated for hA2
delete [] hA2.Ptr;
}
/* From local_passwd.c (C) Regents of Univ. of California blah blah */
static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
void to64(register char *s, register SInt32 v, register int n)
{
while (--n >= 0) {
*s++ = itoa64[v & 0x3f];
v >>= 6;
}
}
/*
* Define the Magic String prefix that identifies a password as being
* hashed using our algorithm.
*/
static char *dufr_id = "$dufr$";
// Doesn't allocate any memory. The size of the result buffer should be nbytes
void MD5Encode(char *pw, char *salt, char *result, int nbytes)
{
/*
* Minimum size is 8 bytes for salt, plus 1 for the trailing NUL,
* plus 4 for the '$' separators, plus the password hash itself.
* Let's leave a goodly amount of leeway.
*/
char passwd[120], *p;
char *sp, *ep;
unsigned char final[kHashLen];
int sl, pl, i;
MD5_CTX ctx, ctx1;
UInt32 l;
/*
* Refine the salt first. It's possible we were given an already-hashed
* string as the salt argument, so extract the actual salt value from it
* if so. Otherwise just use the string up to the first '$' as the salt.
*/
sp = salt;
//If it starts with the magic string, then skip that.
if (!strncmp(sp, dufr_id, strlen(dufr_id)))
{
sp += strlen(dufr_id);
}
//It stops at the first '$' or 8 chars, whichever comes first
for (ep = sp; (*ep != '\0') && (*ep != '$') && (ep < (sp + 8)); ep++)
{
continue;
}
//Get the length of the true salt
sl = ep - sp;
//'Time to make the doughnuts..'
MD5_Init(&ctx);
//The password first, since that is what is most unknown
MD5_Update(&ctx, (unsigned char *)pw, strlen(pw));
//Then our magic string
MD5_Update(&ctx, (unsigned char *)dufr_id, strlen(dufr_id));
//Then the raw salt
MD5_Update(&ctx, (unsigned char *)sp, sl);
//Then just as many characters of the MD5(pw, salt, pw)
MD5_Init(&ctx1);
MD5_Update(&ctx1, (unsigned char *)pw, strlen(pw));
MD5_Update(&ctx1, (unsigned char *)sp, sl);
MD5_Update(&ctx1, (unsigned char *)pw, strlen(pw));
MD5_Final(final, &ctx1);
for (pl = strlen(pw); pl > 0; pl -= kHashLen)
{
MD5_Update(&ctx, (unsigned char *)final,(pl > kHashLen) ? kHashLen : pl);
}
//Don't leave anything around in vm they could use.
memset(final, 0, sizeof(final));
//Then something really weird...
for (i = strlen(pw); i != 0; i >>= 1)
{
if (i & 1) {
MD5_Update(&ctx, (unsigned char *)final, 1);
}
else {
MD5_Update(&ctx, (unsigned char *)pw, 1);
}
}
/*
* Now make the output string. We know our limitations, so we
* can use the string routines without bounds checking.
*/
strcpy(passwd, dufr_id);
strncat(passwd, sp, sl);
strcat(passwd, "$");
MD5_Final(final, &ctx);
/*
* And now, just to make sure things don't run too fast..
* On a 60 Mhz Pentium this takes 34 msec, so you would
* need 30 seconds to build a 1000 entry dictionary...
*/
for (i = 0; i < 1000; i++)
{
MD5_Init(&ctx1);
if (i & 1) {
MD5_Update(&ctx1, (unsigned char *)pw, strlen(pw));
}
else {
MD5_Update(&ctx1, final, kHashLen);
}
if (i % 3) {
MD5_Update(&ctx1, (unsigned char *)sp, sl);
}
if (i % 7) {
MD5_Update(&ctx1, (unsigned char *)pw, strlen(pw));
}
if (i & 1) {
MD5_Update(&ctx1, (unsigned char *)final, kHashLen);
}
else {
MD5_Update(&ctx1, (unsigned char *)pw, strlen(pw));
}
MD5_Final(final,&ctx1);
}
p = passwd + strlen(passwd);
l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p, l, 4); p += 4;
l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p, l, 4); p += 4;
l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p, l, 4); p += 4;
l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p, l, 4); p += 4;
l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p, l, 4); p += 4;
l = final[11] ; to64(p, l, 2); p += 2;
*p = '\0';
//Don't leave anything around in vm they could use.
memset(final, 0, sizeof(final));
strncpy(result, passwd, nbytes - 1);
}