Log in

View Full Version : Diffie Hellman Key Agreement - Cryptlib???


Clandestiny
October 26th, 2003, 23:29
Hiya,

I am attempting to perform a Diffie Hellman key agreement between a client and server using the CryptLib library. According to CryptLib documentation, Diffie Hellman has been implemented according to PKCS-3. I looked up PKCS-3 and think I understand the basic sequence of steps:

1. Each of the 2 parties need a prime number and a base

It seems there is some protocol called SKIP (Simple Key Mangement for Internet Protocols). I'm not quite sure how or if I can use this with cryptlib, however...

2. The parties generate a private number

Which function does this in cryptlib?

3. The parties generate public keys

Once again, I can't figure out which cryptlib function does this.

4. The 2 parties exchage keys and convert the exchanged numbers into a secret key.

I know this is probably something really basic. If only I could locate a piece of example code for crypt lib using Diffie Hellman. Alas, all of my searches have turned up nothing on this topic. I thought about using the cryptEncrypt function with the identifier CRYPT_ALGO_DH, but it didn't quite seem to fit and I couldn't figure out where the prime number or base should come from. I'm a desparate newbie looking for a pointer in the right direction

Thanks,
Clandestiny

Clandestiny
October 27th, 2003, 15:04
Is it just me or is there absolutely NO documentation on this? I have searched the manual over and over looking for some information. Heaven forbid there should be an actual piece of example code lying around somewhere . Finally, I resorted to looking at the source code and found *something* that looks like what I need, but it appears that these are all internal functions and not listed in the main cryptlib.h header that I've used previously.

This is what I found listed below though I can't get the damn thing to include without errors to save my life
The following functions are located in lib_dh.c in case anyone knows anything about the cryptlib library.

I am going to *assume* the first step will be to call dhInitKey... Can't vouch for that though as I can't get everything to compile w/o linker errors.

BTW, I don't really care what library I'm using... Feel free to suggest another one that has Diffie Hellman (with better documentation) if you know of one. At this point, ANY suggestions will be appreciated.

int dhInitKey( CRYPT_INFO *cryptInfo, const void *key, const int keyLength );
int dhEncrypt( CRYPT_INFO *cryptInfo, BYTE *buffer, int noBytes );
int dhDecrypt( CRYPT_INFO *cryptInfo, BYTE *buffer, int noBytes );

/****************************************************************************
* *
* Diffie-Hellman Key Exchange Routines *
* *
****************************************************************************/

/* Perform phase 1 of Diffie-Hellman ("export" */

int dhEncrypt( CRYPT_INFO *cryptInfo, BYTE *buffer, int noBytes )
{
KEYAGREE_PARAMS *keyAgreeParams = ( KEYAGREE_PARAMS * ) buffer;
BN_CTX *bnCTX;
int status = CRYPT_OK;

UNUSED( noBytes );

/* Usually y is generated as a side-effect of the implicit generation of
the x value, so if y is already set we just return its value */
if( !BN_is_zero( cryptInfo->ctxPKC.dlpParam_y ) )
{
keyAgreeParams->publicValueLen = \
BN_bn2bin( cryptInfo->ctxPKC.dlpParam_y,
keyAgreeParams->publicValue );
return( CRYPT_OK );
}

if( ( bnCTX = BN_CTX_new() ) == NULL )
return( CRYPT_ERROR_MEMORY );

/* Export y = g^x mod p. There is no input data since x was set when the
DH values were loaded */
BN_mod_exp( cryptInfo->ctxPKC.dlpParam_y, cryptInfo->ctxPKC.dlpParam_g,
cryptInfo->ctxPKC.dlpParam_x, cryptInfo->ctxPKC.dlpParam_p,
bnCTX );
keyAgreeParams->publicValueLen = \
BN_bn2bin( cryptInfo->ctxPKC.dlpParam_y,
keyAgreeParams->publicValue );
BN_CTX_free( bnCTX );

return( ( status == -1 ) ? CRYPT_ERROR_FAILED : status );
}

/* Perform phase 2 of Diffie-Hellman ("import" */

int dhDecrypt( CRYPT_INFO *cryptInfo, BYTE *buffer, int noBytes )
{
KEYAGREE_PARAMS *keyAgreeParams = ( KEYAGREE_PARAMS * ) buffer;
BN_CTX *bnCTX;
BIGNUM *z;
const int length = bitsToBytes( cryptInfo->ctxPKC.keySizeBits );
int i, status = CRYPT_OK;

/* Make sure we're not being fed suspiciously short data quantities */
for( i = 0; i < length; i++ )
if( keyAgreeParams->publicValue[ i ] )
break;
if( length - i < 56 )
return( CRYPT_ERROR_BADDATA );

if( ( bnCTX = BN_CTX_new() ) == NULL )
return( CRYPT_ERROR_MEMORY );

/* The other parties y value will be stored with the key agreement info
rather than having been read in when we read the DH public key */
BN_bin2bn( keyAgreeParams->publicValue, keyAgreeParams->publicValueLen,
cryptInfo->ctxPKC.dhParam_yPrime );

/* Export z = y^x mod p. We need to use separate y and z values because
the bignum code can't handle modexp with the first two parameters the
same */
z = BN_new();
BN_mod_exp( z, cryptInfo->ctxPKC.dhParam_yPrime,
cryptInfo->ctxPKC.dlpParam_x, cryptInfo->ctxPKC.dlpParam_p,
bnCTX );
keyAgreeParams->wrappedKeyLen = BN_bn2bin( z, keyAgreeParams->wrappedKey );
BN_clear_free( z );

#if 0
y = BN_new();
BN_bin2bn( buffer, length, y );
zeroise( buffer, length ); /* Clear buffer while data is in bignum */
BN_mod_exp( y, y, cryptInfo->ctxPKC.dlpParam_x,
cryptInfo->ctxPKC.dlpParam_p, bnCTX );
length = BN_bn2bin( y, buffer );
BN_clear_free( y );
#endif

BN_CTX_free( bnCTX );

return( ( status == -1 ) ? CRYPT_ERROR_FAILED : status );
}

/****************************************************************************
* *
* Diffie-Hellman Key Management Routines *
* *
****************************************************************************/

/* Load DH public key components into an encryption context */

int dhInitKey( CRYPT_INFO *cryptInfo, const void *key, const int keyLength )
{
CRYPT_PKCINFO_DLP *dhKey = ( CRYPT_PKCINFO_DLP * ) key;
int status;

/* Load the key component from the external representation into the
internal BigNums unless we're doing an internal load */
if( keyLength != sizeof( PKCINFO_LOADINTERNAL ) )
{
cryptInfo->ctxPKC.isPublicKey = dhKey->isPublicKey;

BN_bin2bn( dhKey->p, bitsToBytes( dhKey->pLen ),
cryptInfo->ctxPKC.dlpParam_p );
BN_bin2bn( dhKey->g, bitsToBytes( dhKey->gLen ),
cryptInfo->ctxPKC.dlpParam_g );
BN_bin2bn( dhKey->q, bitsToBytes( dhKey->qLen ),
cryptInfo->ctxPKC.dlpParam_q );
BN_bin2bn( dhKey->y, bitsToBytes( dhKey->yLen ),
cryptInfo->ctxPKC.dlpParam_y );
if( !dhKey->isPublicKey )
BN_bin2bn( dhKey->x, bitsToBytes( dhKey->xLen ),
cryptInfo->ctxPKC.dlpParam_x );
else
{
/* If there's no x value present, generate one implicitly. This
is needed because all DH keys are effectively private keys */
status = generateDLPKey( cryptInfo, CRYPT_UNUSED, CRYPT_UNUSED,
FALSE );
if( cryptStatusError( status ) )
return( status );
}
}

/* Check the parameters and calculate the key ID */
status = checkDLParams( cryptInfo, TRUE );
if( cryptStatusError( status ) )
return( status );
cryptInfo->ctxPKC.keySizeBits = BN_num_bits( cryptInfo->ctxPKC.dlpParam_p );
return( calculateKeyID( cryptInfo ) );
}

/* Generate a Diffie-Hellman key into an encryption context */

int dhGenerateKey( CRYPT_INFO *cryptInfo, const int keySizeBits )
{
int status;

status = generateDLPKey( cryptInfo, keySizeBits, CRYPT_USE_DEFAULT,
TRUE );
if( cryptStatusError( status ) )
return( status );
return( calculateKeyID( cryptInfo ) );
}

Thanks,
--an extremely frustrated,
Clandestiny

mike
October 27th, 2003, 17:40
1. You always call cryptInit ifrst, otherwise nothing works
2. You only include cryptlib.h. Everything you need is there.
3. Link with the library.

Cryptlib expects you to use the high level interface rather than know how to do any particular algorithm.

Here's some Delphi code that shows how to create a key pair and export the public key to a cert file. The only change in your case would be to use CRYPT_ALGO_DSA rather than CRYPT_ALGO_RSA.

Code:

procedure TKGForm.OnBtnOKClick(Sender: TObject);
var
ret: Integer;
keyset: Integer;
context: Integer;
certlabel: String;
cert: Integer;
buf: PChar;
len: Integer;
memFlag: boolean;

certFile: Integer;
fileFlag: boolean;
begin
memFlag := false;
fileFlag := false;
certFile := -1;
certlabel := 'Shuttle Server';
try
ret := cryptInit;
if ret <> CRYPT_OK then
raise Ex.CreateFmt('Cryptlib error %d: could not init Cryptlib.',[ret]);

ret := cryptKeysetOpen(keyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE,
'shuttleserver.keyset', CRYPT_KEYOPT_CREATE);
if ret <> CRYPT_OK then
raise Ex.CreateFmt('Cryptlib error %d: could not create the keyset file "shuttleserver.keyset"',[ret]);

ret := cryptCreateContext(context, CRYPT_UNUSED, CRYPT_ALGO_RSA);
if ret <> CRYPT_OK then
raise Ex.CreateFmt('Cryptlib error %d: could not create context',[ret]);

ret := cryptSetAttributeString(context, CRYPT_CTXINFO_LABEL,
PChar(certlabel), length(certlabel));
if ret <> CRYPT_OK then
raise Ex.CreateFmt('Cryptlib error %d: could not set cert label',[ret]);

ret := cryptGenerateKey(context);
if ret <> CRYPT_OK then
raise Ex.CreateFmt('Cryptlib error %d: could not generate key',[ret]);

ret := cryptAddPrivateKey(keyset, context, PChar(Edit2.Text));
if ret <> CRYPT_OK then
raise Ex.CreateFmt('Cryptlib error %d: could not set password',[ret]);

ret := cryptCreateCert(cert, CRYPT_UNUSED, CRYPT_CERTTYPE_CERTIFICATE);
if ret <> CRYPT_OK then
raise Ex.CreateFmt('Cryptlib error %d: could not create certificate',[ret]);

ret := cryptSetAttribute(cert, CRYPT_CERTINFO_XYZZY, 1);
if ret <> CRYPT_OK then
raise Ex.CreateFmt('Cryptlib error %d: could not set XYZZY attribute',[ret]);

ret := cryptSetAttribute(cert, CRYPT_CERTINFO_SUBJECTPUBLICKEYINFO, context);
if ret <> CRYPT_OK then
raise Ex.CreateFmt('Cryptlib error %d: could not set cert''s public key',[ret]);

ret := cryptSetAttributeString(cert, CRYPT_CERTINFO_COMMONNAME,
PChar(certlabel), length(certlabel));
if ret <> CRYPT_OK then
raise Ex.CreateFmt('Cryptlib error %d: could not set common name',[ret]);

ret := cryptSignCert(cert, context);
if ret <> CRYPT_OK then
raise Ex.CreateFmt('Cryptlib error %d: could not self-sign certificate',[ret]);

len := 0;
buf := nil;
ret := cryptExportCert(buf, len, CRYPT_CERTFORMAT_CERTIFICATE, cert);
if ret <> CRYPT_OK then
raise Ex.CreateFmt('Cryptlib error %d: could not get cert size',[ret]);

GetMem(buf, len); memFlag := true;
ret := cryptExportCert(buf, len, CRYPT_CERTFORMAT_CERTIFICATE, cert);
if ret <> CRYPT_OK then
raise Ex.CreateFmt('Cryptlib error %d: could not get export cert',[ret]);

certFile := FileCreate('shuttleserver.p15');
fileFlag := true;
if certFile = -1 then
raise Ex.CreateFmt('File error: could not create "shuttleserver.p15"',[ret]);

FileWrite(certFile, buf^, len);
FileClose(certFile); fileFlag :=false;

FreeMem(buf); memFlag := false;

ret := cryptDestroyCert(cert);
if ret <> CRYPT_OK then
raise Ex.CreateFmt('Cryptlib error %d: could not destroy cert',[ret]);

ret := cryptDestroyContext(context);
if ret <> CRYPT_OK then
raise Ex.CreateFmt('Cryptlib error %d: could not destroy context',[ret]);

ret := cryptKeysetClose(keyset);
if ret <> CRYPT_OK then
raise Ex.CreateFmt('Cryptlib error %d: could close keyset',[ret]);

ret := cryptEnd;
if ret <> CRYPT_OK then
raise Ex.CreateFmt('Cryptlib error %d: error on shutdown',[ret]);
except
on E:Ex do begin
Application.MessageBox(PChar(E.Message),'Fatal Error',MB_OK);
if fileFlag then FileClose(certFile);
if memFlag then FreeMem(buf);
Halt;
end;
end;

Application.MessageBox('Keys generated successfully!','Success!',MB_OK);
Halt;
end;


Once you've created the cert, you can import it like this:

Code:

{ Allocate a buffer to hold the cert object. }
GetMem(buf,searchRec.Size+1);
memFlag := True;

{ Open the file. }
f := FileOpen(certFileName, fmOpenRead);
if f=-1 then
raise Ex.CreateFmt('File error: could not open ShuttleServer''s certificate! (%s)',[certFileName]);

{ Load the cert buffer. }
FileSeek(f,0,0);
bytes := FileRead(f, buf^, searchRec.Size);

{ Close the file. }
FileClose(f);

{ Create the cert object. }
ret := cryptImportCert(buf, bytes, CRYPT_UNUSED, cert);
if ret <> CRYPT_OK then
raise Ex.CreateFmt('Cryptlib error %d: could not import cert buffer.', [ret]);

{ Free the cert-sized buffer. }
FreeMem(buf); memFlag := False;


Finally, encrypt files using the enveloping API.

mike
October 28th, 2003, 16:40
P.S. You should really subscribe to the mailing list if you want to work with cryptlib. EVERYONE needs lots of help trying to figure it out.

Clandestiny
October 29th, 2003, 00:32
Quote:
[Originally Posted by mike]P.S. You should really subscribe to the mailing list if you want to work with cryptlib. EVERYONE needs lots of help trying to figure it out.


You're not kidding there ! In case you can't tell, I'm about the newest newbie there is when it comes to crypto .

Thank you very much for the example code I really appreciate it. I finally got a key exchange working after so MUCH effort it's almost embarassing to admit . In that previous post, I was *really* heading down the wrong path.

Quote:
[Originally Posted by mike]Cryptlib expects you to use the high level interface rather than know how to do any particular algorithm.


Yeah, thats what I meant about heading down the wrong path... After finally thinking I had a *little* bit of understanding of Diffie Hellman key exchange, I was looking for actual functions that would load the prime # and base and generate each of the public and private keys. I guess I didn't quite understand that these things were "encapsulated" into higher level interfaces for key exchanging. It seems obvious now that I got it working, but I wandered around in the dark for quite a while about the mysteries of encryption contexts, certificates, and initialization vectors.

Thanks again
Clandestiny