Commit 714920a0 authored by Jan Včelák's avatar Jan Včelák 🚀

Merge branch 'dnssec-integer-encoding' into 'master'

Proper integer encoding in signature and public key conversion operations.

Closes #357 #358
parents 64d74dbb c6680129
......@@ -22,6 +22,8 @@ libshared_la_CPPFLAGS = \
libshared_la_SOURCES = \
shared/base32hex.c \
shared/base32hex.h \
shared/bignum.c \
shared/bignum.h \
shared/dname.c \
shared/dname.h \
shared/fs.c \
......
......@@ -140,28 +140,6 @@ int dnssec_binary_cmp(const dnssec_binary_t *one, const dnssec_binary_t *two)
}
}
_public_
void dnssec_binary_ltrim(dnssec_binary_t *binary)
{
if (!binary || !binary->data) {
return;
}
size_t start = 0;
while (start + 1 < binary->size && binary->data[start] == 0x00) {
start += 1;
}
if (start == 0) {
return;
}
size_t new_size = binary->size - start;
memmove(binary->data, binary->data + start, new_size);
binary->size = new_size;
}
_public_
int dnssec_binary_from_base64(const dnssec_binary_t *base64,
dnssec_binary_t *binary)
......
......@@ -111,15 +111,6 @@ int dnssec_binary_resize(dnssec_binary_t *data, size_t new_size);
*/
int dnssec_binary_cmp(const dnssec_binary_t *one, const dnssec_binary_t *two);
/*!
* Trim leading zeroes from the binary data.
*
* If the input are zeroes only, the last zero will be preserved.
*
* \param binary Input to be trimmed.
*/
void dnssec_binary_ltrim(dnssec_binary_t *binary);
/*!
* Allocate binary from Base64 encoded string.
*
......
This diff is collapsed.
......@@ -16,6 +16,7 @@
#include <stdbool.h>
#include "bignum.h"
#include "binary.h"
#include "error.h"
#include "sign/der.h"
......@@ -68,12 +69,12 @@ static int asn1_decode_size(wire_ctx_t *wire, size_t *size)
}
/*!
* Decode an integer object and retrieves a pointer to it.
* Decode an unsigned integer object.
*/
static int asn1_decode_integer(wire_ctx_t *wire, dnssec_binary_t *value)
static int asn1_decode_integer(wire_ctx_t *wire, dnssec_binary_t *_value)
{
assert(wire);
assert(value);
assert(_value);
if (!asn1_expect_type(wire, ASN1_TYPE_INTEGER)) {
return DNSSEC_MALFORMED_DATA;
......@@ -85,14 +86,21 @@ static int asn1_decode_integer(wire_ctx_t *wire, dnssec_binary_t *value)
return result;
}
if (size > wire_available(wire)) {
if (size == 0 || size > wire_available(wire)) {
return DNSSEC_MALFORMED_DATA;
}
value->size = size;
value->data = wire->position;
dnssec_binary_t value = { .data = wire->position, .size = size };
wire->position += size;
// skip leading zeroes (unless equal to zero)
while (value.size > 1 && value.data[0] == 0) {
value.data += 1;
value.size -= 1;
}
*_value = value;
return DNSSEC_EOK;
}
......@@ -109,16 +117,17 @@ static void asn1_write_header(wire_ctx_t *wire, uint8_t type, size_t length)
}
/*!
* Encode integer object.
* Encode unsigned integer object.
*/
static void asn1_write_integer(wire_ctx_t *wire, const dnssec_binary_t *integer)
static void asn1_write_integer(wire_ctx_t *wire, size_t integer_size,
const dnssec_binary_t *integer)
{
assert(wire);
assert(integer);
assert(integer->data);
asn1_write_header(wire, ASN1_TYPE_INTEGER, integer->size);
wire_write_binary(wire, integer);
asn1_write_header(wire, ASN1_TYPE_INTEGER, integer_size);
wire_write_bignum(wire, integer_size, integer);
}
/*!
......@@ -179,19 +188,22 @@ int dss_sig_value_decode(const dnssec_binary_t *der,
* Encode signature parameters from X.509 (EC)DSA signature.
*/
int dss_sig_value_encode(const dnssec_binary_t *r, const dnssec_binary_t *s,
dnssec_binary_t *der)
dnssec_binary_t *_der)
{
if (!r || !r->data || !s || !s->data || !der) {
if (!r || !r->data || !s || !s->data || !_der) {
return DNSSEC_EINVAL;
}
size_t r_size = bignum_size_s(r);
size_t s_size = bignum_size_s(s);
// check supported inputs range
if (r->size > ASN1_MAX_SIZE || s->size > ASN1_MAX_SIZE) {
if (r_size > ASN1_MAX_SIZE || s_size > ASN1_MAX_SIZE) {
return DNSSEC_NOT_IMPLEMENTED_ERROR;
}
size_t seq_size = 2 + r->size + 2 + s->size;
size_t seq_size = 2 + r_size + 2 + s_size;
if (seq_size > ASN1_MAX_SIZE) {
return DNSSEC_NOT_IMPLEMENTED_ERROR;
}
......@@ -199,20 +211,19 @@ int dss_sig_value_encode(const dnssec_binary_t *r, const dnssec_binary_t *s,
// encode result
size_t total_size = 2 + seq_size;
uint8_t *encoded = malloc(total_size);
if (!encoded) {
dnssec_binary_t der = { 0 };
if (dnssec_binary_alloc(&der, total_size)) {
return DNSSEC_ENOMEM;
}
wire_ctx_t wire = wire_init(encoded, total_size);
wire_ctx_t wire = wire_init_binary(&der);
asn1_write_header(&wire, ASN1_TYPE_SEQUENCE, seq_size);
asn1_write_integer(&wire, r);
asn1_write_integer(&wire, s);
asn1_write_integer(&wire, r_size, r);
asn1_write_integer(&wire, s_size, s);
assert(wire_available(&wire) == 0);
der->size = total_size;
der->data = encoded;
*_der = der;
return DNSSEC_EOK;
}
......@@ -25,6 +25,10 @@
* of the signature.
*
* This module provides decoding and encoding of this format.
*
* The 'r' and 's' values are treated as unsigned values: The leading zeroes
* are stripped on decoding; an extra leading zero is added on encoding in case
* the value starts with a set bit.
*/
/*!
......
......@@ -20,6 +20,7 @@
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>
#include "bignum.h"
#include "error.h"
#include "key.h"
#include "key/internal.h"
......@@ -77,17 +78,7 @@ static int rsa_copy_signature(dnssec_sign_ctx_t *ctx,
assert(from);
assert(to);
uint8_t *data = malloc(from->size);
if (!data) {
return DNSSEC_ENOMEM;
}
memmove(data, from->data, from->size);
to->size = from->size;
to->data = data;
return DNSSEC_EOK;
return dnssec_binary_dup(from, to);
}
static const algorithm_functions_t rsa_functions = {
......@@ -133,29 +124,25 @@ static int dsa_x509_to_dnssec(dnssec_sign_ctx_t *ctx,
return result;
}
dnssec_binary_ltrim(&value_r);
dnssec_binary_ltrim(&value_s);
size_t r_size = bignum_size_u(&value_r);
size_t s_size = bignum_size_u(&value_s);
if (value_r.size > 20 || value_s.size > 20) {
if (r_size > 20 || s_size > 20) {
return DNSSEC_MALFORMED_DATA;
}
uint8_t value_t = dsa_dnskey_get_t_value(ctx->key);
size_t size = 41;
uint8_t *data = malloc(size);
if (!data) {
return DNSSEC_ENOMEM;
result = dnssec_binary_alloc(dnssec, 41);
if (result != DNSSEC_EOK) {
return result;
}
wire_ctx_t wire = wire_init(data, size);
wire_ctx_t wire = wire_init_binary(dnssec);
wire_write_u8(&wire, value_t);
wire_write_ralign_binary(&wire, 20, &value_r);
wire_write_ralign_binary(&wire, 20, &value_s);
assert(wire_tell(&wire) == size);
dnssec->size = size;
dnssec->data = data;
wire_write_bignum(&wire, 20, &value_r);
wire_write_bignum(&wire, 20, &value_s);
assert(wire_tell(&wire) == dnssec->size);
return DNSSEC_EOK;
}
......@@ -174,16 +161,8 @@ static int dsa_dnssec_to_x509(dnssec_sign_ctx_t *ctx,
const dnssec_binary_t value_r = { .size = 20, .data = dnssec->data + 1 };
const dnssec_binary_t value_s = { .size = 20, .data = dnssec->data + 21 };
dnssec_binary_t der = { 0 };
int result = dss_sig_value_encode(&value_r, &value_s, &der);
if (result != DNSSEC_EOK) {
return result;
}
*x509 = der;
return DNSSEC_EOK;
return dss_sig_value_encode(&value_r, &value_s, x509);
}
static const algorithm_functions_t dsa_functions = {
......@@ -223,29 +202,23 @@ static int ecdsa_x509_to_dnssec(dnssec_sign_ctx_t *ctx,
return result;
}
dnssec_binary_ltrim(&value_r);
dnssec_binary_ltrim(&value_s);
size_t int_size = ecdsa_sign_integer_size(ctx);
assert(int_size > 0);
size_t r_size = bignum_size_u(&value_r);
size_t s_size = bignum_size_u(&value_s);
if (value_r.size > int_size || value_s.size > int_size) {
if (r_size > int_size || s_size > int_size) {
return DNSSEC_MALFORMED_DATA;
}
size_t size = 2 * int_size;
uint8_t *data = malloc(size);
if (!data) {
return DNSSEC_ENOMEM;
result = dnssec_binary_alloc(dnssec, 2 * int_size);
if (result != DNSSEC_EOK) {
return result;
}
wire_ctx_t wire = wire_init(data, size);
wire_write_ralign_binary(&wire, int_size, &value_r);
wire_write_ralign_binary(&wire, int_size, &value_s);
assert(wire_tell(&wire) == size);
dnssec->size = size;
dnssec->data = data;
wire_ctx_t wire = wire_init_binary(dnssec);
wire_write_bignum(&wire, int_size, &value_r);
wire_write_bignum(&wire, int_size, &value_s);
assert(wire_tell(&wire) == dnssec->size);
return DNSSEC_EOK;
}
......@@ -259,7 +232,6 @@ static int ecdsa_dnssec_to_x509(dnssec_sign_ctx_t *ctx,
assert(dnssec);
size_t int_size = ecdsa_sign_integer_size(ctx);
assert(int_size > 0);
if (dnssec->size != 2 * int_size) {
return DNSSEC_INVALID_SIGNATURE;
......@@ -267,16 +239,8 @@ static int ecdsa_dnssec_to_x509(dnssec_sign_ctx_t *ctx,
const dnssec_binary_t value_r = { .size = int_size, .data = dnssec->data };
const dnssec_binary_t value_s = { .size = int_size, .data = dnssec->data + int_size };
dnssec_binary_t der = { 0 };
int result = dss_sig_value_encode(&value_r, &value_s, &der);
if (result != DNSSEC_EOK) {
return result;
}
*x509 = der;
return DNSSEC_EOK;
return dss_sig_value_encode(&value_r, &value_s, x509);
}
static const algorithm_functions_t ecdsa_functions = {
......@@ -469,7 +433,7 @@ int dnssec_sign_write(dnssec_sign_ctx_t *ctx, dnssec_binary_t *signature)
return DNSSEC_SIGN_ERROR;
}
dnssec_binary_t bin_raw = datum_to_binary(&raw);
dnssec_binary_t bin_raw = binary_from_datum(&raw);
return ctx->functions->x509_to_dnssec(ctx, &bin_raw, signature);
}
......
/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include <string.h>
#include "bignum.h"
static void skip_leading_zeroes(dnssec_binary_t *value)
{
while (value->size > 0 && value->data[0] == 0) {
value->data += 1;
value->size -= 1;
}
}
size_t bignum_size_u(const dnssec_binary_t *_value)
{
dnssec_binary_t value = *_value;
skip_leading_zeroes(&value);
if (value.size == 0) {
return value.size + 1;
} else {
return value.size;
}
}
size_t bignum_size_s(const dnssec_binary_t *_value)
{
dnssec_binary_t value = *_value;
skip_leading_zeroes(&value);
if (value.size == 0 || value.data[0] & 0x80) {
return value.size + 1;
} else {
return value.size;
}
}
void bignum_write(dnssec_binary_t *dest, const dnssec_binary_t *_value)
{
dnssec_binary_t value = *_value;
skip_leading_zeroes(&value);
assert(dest->size >= value.size);
size_t padding = dest->size - value.size;
if (padding > 0) {
memset(dest->data, 0, padding);
}
memmove(dest->data + padding, value.data, value.size);
}
/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdlib.h>
#include "binary.h"
/*!
* Size needed to write unsigned number in unsigned encoding.
*/
size_t bignum_size_u(const dnssec_binary_t *value);
/*!
* Size needed to write unsigned number in signed encoding.
*
* Signed encoding expects the MSB to be zero.
*/
size_t bignum_size_s(const dnssec_binary_t *value);
/*!
* Write unsigned number on a fixed width in a big-endian byte order.
*
* The destination size has to be set properly to accomodate used encoding.
*/
void bignum_write(dnssec_binary_t *dest, const dnssec_binary_t *value);
......@@ -113,7 +113,7 @@ static inline gnutls_datum_t binary_to_datum(const dnssec_binary_t *from)
return to;
}
static inline dnssec_binary_t datum_to_binary(const gnutls_datum_t *from)
static inline dnssec_binary_t binary_from_datum(const gnutls_datum_t *from)
{
dnssec_binary_t to = { .size = from->size, .data = from->data };
return to;
......
......@@ -18,10 +18,10 @@
#include <arpa/inet.h>
#include <assert.h>
#include <gnutls/gnutls.h>
#include <stdint.h>
#include <string.h>
#include "bignum.h"
#include "binary.h"
#include "shared.h"
......@@ -109,13 +109,6 @@ static inline void wire_read_binary(wire_ctx_t *ctx, dnssec_binary_t *data)
wire_read(ctx, data->data, data->size);
}
static inline void wire_read_datum(wire_ctx_t *ctx, gnutls_datum_t *data)
{
assert(data);
wire_read(ctx, data->data, data->size);
}
static inline void wire_available_binary(wire_ctx_t *ctx, dnssec_binary_t *data)
{
assert(ctx);
......@@ -152,38 +145,19 @@ static inline void wire_write(wire_ctx_t *ctx, const uint8_t *data, size_t size)
static inline void wire_write_binary(wire_ctx_t *ctx, const dnssec_binary_t *data)
{
assert(ctx);
assert(data);
wire_write(ctx, data->data, data->size);
}
static inline void wire_write_ralign(wire_ctx_t *ctx, size_t width,
const uint8_t *data, size_t size)
static inline void wire_write_bignum(wire_ctx_t *ctx, size_t width,
const dnssec_binary_t *bignum)
{
assert(ctx);
assert(data);
assert(width >= size);
size_t padding = width - size;
if (padding > 0) {
memset(ctx->position, 0, padding);
ctx->position += padding;
}
wire_write(ctx, data, size);
}
static inline void wire_write_ralign_binary(wire_ctx_t *ctx, size_t width,
const dnssec_binary_t *data)
{
assert(data);
wire_write_ralign(ctx, width, data->data, data->size);
}
assert(bignum);
static inline void wire_write_datum(wire_ctx_t *ctx, gnutls_datum_t *data)
{
assert(data);
wire_write(ctx, data->data, data->size);
dnssec_binary_t dest = { .data = ctx->position, .size = width };
bignum_write(&dest, bignum);
ctx->position += width;
}
......@@ -20,6 +20,7 @@
/nsec_hash
/random
/shared_base32hex
/shared_bignum
/shared_dname
/shared_strtonum
/shared_wire
......
......@@ -39,6 +39,7 @@ check_PROGRAMS = \
sign \
sign_der \
shared_base32hex \
shared_bignum \
shared_dname \
shared_strtonum \
shared_wire \
......
......@@ -82,35 +82,12 @@ static void test_dup(void)
dnssec_binary_free(&dst);
}
static void test_ltrim(void)
{
dnssec_binary_t trim_me = {
.size = 4,
.data = (uint8_t []) { 0x02, 0x00, 0x01, 0x00 }
};
dnssec_binary_ltrim(&trim_me);
ok(trim_me.size == 4 && trim_me.data[0] == 0x02,
"dnssec_binary_ltrim() nothing to trim");
trim_me.data[0] = 0x00;
dnssec_binary_ltrim(&trim_me);
ok(trim_me.size == 2 && trim_me.data[0] == 0x01,
"dnssec_binary_ltrim() trim a few");
trim_me.data[0] = 0x00;
dnssec_binary_ltrim(&trim_me);
ok(trim_me.size == 1 && trim_me.data[0] == 0x00,
"dnssec_binary_ltrim() preserve last zero");
}
int main(void)
{
plan_lazy();
test_base64();
test_dup();
test_ltrim();
return 0;
}
/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <tap/basic.h>
#include <string.h>
#include "bignum.h"
#include "binary.h"
#define bin_init(array) { .data = array, .size = sizeof(array) }
#define test_size(value, usize, ssize, msg) \
dnssec_binary_t __bin = bin_init(value); \
is_int(usize, bignum_size_u(&__bin), "bignum_size_u, " msg); \
is_int(ssize, bignum_size_s(&__bin), "bignum_size_s, " msg)
#define test_write(num, expect, msg) \
uint8_t __buffer[sizeof(expect)]; \
memset(__buffer, 0xaa, sizeof(__buffer)); \
dnssec_binary_t __dst = bin_init(__buffer); \
dnssec_binary_t __num = bin_init(num); \
dnssec_binary_t __exp = bin_init(expect); \
bignum_write(&__dst, &__num); \
ok(dnssec_binary_cmp(&__dst, &__exp) == 0, "bignum_write, " msg)
int main(int argc, char *argv[])
{
plan_lazy();
{
uint8_t num[] = { };
test_size(num, 1, 1, "empty string");
}
{
uint8_t num[] = { 0x00 };
test_size(num, 1, 1, "zero");
}
{
uint8_t num[] = { 0x00, 0x00, 0x00 };
test_size(num, 1, 1, "long zero");
}
{
uint8_t num[] = { 0x01, 0x02, 0x03 };
test_size(num, 3, 3, "no MSB");
}
{
uint8_t num[] = { 0x7f, 0xff, 0x00, 0x00, 0x00 };
test_size(num, 5, 5, "no MSB but all other bits");
}
{
uint8_t num[] = { 0x84, 0x42 };
test_size(num, 2, 3, "MSB");
}
{
uint8_t num[] = { 0x00, 0x84, 0x42 };
test_size(num, 2, 3, "MSB and leading zero");
}
{
uint8_t num[] = { 0x00, 0x00, 0x00, 0x00, 0xfc, 0xe1, 0xda };
test_size(num, 3, 4, "MSB, many leading zeroes");
}
{
uint8_t num[] = { 0x00, 0x00, 0x00, 0x01 };
test_size(num, 1, 1, "no MSB, many leading zeroes");
}
// test writing
{
uint8_t num[] = { };
uint8_t exp[] = { 0x00 };
test_write(num, exp, "empty string");
}
{
uint8_t num[] = { 0x00 };
uint8_t exp[] = { 0x00 };
test_write(num, exp, "zero");
}
{
uint8_t num[] = { 0x11, 0x22, 0x33 };
uint8_t exp[] = { 0x00, 0x00, 0x00, 0x11, 0x22, 0x33 };
test_write(num, exp, "no MSB, right-aligned");
}
{
uint8_t num[] = { 0xff, 0xee, 0xdd };
uint8_t exp[] = { 0x00, 0x00, 0x00, 0xff, 0xee, 0xdd };
test_write(num, exp, "MSB, right-aligned");
}
{
uint8_t num[] = { 0x11, 0x22, 0x33 };
uint8_t exp[] = { 0x11, 0x22, 0x33 };
test_write(num, exp, "no MSB, fitting exactly");
}
{
uint8_t num[] = { 0xff, 0xee, 0xdd };
uint8_t exp[] = { 0xff, 0xee, 0xdd };
test_write(num, exp, "MSB, fitting exactly");
}
return 0;
}
......@@ -76,9 +76,10 @@ int main(void)
ok(memcmp(buffer.data + 12, out, 7) == 0 && wire_tell(&wire) == 19,
"wire_write()");
dnssec_binary_t bignum = { .data = (uint8_t *)out, .size = 4 };
const uint8_t expect[8] = { 0x00, 0x00, 0x00, 0x00, 0xc0, 0x1d, 0xca, 0xfe };
wire_seek(&wire, 2);
wire_write_ralign(&wire, 8, out, 4);
wire_write_bignum(&wire, 8, &bignum);
ok(memcmp(buffer.data + 2, expect, 8) == 0 && wire_tell(&wire) == 10,
"wire_write_ralign()");
......