1/* 2 * Dropbear - a SSH2 server 3 * 4 * Copyright (c) 2002,2003 Matt Johnston 5 * All rights reserved. 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a copy 8 * of this software and associated documentation files (the "Software"), to deal 9 * in the Software without restriction, including without limitation the rights 10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 * copies of the Software, and to permit persons to whom the Software is 12 * furnished to do so, subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included in 15 * all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 * SOFTWARE. */ 24 25#include "includes.h" 26#include "dbutil.h" 27#include "bignum.h" 28#include "dss.h" 29#include "buffer.h" 30#include "ssh.h" 31#include "random.h" 32 33/* Handle DSS (Digital Signature Standard), aka DSA (D.S. Algorithm), 34 * operations, such as key reading, signing, verification. Key generation 35 * is in gendss.c, since it isn't required in the server itself. 36 * 37 * See FIPS186 or the Handbook of Applied Cryptography for details of the 38 * algorithm */ 39 40#ifdef DROPBEAR_DSS 41 42/* Load a dss key from a buffer, initialising the values. 43 * The key will have the same format as buf_put_dss_key. 44 * These should be freed with dss_key_free. 45 * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ 46int buf_get_dss_pub_key(buffer* buf, dss_key *key) { 47 48 TRACE(("enter buf_get_dss_pub_key")) 49 dropbear_assert(key != NULL); 50 key->p = m_malloc(sizeof(mp_int)); 51 key->q = m_malloc(sizeof(mp_int)); 52 key->g = m_malloc(sizeof(mp_int)); 53 key->y = m_malloc(sizeof(mp_int)); 54 m_mp_init_multi(key->p, key->q, key->g, key->y, NULL); 55 key->x = NULL; 56 57 buf_incrpos(buf, 4+SSH_SIGNKEY_DSS_LEN); /* int + "ssh-dss" */ 58 if (buf_getmpint(buf, key->p) == DROPBEAR_FAILURE 59 || buf_getmpint(buf, key->q) == DROPBEAR_FAILURE 60 || buf_getmpint(buf, key->g) == DROPBEAR_FAILURE 61 || buf_getmpint(buf, key->y) == DROPBEAR_FAILURE) { 62 TRACE(("leave buf_get_dss_pub_key: failed reading mpints")) 63 return DROPBEAR_FAILURE; 64 } 65 66 if (mp_count_bits(key->p) < MIN_DSS_KEYLEN) { 67 dropbear_log(LOG_WARNING, "DSS key too short"); 68 TRACE(("leave buf_get_dss_pub_key: short key")) 69 return DROPBEAR_FAILURE; 70 } 71 72 TRACE(("leave buf_get_dss_pub_key: success")) 73 return DROPBEAR_SUCCESS; 74} 75 76/* Same as buf_get_dss_pub_key, but reads a private "x" key at the end. 77 * Loads a private dss key from a buffer 78 * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ 79int buf_get_dss_priv_key(buffer* buf, dss_key *key) { 80 81 int ret = DROPBEAR_FAILURE; 82 83 dropbear_assert(key != NULL); 84 85 ret = buf_get_dss_pub_key(buf, key); 86 if (ret == DROPBEAR_FAILURE) { 87 return DROPBEAR_FAILURE; 88 } 89 90 key->x = m_malloc(sizeof(mp_int)); 91 m_mp_init(key->x); 92 ret = buf_getmpint(buf, key->x); 93 if (ret == DROPBEAR_FAILURE) { 94 m_free(key->x); 95 } 96 97 return ret; 98} 99 100 101/* Clear and free the memory used by a public or private key */ 102void dss_key_free(dss_key *key) { 103 104 TRACE(("enter dsa_key_free")) 105 if (key == NULL) { 106 TRACE(("enter dsa_key_free: key == NULL")) 107 return; 108 } 109 if (key->p) { 110 mp_clear(key->p); 111 m_free(key->p); 112 } 113 if (key->q) { 114 mp_clear(key->q); 115 m_free(key->q); 116 } 117 if (key->g) { 118 mp_clear(key->g); 119 m_free(key->g); 120 } 121 if (key->y) { 122 mp_clear(key->y); 123 m_free(key->y); 124 } 125 if (key->x) { 126 mp_clear(key->x); 127 m_free(key->x); 128 } 129 m_free(key); 130 TRACE(("leave dsa_key_free")) 131} 132 133/* put the dss public key into the buffer in the required format: 134 * 135 * string "ssh-dss" 136 * mpint p 137 * mpint q 138 * mpint g 139 * mpint y 140 */ 141void buf_put_dss_pub_key(buffer* buf, dss_key *key) { 142 143 dropbear_assert(key != NULL); 144 buf_putstring(buf, SSH_SIGNKEY_DSS, SSH_SIGNKEY_DSS_LEN); 145 buf_putmpint(buf, key->p); 146 buf_putmpint(buf, key->q); 147 buf_putmpint(buf, key->g); 148 buf_putmpint(buf, key->y); 149 150} 151 152/* Same as buf_put_dss_pub_key, but with the private "x" key appended */ 153void buf_put_dss_priv_key(buffer* buf, dss_key *key) { 154 155 dropbear_assert(key != NULL); 156 buf_put_dss_pub_key(buf, key); 157 buf_putmpint(buf, key->x); 158 159} 160 161#ifdef DROPBEAR_SIGNKEY_VERIFY 162/* Verify a DSS signature (in buf) made on data by the key given. 163 * returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ 164int buf_dss_verify(buffer* buf, dss_key *key, const unsigned char* data, 165 unsigned int len) { 166 167 unsigned char msghash[SHA1_HASH_SIZE]; 168 hash_state hs; 169 int ret = DROPBEAR_FAILURE; 170 DEF_MP_INT(val1); 171 DEF_MP_INT(val2); 172 DEF_MP_INT(val3); 173 DEF_MP_INT(val4); 174 char * string = NULL; 175 int stringlen; 176 177 TRACE(("enter buf_dss_verify")) 178 dropbear_assert(key != NULL); 179 180 m_mp_init_multi(&val1, &val2, &val3, &val4, NULL); 181 182 /* get blob, check length */ 183 string = buf_getstring(buf, &stringlen); 184 if (stringlen != 2*SHA1_HASH_SIZE) { 185 goto out; 186 } 187 188 /* hash the data */ 189 sha1_init(&hs); 190 sha1_process(&hs, data, len); 191 sha1_done(&hs, msghash); 192 193 /* create the signature - s' and r' are the received signatures in buf */ 194 /* w = (s')-1 mod q */ 195 /* let val1 = s' */ 196 bytes_to_mp(&val1, &string[SHA1_HASH_SIZE], SHA1_HASH_SIZE); 197 198 if (mp_cmp(&val1, key->q) != MP_LT) { 199 TRACE(("verify failed, s' >= q")) 200 goto out; 201 } 202 /* let val2 = w = (s')^-1 mod q*/ 203 if (mp_invmod(&val1, key->q, &val2) != MP_OKAY) { 204 goto out; 205 } 206 207 /* u1 = ((SHA(M')w) mod q */ 208 /* let val1 = SHA(M') = msghash */ 209 bytes_to_mp(&val1, msghash, SHA1_HASH_SIZE); 210 211 /* let val3 = u1 = ((SHA(M')w) mod q */ 212 if (mp_mulmod(&val1, &val2, key->q, &val3) != MP_OKAY) { 213 goto out; 214 } 215 216 /* u2 = ((r')w) mod q */ 217 /* let val1 = r' */ 218 bytes_to_mp(&val1, &string[0], SHA1_HASH_SIZE); 219 if (mp_cmp(&val1, key->q) != MP_LT) { 220 TRACE(("verify failed, r' >= q")) 221 goto out; 222 } 223 /* let val4 = u2 = ((r')w) mod q */ 224 if (mp_mulmod(&val1, &val2, key->q, &val4) != MP_OKAY) { 225 goto out; 226 } 227 228 /* v = (((g)^u1 (y)^u2) mod p) mod q */ 229 /* val2 = g^u1 mod p */ 230 if (mp_exptmod(key->g, &val3, key->p, &val2) != MP_OKAY) { 231 goto out; 232 } 233 /* val3 = y^u2 mod p */ 234 if (mp_exptmod(key->y, &val4, key->p, &val3) != MP_OKAY) { 235 goto out; 236 } 237 /* val4 = ((g)^u1 (y)^u2) mod p */ 238 if (mp_mulmod(&val2, &val3, key->p, &val4) != MP_OKAY) { 239 goto out; 240 } 241 /* val2 = v = (((g)^u1 (y)^u2) mod p) mod q */ 242 if (mp_mod(&val4, key->q, &val2) != MP_OKAY) { 243 goto out; 244 } 245 246 /* check whether signatures verify */ 247 if (mp_cmp(&val2, &val1) == MP_EQ) { 248 /* good sig */ 249 ret = DROPBEAR_SUCCESS; 250 } 251 252out: 253 mp_clear_multi(&val1, &val2, &val3, &val4, NULL); 254 m_free(string); 255 256 return ret; 257 258} 259#endif /* DROPBEAR_SIGNKEY_VERIFY */ 260 261#ifdef DSS_PROTOK 262/* convert an unsigned mp into an array of bytes, malloced. 263 * This array must be freed after use, len contains the length of the array, 264 * if len != NULL */ 265static unsigned char* mptobytes(mp_int *mp, int *len) { 266 267 unsigned char* ret; 268 int size; 269 270 size = mp_unsigned_bin_size(mp); 271 ret = m_malloc(size); 272 if (mp_to_unsigned_bin(mp, ret) != MP_OKAY) { 273 dropbear_exit("mem alloc error"); 274 } 275 if (len != NULL) { 276 *len = size; 277 } 278 return ret; 279} 280#endif 281 282/* Sign the data presented with key, writing the signature contents 283 * to the buffer 284 * 285 * When DSS_PROTOK is #defined: 286 * The alternate k generation method is based on the method used in PuTTY. 287 * In particular to avoid being vulnerable to attacks using flaws in random 288 * generation of k, we use the following: 289 * 290 * proto_k = SHA512 ( SHA512(x) || SHA160(message) ) 291 * k = proto_k mod q 292 * 293 * Now we aren't relying on the random number generation to protect the private 294 * key x, which is a long term secret */ 295void buf_put_dss_sign(buffer* buf, dss_key *key, const unsigned char* data, 296 unsigned int len) { 297 298 unsigned char msghash[SHA1_HASH_SIZE]; 299 unsigned int writelen; 300 unsigned int i; 301#ifdef DSS_PROTOK 302 unsigned char privkeyhash[SHA512_HASH_SIZE]; 303 unsigned char *privkeytmp; 304 unsigned char proto_k[SHA512_HASH_SIZE]; 305 DEF_MP_INT(dss_protok); 306#endif 307 DEF_MP_INT(dss_k); 308 DEF_MP_INT(dss_m); 309 DEF_MP_INT(dss_temp1); 310 DEF_MP_INT(dss_temp2); 311 DEF_MP_INT(dss_r); 312 DEF_MP_INT(dss_s); 313 hash_state hs; 314 315 TRACE(("enter buf_put_dss_sign")) 316 dropbear_assert(key != NULL); 317 318 /* hash the data */ 319 sha1_init(&hs); 320 sha1_process(&hs, data, len); 321 sha1_done(&hs, msghash); 322 323 m_mp_init_multi(&dss_k, &dss_temp1, &dss_temp2, &dss_r, &dss_s, 324 &dss_m, NULL); 325#ifdef DSS_PROTOK 326 /* hash the privkey */ 327 privkeytmp = mptobytes(key->x, &i); 328 sha512_init(&hs); 329 sha512_process(&hs, "the quick brown fox jumped over the lazy dog", 44); 330 sha512_process(&hs, privkeytmp, i); 331 sha512_done(&hs, privkeyhash); 332 m_burn(privkeytmp, i); 333 m_free(privkeytmp); 334 335 /* calculate proto_k */ 336 sha512_init(&hs); 337 sha512_process(&hs, privkeyhash, SHA512_HASH_SIZE); 338 sha512_process(&hs, msghash, SHA1_HASH_SIZE); 339 sha512_done(&hs, proto_k); 340 341 /* generate k */ 342 m_mp_init(&dss_protok); 343 bytes_to_mp(&dss_protok, proto_k, SHA512_HASH_SIZE); 344 if (mp_mod(&dss_protok, key->q, &dss_k) != MP_OKAY) { 345 dropbear_exit("dss error"); 346 } 347 mp_clear(&dss_protok); 348 m_burn(proto_k, SHA512_HASH_SIZE); 349#else /* DSS_PROTOK not defined*/ 350 gen_random_mpint(key->q, &dss_k); 351#endif 352 353 /* now generate the actual signature */ 354 bytes_to_mp(&dss_m, msghash, SHA1_HASH_SIZE); 355 356 /* g^k mod p */ 357 if (mp_exptmod(key->g, &dss_k, key->p, &dss_temp1) != MP_OKAY) { 358 dropbear_exit("dss error"); 359 } 360 /* r = (g^k mod p) mod q */ 361 if (mp_mod(&dss_temp1, key->q, &dss_r) != MP_OKAY) { 362 dropbear_exit("dss error"); 363 } 364 365 /* x*r mod q */ 366 if (mp_mulmod(&dss_r, key->x, key->q, &dss_temp1) != MP_OKAY) { 367 dropbear_exit("dss error"); 368 } 369 /* (SHA1(M) + xr) mod q) */ 370 if (mp_addmod(&dss_m, &dss_temp1, key->q, &dss_temp2) != MP_OKAY) { 371 dropbear_exit("dss error"); 372 } 373 374 /* (k^-1) mod q */ 375 if (mp_invmod(&dss_k, key->q, &dss_temp1) != MP_OKAY) { 376 dropbear_exit("dss error"); 377 } 378 379 /* s = (k^-1(SHA1(M) + xr)) mod q */ 380 if (mp_mulmod(&dss_temp1, &dss_temp2, key->q, &dss_s) != MP_OKAY) { 381 dropbear_exit("dss error"); 382 } 383 384 buf_putstring(buf, SSH_SIGNKEY_DSS, SSH_SIGNKEY_DSS_LEN); 385 buf_putint(buf, 2*SHA1_HASH_SIZE); 386 387 writelen = mp_unsigned_bin_size(&dss_r); 388 dropbear_assert(writelen <= SHA1_HASH_SIZE); 389 /* need to pad to 160 bits with leading zeros */ 390 for (i = 0; i < SHA1_HASH_SIZE - writelen; i++) { 391 buf_putbyte(buf, 0); 392 } 393 if (mp_to_unsigned_bin(&dss_r, buf_getwriteptr(buf, writelen)) 394 != MP_OKAY) { 395 dropbear_exit("dss error"); 396 } 397 mp_clear(&dss_r); 398 buf_incrwritepos(buf, writelen); 399 400 writelen = mp_unsigned_bin_size(&dss_s); 401 dropbear_assert(writelen <= SHA1_HASH_SIZE); 402 /* need to pad to 160 bits with leading zeros */ 403 for (i = 0; i < SHA1_HASH_SIZE - writelen; i++) { 404 buf_putbyte(buf, 0); 405 } 406 if (mp_to_unsigned_bin(&dss_s, buf_getwriteptr(buf, writelen)) 407 != MP_OKAY) { 408 dropbear_exit("dss error"); 409 } 410 mp_clear(&dss_s); 411 buf_incrwritepos(buf, writelen); 412 413 mp_clear_multi(&dss_k, &dss_temp1, &dss_temp2, &dss_r, &dss_s, 414 &dss_m, NULL); 415 416 /* create the signature to return */ 417 418 TRACE(("leave buf_put_dss_sign")) 419} 420 421#endif /* DROPBEAR_DSS */ 422