| 1 |
/* |
| 2 |
* Copyright (c) 1998-2001, Robert O'Callahan |
| 3 |
* (C) 2004- TeraTerm Project |
| 4 |
* All rights reserved. |
| 5 |
* |
| 6 |
* Redistribution and use in source and binary forms, with or without |
| 7 |
* modification, are permitted provided that the following conditions |
| 8 |
* are met: |
| 9 |
* |
| 10 |
* 1. Redistributions of source code must retain the above copyright |
| 11 |
* notice, this list of conditions and the following disclaimer. |
| 12 |
* 2. Redistributions in binary form must reproduce the above copyright |
| 13 |
* notice, this list of conditions and the following disclaimer in the |
| 14 |
* documentation and/or other materials provided with the distribution. |
| 15 |
* 3. The name of the author may not be used to endorse or promote products |
| 16 |
* derived from this software without specific prior written permission. |
| 17 |
* |
| 18 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR |
| 19 |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| 20 |
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| 21 |
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| 22 |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| 23 |
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 24 |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 25 |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 26 |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| 27 |
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 28 |
*/ |
| 29 |
|
| 30 |
/* |
| 31 |
This code is copyright (C) 1998-1999 Robert O'Callahan. |
| 32 |
See LICENSE.TXT for the license. |
| 33 |
*/ |
| 34 |
|
| 35 |
#include "ttxssh.h" |
| 36 |
#include "keyfiles.h" |
| 37 |
#include "key.h" |
| 38 |
|
| 39 |
#include <io.h> |
| 40 |
#include <fcntl.h> |
| 41 |
#include <stdio.h> |
| 42 |
#include <stdlib.h> |
| 43 |
#include <errno.h> |
| 44 |
#include <openssl/rsa.h> |
| 45 |
#include <openssl/dsa.h> |
| 46 |
#include <openssl/evp.h> |
| 47 |
#include <openssl/pem.h> |
| 48 |
#include <openssl/md5.h> |
| 49 |
#include <openssl/err.h> |
| 50 |
|
| 51 |
#include "cipher.h" |
| 52 |
|
| 53 |
static char ID_string[] = "SSH PRIVATE KEY FILE FORMAT 1.1\n"; |
| 54 |
|
| 55 |
typedef struct keyfile_header { |
| 56 |
ssh2_keyfile_type type; |
| 57 |
char *header; |
| 58 |
} keyfile_header_t; |
| 59 |
|
| 60 |
static keyfile_header_t keyfile_headers[] = { |
| 61 |
{SSH2_KEYFILE_TYPE_OPENSSH, "-----BEGIN RSA PRIVATE KEY-----"}, |
| 62 |
{SSH2_KEYFILE_TYPE_OPENSSH, "-----BEGIN DSA PRIVATE KEY-----"}, |
| 63 |
{SSH2_KEYFILE_TYPE_OPENSSH, "-----BEGIN EC PRIVATE KEY-----"}, |
| 64 |
{SSH2_KEYFILE_TYPE_OPENSSH, "-----BEGIN ENCRYPTED PRIVATE KEY-----"}, |
| 65 |
{SSH2_KEYFILE_TYPE_OPENSSH, "-----BEGIN PRIVATE KEY-----"}, |
| 66 |
{SSH2_KEYFILE_TYPE_OPENSSH, "-----BEGIN OPENSSH PRIVATE KEY-----"}, |
| 67 |
{SSH2_KEYFILE_TYPE_PUTTY, "PuTTY-User-Key-File-2"}, |
| 68 |
{SSH2_KEYFILE_TYPE_SECSH, "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----"}, |
| 69 |
{SSH2_KEYFILE_TYPE_NONE, NULL}, |
| 70 |
}; |
| 71 |
|
| 72 |
static BIGNUM *get_bignum(unsigned char *bytes) |
| 73 |
{ |
| 74 |
int bits = get_ushort16_MSBfirst(bytes); |
| 75 |
|
| 76 |
return BN_bin2bn(bytes + 2, (bits + 7) / 8, NULL); |
| 77 |
} |
| 78 |
|
| 79 |
/* normalize the RSA key by precomputing various bits of it. |
| 80 |
This code is adapted from libeay's rsa_gen.c |
| 81 |
It's needed to work around "issues" with LIBEAY/RSAREF. |
| 82 |
If this function returns 0, then something went wrong and the |
| 83 |
key must be discarded. */ |
| 84 |
static BOOL normalize_key(RSA *key) |
| 85 |
{ |
| 86 |
BOOL OK = FALSE; |
| 87 |
BIGNUM *r = BN_new(); |
| 88 |
BN_CTX *ctx = BN_CTX_new(); |
| 89 |
BIGNUM *e, *n, *d, *dmp1, *dmq1, *iqmp, *p, *q; |
| 90 |
|
| 91 |
e = n = d = dmp1 = dmq1 = iqmp = p = q = NULL; |
| 92 |
|
| 93 |
RSA_get0_key(key, &n, &e, &d); |
| 94 |
RSA_get0_factors(key, &p, &q); |
| 95 |
RSA_get0_crt_params(key, &dmp1, &dmq1, &iqmp); |
| 96 |
|
| 97 |
if (BN_cmp(p, q) < 0) { |
| 98 |
BN_swap(p, q); |
| 99 |
} |
| 100 |
|
| 101 |
if (r != NULL && ctx != NULL) { |
| 102 |
dmp1 = BN_new(); |
| 103 |
dmq1 = BN_new(); |
| 104 |
iqmp = BN_mod_inverse(NULL, q, p, ctx); |
| 105 |
RSA_set0_crt_params(key, dmp1, dmq1, iqmp); |
| 106 |
|
| 107 |
if (dmp1 != NULL && dmq1 != NULL && iqmp != NULL) { |
| 108 |
OK = BN_sub(r, p, BN_value_one()) |
| 109 |
&& BN_mod(dmp1, d, r, ctx) |
| 110 |
&& BN_sub(r, q, BN_value_one()) |
| 111 |
&& BN_mod(dmq1, d, r, ctx); |
| 112 |
} |
| 113 |
} |
| 114 |
|
| 115 |
BN_free(r); |
| 116 |
BN_CTX_free(ctx); |
| 117 |
|
| 118 |
return OK; |
| 119 |
} |
| 120 |
|
| 121 |
static RSA *read_RSA_private_key(PTInstVar pvar, |
| 122 |
char * relative_name, |
| 123 |
char * passphrase, |
| 124 |
BOOL * invalid_passphrase, |
| 125 |
BOOL is_auto_login) |
| 126 |
{ |
| 127 |
char filename[2048]; |
| 128 |
int fd; |
| 129 |
unsigned int length, amount_read; |
| 130 |
unsigned char *keyfile_data; |
| 131 |
unsigned int index; |
| 132 |
int cipher; |
| 133 |
RSA *key; |
| 134 |
unsigned int E_index, N_index, D_index, U_index, P_index, Q_index = 0; |
| 135 |
BIGNUM *e, *n, *d, *p, *q; |
| 136 |
|
| 137 |
*invalid_passphrase = FALSE; |
| 138 |
|
| 139 |
get_teraterm_dir_relative_name(filename, sizeof(filename), |
| 140 |
relative_name); |
| 141 |
|
| 142 |
fd = _open(filename, _O_RDONLY | _O_SEQUENTIAL | _O_BINARY); |
| 143 |
if (fd == -1) { |
| 144 |
if (errno == ENOENT) { |
| 145 |
UTIL_get_lang_msg("MSG_KEYFILES_READ_ENOENT_ERROR", pvar, |
| 146 |
"An error occurred while trying to read the key file.\n" |
| 147 |
"The specified filename does not exist."); |
| 148 |
notify_nonfatal_error(pvar, pvar->ts->UIMsg); |
| 149 |
} else { |
| 150 |
UTIL_get_lang_msg("MSG_KEYFILES_READ_ERROR", pvar, |
| 151 |
"An error occurred while trying to read the key file."); |
| 152 |
notify_nonfatal_error(pvar, pvar->ts->UIMsg); |
| 153 |
} |
| 154 |
return NULL; |
| 155 |
} |
| 156 |
|
| 157 |
length = (int) _lseek(fd, 0, SEEK_END); |
| 158 |
_lseek(fd, 0, SEEK_SET); |
| 159 |
|
| 160 |
if (length >= 0 && length < 0x7FFFFFFF) { |
| 161 |
keyfile_data = malloc(length + 1); |
| 162 |
if (keyfile_data == NULL) { |
| 163 |
UTIL_get_lang_msg("MSG_KEYFILES_READ_ALLOC_ERROR", pvar, |
| 164 |
"Memory ran out while trying to allocate space to read the key file."); |
| 165 |
notify_nonfatal_error(pvar, pvar->ts->UIMsg); |
| 166 |
_close(fd); |
| 167 |
return NULL; |
| 168 |
} |
| 169 |
} else { |
| 170 |
UTIL_get_lang_msg("MSG_KEYFILES_READ_ERROR", pvar, |
| 171 |
"An error occurred while trying to read the key file."); |
| 172 |
notify_nonfatal_error(pvar, pvar->ts->UIMsg); |
| 173 |
_close(fd); |
| 174 |
return NULL; |
| 175 |
} |
| 176 |
|
| 177 |
amount_read = _read(fd, keyfile_data, length); |
| 178 |
/* terminate it with 0 so that the strncmp below is guaranteed not to |
| 179 |
crash */ |
| 180 |
keyfile_data[length] = 0; |
| 181 |
|
| 182 |
_close(fd); |
| 183 |
|
| 184 |
if (amount_read != length) { |
| 185 |
UTIL_get_lang_msg("MSG_KEYFILES_READ_ERROR", pvar, |
| 186 |
"An error occurred while trying to read the key file."); |
| 187 |
notify_nonfatal_error(pvar, pvar->ts->UIMsg); |
| 188 |
free(keyfile_data); |
| 189 |
return NULL; |
| 190 |
} |
| 191 |
|
| 192 |
if (strcmp(keyfile_data, ID_string) != 0) { |
| 193 |
UTIL_get_lang_msg("MSG_KEYFILES_PRIVATEKEY_NOTCONTAIN_ERROR", pvar, |
| 194 |
"The specified key file does not contain an SSH private key."); |
| 195 |
notify_nonfatal_error(pvar, pvar->ts->UIMsg); |
| 196 |
free(keyfile_data); |
| 197 |
return NULL; |
| 198 |
} |
| 199 |
|
| 200 |
index = NUM_ELEM(ID_string); |
| 201 |
|
| 202 |
if (length < index + 9) { |
| 203 |
UTIL_get_lang_msg("MSG_KEYFILES_PRIVATEKEY_TRUNCATE_ERROR", pvar, |
| 204 |
"The specified key file has been truncated and does not contain a valid private key."); |
| 205 |
notify_nonfatal_error(pvar, pvar->ts->UIMsg); |
| 206 |
free(keyfile_data); |
| 207 |
return NULL; |
| 208 |
} |
| 209 |
|
| 210 |
cipher = keyfile_data[index]; |
| 211 |
/* skip reserved int32, public key bits int32 */ |
| 212 |
index += 9; |
| 213 |
/* skip public key e and n mp_ints */ |
| 214 |
if (length < index + 2) { |
| 215 |
UTIL_get_lang_msg("MSG_KEYFILES_PRIVATEKEY_TRUNCATE_ERROR", pvar, |
| 216 |
"The specified key file has been truncated and does not contain a valid private key."); |
| 217 |
notify_nonfatal_error(pvar, pvar->ts->UIMsg); |
| 218 |
free(keyfile_data); |
| 219 |
return NULL; |
| 220 |
} |
| 221 |
N_index = index; |
| 222 |
index += (get_ushort16_MSBfirst(keyfile_data + index) + 7) / 8 + 2; |
| 223 |
if (length < index + 2) { |
| 224 |
UTIL_get_lang_msg("MSG_KEYFILES_PRIVATEKEY_TRUNCATE_ERROR", pvar, |
| 225 |
"The specified key file has been truncated and does not contain a valid private key."); |
| 226 |
notify_nonfatal_error(pvar, pvar->ts->UIMsg); |
| 227 |
free(keyfile_data); |
| 228 |
return NULL; |
| 229 |
} |
| 230 |
E_index = index; |
| 231 |
index += (get_ushort16_MSBfirst(keyfile_data + index) + 7) / 8 + 2; |
| 232 |
/* skip comment */ |
| 233 |
if (length < index + 4) { |
| 234 |
UTIL_get_lang_msg("MSG_KEYFILES_PRIVATEKEY_TRUNCATE_ERROR", pvar, |
| 235 |
"The specified key file has been truncated and does not contain a valid private key."); |
| 236 |
notify_nonfatal_error(pvar, pvar->ts->UIMsg); |
| 237 |
free(keyfile_data); |
| 238 |
return NULL; |
| 239 |
} |
| 240 |
index += get_uint32_MSBfirst(keyfile_data + index) + 4; |
| 241 |
|
| 242 |
if (length < index + 6) { |
| 243 |
UTIL_get_lang_msg("MSG_KEYFILES_PRIVATEKEY_TRUNCATE_ERROR", pvar, |
| 244 |
"The specified key file has been truncated and does not contain a valid private key."); |
| 245 |
notify_nonfatal_error(pvar, pvar->ts->UIMsg); |
| 246 |
free(keyfile_data); |
| 247 |
return NULL; |
| 248 |
} |
| 249 |
if (cipher != SSH_CIPHER_NONE) { |
| 250 |
if ((length - index) % 8 != 0) { |
| 251 |
UTIL_get_lang_msg("MSG_KEYFILES_PRIVATEKEY_LENGTH_ERROR", pvar, |
| 252 |
"The specified key file cannot be decrypted using the passphrase.\n" |
| 253 |
"The file does not have the correct length."); |
| 254 |
notify_nonfatal_error(pvar, pvar->ts->UIMsg); |
| 255 |
free(keyfile_data); |
| 256 |
return NULL; |
| 257 |
} |
| 258 |
if (!CRYPT_passphrase_decrypt |
| 259 |
(cipher, passphrase, keyfile_data + index, length - index)) { |
| 260 |
UTIL_get_lang_msg("MSG_KEYFILES_PRIVATEKEY_NOCIPHER_ERROR", pvar, |
| 261 |
"The specified key file cannot be decrypted using the passphrase.\n" |
| 262 |
"The cipher type used to encrypt the file is not supported by TTSSH for this purpose."); |
| 263 |
notify_nonfatal_error(pvar, pvar->ts->UIMsg); |
| 264 |
free(keyfile_data); |
| 265 |
return NULL; |
| 266 |
} |
| 267 |
} |
| 268 |
|
| 269 |
if (keyfile_data[index] != keyfile_data[index + 2] |
| 270 |
|| keyfile_data[index + 1] != keyfile_data[index + 3]) { |
| 271 |
*invalid_passphrase = TRUE; |
| 272 |
if (is_auto_login) { |
| 273 |
UTIL_get_lang_msg("MSG_KEYFILES_PASSPHRASE_EMPTY_ERROR", pvar, |
| 274 |
"The specified key file cannot be decrypted using the empty passphrase.\n" |
| 275 |
"For auto-login, you must create a key file with no passphrase.\n" |
| 276 |
"BEWARE: This means the key can easily be stolen."); |
| 277 |
notify_nonfatal_error(pvar, pvar->ts->UIMsg); |
| 278 |
} |
| 279 |
else { |
| 280 |
UTIL_get_lang_msg("MSG_KEYFILES_PASSPHRASE_ERROR", pvar, |
| 281 |
"The specified key file cannot be decrypted using the passphrase.\n" |
| 282 |
"The passphrase is incorrect."); |
| 283 |
notify_nonfatal_error(pvar, pvar->ts->UIMsg); |
| 284 |
} |
| 285 |
SecureZeroMemory(keyfile_data, length); |
| 286 |
free(keyfile_data); |
| 287 |
return NULL; |
| 288 |
} |
| 289 |
index += 4; |
| 290 |
|
| 291 |
D_index = index; |
| 292 |
if (length >= D_index + 2) { |
| 293 |
U_index = |
| 294 |
D_index + (get_ushort16_MSBfirst(keyfile_data + D_index) + |
| 295 |
7) / 8 + 2; |
| 296 |
if (length >= U_index + 2) { |
| 297 |
P_index = |
| 298 |
U_index + (get_ushort16_MSBfirst(keyfile_data + U_index) + |
| 299 |
7) / 8 + 2; |
| 300 |
if (length >= P_index + 2) { |
| 301 |
Q_index = |
| 302 |
P_index + |
| 303 |
(get_ushort16_MSBfirst(keyfile_data + P_index) + |
| 304 |
7) / 8 + 2; |
| 305 |
} |
| 306 |
} |
| 307 |
} |
| 308 |
if (Q_index == 0 |
| 309 |
|| length < |
| 310 |
Q_index + (get_ushort16_MSBfirst(keyfile_data + Q_index) + 7) / 8 + 2) { |
| 311 |
UTIL_get_lang_msg("MSG_KEYFILES_PRIVATEKEY_TRUNCATE_ERROR", pvar, |
| 312 |
"The specified key file has been truncated and does not contain a valid private key."); |
| 313 |
notify_nonfatal_error(pvar, pvar->ts->UIMsg); |
| 314 |
SecureZeroMemory(keyfile_data, length); |
| 315 |
free(keyfile_data); |
| 316 |
return NULL; |
| 317 |
} |
| 318 |
|
| 319 |
key = RSA_new(); |
| 320 |
n = get_bignum(keyfile_data + N_index); |
| 321 |
e = get_bignum(keyfile_data + E_index); |
| 322 |
d = get_bignum(keyfile_data + D_index); |
| 323 |
RSA_set0_key(key, n, e, d); |
| 324 |
p = get_bignum(keyfile_data + P_index); |
| 325 |
q = get_bignum(keyfile_data + Q_index); |
| 326 |
RSA_set0_factors(key, p, q); |
| 327 |
|
| 328 |
if (!normalize_key(key)) { |
| 329 |
UTIL_get_lang_msg("MSG_KEYFILES_CRYPTOLIB_ERROR", pvar, |
| 330 |
"Error in cryptography library.\n" |
| 331 |
"Perhaps the stored key is invalid."); |
| 332 |
notify_nonfatal_error(pvar, pvar->ts->UIMsg); |
| 333 |
|
| 334 |
RSA_free(key); |
| 335 |
key = NULL; |
| 336 |
} |
| 337 |
|
| 338 |
SecureZeroMemory(keyfile_data, length); |
| 339 |
free(keyfile_data); |
| 340 |
return key; |
| 341 |
} |
| 342 |
|
| 343 |
Key *KEYFILES_read_private_key(PTInstVar pvar, |
| 344 |
char * relative_name, |
| 345 |
char * passphrase, |
| 346 |
BOOL * invalid_passphrase, |
| 347 |
BOOL is_auto_login) |
| 348 |
{ |
| 349 |
RSA *RSA_key = read_RSA_private_key(pvar, relative_name, |
| 350 |
passphrase, invalid_passphrase, |
| 351 |
is_auto_login); |
| 352 |
|
| 353 |
if (RSA_key == NULL) { |
| 354 |
return FALSE; |
| 355 |
} else { |
| 356 |
Key *result = (Key *) malloc(sizeof(Key)); |
| 357 |
|
| 358 |
// �t���[���������� 0 ���������������������������B(2004.12.20 yutaka) |
| 359 |
ZeroMemory(result, sizeof(Key)); |
| 360 |
|
| 361 |
result->rsa = RSA_key; |
| 362 |
return result; |
| 363 |
} |
| 364 |
} |
| 365 |
|
| 366 |
|
| 367 |
// |
| 368 |
// SSH2 |
| 369 |
// |
| 370 |
|
| 371 |
// bcrypt KDF �`�������� |
| 372 |
// based on key_parse_private2() @ OpenSSH 6.5 |
| 373 |
static Key *read_SSH2_private2_key(PTInstVar pvar, |
| 374 |
FILE * fp, |
| 375 |
char * passphrase, |
| 376 |
BOOL * invalid_passphrase, |
| 377 |
BOOL is_auto_login, |
| 378 |
char *errmsg, |
| 379 |
int errmsg_len) |
| 380 |
{ |
| 381 |
/* (A) |
| 382 |
* buffer_consume�n�������g���������Abuffer_len��buffer_ptr���g�����������A |
| 383 |
* buffer_len -> buffer_remain_len |
| 384 |
* buffer_ptr -> buffer_tail_ptr |
| 385 |
* �������g�p���������B |
| 386 |
*/ |
| 387 |
buffer_t *blob = NULL; |
| 388 |
buffer_t *b = NULL; |
| 389 |
buffer_t *kdf = NULL; |
| 390 |
buffer_t *encoded = NULL; |
| 391 |
buffer_t *copy_consumed = NULL; // (A) |
| 392 |
Key *keyfmt = NULL; |
| 393 |
unsigned char buf[1024]; |
| 394 |
unsigned char *cp, last, pad; |
| 395 |
char *ciphername = NULL, *kdfname = NULL, *kdfp = NULL, *key = NULL, *salt = NULL, *comment = NULL; |
| 396 |
unsigned int len, klen, nkeys, blocksize, keylen, ivlen, slen, rounds; |
| 397 |
unsigned int check1, check2, m1len, m2len; |
| 398 |
int dlen, i; |
| 399 |
const struct ssh2cipher *cipher; |
| 400 |
size_t authlen; |
| 401 |
struct sshcipher_ctx *cc = NULL; |
| 402 |
int ret; |
| 403 |
|
| 404 |
blob = buffer_init(); |
| 405 |
b = buffer_init(); |
| 406 |
kdf = buffer_init(); |
| 407 |
encoded = buffer_init(); |
| 408 |
copy_consumed = buffer_init(); |
| 409 |
|
| 410 |
if (blob == NULL || b == NULL || kdf == NULL || encoded == NULL || copy_consumed == NULL) |
| 411 |
goto error; |
| 412 |
|
| 413 |
// �t�@�C������������������ |
| 414 |
for (;;) { |
| 415 |
len = fread(buf, 1, sizeof(buf), fp); |
| 416 |
buffer_append(blob, buf, len); |
| 417 |
if (buffer_len(blob) > MAX_KEY_FILE_SIZE) { |
| 418 |
logprintf(LOG_LEVEL_WARNING, "%s: key file too large.", __FUNCTION__); |
| 419 |
goto error; |
| 420 |
} |
| 421 |
if (len < sizeof(buf)) |
| 422 |
break; |
| 423 |
} |
| 424 |
|
| 425 |
/* base64 decode */ |
| 426 |
m1len = sizeof(MARK_BEGIN) - 1; |
| 427 |
m2len = sizeof(MARK_END) - 1; |
| 428 |
cp = buffer_ptr(blob); |
| 429 |
len = buffer_len(blob); |
| 430 |
if (len < m1len || memcmp(cp, MARK_BEGIN, m1len)) { |
| 431 |
logprintf(LOG_LEVEL_VERBOSE, "%s: missing begin marker", __FUNCTION__); |
| 432 |
goto error; |
| 433 |
} |
| 434 |
cp += m1len; |
| 435 |
len -= m1len; |
| 436 |
while (len) { |
| 437 |
if (*cp != '\n' && *cp != '\r') |
| 438 |
buffer_put_char(encoded, *cp); |
| 439 |
last = *cp; |
| 440 |
len--; |
| 441 |
cp++; |
| 442 |
if (last == '\n') { |
| 443 |
if (len >= m2len && !memcmp(cp, MARK_END, m2len)) { |
| 444 |
buffer_put_char(encoded, '\0'); |
| 445 |
break; |
| 446 |
} |
| 447 |
} |
| 448 |
} |
| 449 |
if (!len) { |
| 450 |
logprintf(LOG_LEVEL_VERBOSE, "%s: no end marker", __FUNCTION__); |
| 451 |
goto error; |
| 452 |
} |
| 453 |
|
| 454 |
// �t�@�C�����X�L�������I�����������Abase64 decode�����B |
| 455 |
len = buffer_len(encoded); |
| 456 |
if ((cp = buffer_append_space(copy_consumed, len)) == NULL) { |
| 457 |
logprintf(LOG_LEVEL_ERROR, "%s: buffer_append_space", __FUNCTION__); |
| 458 |
goto error; |
| 459 |
} |
| 460 |
if ((dlen = b64decode(cp, len, buffer_ptr(encoded))) < 0) { |
| 461 |
logprintf(LOG_LEVEL_ERROR, "%s: base64 decode failed", __FUNCTION__); |
| 462 |
goto error; |
| 463 |
} |
| 464 |
if ((unsigned int)dlen > len) { |
| 465 |
logprintf(LOG_LEVEL_ERROR, "%s: crazy base64 length %d > %u", __FUNCTION__, dlen, len); |
| 466 |
goto error; |
| 467 |
} |
| 468 |
|
| 469 |
buffer_consume_end(copy_consumed, len - dlen); |
| 470 |
if (buffer_remain_len(copy_consumed) < sizeof(AUTH_MAGIC) || |
| 471 |
memcmp(buffer_tail_ptr(copy_consumed), AUTH_MAGIC, sizeof(AUTH_MAGIC))) { |
| 472 |
logprintf(LOG_LEVEL_ERROR, "%s: bad magic", __FUNCTION__); |
| 473 |
goto error; |
| 474 |
} |
| 475 |
buffer_consume(copy_consumed, sizeof(AUTH_MAGIC)); |
| 476 |
|
| 477 |
/* |
| 478 |
* �f�R�[�h�����f�[�^�����������B |
| 479 |
*/ |
| 480 |
// �������A���S���Y�������O |
| 481 |
ciphername = buffer_get_string_msg(copy_consumed, NULL); |
| 482 |
cipher = get_cipher_by_name(ciphername); |
| 483 |
if (cipher == NULL && strcmp(ciphername, "none") != 0) { |
| 484 |
logprintf(LOG_LEVEL_ERROR, "%s: unknown cipher name", __FUNCTION__); |
| 485 |
goto error; |
| 486 |
} |
| 487 |
// �p�X�t���[�Y���`�F�b�N�B�������� none �����������������p�X���[�h���F�������B |
| 488 |
if ((passphrase == NULL || strlen(passphrase) == 0) && |
| 489 |
strcmp(ciphername, "none") != 0) { |
| 490 |
/* passphrase required */ |
| 491 |
goto error; |
| 492 |
} |
| 493 |
|
| 494 |
kdfname = buffer_get_string_msg(copy_consumed, NULL); |
| 495 |
if (kdfname == NULL || |
| 496 |
(!strcmp(kdfname, "none") && !strcmp(kdfname, KDFNAME))) { |
| 497 |
logprintf(LOG_LEVEL_ERROR, "%s: unknown kdf name", __FUNCTION__ ); |
| 498 |
goto error; |
| 499 |
} |
| 500 |
if (!strcmp(kdfname, "none") && strcmp(ciphername, "none") != 0) { |
| 501 |
logprintf(LOG_LEVEL_ERROR, "%s: cipher %s requires kdf", __FUNCTION__, ciphername); |
| 502 |
goto error; |
| 503 |
} |
| 504 |
|
| 505 |
/* kdf options */ |
| 506 |
kdfp = buffer_get_string_msg(copy_consumed, &klen); |
| 507 |
if (kdfp == NULL) { |
| 508 |
logprintf(LOG_LEVEL_ERROR, "%s: kdf options not set", __FUNCTION__); |
| 509 |
goto error; |
| 510 |
} |
| 511 |
if (klen > 0) { |
| 512 |
if ((cp = buffer_append_space(kdf, klen)) == NULL) { |
| 513 |
logprintf(LOG_LEVEL_ERROR, "%s: kdf alloc failed", __FUNCTION__); |
| 514 |
goto error; |
| 515 |
} |
| 516 |
memcpy(cp, kdfp, klen); |
| 517 |
} |
| 518 |
|
| 519 |
/* number of keys */ |
| 520 |
if (buffer_get_int_ret(&nkeys, copy_consumed) < 0) { |
| 521 |
logprintf(LOG_LEVEL_ERROR, "%s: key counter missing", __FUNCTION__); |
| 522 |
goto error; |
| 523 |
} |
| 524 |
if (nkeys != 1) { |
| 525 |
logprintf(LOG_LEVEL_ERROR, "%s: only one key supported", __FUNCTION__); |
| 526 |
goto error; |
| 527 |
} |
| 528 |
|
| 529 |
/* pubkey */ |
| 530 |
cp = buffer_get_string_msg(copy_consumed, &len); |
| 531 |
if (cp == NULL) { |
| 532 |
logprintf(LOG_LEVEL_ERROR, "%s: pubkey not found", __FUNCTION__); |
| 533 |
goto error; |
| 534 |
} |
| 535 |
free(cp); /* XXX check pubkey against decrypted private key */ |
| 536 |
|
| 537 |
/* size of encrypted key blob */ |
| 538 |
len = buffer_get_int(copy_consumed); |
| 539 |
blocksize = get_cipher_block_size(cipher); |
| 540 |
authlen = 0; // TODO: ���������������� |
| 541 |
if (len < blocksize) { |
| 542 |
logprintf(LOG_LEVEL_ERROR, "%s: encrypted data too small", __FUNCTION__); |
| 543 |
goto error; |
| 544 |
} |
| 545 |
if (len % blocksize) { |
| 546 |
logprintf(LOG_LEVEL_ERROR, "%s: length not multiple of blocksize", __FUNCTION__); |
| 547 |
goto error; |
| 548 |
} |
| 549 |
|
| 550 |
/* setup key */ |
| 551 |
keylen = get_cipher_key_len(cipher); |
| 552 |
ivlen = blocksize; |
| 553 |
key = calloc(1, keylen + ivlen); |
| 554 |
if (!strcmp(kdfname, KDFNAME)) { |
| 555 |
salt = buffer_get_string_msg(kdf, &slen); |
| 556 |
if (salt == NULL) { |
| 557 |
logprintf(LOG_LEVEL_ERROR, "%s: salt not set", __FUNCTION__); |
| 558 |
goto error; |
| 559 |
} |
| 560 |
rounds = buffer_get_int(kdf); |
| 561 |
// TODO: error check |
| 562 |
if (bcrypt_pbkdf(passphrase, strlen(passphrase), salt, slen, |
| 563 |
key, keylen + ivlen, rounds) < 0) { |
| 564 |
logprintf(LOG_LEVEL_ERROR, "%s: bcrypt_pbkdf failed", __FUNCTION__); |
| 565 |
goto error; |
| 566 |
} |
| 567 |
} |
| 568 |
|
| 569 |
// ������ |
| 570 |
cp = buffer_append_space(b, len); |
| 571 |
cipher_init_SSH2(&cc, cipher, key, keylen, key + keylen, ivlen, CIPHER_DECRYPT, pvar); |
| 572 |
ret = EVP_Cipher(cc->evp, cp, buffer_tail_ptr(copy_consumed), len); |
| 573 |
if (ret == 0) { |
| 574 |
goto error; |
| 575 |
} |
| 576 |
buffer_consume(copy_consumed, len); |
| 577 |
|
| 578 |
if (buffer_remain_len(copy_consumed) != 0) { |
| 579 |
logprintf(LOG_LEVEL_ERROR, "%s: key blob has trailing data (len = %u)", __FUNCTION__, |
| 580 |
buffer_remain_len(copy_consumed)); |
| 581 |
goto error; |
| 582 |
} |
| 583 |
|
| 584 |
/* check bytes */ |
| 585 |
if (buffer_get_int_ret(&check1, b) < 0 || |
| 586 |
buffer_get_int_ret(&check2, b) < 0) { |
| 587 |
logprintf(LOG_LEVEL_ERROR, "%s: check bytes missing", __FUNCTION__); |
| 588 |
goto error; |
| 589 |
} |
| 590 |
if (check1 != check2) { |
| 591 |
logprintf(LOG_LEVEL_VERBOSE, "%s: decrypt failed: 0x%08x != 0x%08x", __FUNCTION__, |
| 592 |
check1, check2); |
| 593 |
goto error; |
| 594 |
} |
| 595 |
|
| 596 |
keyfmt = key_private_deserialize(b); |
| 597 |
if (keyfmt == NULL) |
| 598 |
goto error; |
| 599 |
|
| 600 |
/* comment */ |
| 601 |
comment = buffer_get_string_msg(b, NULL); |
| 602 |
|
| 603 |
i = 0; |
| 604 |
while (buffer_remain_len(b)) { |
| 605 |
if (buffer_get_char_ret(&pad, b) == -1 || |
| 606 |
pad != (++i & 0xff)) { |
| 607 |
logprintf(LOG_LEVEL_ERROR, "%s: bad padding", __FUNCTION__); |
| 608 |
key_free(keyfmt); |
| 609 |
keyfmt = NULL; |
| 610 |
goto error; |
| 611 |
} |
| 612 |
} |
| 613 |
|
| 614 |
/* success */ |
| 615 |
keyfmt->bcrypt_kdf = 1; |
| 616 |
|
| 617 |
error: |
| 618 |
buffer_free(blob); |
| 619 |
buffer_free(b); |
| 620 |
buffer_free(kdf); |
| 621 |
buffer_free(encoded); |
| 622 |
buffer_free(copy_consumed); |
| 623 |
cipher_free_SSH2(cc); |
| 624 |
|
| 625 |
free(ciphername); |
| 626 |
free(kdfname); |
| 627 |
free(kdfp); |
| 628 |
free(key); |
| 629 |
free(salt); |
| 630 |
free(comment); |
| 631 |
|
| 632 |
// KDF ������������ |
| 633 |
if (keyfmt == NULL) { |
| 634 |
fseek(fp, 0, SEEK_SET); |
| 635 |
|
| 636 |
} else { |
| 637 |
fclose(fp); |
| 638 |
|
| 639 |
} |
| 640 |
|
| 641 |
return (keyfmt); |
| 642 |
} |
| 643 |
|
| 644 |
|
| 645 |
// OpenSSL format |
| 646 |
Key *read_SSH2_private_key(PTInstVar pvar, |
| 647 |
FILE * fp, |
| 648 |
char * passphrase, |
| 649 |
BOOL * invalid_passphrase, |
| 650 |
BOOL is_auto_login, |
| 651 |
char *errmsg, |
| 652 |
int errmsg_len) |
| 653 |
{ |
| 654 |
Key *result = NULL; |
| 655 |
EVP_PKEY *pk = NULL; |
| 656 |
unsigned long err = 0; |
| 657 |
int pk_type; |
| 658 |
|
| 659 |
OpenSSL_add_all_algorithms(); |
| 660 |
ERR_load_crypto_strings(); |
| 661 |
//seed_prng(); |
| 662 |
|
| 663 |
result = read_SSH2_private2_key(pvar, fp, passphrase, invalid_passphrase, is_auto_login, errmsg, errmsg_len); |
| 664 |
if (result) |
| 665 |
return (result); |
| 666 |
|
| 667 |
result = (Key *)malloc(sizeof(Key)); |
| 668 |
ZeroMemory(result, sizeof(Key)); |
| 669 |
|
| 670 |
// �t�@�C�������p�X�t���[�Y�����������������������B |
| 671 |
pk = PEM_read_PrivateKey(fp, NULL, NULL, passphrase); |
| 672 |
if (pk == NULL) { |
| 673 |
err = ERR_get_error(); |
| 674 |
ERR_error_string_n(err, errmsg, errmsg_len); |
| 675 |
*invalid_passphrase = TRUE; |
| 676 |
goto error; |
| 677 |
} |
| 678 |
|
| 679 |
pk_type = EVP_PKEY_id(pk); |
| 680 |
switch (pk_type) { |
| 681 |
case EVP_PKEY_RSA: // RSA key |
| 682 |
result->type = KEY_RSA; |
| 683 |
result->rsa = EVP_PKEY_get1_RSA(pk); |
| 684 |
result->dsa = NULL; |
| 685 |
result->ecdsa = NULL; |
| 686 |
|
| 687 |
// RSA�������������L���������i�^�C�~���O�U���������h���j |
| 688 |
if (RSA_blinding_on(result->rsa, NULL) != 1) { |
| 689 |
err = ERR_get_error(); |
| 690 |
ERR_error_string_n(err, errmsg, errmsg_len); |
| 691 |
goto error; |
| 692 |
} |
| 693 |
break; |
| 694 |
|
| 695 |
case EVP_PKEY_DSA: // DSA key |
| 696 |
result->type = KEY_DSA; |
| 697 |
result->rsa = NULL; |
| 698 |
result->dsa = EVP_PKEY_get1_DSA(pk); |
| 699 |
result->ecdsa = NULL; |
| 700 |
break; |
| 701 |
|
| 702 |
case EVP_PKEY_EC: // ECDSA key |
| 703 |
result->rsa = NULL; |
| 704 |
result->dsa = NULL; |
| 705 |
result->ecdsa = EVP_PKEY_get1_EC_KEY(pk); |
| 706 |
{ |
| 707 |
const EC_GROUP *g = EC_KEY_get0_group(result->ecdsa); |
| 708 |
result->type = nid_to_keytype(EC_GROUP_get_curve_name(g)); |
| 709 |
if (result->type == KEY_UNSPEC) { |
| 710 |
goto error; |
| 711 |
} |
| 712 |
} |
| 713 |
break; |
| 714 |
|
| 715 |
default: |
| 716 |
goto error; |
| 717 |
break; |
| 718 |
} |
| 719 |
|
| 720 |
if (pk != NULL) |
| 721 |
EVP_PKEY_free(pk); |
| 722 |
|
| 723 |
fclose(fp); |
| 724 |
return (result); |
| 725 |
|
| 726 |
error: |
| 727 |
if (pk != NULL) |
| 728 |
EVP_PKEY_free(pk); |
| 729 |
|
| 730 |
if (result != NULL) |
| 731 |
key_free(result); |
| 732 |
|
| 733 |
if (fp != NULL) |
| 734 |
fclose(fp); |
| 735 |
|
| 736 |
return (NULL); |
| 737 |
} |
| 738 |
|
| 739 |
// PuTTY format |
| 740 |
/* |
| 741 |
* PuTTY-User-Key-File-2: ssh-dss |
| 742 |
* Encryption: aes256-cbc |
| 743 |
* Comment: imported-openssh-key |
| 744 |
* Public-Lines: 10 |
| 745 |
* Base64... |
| 746 |
* Private-Lines: 1 |
| 747 |
* Base64... |
| 748 |
* Private-MAC: Base16... |
| 749 |
* Private-Hash: Base16... (PuTTY-User-Key-File-1) ??? |
| 750 |
* |
| 751 |
* for "ssh-rsa", it will be composed of |
| 752 |
* |
| 753 |
* "Public-Lines: " plus a number N. |
| 754 |
* |
| 755 |
* string "ssh-rsa" |
| 756 |
* mpint exponent |
| 757 |
* mpint modulus |
| 758 |
* |
| 759 |
* "Private-Lines: " plus a number N, |
| 760 |
* |
| 761 |
* mpint private_exponent |
| 762 |
* mpint p (the larger of the two primes) |
| 763 |
* mpint q (the smaller prime) |
| 764 |
* mpint iqmp (the inverse of q modulo p) |
| 765 |
* data padding (to reach a multiple of the cipher block size) |
| 766 |
* |
| 767 |
* for "ssh-dss", it will be composed of |
| 768 |
* |
| 769 |
* "Public-Lines: " plus a number N. |
| 770 |
* |
| 771 |
* string "ssh-rsa" |
| 772 |
* mpint p |
| 773 |
* mpint q |
| 774 |
* mpint g |
| 775 |
* mpint y |
| 776 |
* |
| 777 |
* "Private-Lines: " plus a number N, |
| 778 |
* |
| 779 |
* mpint x (the private key parameter) |
| 780 |
* |
| 781 |
* for "ecdsa-sha2-nistp256" or |
| 782 |
* "ecdsa-sha2-nistp384" or |
| 783 |
* "ecdsa-sha2-nistp521", it will be composed of |
| 784 |
* |
| 785 |
* "Public-Lines: " plus a number N. |
| 786 |
* |
| 787 |
* string "ecdsa-sha2-[identifier]" ("ecdsa-sha2-nistp256" or |
| 788 |
* "ecdsa-sha2-nistp384" or |
| 789 |
* "ecdsa-sha2-nistp521") |
| 790 |
* string [identifier] ("nistp256" or "nistp384" or "nistp521") |
| 791 |
* string Q (EC_POINT) |
| 792 |
* |
| 793 |
* "Private-Lines: " plus a number N, |
| 794 |
* |
| 795 |
* mpint n |
| 796 |
* |
| 797 |
* for "ssh-ed25519", it will be composed of |
| 798 |
* |
| 799 |
* "Public-Lines: " plus a number N. |
| 800 |
* |
| 801 |
* string "ssh-ed25519" |
| 802 |
* string key |
| 803 |
* |
| 804 |
* "Private-Lines: " plus a number N, |
| 805 |
* |
| 806 |
* string key |
| 807 |
* |
| 808 |
* "Private-MAC: " plus a hex, HMAC-SHA-1 of: |
| 809 |
* |
| 810 |
* string name of algorithm ("ssh-dss", "ssh-rsa") |
| 811 |
* string encryption type |
| 812 |
* string comment |
| 813 |
* string public-blob |
| 814 |
* string private-plaintext |
| 815 |
*/ |
| 816 |
Key *read_SSH2_PuTTY_private_key(PTInstVar pvar, |
| 817 |
FILE * fp, |
| 818 |
char * passphrase, |
| 819 |
BOOL * invalid_passphrase, |
| 820 |
BOOL is_auto_login, |
| 821 |
char *errmsg, |
| 822 |
int errmsg_len) |
| 823 |
{ |
| 824 |
Key *result = NULL; |
| 825 |
EVP_PKEY *pk = NULL; |
| 826 |
unsigned long err = 0; |
| 827 |
int i, len, len2; |
| 828 |
char *encname = NULL, *comment = NULL, *private_mac = NULL; |
| 829 |
buffer_t *pubkey = NULL, *prikey = NULL; |
| 830 |
const struct ssh2cipher *cipher = NULL; |
| 831 |
struct sshcipher_ctx *cc = NULL; |
| 832 |
|
| 833 |
result = (Key *)malloc(sizeof(Key)); |
| 834 |
ZeroMemory(result, sizeof(Key)); |
| 835 |
result->type = KEY_NONE; |
| 836 |
result->rsa = NULL; |
| 837 |
result->dsa = NULL; |
| 838 |
result->ecdsa = NULL; |
| 839 |
|
| 840 |
pubkey = buffer_init(); |
| 841 |
prikey = buffer_init(); |
| 842 |
|
| 843 |
// parse keyfile & decode blob |
| 844 |
{ |
| 845 |
char line[200], buf[100]; |
| 846 |
BIO *bmem, *b64, *chain; |
| 847 |
while (fgets(line, sizeof(line), fp) != NULL) { |
| 848 |
if (strncmp(line, "PuTTY-User-Key-File-2: ", strlen("PuTTY-User-Key-File-2: ")) == 0) { |
| 849 |
if (strncmp(line + strlen("PuTTY-User-Key-File-2: "), "ssh-dss", strlen("ssh-dss")) == 0) { |
| 850 |
result->type = KEY_DSA; |
| 851 |
} |
| 852 |
else if (strncmp(line + strlen("PuTTY-User-Key-File-2: "), "ssh-rsa", strlen("ssh-rsa")) == 0) { |
| 853 |
result->type = KEY_RSA; |
| 854 |
} |
| 855 |
else if (strncmp(line + strlen("PuTTY-User-Key-File-2: "), "ecdsa-sha2-nistp256", strlen("ecdsa-sha2-nistp256")) == 0) { |
| 856 |
result->type = KEY_ECDSA256; |
| 857 |
} |
| 858 |
else if (strncmp(line + strlen("PuTTY-User-Key-File-2: "), "ecdsa-sha2-nistp384", strlen("ecdsa-sha2-nistp384")) == 0) { |
| 859 |
result->type = KEY_ECDSA384; |
| 860 |
} |
| 861 |
else if (strncmp(line + strlen("PuTTY-User-Key-File-2: "), "ecdsa-sha2-nistp521", strlen("ecdsa-sha2-nistp521")) == 0) { |
| 862 |
result->type = KEY_ECDSA521; |
| 863 |
} |
| 864 |
else if (strncmp(line + strlen("PuTTY-User-Key-File-2: "), "ssh-ed25519", strlen("ssh-ed25519")) == 0) { |
| 865 |
result->type = KEY_ED25519; |
| 866 |
} |
| 867 |
else { |
| 868 |
strncpy_s(errmsg, errmsg_len, "not a PuTTY SSH-2 private key", _TRUNCATE); |
| 869 |
goto error; |
| 870 |
} |
| 871 |
} |
| 872 |
else if (strncmp(line, "Encryption: ", strlen("Encryption: ")) == 0) { |
| 873 |
len = strlen(line + strlen("Encryption: ")); |
| 874 |
encname = (char *)malloc(len); // trim \n |
| 875 |
strncpy_s(encname, len, line + strlen("Encryption: "), _TRUNCATE); |
| 876 |
if (strcmp(encname, "aes256-cbc") == 0) { |
| 877 |
// NOP |
| 878 |
} |
| 879 |
else if (strcmp(encname, "none") == 0) { |
| 880 |
// NOP |
| 881 |
} |
| 882 |
else { |
| 883 |
strncpy_s(errmsg, errmsg_len, "unknown encryption type", _TRUNCATE); |
| 884 |
goto error; |
| 885 |
} |
| 886 |
} |
| 887 |
else if (strncmp(line, "Comment: ", strlen("Comment: ")) == 0) { |
| 888 |
len = strlen(line + strlen("Comment: ")); |
| 889 |
comment = (char *)malloc(len); // trim \n |
| 890 |
strncpy_s(comment, len, line + strlen("Comment: "), _TRUNCATE); |
| 891 |
} |
| 892 |
else if (strncmp(line, "Private-MAC: ", strlen("Private-MAC: ")) == 0) { |
| 893 |
len = strlen(line + strlen("Private-MAC: ")); |
| 894 |
private_mac = (char *)malloc(len); // trim \n |
| 895 |
strncpy_s(private_mac, len, line + strlen("Private-MAC: "), _TRUNCATE); |
| 896 |
} |
| 897 |
else if (strncmp(line, "Private-HASH: ", strlen("Private-HASH: ")) == 0) { |
| 898 |
strncpy_s(errmsg, errmsg_len, "not a PuTTY SSH-2 private key", _TRUNCATE); |
| 899 |
goto error; |
| 900 |
} |
| 901 |
else if (strncmp(line, "Public-Lines: ", strlen("Public-Lines: ")) == 0) { |
| 902 |
len = atoi(line + strlen("Public-Lines: ")); |
| 903 |
b64 = BIO_new(BIO_f_base64()); |
| 904 |
bmem = BIO_new(BIO_s_mem()); |
| 905 |
for (i=0; i<len && fgets(line, sizeof(line), fp)!=NULL; i++) { |
| 906 |
BIO_write(bmem, line, strlen(line)); |
| 907 |
} |
| 908 |
BIO_flush(bmem); |
| 909 |
chain = BIO_push(b64, bmem); |
| 910 |
BIO_set_mem_eof_return(chain, 0); |
| 911 |
while ((len2 = BIO_read(chain, buf, sizeof(buf))) > 0) { |
| 912 |
buffer_append(pubkey, buf, len2); |
| 913 |
} |
| 914 |
BIO_free_all(chain); |
| 915 |
} |
| 916 |
else if (strncmp(line, "Private-Lines: ", strlen("Private-Lines: ")) == 0) { |
| 917 |
len = atoi(line + strlen("Private-Lines: ")); |
| 918 |
b64 = BIO_new(BIO_f_base64()); |
| 919 |
bmem = BIO_new(BIO_s_mem()); |
| 920 |
for (i=0; i<len && fgets(line, sizeof(line), fp)!=NULL; i++) { |
| 921 |
BIO_write(bmem, line, strlen(line)); |
| 922 |
} |
| 923 |
BIO_flush(bmem); |
| 924 |
chain = BIO_push(b64, bmem); |
| 925 |
BIO_set_mem_eof_return(chain, 0); |
| 926 |
while ((len2 = BIO_read(chain, buf, sizeof(buf))) > 0) { |
| 927 |
buffer_append(prikey, buf, len2); |
| 928 |
} |
| 929 |
BIO_free_all(chain); |
| 930 |
} |
| 931 |
else { |
| 932 |
strncpy_s(errmsg, errmsg_len, "not a PuTTY SSH-2 private key", _TRUNCATE); |
| 933 |
goto error; |
| 934 |
} |
| 935 |
} |
| 936 |
} |
| 937 |
|
| 938 |
if (result->type == KEY_NONE || strlen(encname) == 0 || buffer_len(pubkey) == 0 || buffer_len(prikey) == 0) { |
| 939 |
strncpy_s(errmsg, errmsg_len, "key file format error", _TRUNCATE); |
| 940 |
goto error; |
| 941 |
} |
| 942 |
|
| 943 |
// decrypt prikey with aes256-cbc |
| 944 |
if (strcmp(encname, "aes256-cbc") == 0) { |
| 945 |
const EVP_MD *md = EVP_sha1(); |
| 946 |
EVP_MD_CTX *ctx = NULL; |
| 947 |
unsigned char key[40], iv[32]; |
| 948 |
EVP_CIPHER_CTX *cipher_ctx = NULL; |
| 949 |
char *decrypted = NULL; |
| 950 |
int ret; |
| 951 |
|
| 952 |
ctx = EVP_MD_CTX_new(); |
| 953 |
if (ctx == NULL) { |
| 954 |
goto error; |
| 955 |
} |
| 956 |
|
| 957 |
cipher_ctx = EVP_CIPHER_CTX_new(); |
| 958 |
if (cipher_ctx == NULL) { |
| 959 |
EVP_MD_CTX_free(ctx); |
| 960 |
goto error; |
| 961 |
} |
| 962 |
|
| 963 |
EVP_DigestInit(ctx, md); |
| 964 |
EVP_DigestUpdate(ctx, "\0\0\0\0", 4); |
| 965 |
EVP_DigestUpdate(ctx, passphrase, strlen(passphrase)); |
| 966 |
EVP_DigestFinal(ctx, key, &len); |
| 967 |
|
| 968 |
EVP_DigestInit(ctx, md); |
| 969 |
EVP_DigestUpdate(ctx, "\0\0\0\1", 4); |
| 970 |
EVP_DigestUpdate(ctx, passphrase, strlen(passphrase)); |
| 971 |
EVP_DigestFinal(ctx, key + 20, &len); |
| 972 |
|
| 973 |
EVP_MD_CTX_free(ctx); |
| 974 |
|
| 975 |
memset(iv, 0, sizeof(iv)); |
| 976 |
|
| 977 |
// decrypt |
| 978 |
cipher = get_cipher_by_name("aes256-cbc"); |
| 979 |
cipher_init_SSH2(&cc, cipher, key, 32, iv, 16, CIPHER_DECRYPT, pvar); |
| 980 |
len = buffer_len(prikey); |
| 981 |
decrypted = (char *)malloc(len); |
| 982 |
ret = EVP_Cipher(cc->evp, decrypted, prikey->buf, len); |
| 983 |
if (ret == 0) { |
| 984 |
strncpy_s(errmsg, errmsg_len, "Key decrypt error", _TRUNCATE); |
| 985 |
free(decrypted); |
| 986 |
cipher_free_SSH2(cc); |
| 987 |
goto error; |
| 988 |
} |
| 989 |
buffer_clear(prikey); |
| 990 |
buffer_append(prikey, decrypted, len); |
| 991 |
free(decrypted); |
| 992 |
cipher_free_SSH2(cc); |
| 993 |
} |
| 994 |
|
| 995 |
// verity MAC |
| 996 |
{ |
| 997 |
char realmac[41]; |
| 998 |
unsigned char binary[20]; |
| 999 |
buffer_t *macdata; |
| 1000 |
|
| 1001 |
macdata = buffer_init(); |
| 1002 |
|
| 1003 |
len = strlen(get_ssh2_hostkey_type_name(result->type)); |
| 1004 |
buffer_put_int(macdata, len); |
| 1005 |
buffer_append(macdata, get_ssh2_hostkey_type_name(result->type), len); |
| 1006 |
len = strlen(encname); |
| 1007 |
buffer_put_int(macdata, len); |
| 1008 |
buffer_append(macdata, encname, len); |
| 1009 |
len = strlen(comment); |
| 1010 |
buffer_put_int(macdata, len); |
| 1011 |
buffer_append(macdata, comment, len); |
| 1012 |
buffer_put_int(macdata, pubkey->len); |
| 1013 |
buffer_append(macdata, pubkey->buf, pubkey->len); |
| 1014 |
buffer_put_int(macdata, prikey->len); |
| 1015 |
buffer_append(macdata, prikey->buf, prikey->len); |
| 1016 |
|
| 1017 |
if (private_mac != NULL) { |
| 1018 |
unsigned char mackey[20]; |
| 1019 |
char header[] = "putty-private-key-file-mac-key"; |
| 1020 |
const EVP_MD *md = EVP_sha1(); |
| 1021 |
EVP_MD_CTX *ctx = NULL; |
| 1022 |
|
| 1023 |
ctx = EVP_MD_CTX_new(); |
| 1024 |
if (ctx == NULL) { |
| 1025 |
goto error; |
| 1026 |
} |
| 1027 |
|
| 1028 |
EVP_DigestInit(ctx, md); |
| 1029 |
EVP_DigestUpdate(ctx, header, sizeof(header)-1); |
| 1030 |
len = strlen(passphrase); |
| 1031 |
if (strcmp(encname, "aes256-cbc") == 0 && len > 0) { |
| 1032 |
EVP_DigestUpdate(ctx, passphrase, len); |
| 1033 |
} |
| 1034 |
EVP_DigestFinal(ctx, mackey, &len); |
| 1035 |
EVP_MD_CTX_free(ctx); |
| 1036 |
|
| 1037 |
//hmac_sha1_simple(mackey, sizeof(mackey), macdata->buf, macdata->len, binary); |
| 1038 |
{ |
| 1039 |
EVP_MD_CTX *ctx[2] = {0, 0}; |
| 1040 |
unsigned char intermediate[20]; |
| 1041 |
unsigned char foo[64]; |
| 1042 |
int i; |
| 1043 |
|
| 1044 |
ctx[0] = EVP_MD_CTX_new(); |
| 1045 |
if (ctx[0] == NULL) { |
| 1046 |
goto error; |
| 1047 |
} |
| 1048 |
ctx[1] = EVP_MD_CTX_new(); |
| 1049 |
if (ctx[1] == NULL) { |
| 1050 |
EVP_MD_CTX_free(ctx[0]); |
| 1051 |
goto error; |
| 1052 |
} |
| 1053 |
|
| 1054 |
memset(foo, 0x36, sizeof(foo)); |
| 1055 |
for (i = 0; i < sizeof(mackey) && i < sizeof(foo); i++) { |
| 1056 |
foo[i] ^= mackey[i]; |
| 1057 |
} |
| 1058 |
EVP_DigestInit(ctx[0], md); |
| 1059 |
EVP_DigestUpdate(ctx[0], foo, sizeof(foo)); |
| 1060 |
|
| 1061 |
memset(foo, 0x5C, sizeof(foo)); |
| 1062 |
for (i = 0; i < sizeof(mackey) && i < sizeof(foo); i++) { |
| 1063 |
foo[i] ^= mackey[i]; |
| 1064 |
} |
| 1065 |
EVP_DigestInit(ctx[1], md); |
| 1066 |
EVP_DigestUpdate(ctx[1], foo, sizeof(foo)); |
| 1067 |
|
| 1068 |
memset(foo, 0, sizeof(foo)); |
| 1069 |
|
| 1070 |
EVP_DigestUpdate(ctx[0], macdata->buf, macdata->len); |
| 1071 |
EVP_DigestFinal(ctx[0], intermediate, &len); |
| 1072 |
|
| 1073 |
EVP_DigestUpdate(ctx[1], intermediate, sizeof(intermediate)); |
| 1074 |
EVP_DigestFinal(ctx[1], binary, &len); |
| 1075 |
|
| 1076 |
EVP_MD_CTX_free(ctx[0]); |
| 1077 |
EVP_MD_CTX_free(ctx[1]); |
| 1078 |
} |
| 1079 |
|
| 1080 |
memset(mackey, 0, sizeof(mackey)); |
| 1081 |
|
| 1082 |
} |
| 1083 |
else { |
| 1084 |
strncpy_s(errmsg, errmsg_len, "key file format error", _TRUNCATE); |
| 1085 |
buffer_free(macdata); |
| 1086 |
goto error; |
| 1087 |
} |
| 1088 |
|
| 1089 |
buffer_free(macdata); |
| 1090 |
|
| 1091 |
for (i=0; i<20; i++) { |
| 1092 |
sprintf(realmac + 2*i, "%02x", binary[i]); |
| 1093 |
} |
| 1094 |
|
| 1095 |
if (strcmp(private_mac, realmac) != 0) { |
| 1096 |
if (strcmp(encname, "aes256-cbc") == 0) { |
| 1097 |
strncpy_s(errmsg, errmsg_len, "wrong passphrase", _TRUNCATE); |
| 1098 |
*invalid_passphrase = TRUE; |
| 1099 |
goto error; |
| 1100 |
} |
| 1101 |
else { |
| 1102 |
strncpy_s(errmsg, errmsg_len, "MAC verify failed", _TRUNCATE); |
| 1103 |
goto error; |
| 1104 |
} |
| 1105 |
} |
| 1106 |
} |
| 1107 |
|
| 1108 |
switch (result->type) { |
| 1109 |
case KEY_RSA: |
| 1110 |
{ |
| 1111 |
char *pubkey_type, *pub, *pri; |
| 1112 |
BIGNUM *e, *n, *d, *iqmp, *p, *q; |
| 1113 |
|
| 1114 |
pub = pubkey->buf; |
| 1115 |
pri = prikey->buf; |
| 1116 |
pubkey_type = buffer_get_string(&pub, NULL); |
| 1117 |
if (strcmp(pubkey_type, "ssh-rsa") != 0) { |
| 1118 |
strncpy_s(errmsg, errmsg_len, "key type error", _TRUNCATE); |
| 1119 |
free(pubkey_type); |
| 1120 |
goto error; |
| 1121 |
} |
| 1122 |
free(pubkey_type); |
| 1123 |
|
| 1124 |
result->rsa = RSA_new(); |
| 1125 |
if (result->rsa == NULL) { |
| 1126 |
strncpy_s(errmsg, errmsg_len, "key init error", _TRUNCATE); |
| 1127 |
goto error; |
| 1128 |
} |
| 1129 |
e = BN_new(); |
| 1130 |
n = BN_new(); |
| 1131 |
d = BN_new(); |
| 1132 |
RSA_set0_key(result->rsa, n, e, d); |
| 1133 |
p = BN_new(); |
| 1134 |
q = BN_new(); |
| 1135 |
RSA_set0_factors(result->rsa, p, q); |
| 1136 |
iqmp = BN_new(); |
| 1137 |
RSA_set0_crt_params(result->rsa, NULL, NULL, iqmp); |
| 1138 |
if (e == NULL || |
| 1139 |
n == NULL || |
| 1140 |
d == NULL || |
| 1141 |
p == NULL || |
| 1142 |
q == NULL || |
| 1143 |
iqmp == NULL) { |
| 1144 |
strncpy_s(errmsg, errmsg_len, "key init error", _TRUNCATE); |
| 1145 |
goto error; |
| 1146 |
} |
| 1147 |
|
| 1148 |
buffer_get_bignum2(&pub, e); |
| 1149 |
buffer_get_bignum2(&pub, n); |
| 1150 |
|
| 1151 |
buffer_get_bignum2(&pri, d); |
| 1152 |
buffer_get_bignum2(&pri, p); |
| 1153 |
buffer_get_bignum2(&pri, q); |
| 1154 |
buffer_get_bignum2(&pri, iqmp); |
| 1155 |
|
| 1156 |
break; |
| 1157 |
} |
| 1158 |
case KEY_DSA: |
| 1159 |
{ |
| 1160 |
char *pubkey_type, *pub, *pri; |
| 1161 |
BIGNUM *p, *q, *g, *pub_key, *priv_key; |
| 1162 |
|
| 1163 |
pub = pubkey->buf; |
| 1164 |
pri = prikey->buf; |
| 1165 |
pubkey_type = buffer_get_string(&pub, NULL); |
| 1166 |
if (strcmp(pubkey_type, "ssh-dss") != 0) { |
| 1167 |
strncpy_s(errmsg, errmsg_len, "key type error", _TRUNCATE); |
| 1168 |
free(pubkey_type); |
| 1169 |
goto error; |
| 1170 |
} |
| 1171 |
free(pubkey_type); |
| 1172 |
|
| 1173 |
result->dsa = DSA_new(); |
| 1174 |
if (result->dsa == NULL) { |
| 1175 |
strncpy_s(errmsg, errmsg_len, "key init error", _TRUNCATE); |
| 1176 |
goto error; |
| 1177 |
} |
| 1178 |
p = BN_new(); |
| 1179 |
q = BN_new(); |
| 1180 |
g = BN_new(); |
| 1181 |
DSA_set0_pqg(result->dsa, p, q, g); |
| 1182 |
pub_key = BN_new(); |
| 1183 |
priv_key = BN_new(); |
| 1184 |
DSA_set0_key(result->dsa, pub_key, priv_key); |
| 1185 |
if (p == NULL || |
| 1186 |
q == NULL || |
| 1187 |
g == NULL || |
| 1188 |
pub_key == NULL || |
| 1189 |
priv_key == NULL) { |
| 1190 |
strncpy_s(errmsg, errmsg_len, "key init error", _TRUNCATE); |
| 1191 |
goto error; |
| 1192 |
} |
| 1193 |
|
| 1194 |
buffer_get_bignum2(&pub, p); |
| 1195 |
buffer_get_bignum2(&pub, q); |
| 1196 |
buffer_get_bignum2(&pub, g); |
| 1197 |
buffer_get_bignum2(&pub, pub_key); |
| 1198 |
|
| 1199 |
buffer_get_bignum2(&pri, priv_key); |
| 1200 |
|
| 1201 |
break; |
| 1202 |
} |
| 1203 |
case KEY_ECDSA256: |
| 1204 |
case KEY_ECDSA384: |
| 1205 |
case KEY_ECDSA521: |
| 1206 |
{ |
| 1207 |
char *pubkey_type, *pub, *pri; |
| 1208 |
int success = 0; |
| 1209 |
unsigned int nid; |
| 1210 |
char *curve = NULL; |
| 1211 |
ssh_keytype skt; |
| 1212 |
BIGNUM *exponent = NULL; |
| 1213 |
EC_POINT *q = NULL; |
| 1214 |
|
| 1215 |
pub = pubkey->buf; |
| 1216 |
pri = prikey->buf; |
| 1217 |
pubkey_type = buffer_get_string(&pub, NULL); |
| 1218 |
if ((result->type == KEY_ECDSA256 && strcmp(pubkey_type, "ecdsa-sha2-nistp256") != 0) || |
| 1219 |
(result->type == KEY_ECDSA384 && strcmp(pubkey_type, "ecdsa-sha2-nistp384") != 0) || |
| 1220 |
(result->type == KEY_ECDSA521 && strcmp(pubkey_type, "ecdsa-sha2-nistp521") != 0)) { |
| 1221 |
strncpy_s(errmsg, errmsg_len, "key type error", _TRUNCATE); |
| 1222 |
free(pubkey_type); |
| 1223 |
goto error; |
| 1224 |
} |
| 1225 |
free(pubkey_type); |
| 1226 |
|
| 1227 |
nid = keytype_to_cipher_nid(result->type); |
| 1228 |
curve = buffer_get_string(&pub, NULL); |
| 1229 |
skt = key_curve_name_to_keytype(curve); |
| 1230 |
if (nid != keytype_to_cipher_nid(skt)) |
| 1231 |
goto ecdsa_error; |
| 1232 |
|
| 1233 |
if ((result->ecdsa = EC_KEY_new_by_curve_name(nid)) == NULL) |
| 1234 |
goto ecdsa_error; |
| 1235 |
if ((q = EC_POINT_new(EC_KEY_get0_group(result->ecdsa))) == NULL) |
| 1236 |
goto ecdsa_error; |
| 1237 |
if ((exponent = BN_new()) == NULL) |
| 1238 |
goto ecdsa_error; |
| 1239 |
|
| 1240 |
buffer_get_ecpoint(&pub, EC_KEY_get0_group(result->ecdsa), q); |
| 1241 |
buffer_get_bignum2(&pri, exponent); |
| 1242 |
if (EC_KEY_set_public_key(result->ecdsa, q) != 1) |
| 1243 |
goto ecdsa_error; |
| 1244 |
if (EC_KEY_set_private_key(result->ecdsa, exponent) != 1) |
| 1245 |
goto ecdsa_error; |
| 1246 |
if (key_ec_validate_public(EC_KEY_get0_group(result->ecdsa), |
| 1247 |
EC_KEY_get0_public_key(result->ecdsa)) != 0) |
| 1248 |
goto ecdsa_error; |
| 1249 |
if (key_ec_validate_private(result->ecdsa) != 0) |
| 1250 |
goto ecdsa_error; |
| 1251 |
|
| 1252 |
success = 1; |
| 1253 |
|
| 1254 |
ecdsa_error: |
| 1255 |
free(curve); |
| 1256 |
if (exponent) |
| 1257 |
BN_clear_free(exponent); |
| 1258 |
if (q) |
| 1259 |
EC_POINT_free(q); |
| 1260 |
if (success == 0) |
| 1261 |
goto error; |
| 1262 |
|
| 1263 |
break; |
| 1264 |
} |
| 1265 |
case KEY_ED25519: |
| 1266 |
{ |
| 1267 |
char *pubkey_type, *pub, *pri; |
| 1268 |
unsigned int pklen, sklen; |
| 1269 |
char *sk; |
| 1270 |
pub = pubkey->buf; |
| 1271 |
pri = prikey->buf; |
| 1272 |
pubkey_type = buffer_get_string(&pub, NULL); |
| 1273 |
if (strcmp(pubkey_type, "ssh-ed25519") != 0) { |
| 1274 |
strncpy_s(errmsg, errmsg_len, "key type error", _TRUNCATE); |
| 1275 |
free(pubkey_type); |
| 1276 |
goto error; |
| 1277 |
} |
| 1278 |
free(pubkey_type); |
| 1279 |
|
| 1280 |
result->ed25519_pk = buffer_get_string(&pub, &pklen); |
| 1281 |
sk = buffer_get_string(&pri, &sklen); |
| 1282 |
result->ed25519_sk = malloc(pklen + sklen + 1); |
| 1283 |
memcpy(result->ed25519_sk, sk, sklen); |
| 1284 |
memcpy(result->ed25519_sk + sklen, result->ed25519_pk, pklen); |
| 1285 |
result->ed25519_sk[sklen + pklen] = '\0'; |
| 1286 |
free(sk); |
| 1287 |
|
| 1288 |
if (pklen != ED25519_PK_SZ) |
| 1289 |
goto error; |
| 1290 |
if (sklen + pklen != ED25519_SK_SZ) |
| 1291 |
goto error; |
| 1292 |
|
| 1293 |
break; |
| 1294 |
} |
| 1295 |
default: |
| 1296 |
strncpy_s(errmsg, errmsg_len, "key type error", _TRUNCATE); |
| 1297 |
goto error; |
| 1298 |
break; |
| 1299 |
} |
| 1300 |
|
| 1301 |
fclose(fp); |
| 1302 |
|
| 1303 |
if (encname != NULL) |
| 1304 |
free(encname); |
| 1305 |
|
| 1306 |
if (comment != NULL) |
| 1307 |
free(comment); |
| 1308 |
|
| 1309 |
if (pubkey != NULL) |
| 1310 |
buffer_free(pubkey); |
| 1311 |
|
| 1312 |
if (prikey != NULL) |
| 1313 |
buffer_free(prikey); |
| 1314 |
|
| 1315 |
if (private_mac != NULL) |
| 1316 |
free(private_mac); |
| 1317 |
|
| 1318 |
return (result); |
| 1319 |
|
| 1320 |
error: |
| 1321 |
if (result != NULL) |
| 1322 |
key_free(result); |
| 1323 |
|
| 1324 |
if (fp != NULL) |
| 1325 |
fclose(fp); |
| 1326 |
|
| 1327 |
if (encname != NULL) |
| 1328 |
free(encname); |
| 1329 |
|
| 1330 |
if (comment != NULL) |
| 1331 |
free(comment); |
| 1332 |
|
| 1333 |
if (pubkey != NULL) |
| 1334 |
buffer_free(pubkey); |
| 1335 |
|
| 1336 |
if (prikey != NULL) |
| 1337 |
buffer_free(prikey); |
| 1338 |
|
| 1339 |
if (private_mac != NULL) |
| 1340 |
free(private_mac); |
| 1341 |
|
| 1342 |
return (NULL); |
| 1343 |
} |
| 1344 |
|
| 1345 |
// SECSH(ssh.com) format |
| 1346 |
/* |
| 1347 |
* Code to read ssh.com private keys. |
| 1348 |
* |
| 1349 |
* "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----" |
| 1350 |
* "Comment:..." |
| 1351 |
* "Base64..." |
| 1352 |
* "---- END SSH2 ENCRYPTED PRIVATE KEY ----" |
| 1353 |
* |
| 1354 |
* Body of key data |
| 1355 |
* |
| 1356 |
* - uint32 0x3f6ff9eb (magic number) |
| 1357 |
* - uint32 size (total blob size) |
| 1358 |
* - string key-type (see below) |
| 1359 |
* - string cipher-type (tells you if key is encrypted) |
| 1360 |
* - string encrypted-blob |
| 1361 |
* |
| 1362 |
* The key type strings are ghastly. The RSA key I looked at had a type string of |
| 1363 |
* |
| 1364 |
* `if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}' |
| 1365 |
* `dl-modp{sign{dsa-nist-sha1},dh{plain}}' |
| 1366 |
* `ec-modp' |
| 1367 |
* |
| 1368 |
* The encryption. The cipher-type string appears to be either |
| 1369 |
* |
| 1370 |
* `none' |
| 1371 |
* `3des-cbc' |
| 1372 |
* |
| 1373 |
* The payload blob, for an RSA key, contains: |
| 1374 |
* - mpint e |
| 1375 |
* - mpint d |
| 1376 |
* - mpint n (yes, the public and private stuff is intermixed) |
| 1377 |
* - mpint u (presumably inverse of p mod q) |
| 1378 |
* - mpint p (p is the smaller prime) |
| 1379 |
* - mpint q (q is the larger) |
| 1380 |
* |
| 1381 |
* For a DSA key, the payload blob contains: |
| 1382 |
* - uint32 0 |
| 1383 |
* - mpint p |
| 1384 |
* - mpint g |
| 1385 |
* - mpint q |
| 1386 |
* - mpint y |
| 1387 |
* - mpint x |
| 1388 |
* |
| 1389 |
* For a ECDSA key, the payload blob contains: |
| 1390 |
* - uint32 1 |
| 1391 |
* - string [identifier] ("nistp256" or "nistp384" or "nistp521") |
| 1392 |
* - mpint n |
| 1393 |
*/ |
| 1394 |
Key *read_SSH2_SECSH_private_key(PTInstVar pvar, |
| 1395 |
FILE * fp, |
| 1396 |
char * passphrase, |
| 1397 |
BOOL * invalid_passphrase, |
| 1398 |
BOOL is_auto_login, |
| 1399 |
char *errmsg, |
| 1400 |
int errmsg_len) |
| 1401 |
{ |
| 1402 |
Key *result = NULL; |
| 1403 |
unsigned long err = 0; |
| 1404 |
int i, len2; |
| 1405 |
unsigned int len; |
| 1406 |
int encflag; |
| 1407 |
char *encname = NULL; |
| 1408 |
buffer_t *blob = NULL, *blob2 = NULL; |
| 1409 |
const struct ssh2cipher *cipher = NULL; |
| 1410 |
struct sshcipher_ctx *cc = NULL; |
| 1411 |
|
| 1412 |
result = (Key *)malloc(sizeof(Key)); |
| 1413 |
ZeroMemory(result, sizeof(Key)); |
| 1414 |
result->type = KEY_NONE; |
| 1415 |
result->rsa = NULL; |
| 1416 |
result->dsa = NULL; |
| 1417 |
result->ecdsa = NULL; |
| 1418 |
|
| 1419 |
blob = buffer_init(); |
| 1420 |
blob2 = buffer_init(); |
| 1421 |
|
| 1422 |
// parse keyfile & decode blob |
| 1423 |
{ |
| 1424 |
char line[200], buf[100]; |
| 1425 |
BIO *bmem, *b64, *chain; |
| 1426 |
int st = 0; |
| 1427 |
|
| 1428 |
b64 = BIO_new(BIO_f_base64()); |
| 1429 |
bmem = BIO_new(BIO_s_mem()); |
| 1430 |
|
| 1431 |
while (fgets(line, sizeof(line), fp) != NULL) { |
| 1432 |
if (strncmp(line, "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----", strlen("---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----")) == 0) { |
| 1433 |
if (st == 0) { |
| 1434 |
st = 1; |
| 1435 |
continue; |
| 1436 |
} |
| 1437 |
else { |
| 1438 |
break; |
| 1439 |
} |
| 1440 |
} |
| 1441 |
else if (strncmp(line, "---- END SSH2 ENCRYPTED PRIVATE KEY ----", strlen("---- END SSH2 ENCRYPTED PRIVATE KEY ----")) == 0) { |
| 1442 |
break; |
| 1443 |
} |
| 1444 |
|
| 1445 |
if (st == 0) { |
| 1446 |
continue; |
| 1447 |
} |
| 1448 |
|
| 1449 |
if (strchr(line, ':') != NULL) { |
| 1450 |
if (st == 1) { |
| 1451 |
continue; |
| 1452 |
} |
| 1453 |
strncpy_s(errmsg, errmsg_len, "header found in body of key data", _TRUNCATE); |
| 1454 |
BIO_free_all(bmem); |
| 1455 |
goto error; |
| 1456 |
} |
| 1457 |
else if (st == 1) { |
| 1458 |
st = 2; |
| 1459 |
} |
| 1460 |
|
| 1461 |
BIO_write(bmem, line, strlen(line)); |
| 1462 |
} |
| 1463 |
|
| 1464 |
BIO_flush(bmem); |
| 1465 |
chain = BIO_push(b64, bmem); |
| 1466 |
BIO_set_mem_eof_return(chain, 0); |
| 1467 |
while ((len2 = BIO_read(chain, buf, sizeof(buf))) > 0) { |
| 1468 |
buffer_append(blob, buf, len2); |
| 1469 |
} |
| 1470 |
BIO_free_all(chain); |
| 1471 |
buffer_rewind(blob); |
| 1472 |
} |
| 1473 |
|
| 1474 |
if (blob->len < 8) { |
| 1475 |
strncpy_s(errmsg, errmsg_len, "key body not present", _TRUNCATE); |
| 1476 |
goto error; |
| 1477 |
} |
| 1478 |
i = buffer_get_int(blob); |
| 1479 |
if (i != 0x3f6ff9eb) { |
| 1480 |
strncpy_s(errmsg, errmsg_len, "magic number error", _TRUNCATE); |
| 1481 |
goto error; |
| 1482 |
} |
| 1483 |
len = buffer_get_int(blob); |
| 1484 |
if (len == 0 || len > blob->len) { |
| 1485 |
strncpy_s(errmsg, errmsg_len, "body size error", _TRUNCATE); |
| 1486 |
goto error; |
| 1487 |
} |
| 1488 |
|
| 1489 |
len = buffer_get_int(blob); |
| 1490 |
if (strncmp(blob->buf + blob->offset, "if-modn{sign{rsa", strlen("if-modn{sign{rsa") - 1) == 0) { |
| 1491 |
result->type = KEY_RSA; |
| 1492 |
} |
| 1493 |
else if (strncmp(blob->buf + blob->offset, "dl-modp{sign{dsa", strlen("dl-modp{sign{dsa") - 1) == 0) { |
| 1494 |
result->type = KEY_DSA; |
| 1495 |
} |
| 1496 |
else if (strncmp(blob->buf + blob->offset, "ec-modp", strlen("ec-modp") - 1) == 0) { |
| 1497 |
result->type = KEY_ECDSA256; |
| 1498 |
} |
| 1499 |
else { |
| 1500 |
strncpy_s(errmsg, errmsg_len, "unknown key type", _TRUNCATE); |
| 1501 |
goto error; |
| 1502 |
} |
| 1503 |
buffer_consume(blob, len); |
| 1504 |
|
| 1505 |
len = buffer_get_int(blob); |
| 1506 |
encname = (char *)malloc(len + 1); |
| 1507 |
strncpy_s(encname, len + 1, blob->buf + blob->offset, _TRUNCATE); |
| 1508 |
if (strcmp(encname, "3des-cbc") == 0) { |
| 1509 |
encflag = 1; |
| 1510 |
} |
| 1511 |
else if (strcmp(encname, "none") == 0) { |
| 1512 |
encflag = 0; |
| 1513 |
} |
| 1514 |
else { |
| 1515 |
strncpy_s(errmsg, errmsg_len, "unknown encryption type", _TRUNCATE); |
| 1516 |
goto error; |
| 1517 |
} |
| 1518 |
buffer_consume(blob, len); |
| 1519 |
|
| 1520 |
len = buffer_get_int(blob); |
| 1521 |
if (len > (blob->len - blob->offset)) { |
| 1522 |
strncpy_s(errmsg, errmsg_len, "body size error", _TRUNCATE); |
| 1523 |
goto error; |
| 1524 |
} |
| 1525 |
|
| 1526 |
// decrypt privkey with 3des-cbc |
| 1527 |
if (strcmp(encname, "3des-cbc") == 0) { |
| 1528 |
MD5_CTX md; |
| 1529 |
unsigned char key[32], iv[16]; |
| 1530 |
EVP_CIPHER_CTX *cipher_ctx = NULL; |
| 1531 |
char *decrypted = NULL; |
| 1532 |
int ret; |
| 1533 |
|
| 1534 |
cipher_ctx = EVP_CIPHER_CTX_new(); |
| 1535 |
if (cipher_ctx == NULL) { |
| 1536 |
strncpy_s(errmsg, errmsg_len, "Out of memory: EVP_CIPHER_CTX_new()", _TRUNCATE); |
| 1537 |
goto error; |
| 1538 |
} |
| 1539 |
|
| 1540 |
MD5_Init(&md); |
| 1541 |
MD5_Update(&md, passphrase, strlen(passphrase)); |
| 1542 |
MD5_Final(key, &md); |
| 1543 |
|
| 1544 |
MD5_Init(&md); |
| 1545 |
MD5_Update(&md, passphrase, strlen(passphrase)); |
| 1546 |
MD5_Update(&md, key, 16); |
| 1547 |
MD5_Final(key + 16, &md); |
| 1548 |
|
| 1549 |
memset(iv, 0, sizeof(iv)); |
| 1550 |
|
| 1551 |
// decrypt |
| 1552 |
cipher = get_cipher_by_name("3des-cbc"); |
| 1553 |
cipher_init_SSH2(&cc, cipher, key, 24, iv, 8, CIPHER_DECRYPT, pvar); |
| 1554 |
decrypted = (char *)malloc(len); |
| 1555 |
ret = EVP_Cipher(cc->evp, decrypted, blob->buf + blob->offset, len); |
| 1556 |
if (ret == 0) { |
| 1557 |
strncpy_s(errmsg, errmsg_len, "Key decrypt error", _TRUNCATE); |
| 1558 |
cipher_free_SSH2(cc); |
| 1559 |
goto error; |
| 1560 |
} |
| 1561 |
buffer_append(blob2, decrypted, len); |
| 1562 |
free(decrypted); |
| 1563 |
cipher_free_SSH2(cc); |
| 1564 |
|
| 1565 |
*invalid_passphrase = TRUE; |
| 1566 |
} |
| 1567 |
else { // none |
| 1568 |
buffer_append(blob2, blob->buf + blob->offset, len); |
| 1569 |
} |
| 1570 |
buffer_rewind(blob2); |
| 1571 |
|
| 1572 |
len = buffer_get_int(blob2); |
| 1573 |
if (len <= 0 || len > (blob2->len - blob2->offset)) { |
| 1574 |
strncpy_s(errmsg, errmsg_len, "blob size error", _TRUNCATE); |
| 1575 |
goto error; |
| 1576 |
} |
| 1577 |
|
| 1578 |
switch (result->type) { |
| 1579 |
case KEY_RSA: |
| 1580 |
{ |
| 1581 |
BIGNUM *e, *n, *d, *iqmp, *p, *q; |
| 1582 |
|
| 1583 |
result->rsa = RSA_new(); |
| 1584 |
if (result->rsa == NULL) { |
| 1585 |
strncpy_s(errmsg, errmsg_len, "key init error", _TRUNCATE); |
| 1586 |
goto error; |
| 1587 |
} |
| 1588 |
e = BN_new(); |
| 1589 |
n = BN_new(); |
| 1590 |
d = BN_new(); |
| 1591 |
RSA_set0_key(result->rsa, n, e, d); |
| 1592 |
p = BN_new(); |
| 1593 |
q = BN_new(); |
| 1594 |
RSA_set0_factors(result->rsa, p, q); |
| 1595 |
iqmp = BN_new(); |
| 1596 |
RSA_set0_crt_params(result->rsa, NULL, NULL, iqmp); |
| 1597 |
if (e == NULL || |
| 1598 |
n == NULL || |
| 1599 |
d == NULL || |
| 1600 |
p == NULL || |
| 1601 |
q == NULL || |
| 1602 |
iqmp == NULL) { |
| 1603 |
strncpy_s(errmsg, errmsg_len, "key init error", _TRUNCATE); |
| 1604 |
goto error; |
| 1605 |
} |
| 1606 |
|
| 1607 |
buffer_get_bignum_SECSH(blob2, e); |
| 1608 |
buffer_get_bignum_SECSH(blob2, d); |
| 1609 |
buffer_get_bignum_SECSH(blob2, n); |
| 1610 |
buffer_get_bignum_SECSH(blob2, iqmp); |
| 1611 |
buffer_get_bignum_SECSH(blob2, p); |
| 1612 |
buffer_get_bignum_SECSH(blob2, q); |
| 1613 |
|
| 1614 |
break; |
| 1615 |
} |
| 1616 |
case KEY_DSA: |
| 1617 |
{ |
| 1618 |
int param; |
| 1619 |
BIGNUM *p, *q, *g, *pub_key, *priv_key; |
| 1620 |
|
| 1621 |
result->dsa = DSA_new(); |
| 1622 |
if (result->dsa == NULL) { |
| 1623 |
strncpy_s(errmsg, errmsg_len, "key init error", _TRUNCATE); |
| 1624 |
goto error; |
| 1625 |
} |
| 1626 |
p = BN_new(); |
| 1627 |
q = BN_new(); |
| 1628 |
g = BN_new(); |
| 1629 |
DSA_set0_pqg(result->dsa, p, q, g); |
| 1630 |
pub_key = BN_new(); |
| 1631 |
priv_key = BN_new(); |
| 1632 |
DSA_set0_key(result->dsa, pub_key, priv_key); |
| 1633 |
if (p == NULL || |
| 1634 |
q == NULL || |
| 1635 |
g == NULL || |
| 1636 |
pub_key == NULL || |
| 1637 |
priv_key == NULL) { |
| 1638 |
strncpy_s(errmsg, errmsg_len, "key init error", _TRUNCATE); |
| 1639 |
goto error; |
| 1640 |
} |
| 1641 |
|
| 1642 |
param = buffer_get_int(blob2); |
| 1643 |
if (param != 0) { |
| 1644 |
strncpy_s(errmsg, errmsg_len, "predefined DSA parameters not supported", _TRUNCATE); |
| 1645 |
goto error; |
| 1646 |
} |
| 1647 |
buffer_get_bignum_SECSH(blob2, p); |
| 1648 |
buffer_get_bignum_SECSH(blob2, g); |
| 1649 |
buffer_get_bignum_SECSH(blob2, q); |
| 1650 |
buffer_get_bignum_SECSH(blob2, pub_key); |
| 1651 |
buffer_get_bignum_SECSH(blob2, priv_key); |
| 1652 |
|
| 1653 |
break; |
| 1654 |
} |
| 1655 |
case KEY_ECDSA256: |
| 1656 |
{ |
| 1657 |
unsigned int dummy, nid; |
| 1658 |
int success = 0; |
| 1659 |
char *curve = NULL; |
| 1660 |
BIGNUM *exponent = NULL; |
| 1661 |
EC_POINT *q = NULL; |
| 1662 |
BN_CTX *ctx = NULL; |
| 1663 |
|
| 1664 |
dummy = buffer_get_int(blob2); |
| 1665 |
curve = buffer_get_string_msg(blob2, NULL); |
| 1666 |
|
| 1667 |
if (strncmp(curve, "nistp256", strlen("nistp256")) == 0) { |
| 1668 |
result->type = KEY_ECDSA256; |
| 1669 |
} |
| 1670 |
else if (strncmp(curve, "nistp384", strlen("nistp384")) == 0) { |
| 1671 |
result->type = KEY_ECDSA384; |
| 1672 |
} |
| 1673 |
else if (strncmp(curve, "nistp521", strlen("nistp521")) == 0) { |
| 1674 |
result->type = KEY_ECDSA521; |
| 1675 |
} |
| 1676 |
else { |
| 1677 |
strncpy_s(errmsg, errmsg_len, "key type error", _TRUNCATE); |
| 1678 |
goto error; |
| 1679 |
} |
| 1680 |
|
| 1681 |
nid = keytype_to_cipher_nid(result->type); |
| 1682 |
if ((result->ecdsa = EC_KEY_new_by_curve_name(nid)) == NULL) |
| 1683 |
goto ecdsa_error; |
| 1684 |
if ((q = EC_POINT_new(EC_KEY_get0_group(result->ecdsa))) == NULL) |
| 1685 |
goto ecdsa_error; |
| 1686 |
if ((exponent = BN_new()) == NULL) |
| 1687 |
goto ecdsa_error; |
| 1688 |
|
| 1689 |
buffer_get_bignum_SECSH(blob2, exponent); |
| 1690 |
if (EC_KEY_set_private_key(result->ecdsa, exponent) != 1) |
| 1691 |
goto ecdsa_error; |
| 1692 |
if (key_ec_validate_private(result->ecdsa) != 0) |
| 1693 |
goto ecdsa_error; |
| 1694 |
|
| 1695 |
// �t�@�C�����������������i�[�������������������J�����v�Z�������� |
| 1696 |
if ((ctx = BN_CTX_new()) == NULL) |
| 1697 |
goto ecdsa_error; |
| 1698 |
if (!EC_POINT_mul(EC_KEY_get0_group(result->ecdsa), q, exponent, NULL, NULL, ctx)) { |
| 1699 |
goto ecdsa_error; |
| 1700 |
} |
| 1701 |
if (EC_KEY_set_public_key(result->ecdsa, q) != 1) |
| 1702 |
goto ecdsa_error; |
| 1703 |
if (key_ec_validate_public(EC_KEY_get0_group(result->ecdsa), |
| 1704 |
EC_KEY_get0_public_key(result->ecdsa)) != 0) |
| 1705 |
goto ecdsa_error; |
| 1706 |
|
| 1707 |
success = 1; |
| 1708 |
|
| 1709 |
ecdsa_error: |
| 1710 |
free(curve); |
| 1711 |
if (exponent) |
| 1712 |
BN_clear_free(exponent); |
| 1713 |
if (q) |
| 1714 |
EC_POINT_free(q); |
| 1715 |
if (ctx) |
| 1716 |
BN_CTX_free(ctx); |
| 1717 |
if (success == 0) |
| 1718 |
goto error; |
| 1719 |
|
| 1720 |
break; |
| 1721 |
} |
| 1722 |
default: |
| 1723 |
strncpy_s(errmsg, errmsg_len, "key type error", _TRUNCATE); |
| 1724 |
goto error; |
| 1725 |
break; |
| 1726 |
} |
| 1727 |
|
| 1728 |
*invalid_passphrase = FALSE; |
| 1729 |
|
| 1730 |
fclose(fp); |
| 1731 |
|
| 1732 |
if (encname != NULL) |
| 1733 |
free(encname); |
| 1734 |
|
| 1735 |
if (blob != NULL) |
| 1736 |
buffer_free(blob); |
| 1737 |
|
| 1738 |
if (blob2 != NULL) |
| 1739 |
buffer_free(blob2); |
| 1740 |
|
| 1741 |
return (result); |
| 1742 |
|
| 1743 |
error: |
| 1744 |
if (result != NULL) |
| 1745 |
key_free(result); |
| 1746 |
|
| 1747 |
if (fp != NULL) |
| 1748 |
fclose(fp); |
| 1749 |
|
| 1750 |
if (encname != NULL) |
| 1751 |
free(encname); |
| 1752 |
|
| 1753 |
if (blob != NULL) |
| 1754 |
buffer_free(blob); |
| 1755 |
|
| 1756 |
if (blob2 != NULL) |
| 1757 |
buffer_free(blob2); |
| 1758 |
|
| 1759 |
return (NULL); |
| 1760 |
} |
| 1761 |
|
| 1762 |
ssh2_keyfile_type get_ssh2_keytype(char *relative_name, |
| 1763 |
FILE **fp, |
| 1764 |
char *errmsg, |
| 1765 |
int errmsg_len) { |
| 1766 |
ssh2_keyfile_type ret = SSH2_KEYFILE_TYPE_NONE; |
| 1767 |
char filename[2048]; |
| 1768 |
char line[200]; |
| 1769 |
int i; |
| 1770 |
|
| 1771 |
// �����p�X�������p�X�����������B�������������������A�u�h�b�g���n�����v�f�B���N�g���� |
| 1772 |
// �����t�@�C�������������������������B(2005.2.7 yutaka) |
| 1773 |
get_teraterm_dir_relative_name(filename, sizeof(filename), |
| 1774 |
relative_name); |
| 1775 |
|
| 1776 |
*fp = fopen(filename, "r"); |
| 1777 |
if (*fp == NULL) { |
| 1778 |
strncpy_s(errmsg, errmsg_len, strerror(errno), _TRUNCATE); |
| 1779 |
return ret; |
| 1780 |
} |
| 1781 |
|
| 1782 |
while (fgets(line, sizeof(line), *fp) != NULL) { |
| 1783 |
for (i=0; keyfile_headers[i].type != SSH2_KEYFILE_TYPE_NONE; i++) { |
| 1784 |
if ( strncmp(line, keyfile_headers[i].header, strlen(keyfile_headers[i].header)) == 0) { |
| 1785 |
ret = keyfile_headers[i].type; |
| 1786 |
break; |
| 1787 |
} |
| 1788 |
} |
| 1789 |
if (ret != SSH2_KEYFILE_TYPE_NONE) |
| 1790 |
break; |
| 1791 |
} |
| 1792 |
|
| 1793 |
if (ret == SSH2_KEYFILE_TYPE_NONE) { |
| 1794 |
strncpy_s(errmsg, errmsg_len, "Unknown key file type.", _TRUNCATE); |
| 1795 |
fseek(*fp, 0, SEEK_SET); |
| 1796 |
return ret; |
| 1797 |
} |
| 1798 |
|
| 1799 |
fseek(*fp, 0, SEEK_SET); |
| 1800 |
return ret; |
| 1801 |
} |