#include <oscore/protection.h>
#include <oscore_native/platform.h>
#include <oscore_native/crypto.h>
bool extract_requestid(const oscore_oscoreoption_t *option, oscore_requestid_t *request)
{
uint8_t n = option->partial_iv_len;
if (n == 0) {
return false;
}
assert(n <= PIV_BYTES);
request->used_bytes = n;
memset(request->bytes, 0, PIV_BYTES - n);
request->is_first_use = false;
uint8_t *dest = &request->bytes[PIV_BYTES - n];
memcpy(dest, option->partial_iv, n);
return true;
}
size_t cbor_intsize(size_t input) {
if (input <= 23) {
return 1;
}
if (input < 0x100) {
return 2;
}
#if SIZE_WIDTH <= 32
return 3;
#else
if (input < 0x10000) {
return 3;
}
return 5;
#endif
}
size_t cbor_intencode(size_t input, uint8_t buf[5], uint8_t type)
{
size_t ret = cbor_intsize(input);
if (ret == 1) {
buf[0] = input % 256 + type;
} else if (ret == 2) {
buf[0] = 24 + type;
buf[1] = input % 256;
} else if (ret == 3) {
buf[0] = 25 + type;
buf[1] = (input << 8) % 256;
buf[2] = input % 256;
} else {
buf[0] = 26 + type;
buf[1] = (input << 24) % 256;
buf[2] = (input << 16) % 256;
buf[3] = (input << 8) % 256;
buf[4] = input % 256;
}
return ret;
}
size_t cbor_signedintsize(int32_t input) {
return cbor_intsize(input < 0 ? -1 - input : input);
}
size_t cbor_signedintencode(int32_t input, uint8_t buf[5]) {
return cbor_intencode(input < 0 ? -1 - input : input, buf, input < 0 ? 0x20 : 0x00);
}
struct aad_sizes {
size_t class_i_length;
size_t external_aad_length;
size_t aad_length;
};
struct aad_sizes predict_aad_size(
const oscore_context_t *secctx,
enum oscore_context_role requester_role,
oscore_requestid_t *request,
oscore_crypto_aeadalg_t aeadalg,
oscore_msg_native_t class_i_source
)
{
const uint8_t *request_kid; size_t request_kid_len;
oscore_context_get_kid(secctx, requester_role, &request_kid, &request_kid_len);
struct aad_sizes ret;
ret.class_i_length = 0;
(void) class_i_source;
int32_t numeric_identifier = 0;
oscore_crypto_aead_get_number(aeadalg, &numeric_identifier);
ret.external_aad_length = \
1 +
1 +
1 +
cbor_signedintsize(numeric_identifier) +
cbor_intsize(request_kid_len) + request_kid_len +
cbor_intsize(request->used_bytes) + request->used_bytes +
cbor_intsize(ret.class_i_length) + ret.class_i_length;
ret.aad_length = \
1 +
9 +
1 +
cbor_intsize(ret.external_aad_length) + ret.external_aad_length;
return ret;
}
oscore_cryptoerr_t feed_aad(
oscore_cryptoerr_t (*feeder)(void *, const uint8_t *, size_t),
void *state,
struct aad_sizes aad_sizes,
const oscore_context_t *secctx,
enum oscore_context_role requester_role,
oscore_requestid_t *request,
oscore_crypto_aeadalg_t aeadalg,
oscore_msg_native_t class_i_source
)
{
oscore_cryptoerr_t err;
uint8_t intbuf[5];
err = feeder(state, (uint8_t*) "\x83\x68" "Encrypt0" "\x40", 11);
if (oscore_cryptoerr_is_error(err)) { return err; }
err = feeder(state, intbuf, cbor_intencode(aad_sizes.external_aad_length, intbuf, 0x40));
if (oscore_cryptoerr_is_error(err)) { return err; }
err = feeder(state, (uint8_t*) "\x85\x01\x81", 3);
if (oscore_cryptoerr_is_error(err)) { return err; }
int32_t numeric_identifier = 0;
err = oscore_crypto_aead_get_number(aeadalg, &numeric_identifier);
if (oscore_cryptoerr_is_error(err)) { return err; }
err = feeder(state, intbuf, cbor_signedintencode(numeric_identifier, intbuf));
if (oscore_cryptoerr_is_error(err)) { return err; }
const uint8_t *request_kid;
size_t request_kid_len;
oscore_context_get_kid(secctx, requester_role, &request_kid, &request_kid_len);
err = feeder(state, intbuf, cbor_intencode(request_kid_len, intbuf, 0x40));
if (oscore_cryptoerr_is_error(err)) { return err; }
err = feeder(state, request_kid, request_kid_len);
if (oscore_cryptoerr_is_error(err)) { return err; }
err = feeder(state, intbuf, cbor_intencode(request->used_bytes, intbuf, 0x40));
if (oscore_cryptoerr_is_error(err)) { return err; }
err = feeder(state, &request->bytes[PIV_BYTES - request->used_bytes], request->used_bytes);
if (oscore_cryptoerr_is_error(err)) { return err; }
assert(aad_sizes.class_i_length == 0);
(void) class_i_source;
err = feeder(state, (uint8_t*) "\x40", 1);
return err;
}
void build_iv(
uint8_t iv[OSCORE_CRYPTO_AEAD_IV_MAXLEN],
const oscore_requestid_t *requestid,
const oscore_context_t *secctx,
enum oscore_context_role piv_role
)
{
size_t iv_len = oscore_crypto_aead_get_ivlength(oscore_context_get_aeadalg(secctx));
const uint8_t *common_iv = oscore_context_get_commoniv(secctx);
assert(iv_len >= 7);
assert(iv_len <= OSCORE_CRYPTO_AEAD_IV_MAXLEN);
const uint8_t *id_piv;
size_t id_piv_len;
oscore_context_get_kid(secctx, piv_role, &id_piv, &id_piv_len);
assert(id_piv_len <= iv_len - 6);
iv[0] = id_piv_len;
size_t pad1_len = iv_len - 6 - id_piv_len;
memset(&iv[1], 0, pad1_len);
memcpy(&iv[1 + pad1_len], id_piv, id_piv_len);
memcpy(&iv[iv_len - PIV_BYTES], requestid->bytes, PIV_BYTES);
for (size_t i = 0; i < iv_len; i++) {
iv[i] ^= common_iv[i];
}
}
bool oscore_oscoreoption_parse(oscore_oscoreoption_t *out, const uint8_t *input, size_t input_len)
{
if (input_len != 0) {
uint8_t header = input[0];
if (header & 0xe0) {
return false;
}
uint8_t n = header & 0x07;
if (n > PIV_BYTES) {
return false;
}
out->partial_iv_len = n;
out->partial_iv = n > 0 ? &input[1] : NULL;
size_t tail_start = n + 1;
if (header & 0x10) {
if (tail_start >= input_len) {
return false;
}
out->kid_context_len = input[tail_start];
out->kid_context = &input[tail_start + 1];
tail_start += input[tail_start] + 1;
} else {
out->kid_context = NULL;
}
if (header & 0x08) {
if (tail_start > input_len) {
return false;
}
out->kid = &input[tail_start];
out->kid_len = input_len - tail_start;
} else {
if (tail_start != input_len) {
return false;
}
out->kid = NULL;
}
} else {
out->partial_iv_len = 0;
out->partial_iv = NULL;
out->kid_context = NULL;
out->kid = NULL;
}
return true;
}
void oscore_requestid_clone(oscore_requestid_t *dest, oscore_requestid_t *src)
{
memcpy(dest, src, sizeof(oscore_requestid_t));
dest->is_first_use = false;
}
bool _decrypt(
oscore_msg_native_t protected,
oscore_msg_protected_t *unprotected,
oscore_context_t *secctx,
enum oscore_context_role piv_kid,
enum oscore_context_role request_kid
)
{
oscore_crypto_aeadalg_t aeadalg = oscore_context_get_aeadalg(secctx);
size_t tag_length = oscore_crypto_aead_get_taglength(aeadalg);
size_t minimum_ciphertext_length = 1 + tag_length;
uint8_t *ciphertext;
size_t ciphertext_length;
oscore_msg_native_map_payload(protected, &ciphertext, &ciphertext_length);
if (ciphertext_length < minimum_ciphertext_length) {
return false;
}
size_t plaintext_length = ciphertext_length - tag_length;
struct aad_sizes aad_sizes = predict_aad_size(secctx, request_kid, &unprotected->request_id, aeadalg, protected);
uint8_t iv[OSCORE_CRYPTO_AEAD_IV_MAXLEN];
build_iv(iv, &unprotected->partial_iv, secctx, piv_kid);
oscore_cryptoerr_t err;
oscore_crypto_aead_decryptstate_t dec;
err = oscore_crypto_aead_decrypt_start(
&dec,
aeadalg,
aad_sizes.aad_length,
plaintext_length,
iv,
oscore_context_get_key(secctx, OSCORE_ROLE_RECIPIENT)
);
if (!oscore_cryptoerr_is_error(err)) {
err = feed_aad(oscore_crypto_aead_decrypt_feed_aad, &dec, aad_sizes, secctx, request_kid, &unprotected->request_id, aeadalg, protected);
}
if (!oscore_cryptoerr_is_error(err)) {
err = oscore_crypto_aead_decrypt_inplace(
&dec,
ciphertext,
ciphertext_length);
}
if (oscore_cryptoerr_is_error(err)) {
return false;
}
unprotected->backend = protected;
unprotected->flags = OSCORE_MSG_PROTECTED_FLAG_NONE;
unprotected->tag_length = tag_length;
unprotected->payload_offset = 0;
return true;
}
enum oscore_unprotect_request_result oscore_unprotect_request(
oscore_msg_native_t protected,
oscore_msg_protected_t *unprotected,
const oscore_oscoreoption_t *header,
oscore_context_t *secctx,
oscore_requestid_t *request_id
)
{
bool has_request_id = extract_requestid(header, request_id);
if (!has_request_id) {
return OSCORE_UNPROTECT_REQUEST_INVALID;
}
oscore_requestid_clone(&unprotected->request_id, request_id);
oscore_requestid_clone(&unprotected->partial_iv, request_id);
bool success = _decrypt(protected, unprotected, secctx, OSCORE_ROLE_RECIPIENT, OSCORE_ROLE_RECIPIENT);
if (!success)
return OSCORE_UNPROTECT_REQUEST_INVALID;
oscore_context_strikeout_requestid(secctx, request_id);
return request_id->is_first_use ? OSCORE_UNPROTECT_REQUEST_OK : OSCORE_UNPROTECT_REQUEST_DUPLICATE;
}
enum oscore_unprotect_response_result oscore_unprotect_response(
oscore_msg_native_t protected,
oscore_msg_protected_t *unprotected,
const oscore_oscoreoption_t *header,
oscore_context_t *secctx,
oscore_requestid_t *request_id
)
{
bool has_piv = extract_requestid(header, &unprotected->partial_iv);
enum oscore_context_role piv_kid;
if (has_piv) {
piv_kid = OSCORE_ROLE_RECIPIENT;
} else {
oscore_requestid_clone(&unprotected->partial_iv, request_id);
piv_kid = OSCORE_ROLE_SENDER;
}
oscore_requestid_clone(&unprotected->request_id, request_id);
bool success = _decrypt(protected, unprotected, secctx, piv_kid, OSCORE_ROLE_SENDER);
if (!success)
return OSCORE_UNPROTECT_RESPONSE_INVALID;
return OSCORE_UNPROTECT_RESPONSE_OK;
}
oscore_msg_native_t oscore_release_unprotected(
oscore_msg_protected_t *unprotected
)
{
return unprotected->backend;
}
enum oscore_prepare_result _prepare_encrypt(
oscore_msg_native_t protected,
oscore_msg_protected_t *unprotected,
oscore_context_t *secctx
)
{
oscore_crypto_aeadalg_t aeadalg = oscore_context_get_aeadalg(secctx);
size_t tag_length = oscore_crypto_aead_get_taglength(aeadalg);
unprotected->backend = protected;
unprotected->flags = OSCORE_MSG_PROTECTED_FLAG_WRITABLE | OSCORE_MSG_PROTECTED_FLAG_PENDING_OSCORE;
unprotected->tag_length = tag_length;
unprotected->payload_offset = 0;
unprotected->secctx = secctx;
unprotected->class_e.cursor = 0;
unprotected->class_e.option_number = 0;
return OSCORE_PREPARE_OK;
}
enum oscore_prepare_result oscore_prepare_response(
oscore_msg_native_t protected,
oscore_msg_protected_t *unprotected,
oscore_context_t *secctx,
oscore_requestid_t *request_id
)
{
memcpy(&unprotected->request_id, request_id, sizeof(oscore_requestid_t));
request_id->is_first_use = false;
if (!unprotected->request_id.is_first_use) {
bool ok = oscore_context_take_seqno(secctx, &unprotected->partial_iv);
if (!ok) {
return OSCORE_PREPARE_SECCTX_UNAVAILABLE;
}
} else {
oscore_requestid_clone(&unprotected->partial_iv, &unprotected->request_id);
}
unprotected->partial_iv.is_first_use = true;
oscore_msg_native_set_code(protected, 0x45);
return _prepare_encrypt(protected, unprotected, secctx);
}
enum oscore_prepare_result oscore_prepare_request(
oscore_msg_native_t protected,
oscore_msg_protected_t *unprotected,
oscore_context_t *secctx,
oscore_requestid_t *request_id
)
{
bool ok = oscore_context_take_seqno(secctx, &unprotected->request_id);
if (!ok) {
return OSCORE_PREPARE_SECCTX_UNAVAILABLE;
}
oscore_requestid_clone(request_id, &unprotected->request_id);
oscore_requestid_clone(&unprotected->partial_iv, &unprotected->request_id);
unprotected->partial_iv.is_first_use = true;
oscore_msg_native_set_code(protected, 0x2);
enum oscore_prepare_result result = _prepare_encrypt(protected, unprotected, secctx);
unprotected->flags |= OSCORE_MSG_PROTECTED_FLAG_REQUEST;
return result;
}
enum oscore_finish_result oscore_encrypt_message(
oscore_msg_protected_t *unprotected,
oscore_msg_native_t *protected
)
{
const oscore_context_t *secctx = unprotected->secctx;
oscore_crypto_aeadalg_t aeadalg = oscore_context_get_aeadalg(secctx);
size_t tag_length = unprotected->tag_length;
bool is_request = (unprotected->flags & OSCORE_MSG_PROTECTED_FLAG_REQUEST);
enum oscore_context_role requester_role = is_request ? OSCORE_ROLE_SENDER : OSCORE_ROLE_RECIPIENT;
enum oscore_context_role nonceprovider_role = is_request ?
OSCORE_ROLE_SENDER : (
unprotected->request_id.is_first_use ?
OSCORE_ROLE_RECIPIENT :
OSCORE_ROLE_SENDER
);
*protected = unprotected->backend;
assert(unprotected->partial_iv.is_first_use);
unprotected->partial_iv.is_first_use = false;
uint8_t *ciphertext;
size_t ciphertext_length;
oscore_msg_native_map_payload(unprotected->backend, &ciphertext, &ciphertext_length);
if (ciphertext_length < tag_length) {
return OSCORE_FINISH_ERROR_SIZE;
}
size_t plaintext_length = ciphertext_length - tag_length;
struct aad_sizes aad_sizes = predict_aad_size(secctx, requester_role, &unprotected->request_id, aeadalg, unprotected->backend);
uint8_t encrypt_iv[OSCORE_CRYPTO_AEAD_IV_MAXLEN];
build_iv(encrypt_iv, &unprotected->partial_iv, secctx, nonceprovider_role);
oscore_crypto_aead_encryptstate_t enc;
oscore_cryptoerr_t err = oscore_crypto_aead_encrypt_start(
&enc,
oscore_context_get_aeadalg(secctx),
aad_sizes.aad_length,
plaintext_length,
encrypt_iv,
oscore_context_get_key(secctx, OSCORE_ROLE_SENDER)
);
if (!oscore_cryptoerr_is_error(err)) {
err = feed_aad(
oscore_crypto_aead_encrypt_feed_aad,
&enc,
aad_sizes,
secctx,
requester_role,
&unprotected->request_id,
aeadalg,
unprotected->backend
);
}
if (!oscore_cryptoerr_is_error(err)) {
err = oscore_crypto_aead_encrypt_inplace(
&enc,
ciphertext,
ciphertext_length);
}
if (oscore_cryptoerr_is_error(err)) {
return OSCORE_FINISH_ERROR_CRYPTO;
}
return OSCORE_FINISH_OK;
}