1 /* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2
3 This program is free software: you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation, either version 3 of the License, or
6 (at your option) any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <https://www.gnu.org/licenses/>.
15 */
16
17 #include <stdbool.h>
18
19 #include "libdnssec/shared/bignum.h"
20 #include "libdnssec/binary.h"
21 #include "libdnssec/error.h"
22 #include "libdnssec/sign/der.h"
23 #include "libdnssec/shared/binary_wire.h"
24
25 /*
26 * In fact, this is a very tiny subset of ASN.1 encoding format implementation,
27 * which is necessary for the purpose of DNSSEC.
28 *
29 * References: RFC 3279 (X.509 PKI), X.690, RFC 6605 (ECDSA), RFC8080 (EDDSA)
30 *
31 * Dss-Sig-Value ::= SEQUENCE { r INTEGER, s INTEGER }
32 */
33
34 #define ASN1_TYPE_SEQUENCE 0x30
35 #define ASN1_TYPE_INTEGER 0x02
36
37 #define ASN1_MAX_SIZE 127
38
39 /*!
40 * Check if the next object has a given type.
41 */
asn1_expect_type(wire_ctx_t * wire,uint8_t type)42 static bool asn1_expect_type(wire_ctx_t *wire, uint8_t type)
43 {
44 assert(wire);
45 return (wire_ctx_available(wire) >= 1 && wire_ctx_read_u8(wire) == type);
46 }
47
48 /*!
49 * Decode the size of the object (only short format is supported).
50 */
asn1_decode_size(wire_ctx_t * wire,size_t * size)51 static int asn1_decode_size(wire_ctx_t *wire, size_t *size)
52 {
53 assert(wire);
54 assert(size);
55
56 if (wire_ctx_available(wire) < 1) {
57 return DNSSEC_MALFORMED_DATA;
58 }
59
60 uint8_t byte = wire_ctx_read_u8(wire);
61 if (byte & 0x80) {
62 // long form, we do not need it for DNSSEC
63 return DNSSEC_NOT_IMPLEMENTED_ERROR;
64 }
65
66 *size = byte;
67
68 return DNSSEC_EOK;
69 }
70
71 /*!
72 * Decode an unsigned integer object.
73 */
asn1_decode_integer(wire_ctx_t * wire,dnssec_binary_t * _value)74 static int asn1_decode_integer(wire_ctx_t *wire, dnssec_binary_t *_value)
75 {
76 assert(wire);
77 assert(_value);
78
79 if (!asn1_expect_type(wire, ASN1_TYPE_INTEGER)) {
80 return DNSSEC_MALFORMED_DATA;
81 }
82
83 size_t size;
84 int result = asn1_decode_size(wire, &size);
85 if (result != DNSSEC_EOK) {
86 return result;
87 }
88
89 if (size == 0 || size > wire_ctx_available(wire)) {
90 return DNSSEC_MALFORMED_DATA;
91 }
92
93 dnssec_binary_t value = { .data = wire->position, .size = size };
94 wire->position += size;
95
96 // skip leading zeroes (unless equal to zero)
97 while (value.size > 1 && value.data[0] == 0) {
98 value.data += 1;
99 value.size -= 1;
100 }
101
102 *_value = value;
103
104 return DNSSEC_EOK;
105 }
106
107 /*!
108 * Encode object header (type and length).
109 */
asn1_write_header(wire_ctx_t * wire,uint8_t type,size_t length)110 static void asn1_write_header(wire_ctx_t *wire, uint8_t type, size_t length)
111 {
112 assert(wire);
113 assert(length < ASN1_MAX_SIZE);
114
115 wire_ctx_write_u8(wire, type);
116 wire_ctx_write_u8(wire, length);
117 }
118
119 /*!
120 * Encode unsigned integer object.
121 */
asn1_write_integer(wire_ctx_t * wire,size_t integer_size,const dnssec_binary_t * integer)122 static void asn1_write_integer(wire_ctx_t *wire, size_t integer_size,
123 const dnssec_binary_t *integer)
124 {
125 assert(wire);
126 assert(integer);
127 assert(integer->data);
128
129 asn1_write_header(wire, ASN1_TYPE_INTEGER, integer_size);
130 bignum_write(wire, integer_size, integer);
131 }
132
133 /*!
134 * Decode signature parameters from X.509 ECDSA signature.
135 */
dss_sig_value_decode(const dnssec_binary_t * der,dnssec_binary_t * r,dnssec_binary_t * s)136 int dss_sig_value_decode(const dnssec_binary_t *der,
137 dnssec_binary_t *r, dnssec_binary_t *s)
138 {
139 if (!der || !der->data || !r || !s) {
140 return DNSSEC_EINVAL;
141 }
142
143 wire_ctx_t wire = binary_init(der);
144
145 size_t size;
146 int result;
147
148 // decode the sequence
149
150 if (!asn1_expect_type(&wire, ASN1_TYPE_SEQUENCE)) {
151 return DNSSEC_MALFORMED_DATA;
152 }
153
154 result = asn1_decode_size(&wire, &size);
155 if (result != DNSSEC_EOK) {
156 return result;
157 }
158
159 if (size != wire_ctx_available(&wire)) {
160 return DNSSEC_MALFORMED_DATA;
161 }
162
163 // decode the 'r' and 's' values
164
165 dnssec_binary_t der_r;
166 result = asn1_decode_integer(&wire, &der_r);
167 if (result != DNSSEC_EOK) {
168 return result;
169 }
170
171 dnssec_binary_t der_s;
172 result = asn1_decode_integer(&wire, &der_s);
173 if (result != DNSSEC_EOK) {
174 return result;
175 }
176
177 if (wire_ctx_available(&wire) != 0) {
178 return DNSSEC_MALFORMED_DATA;
179 }
180
181 *r = der_r;
182 *s = der_s;
183
184 return DNSSEC_EOK;
185 }
186
187 /*!
188 * Encode signature parameters from X.509 ECDSA signature.
189 */
dss_sig_value_encode(const dnssec_binary_t * r,const dnssec_binary_t * s,dnssec_binary_t * der)190 int dss_sig_value_encode(const dnssec_binary_t *r, const dnssec_binary_t *s,
191 dnssec_binary_t *der)
192 {
193 if (!r || !r->data || !s || !s->data || !der) {
194 return DNSSEC_EINVAL;
195 }
196
197 size_t r_size = bignum_size_s(r);
198 size_t s_size = bignum_size_s(s);
199
200 // check supported inputs range
201
202 if (r_size > ASN1_MAX_SIZE || s_size > ASN1_MAX_SIZE) {
203 return DNSSEC_NOT_IMPLEMENTED_ERROR;
204 }
205
206 size_t seq_size = 2 + r_size + 2 + s_size;
207 if (seq_size > ASN1_MAX_SIZE) {
208 return DNSSEC_NOT_IMPLEMENTED_ERROR;
209 }
210
211 // encode result
212
213 size_t total_size = 2 + seq_size;
214
215 dnssec_binary_t _der = { 0 };
216 if (dnssec_binary_alloc(&_der, total_size)) {
217 return DNSSEC_ENOMEM;
218 }
219
220 wire_ctx_t wire = binary_init(&_der);
221 asn1_write_header(&wire, ASN1_TYPE_SEQUENCE, seq_size);
222 asn1_write_integer(&wire, r_size, r);
223 asn1_write_integer(&wire, s_size, s);
224 assert(wire_ctx_available(&wire) == 0);
225
226 *der = _der;
227
228 return DNSSEC_EOK;
229 }
230