1 /*- 2 * Copyright (c) 2012 Alistair Crooks <agc@NetBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 #include "config.h" 26 27 #include <sys/types.h> 28 #include <sys/stat.h> 29 #include <sys/mman.h> 30 31 #include <inttypes.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <unistd.h> 36 37 #include "digest.h" 38 #include "pgpsum.h" 39 40 #ifndef USE_ARG 41 #define USE_ARG(x) /*LINTED*/(void)&(x) 42 #endif 43 44 #undef swap16 45 #undef swap32 46 47 /* ignore any dash-escape at the start of a line */ 48 static void 49 dash_escaped_update(digest_t *hash, uint8_t *in, size_t insize) 50 { 51 if (insize >= 2 && memcmp(in, "- ", 2) == 0) { 52 in += 2; 53 insize -= 2; 54 } 55 digest_update(hash, in, insize); 56 57 } 58 59 /* add the ascii armor line endings (except for last line) */ 60 static size_t 61 don_armor(digest_t *hash, uint8_t *in, size_t insize, int doarmor) 62 { 63 uint8_t *from; 64 uint8_t *newp; 65 uint8_t *p; 66 uint8_t dos_line_end[2]; 67 68 dos_line_end[0] = '\r'; 69 dos_line_end[1] = '\n'; 70 for (from = in ; (p = memchr(from, '\n', insize - (size_t)(from - in))) != NULL ; from = p + 1) { 71 for (newp = p ; doarmor == 'w' && newp > from ; --newp) { 72 if (*(newp - 1) != ' ' && *(newp - 1) != '\t') { 73 break; 74 } 75 } 76 dash_escaped_update(hash, from, (size_t)(newp - from)); 77 digest_update(hash, dos_line_end, sizeof(dos_line_end)); 78 } 79 dash_escaped_update(hash, from, insize - (size_t)(from - in)); 80 return 1; 81 } 82 83 #ifdef NETPGPV_DEBUG 84 /* just for giggles, write what we're about to checksum */ 85 static int 86 writefile(uint8_t *mem, size_t insize) 87 { 88 size_t cc; 89 size_t wc; 90 char template[256]; 91 int fd; 92 93 snprintf(template, sizeof(template), "netpgpvmd.XXXXXX"); 94 if ((fd = mkstemp(template)) < 0) { 95 fprintf(stderr, "can't mkstemp %s\n", template); 96 return 0; 97 } 98 for (cc = 0 ; cc < insize ; cc += wc) { 99 if ((wc = write(fd, &mem[cc], insize - cc)) <= 0) { 100 fprintf(stderr, "short write\n"); 101 break; 102 } 103 } 104 close(fd); 105 return 1; 106 } 107 #endif 108 109 /* return non-zero if this is actually an armored piece already */ 110 static int 111 already_armored(uint8_t *in, size_t insize) 112 { 113 uint8_t *from; 114 uint8_t *p; 115 116 for (from = in ; (p = memchr(from, '\n', insize - (size_t)(from - in))) != NULL ; from = p + 1) { 117 if (*(p - 1) != '\r') { 118 return 0; 119 } 120 } 121 return 1; 122 } 123 124 /* calculate the checksum for the data we have */ 125 static int 126 calcsum(uint8_t *out, size_t size, uint8_t *mem, size_t cc, const uint8_t *hashed, size_t hashsize, int doarmor) 127 { 128 digest_t hash; 129 uint32_t len32; 130 uint16_t len16; 131 uint8_t hashalg; 132 uint8_t trailer[6]; 133 134 USE_ARG(size); 135 /* hashed data is non-null (previously checked) */ 136 hashalg = hashed[3]; 137 memcpy(&len16, &hashed[4], sizeof(len16)); 138 len32 = pgp_ntoh16(len16) + 6; 139 len32 = pgp_hton32(len32); 140 trailer[0] = 0x04; 141 trailer[1] = 0xff; 142 memcpy(&trailer[2], &len32, sizeof(len32)); 143 #ifdef NETPGPV_DEBUG 144 writefile(mem, cc); 145 #endif 146 digest_init(&hash, (const unsigned)hashalg); 147 if (strchr("tw", doarmor) != NULL && !already_armored(mem, cc)) { 148 /* this took me ages to find - something causes gpg to truncate its input */ 149 don_armor(&hash, mem, cc - 1, doarmor); 150 } else { 151 digest_update(&hash, mem, cc); 152 } 153 if (hashed) { 154 digest_update(&hash, hashed, hashsize); 155 } 156 digest_update(&hash, trailer, sizeof(trailer)); 157 return digest_final(out, &hash); 158 } 159 160 /* used to byteswap 16 bit words */ 161 typedef union { 162 uint16_t i16; 163 uint8_t i8[2]; 164 } u16; 165 166 /* used to byte swap 32 bit words */ 167 typedef union { 168 uint32_t i32; 169 uint8_t i8[4]; 170 } u32; 171 172 static inline uint16_t 173 swap16(uint16_t in) 174 { 175 u16 u; 176 177 u.i16 = in; 178 return ((uint16_t)u.i8[0] << 8) | u.i8[1]; 179 } 180 181 static inline uint32_t 182 swap32(uint32_t in) 183 { 184 u32 u; 185 186 u.i32 = in; 187 return ((uint32_t)u.i8[0] << 24) | (u.i8[1] << 16) | (u.i8[2] << 8) | u.i8[3]; 188 } 189 190 static inline int 191 is_little_endian(void) 192 { 193 static const int indian = 1; 194 195 return (*(const char *)(const void *)&indian != 0); 196 } 197 198 /************************************************************/ 199 200 /* exportable routines */ 201 202 /* open the file, mmap it, and then get the checksum on that */ 203 int 204 pgpv_digest_file(uint8_t *data, size_t size, const char *name, const uint8_t *hashed, size_t hashsize, int doarmor) 205 { 206 struct stat st; 207 uint8_t *mem; 208 size_t cc; 209 FILE *fp; 210 int ret; 211 212 if (hashed == NULL || data == NULL || name == NULL) { 213 fprintf(stderr, "no hashed data provided\n"); 214 return 0; 215 } 216 ret = 0; 217 mem = NULL; 218 cc = 0; 219 if ((fp = fopen(name, "r")) == NULL) { 220 fprintf(stderr, "%s - not found", name); 221 return 0; 222 } 223 if (fstat(fileno(fp), &st) < 0) { 224 fprintf(stderr, "%s - can't stat", name); 225 goto done; 226 } 227 cc = (size_t)(st.st_size); 228 if ((mem = mmap(NULL, cc, PROT_READ, MAP_SHARED, fileno(fp), 0)) == MAP_FAILED) { 229 fprintf(stderr, "%s - can't mmap", name); 230 goto done; 231 } 232 ret = calcsum(data, size, mem, cc, hashed, hashsize, doarmor); 233 done: 234 if (data) { 235 munmap(mem, cc); 236 } 237 fclose(fp); 238 return ret; 239 } 240 241 /* calculate the digest over memory too */ 242 int 243 pgpv_digest_memory(uint8_t *data, size_t size, void *mem, size_t cc, const uint8_t *hashed, size_t hashsize, int doarmor) 244 { 245 if (hashed == NULL || data == NULL || mem == NULL) { 246 fprintf(stderr, "no hashed data provided\n"); 247 return 0; 248 } 249 return calcsum(data, size, mem, cc, hashed, hashsize, doarmor); 250 } 251 252 /* our 16bit byte swap if LE host */ 253 uint16_t 254 pgp_ntoh16(uint16_t in) 255 { 256 return (is_little_endian()) ? swap16(in) : in; 257 } 258 259 /* our 16bit byte swap if LE host */ 260 uint16_t 261 pgp_hton16(uint16_t in) 262 { 263 return (is_little_endian()) ? swap16(in) : in; 264 } 265 266 /* our 32bit byte swap if LE host */ 267 uint32_t 268 pgp_ntoh32(uint32_t in) 269 { 270 return (is_little_endian()) ? swap32(in) : in; 271 } 272 273 /* our 32bit byte swap if LE host */ 274 uint32_t 275 pgp_hton32(uint32_t in) 276 { 277 return (is_little_endian()) ? swap32(in) : in; 278 } 279