1 /* $OpenBSD: test_fuzz.c,v 1.14 2024/01/11 01:45:58 djm Exp $ */ 2 /* 3 * Fuzz tests for key parsing 4 * 5 * Placed in the public domain 6 */ 7 8 #include "includes.h" 9 10 #include <sys/types.h> 11 #include <sys/stat.h> 12 #include <fcntl.h> 13 #include <stdio.h> 14 #ifdef HAVE_STDINT_H 15 #include <stdint.h> 16 #endif 17 #include <stdlib.h> 18 #include <string.h> 19 #include <unistd.h> 20 21 #ifdef WITH_OPENSSL 22 #include <openssl/bn.h> 23 #include <openssl/rsa.h> 24 #include <openssl/dsa.h> 25 #include <openssl/objects.h> 26 #ifdef OPENSSL_HAS_NISTP256 27 # include <openssl/ec.h> 28 #endif 29 #endif 30 31 #include "../test_helper/test_helper.h" 32 33 #include "ssherr.h" 34 #include "authfile.h" 35 #include "sshkey.h" 36 #include "sshbuf.h" 37 38 #include "common.h" 39 40 void sshkey_fuzz_tests(void); 41 42 static void 43 onerror(void *fuzz) 44 { 45 fprintf(stderr, "Failed during fuzz:\n"); 46 fuzz_dump((struct fuzz *)fuzz); 47 } 48 49 static void 50 public_fuzz(struct sshkey *k) 51 { 52 struct sshkey *k1; 53 struct sshbuf *buf; 54 struct fuzz *fuzz; 55 u_int fuzzers = FUZZ_1_BIT_FLIP | FUZZ_1_BYTE_FLIP | 56 FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END; 57 58 if (test_is_fast()) 59 fuzzers &= ~FUZZ_1_BIT_FLIP; 60 if (test_is_slow()) 61 fuzzers |= FUZZ_2_BIT_FLIP | FUZZ_2_BYTE_FLIP; 62 ASSERT_PTR_NE(buf = sshbuf_new(), NULL); 63 ASSERT_INT_EQ(sshkey_putb(k, buf), 0); 64 fuzz = fuzz_begin(fuzzers, sshbuf_mutable_ptr(buf), sshbuf_len(buf)); 65 ASSERT_INT_EQ(sshkey_from_blob(sshbuf_ptr(buf), sshbuf_len(buf), 66 &k1), 0); 67 sshkey_free(k1); 68 sshbuf_free(buf); 69 TEST_ONERROR(onerror, fuzz); 70 for(; !fuzz_done(fuzz); fuzz_next(fuzz)) { 71 if (sshkey_from_blob(fuzz_ptr(fuzz), fuzz_len(fuzz), &k1) == 0) 72 sshkey_free(k1); 73 } 74 fuzz_cleanup(fuzz); 75 } 76 77 static void 78 sig_fuzz(struct sshkey *k, const char *sig_alg) 79 { 80 struct fuzz *fuzz; 81 u_char *sig, c[] = "some junk to be signed"; 82 size_t l; 83 u_int fuzzers = FUZZ_1_BIT_FLIP | FUZZ_1_BYTE_FLIP | FUZZ_2_BYTE_FLIP | 84 FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END; 85 86 if (test_is_fast()) 87 fuzzers &= ~FUZZ_2_BYTE_FLIP; 88 if (test_is_slow()) 89 fuzzers |= FUZZ_2_BIT_FLIP; 90 91 ASSERT_INT_EQ(sshkey_sign(k, &sig, &l, c, sizeof(c), 92 sig_alg, NULL, NULL, 0), 0); 93 ASSERT_SIZE_T_GT(l, 0); 94 fuzz = fuzz_begin(fuzzers, sig, l); 95 ASSERT_INT_EQ(sshkey_verify(k, sig, l, c, sizeof(c), NULL, 0, NULL), 0); 96 free(sig); 97 TEST_ONERROR(onerror, fuzz); 98 for(; !fuzz_done(fuzz); fuzz_next(fuzz)) { 99 /* Ensure 1-bit difference at least */ 100 if (fuzz_matches_original(fuzz)) 101 continue; 102 ASSERT_INT_NE(sshkey_verify(k, fuzz_ptr(fuzz), fuzz_len(fuzz), 103 c, sizeof(c), NULL, 0, NULL), 0); 104 } 105 fuzz_cleanup(fuzz); 106 } 107 108 #define NUM_FAST_BASE64_TESTS 1024 109 110 void 111 sshkey_fuzz_tests(void) 112 { 113 struct sshkey *k1; 114 struct sshbuf *buf, *fuzzed; 115 struct fuzz *fuzz; 116 int r, i; 117 118 #ifdef WITH_OPENSSL 119 TEST_START("fuzz RSA private"); 120 buf = load_file("rsa_1"); 121 fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf), 122 sshbuf_len(buf)); 123 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); 124 sshkey_free(k1); 125 sshbuf_free(buf); 126 ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL); 127 TEST_ONERROR(onerror, fuzz); 128 for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) { 129 r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz)); 130 ASSERT_INT_EQ(r, 0); 131 if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0) 132 sshkey_free(k1); 133 sshbuf_reset(fuzzed); 134 if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS) 135 break; 136 } 137 sshbuf_free(fuzzed); 138 fuzz_cleanup(fuzz); 139 TEST_DONE(); 140 141 TEST_START("fuzz RSA new-format private"); 142 buf = load_file("rsa_n"); 143 fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf), 144 sshbuf_len(buf)); 145 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); 146 sshkey_free(k1); 147 sshbuf_free(buf); 148 ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL); 149 TEST_ONERROR(onerror, fuzz); 150 for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) { 151 r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz)); 152 ASSERT_INT_EQ(r, 0); 153 if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0) 154 sshkey_free(k1); 155 sshbuf_reset(fuzzed); 156 if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS) 157 break; 158 } 159 sshbuf_free(fuzzed); 160 fuzz_cleanup(fuzz); 161 TEST_DONE(); 162 163 #ifdef WITH_DSA 164 TEST_START("fuzz DSA private"); 165 buf = load_file("dsa_1"); 166 fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf), 167 sshbuf_len(buf)); 168 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); 169 sshkey_free(k1); 170 sshbuf_free(buf); 171 ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL); 172 TEST_ONERROR(onerror, fuzz); 173 for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) { 174 r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz)); 175 ASSERT_INT_EQ(r, 0); 176 if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0) 177 sshkey_free(k1); 178 sshbuf_reset(fuzzed); 179 if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS) 180 break; 181 } 182 sshbuf_free(fuzzed); 183 fuzz_cleanup(fuzz); 184 TEST_DONE(); 185 186 TEST_START("fuzz DSA new-format private"); 187 buf = load_file("dsa_n"); 188 fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf), 189 sshbuf_len(buf)); 190 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); 191 sshkey_free(k1); 192 sshbuf_free(buf); 193 ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL); 194 TEST_ONERROR(onerror, fuzz); 195 for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) { 196 r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz)); 197 ASSERT_INT_EQ(r, 0); 198 if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0) 199 sshkey_free(k1); 200 sshbuf_reset(fuzzed); 201 if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS) 202 break; 203 } 204 sshbuf_free(fuzzed); 205 fuzz_cleanup(fuzz); 206 TEST_DONE(); 207 #endif 208 209 #ifdef OPENSSL_HAS_ECC 210 TEST_START("fuzz ECDSA private"); 211 buf = load_file("ecdsa_1"); 212 fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf), 213 sshbuf_len(buf)); 214 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); 215 sshkey_free(k1); 216 sshbuf_free(buf); 217 ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL); 218 TEST_ONERROR(onerror, fuzz); 219 for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) { 220 r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz)); 221 ASSERT_INT_EQ(r, 0); 222 if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0) 223 sshkey_free(k1); 224 sshbuf_reset(fuzzed); 225 if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS) 226 break; 227 } 228 sshbuf_free(fuzzed); 229 fuzz_cleanup(fuzz); 230 TEST_DONE(); 231 232 TEST_START("fuzz ECDSA new-format private"); 233 buf = load_file("ecdsa_n"); 234 fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf), 235 sshbuf_len(buf)); 236 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); 237 sshkey_free(k1); 238 sshbuf_free(buf); 239 ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL); 240 TEST_ONERROR(onerror, fuzz); 241 for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) { 242 r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz)); 243 ASSERT_INT_EQ(r, 0); 244 if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0) 245 sshkey_free(k1); 246 sshbuf_reset(fuzzed); 247 if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS) 248 break; 249 } 250 sshbuf_free(fuzzed); 251 fuzz_cleanup(fuzz); 252 TEST_DONE(); 253 #endif /* OPENSSL_HAS_ECC */ 254 #endif /* WITH_OPENSSL */ 255 256 TEST_START("fuzz Ed25519 private"); 257 buf = load_file("ed25519_1"); 258 fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf), 259 sshbuf_len(buf)); 260 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); 261 sshkey_free(k1); 262 sshbuf_free(buf); 263 ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL); 264 TEST_ONERROR(onerror, fuzz); 265 for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) { 266 r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz)); 267 ASSERT_INT_EQ(r, 0); 268 if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0) 269 sshkey_free(k1); 270 sshbuf_reset(fuzzed); 271 if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS) 272 break; 273 } 274 sshbuf_free(fuzzed); 275 fuzz_cleanup(fuzz); 276 TEST_DONE(); 277 278 #ifdef WITH_OPENSSL 279 TEST_START("fuzz RSA public"); 280 buf = load_file("rsa_1"); 281 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); 282 sshbuf_free(buf); 283 public_fuzz(k1); 284 sshkey_free(k1); 285 TEST_DONE(); 286 287 TEST_START("fuzz RSA cert"); 288 ASSERT_INT_EQ(sshkey_load_cert(test_data_file("rsa_1"), &k1), 0); 289 public_fuzz(k1); 290 sshkey_free(k1); 291 TEST_DONE(); 292 293 #ifdef WITH_DSA 294 TEST_START("fuzz DSA public"); 295 buf = load_file("dsa_1"); 296 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); 297 sshbuf_free(buf); 298 public_fuzz(k1); 299 sshkey_free(k1); 300 TEST_DONE(); 301 302 TEST_START("fuzz DSA cert"); 303 ASSERT_INT_EQ(sshkey_load_cert(test_data_file("dsa_1"), &k1), 0); 304 public_fuzz(k1); 305 sshkey_free(k1); 306 TEST_DONE(); 307 #endif 308 309 #ifdef OPENSSL_HAS_ECC 310 TEST_START("fuzz ECDSA public"); 311 buf = load_file("ecdsa_1"); 312 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); 313 sshbuf_free(buf); 314 public_fuzz(k1); 315 sshkey_free(k1); 316 TEST_DONE(); 317 318 TEST_START("fuzz ECDSA cert"); 319 ASSERT_INT_EQ(sshkey_load_cert(test_data_file("ecdsa_1"), &k1), 0); 320 public_fuzz(k1); 321 sshkey_free(k1); 322 TEST_DONE(); 323 #endif /* OPENSSL_HAS_ECC */ 324 #endif /* WITH_OPENSSL */ 325 326 TEST_START("fuzz Ed25519 public"); 327 buf = load_file("ed25519_1"); 328 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); 329 sshbuf_free(buf); 330 public_fuzz(k1); 331 sshkey_free(k1); 332 TEST_DONE(); 333 334 TEST_START("fuzz Ed25519 cert"); 335 ASSERT_INT_EQ(sshkey_load_cert(test_data_file("ed25519_1"), &k1), 0); 336 public_fuzz(k1); 337 sshkey_free(k1); 338 TEST_DONE(); 339 340 #ifdef WITH_OPENSSL 341 TEST_START("fuzz RSA sig"); 342 buf = load_file("rsa_1"); 343 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); 344 sshbuf_free(buf); 345 sig_fuzz(k1, "ssh-rsa"); 346 sshkey_free(k1); 347 TEST_DONE(); 348 349 TEST_START("fuzz RSA SHA256 sig"); 350 buf = load_file("rsa_1"); 351 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); 352 sshbuf_free(buf); 353 sig_fuzz(k1, "rsa-sha2-256"); 354 sshkey_free(k1); 355 TEST_DONE(); 356 357 TEST_START("fuzz RSA SHA512 sig"); 358 buf = load_file("rsa_1"); 359 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); 360 sshbuf_free(buf); 361 sig_fuzz(k1, "rsa-sha2-512"); 362 sshkey_free(k1); 363 TEST_DONE(); 364 365 #ifdef WITH_DSA 366 TEST_START("fuzz DSA sig"); 367 buf = load_file("dsa_1"); 368 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); 369 sshbuf_free(buf); 370 sig_fuzz(k1, NULL); 371 sshkey_free(k1); 372 TEST_DONE(); 373 #endif 374 375 #ifdef OPENSSL_HAS_ECC 376 TEST_START("fuzz ECDSA sig"); 377 buf = load_file("ecdsa_1"); 378 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); 379 sshbuf_free(buf); 380 sig_fuzz(k1, NULL); 381 sshkey_free(k1); 382 TEST_DONE(); 383 #endif /* OPENSSL_HAS_ECC */ 384 #endif /* WITH_OPENSSL */ 385 386 TEST_START("fuzz Ed25519 sig"); 387 buf = load_file("ed25519_1"); 388 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); 389 sshbuf_free(buf); 390 sig_fuzz(k1, NULL); 391 sshkey_free(k1); 392 TEST_DONE(); 393 394 /* XXX fuzz decoded new-format blobs too */ 395 /* XXX fuzz XMSS too */ 396 397 } 398