| /* verify_mbedtls.c |
| * |
| * Copyright (c) 2018 Apple Computer, Inc. All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| * DNS SIG(0) signature verification for DNSSD SRP using mbedtls. |
| * |
| * Provides functions for generating a public key validating context based on SIG(0) KEY RR data, and |
| * validating a signature using a context generated with that public key. Currently only ECDSASHA256 is |
| * supported. |
| */ |
| |
| #include <stdio.h> |
| #include <arpa/inet.h> |
| #include <string.h> |
| #include <stdlib.h> |
| |
| #include "srp.h" |
| #include "dns-msg.h" |
| #include "srp-crypto.h" |
| |
| |
| // Given a DNS message, a signature, and a public key, validate the message |
| bool |
| srp_sig0_verify(dns_wire_t *message, dns_rr_t *key, dns_rr_t *signature) |
| { |
| mbedtls_ecp_point pubkey; |
| mbedtls_ecp_group group; |
| mbedtls_sha256_context sha; |
| int status; |
| char errbuf[128]; |
| uint8_t hash[ECDSA_SHA256_HASH_SIZE]; |
| mbedtls_mpi r, s; |
| uint8_t *rdata; |
| size_t rdlen; |
| |
| // The key algorithm and the signature algorithm have to match or we can't validate the signature. |
| if (key->data.key.algorithm != signature->data.sig.algorithm) { |
| return false; |
| } |
| |
| // Key must be the right length (DNS ECDSA KEY isn't compressed). |
| if (key->data.key.len != ECDSA_KEY_SIZE) { |
| return false; |
| } |
| |
| // Currently only support ecdsa |
| if (signature->data.sig.algorithm != dnssec_keytype_ecdsa) { |
| return false; |
| } |
| |
| // Make sure the signature is the right size. |
| if (signature->data.sig.len != ECDSA_SHA256_SIG_SIZE) { |
| return false; |
| } |
| |
| // Take the KEY RR and turn it into a public key we can use to check the signature. |
| // Initialize the ECP group (SECP256). |
| mbedtls_ecp_point_init(&pubkey); |
| mbedtls_ecp_group_init(&group); |
| mbedtls_ecp_group_load(&group, MBEDTLS_ECP_DP_SECP256R1); |
| mbedtls_mpi_init(&r); |
| mbedtls_mpi_init(&s); |
| mbedtls_sha256_init(&sha); |
| memset(hash, 0, sizeof hash); |
| |
| if ((status = mbedtls_mpi_read_binary(&pubkey.X, key->data.key.key, ECDSA_KEY_PART_SIZE)) != 0 || |
| (status = mbedtls_mpi_read_binary(&pubkey.Y, key->data.key.key + ECDSA_KEY_PART_SIZE, ECDSA_KEY_PART_SIZE)) != 0) { |
| mbedtls_strerror(status, errbuf, sizeof errbuf); |
| ERROR("mbedtls_mpi_read_binary: reading key: " PUB_S_SRP, errbuf); |
| } |
| mbedtls_mpi_lset(&pubkey.Z, 1); |
| |
| if ((status = mbedtls_mpi_read_binary(&r, signature->data.sig.signature, ECDSA_SHA256_SIG_PART_SIZE)) != 0 || |
| (status = mbedtls_mpi_read_binary(&s, signature->data.sig.signature + ECDSA_SHA256_SIG_PART_SIZE, |
| ECDSA_SHA256_SIG_PART_SIZE)) != 0) { |
| mbedtls_strerror(status, errbuf, sizeof errbuf); |
| ERROR("mbedtls_mpi_read_binary: reading signature: " PUB_S_SRP, errbuf); |
| } |
| |
| // The hash is across the message _before_ the SIG RR is added, so we have to decrement arcount before |
| // computing it. |
| message->arcount = htons(ntohs(message->arcount) - 1); |
| |
| // And the SIG RRDATA that we hash includes the canonical version of the name, not whatever bits |
| // are in the actual wire format message, so we have to just make a copy of it. |
| rdlen = SIG_STATIC_RDLEN + dns_name_wire_length(signature->data.sig.signer); |
| rdata = malloc(rdlen); |
| if (rdata == NULL) { |
| ERROR("no memory for SIG RR canonicalization"); |
| return 0; |
| } |
| memcpy(rdata, &message->data[signature->data.sig.start + SIG_HEADERLEN], SIG_STATIC_RDLEN); |
| if (!dns_name_to_wire_canonical(rdata + SIG_STATIC_RDLEN, rdlen - SIG_STATIC_RDLEN, |
| signature->data.sig.signer)) { |
| // Should never happen. |
| ERROR("dns_name_wire_length and dns_name_to_wire_canonical got different lengths!"); |
| return 0; |
| } |
| |
| // First compute the hash across the SIG RR, then hash the message up to the SIG RR |
| if ((status = mbedtls_sha256_starts_ret(&sha, 0)) != 0 || |
| (status = srp_mbedtls_sha256_update_ret("rdata", &sha, rdata, rdlen)) != 0 || |
| (status = srp_mbedtls_sha256_update_ret("message", &sha, (uint8_t *)message, |
| signature->data.sig.start + |
| (sizeof *message) - DNS_DATA_SIZE)) != 0 || |
| (status = srp_mbedtls_sha256_finish_ret(&sha, hash)) != 0) { |
| // Put it back |
| message->arcount = htons(ntohs(message->arcount) + 1); |
| mbedtls_strerror(status, errbuf, sizeof errbuf); |
| ERROR("mbedtls_sha_256 hash failed: " PUB_S_SRP, errbuf); |
| return 0; |
| } |
| message->arcount = htons(ntohs(message->arcount) + 1); |
| free(rdata); |
| |
| // Now check the signature against the hash |
| status = mbedtls_ecdsa_verify(&group, hash, sizeof hash, &pubkey, &r, &s); |
| if (status != 0) { |
| mbedtls_strerror(status, errbuf, sizeof errbuf); |
| ERROR("mbedtls_ecdsa_verify failed: " PUB_S_SRP, errbuf); |
| return 0; |
| } |
| return 1; |
| } |
| |
| // Function to copy out the public key as binary data |
| void |
| srp_print_key(srp_key_t *key) |
| { |
| mbedtls_ecp_keypair *ecp = mbedtls_pk_ec(key->key); |
| char errbuf[64]; |
| uint8_t buf[ECDSA_KEY_SIZE]; |
| uint8_t b64buf[((ECDSA_KEY_SIZE * 4) / 3) + 6]; |
| size_t b64len; |
| int status; |
| |
| // Currently ECP only. |
| if ((status = mbedtls_mpi_write_binary(&ecp->Q.X, buf, ECDSA_KEY_PART_SIZE)) != 0 || |
| (status = mbedtls_mpi_write_binary(&ecp->Q.Y, buf + ECDSA_KEY_PART_SIZE, ECDSA_KEY_PART_SIZE)) != 0) { |
| mbedtls_strerror(status, errbuf, sizeof errbuf); |
| ERROR("mbedtls_mpi_write_binary: " PUB_S_SRP, errbuf); |
| return; |
| } |
| |
| status = mbedtls_base64_encode(b64buf, sizeof b64buf, &b64len, buf, ECDSA_KEY_SIZE); |
| if (status != 0) { |
| mbedtls_strerror(status, errbuf, sizeof errbuf); |
| ERROR("mbedtls_mpi_write_binary: " PUB_S_SRP, errbuf); |
| return; |
| } |
| fputs("thread-demo.default.service.arpa. IN KEY 513 3 13 ", stdout); |
| fwrite(b64buf, b64len, 1, stdout); |
| putc('\n', stdout); |
| } |
| |
| // Local Variables: |
| // mode: C |
| // tab-width: 4 |
| // c-file-style: "bsd" |
| // c-basic-offset: 4 |
| // fill-column: 108 |
| // indent-tabs-mode: nil |
| // End: |