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 <sys/types.h> 26 #include <sys/stat.h> 27 #include <sys/mman.h> 28 29 #include <err.h> 30 #include <inttypes.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <unistd.h> 35 36 #include "digest.h" 37 #include "pgpsum.h" 38 39 #ifndef USE_ARG 40 #define USE_ARG(x) /*LINTED*/(void)&(x) 41 #endif 42 43 /* add the ascii armor line endings (except for last line) */ 44 static size_t 45 don_armor(digest_t *hash, uint8_t *in, size_t insize, int doarmor) 46 { 47 uint8_t *from; 48 uint8_t *newp; 49 uint8_t *p; 50 uint8_t dos_line_end[2]; 51 52 dos_line_end[0] = '\r'; 53 dos_line_end[1] = '\n'; 54 for (from = in ; (p = memchr(from, '\n', insize - (size_t)(from - in))) != NULL ; from = p + 1) { 55 for (newp = p ; doarmor == 'w' && newp > from ; --newp) { 56 if (*(newp - 1) != ' ' && *(newp - 1) != '\t') { 57 break; 58 } 59 } 60 digest_update(hash, from, (size_t)(newp - from)); 61 digest_update(hash, dos_line_end, sizeof(dos_line_end)); 62 } 63 digest_update(hash, from, insize - (size_t)(from - in)); 64 return 1; 65 } 66 67 #ifdef NETPGPV_DEBUG 68 /* just for giggles, write what we're about to checksum */ 69 static int 70 writefile(uint8_t *mem, size_t insize) 71 { 72 size_t cc; 73 size_t wc; 74 char template[256]; 75 int fd; 76 77 snprintf(template, sizeof(template), "netpgpvmd.XXXXXX"); 78 if ((fd = mkstemp(template)) < 0) { 79 fprintf(stderr, "can't mkstemp %s\n", template); 80 return 0; 81 } 82 for (cc = 0 ; cc < insize ; cc += wc) { 83 if ((wc = write(fd, &mem[cc], insize - cc)) <= 0) { 84 fprintf(stderr, "short write\n"); 85 break; 86 } 87 } 88 close(fd); 89 return 1; 90 } 91 #endif 92 93 /* return non-zero if this is actually an armored piece already */ 94 static int 95 already_armored(uint8_t *in, size_t insize) 96 { 97 uint8_t *from; 98 uint8_t *p; 99 100 for (from = in ; (p = memchr(from, '\n', insize - (size_t)(from - in))) != NULL ; from = p + 1) { 101 if (*(p - 1) != '\r') { 102 return 0; 103 } 104 } 105 return 1; 106 } 107 108 /* calculate the checksum for the data we have */ 109 static int 110 calcsum(uint8_t *out, size_t size, uint8_t *mem, size_t cc, const uint8_t *hashed, size_t hashsize, int doarmor) 111 { 112 digest_t hash; 113 uint32_t len32; 114 uint16_t len16; 115 uint8_t hashalg; 116 uint8_t trailer[6]; 117 118 USE_ARG(size); 119 /* hashed data is non-null (previously checked) */ 120 hashalg = hashed[3]; 121 memcpy(&len16, &hashed[4], sizeof(len16)); 122 len32 = ntohs(len16) + 6; 123 len32 = htonl(len32); 124 trailer[0] = 0x04; 125 trailer[1] = 0xff; 126 memcpy(&trailer[2], &len32, sizeof(len32)); 127 #ifdef NETPGPV_DEBUG 128 writefile(mem, cc); 129 #endif 130 digest_init(&hash, (const unsigned)hashalg); 131 if (strchr("tw", doarmor) != NULL && !already_armored(mem, cc)) { 132 /* this took me ages to find - something causes gpg to truncate its input */ 133 don_armor(&hash, mem, cc - 1, doarmor); 134 } else { 135 digest_update(&hash, mem, cc); 136 } 137 if (hashed) { 138 digest_update(&hash, hashed, hashsize); 139 } 140 digest_update(&hash, trailer, sizeof(trailer)); 141 return digest_final(out, &hash); 142 } 143 144 /* open the file, mmap it, and then get the checksum on that */ 145 int 146 pgpv_digest_file(uint8_t *data, size_t size, const char *name, const uint8_t *hashed, size_t hashsize, int doarmor) 147 { 148 struct stat st; 149 uint8_t *mem; 150 size_t cc; 151 FILE *fp; 152 int ret; 153 154 if (hashed == NULL || data == NULL || name == NULL) { 155 fprintf(stderr, "no hashed data provided\n"); 156 return 0; 157 } 158 ret = 0; 159 mem = NULL; 160 cc = 0; 161 if ((fp = fopen(name, "r")) == NULL) { 162 warn("%s - not found", name); 163 return 0; 164 } 165 if (fstat(fileno(fp), &st) < 0) { 166 warn("%s - can't stat", name); 167 goto done; 168 } 169 cc = (size_t)(st.st_size); 170 if ((mem = mmap(NULL, cc, PROT_READ, MAP_SHARED, fileno(fp), 0)) == MAP_FAILED) { 171 warn("%s - can't mmap", name); 172 goto done; 173 } 174 ret = calcsum(data, size, mem, cc, hashed, hashsize, doarmor); 175 done: 176 if (data) { 177 munmap(mem, cc); 178 } 179 fclose(fp); 180 return ret; 181 } 182 183 /* calculate the digest over memory too */ 184 int 185 pgpv_digest_memory(uint8_t *data, size_t size, void *mem, size_t cc, const uint8_t *hashed, size_t hashsize, int doarmor) 186 { 187 if (hashed == NULL || data == NULL || mem == NULL) { 188 fprintf(stderr, "no hashed data provided\n"); 189 return 0; 190 } 191 return calcsum(data, size, mem, cc, hashed, hashsize, doarmor); 192 } 193