1 /* $NetBSD: a_md5encrypt.c,v 1.13 2024/08/18 20:47:13 christos Exp $ */ 2 3 /* 4 * digest support for NTP, MD5 and with OpenSSL more 5 */ 6 #ifdef HAVE_CONFIG_H 7 #include <config.h> 8 #endif 9 10 #include "ntp_fp.h" 11 #include "ntp_string.h" 12 #include "ntp_stdlib.h" 13 #include "ntp.h" 14 #include "isc/string.h" 15 16 typedef struct { 17 const void * buf; 18 size_t len; 19 } robuffT; 20 21 typedef struct { 22 void * buf; 23 size_t len; 24 } rwbuffT; 25 26 #if defined(OPENSSL) && defined(ENABLE_CMAC) 27 static size_t 28 cmac_ctx_size( 29 CMAC_CTX * ctx 30 ) 31 { 32 size_t mlen = 0; 33 34 if (ctx) { 35 EVP_CIPHER_CTX * cctx; 36 if (NULL != (cctx = CMAC_CTX_get0_cipher_ctx (ctx))) 37 mlen = EVP_CIPHER_CTX_block_size(cctx); 38 } 39 return mlen; 40 } 41 #endif /* OPENSSL && ENABLE_CMAC */ 42 43 44 /* 45 * Allocate and initialize a digest context. As a speed optimization, 46 * take an idea from ntpsec and cache the context to avoid malloc/free 47 * overhead in time-critical paths. ntpsec also caches the algorithms 48 * with each key. 49 * This is not thread-safe, but that is 50 * not a problem at present. 51 */ 52 static EVP_MD_CTX * 53 get_md_ctx( 54 int nid 55 ) 56 { 57 #ifndef OPENSSL 58 static MD5_CTX md5_ctx; 59 60 DEBUG_INSIST(NID_md5 == nid); 61 MD5Init(&md5_ctx); 62 63 return &md5_ctx; 64 #else 65 if (!EVP_DigestInit(digest_ctx, EVP_get_digestbynid(nid))) { 66 msyslog(LOG_ERR, "%s init failed", OBJ_nid2sn(nid)); 67 return NULL; 68 } 69 70 return digest_ctx; 71 #endif /* OPENSSL */ 72 } 73 74 75 static size_t 76 make_mac( 77 const rwbuffT * digest, 78 int ktype, 79 const robuffT * key, 80 const robuffT * msg 81 ) 82 { 83 /* 84 * Compute digest of key concatenated with packet. Note: the 85 * key type and digest type have been verified when the key 86 * was created. 87 */ 88 size_t retlen = 0; 89 90 #ifdef OPENSSL 91 92 INIT_SSL(); 93 94 /* Check if CMAC key type specific code required */ 95 # ifdef ENABLE_CMAC 96 if (ktype == NID_cmac) { 97 CMAC_CTX * ctx = NULL; 98 void const * keyptr = key->buf; 99 u_char keybuf[AES_128_KEY_SIZE]; 100 101 /* adjust key size (zero padded buffer) if necessary */ 102 if (AES_128_KEY_SIZE > key->len) { 103 memcpy(keybuf, keyptr, key->len); 104 zero_mem((keybuf + key->len), 105 (AES_128_KEY_SIZE - key->len)); 106 keyptr = keybuf; 107 } 108 109 if (NULL == (ctx = CMAC_CTX_new())) { 110 msyslog(LOG_ERR, "MAC encrypt: CMAC %s CTX new failed.", CMAC); 111 goto cmac_fail; 112 } 113 if (!CMAC_Init(ctx, keyptr, AES_128_KEY_SIZE, EVP_aes_128_cbc(), NULL)) { 114 msyslog(LOG_ERR, "MAC encrypt: CMAC %s Init failed.", CMAC); 115 goto cmac_fail; 116 } 117 if (cmac_ctx_size(ctx) > digest->len) { 118 msyslog(LOG_ERR, "MAC encrypt: CMAC %s buf too small.", CMAC); 119 goto cmac_fail; 120 } 121 if (!CMAC_Update(ctx, msg->buf, msg->len)) { 122 msyslog(LOG_ERR, "MAC encrypt: CMAC %s Update failed.", CMAC); 123 goto cmac_fail; 124 } 125 if (!CMAC_Final(ctx, digest->buf, &retlen)) { 126 msyslog(LOG_ERR, "MAC encrypt: CMAC %s Final failed.", CMAC); 127 retlen = 0; 128 } 129 cmac_fail: 130 if (ctx) 131 CMAC_CTX_free(ctx); 132 } 133 else 134 # endif /* ENABLE_CMAC */ 135 { /* generic MAC handling */ 136 EVP_MD_CTX * ctx; 137 u_int uilen = 0; 138 139 ctx = get_md_ctx(ktype); 140 if (NULL == ctx) { 141 goto mac_fail; 142 } 143 if ((size_t)EVP_MD_CTX_size(ctx) > digest->len) { 144 msyslog(LOG_ERR, "MAC encrypt: MAC %s buf too small.", 145 OBJ_nid2sn(ktype)); 146 goto mac_fail; 147 } 148 if (!EVP_DigestUpdate(ctx, key->buf, (u_int)key->len)) { 149 msyslog(LOG_ERR, "MAC encrypt: MAC %s Digest Update key failed.", 150 OBJ_nid2sn(ktype)); 151 goto mac_fail; 152 } 153 if (!EVP_DigestUpdate(ctx, msg->buf, (u_int)msg->len)) { 154 msyslog(LOG_ERR, "MAC encrypt: MAC %s Digest Update data failed.", 155 OBJ_nid2sn(ktype)); 156 goto mac_fail; 157 } 158 if (!EVP_DigestFinal(ctx, digest->buf, &uilen)) { 159 msyslog(LOG_ERR, "MAC encrypt: MAC %s Digest Final failed.", 160 OBJ_nid2sn(ktype)); 161 uilen = 0; 162 } 163 mac_fail: 164 retlen = (size_t)uilen; 165 } 166 167 #else /* !OPENSSL follows */ 168 169 if (NID_md5 == ktype) { 170 EVP_MD_CTX * ctx; 171 172 ctx = get_md_ctx(ktype); 173 if (digest->len < MD5_LENGTH) { 174 msyslog(LOG_ERR, "%s", "MAC encrypt: MAC md5 buf too small."); 175 } else { 176 MD5Init(ctx); 177 MD5Update(ctx, (const void *)key->buf, key->len); 178 MD5Update(ctx, (const void *)msg->buf, msg->len); 179 MD5Final(digest->buf, ctx); 180 retlen = MD5_LENGTH; 181 } 182 } else { 183 msyslog(LOG_ERR, "MAC encrypt: invalid key type %d", ktype); 184 } 185 186 #endif /* !OPENSSL */ 187 188 return retlen; 189 } 190 191 192 /* 193 * MD5authencrypt - generate message digest 194 * 195 * Returns 0 on failure or length of MAC including key ID. 196 */ 197 size_t 198 MD5authencrypt( 199 int type, /* hash algorithm */ 200 const u_char * key, /* key pointer */ 201 size_t klen, /* key length */ 202 u_int32 * pkt, /* packet pointer */ 203 size_t length /* packet length */ 204 ) 205 { 206 u_char digest[EVP_MAX_MD_SIZE]; 207 rwbuffT digb = { digest, sizeof(digest) }; 208 robuffT keyb = { key, klen }; 209 robuffT msgb = { pkt, length }; 210 size_t dlen; 211 212 dlen = make_mac(&digb, type, &keyb, &msgb); 213 if (0 == dlen) { 214 return 0; 215 } 216 memcpy((u_char *)pkt + length + KEY_MAC_LEN, digest, 217 min(dlen, MAX_MDG_LEN)); 218 return (dlen + KEY_MAC_LEN); 219 } 220 221 222 /* 223 * MD5authdecrypt - verify MD5 message authenticator 224 * 225 * Returns one if digest valid, zero if invalid. 226 */ 227 int 228 MD5authdecrypt( 229 int type, /* hash algorithm */ 230 const u_char * key, /* key pointer */ 231 size_t klen, /* key length */ 232 u_int32 * pkt, /* packet pointer */ 233 size_t length, /* packet length */ 234 size_t size, /* MAC size */ 235 keyid_t keyno /* key id (for err log) */ 236 ) 237 { 238 u_char digest[EVP_MAX_MD_SIZE]; 239 rwbuffT digb = { digest, sizeof(digest) }; 240 robuffT keyb = { key, klen }; 241 robuffT msgb = { pkt, length }; 242 size_t dlen = 0; 243 244 dlen = make_mac(&digb, type, &keyb, &msgb); 245 if (0 == dlen || size != dlen + KEY_MAC_LEN) { 246 msyslog(LOG_ERR, 247 "MAC decrypt: MAC length error: %u not %u for key %u", 248 (u_int)size, (u_int)(dlen + KEY_MAC_LEN), keyno); 249 return FALSE; 250 } 251 return !isc_tsmemcmp(digest, 252 (u_char *)pkt + length + KEY_MAC_LEN, dlen); 253 } 254 255 /* 256 * Calculate the reference id from the address. If it is an IPv4 257 * address, use it as is. If it is an IPv6 address, do a md5 on 258 * it and use the bottom 4 bytes. 259 * The result is in network byte order for IPv4 addreseses. For 260 * IPv6, ntpd long differed in the hash calculated on big-endian 261 * vs. little-endian because the first four bytes of the MD5 hash 262 * were used as a u_int32 without any byte swapping. This broke 263 * the refid-based loop detection between mixed-endian systems. 264 * In order to preserve behavior on the more-common little-endian 265 * systems, the hash is now byte-swapped on big-endian systems to 266 * match the little-endian hash. This is ugly but it seems better 267 * than changing the IPv6 refid calculation on the more-common 268 * systems. 269 * This is not thread safe, not a problem so far. 270 */ 271 u_int32 272 addr2refid(sockaddr_u *addr) 273 { 274 static MD5_CTX md5_ctx; 275 union u_tag { 276 u_char digest[MD5_DIGEST_LENGTH]; 277 u_int32 addr_refid; 278 } u; 279 280 if (IS_IPV4(addr)) { 281 return (NSRCADR(addr)); 282 } 283 /* MD5 is not used for authentication here. */ 284 MD5Init(&md5_ctx); 285 MD5Update(&md5_ctx, (void *)&SOCK_ADDR6(addr), sizeof(SOCK_ADDR6(addr))); 286 MD5Final(u.digest, &md5_ctx); 287 #ifdef WORDS_BIGENDIAN 288 u.addr_refid = BYTESWAP32(u.addr_refid); 289 #endif 290 return u.addr_refid; 291 } 292