1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: stable/11/sbin/hastd/hast_checksum.c 330449 2018-03-05 07:26:05Z eadler $");
31
32#include <errno.h>
33#include <string.h>
34#include <strings.h>
35
36#ifdef HAVE_CRYPTO
37#include <openssl/sha.h>
38#endif
39
40#include <crc32.h>
41#include <hast.h>
42#include <nv.h>
43#include <pjdlog.h>
44
45#include "hast_checksum.h"
46
47#ifdef HAVE_CRYPTO
48#define	MAX_HASH_SIZE	SHA256_DIGEST_LENGTH
49#else
50#define	MAX_HASH_SIZE	4
51#endif
52
53static void
54hast_crc32_checksum(const unsigned char *data, size_t size,
55    unsigned char *hash, size_t *hsizep)
56{
57	uint32_t crc;
58
59	crc = crc32(data, size);
60	/* XXXPJD: Do we have to use htole32() on crc first? */
61	bcopy(&crc, hash, sizeof(crc));
62	*hsizep = sizeof(crc);
63}
64
65#ifdef HAVE_CRYPTO
66static void
67hast_sha256_checksum(const unsigned char *data, size_t size,
68    unsigned char *hash, size_t *hsizep)
69{
70	SHA256_CTX ctx;
71
72	SHA256_Init(&ctx);
73	SHA256_Update(&ctx, data, size);
74	SHA256_Final(hash, &ctx);
75	*hsizep = SHA256_DIGEST_LENGTH;
76}
77#endif	/* HAVE_CRYPTO */
78
79const char *
80checksum_name(int num)
81{
82
83	switch (num) {
84	case HAST_CHECKSUM_NONE:
85		return ("none");
86	case HAST_CHECKSUM_CRC32:
87		return ("crc32");
88	case HAST_CHECKSUM_SHA256:
89		return ("sha256");
90	}
91	return ("unknown");
92}
93
94int
95checksum_send(const struct hast_resource *res, struct nv *nv, void **datap,
96    size_t *sizep, bool *freedatap __unused)
97{
98	unsigned char hash[MAX_HASH_SIZE];
99	size_t hsize;
100
101	switch (res->hr_checksum) {
102	case HAST_CHECKSUM_NONE:
103		return (0);
104	case HAST_CHECKSUM_CRC32:
105		hast_crc32_checksum(*datap, *sizep, hash, &hsize);
106		break;
107#ifdef HAVE_CRYPTO
108	case HAST_CHECKSUM_SHA256:
109		hast_sha256_checksum(*datap, *sizep, hash, &hsize);
110		break;
111#endif
112	default:
113		PJDLOG_ABORT("Invalid checksum: %d.", res->hr_checksum);
114	}
115	nv_add_string(nv, checksum_name(res->hr_checksum), "checksum");
116	nv_add_uint8_array(nv, hash, hsize, "hash");
117	if (nv_error(nv) != 0) {
118		errno = nv_error(nv);
119		return (-1);
120	}
121	return (0);
122}
123
124int
125checksum_recv(const struct hast_resource *res __unused, struct nv *nv,
126    void **datap, size_t *sizep, bool *freedatap __unused)
127{
128	unsigned char chash[MAX_HASH_SIZE];
129	const unsigned char *rhash;
130	size_t chsize, rhsize;
131	const char *algo;
132
133	algo = nv_get_string(nv, "checksum");
134	if (algo == NULL)
135		return (0);	/* No checksum. */
136	rhash = nv_get_uint8_array(nv, &rhsize, "hash");
137	if (rhash == NULL) {
138		pjdlog_error("Hash is missing.");
139		return (-1);	/* Hash not found. */
140	}
141	if (strcmp(algo, "crc32") == 0)
142		hast_crc32_checksum(*datap, *sizep, chash, &chsize);
143#ifdef HAVE_CRYPTO
144	else if (strcmp(algo, "sha256") == 0)
145		hast_sha256_checksum(*datap, *sizep, chash, &chsize);
146#endif
147	else {
148		pjdlog_error("Unknown checksum algorithm '%s'.", algo);
149		return (-1);	/* Unknown checksum algorithm. */
150	}
151	if (rhsize != chsize) {
152		pjdlog_error("Invalid hash size (%zu) for %s, should be %zu.",
153		    rhsize, algo, chsize);
154		return (-1);	/* Different hash size. */
155	}
156	if (bcmp(rhash, chash, chsize) != 0) {
157		pjdlog_error("Hash mismatch.");
158		return (-1);	/* Hash mismatch. */
159	}
160
161	return (0);
162}
163