CryptoSys KeyExchange is a library of primitive functions and methods to enable developers to build applications using Diffie-Hellman key agreement schemes and key establishment protocols. The library conforms to ANSI X9.42-2003 Agreement of Symmetric Keys Using Discrete Logarithm Cryptography [ANSX942]. The random number generator (RNG) uses techniques and recommendations from NIST Special Publication 800-90 Recommendation for Random Number Generation Using Deterministic Random Bit Generators [SP80090].
Key establishment schemes are used by two parties to establish common shared secret information like cryptographic keys. CryptoSys KeyExchange provides functions and methods for programmers in C, C++, Classic Visual Basic (VB6), VBA, COM, ASP, C# and VB.NET. The core executable has a small footprint under 150 kB. It works on all Windows® Win32 systems (W95/98/Me/NT4/2000/XP/2003).
[Contents]
[Contents]
[Contents]
[Contents]
Domain.EnforceStdLen or KX_ENFORCE_STDLEN option.[Contents]
For the record, the current implementation of DeriveKeyingData computes the value of
SHA-1(ZZ || Counter || OtherInfo)for successive values of
Counter = (0000000116, 0000000216, ...)
where Counter is represented as a 32-bit octet string in "big-endian" order.
The values of ZZ and, if provided, OtherInfo are digested directly as bit strings.
[Contents]
CryptoSysKX.chm available on the .NET methods and classes.
Only the DiffieHellman and kxDh classes require instantiation.
CAUTION: Do not use the GUI option Domain.ShowProgress or the "WithPrompt" methods with an ASP program
or in any other environment where dialogs or console windows may interfere with your application.
| .NET Class | COM Class | Description | Instantiate with New |
|---|---|---|---|
DiffieHellman | kxDh | Diffie-Hellman | Yes |
Rng | kxRng | Random number generator | No |
Hex | kxHex | Hexadecimal encoding and decoding | No |
General | kxGen | General utilities | No |
DiffieHellman and kxDh ClassesFalse value
together with a description in the ErrMsg property.
DiffieHellman and kxDh PropertiesP, Q, G) and validation parameters (Seed, pGenCounter)
can be set directly and then only by using the SetDomainParameters method.
The private and public key properties (X, Y) are only used by the GenerateKeyPair method.
The ErrMsg property is set if an error occurs.
Rng and kxRng ClassesHex and kxHex classesGeneral and kxGen ClassesDomain EnumerationPrngStrength Enumeration[Contents]
/* DIFFIE-HELLMAN FUNCTIONS */ nRet = KXDH_GenerateDomainParams(szdhP, szdhQ, szdhG, nMaxChars, nKeyBits, nOptions); nRet = KXDH_GenerateDomainParamsEx(szdhP, szdhQ, szdhG, szValidationSeed, nMaxChars, nKeyBits, nSeedBits, nOptions); nRet = KXDH_ValidateDomainParams(szdhP, szdhQ, szdhG, nBits, szValidationSeed, nPgenCounter, nOptions); nRet = KXDH_GenerateKeyPair(szdhPrivate, szdhPublic, nMaxChars, szdhP, szdhQ, szdhG, szUserEntropy, nOptions); nRet = KXDH_ValidatePublicKey(szdhPublic, szdhP, szdhQ, szdhG); nRet = KXDH_DeriveSharedSecret(szdhZ, nMaxChars, szdhPrivate, szdhPublic, szdhP, szdhQ, szdhG); nRet = KXDH_DeriveKeyingData(szdhKEK, nMaxChars, szdhZZ, nKeyBits, szOtherInfo); /* RANDOM NUMBER FUNCTIONS */ nRet = KX_GenerateRandomData(szOutput, nMaxChars, nBitsReq); nRet = KX_GenerateRandomDataEx(szOutput, nMaxChars, nBitsReq, szUserEntropy, nOptions); nRet = KX_PRNG_Initialize(szSeedFile, nOptions); nRet = KX_PRNG_MakeSeedFile(szSeedFile, szPrompt, nOptions); nRet = KX_PRNG_UpdateSeedFile(szSeedFile, nOptions); nRet = KX_PRNG_Test(szFileName, nOptions); /* HEX FUNCTIONS */ nRet = KX_HexEncode(szOutput, nMaxChars, lpData, nDataLen); nRet = KX_HexDecode(lpOutput, nOutLen, szHex); nRet = KX_HexFilter(szOutput, szInput, nChars); /* GENERAL FUNCTIONS */ nRet = KX_WipeString(szToBeWiped); nRet = KX_BitLength(szData); nRet = KX_ErrorLookup(szOutput, nMaxChars, nErrCode); nRet = KX_Version(); nRet = KX_CompileTime(szOutput, nMaxChars); nRet = KX_ModuleName(szOutput, nMaxChars, nReserved); nRet = KX_LicenceType(nReserved); nRet = KX_PowerUpTests(nReserved);
lpData).szOutput require the string to be
pre-dimensioned beforehand to at least size nMaxChars+1.nMaxChars+1
regardless of the final size of the actual output.KXDH_GenerateDomainParams returns zero to indicate success but
KXDH_GenerateDomainParamsEx returns a positive value of pgenCounter.)
diCrKX.h.
0 = OK, success, no error 101 = Parameter is wrong or missing 102 = Data is in wrong format 103 = Input is too short 104 = Invalid hex string 105 = Not enough room in output buffer 106 = Invalid size specified 110 = Unable to generate a prime 111 = Unable to find a value for g 112 = Parameter p is invalid 113 = Parameter q is invalid 114 = Parameter g is invalid 115 = Parameter pgenCounter is invalid 116 = Validation failed 120 = Public key y is invalid 199 = Algorithm failed 201 = Cannot open input file 202 = Cannot create output file 203 = File read error 204 = File write error 205 = File locking error 210 = PRNG: Uninstantiation failed 211 = PRNG: Requested length is too large 212 = PRNG: Function failed 213 = PRNG: Invalid input parameter 214 = PRNG: Function is not available 299 = PRNG: Catastrophic failure
[Contents]
setup.exe program provided with the distribution.
This installs the core Win32 DLL in your main windows system directory and creates copies of all other required files
including this manual in C:\Program Files\CryptoSysKX.
Instructions for distributing to end users with your applications are provided separately with the Licensed version
in the file distrib.txt.
To uninstall, use Start > Settings > Control Panel > Add/Remove Programs > CryptoSys KeyExchange to remove the application.
[Contents]
diCrKX.dll which is a simple Win32 DLL. This file must
exist on the user's system for all programming language interfaces.
The file diCrKX.dll is not registered with RegSvr32 (it can't be).
To install, just copy to a directory on the user's system in the library search path.
This can be the same directory as the executable that is calling it, the current directory,
or a directory in the PATH environment setting,
but the easiest place is the
%windows%\system32 directory which will allow it to be found by anything.
For more information on the library search path,
refer to the Windows documentation under "SearchPath" or "LoadLibrary".
diCrSysKXCom.dll is an ActiveX DLL which exposes various classes to allow
programming access from ASP, VBscript and other COM applications including VB6.
This DLL does require registering with regsvr32.exe.
The setup procedure registers this by default.
diCrSysKXNet.dll is a .NET Class Library which exposes various classes
to allow programming access from C# and VB.NET projects.
This file is referenced from your .NET project. It is not registered.
| Language | diCrKX.dll | diCrSysKXCom.dll | diCrSysKXNet.dll |
|---|---|---|---|
| C/C++ | Yes | No | No |
| VB6/VBA | Yes | Optional | No |
| ASP/VBScript | Yes | Yes | No |
| C# | Yes | No | Yes |
| VB.NET | Yes | No | Yes |
| Registered? | No | Yes | No |
[Contents]
diCrKX.h in your source code and link to diCrKX.lib.
#include "diCrKX.h"The C interface accesses the functions in the core DLL directly and only requires that the Win32
diCrKX.dll file exists in the system's library search path.
See the examples in diCrKX_Example.c and the core function list below.
The diCrKX.lib library provided was compiled
using MSVC++5 and has been tested with versions 5, 6, 7, and 8 (2005 Express) of Visual C++.
implib diCrKX.lib diCrKX.dll(note the order of the parameters for this command - get it wrong and it destroys your DLL!). The example code has been successfully tested with Borland C++ 5.5.1 for Win32.
/* kx_min.c */
#include <stdio.h>
#include "diCrKX.h"
// Compiler-specific explicit link to library
// This works in MSVC and Borland for LIB in same dir
#pragma comment(lib, ".\\diCrKX.lib")
int main(void)
{
long ver;
ver = KX_Version();
printf("Version=%03ld\n", ver);
return 0;
}
[Contents]
diCrSysKXNet.dll library file into a convenient folder.diCrSysKXNet.dll.using CryptoSysKX;or (for VB.NET)
Imports CryptoSysKXto your code.
CryptoSysKX.cs in your project
and there is no need to reference the class library.
using System;
using CryptoSysKX;
namespace KX_min
{
class KX_version
{
[STAThread]
static void Main(string[] args)
{
int n;
string s;
n = General.Version();
Console.WriteLine("Version = {0}", n);
s = General.CompileTime();
Console.WriteLine("CompileTime={0}", s);
s = General.ModuleName();
Console.WriteLine("ModuleName={0}", s);
}
}
}
Imports System
Imports CryptoSysKX
Module KX_min
Sub Main(ByVal args() As String)
Dim n As Integer
Dim s As String
n = General.Version()
Console.WriteLine("Version = {0}", n)
s = General.CompileTime()
Console.WriteLine("CompileTime={0}", s)
s = General.ModuleName()
Console.WriteLine("ModuleName={0}", s)
End Sub
End Module
[Contents]
diCrSysKXCom.dll ActiveX DLL (Project>References>Browse).basCrKX.bas in your project
or (b) make a reference to diCrKX.tbl in your project (but not both).
These files are in the zipped COM source.
String(nChars, " ") function. See the source code of diCrSysKXCom for examples
and the list of core functions below.
diCrSysKXCom.dll:
Public Sub Test_kxGen_version()
' Display the version number, etc
Dim oGen As New kxGen
Debug.Print "Version = " & oGen.Version
Debug.Print "CompileTime = " & oGen.CompileTime
Debug.Print "ModuleName = " & oGen.ModuleName
Debug.Print "LicenceType = " & oGen.LicenceType
End Sub
[Contents]
Dim oGen
Set oGen = Server.CreateObject("diCrSysKXCom.kxGen")
Response.Write "Version=" & oGen.Version & Chr(13) & Chr(10)
Response.Write "Module Name=" & oGen.ModuleName & Chr(13) & Chr(10)
Response.Write "Compiled=" & oGen.CompileTime & Chr(13) & Chr(10)
[Contents]
Because of the different environments in which this library will be used, we are unable to set up, say, a permanent keystroke timer logger on the end-user's machine or have a GenerateData function that just sits there until it's gathered enough entropy to meet a particular security requirement.
The underlying RNG (or PRNG) functions use the algorithms recommended in NIST SP 800-90 [SP80090] (the "DRBG Standard") to provide a Deterministic Random Bit Generator (DRBG). This outputs a sequence of binary bits that appears to be statistically independent and unbiased. The output is effectively random so long as internal actions of the process are hidden from observation. In particular the algorithm provides good Backtracking Resistance and, depending how it is used, Prediction Resistance.
Entropy is accumulated at startup and whenever any function in the library is called. We use the "Fortuna" method of pooling to prevent certain attacks from someone who controls some but not all of the entropy sources (see chapter 10 of Ferguson and Schneier, Practical Cryptography, [FERG03]). The more times your application calls the functions in the library before needing some random data, the more entropy will be accumulated.
However, if the main algorithm
requires new entropy data before sufficient entropy is available then, rather
than failing and returning a "catastrophic" error as specified by the DRBG Standard,
our algorithm uses whatever data is available in the relevant pools. This is
similar to the behaviour of the dev/urandom facility in Linux.
This means that the security strength of the output might be anything from zero to the design limit of 128 bits. In the absence of further action by the programmer as described below, you would have to assume the worst case of zero bits.
We strongly recommend that you use and initialize with a seed file wherever possible.
User-supplied entropy is added as "additional input" to the generation process (see the DRBG Standard). It does not affect the accumulation pools and cannot be used by an attacker to control the output.
Remember it's not how "random" your user-supplied entropy is, but how little an attacker knows about it. Using the current time is useless. If you can provide 32 bytes of data of which an attacker knows nothing and cannot later discover, then you have added 128 bits of security strength.
Highest supported security strength = 128 Output block length (outlen) = 160 bits Maximum number of bits per request = 2^19 bits (i.e. 65536 bytes) Reseed interval = 1000000 or as dictated by the Fortuna algorithm Maximum length of personalization string = 160 bits Maximum length of entropy input = 5120 bits (32 pools x 160 bits)
In Windows®, each thread has its own Generator in Thread Local Storage. Each process has one Accumulator accessed by all Generators and protected by a Critical Section when accessed. The accumulator has 32 "Fortuna" accumulation pools with the minimum pool size before a reseed set to 32 bytes.
The "personalization string" used on instantiation in each thread is derived by hashing the current time, process ID, thread ID, user name and computer name, and so is almost certain to be unique. The "instantiation nonce" is a 32-bit value derived from the current time and process ID which is incremented on the instantiation of any new Generator in a different thread.
In the absolute worst case, if no seed file is used and an attacker can call the GenerateRandomData function before any additional entropy has been generated by the system, the output effectively reduces to a "strong" hash of the current time and personalization string with good backtracking protection. Note that the output will always pass a FIPS-140-2 statistical test because the DRBG_HMAC algorithm used will always ensure that the output of successive requests look random, even given knowledge and control over the inputs.
Use MakeSeedFile to create a fresh seed file on a new installation.
This uses a dialog window and expects the user to type in random keystrokes.
Such a GUI interface may not be appropriate in all circumstances.
Alternatively, use the command-line program MkRngSeed.exe included in the distribution
to create a seed file.
[Contents]
diCrKX.dll. It is intended to meet FIPS 140-2 security level 1.
The cryptographic boundary for CryptoSys KeyExchange
is defined as the enclosure of the computer on which the cryptographic module is installed.
As a pure software product, CryptoSys KeyExchange provides no physical security by itself.
The computer itself must be appropriately physically secured.
[Contents]
In addition to this automatic software integrity test, the integrity of the entire DLL file can be independently verified by the user using published SHA-1 and MD5 message digest and CRC-32 values before and after installation.
Rng.Test and General.PowerUpTests methods
(or the KX_PRNG_Test and KX_PowerUpTests functions).
The health check performs self-tests to obtain assurance that the DRBG
continues to operate as designed and implemented according to section 11.3 of [SP80090]
including Known Answer Testing, Testing the Instantiate Function, Testing the Generate Function,
Testing the Reseed Function and Testing the Uninstantiate Function.
* The error log file will be given a filename "kxerr.log". If the process does not have permissions to write to that directory, no log file be created.
You can make settings in the machine's registry to prevent the message box displaying and to change the destination directory of the log file. See Optional Registry Settings. It is not possible to prevent the DLL from exiting if a critical error happens.
The user may call the power-up self-tests on demand with the KX_PowerUpTests
function or General.PowerUpTests method.
In the event that such an "on demand" test fails, the module will return an error
code but will not terminate the process.
Note that the automatic self-tests fail only in exceptional circumstances. You should never see one in practice unless the software module has been tampered with.
[Contents]
Disclaimer Modifying the registry can cause serious problems that may require you to reinstall your operating system. We cannot guarantee that problems resulting from the incorrect use of the registry can be solved. Use the information provided at your own risk.
[HKEY_LOCAL_MACHINE\Software\DI Management\CryptoSysKX\Options]NoMessageBox[HKEY_LOCAL_MACHINE\Software\DI Management\CryptoSysKX\Options]ErrorLogDir[HKEY_LOCAL_MACHINE\Software\DI Management\CryptoSysKX\Options]NoErrorLogThe description for Event ID ( 8xxx ) in Source ( diCrKX ) cannot be found. The local computer may not have the necessary registry information or message DLL files to display messages from a remote computer.For correct formatting of the message, create the REG_SZ and REG_DWORD values in the key below. The message will still be recorded even if this entry is not present.
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Application\diCrKX]EventMessageFileTypesSupported[Contents]
[Contents]
The source code used in CryptoSys KeyExchange is original code written by David Ireland except for the following:
[Contents]
We use a key length of 512 bits here just for speed in the demo. Remember that this is not a valid
length for strict X9.42 requirements, so don't use the Domain.EnforceStdLength
option to test this.
Note also that generating new domain parameters takes time and is usually a one-off event - i.e. one set of domain parameters is generated and distributed to all parties who then use the same domain parameters for each new set of keys. We just include the operation here to demonstrate its use.
#include <stdio.h>
#include <assert.h>
#include "diCrKX.h"
/* Test size for prime modulus (NB 512 is non-standard for X9.42!) */
#define PBITS 512
#define PCHARS ((PBITS+7)/8)*2
#define PBYTES (PCHARS/2)
/* Required size for key material */
#define KBITS 128
#define KCHARS ((KBITS+7)/8)*2
/* GLOBALS - we use these for variables meant to be transmitted between the parties */
/* Domain parameters */
char g_P[PCHARS+1], g_Q[PCHARS+1], g_G[PCHARS+1];
/* Validation parameters */
char g_ValidationSeed[PCHARS+1];
long g_pgenCounter;
/* Public keys */
char g_Ya[PCHARS+1], g_Yb[PCHARS+1];
void full_exchange(void)
{
/* This example demonstrates an exchange of data between two parties, Alice
and Bob. Here we use global variables to "pass" data between the two parties
and we pretend that the two sets of "local" variables are really private.
In practice Alice's and Bob's actions would be carried out on separate machines.
*/
/* ALICE'S LOCAL VARIABLES */
long ra;
char szdhXa[PCHARS+1];
char szdhZa[PCHARS+1];
char szdhKa[KCHARS+1];
/* BOB'S LOCAL VARIABLES */
long rb;
char szdhXb[PCHARS+1];
char szdhZb[PCHARS+1];
char szdhKb[KCHARS+1];
/* ALICE generates a set of Domain Parameters (this could be done by a 3rd party) */
printf("Generating Domain Parameters...\n");
ra = KXDH_GenerateDomainParamsEx(g_P, g_Q, g_G, g_ValidationSeed, PCHARS, PBITS, 160, KX_SHOW_PROGRESS);
assert(ra > 0);
g_pgenCounter = ra;
printf("Domain Parameters:\n");
printf("p=%s\nq=%s\ng=%s\n", g_P, g_Q, g_G);
printf("Validation Parameters:\n");
printf("seed=%s\npgenCounter=%ld\n", g_ValidationSeed, g_pgenCounter);
/* ALICE carries out a quick check */
ra = KXDH_ValidateDomainParams(g_P, g_Q, g_G, PBITS, "", 0, KX_DEFAULT);
if (ra != 0) fatal("Failed to validate domain parameters.");
printf("Alice has checked the domain parameters, OK.\n");
/* ALICE transmits (p,q,g, seed, pgenCounter) to Bob... */
/* BOB validates (properly) the domain parameters he receives */
printf("Validating Domain Parameters...\n");
rb = KXDH_ValidateDomainParams(g_P, g_Q, g_G, PBITS, g_ValidationSeed, g_pgenCounter, KX_SHOW_PROGRESS);
if (rb != 0) fatal("Failed to validate domain parameters.");
printf("Bob has validated the domain parameters, OK.\n");
/* ALICE generates a random private/public key pair */
ra = KXDH_GenerateKeyPair(szdhXa, g_Ya, PCHARS, g_P, g_Q, g_G, "", KX_DEFAULT);
assert(ra == 0);
printf("Alice's private/public key pair:\n");
printf("Xa=%s\nYa=%s\n", szdhXa, g_Ya);
/* ALICE transmits her public key Ya to Bob but keeps her private key Xa secret... */
/* BOB validates Alice's public key, Ya */
rb = KXDH_ValidatePublicKey(g_Ya, g_P, g_Q, g_G);
if (rb != 0) fatal("Alice's public key is not valid.");
printf("Bob has validated Alice's public key, OK.\n");
/* BOB generates a random private/public key pair */
rb = KXDH_GenerateKeyPair(szdhXb, g_Yb, PCHARS, g_P, g_Q, g_G, "", KX_DEFAULT);
assert(rb == 0);
printf("Bob's private/public key pair:\n");
printf("Xb=%s\nYb=%s\n", szdhXb, g_Yb);
/* BOB transmits his public key Yb to Alice but keeps his private key Xb secret... */
/* ALICE validates Bob's public key, Yb */
ra = KXDH_ValidatePublicKey(g_Yb, g_P, g_Q, g_G);
if (ra != 0) fatal("Bob's public key is not valid.");
printf("Alice has validated Bob's public key, OK.\n");
/* Now that Alice and Bob have exchanged their public keys, they can both
generate their own copies of the shared secret and keying material */
/* ALICE computes the shared secret */
ra = KXDH_DeriveSharedSecret(szdhZa, PCHARS, szdhXa, g_Yb, g_P, g_Q, g_G);
assert(ra == 0);
printf("Shared secret as computed by Alice, Za=\n%s\n", szdhZa);
/* ALICE computes a 128-bit AES key shared with Bob */
ra = KXDH_DeriveKeyingData(szdhKa, KCHARS, szdhZa, KBITS, NULL);
assert(ra > 0);
printf("128-bit key as computed by Alice, Ka=%s\n", szdhKa);
/* BOB computes the shared secret */
rb = KXDH_DeriveSharedSecret(szdhZb, PCHARS, szdhXb, g_Ya, g_P, g_Q, g_G);
assert(rb == 0);
printf("Shared secret as computed by Bob, Zb=\n%s\n", szdhZb);
/* BOB computes a 128-bit AES key shared with Alice */
rb = KXDH_DeriveKeyingData(szdhKb, KCHARS, szdhZb, KBITS, NULL);
assert(rb > 0);
printf("128-bit key as computed by Bob, Kb=%s\n", szdhKb);
/*An omniscient entity checks they have the same values */
assert(strcmp(szdhZa, szdhZb) == 0);
assert(strcmp(szdhKa, szdhKb) == 0);
/* ALICE clears sensitive data */
KX_WipeString(szdhKa);
KX_WipeString(szdhZa);
KX_WipeString(szdhXa);
/* BOB clears sensitive data */
KX_WipeString(szdhKb);
KX_WipeString(szdhZb);
KX_WipeString(szdhXb);
}
//**************************
// EXAMPLE OF FULL EXCHANGE
//**************************
// Use global variables to "transmit" data between the parties
// Domain parameters
static string g_dhP, g_dhQ, g_dhG;
// Validation parameters
static string g_dhSeed;
static int g_dhCounter;
// Public keys
static string g_dhYa, g_dhYb;
static void Full_Exchange()
{
int nbits = 512; // We use non-standard 512 for speed in this demo
// ALICE's PRIVATE VARIABLES
bool oka;
string dhZa, dhKa;
DiffieHellman oDHa = new DiffieHellman();
// BOB'S PRIVATE VARIABLES
bool okb;
string dhZb, dhKb;
DiffieHellman oDHb = new DiffieHellman();
Console.WriteLine("FULL KEY EXCHANGE:");
// ALICE generates a set of domain parameters (this could be done by a 3rd party)
Console.WriteLine("Generating domain parameters...");
oka = oDHa.GenerateDomainParams(nbits, Domain.Default);
Debug.Assert(oka, "GenerateDomainParams failed");
Console.WriteLine("p={0}", oDHa.P);
Console.WriteLine("q={0}", oDHa.Q);
Console.WriteLine("g={0}", oDHa.G);
Console.WriteLine("seed={0}", oDHa.Seed);
Console.WriteLine("pgenCounter={0}", oDHa.pgenCounter);
// ALICE does a quick check
oka = oDHa.IsValidDomainParamsQuick(Domain.Default);
Console.WriteLine("IsValidDomainParamsQuick returns {0}", (oka ? "Valid" : "INVALID"));
Debug.Assert(oka, "IsValidDomainParamsQuick failed");
// ALICE sends these parameters to BOB
g_dhP = oDHa.P;
g_dhQ = oDHa.Q;
g_dhG = oDHa.G;
g_dhSeed = oDHa.Seed;
g_dhCounter = oDHa.pgenCounter;
// (transmit...)
// BOB receives them and validates them
oDHb.SetDomainParams(g_dhP, g_dhQ, g_dhG, g_dhSeed, g_dhCounter);
okb = oDHb.IsValidDomainParams(Domain.ShowProgress);
Debug.Assert(okb, "IsValidDomainParams failed");
Console.WriteLine("OK, Bob has validated the domain parameters.");
// ALICE generates a random private/public key pair
oka = oDHa.GenerateKeyPair();
Console.WriteLine("GenerateKeyPair returns {0}", (oka ? "Success" : "FAILURE"));
Debug.Assert(oka, "GenerateKeyPair failed");
Console.WriteLine("Alice's private public key pair:\nXa={0}\nYa={1}", oDHa.X, oDHa.Y);
// ALICE sends her public key Ya to BOB but keeps her private key Xa secret
g_dhYa = oDHa.Y;
// (transmit...)
// BOB receives ALICE's public key and validates it
okb = oDHb.IsValidPublicKey(g_dhYa);
Console.WriteLine("IsValidPublicKey returns {0}", (okb ? "Valid" : "INVALID"));
Debug.Assert(okb, "IsValidPublicKey failed");
Console.WriteLine("OK, Bob has validated Alice's public key");
// BOB generates a random private/public key pair
okb = oDHb.GenerateKeyPair();
Console.WriteLine("GenerateKeyPair returns {0}", (okb ? "Success" : "FAILURE"));
Debug.Assert(okb, "GenerateKeyPair failed");
Console.WriteLine("Bob's private public key pair:\nXb={0}\nYb={1}", oDHb.X, oDHb.Y);
// BOB sends his public key Yb to ALICE but keeps his private key Xb secret
g_dhYb = oDHb.Y;
// (transmit...)
// ALICE receives BOB's public key and validates it
oka = oDHa.IsValidPublicKey(g_dhYb);
Console.WriteLine("IsValidPublicKey returns {0}", (oka ? "Valid" : "INVALID"));
Debug.Assert(oka, "IsValidPublicKey failed");
Console.WriteLine("OK, Alice has validated Bob's public key");
// Now that ALICE and BOB have exchanged their public keys, they can both
// generate their own private copies of the shared secret and keying material.
// In this case, they do not use OtherInfo.
// ALICE computes the shared secret
dhZa = oDHa.DeriveSharedSecret(oDHa.X, g_dhYb);
Debug.Assert(dhZa.Length > 0, "Failed to derive shared secret");
Console.WriteLine("Shared secret as computed by Alice,\nZa={0}", dhZa);
// ALICE computes a 128-bit key to be used to encrypt data with BOB
// using the `dhStatic' key agreement scheme (ANSI X9.42-2003 8.1.1)
dhKa = oDHa.DeriveKeyingData(dhZa, 128, null);
Debug.Assert(dhKa.Length > 0, "Failed to derive keying data");
Console.WriteLine("128-bit key as computed by Alice,\nKa={0}", dhKa);
// BOB computes the shared secret
dhZb = oDHb.DeriveSharedSecret(oDHb.X, g_dhYa);
Debug.Assert(dhZb.Length > 0, "Failed to derive shared secret");
Console.WriteLine("Shared secret as computed by Bob,\nZb={0}", dhZb);
// BOB computes a 128-bit key to be used to encrypt data with ALICE
dhKb = oDHb.DeriveKeyingData(dhZb, 128, null);
Debug.Assert(dhKb.Length > 0, "Failed to derive keying data");
Console.WriteLine("128-bit key as computed by Bob,\nKb={0}", dhKb);
// An `omniscient entity' checks they have the same values
Debug.Assert(String.Compare(dhZa, dhZb, true)==0, "Shared secret does not match");
Debug.Assert(String.Compare(dhKa, dhKb, true)==0, "Keying data does not match");
Console.WriteLine("OK, shared secrets and keying material matches for Alice and Bob.");
// ALICE and BOB clear their private keys and other DiffieHellman data
oDHa.Clear();
oDHb.Clear();
}
Public Sub Test_Full_Exchange() ' Model the full exchange of a secret key between two parties, Alice and Bob, ' including the generation of a new set of domain parameters ' We use "global" variables for data to be "transmitted" between the parties ' Domain parameters Dim g_dhP As String Dim g_dhQ As String Dim g_dhG As String ' Validation parameters Dim g_Seed As String Dim g_Counter As Long ' Public keys Dim g_dhYa As String Dim g_dhYb As String ' ALICE's local "private" variables Dim oDHa As New kxDh Dim IsOkA As Boolean Dim dhzA As String Dim dhkA As String ' BOB's local "private" variables Dim oDHb As New kxDh Dim IsOkB As Boolean Dim dhzB As String Dim dhkB As String ' We use a 512-bit key for speed in this demo ' (NB this is not a X9.42 standard length) Const nKeyBits As Long = 512 ' ALICE generates a new set of Domain Parameters (this could be done by a 3rd party) Debug.Print "Generating new domain parameters..." Debug.Print oDHa.GenerateDomainParams(nKeyBits) Debug.Print "L=" & oDHa.KeyBits Debug.Print "p=" & oDHa.P Debug.Print "q=" & oDHa.Q Debug.Print "g=" & oDHa.G Debug.Print "seed=" & oDHa.Seed Debug.Print "pgenCounter=" & oDHa.PgenCounter ' ALICE does a quick check Debug.Print "Quick validation..." IsOkA = oDHa.IsValidDomainParamsQuick() Debug.Print "IsValidDomainParamsQuick returns " & IsOkA Debug.Assert IsOkA ' ALICE sends these to BOB g_dhP = oDHa.P g_dhQ = oDHa.Q g_dhG = oDHa.G g_Seed = oDHa.Seed g_Counter = oDHa.PgenCounter ' (transmit...) ' BOB receives these and validates them Call oDHb.SetDomainParams(g_dhP, g_dhQ, g_dhG, g_Seed, g_Counter) Debug.Print "Validating domain parameters..." IsOkB = oDHb.IsValidDomainParams() Debug.Print "IsValidDomainParamsQuick returns " & IsOkB Debug.Assert IsOkB ' ALICE generates a fresh private/public key pair (xA, yA) Debug.Print "Alice generates a new key pair..." IsOkA = oDHa.GenerateKeyPair() Debug.Print "GenerateKeyPair returns " & IsOkA Debug.Assert IsOkA Debug.Print "xA=" & oDHa.X Debug.Print "yA=" & oDHa.Y ' ALICE sends her public key to BOB g_dhYa = oDHa.Y ' (transmit...) ' BOB receives ALICE's public key and validates it Debug.Print "Bob validates Alice's public key..." IsOkB = oDHb.IsValidPublicKey(g_dhYa) Debug.Print "Alices's public key is " & IIf(IsOkB, "Valid", "INVALID!") Debug.Assert IsOkB ' BOB generates a fresh private/public key pair (xB, yB) Debug.Print "Bob generates a new key pair..." IsOkB = oDHb.GenerateKeyPair() Debug.Print "GenerateKeyPair returns " & IsOkB Debug.Assert IsOkB Debug.Print "xB=" & oDHb.X Debug.Print "yB=" & oDHb.Y ' BOB sends his public key to ALICE g_dhYb = oDHb.Y ' (transmit...) ' ALICE receives BOB's public key and validates it Debug.Print "Alice validates Bob's public key..." IsOkA = oDHa.IsValidPublicKey(g_dhYb) Debug.Print "Bob's public key is " & IIf(IsOkA, "Valid", "INVALID!") Debug.Assert IsOkA ' Now that ALICE and BOB have exchanged their public keys, ' they can both generate their own private copies of the shared secret ' and keying data. In this case, they do not use `OtherInfo' ' ALICE derives the shared secret Z given BOB's public key (yB) and her secret key (xA) dhzA = oDHa.DeriveSharedSecret(oDHa.X, g_dhYb) Debug.Print "Alices derives shared secret = " & dhzA Debug.Assert Len(dhzA) > 0 ' BOB derives the shared secret Z given ALICE's public key (yA) and his secret key (xB) dhzB = oDHb.DeriveSharedSecret(oDHb.X, g_dhYa) Debug.Print "Bob derives shared secret = " & dhzB Debug.Assert Len(dhzB) > 0 ' ALICE derives 128 bits of keying data from the shared secret ' using the dhStatic Key Agreement method (ZZ = Z) dhkA = oDHa.DeriveKeyingData(dhzA, 128, "") Debug.Print "Alice's KeyingData = " & dhkA Debug.Assert Len(dhkA) > 0 ' BOB derives 128 bits of keying data from the shared secret ' using the dhStatic Key Agreement method (ZZ = Z) dhkB = oDHb.DeriveKeyingData(dhzB, 128, "") Debug.Print "Bob's KeyingData = " & dhkB Debug.Assert Len(dhkB) > 0 ' An "omniscient entity" confirms they have the same values Debug.Assert (dhzA = dhzB) Debug.Assert (dhkA = dhkB) Debug.Print "OK, shared secrets and keying material match for Alice and Bob" ' ALICE and BOB clear their private keys and other D-H data oDHa.Clear oDHb.Clear End Sub
[Contents]
Version 1.0.0. Released 19 February 2007. Beta Test Version 0.8.0. First published 25 December 2006 by DI Management Services Pty Limited.
[Contents]
Copyright © 2006-7 D.I. Management Services Pty Limited ABN 78 083 210 584,
Sydney, Australia.
All rights reserved.
<www.cryptosys.net>
<www.di-mgt.com.au>