1 /* verify_mbedtls.c
2  *
3  * Copyright (c) 2018 Apple Computer, Inc. All rights reserved.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * DNS SIG(0) signature verification for DNSSD SRP using mbedtls.
18  *
19  * Provides functions for generating a public key validating context based on SIG(0) KEY RR data, and
20  * validating a signature using a context generated with that public key.  Currently only ECDSASHA256 is
21  * supported.
22  */
23 
24 #include <stdio.h>
25 #include <arpa/inet.h>
26 #include <string.h>
27 #include <stdlib.h>
28 
29 #include "srp.h"
30 #define SRP_CRYPTO_MBEDTLS_INTERNAL
31 #include "dns-msg.h"
32 #include "srp-crypto.h"
33 
34 
35 // Given a DNS message, a signature, and a public key, validate the message
36 bool
srp_sig0_verify(dns_wire_t * message,dns_rr_t * key,dns_rr_t * signature)37 srp_sig0_verify(dns_wire_t *message, dns_rr_t *key, dns_rr_t *signature)
38 {
39     mbedtls_ecp_point pubkey;
40     mbedtls_ecp_group group;
41     mbedtls_sha256_context sha;
42     int status;
43     char errbuf[128];
44     uint8_t hash[ECDSA_SHA256_HASH_SIZE];
45     mbedtls_mpi r, s;
46     uint8_t *rdata;
47     size_t rdlen;
48 
49     // The key algorithm and the signature algorithm have to match or we can't validate the signature.
50     if (key->data.key.algorithm != signature->data.sig.algorithm) {
51         return false;
52     }
53 
54     // Key must be the right length (DNS ECDSA KEY isn't compressed).
55     if (key->data.key.len != ECDSA_KEY_SIZE) {
56         return false;
57     }
58 
59     // Currently only support ecdsa
60     if (signature->data.sig.algorithm != dnssec_keytype_ecdsa) {
61         return false;
62     }
63 
64     // Make sure the signature is the right size.
65     if (signature->data.sig.len != ECDSA_SHA256_SIG_SIZE) {
66         return false;
67     }
68 
69     // Take the KEY RR and turn it into a public key we can use to check the signature.
70     // Initialize the ECP group (SECP256).
71     mbedtls_ecp_point_init(&pubkey);
72     mbedtls_ecp_group_init(&group);
73     mbedtls_ecp_group_load(&group, MBEDTLS_ECP_DP_SECP256R1);
74     mbedtls_mpi_init(&r);
75     mbedtls_mpi_init(&s);
76     mbedtls_sha256_init(&sha);
77     memset(hash, 0, sizeof hash);
78 
79     if ((status = mbedtls_mpi_read_binary(&pubkey.X, key->data.key.key, ECDSA_KEY_PART_SIZE)) != 0 ||
80         (status = mbedtls_mpi_read_binary(&pubkey.Y, key->data.key.key + ECDSA_KEY_PART_SIZE, ECDSA_KEY_PART_SIZE)) != 0) {
81         mbedtls_strerror(status, errbuf, sizeof errbuf);
82         ERROR("mbedtls_mpi_read_binary: reading key: " PUB_S_SRP, errbuf);
83     }
84     mbedtls_mpi_lset(&pubkey.Z, 1);
85 
86     if ((status = mbedtls_mpi_read_binary(&r, signature->data.sig.signature, ECDSA_SHA256_SIG_PART_SIZE)) != 0 ||
87         (status = mbedtls_mpi_read_binary(&s, signature->data.sig.signature + ECDSA_SHA256_SIG_PART_SIZE,
88                                           ECDSA_SHA256_SIG_PART_SIZE)) != 0) {
89         mbedtls_strerror(status, errbuf, sizeof errbuf);
90         ERROR("mbedtls_mpi_read_binary: reading signature: " PUB_S_SRP, errbuf);
91     }
92 
93     // The hash is across the message _before_ the SIG RR is added, so we have to decrement arcount before
94  	// computing it.
95     message->arcount = htons(ntohs(message->arcount) - 1);
96 
97     // And the SIG RRDATA that we hash includes the canonical version of the name, not whatever bits
98     // are in the actual wire format message, so we have to just make a copy of it.
99     rdlen = SIG_STATIC_RDLEN + dns_name_wire_length(signature->data.sig.signer);
100     rdata = malloc(rdlen);
101     if (rdata == NULL) {
102         ERROR("no memory for SIG RR canonicalization");
103         return 0;
104     }
105     memcpy(rdata, &message->data[signature->data.sig.start + SIG_HEADERLEN], SIG_STATIC_RDLEN);
106     if (!dns_name_to_wire_canonical(rdata + SIG_STATIC_RDLEN, rdlen - SIG_STATIC_RDLEN,
107                                     signature->data.sig.signer)) {
108         // Should never happen.
109         ERROR("dns_name_wire_length and dns_name_to_wire_canonical got different lengths!");
110         return 0;
111     }
112 
113     // First compute the hash across the SIG RR, then hash the message up to the SIG RR
114     if ((status = mbedtls_sha256_starts_ret(&sha, 0)) != 0 ||
115         (status = srp_mbedtls_sha256_update_ret("rdata", &sha, rdata, rdlen)) != 0 ||
116         (status = srp_mbedtls_sha256_update_ret("message", &sha, (uint8_t *)message,
117                                                 signature->data.sig.start +
118                                                 (sizeof *message) - DNS_DATA_SIZE)) != 0 ||
119         (status = srp_mbedtls_sha256_finish_ret(&sha, hash)) != 0) {
120         // Put it back
121         message->arcount = htons(ntohs(message->arcount) + 1);
122         mbedtls_strerror(status, errbuf, sizeof errbuf);
123         ERROR("mbedtls_sha_256 hash failed: " PUB_S_SRP, errbuf);
124         return 0;
125     }
126     message->arcount = htons(ntohs(message->arcount) + 1);
127     free(rdata);
128 
129     // Now check the signature against the hash
130     status = mbedtls_ecdsa_verify(&group, hash, sizeof hash, &pubkey, &r, &s);
131     if (status != 0) {
132         mbedtls_strerror(status, errbuf, sizeof errbuf);
133         ERROR("mbedtls_ecdsa_verify failed: " PUB_S_SRP, errbuf);
134         return 0;
135     }
136     return 1;
137 }
138 
139 // Function to copy out the public key as binary data
140 void
srp_print_key(srp_key_t * key)141 srp_print_key(srp_key_t *key)
142 {
143     mbedtls_ecp_keypair *ecp = mbedtls_pk_ec(key->key);
144     char errbuf[64];
145     uint8_t buf[ECDSA_KEY_SIZE];
146     uint8_t b64buf[((ECDSA_KEY_SIZE * 4) / 3) + 6];
147     size_t b64len;
148     int status;
149 
150     // Currently ECP only.
151     if ((status = mbedtls_mpi_write_binary(&ecp->Q.X, buf, ECDSA_KEY_PART_SIZE)) != 0 ||
152         (status = mbedtls_mpi_write_binary(&ecp->Q.Y, buf + ECDSA_KEY_PART_SIZE, ECDSA_KEY_PART_SIZE)) != 0) {
153         mbedtls_strerror(status, errbuf, sizeof errbuf);
154         ERROR("mbedtls_mpi_write_binary: " PUB_S_SRP, errbuf);
155         return;
156     }
157 
158     status = mbedtls_base64_encode(b64buf, sizeof b64buf, &b64len, buf, ECDSA_KEY_SIZE);
159     if (status != 0) {
160         mbedtls_strerror(status, errbuf, sizeof errbuf);
161         ERROR("mbedtls_mpi_write_binary: " PUB_S_SRP, errbuf);
162         return;
163     }
164     fputs("thread-demo.default.service.arpa. IN KEY 513 3 13 ", stdout);
165     fwrite(b64buf, b64len, 1, stdout);
166     putc('\n', stdout);
167 }
168 
169 // Local Variables:
170 // mode: C
171 // tab-width: 4
172 // c-file-style: "bsd"
173 // c-basic-offset: 4
174 // fill-column: 108
175 // indent-tabs-mode: nil
176 // End:
177