src/vsl_util.c

Go to the documentation of this file.
00001 /*
00002  * Copyright 2006, Internet2
00003  * Legal conditions are in file LICENSE
00004  * (MD5 = c434f2e53b8089d8b4d0172c7ce07360).
00005  */
00006 
00007 /**
00008  * @file   vsl_util.c
00009  * @author Nikolaus Rath
00010  * @brief  Utility functions for the security layer
00011  *
00012  **/
00013 
00014 #include "vsl_api.h"
00015 #include "vsl_util.h"
00016 #include "vsl.h"
00017 #include "../poly1305aes/aes.h"
00018 #include "../poly1305aes/poly1305aes.h"
00019 #include <openssl/md5.h>
00020 #include <openssl/sha.h>
00021 #include <sys/param.h>
00022 #include <sys/types.h>
00023 #include <sys/stat.h>
00024 #include <fcntl.h>
00025 
00026 /**
00027  * Generate a random shared secret of length #VSL_KEYLEN
00028  *
00029  * @param[out] key generated key
00030  * @return
00031  *   - 0 on success
00032  **/
00033 int gen_key (unsigned char* key) {
00034     int fd;
00035     if ( (fd = open(RANDDEV, 0)) < 0)
00036         LOGFAIL(VSL_ERRNO, "cannot open %s for reading: %s!\n",
00037                 RANDDEV, strerror(errno));
00038     int ret;
00039     if ( (ret=read(fd, (void*)key, VSL_KEYLEN)) != VSL_KEYLEN) {
00040         close(fd);
00041         if (ret < 0)
00042             LOGFAIL(VSL_ERRNO, "cannot read from %s: %s!\n",
00043                     RANDDEV, strerror(errno));
00044         else
00045             LOGFATAL("cannot read enough random bytes!");
00046     }
00047 
00048     if ( close(fd) != 0)
00049         LOGFAIL(VSL_ERRNO, "cannot close %s: %s!\n",
00050                 RANDDEV, strerror(errno));
00051     return(0);
00052 }
00053 
00054 
00055 /**
00056  * Initialize session keys (client mode) from a shared
00057  * secret.
00058  *
00059  * @param[in] sock VSL socket
00060  * @param[in] key shared secret to generate keys from
00061  *
00062  **/
00063 void init_keys_c(vsl_sock* sock, const unsigned char* key) {
00064     LOG("Initializing client keys...");
00065     md5(key, VSL_ENC_KEYLEN, (unsigned char*) "SRVENC", 7, sock->enc_recv_key);
00066     md5(key, VSL_ENC_KEYLEN, (unsigned char*) "CLTENC", 7, sock->enc_send_key);
00067     sha256(key, VSL_AUTH_KEYLEN, (unsigned char*) "SRVAUT", 7, sock->auth_recv_key);
00068     sha256(key, VSL_AUTH_KEYLEN, (unsigned char*) "CLTAUT", 7, sock->auth_send_key);
00069     poly1305aes_clamp(sock->auth_send_key);
00070     poly1305aes_clamp(sock->auth_recv_key);
00071     LOG("enc send key is " KEY_STR ".", KEY_VEC(sock->enc_send_key));
00072     LOG("enc recv key is " KEY_STR ".", KEY_VEC(sock->enc_recv_key));
00073     LOG("auth send key is " KEY_STR ".", KEY_VEC(sock->auth_send_key));
00074     LOG("auth recv key is " KEY_STR ".", KEY_VEC(sock->auth_recv_key));
00075 }
00076 
00077 
00078 
00079 /**
00080  * Initialize session keys (server mode) from a shared
00081  * secret.
00082  *
00083  * @param[in] sock VSL socket
00084  * @param[in] key shared secret to generate keys from
00085  *
00086  **/
00087 void init_keys_s(vsl_sock* sock, const unsigned char* key) {
00088     LOG("Initializing server keys...");
00089     md5(key, VSL_ENC_KEYLEN, (unsigned char*) "SRVENC", 7, sock->enc_send_key);
00090     md5(key, VSL_ENC_KEYLEN, (unsigned char*) "CLTENC", 7, sock->enc_recv_key);
00091     sha256(key, VSL_AUTH_KEYLEN, (unsigned char*) "SRVAUT", 7, sock->auth_send_key);
00092     sha256(key, VSL_AUTH_KEYLEN, (unsigned char*) "CLTAUT", 7, sock->auth_recv_key);
00093     poly1305aes_clamp(sock->auth_send_key);
00094     poly1305aes_clamp(sock->auth_recv_key);
00095     LOG("enc send key is " KEY_STR ".", KEY_VEC(sock->enc_send_key));
00096     LOG("enc recv key is " KEY_STR ".", KEY_VEC(sock->enc_recv_key));
00097     LOG("auth send key is " KEY_STR ".", KEY_VEC(sock->auth_send_key));
00098     LOG("auth recv key is " KEY_STR ".", KEY_VEC(sock->auth_recv_key));
00099 }
00100 
00101 
00102 
00103 /**
00104  * Generate a 16 byte MD5 hash of two char arrays
00105  *
00106  * @param[in] s1 first char array
00107  * @param[in] l1 length of first array
00108  * @param[in] s2 second char array
00109  * @param[in] l2 length of second array
00110  * @param[out] hash resulting hash (length 16 bytes)
00111  **/
00112 void md5(const unsigned char* s1, int l1,
00113          const unsigned char* s2, int l2, unsigned char *hash) {
00114     unsigned char s[l1+l2];
00115     memcpy((void*)s, (void*) s1, l1);
00116     memcpy((void*)s + l1, (void*) s2, l2);
00117     MD5(s, l1+l2, hash);
00118 }
00119 
00120 /**
00121  * Generate a 32 byte SHA256 hash of two char arrays
00122  *
00123  * @param[in] s1 first char array
00124  * @param[in] l1 length of first array
00125  * @param[in] s2 second char array
00126  * @param[in] l2 length of second array
00127  * @param[out] hash resulting hash (length 32 bytes)
00128  **/
00129 void sha256(const unsigned char* s1, int l1,
00130             const unsigned char* s2, int l2, unsigned char *hash) {
00131     unsigned char s[l1+l2];
00132     memcpy((void*)s, (void*) s1, l1);
00133     memcpy((void*)s + l1, (void*) s2, l2);
00134     SHA256(s, l1+l2, hash);
00135 }
00136 
00137 
00138 /**
00139  * Check whether the underlying vfer fd is blocking
00140  *
00141  * @param[in] socket VSL socket
00142  *
00143  **/
00144 bool is_blocking (vsl_sock* socket) {
00145     int nonblocking = 0;
00146     int len = sizeof(int);
00147     int ret;
00148     if ( (ret=vfer_getsockopt(socket->vfd, VFERSO_NONBLOCK,
00149                               &nonblocking, &len)) != 0)
00150         LOGFATAL("can't get socket state: %s", vfer_errortext(ret));
00151     return(nonblocking == 0);
00152 }
00153 
00154 /**
00155  * Sets the underlying vfer fd to blocking or nonblocking
00156  *
00157  * @param[in] socket VSL socket
00158  * \param[in] mode whether to block or not
00159  *
00160  **/
00161 void set_blocking (vsl_sock* socket, bool mode) {
00162     int val;
00163     int ret;
00164     if (mode)
00165         val = 0;
00166     else
00167         val = 1;
00168     if ( (ret = vfer_setsockopt(socket->vfd, VFERSO_NONBLOCK,
00169                                 (void*) &val, sizeof(val))) != 0)
00170         LOGFATAL("can't set socket state: %s!", vfer_errortext(ret));
00171 }
00172 
00173 /**
00174  * Allocates a buffer and writes a sequence of variables into it. The
00175  * variables have to be given as pairs of a void pointer to the
00176  * variable and an size_t value describing the length of the variable in
00177  * bytes. The argument list has to be terminated with a \c NULL
00178  * pointer. The total length of the allocated buffer is written into
00179  * \a total.
00180  *
00181  * Example: \code
00182  * buf = write_objs(len,
00183  *                  (void*) &data1, sizeof(data1),
00184  *                  (void*) &data2, sizeof(data2));
00185  * \endcode
00186  * This example allocates a buffer of length <tt>sizeof(data1) +
00187  * sizeof(data2)</tt>, and writes the objects \c data1 and \c data2
00188  * into it.
00189  *
00190  * @param[out] total total allocated length
00191  * @param[in] ... list of alternating \c void* and \c size_t arguments,
00192  *                terminated by \c NULL.
00193  *
00194  * @return
00195  *    - pointer to the allocated buffer
00196  *    - NULL if allocation failed
00197  **/
00198 unsigned char* write_objs(size_t* total, ... ) {
00199     void *obj;
00200     unsigned char *buf, *start;
00201     size_t len;
00202 
00203     va_list ap1;
00204     va_list ap2;
00205 
00206     va_start(ap1, total);
00207     va_copy(ap2, ap1);
00208 
00209     /* Collect Lengths */
00210     len = 0;
00211     while ( va_arg(ap1, void*) != NULL )
00212         len += va_arg(ap1, size_t);
00213     va_end(ap1);
00214 
00215     /* Allocate memory */
00216     if ( (start = malloc(len)) == NULL)
00217         return NULL;
00218     buf = start;
00219     *total = len;
00220 
00221     /* Copy elements */
00222     while ( (obj = va_arg(ap2, void*)) != NULL ) {
00223         len = va_arg(ap2, size_t);
00224         memcpy(buf, obj, len);
00225         buf += len;
00226     }
00227 
00228     va_end(ap2);
00229     return(start);
00230 }
00231 
00232 
00233 /**
00234  * Decomposes a buffer into a number of separate variables.
00235  *
00236  * The variables have to be given as pairs of a void pointer to the
00237  * variable and an size_t value describing the length of the variable in
00238  * bytes. The argument list has to be terminated with a \c NULL
00239  * pointer. For each pair <tt> void* dat, size_t len</tt>, the next \a
00240  * len bytes of \a src are copied into \a dat. After the last object
00241  * was copied, a pointer to the remainder of \a src is returned.
00242  *
00243  * Example: \code
00244  * remainder = read_objs(src,
00245  *                       (void*) &data1, sizeof(data1),
00246  *                       (void*) &data2, sizeof(data2));
00247  * \endcode
00248  *
00249  * @param[in] src source buffer
00250  * @param[in] max length of source buffer
00251  * @param[out] ... list of alternating \c void* and \c size_t arguments,
00252  *                terminated by \c NULL.
00253  *
00254  * @return
00255  *    - pointer into to uncopied remainder  of \a src
00256  *    - \c NULL the length of the varables was bigger than the source
00257  *      buffer
00258  **/
00259 unsigned char* read_objs(unsigned char* src, size_t max, ... ) {
00260     void *buf;
00261     size_t len;
00262     va_list ap;
00263 
00264     va_start(ap, max);
00265 
00266     /* Read objects */
00267     while ( (buf = va_arg(ap, void*)) != NULL ) {
00268         len = va_arg(ap, size_t);
00269         max -= len;
00270         if (max < 0) {
00271             // clean the argument list before returning
00272             while ( va_arg(ap, void*) != NULL )
00273                 (void) va_arg(ap, size_t);
00274             va_end(ap);
00275             return NULL;
00276         }
00277         memcpy(buf, src, len);
00278         src += len;
00279     }
00280 
00281     va_end(ap);
00282     return(src);
00283 }
00284 
00285 
00286 /**
00287  * Convert uint64 from host byte order to network byte order.
00288  *
00289  * \param[in] val value to convert
00290  * \return
00291  *   \a val in network byte order
00292  **/
00293 #if __BYTE_ORDER == __BIG_ENDIAN || BYTE_ORDER == BIG_ENDIAN
00294 inline uint64_t htonll(uint64_t val) {
00295     return val;
00296 }
00297 #elif __BYTE_ORDER == __LITTLE_ENDIAN || BYTE_ORDER == LITTLE_ENDIAN
00298 inline uint64_t htonll(uint64_t val) {
00299      uint32_t high = (uint32_t) (val >> 32);
00300      uint32_t low = (uint32_t) val;
00301      return ( (uint64_t) htonl(high) + ((uint64_t) htonl(low) << 32));
00302 }
00303 #else
00304 #error "Can't detect byte order"
00305 #endif
00306 
00307 
00308 /**
00309  * Convert uint64 from host network order to host byte order.
00310  *
00311  * \param[in] val value to convert
00312  * \return
00313  *   \a val in network byte order
00314  **/
00315 #if __BYTE_ORDER == __BIG_ENDIAN || BYTE_ORDER == BIG_ENDIAN
00316 inline uint64_t ntohll(uint64_t val) {
00317     return val;
00318 }
00319 #elif __BYTE_ORDER == __LITTLE_ENDIAN || BYTE_ORDER == LITTLE_ENDIAN
00320 inline uint64_t ntohll(uint64_t val) {
00321      uint32_t high = (uint32_t) (val >> 32);
00322      uint32_t low = (uint32_t) val;
00323      return ( (uint64_t) ntohl(high) + ((uint64_t) ntohl(low) << 32));
00324 }
00325 #else
00326 #error "Can't detect byte order"
00327 #endif
00328 
00329 
00330 /**
00331  * Encrypts data in AES CTR mode. The counter is modified in place.
00332  * The first 8 bytes of the counter must contain the nonce and
00333  * the last 8 bytes should be initialised to zero.
00334  *
00335  * Note that the function cannot be called multiple times without
00336  * reinitalizing the counter before each invocation.
00337  *
00338  * @param ctr counter
00339  * \param[in] key encryption key, length #VSL_ENC_KEYLEN
00340  * \param buf buffer to encrypt
00341  * \param[in] len length of buffer
00342  *
00343  **/
00344 void aes_ctr (unsigned char* ctr,
00345               const unsigned char* key,
00346               unsigned char* buf,
00347               const size_t len) {
00348     int blocks = len / 16;
00349     int bytes = len % 16;
00350 
00351     int i;
00352     unsigned char c[16];
00353     for (i=1; i <= blocks; i++) {
00354         aes(c, key, ctr);
00355         xor16(buf, c);
00356         ctr_inc(ctr);
00357         buf += 16;
00358     }
00359 
00360     // Last block
00361     if (bytes != 0) {
00362         unsigned char b[16];
00363         aes(c, key, ctr);
00364         memcpy( (void*)b, (void*)buf, bytes);
00365         xor16(b, c);
00366         memcpy( (void*)buf, (void*)b, bytes);
00367     }
00368 }
00369 
00370 /**
00371  * Increase the counter by one. Uses only the last 8 bytes, aborts in
00372  * case of an overflow. Works in Little Endian.
00373  *
00374  * \param ctr counter to increase
00375  *
00376  **/
00377 inline void ctr_inc (unsigned char* ctr) {
00378     int i;
00379     for (i=15; i > 7; i--)
00380         if ( ctr[i] == 255 )
00381             ctr[i] = 0;
00382         else {
00383             ctr[i]++;
00384             break;
00385         }
00386     if ( i == 7 )
00387         LOGFATAL("counter overflow!");
00388 }
00389 
00390 /**
00391  * XOR 2 16 byte blocks. Argument 1 is modified in place.
00392  *
00393  * \param b1 first block
00394  * \param[in] b2 second block
00395  *
00396  * \todo Make this 16 byte xor faster
00397  */
00398 inline void xor16 (unsigned char* b1, unsigned char* b2) {
00399     /* Safe, but slow */
00400     int i;
00401     for (i=0; i<16; i++)
00402         b1[i] ^= b2[i];
00403 
00404     /* This can break...
00405     *((uint64_t*)  &b1)    ^= *((uint64_t*)  &b2);
00406     *((uint64_t*) (&b1+8)) ^= *((uint64_t*) (&b2+8));
00407     */
00408 }
00409 
00410 /**
00411  * Transfer's exactly \a len (streaming socket) or between \a min
00412  * and \a len (datagram socket) bytes using the specified function
00413  * (either vfer_send() or vfer_recv()).
00414  *
00415  * \param[in] sock socket to use
00416  * \param[in] fn function to use
00417  * \param buf buffer to transmit
00418  * \param[in] min minimum amount to process for datagram sockets
00419  * \param[in] len length of \a buf
00420  *
00421  * \return
00422  *   - nr of bytes processed on success
00423  *  - #VFER_UNCONN sock is not connected
00424  *  - #VFER_INVAL invalid buf or len
00425  *  - #VFER_WOULDBLOCK socket is non-blocking and requested operation
00426  *    would block
00427  *
00428  **/
00429 int transfer ( vsl_sock *sock, transmit_fn fn,
00430                unsigned char* buf, size_t min, size_t len) {
00431 
00432     // Check for nonblocking IO
00433     if ( !is_blocking(sock) &&
00434          vfer_is_stream(sock->vfd) )
00435         LOGFATAL("nonblocking IO not supported for streamed connections!");
00436 
00437     ssize_t processed = 0;
00438     size_t remainder = len;
00439     do {
00440         LOG("trying to process %zd bytes...", remainder);
00441         if ( (processed = (*fn)(sock->vfd, buf, remainder)) < 0 ) {
00442             if ( processed == VFER_WOULDBLOCK ||
00443                  processed == VFER_INVAL ||
00444                  processed == VFER_UNCONN)
00445                 LOGFAIL(processed, "transmit function returned %s", vfer_errortext(processed));
00446             else
00447                 LOGFATAL("processing failed: %s", vfer_errortext(processed));
00448         }
00449         else if (processed != remainder) {
00450             if ( vfer_is_stream(sock->vfd) ) {
00451                 LOG("processed %zd bytes, trying more..", processed);
00452                 buf += processed;
00453                 remainder -= processed;
00454                 continue;
00455             }
00456             else if (processed >= min)
00457                 LOGFAIL(len - remainder, "processed %zd bytes, returning.", len - remainder);
00458             else
00459                 LOGFATAL("fn processed %zd instead of %zd bytes!", processed, len);
00460         }
00461         break;
00462     } while(1);
00463     LOGFAIL(len, "processed %zd bytes, returning.", len);
00464 }
00465 
00466 
00467 
00468 /*
00469  * Local Variables:
00470  * compile-command: "cd ..; make tests file_xfer"
00471  * compilation-search-path: ("..")
00472  * End:
00473  */

Generated on Tue Aug 8 16:07:20 2006 for VFER by  doxygen 1.4.7