| 1 | /* |
| 2 | Name: rsakey.c |
| 3 | Purpose: Generate keys for the RSA cryptosystem. |
| 4 | Author: M. J. Fromberger |
| 5 | |
| 6 | Usage: rsakey [-e <expt>] <modbits> [<outfile>] |
| 7 | |
| 8 | Generates an RSA key pair with a modulus having <modbits> significant bits, |
| 9 | and writes it to the specified output file, or to the standard output. The |
| 10 | -e option allows the user to specify an encryption exponent; otherwise, an |
| 11 | encryption exponent is chosen at random. |
| 12 | |
| 13 | Primes p and q are obtained by reading random bits from /dev/random, setting |
| 14 | the low-order bit, and testing for primality. If the first candidate is not |
| 15 | prime, successive odd candidates are tried until a probable prime is found. |
| 16 | |
| 17 | Copyright (C) 2002-2008 Michael J. Fromberger, All Rights Reserved. |
| 18 | |
| 19 | Permission is hereby granted, free of charge, to any person obtaining a copy |
| 20 | of this software and associated documentation files (the "Software"), to deal |
| 21 | in the Software without restriction, including without limitation the rights |
| 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 23 | copies of the Software, and to permit persons to whom the Software is |
| 24 | furnished to do so, subject to the following conditions: |
| 25 | |
| 26 | The above copyright notice and this permission notice shall be included in |
| 27 | all copies or substantial portions of the Software. |
| 28 | |
| 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| 35 | SOFTWARE. |
| 36 | */ |
| 37 | |
| 38 | #include <errno.h> |
| 39 | #include <limits.h> |
| 40 | #include <stdio.h> |
| 41 | #include <stdlib.h> |
| 42 | #include <string.h> |
| 43 | |
| 44 | #include <getopt.h> |
| 45 | #include <unistd.h> |
| 46 | |
| 47 | #include "imath.h" |
| 48 | #include "iprime.h" |
| 49 | |
| 50 | typedef struct { |
| 51 | mpz_t p; |
| 52 | mpz_t q; |
| 53 | mpz_t n; |
| 54 | mpz_t e; |
| 55 | mpz_t d; |
| 56 | } rsa_key; |
| 57 | |
| 58 | /* Load the specified buffer with random bytes */ |
| 59 | int randomize(unsigned char *buf, size_t len); |
| 60 | |
| 61 | /* Overwrite the specified value with n_bits random bits */ |
| 62 | mp_result mp_int_randomize(mp_int a, mp_size n_bits); |
| 63 | |
| 64 | /* Find a prime starting from the given odd seed */ |
| 65 | mp_result find_prime(mp_int seed, FILE *fb); |
| 66 | |
| 67 | /* Initialize/destroy an rsa_key structure */ |
| 68 | mp_result rsa_key_init(rsa_key *kp); |
| 69 | void rsa_key_clear(rsa_key *kp); |
| 70 | void rsa_key_write(rsa_key *kp, FILE *ofp); |
| 71 | |
| 72 | int main(int argc, char *argv[]) { |
| 73 | int opt, modbits; |
| 74 | FILE *ofp = stdout; |
| 75 | char *expt = NULL; |
| 76 | rsa_key the_key; |
| 77 | mp_result res; |
| 78 | |
| 79 | /* Process command-line arguments */ |
| 80 | while ((opt = getopt(argc: argc, argv: argv, shortopts: "e:" )) != EOF) { |
| 81 | switch (opt) { |
| 82 | case 'e': |
| 83 | expt = optarg; |
| 84 | break; |
| 85 | default: |
| 86 | fprintf(stderr, format: "Usage: rsakey [-e <expt>] <modbits> [<outfile>]\n" ); |
| 87 | return 1; |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | if (optind >= argc) { |
| 92 | fprintf(stderr, format: "Error: You must specify the number of modulus bits.\n" ); |
| 93 | fprintf(stderr, format: "Usage: rsakey [-e <expt>] <modbits> [<outfile>]\n" ); |
| 94 | return 1; |
| 95 | } |
| 96 | modbits = (int)strtol(nptr: argv[optind++], NULL, base: 0); |
| 97 | if (modbits < CHAR_BIT) { |
| 98 | fprintf(stderr, format: "Error: Invalid value for number of modulus bits.\n" ); |
| 99 | return 1; |
| 100 | } |
| 101 | if (modbits % 2 == 1) ++modbits; |
| 102 | |
| 103 | /* Check if output file is specified */ |
| 104 | if (optind < argc) { |
| 105 | if ((ofp = fopen(filename: argv[optind], modes: "wt" )) == NULL) { |
| 106 | fprintf(stderr, |
| 107 | format: "Error: Unable to open output file for writing.\n" |
| 108 | " - Filename: %s\n" |
| 109 | " - Error: %s\n" , |
| 110 | argv[optind], strerror(errno)); |
| 111 | return 1; |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | if ((res = rsa_key_init(kp: &the_key)) != MP_OK) { |
| 116 | fprintf(stderr, |
| 117 | format: "Error initializing RSA key structure:\n" |
| 118 | " - %s (%d)\n" , |
| 119 | mp_error_string(res), res); |
| 120 | return 1; |
| 121 | } |
| 122 | |
| 123 | /* If specified, try to load the key exponent */ |
| 124 | if (expt != NULL) { |
| 125 | if ((res = mp_int_read_string(z: &(the_key.e), radix: 10, str: expt)) != MP_OK) { |
| 126 | fprintf(stderr, |
| 127 | format: "Error: Invalid value for encryption exponent.\n" |
| 128 | " - %s (%d)\n" , |
| 129 | mp_error_string(res), res); |
| 130 | goto EXIT; |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | if ((res = mp_int_randomize(a: &(the_key.p), n_bits: (modbits / 2))) != MP_OK) { |
| 135 | fprintf(stderr, |
| 136 | format: "Error: Unable to randomize first prime.\n" |
| 137 | " - %s (%d)\n" , |
| 138 | mp_error_string(res), res); |
| 139 | goto EXIT; |
| 140 | } |
| 141 | fprintf(stderr, format: "p: " ); |
| 142 | find_prime(seed: &(the_key.p), stderr); |
| 143 | |
| 144 | if ((res = mp_int_randomize(a: &(the_key.q), n_bits: (modbits / 2))) != MP_OK) { |
| 145 | fprintf(stderr, |
| 146 | format: "Error: Unable to randomize second prime.\n" |
| 147 | " - %s (%d)\n" , |
| 148 | mp_error_string(res), res); |
| 149 | goto EXIT; |
| 150 | } |
| 151 | fprintf(stderr, format: "\nq: " ); |
| 152 | find_prime(seed: &(the_key.q), stderr); |
| 153 | fputc(c: '\n', stderr); |
| 154 | |
| 155 | /* Temporarily, the key's "n" field will be (p - 1) * (q - 1) for |
| 156 | purposes of computing the decryption exponent. |
| 157 | */ |
| 158 | mp_int_mul(a: &(the_key.p), b: &(the_key.q), c: &(the_key.n)); |
| 159 | mp_int_sub(a: &(the_key.n), b: &(the_key.p), c: &(the_key.n)); |
| 160 | mp_int_sub(a: &(the_key.n), b: &(the_key.q), c: &(the_key.n)); |
| 161 | mp_int_add_value(a: &(the_key.n), value: 1, c: &(the_key.n)); |
| 162 | |
| 163 | if (expt == NULL && |
| 164 | (res = mp_int_randomize(a: &(the_key.e), n_bits: (modbits / 2))) != MP_OK) { |
| 165 | fprintf(stderr, |
| 166 | format: "Error: Unable to randomize encryption exponent.\n" |
| 167 | " - %s (%d)\n" , |
| 168 | mp_error_string(res), res); |
| 169 | goto EXIT; |
| 170 | } |
| 171 | while ((res = mp_int_invmod(a: &(the_key.e), m: &(the_key.n), c: &(the_key.d))) != |
| 172 | MP_OK) { |
| 173 | if (expt != NULL) { |
| 174 | fprintf(stderr, |
| 175 | format: "Error: Unable to compute decryption exponent.\n" |
| 176 | " - %s (%d)\n" , |
| 177 | mp_error_string(res), res); |
| 178 | goto EXIT; |
| 179 | } |
| 180 | if ((res = mp_int_randomize(a: &(the_key.e), n_bits: (modbits / 2))) != MP_OK) { |
| 181 | fprintf(stderr, |
| 182 | format: "Error: Unable to re-randomize encryption exponent.\n" |
| 183 | " - %s (%d)\n" , |
| 184 | mp_error_string(res), res); |
| 185 | goto EXIT; |
| 186 | } |
| 187 | } |
| 188 | |
| 189 | /* Recompute the real modulus, now that exponents are done. */ |
| 190 | mp_int_mul(a: &(the_key.p), b: &(the_key.q), c: &(the_key.n)); |
| 191 | |
| 192 | /* Write completed key to the specified output file */ |
| 193 | rsa_key_write(kp: &the_key, ofp); |
| 194 | |
| 195 | EXIT: |
| 196 | fclose(stream: ofp); |
| 197 | rsa_key_clear(kp: &the_key); |
| 198 | return 0; |
| 199 | } |
| 200 | |
| 201 | int randomize(unsigned char *buf, size_t len) { |
| 202 | FILE *rnd = fopen(filename: "/dev/random" , modes: "rb" ); |
| 203 | size_t nr; |
| 204 | |
| 205 | if (rnd == NULL) return -1; |
| 206 | |
| 207 | nr = fread(ptr: buf, size: sizeof(*buf), n: len, stream: rnd); |
| 208 | fclose(stream: rnd); |
| 209 | |
| 210 | return (int)nr; |
| 211 | } |
| 212 | |
| 213 | mp_result mp_int_randomize(mp_int a, mp_size n_bits) { |
| 214 | mp_size n_bytes = (n_bits + CHAR_BIT - 1) / CHAR_BIT; |
| 215 | unsigned char *buf; |
| 216 | mp_result res = MP_OK; |
| 217 | |
| 218 | if ((buf = malloc(size: n_bytes)) == NULL) return MP_MEMORY; |
| 219 | |
| 220 | if ((mp_size)randomize(buf, len: n_bytes) != n_bytes) { |
| 221 | res = MP_TRUNC; |
| 222 | goto CLEANUP; |
| 223 | } |
| 224 | |
| 225 | /* Clear bits beyond the number requested */ |
| 226 | if (n_bits % CHAR_BIT != 0) { |
| 227 | unsigned char b_mask = (1 << (n_bits % CHAR_BIT)) - 1; |
| 228 | unsigned char t_mask = (1 << (n_bits % CHAR_BIT)) >> 1; |
| 229 | |
| 230 | buf[0] &= b_mask; |
| 231 | buf[0] |= t_mask; |
| 232 | } |
| 233 | |
| 234 | /* Set low-order bit to insure value is odd */ |
| 235 | buf[n_bytes - 1] |= 1; |
| 236 | |
| 237 | res = mp_int_read_unsigned(z: a, buf, len: n_bytes); |
| 238 | |
| 239 | CLEANUP: |
| 240 | memset(s: buf, c: 0, n: n_bytes); |
| 241 | free(ptr: buf); |
| 242 | |
| 243 | return res; |
| 244 | } |
| 245 | |
| 246 | mp_result find_prime(mp_int seed, FILE *fb) { |
| 247 | mp_result res; |
| 248 | int count = 0; |
| 249 | |
| 250 | if (mp_int_is_even(z: seed)) |
| 251 | if ((res = mp_int_add_value(a: seed, value: 1, c: seed)) != MP_OK) return res; |
| 252 | |
| 253 | while ((res = mp_int_is_prime(z: seed)) == MP_FALSE) { |
| 254 | ++count; |
| 255 | |
| 256 | if (fb != NULL && (count % 50) == 0) fputc(c: '.', stream: fb); |
| 257 | |
| 258 | if ((res = mp_int_add_value(a: seed, value: 2, c: seed)) != MP_OK) return res; |
| 259 | } |
| 260 | |
| 261 | if (res == MP_TRUE && fb != NULL) fputc(c: '+', stream: fb); |
| 262 | |
| 263 | return res; |
| 264 | } |
| 265 | |
| 266 | mp_result rsa_key_init(rsa_key *kp) { |
| 267 | mp_int_init(z: &(kp->p)); |
| 268 | mp_int_init(z: &(kp->q)); |
| 269 | mp_int_init(z: &(kp->n)); |
| 270 | mp_int_init(z: &(kp->e)); |
| 271 | mp_int_init(z: &(kp->d)); |
| 272 | |
| 273 | return MP_OK; |
| 274 | } |
| 275 | |
| 276 | void rsa_key_clear(rsa_key *kp) { |
| 277 | mp_int_clear(z: &(kp->p)); |
| 278 | mp_int_clear(z: &(kp->q)); |
| 279 | mp_int_clear(z: &(kp->n)); |
| 280 | mp_int_clear(z: &(kp->e)); |
| 281 | mp_int_clear(z: &(kp->d)); |
| 282 | } |
| 283 | |
| 284 | void rsa_key_write(rsa_key *kp, FILE *ofp) { |
| 285 | int len; |
| 286 | char *obuf; |
| 287 | |
| 288 | len = mp_int_string_len(z: &(kp->n), radix: 10); |
| 289 | obuf = malloc(size: len); |
| 290 | mp_int_to_string(z: &(kp->p), radix: 10, str: obuf, limit: len); |
| 291 | fprintf(stream: ofp, format: "p = %s\n" , obuf); |
| 292 | mp_int_to_string(z: &(kp->q), radix: 10, str: obuf, limit: len); |
| 293 | fprintf(stream: ofp, format: "q = %s\n" , obuf); |
| 294 | mp_int_to_string(z: &(kp->e), radix: 10, str: obuf, limit: len); |
| 295 | fprintf(stream: ofp, format: "e = %s\n" , obuf); |
| 296 | mp_int_to_string(z: &(kp->d), radix: 10, str: obuf, limit: len); |
| 297 | fprintf(stream: ofp, format: "d = %s\n" , obuf); |
| 298 | mp_int_to_string(z: &(kp->n), radix: 10, str: obuf, limit: len); |
| 299 | fprintf(stream: ofp, format: "n = %s\n" , obuf); |
| 300 | |
| 301 | free(ptr: obuf); |
| 302 | } |
| 303 | |
| 304 | /* Here there be dragons */ |
| 305 | |