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