/* Copyright (C) 2018 CZ.NIC, z.s.p.o. 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 . */ #include #include "libdnssec/shared/bignum.h" #include "libdnssec/binary.h" #include "libdnssec/error.h" #include "libdnssec/sign/der.h" #include "libdnssec/shared/binary_wire.h" /* * In fact, this is a very tiny subset of ASN.1 encoding format implementation, * which is necessary for the purpose of DNSSEC. * * References: RFC 3279 (X.509 PKI), X.690, RFC 6605 (ECDSA), RFC8080 (EDDSA) * * Dss-Sig-Value ::= SEQUENCE { r INTEGER, s INTEGER } */ #define ASN1_TYPE_SEQUENCE 0x30 #define ASN1_TYPE_INTEGER 0x02 #define ASN1_MAX_SIZE 127 /*! * Check if the next object has a given type. */ static bool asn1_expect_type(wire_ctx_t *wire, uint8_t type) { assert(wire); return (wire_ctx_available(wire) >= 1 && wire_ctx_read_u8(wire) == type); } /*! * Decode the size of the object (only short format is supported). */ static int asn1_decode_size(wire_ctx_t *wire, size_t *size) { assert(wire); assert(size); if (wire_ctx_available(wire) < 1) { return DNSSEC_MALFORMED_DATA; } uint8_t byte = wire_ctx_read_u8(wire); if (byte & 0x80) { // long form, we do not need it for DNSSEC return DNSSEC_NOT_IMPLEMENTED_ERROR; } *size = byte; return DNSSEC_EOK; } /*! * Decode an unsigned integer object. */ static int asn1_decode_integer(wire_ctx_t *wire, dnssec_binary_t *_value) { assert(wire); assert(_value); if (!asn1_expect_type(wire, ASN1_TYPE_INTEGER)) { return DNSSEC_MALFORMED_DATA; } size_t size; int result = asn1_decode_size(wire, &size); if (result != DNSSEC_EOK) { return result; } if (size == 0 || size > wire_ctx_available(wire)) { return DNSSEC_MALFORMED_DATA; } 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; } /*! * Encode object header (type and length). */ static void asn1_write_header(wire_ctx_t *wire, uint8_t type, size_t length) { assert(wire); assert(length < ASN1_MAX_SIZE); wire_ctx_write_u8(wire, type); wire_ctx_write_u8(wire, length); } /*! * Encode unsigned integer object. */ 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); bignum_write(wire, integer_size, integer); } /*! * Decode signature parameters from X.509 ECDSA signature. */ int dss_sig_value_decode(const dnssec_binary_t *der, dnssec_binary_t *r, dnssec_binary_t *s) { if (!der || !der->data || !r || !s) { return DNSSEC_EINVAL; } wire_ctx_t wire = binary_init(der); size_t size; int result; // decode the sequence if (!asn1_expect_type(&wire, ASN1_TYPE_SEQUENCE)) { return DNSSEC_MALFORMED_DATA; } result = asn1_decode_size(&wire, &size); if (result != DNSSEC_EOK) { return result; } if (size != wire_ctx_available(&wire)) { return DNSSEC_MALFORMED_DATA; } // decode the 'r' and 's' values dnssec_binary_t der_r; result = asn1_decode_integer(&wire, &der_r); if (result != DNSSEC_EOK) { return result; } dnssec_binary_t der_s; result = asn1_decode_integer(&wire, &der_s); if (result != DNSSEC_EOK) { return result; } if (wire_ctx_available(&wire) != 0) { return DNSSEC_MALFORMED_DATA; } *r = der_r; *s = der_s; return DNSSEC_EOK; } /*! * Encode signature parameters from X.509 ECDSA signature. */ int dss_sig_value_encode(const dnssec_binary_t *r, const dnssec_binary_t *s, dnssec_binary_t *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) { return DNSSEC_NOT_IMPLEMENTED_ERROR; } size_t seq_size = 2 + r_size + 2 + s_size; if (seq_size > ASN1_MAX_SIZE) { return DNSSEC_NOT_IMPLEMENTED_ERROR; } // encode result size_t total_size = 2 + seq_size; dnssec_binary_t _der = { 0 }; if (dnssec_binary_alloc(&_der, total_size)) { return DNSSEC_ENOMEM; } wire_ctx_t wire = binary_init(&_der); asn1_write_header(&wire, ASN1_TYPE_SEQUENCE, seq_size); asn1_write_integer(&wire, r_size, r); asn1_write_integer(&wire, s_size, s); assert(wire_ctx_available(&wire) == 0); *der = _der; return DNSSEC_EOK; }