1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00

Added a framework for importing foreign key formats, and implemented

importing of OpenSSH SSH2 private key files (both encrypted and
unencrypted). Seems to work fine.

[originally from svn r1668]
This commit is contained in:
Simon Tatham 2002-05-11 16:45:29 +00:00
parent 07d48d8b27
commit 9a8c58a64b
7 changed files with 624 additions and 20 deletions

2
Recipe
View File

@ -128,4 +128,4 @@ pageant : [G] pageant sshrsa sshpubk sshdes sshbn sshmd5 version tree234
puttygen : [G] puttygen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
+ sshrand noise sshsha winstore misc winctrls sshrsa sshdss
+ sshpubk sshaes sshsh512 puttygen.res LIBS
+ sshpubk sshaes sshsh512 import puttygen.res LIBS

View File

@ -1,4 +1,4 @@
\versionid $Id: faq.but,v 1.24 2002/04/01 15:18:29 simon Exp $
\versionid $Id: faq.but,v 1.25 2002/05/11 16:45:29 simon Exp $
\A{faq} PuTTY FAQ
@ -37,10 +37,10 @@ version 0.52.
\S{faq-ssh2-keyfmt}{Question} Does PuTTY support reading OpenSSH or
\cw{ssh.com} SSHv2 private key files?
Not at present. OpenSSH and \cw{ssh.com} have totally different
formats for private key files, and neither one is particularly
pleasant, so PuTTY has its own. We do plan to write a converter at
some stage.
Version 0.52 doesn't, but in the latest development snapshots
PuTTYgen can load OpenSSH private keys. We plan to add an export
feature so that it can save them as well, and we also plan to
support the \cw{ssh.com} key format.
\S{faq-ssh1}{Question} Does PuTTY support SSH v1?

View File

@ -1,4 +1,4 @@
\versionid $Id: pubkey.but,v 1.13 2001/12/14 09:58:07 simon Exp $
\versionid $Id: pubkey.but,v 1.14 2002/05/11 16:45:29 simon Exp $
\C{pubkey} Using public keys for SSH authentication
@ -63,6 +63,8 @@ supported by PuTTY are described in \k{puttygen-keytype}.
\H{pubkey-puttygen} Using PuTTYgen, the PuTTY key generator
\cfg{winhelp-topic}{puttygen.general}
PuTTYgen is a key generator. It generates pairs of public and private
keys to be used with PuTTY, PSCP, and Plink, as well as the PuTTY
authentication agent, Pageant (see \k{pageant}). PuTTYgen generates

495
import.c Normal file
View File

@ -0,0 +1,495 @@
/*
* Code for PuTTY to import and export private key files in other
* SSH clients' formats.
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
#include "ssh.h"
#include "misc.h"
#define PUT_32BIT(cp, value) do { \
(cp)[3] = (value); \
(cp)[2] = (value) >> 8; \
(cp)[1] = (value) >> 16; \
(cp)[0] = (value) >> 24; } while (0)
#define GET_32BIT(cp) \
(((unsigned long)(unsigned char)(cp)[0] << 24) | \
((unsigned long)(unsigned char)(cp)[1] << 16) | \
((unsigned long)(unsigned char)(cp)[2] << 8) | \
((unsigned long)(unsigned char)(cp)[3]))
int openssh_encrypted(char *filename);
struct ssh2_userkey *openssh_read(char *filename, char *passphrase);
/*
* Given a key type, determine whether we know how to import it.
*/
int import_possible(int type)
{
if (type == SSH_KEYTYPE_OPENSSH)
return 1;
return 0;
}
/*
* Given a key type, determine what native key type
* (SSH_KEYTYPE_SSH1 or SSH_KEYTYPE_SSH2) it will come out as once
* we've imported it.
*/
int import_target_type(int type)
{
/*
* There are no known foreign SSH1 key formats.
*/
return SSH_KEYTYPE_SSH2;
}
/*
* Determine whether a foreign key is encrypted.
*/
int import_encrypted(char *filename, int type, char **comment)
{
if (type == SSH_KEYTYPE_OPENSSH) {
*comment = filename; /* OpenSSH doesn't do key comments */
return openssh_encrypted(filename);
}
return 0;
}
/*
* Import an SSH1 key.
*/
int import_ssh1(char *filename, int type, struct RSAKey *key, char *passphrase)
{
return 0;
}
/*
* Import an SSH2 key.
*/
struct ssh2_userkey *import_ssh2(char *filename, int type, char *passphrase)
{
if (type == SSH_KEYTYPE_OPENSSH)
return openssh_read(filename, passphrase);
return NULL;
}
/* ----------------------------------------------------------------------
* Helper routines. (The base64 ones are defined in sshpubk.c.)
*/
#define isbase64(c) ( ((c) >= 'A' && (c) <= 'Z') || \
((c) >= 'a' && (c) <= 'z') || \
((c) >= '0' && (c) <= '9') || \
(c) == '+' || (c) == '/' || (c) == '=' \
)
extern int base64_decode_atom(char *atom, unsigned char *out);
extern int base64_lines(int datalen);
extern void base64_encode_atom(unsigned char *data, int n, char *out);
extern void base64_encode(FILE * fp, unsigned char *data, int datalen);
/*
* Read an ASN.1/BER identifier and length pair.
*
* Flags are a combination of the #defines listed below.
*
* Returns -1 if unsuccessful; otherwise returns the number of
* bytes used out of the source data.
*/
/* ASN.1 tag classes. */
#define ASN1_CLASS_UNIVERSAL (0 << 6)
#define ASN1_CLASS_APPLICATION (1 << 6)
#define ASN1_CLASS_CONTEXT_SPECIFIC (2 << 6)
#define ASN1_CLASS_PRIVATE (3 << 6)
#define ASN1_CLASS_MASK (3 << 6)
/* Primitive versus constructed bit. */
#define ASN1_CONSTRUCTED (1 << 5)
int ber_read_id_len(void *source, int sourcelen,
int *id, int *length, int *flags)
{
unsigned char *p = (unsigned char *) source;
if (sourcelen == 0)
return -1;
*flags = (*p & 0xE0);
if ((*p & 0x1F) == 0x1F) {
*id = 0;
while (*p & 0x80) {
*id = (*id << 7) | (*p & 0x7F);
p++, sourcelen--;
if (sourcelen == 0)
return -1;
}
*id = (*id << 7) | (*p & 0x7F);
p++, sourcelen--;
} else {
*id = *p & 0x1F;
p++, sourcelen--;
}
if (sourcelen == 0)
return -1;
if (*p & 0x80) {
int n = *p & 0x7F;
p++, sourcelen--;
if (sourcelen < n)
return -1;
*length = 0;
while (n--)
*length = (*length << 8) | (*p++);
sourcelen -= n;
} else {
*length = *p;
p++, sourcelen--;
}
return p - (unsigned char *) source;
}
/* ----------------------------------------------------------------------
* Code to read OpenSSH private keys.
*/
enum { OSSH_DSA, OSSH_RSA };
struct openssh_key {
int type;
int encrypted;
char iv[32];
unsigned char *keyblob;
int keyblob_len, keyblob_size;
};
struct openssh_key *load_openssh_key(char *filename)
{
struct openssh_key *ret;
FILE *fp;
char buffer[256];
char *errmsg, *p;
int headers_done;
ret = smalloc(sizeof(*ret));
ret->keyblob = NULL;
ret->keyblob_len = ret->keyblob_size = 0;
ret->encrypted = 0;
memset(ret->iv, 0, sizeof(ret->iv));
fp = fopen(filename, "r");
if (!fp) {
errmsg = "Unable to open key file";
goto error;
}
if (!fgets(buffer, sizeof(buffer), fp) ||
0 != strncmp(buffer, "-----BEGIN ", 11) ||
0 != strcmp(buffer+strlen(buffer)-17, "PRIVATE KEY-----\n")) {
errmsg = "File does not begin with OpenSSH key header";
goto error;
}
if (!strcmp(buffer, "-----BEGIN RSA PRIVATE KEY-----\n"))
ret->type = OSSH_RSA;
else if (!strcmp(buffer, "-----BEGIN DSA PRIVATE KEY-----\n"))
ret->type = OSSH_DSA;
else {
errmsg = "Unrecognised key type";
goto error;
}
headers_done = 0;
while (1) {
if (!fgets(buffer, sizeof(buffer), fp)) {
errmsg = "Unexpected end of file";
goto error;
}
if (0 == strncmp(buffer, "-----END ", 9) &&
0 == strcmp(buffer+strlen(buffer)-17, "PRIVATE KEY-----\n"))
break; /* done */
if ((p = strchr(buffer, ':')) != NULL) {
if (headers_done) {
errmsg = "Header found in body of key data";
goto error;
}
*p++ = '\0';
while (*p && isspace((unsigned char)*p)) p++;
if (!strcmp(buffer, "Proc-Type")) {
if (p[0] != '4' || p[1] != ',') {
errmsg = "Proc-Type is not 4 (only 4 is supported)";
goto error;
}
p += 2;
if (!strcmp(p, "ENCRYPTED\n"))
ret->encrypted = 1;
} else if (!strcmp(buffer, "DEK-Info")) {
int i, j;
if (strncmp(p, "DES-EDE3-CBC,", 13)) {
errmsg = "Ciphers other than DES-EDE3-CBC not supported";
goto error;
}
p += 13;
for (i = 0; i < 8; i++) {
if (1 != sscanf(p, "%2x", &j))
break;
ret->iv[i] = j;
p += 2;
}
if (i < 8) {
errmsg = "Expected 16-digit iv in DEK-Info";
goto error;
}
}
} else {
headers_done = 1;
p = buffer;
while (isbase64(p[0]) && isbase64(p[1]) &&
isbase64(p[2]) && isbase64(p[3])) {
int len;
unsigned char out[3];
len = base64_decode_atom(p, out);
if (len <= 0) {
errmsg = "Invalid base64 encoding";
goto error;
}
if (ret->keyblob_len + len > ret->keyblob_size) {
ret->keyblob_size = ret->keyblob_len + len + 256;
ret->keyblob = srealloc(ret->keyblob, ret->keyblob_size);
}
memcpy(ret->keyblob + ret->keyblob_len, out, len);
ret->keyblob_len += len;
p += 4;
}
if (isbase64(*p)) {
errmsg = "base64 characters left at end of line";
goto error;
}
}
}
if (ret->keyblob_len == 0 || !ret->keyblob) {
errmsg = "Key body not present";
goto error;
}
if (ret->encrypted && ret->keyblob_len % 8 != 0) {
errmsg = "Encrypted key blob is not a multiple of cipher block size";
goto error;
}
return ret;
error:
if (ret) {
if (ret->keyblob) sfree(ret->keyblob);
sfree(ret);
}
return NULL;
}
int openssh_encrypted(char *filename)
{
struct openssh_key *key = load_openssh_key(filename);
int ret;
if (!key)
return 0;
ret = key->encrypted;
sfree(key->keyblob);
sfree(key);
return ret;
}
struct ssh2_userkey *openssh_read(char *filename, char *passphrase)
{
struct openssh_key *key = load_openssh_key(filename);
struct ssh2_userkey *retkey;
unsigned char *p;
int ret, id, len, flags;
int i, num_integers;
struct ssh2_userkey *retval = NULL;
char *errmsg;
unsigned char *blob;
int blobptr, privptr;
char *modptr;
int modlen;
if (!key)
return NULL;
if (key->encrypted) {
/*
* Derive encryption key from passphrase and iv/salt:
*
* - let block A equal MD5(passphrase || iv)
* - let block B equal MD5(A || passphrase || iv)
* - block C would be MD5(B || passphrase || iv) and so on
* - encryption key is the first N bytes of A || B
*/
struct MD5Context md5c;
unsigned char keybuf[32];
MD5Init(&md5c);
MD5Update(&md5c, passphrase, strlen(passphrase));
MD5Update(&md5c, key->iv, 8);
MD5Final(keybuf, &md5c);
MD5Init(&md5c);
MD5Update(&md5c, keybuf, 16);
MD5Update(&md5c, passphrase, strlen(passphrase));
MD5Update(&md5c, key->iv, 8);
MD5Final(keybuf+16, &md5c);
/*
* Now decrypt the key blob.
*/
des3_decrypt_pubkey_ossh(keybuf, key->iv,
key->keyblob, key->keyblob_len);
}
/*
* Now we have a decrypted key blob, which contains an ASN.1
* encoded private key. We must now untangle the ASN.1.
*
* We expect the whole key blob to be formatted as a SEQUENCE
* (0x30 followed by a length code indicating that the rest of
* the blob is part of the sequence). Within that SEQUENCE we
* expect to see a bunch of INTEGERs. What those integers mean
* depends on the key type:
*
* - For RSA, we expect the integers to be 0, n, e, d, p, q,
* dmp1, dmq1, iqmp in that order. (The last three are d mod
* (p-1), d mod (q-1), inverse of q mod p respectively.)
*
* - For DSA, we expect them to be 0, p, q, g, y, x in that
* order.
*/
p = key->keyblob;
/* Expect the SEQUENCE header. Take its absence as a failure to decrypt. */
ret = ber_read_id_len(p, key->keyblob_len, &id, &len, &flags);
p += ret;
if (ret < 0 || id != 16) {
errmsg = "ASN.1 decoding failure";
retval = SSH2_WRONG_PASSPHRASE;
goto error;
}
/* Expect a load of INTEGERs. */
if (key->type == OSSH_RSA)
num_integers = 9;
else if (key->type == OSSH_DSA)
num_integers = 6;
/*
* Space to create key blob in.
*/
blob = smalloc(256+key->keyblob_len);
PUT_32BIT(blob, 7);
if (key->type == OSSH_DSA)
memcpy(blob+4, "ssh-dss", 7);
else if (key->type == OSSH_RSA)
memcpy(blob+4, "ssh-rsa", 7);
blobptr = 4+7;
privptr = -1;
for (i = 0; i < num_integers; i++) {
ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
&id, &len, &flags);
p += ret;
if (ret < 0 || id != 2 ||
key->keyblob+key->keyblob_len-p < len) {
errmsg = "ASN.1 decoding failure";
goto error;
}
if (i == 0) {
/*
* The first integer should be zero always (I think
* this is some sort of version indication).
*/
if (len != 1 || p[0] != 0) {
errmsg = "Version number mismatch";
goto error;
}
} else if (key->type == OSSH_RSA) {
/*
* Integers 1 and 2 go into the public blob but in the
* opposite order; integers 3, 4, 5 and 8 go into the
* private blob. The other two (6 and 7) are ignored.
*/
if (i == 1) {
/* Save the details for after we deal with number 2. */
modptr = p;
modlen = len;
} else if (i != 6 && i != 7) {
PUT_32BIT(blob+blobptr, len);
memcpy(blob+blobptr+4, p, len);
blobptr += 4+len;
if (i == 2) {
PUT_32BIT(blob+blobptr, modlen);
memcpy(blob+blobptr+4, modptr, modlen);
blobptr += 4+modlen;
privptr = blobptr;
}
}
} else if (key->type == OSSH_DSA) {
/*
* Integers 1-4 go into the public blob; integer 5 goes
* into the private blob.
*/
PUT_32BIT(blob+blobptr, len);
memcpy(blob+blobptr+4, p, len);
blobptr += 4+len;
if (i == 4)
privptr = blobptr;
}
/* Skip past the number. */
p += len;
}
/*
* Now put together the actual key. Simplest way to do this is
* to assemble our own key blobs and feed them to the createkey
* functions; this is a bit faffy but it does mean we get all
* the sanity checks for free.
*/
assert(privptr > 0); /* should have bombed by now if not */
retkey = smalloc(sizeof(struct ssh2_userkey));
retkey->alg = (key->type == OSSH_RSA ? &ssh_rsa : &ssh_dss);
retkey->data = retkey->alg->createkey(blob, privptr,
blob+privptr, blobptr-privptr);
if (!retkey->data) {
sfree(retkey);
errmsg = "unable to create key data structure";
goto error;
}
retkey->comment = dupstr("imported-openssh-key");
if (blob) sfree(blob);
sfree(key->keyblob);
sfree(key);
return retkey;
error:
if (blob) sfree(blob);
sfree(key->keyblob);
sfree(key);
return retval;
}

View File

@ -501,6 +501,8 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
IDC_BITSSTATIC, IDC_BITS,
IDC_ABOUT,
IDC_GIVEHELP,
IDC_IMPORT, IDC_EXPORT_OPENSSH, IDC_EXPORT_SSHCOM
};
static const int nokey_ids[] = { IDC_NOKEY, 0 };
static const int generating_ids[] =
@ -531,6 +533,44 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
}
requested_help = FALSE;
{
HMENU menu, menu1;
menu = CreateMenu();
menu1 = CreateMenu();
AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&File");
#if 0
/*
* Exporting not yet supported, but when we do it we
* should just put this lot back in.
*/
menu1 = CreateMenu();
AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH,
"Export &OpenSSH key");
AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
"Export &ssh.com key");
AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1,
"&Export");
#endif
menu1 = CreateMenu();
AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
if (help_path)
AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Help");
SetMenu(hwnd, menu);
}
/*
* Centre the window.
*/
@ -558,8 +598,6 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
/* Accelerators used: acglops1rbd */
ctlposinit(&cp, hwnd, 4, 4, 4);
bartitle(&cp, "Public and private key generation for PuTTY",
IDC_TITLE);
beginbox(&cp, "Key", IDC_BOX_KEY);
cp2 = cp;
statictext(&cp2, "No key.", 1, IDC_NOKEY);
@ -689,6 +727,16 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
EnableWindow(hwnd, 1);
SetActiveWindow(hwnd);
return 0;
case IDC_GIVEHELP:
if (HIWORD(wParam) == BN_CLICKED ||
HIWORD(wParam) == BN_DOUBLECLICKED) {
if (help_path) {
WinHelp(hwnd, help_path, HELP_COMMAND,
(DWORD)"JI(`',`puttygen.general')");
requested_help = TRUE;
}
}
return 0;
case IDC_GENERATE:
state =
(struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
@ -846,15 +894,17 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
if (prompt_keyfile(hwnd, "Load private key:", filename, 0)) {
char passphrase[PASSPHRASE_MAXLEN];
int needs_pass;
int type;
int type, realtype;
int ret;
char *comment;
struct PassphraseProcStruct pps;
struct RSAKey newkey1;
struct ssh2_userkey *newkey2 = NULL;
type = key_type(filename);
if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) {
type = realtype = key_type(filename);
if (type != SSH_KEYTYPE_SSH1 &&
type != SSH_KEYTYPE_SSH2 &&
!import_possible(type)) {
char msg[256];
sprintf(msg, "Couldn't load private key (%s)",
key_type_to_str(type));
@ -863,12 +913,21 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
break;
}
if (type != SSH_KEYTYPE_SSH1 &&
type != SSH_KEYTYPE_SSH2) {
realtype = type;
type = import_target_type(type);
}
comment = NULL;
if (type == SSH_KEYTYPE_SSH1)
if (realtype == SSH_KEYTYPE_SSH1)
needs_pass = rsakey_encrypted(filename, &comment);
else
else if (realtype == SSH_KEYTYPE_SSH2)
needs_pass =
ssh2_userkey_encrypted(filename, &comment);
else
needs_pass = import_encrypted(filename, realtype,
&comment);
pps.passphrase = passphrase;
pps.comment = comment;
do {
@ -884,12 +943,20 @@ static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
}
} else
*passphrase = '\0';
if (type == SSH_KEYTYPE_SSH1)
ret =
loadrsakey(filename, &newkey1, passphrase);
else {
newkey2 =
ssh2_load_userkey(filename, passphrase);
if (type == SSH_KEYTYPE_SSH1) {
if (realtype == type)
ret = loadrsakey(filename, &newkey1,
passphrase);
else
ret = import_ssh1(filename, realtype,
&newkey1, passphrase);
} else {
if (realtype == type)
newkey2 = ssh2_load_userkey(filename,
passphrase);
else
newkey2 = import_ssh2(filename, realtype,
passphrase);
if (newkey2 == SSH2_WRONG_PASSPHRASE)
ret = -1;
else if (!newkey2)

10
ssh.h
View File

@ -306,8 +306,18 @@ enum {
int key_type(char *filename);
char *key_type_to_str(int type);
int import_possible(int type);
int import_target_type(int type);
int import_encrypted(char *filename, int type, char **comment);
int import_ssh1(char *filename, int type, struct RSAKey *key,char *passphrase);
struct ssh2_userkey *import_ssh2(char *filename, int type, char *passphrase);
void des3_decrypt_pubkey(unsigned char *key, unsigned char *blk, int len);
void des3_encrypt_pubkey(unsigned char *key, unsigned char *blk, int len);
void des3_decrypt_pubkey_ossh(unsigned char *key, unsigned char *iv,
unsigned char *blk, int len);
void des3_encrypt_pubkey_ossh(unsigned char *key, unsigned char *iv,
unsigned char *blk, int len);
void aes256_encrypt_pubkey(unsigned char *key, unsigned char *blk,
int len);
void aes256_decrypt_pubkey(unsigned char *key, unsigned char *blk,

View File

@ -854,6 +854,36 @@ void des3_encrypt_pubkey(unsigned char *key, unsigned char *blk, int len)
des_3cbc_encrypt(blk, blk, len, ourkeys);
}
void des3_decrypt_pubkey_ossh(unsigned char *key, unsigned char *iv,
unsigned char *blk, int len)
{
DESContext ourkeys[3];
des_key_setup(GET_32BIT_MSB_FIRST(key),
GET_32BIT_MSB_FIRST(key + 4), &ourkeys[0]);
des_key_setup(GET_32BIT_MSB_FIRST(key + 8),
GET_32BIT_MSB_FIRST(key + 12), &ourkeys[1]);
des_key_setup(GET_32BIT_MSB_FIRST(key + 16),
GET_32BIT_MSB_FIRST(key + 20), &ourkeys[2]);
ourkeys[0].div0 = GET_32BIT_MSB_FIRST(iv);
ourkeys[0].div1 = GET_32BIT_MSB_FIRST(iv+4);
des_cbc3_decrypt(blk, blk, len, ourkeys);
}
void des3_encrypt_pubkey_ossh(unsigned char *key, unsigned char *iv,
unsigned char *blk, int len)
{
DESContext ourkeys[3];
des_key_setup(GET_32BIT_MSB_FIRST(key),
GET_32BIT_MSB_FIRST(key + 4), &ourkeys[0]);
des_key_setup(GET_32BIT_MSB_FIRST(key + 8),
GET_32BIT_MSB_FIRST(key + 12), &ourkeys[1]);
des_key_setup(GET_32BIT_MSB_FIRST(key + 16),
GET_32BIT_MSB_FIRST(key + 20), &ourkeys[2]);
ourkeys[0].eiv0 = GET_32BIT_MSB_FIRST(iv);
ourkeys[0].eiv1 = GET_32BIT_MSB_FIRST(iv+4);
des_cbc3_encrypt(blk, blk, len, ourkeys);
}
static const struct ssh2_cipher ssh_3des_ssh2 = {
des3_csiv, des3_cskey,
des3_sciv, des3_sckey,