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 /* add the ascii armor line endings (except for last line) */ 48 static size_t 49 don_armor(digest_t *hash, uint8_t *in, size_t insize, int doarmor) 50 { 51 uint8_t *from; 52 uint8_t *newp; 53 uint8_t *p; 54 uint8_t dos_line_end[2]; 55 56 dos_line_end[0] = '\r'; 57 dos_line_end[1] = '\n'; 58 for (from = in ; (p = memchr(from, '\n', insize - (size_t)(from - in))) != NULL ; from = p + 1) { 59 for (newp = p ; doarmor == 'w' && newp > from ; --newp) { 60 if (*(newp - 1) != ' ' && *(newp - 1) != '\t') { 61 break; 62 } 63 } 64 digest_update(hash, from, (size_t)(newp - from)); 65 digest_update(hash, dos_line_end, sizeof(dos_line_end)); 66 } 67 digest_update(hash, from, insize - (size_t)(from - in)); 68 return 1; 69 } 70 71 #ifdef NETPGPV_DEBUG 72 /* just for giggles, write what we're about to checksum */ 73 static int 74 writefile(uint8_t *mem, size_t insize) 75 { 76 size_t cc; 77 size_t wc; 78 char template[256]; 79 int fd; 80 81 snprintf(template, sizeof(template), "netpgpvmd.XXXXXX"); 82 if ((fd = mkstemp(template)) < 0) { 83 fprintf(stderr, "can't mkstemp %s\n", template); 84 return 0; 85 } 86 for (cc = 0 ; cc < insize ; cc += wc) { 87 if ((wc = write(fd, &mem[cc], insize - cc)) <= 0) { 88 fprintf(stderr, "short write\n"); 89 break; 90 } 91 } 92 close(fd); 93 return 1; 94 } 95 #endif 96 97 /* return non-zero if this is actually an armored piece already */ 98 static int 99 already_armored(uint8_t *in, size_t insize) 100 { 101 uint8_t *from; 102 uint8_t *p; 103 104 for (from = in ; (p = memchr(from, '\n', insize - (size_t)(from - in))) != NULL ; from = p + 1) { 105 if (*(p - 1) != '\r') { 106 return 0; 107 } 108 } 109 return 1; 110 } 111 112 /* calculate the checksum for the data we have */ 113 static int 114 calcsum(uint8_t *out, size_t size, uint8_t *mem, size_t cc, const uint8_t *hashed, size_t hashsize, int doarmor) 115 { 116 digest_t hash; 117 uint32_t len32; 118 uint16_t len16; 119 uint8_t hashalg; 120 uint8_t trailer[6]; 121 122 USE_ARG(size); 123 /* hashed data is non-null (previously checked) */ 124 hashalg = hashed[3]; 125 memcpy(&len16, &hashed[4], sizeof(len16)); 126 len32 = pgp_ntoh16(len16) + 6; 127 len32 = pgp_hton32(len32); 128 trailer[0] = 0x04; 129 trailer[1] = 0xff; 130 memcpy(&trailer[2], &len32, sizeof(len32)); 131 #ifdef NETPGPV_DEBUG 132 writefile(mem, cc); 133 #endif 134 digest_init(&hash, (const unsigned)hashalg); 135 if (strchr("tw", doarmor) != NULL && !already_armored(mem, cc)) { 136 /* this took me ages to find - something causes gpg to truncate its input */ 137 don_armor(&hash, mem, cc - 1, doarmor); 138 } else { 139 digest_update(&hash, mem, cc); 140 } 141 if (hashed) { 142 digest_update(&hash, hashed, hashsize); 143 } 144 digest_update(&hash, trailer, sizeof(trailer)); 145 return digest_final(out, &hash); 146 } 147 148 /* used to byteswap 16 bit words */ 149 typedef union { 150 uint16_t i16; 151 uint8_t i8[2]; 152 } u16; 153 154 /* used to byte swap 32 bit words */ 155 typedef union { 156 uint32_t i32; 157 uint8_t i8[4]; 158 } u32; 159 160 static inline uint16_t 161 swap16(uint16_t in) 162 { 163 u16 u; 164 165 u.i16 = in; 166 return (u.i8[0] << 8) | u.i8[1]; 167 } 168 169 static inline uint32_t 170 swap32(uint32_t in) 171 { 172 u32 u; 173 174 u.i32 = in; 175 return (u.i8[0] << 24) | (u.i8[1] << 16) | (u.i8[2] << 8) | u.i8[3]; 176 } 177 178 static inline int 179 is_little_endian(void) 180 { 181 static const int indian = 1; 182 183 return (*(const char *)(const void *)&indian != 0); 184 } 185 186 /************************************************************/ 187 188 /* exportable routines */ 189 190 /* open the file, mmap it, and then get the checksum on that */ 191 int 192 pgpv_digest_file(uint8_t *data, size_t size, const char *name, const uint8_t *hashed, size_t hashsize, int doarmor) 193 { 194 struct stat st; 195 uint8_t *mem; 196 size_t cc; 197 FILE *fp; 198 int ret; 199 200 if (hashed == NULL || data == NULL || name == NULL) { 201 fprintf(stderr, "no hashed data provided\n"); 202 return 0; 203 } 204 ret = 0; 205 mem = NULL; 206 cc = 0; 207 if ((fp = fopen(name, "r")) == NULL) { 208 fprintf(stderr, "%s - not found", name); 209 return 0; 210 } 211 if (fstat(fileno(fp), &st) < 0) { 212 fprintf(stderr, "%s - can't stat", name); 213 goto done; 214 } 215 cc = (size_t)(st.st_size); 216 if ((mem = mmap(NULL, cc, PROT_READ, MAP_SHARED, fileno(fp), 0)) == MAP_FAILED) { 217 fprintf(stderr, "%s - can't mmap", name); 218 goto done; 219 } 220 ret = calcsum(data, size, mem, cc, hashed, hashsize, doarmor); 221 done: 222 if (data) { 223 munmap(mem, cc); 224 } 225 fclose(fp); 226 return ret; 227 } 228 229 /* calculate the digest over memory too */ 230 int 231 pgpv_digest_memory(uint8_t *data, size_t size, void *mem, size_t cc, const uint8_t *hashed, size_t hashsize, int doarmor) 232 { 233 if (hashed == NULL || data == NULL || mem == NULL) { 234 fprintf(stderr, "no hashed data provided\n"); 235 return 0; 236 } 237 return calcsum(data, size, mem, cc, hashed, hashsize, doarmor); 238 } 239 240 /* our 16bit byte swap if LE host */ 241 uint16_t 242 pgp_ntoh16(uint16_t in) 243 { 244 return (is_little_endian()) ? swap16(in) : in; 245 } 246 247 /* our 16bit byte swap if LE host */ 248 uint16_t 249 pgp_hton16(uint16_t in) 250 { 251 return (is_little_endian()) ? swap16(in) : in; 252 } 253 254 /* our 32bit byte swap if LE host */ 255 uint32_t 256 pgp_ntoh32(uint32_t in) 257 { 258 return (is_little_endian()) ? swap32(in) : in; 259 } 260 261 /* our 32bit byte swap if LE host */ 262 uint32_t 263 pgp_hton32(uint32_t in) 264 { 265 return (is_little_endian()) ? swap32(in) : in; 266 } 267