From 8f203108d91b3aef5a379a6d49f6cb2a546eebbc Mon Sep 17 00:00:00 2001 From: simon Date: Fri, 24 Mar 2000 09:45:49 +0000 Subject: [PATCH] Peter Schellenbach's patch: re-implement the PuTTY cryptographic functions as calls to the MS Crypto API. Not integrated into the Makefile yet, but should eventually allow building of an SSH-enabled PuTTY which contains no native crypto code, so it can be used everywhere (and anyone who can get the MS encryption pack can still use the SSH parts). git-svn-id: svn://svn.tartarus.org/sgt/putty@425 cda61777-01e9-0310-a592-d414129be87e --- mscrypto.c | 424 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ putty.h | 8 ++ scp.c | 3 + scp.h | 8 ++ scpssh.c | 21 ++- ssh.c | 21 ++- ssh.h | 17 ++- window.c | 6 +- 8 files changed, 502 insertions(+), 6 deletions(-) create mode 100644 mscrypto.c diff --git a/mscrypto.c b/mscrypto.c new file mode 100644 index 00000000..e477781c --- /dev/null +++ b/mscrypto.c @@ -0,0 +1,424 @@ +#define _WIN32_WINNT 0x0400 +#include +#include +#include +#include +#include "ssh.h" + +void fatalbox(char *fmt, ...); + +static HCRYPTKEY create_des_key(unsigned char *key); + + +HCRYPTPROV hCryptProv; +HCRYPTKEY hDESKey[2][3] = {{0,0,0},{0,0,0}}; /* global for now */ + + +/* use Microsoft Enhanced Cryptographic Service Provider */ +#define CSP MS_ENHANCED_PROV + + +static BYTE PrivateKeyWithExponentOfOne[] = +{ + 0x07, 0x02, 0x00, 0x00, 0x00, 0xA4, 0x00, 0x00, + 0x52, 0x53, 0x41, 0x32, 0x00, 0x02, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xAB, 0xEF, 0xFA, 0xC6, + 0x7D, 0xE8, 0xDE, 0xFB, 0x68, 0x38, 0x09, 0x92, + 0xD9, 0x42, 0x7E, 0x6B, 0x89, 0x9E, 0x21, 0xD7, + 0x52, 0x1C, 0x99, 0x3C, 0x17, 0x48, 0x4E, 0x3A, + 0x44, 0x02, 0xF2, 0xFA, 0x74, 0x57, 0xDA, 0xE4, + 0xD3, 0xC0, 0x35, 0x67, 0xFA, 0x6E, 0xDF, 0x78, + 0x4C, 0x75, 0x35, 0x1C, 0xA0, 0x74, 0x49, 0xE3, + 0x20, 0x13, 0x71, 0x35, 0x65, 0xDF, 0x12, 0x20, + 0xF5, 0xF5, 0xF5, 0xC1, 0xED, 0x5C, 0x91, 0x36, + 0x75, 0xB0, 0xA9, 0x9C, 0x04, 0xDB, 0x0C, 0x8C, + 0xBF, 0x99, 0x75, 0x13, 0x7E, 0x87, 0x80, 0x4B, + 0x71, 0x94, 0xB8, 0x00, 0xA0, 0x7D, 0xB7, 0x53, + 0xDD, 0x20, 0x63, 0xEE, 0xF7, 0x83, 0x41, 0xFE, + 0x16, 0xA7, 0x6E, 0xDF, 0x21, 0x7D, 0x76, 0xC0, + 0x85, 0xD5, 0x65, 0x7F, 0x00, 0x23, 0x57, 0x45, + 0x52, 0x02, 0x9D, 0xEA, 0x69, 0xAC, 0x1F, 0xFD, + 0x3F, 0x8C, 0x4A, 0xD0, + + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x64, 0xD5, 0xAA, 0xB1, + 0xA6, 0x03, 0x18, 0x92, 0x03, 0xAA, 0x31, 0x2E, + 0x48, 0x4B, 0x65, 0x20, 0x99, 0xCD, 0xC6, 0x0C, + 0x15, 0x0C, 0xBF, 0x3E, 0xFF, 0x78, 0x95, 0x67, + 0xB1, 0x74, 0x5B, 0x60, + + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + + + +/* ---------------------------------------------------------* + * Utility functions * + * ---------------------------------------------------------*/ + + +int crypto_startup() { + if(CryptAcquireContext(&hCryptProv, "Putty", CSP, PROV_RSA_FULL, + CRYPT_NEWKEYSET) == 0) { + if(GetLastError() == NTE_EXISTS) { + if(CryptAcquireContext(&hCryptProv, "Putty", CSP, + PROV_RSA_FULL, 0) == 0) { + return FALSE; /* failed to acquire context - probably + * don't have high encryption installed! */ + } + } else + return FALSE; /* failed to acquire context - probably + * don't have high encryption installed! */ + } + return TRUE; +} + + +void crypto_wrapup() { + int i, j; + for(i=0; i<2; i++) { + for(j=0; j<3; j++) { + if(hDESKey[i][j]) + CryptDestroyKey(hDESKey[i][j]); + hDESKey[i][j] = 0; + } + } + if(hCryptProv) + CryptReleaseContext(hCryptProv, 0); + hCryptProv = 0; +} + + +/* ---------------------------------------------------------* + * Random number functions * + * ---------------------------------------------------------*/ + +int random_byte(void) { + unsigned char b; + if(!CryptGenRandom(hCryptProv, 1, &b)) + fatalbox("random number generator failure!"); + return b; +} + +void random_add_noise(void *noise, int length) { + /* do nothing */ +} +void random_init(void) { + /* do nothing */ +} +void random_get_savedata(void **data, int *len) { + /* do nothing */ +} +void noise_get_heavy(void (*func) (void *, int)) { + /* do nothing */ +} +void noise_get_light(void (*func) (void *, int)) { + /* do nothing */ +} +void noise_ultralight(DWORD data) { + /* do nothing */ +} +void random_save_seed(void) { + /* do nothing */ +} + + +/* ---------------------------------------------------------* + * MD5 hash functions * + * ---------------------------------------------------------*/ + + +void MD5Init(struct MD5Context *ctx) { + if(!CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &ctx->hHash)) + fatalbox("Error during CryptBeginHash!\n"); +} + + +void MD5Update(struct MD5Context *ctx, + unsigned char const *buf, unsigned len) { + if(CryptHashData(ctx->hHash, buf, len, 0) == 0) + fatalbox("Error during CryptHashSessionKey!\n"); +} + + +void MD5Final(unsigned char digest[16], struct MD5Context *ctx) { + DWORD cb = 16; + if(CryptGetHashParam(ctx->hHash, HP_HASHVAL, digest, &cb, 0) == 0) + fatalbox("Error during CryptGetHashParam!\n"); + if(ctx->hHash) + CryptDestroyHash(ctx->hHash); + ctx->hHash = 0; +} + + +/* ---------------------------------------------------------* + * RSA public key functions * + * ---------------------------------------------------------*/ + +int makekey(unsigned char *data, struct RSAKey *result, + unsigned char **keystr) { + + unsigned char *p = data; + int i; + int w, b; + + /* get size (bits) of modulus */ + result->bits = 0; + for(i=0; i<4; i++) + result->bits = (result->bits << 8) + *p++; + + /* get size (bits) of public exponent */ + w = 0; + for (i=0; i<2; i++) + w = (w << 8) + *p++; + b = (w+7)/8; /* bits -> bytes */ + + /* convert exponent to DWORD */ + result->exponent = 0; + for (i=0; iexponent = (result->exponent << 8) + *p++; + + /* get size (bits) of modulus */ + w = 0; + for (i=0; i<2; i++) + w = (w << 8) + *p++; + result->bytes = b = (w+7)/8; /* bits -> bytes */ + + /* allocate buffer for modulus & copy it */ + result->modulus = malloc(b); + memcpy(result->modulus, p, b); + + /* update callers pointer */ + if (keystr) *keystr = p; /* point at key string, second time */ + + return (p - data) + b; +} + + +void rsaencrypt(unsigned char *data, int length, struct RSAKey *rsakey) { + + int i; + unsigned char *pKeybuf, *pKeyin; + HCRYPTKEY hRsaKey; + PUBLICKEYSTRUC *pBlob; + RSAPUBKEY *pRPK; + unsigned char *buf; + DWORD dlen; + DWORD bufsize; + + /* allocate buffer for public key blob */ + if((pBlob = malloc(sizeof(PUBLICKEYSTRUC) + sizeof(RSAPUBKEY) + + rsakey->bytes)) == NULL) + fatalbox("Out of memory"); + + /* allocate buffer for message encryption block */ + bufsize = (length + rsakey->bytes) << 1; + if((buf = malloc(bufsize)) == NULL) + fatalbox("Out of memory"); + + /* construct public key blob from host public key */ + pKeybuf = ((unsigned char*)pBlob) + sizeof(PUBLICKEYSTRUC) + + sizeof(RSAPUBKEY); + pKeyin = ((unsigned char*)rsakey->modulus); + /* change big endian to little endian */ + for(i=0; ibytes; i++) + pKeybuf[i] = pKeyin[rsakey->bytes-i-1]; + pBlob->bType = PUBLICKEYBLOB; + pBlob->bVersion = 0x02; + pBlob->reserved = 0; + pBlob->aiKeyAlg = CALG_RSA_KEYX; + pRPK = (RSAPUBKEY*)(((unsigned char*)pBlob) + sizeof(PUBLICKEYSTRUC)); + pRPK->magic = 0x31415352; /* "RSA1" */ + pRPK->bitlen = rsakey->bits; + pRPK->pubexp = rsakey->exponent; + + /* import public key blob into key container */ + if(CryptImportKey(hCryptProv, (void*)pBlob, + sizeof(PUBLICKEYSTRUC)+sizeof(RSAPUBKEY)+rsakey->bytes, + 0, 0, &hRsaKey) == 0) + fatalbox("Error importing RSA key!"); + + /* copy message into buffer */ + memcpy(buf, data, length); + dlen = length; + + /* using host public key, encrypt the message */ + if(CryptEncrypt(hRsaKey, 0, TRUE, 0, buf, &dlen, bufsize) == 0) + fatalbox("Error encrypting using RSA key!"); + + /* + * For some strange reason, Microsoft CryptEncrypt using public + * key, returns the cyphertext in backwards (little endian) + * order, so reverse it! + */ + for(i = 0; i < (int)dlen; i++) + data[i] = buf[dlen - i - 1]; /* make it big endian */ + + CryptDestroyKey(hRsaKey); + free(buf); + free(pBlob); + +} + + +int rsastr_len(struct RSAKey *key) { + return 2 * (sizeof(DWORD) + key->bytes) + 10; +} + + +void rsastr_fmt(char *str, struct RSAKey *key) { + + int len = 0, i; + + sprintf(str+len, "%04x", key->exponent); + len += strlen(str+len); + + str[len++] = '/'; + for (i=1; ibytes; i++) { + sprintf(str+len, "%02x", key->modulus[i]); + len += strlen(str+len); + } + str[len] = '\0'; +} + + + +/* ---------------------------------------------------------* + * DES encryption / decryption functions * + * ---------------------------------------------------------*/ + + +void des3_sesskey(unsigned char *key) { + int i, j; + for(i = 0; i < 2; i++) { + for(j = 0; j < 3; j++) { + hDESKey[i][j] = create_des_key(key + (j * 8)); + } + } +} + + +void des3_encrypt_blk(unsigned char *blk, int len) { + + DWORD dlen; + dlen = len; + + if(CryptEncrypt(hDESKey[0][0], 0, FALSE, 0, blk, &dlen, len + 8) == 0) + fatalbox("Error encrypting block!\n"); + if(CryptDecrypt(hDESKey[0][1], 0, FALSE, 0, blk, &dlen) == 0) + fatalbox("Error encrypting block!\n"); + if(CryptEncrypt(hDESKey[0][2], 0, FALSE, 0, blk, &dlen, len + 8) == 0) + fatalbox("Error encrypting block!\n"); +} + + +void des3_decrypt_blk(unsigned char *blk, int len) { + DWORD dlen; + dlen = len; + + if(CryptDecrypt(hDESKey[1][2], 0, FALSE, 0, blk, &dlen) == 0) + fatalbox("Error decrypting block!\n"); + if(CryptEncrypt(hDESKey[1][1], 0, FALSE, 0, blk, &dlen, len + 8) == 0) + fatalbox("Error decrypting block!\n"); + if(CryptDecrypt(hDESKey[1][0], 0, FALSE, 0, blk, &dlen) == 0) + fatalbox("Error decrypting block!\n"); +} + + +struct ssh_cipher ssh_3des = { + des3_sesskey, + des3_encrypt_blk, + des3_decrypt_blk +}; + + +void des_sesskey(unsigned char *key) { + int i; + for(i = 0; i < 2; i++) { + hDESKey[i][0] = create_des_key(key); + } +} + + +void des_encrypt_blk(unsigned char *blk, int len) { + DWORD dlen; + dlen = len; + if(CryptEncrypt(hDESKey[0][0], 0, FALSE, 0, blk, &dlen, len + 8) == 0) + fatalbox("Error encrypting block!\n"); +} + + +void des_decrypt_blk(unsigned char *blk, int len) { + DWORD dlen; + dlen = len; + if(CryptDecrypt(hDESKey[1][0], 0, FALSE, 0, blk, &dlen) == 0) + fatalbox("Error decrypting block!\n"); +} + +struct ssh_cipher ssh_des = { + des_sesskey, + des_encrypt_blk, + des_decrypt_blk +}; + + +static HCRYPTKEY create_des_key(unsigned char *key) { + + HCRYPTKEY hSessionKey, hPrivateKey; + DWORD dlen = 8; + BLOBHEADER *pbh; + char buf[sizeof(BLOBHEADER) + sizeof(ALG_ID) + 256]; + + /* + * Need special private key to encrypt session key so we can + * import session key, since only encrypted session keys can be + * imported + */ + if(CryptImportKey(hCryptProv, PrivateKeyWithExponentOfOne, + sizeof(PrivateKeyWithExponentOfOne), + 0, 0, &hPrivateKey) == 0) + return 0; + + /* now encrypt session key using special private key */ + memcpy(buf + sizeof(BLOBHEADER) + sizeof(ALG_ID), key, 8); + if(CryptEncrypt(hPrivateKey, 0, TRUE, 0, + buf + sizeof(BLOBHEADER) + sizeof(ALG_ID), + &dlen, 256) == 0) + return 0; + + /* build session key blob */ + pbh = (BLOBHEADER*)buf; + pbh->bType = SIMPLEBLOB; + pbh->bVersion = 0x02; + pbh->reserved = 0; + pbh->aiKeyAlg = CALG_DES; + *((ALG_ID*)(buf+sizeof(BLOBHEADER))) = CALG_RSA_KEYX; + + /* import session key into key container */ + if(CryptImportKey(hCryptProv, buf, + dlen + sizeof(BLOBHEADER) + sizeof(ALG_ID), + hPrivateKey, 0, &hSessionKey) == 0) + return 0; + + if(hPrivateKey) + CryptDestroyKey(hPrivateKey); + + return hSessionKey; + +} diff --git a/putty.h b/putty.h index 045c7da7..1029de3f 100644 --- a/putty.h +++ b/putty.h @@ -324,6 +324,14 @@ unsigned char xlat_tty2scr(unsigned char c); unsigned char xlat_latkbd2win(unsigned char c); /* + * Exports from mscrypto.c + */ +#ifdef MSCRYPTOAPI +int crypto_startup(); +void crypto_wrapup(); +#endif + +/* * A debug system. */ #ifdef DEBUG diff --git a/scp.c b/scp.c index 8a82b2f7..d4446a77 100644 --- a/scp.c +++ b/scp.c @@ -814,6 +814,9 @@ int main(int argc, char *argv[]) ssh_send_eof(); ssh_recv(&ch, 1); } +#ifdef MSCRYPTOAPI + crypto_wrapup(); +#endif WSACleanup(); random_save_seed(); diff --git a/scp.h b/scp.h index 160e432b..9aaccf63 100644 --- a/scp.h +++ b/scp.h @@ -19,3 +19,11 @@ int ssh_recv(unsigned char *buf, int len); void ssh_send(unsigned char *buf, int len); void ssh_send_eof(void); +/* + * Exports from mscrypto.c + */ +#ifdef MSCRYPTOAPI +int crypto_startup(); +void crypto_wrapup(); +#endif + diff --git a/scpssh.c b/scpssh.c index 3c45c9ac..3d2c8240 100644 --- a/scpssh.c +++ b/scpssh.c @@ -121,8 +121,15 @@ next_packet: pktin.length = len; if (pktin.maxlen < biglen) { pktin.maxlen = biglen; +#ifdef MSCRYPTOAPI + /* allocate enough buffer space for extra block + * for MS CryptEncrypt() */ + pktin.data = (pktin.data == NULL) ? + smalloc(biglen+8) : srealloc(pktin.data, biglen+8); +#else pktin.data = (pktin.data == NULL) ? - smalloc(biglen) : srealloc(pktin.data, biglen); + smalloc(biglen) : srealloc(pktin.data, biglen); +#endif } ret = s_read(pktin.data, biglen); @@ -162,8 +169,15 @@ static void s_wrpkt_start(int type, int len) { pktout.length = len-5; if (pktout.maxlen < biglen) { pktout.maxlen = biglen; +#ifdef MSCRYPTOAPI + /* Allocate enough buffer space for extra block + * for MS CryptEncrypt() */ + pktout.data = (pktout.data == NULL ? malloc(biglen+8) : + realloc(pktout.data, biglen+8)); +#else pktout.data = (pktout.data == NULL ? malloc(biglen+4) : realloc(pktout.data, biglen+4)); +#endif if (!pktout.data) fatalbox("Out of memory"); } @@ -488,6 +502,11 @@ char *ssh_init(char *host, int port, char *cmd, char **realhost) { int FWport; #endif +#ifdef MSCRYPTOAPI + if(crypto_startup() == 0) + return "Microsoft high encryption pack not installed!"; +#endif + savedhost = malloc(1+strlen(host)); if (!savedhost) fatalbox("Out of memory"); diff --git a/ssh.c b/ssh.c index d31d823e..a1b0deb0 100644 --- a/ssh.c +++ b/ssh.c @@ -152,8 +152,15 @@ static void ssh_gotdata(unsigned char *data, int datalen) { pktin.length = len; if (pktin.maxlen < biglen) { pktin.maxlen = biglen; +#ifdef MSCRYPTOAPI + /* Allocate enough buffer space for extra block + * for MS CryptEncrypt() */ + pktin.data = (pktin.data == NULL ? malloc(biglen+8) : + realloc(pktin.data, biglen+8)); +#else pktin.data = (pktin.data == NULL ? malloc(biglen) : - realloc(pktin.data, biglen)); + realloc(pktin.data, biglen)); +#endif if (!pktin.data) fatalbox("Out of memory"); } @@ -199,8 +206,15 @@ static void s_wrpkt_start(int type, int len) { pktout.length = len-5; if (pktout.maxlen < biglen) { pktout.maxlen = biglen; +#ifdef MSCRYPTOAPI + /* Allocate enough buffer space for extra block + * for MS CryptEncrypt() */ + pktout.data = (pktout.data == NULL ? malloc(biglen+8) : + realloc(pktout.data, biglen+8)); +#else pktout.data = (pktout.data == NULL ? malloc(biglen+4) : realloc(pktout.data, biglen+4)); +#endif if (!pktout.data) fatalbox("Out of memory"); } @@ -685,6 +699,11 @@ static char *ssh_init (HWND hwnd, char *host, int port, char **realhost) { char *FWhost; int FWport; #endif + +#ifdef MSCRYPTOAPI + if(crypto_startup() == 0) + return "Microsoft high encryption pack not installed!"; +#endif savedhost = malloc(1+strlen(host)); if (!savedhost) diff --git a/ssh.h b/ssh.h index d154af7b..f758174f 100644 --- a/ssh.h +++ b/ssh.h @@ -8,8 +8,13 @@ struct RSAKey { int bits; int bytes; +#ifdef MSCRYPTOAPI + unsigned long exponent; + unsigned char *modulus; +#else void *modulus; void *exponent; +#endif }; int makekey(unsigned char *data, struct RSAKey *result, @@ -24,9 +29,13 @@ typedef unsigned int uint32; unsigned long crc32(const void *s, size_t len); struct MD5Context { - uint32 buf[4]; - uint32 bits[2]; - unsigned char in[64]; +#ifdef MSCRYPTOAPI + unsigned long hHash; +#else + uint32 buf[4]; + uint32 bits[2]; + unsigned char in[64]; +#endif }; void MD5Init(struct MD5Context *context); @@ -40,7 +49,9 @@ struct ssh_cipher { void (*decrypt)(unsigned char *blk, int len); }; +#ifndef MSCRYPTOAPI void SHATransform(word32 *digest, word32 *data); +#endif int random_byte(void); void random_add_noise(void *noise, int length); diff --git a/window.c b/window.c index 83e709e5..8a87584f 100644 --- a/window.c +++ b/window.c @@ -474,8 +474,12 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { DeleteObject(pal); WSACleanup(); - if (cfg.protocol == PROT_SSH) + if (cfg.protocol == PROT_SSH) { random_save_seed(); +#ifdef MSCRYPTOAPI + crypto_wrapup(); +#endif + } return msg.wParam; } -- 2.11.0