181464Sbrian/*- 281464Sbrian * Copyright (c) 1997 Gabor Kincses <gabor@acm.org> 381464Sbrian * 1997 - 2001 Brian Somers <brian@Awfulhak.org> 481464Sbrian * based on work by Eric Rosenquist 581464Sbrian * Strata Software Limited. 629841Sbrian * All rights reserved. 729841Sbrian * 881464Sbrian * Redistribution and use in source and binary forms, with or without 981464Sbrian * modification, are permitted provided that the following conditions 1081464Sbrian * are met: 1181464Sbrian * 1. Redistributions of source code must retain the above copyright 1281464Sbrian * notice, this list of conditions and the following disclaimer. 1381464Sbrian * 2. Redistributions in binary form must reproduce the above copyright 1481464Sbrian * notice, this list of conditions and the following disclaimer in the 1581464Sbrian * documentation and/or other materials provided with the distribution. 1629841Sbrian * 1781464Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1881464Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1981464Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2081464Sbrian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2181464Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2281464Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2381464Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2481464Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2581464Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2681464Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2781464Sbrian * SUCH DAMAGE. 2830715Sbrian * 2950479Speter * $FreeBSD$ 3029841Sbrian */ 3129841Sbrian 3244106Sbrian#include <ctype.h> 3358040Sbrian#ifdef __FreeBSD__ 3457451Smarkm#include <openssl/des.h> 3568344Sbrian#include <sha.h> 3658040Sbrian#else 3775212Sbrian#include <sys/types.h> 3868344Sbrian#include <stdlib.h> 3996387Sbrian#ifdef __NetBSD__ 4096387Sbrian#include <openssl/des.h> 4196387Sbrian#else 4258040Sbrian#include <des.h> 4396387Sbrian#endif 4468344Sbrian#include <openssl/sha.h> 4558040Sbrian#endif 4667910Sbrian#include <md4.h> 4730715Sbrian#include <string.h> 4829841Sbrian 4929841Sbrian#include "chap_ms.h" 5029841Sbrian 5167910Sbrian/* 5267910Sbrian * Documentation & specifications: 5367910Sbrian * 5467910Sbrian * MS-CHAP (CHAP80) rfc2433 5567910Sbrian * MS-CHAP-V2 (CHAP81) rfc2759 5667910Sbrian * MPPE key management draft-ietf-pppext-mppe-keys-02.txt 5767910Sbrian */ 5867910Sbrian 5967910Sbrianstatic char SHA1_Pad1[40] = 6067910Sbrian {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 6167910Sbrian 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 6267910Sbrian 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 6367910Sbrian 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 6467910Sbrian 6567910Sbrianstatic char SHA1_Pad2[40] = 6667910Sbrian {0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 6767910Sbrian 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 6867910Sbrian 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 6967910Sbrian 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2}; 7067910Sbrian 7129841Sbrian/* unused, for documentation only */ 7229841Sbrian/* only NTResp is filled in for FreeBSD */ 7336285Sbrianstruct MS_ChapResponse { 7429841Sbrian u_char LANManResp[24]; 7529841Sbrian u_char NTResp[24]; 7629841Sbrian u_char UseNT; /* If 1, ignore the LANMan response field */ 7736285Sbrian}; 7829841Sbrian 7967912Sbrianstatic u_char 8067912SbrianGet7Bits(u_char *input, int startBit) 8129841Sbrian{ 8229841Sbrian register unsigned int word; 8329841Sbrian 8429841Sbrian word = (unsigned)input[startBit / 8] << 8; 8529841Sbrian word |= (unsigned)input[startBit / 8 + 1]; 8629841Sbrian 8729841Sbrian word >>= 15 - (startBit % 8 + 7); 8829841Sbrian 8929841Sbrian return word & 0xFE; 9029841Sbrian} 9129841Sbrian 9229841Sbrian/* IN 56 bit DES key missing parity bits 9329841Sbrian OUT 64 bit DES key with parity bits added */ 9467912Sbrianstatic void 9567912SbrianMakeKey(u_char *key, u_char *des_key) 9629841Sbrian{ 9729841Sbrian des_key[0] = Get7Bits(key, 0); 9829841Sbrian des_key[1] = Get7Bits(key, 7); 9929841Sbrian des_key[2] = Get7Bits(key, 14); 10029841Sbrian des_key[3] = Get7Bits(key, 21); 10129841Sbrian des_key[4] = Get7Bits(key, 28); 10229841Sbrian des_key[5] = Get7Bits(key, 35); 10329841Sbrian des_key[6] = Get7Bits(key, 42); 10429841Sbrian des_key[7] = Get7Bits(key, 49); 10529841Sbrian 10629841Sbrian des_set_odd_parity((des_cblock *)des_key); 10729841Sbrian} 10829841Sbrian 10944106Sbrianstatic void /* IN 8 octets IN 7 octest OUT 8 octets */ 11044106SbrianDesEncrypt(u_char *clear, u_char *key, u_char *cipher) 11144106Sbrian{ 11244106Sbrian des_cblock des_key; 11344106Sbrian des_key_schedule key_schedule; 11444106Sbrian 11544106Sbrian MakeKey(key, des_key); 11644106Sbrian des_set_key(&des_key, key_schedule); 11744106Sbrian des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1); 11844106Sbrian} 11944106Sbrian 12044106Sbrianstatic void /* IN 8 octets IN 16 octets OUT 24 octets */ 12144106SbrianChallengeResponse(u_char *challenge, u_char *pwHash, u_char *response) 12244106Sbrian{ 12344106Sbrian char ZPasswordHash[21]; 12444106Sbrian 12544106Sbrian memset(ZPasswordHash, '\0', sizeof ZPasswordHash); 12644106Sbrian memcpy(ZPasswordHash, pwHash, 16); 12744106Sbrian 12844106Sbrian DesEncrypt(challenge, ZPasswordHash + 0, response + 0); 12944106Sbrian DesEncrypt(challenge, ZPasswordHash + 7, response + 8); 13044106Sbrian DesEncrypt(challenge, ZPasswordHash + 14, response + 16); 13144106Sbrian} 13244106Sbrian 13367910Sbrianvoid 13467912SbrianNtPasswordHash(char *key, int keylen, char *hash) 13598243Sbrian{ 13667910Sbrian MD4_CTX MD4context; 13767910Sbrian 13867910Sbrian MD4Init(&MD4context); 13967910Sbrian MD4Update(&MD4context, key, keylen); 14067910Sbrian MD4Final(hash, &MD4context); 14167910Sbrian} 14267910Sbrian 14367910Sbrianvoid 14467912SbrianHashNtPasswordHash(char *hash, char *hashhash) 14598243Sbrian{ 14667910Sbrian MD4_CTX MD4context; 14767910Sbrian 14867910Sbrian MD4Init(&MD4context); 14967910Sbrian MD4Update(&MD4context, hash, 16); 15067910Sbrian MD4Final(hashhash, &MD4context); 15167910Sbrian} 15267910Sbrian 153134789Sbrianstatic void 15467912SbrianChallengeHash(char *PeerChallenge, char *AuthenticatorChallenge, 155134789Sbrian char *UserName, char *Challenge) 15667912Sbrian{ 15767910Sbrian SHA_CTX Context; 15867910Sbrian char Digest[SHA_DIGEST_LENGTH]; 15967910Sbrian char *Name; 16067910Sbrian 16167910Sbrian Name = strrchr(UserName, '\\'); 16298243Sbrian if(NULL == Name) 16367910Sbrian Name = UserName; 16467910Sbrian else 16567910Sbrian Name++; 16667910Sbrian 16767910Sbrian SHA1_Init(&Context); 16867910Sbrian 16998243Sbrian SHA1_Update(&Context, PeerChallenge, 16); 17067910Sbrian SHA1_Update(&Context, AuthenticatorChallenge, 16); 17169330Sbrian SHA1_Update(&Context, Name, strlen(Name)); 17267910Sbrian 17367910Sbrian SHA1_Final(Digest, &Context); 17467910Sbrian memcpy(Challenge, Digest, 8); 17567910Sbrian} 17667910Sbrian 17767910Sbrianvoid 17867912SbrianGenerateNTResponse(char *AuthenticatorChallenge, char *PeerChallenge, 179134789Sbrian char *UserName, char *Password, 18067912Sbrian int PasswordLen, char *Response) 18167912Sbrian{ 18267910Sbrian char Challenge[8]; 18367910Sbrian char PasswordHash[16]; 18467910Sbrian 185134789Sbrian ChallengeHash(PeerChallenge, AuthenticatorChallenge, UserName, Challenge); 18667910Sbrian NtPasswordHash(Password, PasswordLen, PasswordHash); 18767910Sbrian ChallengeResponse(Challenge, PasswordHash, Response); 18867910Sbrian} 18967910Sbrian 19068344Sbrian#ifndef __FreeBSD__ 19168344Sbrian#define LENGTH 20 19279437Sbrianstatic char * 19368344SbrianSHA1_End(SHA_CTX *ctx, char *buf) 19468344Sbrian{ 19568344Sbrian int i; 19668344Sbrian unsigned char digest[LENGTH]; 19768344Sbrian static const char hex[]="0123456789abcdef"; 19868344Sbrian 19968344Sbrian if (!buf) 20068344Sbrian buf = malloc(2*LENGTH + 1); 20168344Sbrian if (!buf) 20268344Sbrian return 0; 20368344Sbrian SHA1_Final(digest, ctx); 20468344Sbrian for (i = 0; i < LENGTH; i++) { 20568344Sbrian buf[i+i] = hex[digest[i] >> 4]; 20668344Sbrian buf[i+i+1] = hex[digest[i] & 0x0f]; 20768344Sbrian } 20868344Sbrian buf[i+i] = '\0'; 20968344Sbrian return buf; 21068344Sbrian} 21168344Sbrian#endif 21268344Sbrian 21367910Sbrianvoid 21467912SbrianGenerateAuthenticatorResponse(char *Password, int PasswordLen, 21567912Sbrian char *NTResponse, char *PeerChallenge, 21667912Sbrian char *AuthenticatorChallenge, char *UserName, 217134789Sbrian char *AuthenticatorResponse) 21867912Sbrian{ 21967910Sbrian SHA_CTX Context; 22067910Sbrian char PasswordHash[16]; 22167910Sbrian char PasswordHashHash[16]; 22267910Sbrian char Challenge[8]; 22367910Sbrian u_char Digest[SHA_DIGEST_LENGTH]; 22467910Sbrian int i; 22567910Sbrian 22667910Sbrian /* 22767910Sbrian * "Magic" constants used in response generation 22867910Sbrian */ 22967910Sbrian char Magic1[39] = 23067910Sbrian {0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76, 23167910Sbrian 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65, 23267910Sbrian 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, 23367910Sbrian 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74}; 23467910Sbrian 23567910Sbrian 23667910Sbrian char Magic2[41] = 23767910Sbrian {0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B, 23867910Sbrian 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F, 23967910Sbrian 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E, 24067910Sbrian 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F, 24167910Sbrian 0x6E}; 24267910Sbrian /* 24367910Sbrian * Hash the password with MD4 24467910Sbrian */ 24567910Sbrian NtPasswordHash(Password, PasswordLen, PasswordHash); 24667910Sbrian /* 24767910Sbrian * Now hash the hash 24867910Sbrian */ 24967910Sbrian HashNtPasswordHash(PasswordHash, PasswordHashHash); 25067910Sbrian 25167910Sbrian SHA1_Init(&Context); 25267910Sbrian SHA1_Update(&Context, PasswordHashHash, 16); 25367910Sbrian SHA1_Update(&Context, NTResponse, 24); 25467910Sbrian SHA1_Update(&Context, Magic1, 39); 25567910Sbrian SHA1_Final(Digest, &Context); 256134789Sbrian ChallengeHash(PeerChallenge, AuthenticatorChallenge, UserName, Challenge); 25767910Sbrian SHA1_Init(&Context); 25867910Sbrian SHA1_Update(&Context, Digest, 20); 25967910Sbrian SHA1_Update(&Context, Challenge, 8); 26067910Sbrian SHA1_Update(&Context, Magic2, 41); 26167910Sbrian 26267910Sbrian /* 26367910Sbrian * Encode the value of 'Digest' as "S=" followed by 26467910Sbrian * 40 ASCII hexadecimal digits and return it in 26567910Sbrian * AuthenticatorResponse. 26667910Sbrian * For example, 26767910Sbrian * "S=0123456789ABCDEF0123456789ABCDEF01234567" 26867910Sbrian */ 26967910Sbrian AuthenticatorResponse[0] = 'S'; 27067910Sbrian AuthenticatorResponse[1] = '='; 27167910Sbrian SHA1_End(&Context, AuthenticatorResponse + 2); 27267910Sbrian for (i=2; i<42; i++) 27367910Sbrian AuthenticatorResponse[i] = toupper(AuthenticatorResponse[i]); 27467910Sbrian 27567910Sbrian} 27698243Sbrian 27767910Sbrianvoid 27867912SbrianGetMasterKey(char *PasswordHashHash, char *NTResponse, char *MasterKey) 27967912Sbrian{ 28067910Sbrian char Digest[SHA_DIGEST_LENGTH]; 28167910Sbrian SHA_CTX Context; 28267910Sbrian static char Magic1[27] = 28367910Sbrian {0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 28467910Sbrian 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d, 28567910Sbrian 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79}; 28667910Sbrian 28767910Sbrian SHA1_Init(&Context); 28867910Sbrian SHA1_Update(&Context, PasswordHashHash, 16); 28967910Sbrian SHA1_Update(&Context, NTResponse, 24); 29067910Sbrian SHA1_Update(&Context, Magic1, 27); 29167910Sbrian SHA1_Final(Digest, &Context); 29267910Sbrian memcpy(MasterKey, Digest, 16); 29367910Sbrian} 29467910Sbrian 29567910Sbrianvoid 29667912SbrianGetAsymetricStartKey(char *MasterKey, char *SessionKey, int SessionKeyLength, 29767912Sbrian int IsSend, int IsServer) 29867912Sbrian{ 29967910Sbrian char Digest[SHA_DIGEST_LENGTH]; 30067910Sbrian SHA_CTX Context; 30167910Sbrian char *s; 30267910Sbrian 30367910Sbrian static char Magic2[84] = 30467910Sbrian {0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, 30567910Sbrian 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, 30667910Sbrian 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 30767910Sbrian 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79, 30867910Sbrian 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 30967910Sbrian 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65, 31067910Sbrian 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 31167910Sbrian 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, 31267910Sbrian 0x6b, 0x65, 0x79, 0x2e}; 31367910Sbrian 31467910Sbrian static char Magic3[84] = 31567910Sbrian {0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, 31667910Sbrian 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, 31767910Sbrian 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 31867910Sbrian 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, 31967910Sbrian 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 32067910Sbrian 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 32167910Sbrian 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 32267910Sbrian 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 32367910Sbrian 0x6b, 0x65, 0x79, 0x2e}; 32467910Sbrian 32567910Sbrian if (IsSend) { 32667910Sbrian if (IsServer) { 32767910Sbrian s = Magic3; 32867910Sbrian } else { 32967910Sbrian s = Magic2; 33067910Sbrian } 33167910Sbrian } else { 33267910Sbrian if (IsServer) { 33367910Sbrian s = Magic2; 33467910Sbrian } else { 33567910Sbrian s = Magic3; 33667910Sbrian } 33767910Sbrian } 33867910Sbrian 33967910Sbrian SHA1_Init(&Context); 34067910Sbrian SHA1_Update(&Context, MasterKey, 16); 34167910Sbrian SHA1_Update(&Context, SHA1_Pad1, 40); 34267910Sbrian SHA1_Update(&Context, s, 84); 34367910Sbrian SHA1_Update(&Context, SHA1_Pad2, 40); 34467910Sbrian SHA1_Final(Digest, &Context); 34567910Sbrian 34667910Sbrian memcpy(SessionKey, Digest, SessionKeyLength); 34767910Sbrian} 34867910Sbrian 34967910Sbrianvoid 35067912SbrianGetNewKeyFromSHA(char *StartKey, char *SessionKey, long SessionKeyLength, 35167912Sbrian char *InterimKey) 35267912Sbrian{ 35367910Sbrian SHA_CTX Context; 35467910Sbrian char Digest[SHA_DIGEST_LENGTH]; 35567910Sbrian 35667910Sbrian SHA1_Init(&Context); 35767910Sbrian SHA1_Update(&Context, StartKey, SessionKeyLength); 35867910Sbrian SHA1_Update(&Context, SHA1_Pad1, 40); 35967910Sbrian SHA1_Update(&Context, SessionKey, SessionKeyLength); 36067910Sbrian SHA1_Update(&Context, SHA1_Pad2, 40); 36167910Sbrian SHA1_Final(Digest, &Context); 36267910Sbrian 36367910Sbrian memcpy(InterimKey, Digest, SessionKeyLength); 36467910Sbrian} 36567910Sbrian 36667912Sbrian#if 0 36767912Sbrianstatic void 36867912SbrianGet_Key(char *InitialSessionKey, char *CurrentSessionKey, 36967912Sbrian int LengthOfDesiredKey) 37067912Sbrian{ 37167910Sbrian SHA_CTX Context; 37267910Sbrian char Digest[SHA_DIGEST_LENGTH]; 37367910Sbrian 37467910Sbrian SHA1_Init(&Context); 37567910Sbrian SHA1_Update(&Context, InitialSessionKey, LengthOfDesiredKey); 37667910Sbrian SHA1_Update(&Context, SHA1_Pad1, 40); 37767910Sbrian SHA1_Update(&Context, CurrentSessionKey, LengthOfDesiredKey); 37867910Sbrian SHA1_Update(&Context, SHA1_Pad2, 40); 37967910Sbrian SHA1_Final(Digest, &Context); 38067910Sbrian 38167910Sbrian memcpy(CurrentSessionKey, Digest, LengthOfDesiredKey); 38267910Sbrian} 38367912Sbrian#endif 38467910Sbrian 38529841Sbrian/* passwordHash 16-bytes MD4 hashed password 38629841Sbrian challenge 8-bytes peer CHAP challenge 38729841Sbrian since passwordHash is in a 24-byte buffer, response is written in there */ 38829841Sbrianvoid 38944106Sbrianmschap_NT(char *passwordHash, char *challenge) 39029841Sbrian{ 39129841Sbrian u_char response[24]; 39229841Sbrian 39329841Sbrian ChallengeResponse(challenge, passwordHash, response); 39430715Sbrian memcpy(passwordHash, response, 24); 39544106Sbrian passwordHash[24] = 1; /* NT-style response */ 39629841Sbrian} 39744106Sbrian 39844106Sbrianvoid 39944106Sbrianmschap_LANMan(char *digest, char *challenge, char *secret) 40044106Sbrian{ 40144106Sbrian static u_char salt[] = "KGS!@#$%"; /* RASAPI32.dll */ 40244106Sbrian char SECRET[14], *ptr, *end; 40344106Sbrian u_char hash[16]; 40444106Sbrian 40544106Sbrian end = SECRET + sizeof SECRET; 40644106Sbrian for (ptr = SECRET; *secret && ptr < end; ptr++, secret++) 40744106Sbrian *ptr = toupper(*secret); 40844106Sbrian if (ptr < end) 40944106Sbrian memset(ptr, '\0', end - ptr); 41044106Sbrian 41144106Sbrian DesEncrypt(salt, SECRET, hash); 41244106Sbrian DesEncrypt(salt, SECRET + 7, hash + 8); 41344106Sbrian 41444106Sbrian ChallengeResponse(challenge, hash, digest); 41544106Sbrian} 416