| 1 | /* Copyright (C) 2012 Phil Pennock. |
| 2 | * This is distributed as part of Exim and licensed under the GPL. |
| 3 | * See the file "NOTICE" for more details. |
| 4 | */ |
| 5 | |
| 6 | /* Build with: |
| 7 | * c99 $(pkg-config --cflags openssl) gen_pkcs3.c $(pkg-config --libs openssl) |
| 8 | */ |
| 9 | |
| 10 | /* |
| 11 | * Rationale: |
| 12 | * The Diffie-Hellman primes which are embedded into Exim as named primes for |
| 13 | * the tls_dhparam option are in the std-crypto.c file. The source for those |
| 14 | * comes from various RFCs, where they are given in hexadecimal form. |
| 15 | * |
| 16 | * This tool provides convenient conversion, to reduce the risk of human |
| 17 | * error in transcription. |
| 18 | */ |
| 19 | |
| 20 | #include <ctype.h> |
| 21 | #include <errno.h> |
| 22 | #include <stdbool.h> |
| 23 | #include <stdio.h> |
| 24 | #include <stdlib.h> |
| 25 | #include <string.h> |
| 26 | #include <unistd.h> |
| 27 | |
| 28 | #include <openssl/bio.h> |
| 29 | #include <openssl/bn.h> |
| 30 | #include <openssl/dh.h> |
| 31 | #include <openssl/err.h> |
| 32 | #include <openssl/pem.h> |
| 33 | |
| 34 | extern const char *__progname; |
| 35 | |
| 36 | |
| 37 | void __attribute__((__noreturn__)) __attribute__((__format__(printf, 1, 2))) |
| 38 | die(const char *fmt, ...) |
| 39 | { |
| 40 | va_list ap; |
| 41 | |
| 42 | fflush(NULL); |
| 43 | fprintf(stderr, "%s: ", __progname); |
| 44 | va_start(ap, fmt); |
| 45 | vfprintf(stderr, fmt, ap); |
| 46 | va_end(ap); |
| 47 | fprintf(stderr, "\n"); |
| 48 | fflush(stderr); |
| 49 | exit(1); |
| 50 | } |
| 51 | |
| 52 | |
| 53 | void __attribute__((__noreturn__)) |
| 54 | die_openssl_err(const char *msg) |
| 55 | { |
| 56 | char err_string[250]; |
| 57 | unsigned long e; |
| 58 | |
| 59 | ERR_error_string_n(ERR_get_error(), err_string, sizeof(err_string)); |
| 60 | die("%s: %s", msg, err_string); |
| 61 | } |
| 62 | |
| 63 | |
| 64 | static BIGNUM * |
| 65 | bn_from_text(const char *text) |
| 66 | { |
| 67 | BIGNUM *b; |
| 68 | char *p, *spaceless; |
| 69 | const char *q, *end; |
| 70 | size_t len; |
| 71 | int rc; |
| 72 | |
| 73 | len = strlen(text); |
| 74 | spaceless = malloc(len); |
| 75 | if (!spaceless) |
| 76 | die("malloc(%zu) failed: %s", len, strerror(errno)); |
| 77 | |
| 78 | for (p = spaceless, q = text, end = text + len; |
| 79 | q < end; |
| 80 | ++q) { |
| 81 | if (!isspace(*q)) |
| 82 | *p++ = *q; |
| 83 | } |
| 84 | |
| 85 | b = NULL; |
| 86 | rc = BN_hex2bn(&b, spaceless); |
| 87 | |
| 88 | if (rc != p - spaceless) |
| 89 | die("BN_hex2bn did not convert entire input; took %d of %z bytes", |
| 90 | rc, p - spaceless); |
| 91 | |
| 92 | return b; |
| 93 | } |
| 94 | |
| 95 | |
| 96 | static void |
| 97 | our_dh_check(DH *dh) |
| 98 | { |
| 99 | int rc, errflags = 0; |
| 100 | |
| 101 | rc = DH_check(dh, &errflags); |
| 102 | if (!rc) die_openssl_err("DH_check() could not be performed");; |
| 103 | |
| 104 | /* We ignore DH_UNABLE_TO_CHECK_GENERATOR because some of the invocations |
| 105 | * deliberately provide generators other than 2 or 5. */ |
| 106 | |
| 107 | if (errflags & DH_CHECK_P_NOT_SAFE_PRIME) |
| 108 | die("DH_check(): p not a safe prime"); |
| 109 | if (errflags & DH_NOT_SUITABLE_GENERATOR) |
| 110 | die("DH_check(): g not suitable as generator"); |
| 111 | } |
| 112 | |
| 113 | |
| 114 | static void |
| 115 | emit_c_format_dh(FILE *stream, DH *dh) |
| 116 | { |
| 117 | BIO *bio; |
| 118 | long length; |
| 119 | char *data, *end, *p, *nl; |
| 120 | |
| 121 | bio = BIO_new(BIO_s_mem()); |
| 122 | PEM_write_bio_DHparams(bio, dh); |
| 123 | length = BIO_get_mem_data(bio, &data); |
| 124 | if (!length) |
| 125 | die("no data in memory BIO to format for printing"); |
| 126 | if (length < 0) |
| 127 | die("grr, negative length memory not supported"); |
| 128 | end = data + length; |
| 129 | |
| 130 | for (p = data; p < end; /**/) { |
| 131 | nl = strchr(p, '\n'); |
| 132 | if (!nl) { |
| 133 | fprintf(stream, "\"%s\\n\"\n/* missing final newline */\n", p); |
| 134 | break; |
| 135 | } |
| 136 | *nl = '\0'; |
| 137 | fprintf(stream, "\"%s\\n\"\n", p); |
| 138 | p = nl + 1; |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | |
| 143 | void __attribute__((__noreturn__)) |
| 144 | usage(FILE *stream, int exitcode) |
| 145 | { |
| 146 | fprintf(stream, "Usage: %s [-CPcst] <dh_p> <dh_g>\n" |
| 147 | "Both dh_p and dh_g should be hex strings representing the numbers\n" |
| 148 | "They may contain whitespace.\n" |
| 149 | "\n" |
| 150 | " -C show C string form of PEM result\n" |
| 151 | " -P do not show PEM\n" |
| 152 | " -c run OpenSSL DH_check() on the DH object\n" |
| 153 | " -s show the parsed p and g\n" |
| 154 | " -t show text form of certificate\n" |
| 155 | |
| 156 | , __progname); |
| 157 | exit(exitcode); |
| 158 | } |
| 159 | |
| 160 | |
| 161 | int |
| 162 | main(int argc, char *argv[]) |
| 163 | { |
| 164 | BIGNUM *p, *g; |
| 165 | DH *dh; |
| 166 | int ch; |
| 167 | bool perform_dh_check = false; |
| 168 | bool show_c_form = false; |
| 169 | bool show_numbers = false; |
| 170 | bool show_pem = true; |
| 171 | bool show_text = false; |
| 172 | |
| 173 | while ((ch = getopt(argc, argv, "CPcsth")) != -1) { |
| 174 | switch (ch) { |
| 175 | case 'C': |
| 176 | show_c_form = true; |
| 177 | break; |
| 178 | case 'P': |
| 179 | show_pem = false; |
| 180 | break; |
| 181 | case 'c': |
| 182 | perform_dh_check = true; |
| 183 | break; |
| 184 | case 's': |
| 185 | show_numbers = true; |
| 186 | break; |
| 187 | case 't': |
| 188 | show_text = true; |
| 189 | break; |
| 190 | |
| 191 | case 'h': |
| 192 | usage(stdout, 0); |
| 193 | case '?': |
| 194 | die("Unknown option or missing argument -%c", optopt); |
| 195 | default: |
| 196 | die("Unhandled option -%c", ch); |
| 197 | } |
| 198 | } |
| 199 | |
| 200 | optind -= 1; |
| 201 | argc -= optind; |
| 202 | argv += optind; |
| 203 | |
| 204 | if (argc != 3) { |
| 205 | fprintf(stderr, "argc: %d\n", argc); |
| 206 | usage(stderr, 1); |
| 207 | } |
| 208 | |
| 209 | p = bn_from_text(argv[1]); |
| 210 | g = bn_from_text(argv[2]); |
| 211 | |
| 212 | if (show_numbers) { |
| 213 | printf("p = "); |
| 214 | BN_print_fp(stdout, p); |
| 215 | printf("\ng = "); |
| 216 | BN_print_fp(stdout, g); |
| 217 | printf("\n"); |
| 218 | } |
| 219 | |
| 220 | dh = DH_new(); |
| 221 | dh->p = p; |
| 222 | dh->g = g; |
| 223 | |
| 224 | if (perform_dh_check) |
| 225 | our_dh_check(dh); |
| 226 | |
| 227 | if (show_text) |
| 228 | DHparams_print_fp(stdout, dh); |
| 229 | |
| 230 | if (show_pem) { |
| 231 | if (show_c_form) |
| 232 | emit_c_format_dh(stdout, dh); |
| 233 | else |
| 234 | PEM_write_DHparams(stdout, dh); |
| 235 | } |
| 236 | |
| 237 | DH_free(dh); /* should free p & g too */ |
| 238 | return 0; |
| 239 | } |