1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
|
/*
* rocks/crypt-openssl.c
*
* Cryptography for reliable sockets over the OpenSSL API.
* See www.openssl.org.
*
* Copyright (C) 2001 Victor Zandy
* See COPYING for distribution terms.
*/
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <netinet/in.h>
#include <assert.h>
#include <openssl/dh.h>
#include <openssl/rand.h>
#include <openssl/evp.h>
#include <openssl/bn.h>
#include "rs.h"
#include "log.h"
#define MAX_KEY_LEN 1024 /* Maximum size of all key buffers */
#define MSGLEN 32 /* Bytes in authentication challenge */
#define BUFLEN 128 /* Maximum buffer size for encryption ops */
#define CIPHER (EVP_bf_ecb()) /* OpenSSL cipher selector */
/* Shared Diffie-Hellman key exchange parameters. */
static const char *P = "DC04EB6EB146437F17F6422B78DE6F7B"; /* 128-bit prime */
static const char *G = "02";
/* FIXME: Make a per-rock copy of this? */
static DH *DH_PARAM; /* The parameters go in here for openssl. */
/* Debug flag. Debugging does not reveal sensitive keys or data. */
static int DEBUG = 0;
struct rs_key
{
char key[MAX_KEY_LEN]; /* secret shared key */
unsigned keylen; /* its length */
};
int
rs_init_crypt()
{
int fd;
char buf[128];
int rv;
if (DEBUG)
rs_log("crypto: Initializing OpenSSL cryptography.");
/* Seed the OpenSSL PRNG. */
fd = open("/dev/urandom", O_RDONLY); /* FIXME */
if (0 > fd)
return -1;
rv = read(fd, buf, sizeof(buf));
close(fd);
if (0 > rv)
return -1;
RAND_seed(buf, rv);
/* Convert DH parameters from ascii to bignums. */
DH_PARAM = DH_new();
if (!DH_PARAM)
return -1;
DH_PARAM->p = DH_PARAM->g = NULL;
if (!BN_hex2bn(&DH_PARAM->p, P))
return -1;
if (!BN_hex2bn(&DH_PARAM->g, G))
return -1;
if (DEBUG) {
rs_log("crypto: DH key exchange P = %s", P);
rs_log("crypto: DH key exchange G = %s", G);
}
return 0;
}
static rs_key_t
rs_key_new()
{
rs_key_t key;
key = (rs_key_t) malloc(sizeof(struct rs_key));
if (!key)
return NULL;
bzero(key, sizeof(struct rs_key));
return key;
}
int
rs_key_save(rs_key_t key, int fd)
{
return rs_xwrite(fd, key, sizeof(*key));
}
rs_key_t
rs_key_restore(int fd)
{
rs_key_t key;
key = rs_key_new();
if (!key)
return NULL;
if (0 > rs_xread(fd, key, sizeof(*key), 0))
return NULL;
return key;
}
void
rs_key_free(rs_key_t key)
{
if (key) {
bzero(key, sizeof(struct rs_key));
free(key);
}
}
rs_key_t
rs_key_exchange(int sock)
{
unsigned long len, nlen;
char buf[MAX_KEY_LEN];
BIGNUM *peer_key;
rs_key_t ret = NULL;
rs_key_t key;
if (DEBUG)
rs_log("crypto: Begin DH key exchange");
/* Make sure DH_generate_key and exit cleanup use fresh keys. */
DH_PARAM->priv_key = NULL;
key = rs_key_new();
if (!key)
goto out;
/* Create the public and private DH keys from P and G */
if (!DH_generate_key(DH_PARAM))
goto out;
/* Send our public key to peer. */
len = BN_num_bytes(DH_PARAM->pub_key);
assert(len <= MAX_KEY_LEN);
BN_bn2bin(DH_PARAM->pub_key, buf);
nlen = htonl(len);
if (0 > rs_xwrite(sock, &nlen, sizeof(nlen)))
goto out;
if (0 > rs_xwrite(sock, buf, len))
goto out;
/* Receive peer's public key. */
if (0 > rs_xread(sock, &nlen, sizeof(nlen), 0))
goto out;
len = ntohl(nlen);
if (len > MAX_KEY_LEN)
goto out;
if (0 > rs_xread(sock, buf, len, 0))
goto out;
peer_key = BN_bin2bn(buf, len, NULL);
if (!peer_key)
goto out;
/* Compute and store the secret shared key. */
len = DH_compute_key(buf, peer_key, DH_PARAM);
assert(len <= MAX_KEY_LEN);
memcpy(key->key, buf, len);
key->keylen = len;
if (DEBUG)
rs_log("crypto: established %d-bit key", len * 8);
BN_free(peer_key);
ret = key;
out:
/* Free PRIV_KEY to get new keys next time. */
BN_clear_free(DH_PARAM->priv_key);
DH_PARAM->priv_key = NULL;
if (!ret && DEBUG)
rs_log("crypto: Key exchange error.");
return ret;
}
static
char *ascii_text(unsigned char *bin, int len)
{
/* This function is for debugging only. */
static char buf[BUFLEN], *p;
int i;
for (i = 0, p = buf; i < len; i++, p += 2)
sprintf(p, "%02x", bin[i]);
buf[2*len] = '\0';
return buf;
}
/* Return 1 if peer authenticated successfully
Return 0 if peer failed
Return -1 on error. */
int
rs_mutual_auth(rs_key_t key, int sock)
{
char my_plain[BUFLEN];
char my_cipher[BUFLEN];
char peer_cipher[BUFLEN];
char peer_plain[BUFLEN];
char peer_reply[BUFLEN];
unsigned char iv[EVP_MAX_IV_LENGTH]; /* Initial vector */
EVP_CIPHER_CTX ex, dx;
EVP_CIPHER *cp;
int len, nlen, nl;
int my_cipher_len, peer_cipher_len, peer_plain_len, peer_reply_len;
int ret = -1;
if (DEBUG)
rs_log("crypto: Begin mutual authentication");
/* Initialize */
bzero(iv, EVP_MAX_IV_LENGTH); /* For Blowfish, okay to zero. */
EVP_EncryptInit(&ex, CIPHER, key->key, iv);
EVP_DecryptInit(&dx, CIPHER, key->key, iv);
/* FIXME: This avoids warnings in older versions of openssl;
but this would be better:
EVP_CIPHER_CTX_key_length(&ex) = key->keylen;
EVP_CIPHER_CTX_key_length(&dx) = key->keylen;
*/
cp = (EVP_CIPHER *) EVP_CIPHER_CTX_cipher(&ex);
EVP_CIPHER_key_length(cp) = key->keylen;
cp = (EVP_CIPHER *) EVP_CIPHER_CTX_cipher(&dx);
EVP_CIPHER_key_length(cp) = key->keylen;
/* Encrypt unpredictable plaintext message. */
if (!RAND_bytes(my_plain, MSGLEN))
goto out;
EVP_EncryptUpdate(&ex, my_cipher, &len, my_plain, MSGLEN);
EVP_EncryptFinal(&ex, my_cipher + len, &nl);
my_cipher_len = len + nl;
if (my_cipher_len > BUFLEN)
goto out;
/* Send ciphertext challenge to peer. */
nlen = htonl(my_cipher_len);
if (0 > rs_xwrite(sock, &nlen, sizeof(nlen)))
goto out;
if (0 > rs_xwrite(sock, my_cipher, my_cipher_len))
goto out;
if (DEBUG)
rs_log("crypto: Sent %d byte ciphertext challenge: %s",
MSGLEN, ascii_text(my_cipher, my_cipher_len));
/* Receive ciphertext challenge from peer. */
if (0 > rs_xread(sock, &nlen, sizeof(nlen), rs_opt_auth_timeout)) {
rs_log("crypto: timeout or error reading crypto from peer");
goto out;
}
peer_cipher_len = ntohl(nlen);
if (peer_cipher_len > BUFLEN)
goto out;
if (0 > rs_xread(sock, peer_cipher, peer_cipher_len,
rs_opt_auth_timeout)) {
rs_log("crypto: timeout or error reading crypto from peer");
goto out;
}
if (DEBUG)
rs_log("crypto: Received %d byte ciphertext challenge: %s",
peer_cipher_len,
ascii_text(peer_cipher, peer_cipher_len));
/* Compute and send plaintext response. */
EVP_DecryptUpdate(&dx, peer_plain, &len,
peer_cipher, peer_cipher_len);
if (!EVP_DecryptFinal(&dx, peer_plain + len, &nl))
goto out;
peer_plain_len = len + nl;
if (peer_plain_len > BUFLEN)
goto out;
nlen = htonl(peer_plain_len);
if (0 > rs_xwrite(sock, &nlen, sizeof(nlen)))
goto out;
if (0 > rs_xwrite(sock, peer_plain, peer_plain_len))
goto out;
if (DEBUG)
rs_log("crypto: Sent deciphered response to peer: %s",
ascii_text(peer_plain, peer_plain_len));
/* Receive plaintext response. */
if (0 > rs_xread(sock, &nlen, sizeof(nlen), rs_opt_auth_timeout)) {
rs_log("crypto: timeout or error reading crypto from peer");
goto out;
}
peer_reply_len = ntohl(nlen);
if (peer_reply_len > BUFLEN)
goto out;
if (0 > rs_xread(sock, peer_reply, peer_reply_len,
rs_opt_auth_timeout)) {
rs_log("crypto: timeout or error reading crypto from peer");
goto out;
}
if (DEBUG)
rs_log("crypto: Received deciphered response from peer: %s",
ascii_text(peer_reply, peer_reply_len));
/* Check */
if (peer_reply_len == MSGLEN
&& !memcmp(peer_reply, my_plain, MSGLEN)) {
ret = 1;
if (DEBUG)
rs_log("crypto: Peer authenticated");
} else {
ret = 0;
if (DEBUG)
rs_log("crypto: Peer REJECTED");
}
out:
if (0 > ret && DEBUG)
rs_log("crypto: Mutual authentication error.");
return ret;
}
|