394 lines
13 KiB
C++
394 lines
13 KiB
C++
/*
|
|
*
|
|
* @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);
|
|
}
|