mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 09:27:59 +00:00
Add support for RFC 4432 RSA key exchange, the patch for which has been
lying around in my home directory for _years_. [originally from svn r7496]
This commit is contained in:
parent
9f7f5157fe
commit
dad558a1e5
10
Recipe
10
Recipe
@ -312,8 +312,8 @@ pageant : [G] winpgnt sshrsa sshpubk sshdes sshbn sshmd5 version tree234
|
||||
|
||||
puttygen : [G] winpgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
|
||||
+ sshrand winnoise sshsha winstore misc winctrls sshrsa sshdss winmisc
|
||||
+ sshpubk sshaes sshsh512 import winutils puttygen.res tree234
|
||||
+ notiming winhelp LIBS wintime
|
||||
+ sshpubk sshaes sshsh256 sshsh512 import winutils puttygen.res
|
||||
+ tree234 notiming winhelp LIBS wintime
|
||||
|
||||
pterm : [X] GTKTERM uxmisc misc ldisc settings uxpty uxsel BE_NONE uxstore
|
||||
+ uxsignal CHARSET cmdline uxpterm version time xpmpterm xpmptcfg
|
||||
@ -328,8 +328,8 @@ plink : [U] uxplink uxcons NONSSH UXSSH U_BE_ALL logging UXMISC uxsignal
|
||||
|
||||
puttygen : [U] cmdgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
|
||||
+ sshrand uxnoise sshsha misc sshrsa sshdss uxcons uxstore uxmisc
|
||||
+ sshpubk sshaes sshsh512 import puttygen.res time tree234 uxgen
|
||||
+ notiming
|
||||
+ sshpubk sshaes sshsh256 sshsh512 import puttygen.res time tree234
|
||||
+ uxgen notiming
|
||||
|
||||
pscp : [U] pscp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC
|
||||
psftp : [U] psftp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC
|
||||
@ -342,7 +342,7 @@ PuTTYtel : [M] terminal wcwidth ldiscucs logging BE_NOSSH mac macdlg
|
||||
+ CHARSET stricmp vsnprint dialog config macctrls minibidi
|
||||
PuTTYgen : [M] macpgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
|
||||
+ sshrand macnoise sshsha macstore misc sshrsa sshdss macmisc sshpubk
|
||||
+ sshaes sshsh512 import macpgen.rsrc macpgkey macabout
|
||||
+ sshaes sshsh256 sshsh512 import macpgen.rsrc macpgkey macabout
|
||||
|
||||
PuTTY : [MX] osxmain OSXTERM OSXMISC CHARSET U_BE_ALL NONSSH UXSSH
|
||||
+ ux_x11 uxpty uxsignal testback putty.icns info.plist
|
||||
|
1
config.c
1
config.c
@ -256,6 +256,7 @@ static void kexlist_handler(union control *ctrl, void *dlg,
|
||||
{ "Diffie-Hellman group 1", KEX_DHGROUP1 },
|
||||
{ "Diffie-Hellman group 14", KEX_DHGROUP14 },
|
||||
{ "Diffie-Hellman group exchange", KEX_DHGEX },
|
||||
{ "RSA-based key exchange", KEX_RSA },
|
||||
{ "-- warn below here --", KEX_WARN }
|
||||
};
|
||||
|
||||
|
@ -2282,6 +2282,10 @@ exchange; the server can avoid groups known to be weak, and possibly
|
||||
invent new ones over time, without any changes required to PuTTY's
|
||||
configuration. We recommend use of this method, if possible.
|
||||
|
||||
In addition, PuTTY supports \i{RSA key exchange}, which requires much less
|
||||
computational effort on the part of the client, and somewhat less on
|
||||
the part of the server, than Diffie-Hellman key exchange.
|
||||
|
||||
If the first algorithm PuTTY finds is below the \q{warn below here}
|
||||
line, you will see a warning box when you make the connection, similar
|
||||
to that for cipher selection (see \k{config-ssh-encryption}).
|
||||
|
1
putty.h
1
putty.h
@ -252,6 +252,7 @@ enum {
|
||||
KEX_DHGROUP1,
|
||||
KEX_DHGROUP14,
|
||||
KEX_DHGEX,
|
||||
KEX_RSA,
|
||||
KEX_MAX
|
||||
};
|
||||
|
||||
|
@ -27,6 +27,7 @@ static const struct keyval kexnames[] = {
|
||||
{ "dh-gex-sha1", KEX_DHGEX },
|
||||
{ "dh-group14-sha1", KEX_DHGROUP14 },
|
||||
{ "dh-group1-sha1", KEX_DHGROUP1 },
|
||||
{ "rsa", KEX_RSA },
|
||||
{ "WARN", KEX_WARN }
|
||||
};
|
||||
|
||||
@ -571,9 +572,9 @@ void load_open_settings(void *sesskey, Config *cfg)
|
||||
char *default_kexes;
|
||||
gppi(sesskey, "BugDHGEx2", 0, &i); i = 2-i;
|
||||
if (i == FORCE_ON)
|
||||
default_kexes = "dh-group14-sha1,dh-group1-sha1,WARN,dh-gex-sha1";
|
||||
default_kexes = "dh-group14-sha1,dh-group1-sha1,rsa,WARN,dh-gex-sha1";
|
||||
else
|
||||
default_kexes = "dh-gex-sha1,dh-group14-sha1,dh-group1-sha1,WARN";
|
||||
default_kexes = "dh-gex-sha1,dh-group14-sha1,dh-group1-sha1,rsa,WARN";
|
||||
gprefs(sesskey, "KEX", default_kexes,
|
||||
kexnames, KEX_MAX, cfg->ssh_kexlist);
|
||||
}
|
||||
|
138
ssh.c
138
ssh.c
@ -83,6 +83,9 @@
|
||||
#define SSH2_MSG_KEX_DH_GEX_GROUP 31 /* 0x1f */
|
||||
#define SSH2_MSG_KEX_DH_GEX_INIT 32 /* 0x20 */
|
||||
#define SSH2_MSG_KEX_DH_GEX_REPLY 33 /* 0x21 */
|
||||
#define SSH2_MSG_KEXRSA_PUBKEY 30 /* 0x1e */
|
||||
#define SSH2_MSG_KEXRSA_SECRET 31 /* 0x1f */
|
||||
#define SSH2_MSG_KEXRSA_DONE 32 /* 0x20 */
|
||||
#define SSH2_MSG_USERAUTH_REQUEST 50 /* 0x32 */
|
||||
#define SSH2_MSG_USERAUTH_FAILURE 51 /* 0x33 */
|
||||
#define SSH2_MSG_USERAUTH_SUCCESS 52 /* 0x34 */
|
||||
@ -112,6 +115,7 @@
|
||||
*/
|
||||
#define SSH2_PKTCTX_DHGROUP 0x0001
|
||||
#define SSH2_PKTCTX_DHGEX 0x0002
|
||||
#define SSH2_PKTCTX_RSAKEX 0x0004
|
||||
#define SSH2_PKTCTX_KEX_MASK 0x000F
|
||||
#define SSH2_PKTCTX_PUBLICKEY 0x0010
|
||||
#define SSH2_PKTCTX_PASSWORD 0x0020
|
||||
@ -339,6 +343,9 @@ static char *ssh2_pkt_type(int pkt_ctx, int type)
|
||||
translatec(SSH2_MSG_KEX_DH_GEX_GROUP, SSH2_PKTCTX_DHGEX);
|
||||
translatec(SSH2_MSG_KEX_DH_GEX_INIT, SSH2_PKTCTX_DHGEX);
|
||||
translatec(SSH2_MSG_KEX_DH_GEX_REPLY, SSH2_PKTCTX_DHGEX);
|
||||
translatec(SSH2_MSG_KEXRSA_PUBKEY, SSH2_PKTCTX_RSAKEX);
|
||||
translatec(SSH2_MSG_KEXRSA_SECRET, SSH2_PKTCTX_RSAKEX);
|
||||
translatec(SSH2_MSG_KEXRSA_DONE, SSH2_PKTCTX_RSAKEX);
|
||||
translate(SSH2_MSG_USERAUTH_REQUEST);
|
||||
translate(SSH2_MSG_USERAUTH_FAILURE);
|
||||
translate(SSH2_MSG_USERAUTH_SUCCESS);
|
||||
@ -5105,9 +5112,10 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
|
||||
const struct ssh_mac *scmac_tobe;
|
||||
const struct ssh_compress *cscomp_tobe;
|
||||
const struct ssh_compress *sccomp_tobe;
|
||||
char *hostkeydata, *sigdata, *keystr, *fingerprint;
|
||||
int hostkeylen, siglen;
|
||||
char *hostkeydata, *sigdata, *rsakeydata, *keystr, *fingerprint;
|
||||
int hostkeylen, siglen, rsakeylen;
|
||||
void *hkey; /* actual host key */
|
||||
void *rsakey; /* for RSA kex */
|
||||
unsigned char exchange_hash[SSH2_KEX_MAX_HASH_LEN];
|
||||
int n_preferred_kex;
|
||||
const struct ssh_kexes *preferred_kex[KEX_MAX];
|
||||
@ -5161,6 +5169,10 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
|
||||
s->preferred_kex[s->n_preferred_kex++] =
|
||||
&ssh_diffiehellman_group1;
|
||||
break;
|
||||
case KEX_RSA:
|
||||
s->preferred_kex[s->n_preferred_kex++] =
|
||||
&ssh_rsa_kex;
|
||||
break;
|
||||
case KEX_WARN:
|
||||
/* Flag for later. Don't bother if it's the last in
|
||||
* the list. */
|
||||
@ -5560,6 +5572,8 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
|
||||
crWaitUntil(pktin); /* Ignore packet */
|
||||
}
|
||||
|
||||
if (ssh->kex->main_type == KEXTYPE_DH) {
|
||||
/* XXX The lines below should be reindented before this is committed.*/
|
||||
/*
|
||||
* Work out the number of bits of key we will need from the key
|
||||
* exchange. We start with the maximum key length of either
|
||||
@ -5635,6 +5649,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
|
||||
}
|
||||
set_busy_status(ssh->frontend, BUSY_CPU); /* cogitate */
|
||||
ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
|
||||
s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen);
|
||||
s->f = ssh2_pkt_getmp(pktin);
|
||||
if (!s->f) {
|
||||
bombout(("unable to parse key exchange reply packet"));
|
||||
@ -5656,11 +5671,120 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
|
||||
}
|
||||
hash_mpint(ssh->kex->hash, ssh->exhash, s->e);
|
||||
hash_mpint(ssh->kex->hash, ssh->exhash, s->f);
|
||||
|
||||
dh_cleanup(ssh->kex_ctx);
|
||||
freebn(s->f);
|
||||
if (!ssh->kex->pdata) {
|
||||
freebn(s->g);
|
||||
freebn(s->p);
|
||||
}
|
||||
/* XXX end incorrectly-indented section */
|
||||
} else {
|
||||
logeventf(ssh, "Doing RSA key exchange with hash %s",
|
||||
ssh->kex->hash->text_name);
|
||||
ssh->pkt_ctx |= SSH2_PKTCTX_RSAKEX;
|
||||
/*
|
||||
* RSA key exchange. First expect a KEXRSA_PUBKEY packet
|
||||
* from the server.
|
||||
*/
|
||||
crWaitUntil(pktin);
|
||||
if (pktin->type != SSH2_MSG_KEXRSA_PUBKEY) {
|
||||
bombout(("expected RSA public key packet from server"));
|
||||
crStop(0);
|
||||
}
|
||||
|
||||
ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
|
||||
hash_string(ssh->kex->hash, ssh->exhash,
|
||||
s->hostkeydata, s->hostkeylen);
|
||||
s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen);
|
||||
|
||||
{
|
||||
char *keydata;
|
||||
ssh_pkt_getstring(pktin, &keydata, &s->rsakeylen);
|
||||
s->rsakeydata = snewn(s->rsakeylen, char);
|
||||
memcpy(s->rsakeydata, keydata, s->rsakeylen);
|
||||
}
|
||||
|
||||
s->rsakey = ssh_rsakex_newkey(s->rsakeydata, s->rsakeylen);
|
||||
if (!s->rsakey) {
|
||||
sfree(s->rsakeydata);
|
||||
bombout(("unable to parse RSA public key from server"));
|
||||
crStop(0);
|
||||
}
|
||||
|
||||
hash_string(ssh->kex->hash, ssh->exhash, s->rsakeydata, s->rsakeylen);
|
||||
|
||||
/*
|
||||
* Next, set up a shared secret K, of precisely KLEN -
|
||||
* 2*HLEN - 49 bits, where KLEN is the bit length of the
|
||||
* RSA key modulus and HLEN is the bit length of the hash
|
||||
* we're using.
|
||||
*/
|
||||
{
|
||||
int klen = ssh_rsakex_klen(s->rsakey);
|
||||
int nbits = klen - (2*ssh->kex->hash->hlen*8 + 49);
|
||||
int i, byte = 0;
|
||||
unsigned char *kstr1, *kstr2, *outstr;
|
||||
int kstr1len, kstr2len, outstrlen;
|
||||
|
||||
s->K = bn_power_2(nbits - 1);
|
||||
|
||||
for (i = 0; i < nbits; i++) {
|
||||
if ((i & 7) == 0) {
|
||||
byte = random_byte();
|
||||
}
|
||||
bignum_set_bit(s->K, i, (byte >> (i & 7)) & 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode this as an mpint.
|
||||
*/
|
||||
kstr1 = ssh2_mpint_fmt(s->K, &kstr1len);
|
||||
kstr2 = snewn(kstr2len = 4 + kstr1len, unsigned char);
|
||||
PUT_32BIT(kstr2, kstr1len);
|
||||
memcpy(kstr2 + 4, kstr1, kstr1len);
|
||||
|
||||
/*
|
||||
* Encrypt it with the given RSA key.
|
||||
*/
|
||||
outstrlen = (klen + 7) / 8;
|
||||
outstr = snewn(outstrlen, unsigned char);
|
||||
ssh_rsakex_encrypt(ssh->kex->hash, kstr2, kstr2len,
|
||||
outstr, outstrlen, s->rsakey);
|
||||
|
||||
/*
|
||||
* And send it off in a return packet.
|
||||
*/
|
||||
s->pktout = ssh2_pkt_init(SSH2_MSG_KEXRSA_SECRET);
|
||||
ssh2_pkt_addstring_start(s->pktout);
|
||||
ssh2_pkt_addstring_data(s->pktout, outstr, outstrlen);
|
||||
ssh2_pkt_send_noqueue(ssh, s->pktout);
|
||||
|
||||
hash_string(ssh->kex->hash, ssh->exhash, outstr, outstrlen);
|
||||
|
||||
sfree(kstr2);
|
||||
sfree(kstr1);
|
||||
sfree(outstr);
|
||||
}
|
||||
|
||||
ssh_rsakex_freekey(s->rsakey);
|
||||
|
||||
crWaitUntil(pktin);
|
||||
if (pktin->type != SSH2_MSG_KEXRSA_DONE) {
|
||||
sfree(s->rsakeydata);
|
||||
bombout(("expected signature packet from server"));
|
||||
crStop(0);
|
||||
}
|
||||
|
||||
ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen);
|
||||
|
||||
sfree(s->rsakeydata);
|
||||
}
|
||||
|
||||
hash_mpint(ssh->kex->hash, ssh->exhash, s->K);
|
||||
assert(ssh->kex->hash->hlen <= sizeof(s->exchange_hash));
|
||||
ssh->kex->hash->final(ssh->exhash, s->exchange_hash);
|
||||
|
||||
dh_cleanup(ssh->kex_ctx);
|
||||
ssh->kex_ctx = NULL;
|
||||
|
||||
#if 0
|
||||
@ -5668,7 +5792,6 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
|
||||
dmemdump(s->exchange_hash, ssh->kex->hash->hlen);
|
||||
#endif
|
||||
|
||||
s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen);
|
||||
if (!s->hkey ||
|
||||
!ssh->hostkey->verifysig(s->hkey, s->sigdata, s->siglen,
|
||||
(char *)s->exchange_hash,
|
||||
@ -5850,14 +5973,9 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
|
||||
ssh->sccomp->text_name);
|
||||
|
||||
/*
|
||||
* Free key exchange data.
|
||||
* Free shared secret.
|
||||
*/
|
||||
freebn(s->f);
|
||||
freebn(s->K);
|
||||
if (!ssh->kex->pdata) {
|
||||
freebn(s->g);
|
||||
freebn(s->p);
|
||||
}
|
||||
|
||||
/*
|
||||
* Key exchange is over. Loop straight back round if we have a
|
||||
|
23
ssh.h
23
ssh.h
@ -82,6 +82,17 @@ void crcda_free_context(void *handle);
|
||||
int detect_attack(void *handle, unsigned char *buf, uint32 len,
|
||||
unsigned char *IV);
|
||||
|
||||
/*
|
||||
* SSH2 RSA key exchange functions
|
||||
*/
|
||||
struct ssh_hash;
|
||||
void *ssh_rsakex_newkey(char *data, int len);
|
||||
void ssh_rsakex_freekey(void *key);
|
||||
int ssh_rsakex_klen(void *key);
|
||||
void ssh_rsakex_encrypt(const struct ssh_hash *h, unsigned char *in, int inlen,
|
||||
unsigned char *out, int outlen,
|
||||
void *key);
|
||||
|
||||
typedef struct {
|
||||
uint32 h[4];
|
||||
} MD5_Core_State;
|
||||
@ -194,15 +205,10 @@ struct ssh_hash {
|
||||
};
|
||||
|
||||
struct ssh_kex {
|
||||
/*
|
||||
* Plugging in another KEX algorithm requires structural chaos,
|
||||
* so it's hard to abstract them into nice little structures
|
||||
* like this. Fortunately, all our KEXes are basically
|
||||
* Diffie-Hellman at the moment, so in this structure I simply
|
||||
* parametrise the DH exchange a bit.
|
||||
*/
|
||||
char *name, *groupname;
|
||||
const unsigned char *pdata, *gdata;/* NULL means use group exchange */
|
||||
enum { KEXTYPE_DH, KEXTYPE_RSA } main_type;
|
||||
/* For DH */
|
||||
const unsigned char *pdata, *gdata; /* NULL means group exchange */
|
||||
int plen, glen;
|
||||
const struct ssh_hash *hash;
|
||||
};
|
||||
@ -268,6 +274,7 @@ extern const struct ssh_hash ssh_sha256;
|
||||
extern const struct ssh_kexes ssh_diffiehellman_group1;
|
||||
extern const struct ssh_kexes ssh_diffiehellman_group14;
|
||||
extern const struct ssh_kexes ssh_diffiehellman_gex;
|
||||
extern const struct ssh_kexes ssh_rsa_kex;
|
||||
extern const struct ssh_signkey ssh_dss;
|
||||
extern const struct ssh_signkey ssh_rsa;
|
||||
extern const struct ssh_mac ssh_hmac_md5;
|
||||
|
8
sshdh.c
8
sshdh.c
@ -52,7 +52,7 @@ static const unsigned char G[] = { 2 };
|
||||
|
||||
static const struct ssh_kex ssh_diffiehellman_group1_sha1 = {
|
||||
"diffie-hellman-group1-sha1", "group1",
|
||||
P1, G, lenof(P1), lenof(G), &ssh_sha1
|
||||
KEXTYPE_DH, P1, G, lenof(P1), lenof(G), &ssh_sha1
|
||||
};
|
||||
|
||||
static const struct ssh_kex *const group1_list[] = {
|
||||
@ -66,7 +66,7 @@ const struct ssh_kexes ssh_diffiehellman_group1 = {
|
||||
|
||||
static const struct ssh_kex ssh_diffiehellman_group14_sha1 = {
|
||||
"diffie-hellman-group14-sha1", "group14",
|
||||
P14, G, lenof(P14), lenof(G), &ssh_sha1
|
||||
KEXTYPE_DH, P14, G, lenof(P14), lenof(G), &ssh_sha1
|
||||
};
|
||||
|
||||
static const struct ssh_kex *const group14_list[] = {
|
||||
@ -80,12 +80,12 @@ const struct ssh_kexes ssh_diffiehellman_group14 = {
|
||||
|
||||
static const struct ssh_kex ssh_diffiehellman_gex_sha256 = {
|
||||
"diffie-hellman-group-exchange-sha256", NULL,
|
||||
NULL, NULL, 0, 0, &ssh_sha256
|
||||
KEXTYPE_DH, NULL, NULL, 0, 0, &ssh_sha256
|
||||
};
|
||||
|
||||
static const struct ssh_kex ssh_diffiehellman_gex_sha1 = {
|
||||
"diffie-hellman-group-exchange-sha1", NULL,
|
||||
NULL, NULL, 0, 0, &ssh_sha1
|
||||
KEXTYPE_DH, NULL, NULL, 0, 0, &ssh_sha1
|
||||
};
|
||||
|
||||
static const struct ssh_kex *const gex_list[] = {
|
||||
|
153
sshrsa.c
153
sshrsa.c
@ -836,3 +836,156 @@ const struct ssh_signkey ssh_rsa = {
|
||||
"ssh-rsa",
|
||||
"rsa2"
|
||||
};
|
||||
|
||||
void *ssh_rsakex_newkey(char *data, int len)
|
||||
{
|
||||
return rsa2_newkey(data, len);
|
||||
}
|
||||
|
||||
void ssh_rsakex_freekey(void *key)
|
||||
{
|
||||
rsa2_freekey(key);
|
||||
}
|
||||
|
||||
int ssh_rsakex_klen(void *key)
|
||||
{
|
||||
struct RSAKey *rsa = (struct RSAKey *) key;
|
||||
|
||||
return bignum_bitcount(rsa->modulus);
|
||||
}
|
||||
|
||||
static void oaep_mask(const struct ssh_hash *h, void *seed, int seedlen,
|
||||
void *vdata, int datalen)
|
||||
{
|
||||
unsigned char *data = (unsigned char *)vdata;
|
||||
unsigned count = 0;
|
||||
|
||||
while (datalen > 0) {
|
||||
int i, max = (datalen > h->hlen ? h->hlen : datalen);
|
||||
void *s;
|
||||
unsigned char counter[4], hash[h->hlen];
|
||||
|
||||
PUT_32BIT(counter, count);
|
||||
s = h->init();
|
||||
h->bytes(s, seed, seedlen);
|
||||
h->bytes(s, counter, 4);
|
||||
h->final(s, hash);
|
||||
count++;
|
||||
|
||||
for (i = 0; i < max; i++)
|
||||
data[i] ^= hash[i];
|
||||
|
||||
data += max;
|
||||
datalen -= max;
|
||||
}
|
||||
}
|
||||
|
||||
void ssh_rsakex_encrypt(const struct ssh_hash *h, unsigned char *in, int inlen,
|
||||
unsigned char *out, int outlen,
|
||||
void *key)
|
||||
{
|
||||
Bignum b1, b2;
|
||||
struct RSAKey *rsa = (struct RSAKey *) key;
|
||||
int k, i;
|
||||
char *p;
|
||||
const int HLEN = h->hlen;
|
||||
|
||||
/*
|
||||
* Here we encrypt using RSAES-OAEP. Essentially this means:
|
||||
*
|
||||
* - we have a SHA-based `mask generation function' which
|
||||
* creates a pseudo-random stream of mask data
|
||||
* deterministically from an input chunk of data.
|
||||
*
|
||||
* - we have a random chunk of data called a seed.
|
||||
*
|
||||
* - we use the seed to generate a mask which we XOR with our
|
||||
* plaintext.
|
||||
*
|
||||
* - then we use _the masked plaintext_ to generate a mask
|
||||
* which we XOR with the seed.
|
||||
*
|
||||
* - then we concatenate the masked seed and the masked
|
||||
* plaintext, and RSA-encrypt that lot.
|
||||
*
|
||||
* The result is that the data input to the encryption function
|
||||
* is random-looking and (hopefully) contains no exploitable
|
||||
* structure such as PKCS1-v1_5 does.
|
||||
*
|
||||
* For a precise specification, see RFC 3447, section 7.1.1.
|
||||
* Some of the variable names below are derived from that, so
|
||||
* it'd probably help to read it anyway.
|
||||
*/
|
||||
|
||||
/* k denotes the length in octets of the RSA modulus. */
|
||||
k = (7 + bignum_bitcount(rsa->modulus)) / 8;
|
||||
|
||||
/* The length of the input data must be at most k - 2hLen - 2. */
|
||||
assert(inlen > 0 && inlen <= k - 2*HLEN - 2);
|
||||
|
||||
/* The length of the output data wants to be precisely k. */
|
||||
assert(outlen == k);
|
||||
|
||||
/*
|
||||
* Now perform EME-OAEP encoding. First set up all the unmasked
|
||||
* output data.
|
||||
*/
|
||||
/* Leading byte zero. */
|
||||
out[0] = 0;
|
||||
/* At position 1, the seed: HLEN bytes of random data. */
|
||||
for (i = 0; i < HLEN; i++)
|
||||
out[i + 1] = random_byte();
|
||||
/* At position 1+HLEN, the data block DB, consisting of: */
|
||||
/* The hash of the label (we only support an empty label here) */
|
||||
h->final(h->init(), out + HLEN + 1);
|
||||
/* A bunch of zero octets */
|
||||
memset(out + 2*HLEN + 1, 0, outlen - (2*HLEN + 1));
|
||||
/* A single 1 octet, followed by the input message data. */
|
||||
out[outlen - inlen - 1] = 1;
|
||||
memcpy(out + outlen - inlen, in, inlen);
|
||||
|
||||
/*
|
||||
* Now use the seed data to mask the block DB.
|
||||
*/
|
||||
oaep_mask(h, out+1, HLEN, out+HLEN+1, outlen-HLEN-1);
|
||||
|
||||
/*
|
||||
* And now use the masked DB to mask the seed itself.
|
||||
*/
|
||||
oaep_mask(h, out+HLEN+1, outlen-HLEN-1, out+1, HLEN);
|
||||
|
||||
/*
|
||||
* Now `out' contains precisely the data we want to
|
||||
* RSA-encrypt.
|
||||
*/
|
||||
b1 = bignum_from_bytes(out, outlen);
|
||||
b2 = modpow(b1, rsa->exponent, rsa->modulus);
|
||||
p = out;
|
||||
for (i = outlen; i--;) {
|
||||
*p++ = bignum_byte(b2, i);
|
||||
}
|
||||
freebn(b1);
|
||||
freebn(b2);
|
||||
|
||||
/*
|
||||
* And we're done.
|
||||
*/
|
||||
}
|
||||
|
||||
static const struct ssh_kex ssh_rsa_kex_sha1 = {
|
||||
"rsa1024-sha1", NULL, KEXTYPE_RSA, NULL, NULL, 0, 0, &ssh_sha1
|
||||
};
|
||||
|
||||
static const struct ssh_kex ssh_rsa_kex_sha256 = {
|
||||
"rsa2048-sha256", NULL, KEXTYPE_RSA, NULL, NULL, 0, 0, &ssh_sha256
|
||||
};
|
||||
|
||||
static const struct ssh_kex *const rsa_kex_list[] = {
|
||||
&ssh_rsa_kex_sha256,
|
||||
&ssh_rsa_kex_sha1
|
||||
};
|
||||
|
||||
const struct ssh_kexes ssh_rsa_kex = {
|
||||
sizeof(rsa_kex_list) / sizeof(*rsa_kex_list),
|
||||
rsa_kex_list
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user