xref: /netbsd-src/external/bsd/tcpdump/dist/signature.c (revision 3117ece4fc4a4ca4489ba793710b60b0d26bab6c)
1 /*
2  * Redistribution and use in source and binary forms, with or without
3  * modification, are permitted provided that: (1) source code
4  * distributions retain the above copyright notice and this paragraph
5  * in its entirety, and (2) distributions including binary code include
6  * the above copyright notice and this paragraph in its entirety in
7  * the documentation or other materials provided with the distribution.
8  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
9  * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
10  * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
11  * FOR A PARTICULAR PURPOSE.
12  *
13  * Functions for signature and digest verification.
14  *
15  * Original code by Hannes Gredler (hannes@gredler.at)
16  */
17 
18 #include <sys/cdefs.h>
19 #ifndef lint
20 __RCSID("$NetBSD: signature.c,v 1.11 2024/09/02 16:15:33 christos Exp $");
21 #endif
22 
23 #include <config.h>
24 
25 #include "netdissect-stdinc.h"
26 
27 #include <string.h>
28 #include <stdlib.h>
29 
30 #include "netdissect.h"
31 #include "signature.h"
32 #include "diag-control.h"
33 
34 #ifdef HAVE_LIBCRYPTO
35 #include <openssl/md5.h>
36 #endif
37 
38 const struct tok signature_check_values[] = {
39     { SIGNATURE_VALID, "valid"},
40     { SIGNATURE_INVALID, "invalid"},
41     { CANT_ALLOCATE_COPY, "can't allocate memory"},
42     { CANT_CHECK_SIGNATURE, "unchecked"},
43     { 0, NULL }
44 };
45 
46 
47 #ifdef HAVE_LIBCRYPTO
48 /*
49  * Compute a HMAC MD5 sum.
50  * Taken from rfc2104, Appendix.
51  */
52 DIAG_OFF_DEPRECATION
53 static void
54 signature_compute_hmac_md5(const uint8_t *text, int text_len, unsigned char *key,
55                            unsigned int key_len, uint8_t *digest)
56 {
57     MD5_CTX context;
58     unsigned char k_ipad[65];    /* inner padding - key XORd with ipad */
59     unsigned char k_opad[65];    /* outer padding - key XORd with opad */
60     unsigned char tk[16];
61     int i;
62 
63     /* if key is longer than 64 bytes reset it to key=MD5(key) */
64     if (key_len > 64) {
65 
66         MD5_CTX tctx;
67 
68         MD5_Init(&tctx);
69         MD5_Update(&tctx, key, key_len);
70         MD5_Final(tk, &tctx);
71 
72         key = tk;
73         key_len = 16;
74     }
75 
76     /*
77      * the HMAC_MD5 transform looks like:
78      *
79      * MD5(K XOR opad, MD5(K XOR ipad, text))
80      *
81      * where K is an n byte key
82      * ipad is the byte 0x36 repeated 64 times
83      * opad is the byte 0x5c repeated 64 times
84      * and text is the data being protected
85      */
86 
87     /* start out by storing key in pads */
88     memset(k_ipad, 0, sizeof(k_ipad));
89     memset(k_opad, 0, sizeof(k_opad));
90     memcpy(k_ipad, key, key_len);
91     memcpy(k_opad, key, key_len);
92 
93     /* XOR key with ipad and opad values */
94     for (i=0; i<64; i++) {
95         k_ipad[i] ^= 0x36;
96         k_opad[i] ^= 0x5c;
97     }
98 
99     /*
100      * perform inner MD5
101      */
102     MD5_Init(&context);                   /* init context for 1st pass */
103     MD5_Update(&context, k_ipad, 64);     /* start with inner pad */
104     MD5_Update(&context, text, text_len); /* then text of datagram */
105     MD5_Final(digest, &context);          /* finish up 1st pass */
106 
107     /*
108      * perform outer MD5
109      */
110     MD5_Init(&context);                   /* init context for 2nd pass */
111     MD5_Update(&context, k_opad, 64);     /* start with outer pad */
112     MD5_Update(&context, digest, 16);     /* then results of 1st hash */
113     MD5_Final(digest, &context);          /* finish up 2nd pass */
114 }
115 DIAG_ON_DEPRECATION
116 
117 /*
118  * Verify a cryptographic signature of the packet.
119  * Currently only MD5 is supported.
120  */
121 int
122 signature_verify(netdissect_options *ndo, const u_char *pptr, u_int plen,
123                  const u_char *sig_ptr, void (*clear_rtn)(void *),
124                  const void *clear_arg)
125 {
126     uint8_t *packet_copy, *sig_copy;
127     uint8_t sig[16];
128     unsigned int i;
129 
130     if (!ndo->ndo_sigsecret) {
131         return (CANT_CHECK_SIGNATURE);
132     }
133 
134     /*
135      * Do we have all the packet data to be checked?
136      */
137     if (!ND_TTEST_LEN(pptr, plen)) {
138         /* No. */
139         return (CANT_CHECK_SIGNATURE);
140     }
141 
142     /*
143      * Do we have the entire signature to check?
144      */
145     if (!ND_TTEST_LEN(sig_ptr, sizeof(sig))) {
146         /* No. */
147         return (CANT_CHECK_SIGNATURE);
148     }
149     if (sig_ptr + sizeof(sig) > pptr + plen) {
150         /* No. */
151         return (CANT_CHECK_SIGNATURE);
152     }
153 
154     /*
155      * Make a copy of the packet, so we don't overwrite the original.
156      */
157     packet_copy = malloc(plen);
158     if (packet_copy == NULL) {
159         return (CANT_ALLOCATE_COPY);
160     }
161 
162     memcpy(packet_copy, pptr, plen);
163 
164     /*
165      * Clear the signature in the copy.
166      */
167     sig_copy = packet_copy + (sig_ptr - pptr);
168     memset(sig_copy, 0, sizeof(sig));
169 
170     /*
171      * Clear anything else that needs to be cleared in the copy.
172      * Our caller is assumed to have vetted the clear_arg pointer.
173      */
174     (*clear_rtn)((void *)(packet_copy + ((const uint8_t *)clear_arg - pptr)));
175 
176     /*
177      * Compute the signature.
178      */
179     signature_compute_hmac_md5(packet_copy, plen,
180                                (unsigned char *)ndo->ndo_sigsecret,
181                                strlen(ndo->ndo_sigsecret), sig);
182 
183     /*
184      * Free the copy.
185      */
186     free(packet_copy);
187 
188     /*
189      * Does the computed signature match the signature in the packet?
190      */
191     if (memcmp(sig_ptr, sig, sizeof(sig)) == 0) {
192         /* Yes. */
193         return (SIGNATURE_VALID);
194     } else {
195         /* No - print the computed signature. */
196         for (i = 0; i < sizeof(sig); ++i) {
197             ND_PRINT("%02x", sig[i]);
198         }
199 
200         return (SIGNATURE_INVALID);
201     }
202 }
203 #else
204 int
205 signature_verify(netdissect_options *ndo _U_, const u_char *pptr _U_,
206                  u_int plen _U_, const u_char *sig_ptr _U_,
207                  void (*clear_rtn)(void *) _U_, const void *clear_arg _U_)
208 {
209     return (CANT_CHECK_SIGNATURE);
210 }
211 #endif
212