mirror of
https://github.com/neocturne/fastd.git
synced 2025-05-15 04:35:08 +02:00
Some work towards a composable GMAC method
This commit is contained in:
parent
190878060d
commit
3e8e646b91
3 changed files with 350 additions and 0 deletions
|
@ -24,6 +24,7 @@ endmacro(fastd_method_require)
|
|||
|
||||
add_subdirectory(null)
|
||||
add_subdirectory(generic_gcm)
|
||||
add_subdirectory(generic_gmac)
|
||||
add_subdirectory(xsalsa20_poly1305)
|
||||
|
||||
|
||||
|
|
4
src/methods/generic_gmac/CMakeLists.txt
Normal file
4
src/methods/generic_gmac/CMakeLists.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
fastd_method(generic-gmac
|
||||
generic_gmac.c
|
||||
)
|
||||
fastd_method_link_libraries(generic-gmac method_common)
|
345
src/methods/generic_gmac/generic_gmac.c
Normal file
345
src/methods/generic_gmac/generic_gmac.c
Normal file
|
@ -0,0 +1,345 @@
|
|||
/*
|
||||
Copyright (c) 2012-2013, Matthias Schiffer <mschiffer@universe-factory.net>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "../../fastd.h"
|
||||
#include "../common.h"
|
||||
|
||||
|
||||
struct fastd_method_session_state {
|
||||
fastd_method_common_t common;
|
||||
|
||||
const fastd_cipher_t *cipher;
|
||||
const fastd_cipher_context_t *cipher_ctx;
|
||||
fastd_cipher_state_t *cipher_state;
|
||||
size_t ivlen;
|
||||
|
||||
const fastd_cipher_t *gmac_cipher;
|
||||
const fastd_cipher_context_t *gmac_cipher_ctx;
|
||||
fastd_cipher_state_t *gmac_cipher_state;
|
||||
size_t gmac_ivlen;
|
||||
|
||||
const fastd_mac_t *ghash;
|
||||
const fastd_mac_context_t *ghash_ctx;
|
||||
fastd_mac_state_t *ghash_state;
|
||||
};
|
||||
|
||||
|
||||
static bool cipher_get(fastd_context_t *ctx, const char *name, const fastd_cipher_t **cipher, const fastd_cipher_context_t **cctx, const fastd_cipher_t **gmac_cipher, const fastd_cipher_context_t **gmac_cctx) {
|
||||
if (!fastd_mac_available("ghash"))
|
||||
return false;
|
||||
|
||||
size_t len = strlen(name);
|
||||
|
||||
if (len < 5)
|
||||
return false;
|
||||
|
||||
if (strcmp(name+len-5, "-gmac"))
|
||||
return false;
|
||||
|
||||
char cipher_name[len];
|
||||
memcpy(cipher_name, name, len-4);
|
||||
strncpy(cipher_name+len-4, "ctr", 4);
|
||||
|
||||
char *gmac_cipher_name = strchr(cipher_name, '+');
|
||||
|
||||
if (!gmac_cipher_name)
|
||||
return false;
|
||||
|
||||
*gmac_cipher_name = 0;
|
||||
gmac_cipher_name++;
|
||||
|
||||
if (ctx) {
|
||||
*cipher = fastd_cipher_get_by_name(ctx, cipher_name, cctx);
|
||||
*gmac_cipher = fastd_cipher_get_by_name(ctx, gmac_cipher_name, gmac_cctx);
|
||||
return *cipher && *gmac_cipher;
|
||||
}
|
||||
else {
|
||||
return fastd_cipher_available(cipher_name) && fastd_cipher_available(gmac_cipher_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool method_provides(const char *name) {
|
||||
return cipher_get(NULL, name, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static size_t method_max_packet_size(fastd_context_t *ctx) {
|
||||
return (fastd_max_packet_size(ctx) + COMMON_HEADBYTES + sizeof(fastd_block128_t));
|
||||
}
|
||||
|
||||
|
||||
static size_t method_min_encrypt_head_space(fastd_context_t *ctx UNUSED) {
|
||||
return sizeof(fastd_block128_t);
|
||||
}
|
||||
|
||||
static size_t method_min_decrypt_head_space(fastd_context_t *ctx UNUSED) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t method_min_encrypt_tail_space(fastd_context_t *ctx UNUSED) {
|
||||
return (sizeof(fastd_block128_t)-1);
|
||||
}
|
||||
|
||||
static size_t method_min_decrypt_tail_space(fastd_context_t *ctx UNUSED) {
|
||||
return (2*sizeof(fastd_block128_t)-1);
|
||||
}
|
||||
|
||||
|
||||
static size_t method_key_length(fastd_context_t *ctx, const char *name) {
|
||||
const fastd_cipher_t *cipher = NULL;
|
||||
const fastd_cipher_context_t *cctx;
|
||||
|
||||
const fastd_cipher_t *gmac_cipher = NULL;
|
||||
const fastd_cipher_context_t *gmac_cctx;
|
||||
|
||||
if (!cipher_get(ctx, name, &cipher, &cctx, &gmac_cipher, &gmac_cctx))
|
||||
exit_bug(ctx, "generic-gmac: can't get cipher key length");
|
||||
|
||||
return cipher->key_length(ctx, cctx) + gmac_cipher->key_length(ctx, gmac_cctx);
|
||||
}
|
||||
|
||||
static fastd_method_session_state_t* method_session_init(fastd_context_t *ctx, const char *name, const uint8_t *secret, bool initiator) {
|
||||
fastd_method_session_state_t *session = malloc(sizeof(fastd_method_session_state_t));
|
||||
|
||||
fastd_method_common_init(ctx, &session->common, initiator);
|
||||
|
||||
if (!cipher_get(ctx, name, &session->cipher, &session->cipher_ctx, &session->gmac_cipher, &session->gmac_cipher_ctx))
|
||||
exit_bug(ctx, "generic-gmac: can't instanciate cipher");
|
||||
|
||||
session->cipher_state = session->cipher->init_state(ctx, session->cipher_ctx, secret);
|
||||
session->ivlen = session->cipher->iv_length(ctx, session->cipher_state);
|
||||
if (session->ivlen && session->ivlen <= COMMON_NONCEBYTES)
|
||||
exit_bug(ctx, "generic-gmac: iv_length to small");
|
||||
|
||||
session->gmac_cipher_state = session->gmac_cipher->init_state(ctx, session->gmac_cipher_ctx, secret + session->cipher->key_length(ctx, session->cipher_ctx));
|
||||
session->gmac_ivlen = session->gmac_cipher->iv_length(ctx, session->gmac_cipher_state);
|
||||
if (session->gmac_ivlen <= COMMON_NONCEBYTES)
|
||||
exit_bug(ctx, "generic-gmac: gmac cipher iv_length to small");
|
||||
|
||||
static const fastd_block128_t zeroblock = {};
|
||||
fastd_block128_t H;
|
||||
|
||||
uint8_t zeroiv[session->gmac_ivlen];
|
||||
memset(zeroiv, 0, session->gmac_ivlen);
|
||||
|
||||
session->gmac_cipher->crypt(ctx, session->gmac_cipher_state, &H, &zeroblock, sizeof(fastd_block128_t), zeroiv);
|
||||
|
||||
session->ghash = fastd_mac_get_by_name(ctx, "ghash", &session->ghash_ctx);
|
||||
if (!session->ghash)
|
||||
exit_bug(ctx, "generic-gmac: can't instanciate ghash mac");
|
||||
|
||||
session->ghash_state = session->ghash->init_state(ctx, session->ghash_ctx, H.b);
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
static bool method_session_is_valid(fastd_context_t *ctx, fastd_method_session_state_t *session) {
|
||||
return (session && fastd_method_session_common_is_valid(ctx, &session->common));
|
||||
}
|
||||
|
||||
static bool method_session_is_initiator(fastd_context_t *ctx UNUSED, fastd_method_session_state_t *session) {
|
||||
return fastd_method_session_common_is_initiator(&session->common);
|
||||
}
|
||||
|
||||
static bool method_session_want_refresh(fastd_context_t *ctx, fastd_method_session_state_t *session) {
|
||||
return fastd_method_session_common_want_refresh(ctx, &session->common);
|
||||
}
|
||||
|
||||
static void method_session_superseded(fastd_context_t *ctx, fastd_method_session_state_t *session) {
|
||||
fastd_method_session_common_superseded(ctx, &session->common);
|
||||
}
|
||||
|
||||
static void method_session_free(fastd_context_t *ctx, fastd_method_session_state_t *session) {
|
||||
if (session) {
|
||||
session->cipher->free_state(ctx, session->cipher_state);
|
||||
session->gmac_cipher->free_state(ctx, session->gmac_cipher_state);
|
||||
session->ghash->free_state(ctx, session->ghash_state);
|
||||
|
||||
free(session);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void put_size(fastd_block128_t *out, size_t len) {
|
||||
memset(out, 0, sizeof(fastd_block128_t));
|
||||
out->b[3] = len >> 29;
|
||||
out->b[4] = len >> 21;
|
||||
out->b[5] = len >> 13;
|
||||
out->b[6] = len >> 5;
|
||||
out->b[7] = len << 3;
|
||||
}
|
||||
|
||||
static bool method_encrypt(fastd_context_t *ctx, fastd_peer_t *peer UNUSED, fastd_method_session_state_t *session, fastd_buffer_t *out, fastd_buffer_t in) {
|
||||
fastd_buffer_pull_head(ctx, &in, sizeof(fastd_block128_t));
|
||||
memset(in.data, 0, sizeof(fastd_block128_t));
|
||||
|
||||
size_t tail_len = alignto(in.len, sizeof(fastd_block128_t))-in.len;
|
||||
*out = fastd_buffer_alloc(ctx, in.len, alignto(COMMON_HEADBYTES, 16), sizeof(fastd_block128_t)+tail_len);
|
||||
|
||||
if (tail_len)
|
||||
memset(in.data+in.len, 0, tail_len);
|
||||
|
||||
int n_blocks = block_count(in.len, sizeof(fastd_block128_t));
|
||||
|
||||
fastd_block128_t *inblocks = in.data;
|
||||
fastd_block128_t *outblocks = out->data;
|
||||
fastd_block128_t sig;
|
||||
|
||||
uint8_t gmac_nonce[session->gmac_ivlen];
|
||||
memset(gmac_nonce, 0, session->gmac_ivlen);
|
||||
memcpy(gmac_nonce, session->common.send_nonce, COMMON_NONCEBYTES);
|
||||
gmac_nonce[session->gmac_ivlen-1] = 1;
|
||||
|
||||
bool ok = session->gmac_cipher->crypt(ctx, session->gmac_cipher_state, outblocks, inblocks, sizeof(fastd_block128_t), gmac_nonce);
|
||||
|
||||
if (ok) {
|
||||
uint8_t nonce[session->ivlen];
|
||||
if (session->ivlen) {
|
||||
memset(nonce, 0, session->ivlen);
|
||||
memcpy(nonce, session->common.send_nonce, COMMON_NONCEBYTES);
|
||||
nonce[session->ivlen-1] = 1;
|
||||
}
|
||||
|
||||
ok = session->cipher->crypt(ctx, session->cipher_state, outblocks+1, inblocks+1, (n_blocks-1)*sizeof(fastd_block128_t), nonce);
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
if (tail_len)
|
||||
memset(out->data+out->len, 0, tail_len);
|
||||
|
||||
put_size(&outblocks[n_blocks], in.len-sizeof(fastd_block128_t));
|
||||
|
||||
ok = session->ghash->hash(ctx, session->ghash_state, &sig, outblocks+1, n_blocks);
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
/* restore original buffer */
|
||||
fastd_buffer_push_head(ctx, &in, sizeof(fastd_block128_t));
|
||||
fastd_buffer_free(*out);
|
||||
return false;
|
||||
}
|
||||
|
||||
xor_a(&outblocks[0], &sig);
|
||||
|
||||
fastd_buffer_free(in);
|
||||
|
||||
fastd_buffer_pull_head(ctx, out, COMMON_HEADBYTES);
|
||||
|
||||
memcpy(out->data, session->common.send_nonce, COMMON_NONCEBYTES);
|
||||
fastd_method_increment_nonce(&session->common);
|
||||
|
||||
((uint8_t*)out->data)[COMMON_NONCEBYTES] = 0; /* flags */
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool method_decrypt(fastd_context_t *ctx, fastd_peer_t *peer, fastd_method_session_state_t *session, fastd_buffer_t *out, fastd_buffer_t in) {
|
||||
if (in.len < COMMON_HEADBYTES+sizeof(fastd_block128_t))
|
||||
return false;
|
||||
|
||||
if (!method_session_is_valid(ctx, session))
|
||||
return false;
|
||||
|
||||
if (((const uint8_t*)in.data)[COMMON_NONCEBYTES]) /* flags */
|
||||
return false;
|
||||
|
||||
int64_t age;
|
||||
if (!fastd_method_is_nonce_valid(ctx, &session->common, in.data, &age))
|
||||
return false;
|
||||
|
||||
uint8_t gmac_nonce[session->gmac_ivlen];
|
||||
memset(gmac_nonce, 0, session->gmac_ivlen);
|
||||
memcpy(gmac_nonce, in.data, COMMON_NONCEBYTES);
|
||||
gmac_nonce[session->gmac_ivlen-1] = 1;
|
||||
|
||||
uint8_t nonce[session->ivlen];
|
||||
if (session->ivlen) {
|
||||
memset(nonce, 0, session->ivlen);
|
||||
memcpy(nonce, in.data, COMMON_NONCEBYTES);
|
||||
nonce[session->ivlen-1] = 1;
|
||||
}
|
||||
|
||||
fastd_buffer_push_head(ctx, &in, COMMON_HEADBYTES);
|
||||
|
||||
size_t tail_len = alignto(in.len, sizeof(fastd_block128_t))-in.len;
|
||||
*out = fastd_buffer_alloc(ctx, in.len, 0, tail_len);
|
||||
|
||||
int n_blocks = block_count(in.len, sizeof(fastd_block128_t));
|
||||
|
||||
fastd_block128_t *inblocks = in.data;
|
||||
fastd_block128_t *outblocks = out->data;
|
||||
fastd_block128_t sig;
|
||||
|
||||
bool ok = session->gmac_cipher->crypt(ctx, session->gmac_cipher_state, outblocks, inblocks, sizeof(fastd_block128_t), gmac_nonce);
|
||||
|
||||
if (ok)
|
||||
ok = session->cipher->crypt(ctx, session->cipher_state, outblocks+1, inblocks+1, (n_blocks-1)*sizeof(fastd_block128_t), nonce);
|
||||
|
||||
if (ok) {
|
||||
if (tail_len)
|
||||
memset(in.data+in.len, 0, tail_len);
|
||||
|
||||
put_size(&inblocks[n_blocks], in.len-sizeof(fastd_block128_t));
|
||||
|
||||
ok = session->ghash->hash(ctx, session->ghash_state, &sig, inblocks+1, n_blocks);
|
||||
}
|
||||
|
||||
if (!ok || memcmp(&sig, &outblocks[0], sizeof(fastd_block128_t)) != 0) {
|
||||
fastd_buffer_free(*out);
|
||||
return false;
|
||||
}
|
||||
|
||||
fastd_buffer_free(in);
|
||||
|
||||
fastd_buffer_push_head(ctx, out, sizeof(fastd_block128_t));
|
||||
|
||||
if (!fastd_method_reorder_check(ctx, peer, &session->common, nonce, age)) {
|
||||
fastd_buffer_free(*out);
|
||||
*out = fastd_buffer_alloc(ctx, 0, 0, 0);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const fastd_method_t fastd_method_generic_gmac = {
|
||||
.provides = method_provides,
|
||||
|
||||
.max_packet_size = method_max_packet_size,
|
||||
.min_encrypt_head_space = method_min_encrypt_head_space,
|
||||
.min_decrypt_head_space = method_min_decrypt_head_space,
|
||||
.min_encrypt_tail_space = method_min_encrypt_tail_space,
|
||||
.min_decrypt_tail_space = method_min_decrypt_tail_space,
|
||||
|
||||
.key_length = method_key_length,
|
||||
.session_init = method_session_init,
|
||||
.session_is_valid = method_session_is_valid,
|
||||
.session_is_initiator = method_session_is_initiator,
|
||||
.session_want_refresh = method_session_want_refresh,
|
||||
.session_superseded = method_session_superseded,
|
||||
.session_free = method_session_free,
|
||||
|
||||
.encrypt = method_encrypt,
|
||||
.decrypt = method_decrypt,
|
||||
};
|
Loading…
Add table
Reference in a new issue