Browse code

unzip: added traditional PKWARE decryption password verification

Kevin Lin authored on 2015/07/15 06:25:01
Showing 2 changed files
... ...
@@ -53,10 +53,139 @@
53 53
 #define UNZIP_PRIVATE
54 54
 #include "unzip.h"
55 55
 
56
+#define ZIP_CRC32(r,c,b,l)			\
57
+    do {					\
58
+	r = crc32(~c,b,l);			\
59
+	r = ~r;					\
60
+    } while(0)
61
+
62
+
56 63
 static int wrap_inflateinit2(void *a, int b) {
57 64
   return inflateInit2(a, b);
58 65
 }
59 66
 
67
+/* zip update keys, taken from zip specification */
68
+static inline void zupdatekey(uint32_t key[3], unsigned char input)
69
+{
70
+    unsigned char tmp[1];
71
+    unsigned long crctmp;
72
+
73
+    tmp[0] = input;
74
+    ZIP_CRC32(key[0], key[0], tmp, 1);
75
+
76
+    key[1] = key[1] + (key[0] & 0xff);
77
+    key[1] = key[1] * 134775813 + 1;
78
+
79
+    tmp[0] = key[1] >> 24;
80
+    ZIP_CRC32(key[2], key[2], tmp, 1);
81
+}
82
+
83
+/* zip init keys */
84
+static inline void zinitkey(uint32_t key[3], struct cli_pwdict *password)
85
+{
86
+    int i;
87
+
88
+    /* intialize keys, these are specified but the zip specification */
89
+    key[0] = 305419896L;
90
+    key[1] = 591751049L;
91
+    key[2] = 878082192L;
92
+
93
+    /* update keys with password  */
94
+    for (i = 0; i < password->length; i++)
95
+	zupdatekey(key, password->passwd[i]);
96
+}
97
+
98
+/* zip decrypt byte */
99
+static inline unsigned char zdecryptbyte(uint32_t key[3])
100
+{
101
+    unsigned short temp;
102
+    temp = key[2] | 2;
103
+    return ((temp * (temp ^ 1)) >> 8);
104
+}
105
+
106
+/* zip decrypt, CL_EPARSE = could not apply a password, csize includes the decryption header */
107
+/* TODO - search for strong encryption header (0x0017) and handle them */
108
+static inline int zdecrypt(const uint8_t *src, uint32_t csize, uint32_t usize, const uint8_t *lh, unsigned int *fu, cli_ctx *ctx, char *tmpd, zip_cb zcb)
109
+{
110
+    int i, ret, v = 0;
111
+    uint32_t key[3];
112
+    uint8_t eh[12]; /* encryption header buffer */
113
+    struct cli_pwdict *password;
114
+
115
+    if (!ctx || !ctx->engine)
116
+	return CL_ENULLARG;
117
+
118
+    /* TODO - dconf going here */
119
+
120
+    password = ctx->engine->pw_dict;
121
+
122
+    while (password) {
123
+	if ((password->container != CL_TYPE_ANY) && (password->container != CL_TYPE_ZIP)) {
124
+	    if ((password->container > CL_TYPE_ZIP) && (password->container > CL_TYPE_ANY))
125
+		break;
126
+	    password = password->next;
127
+	    continue;
128
+	}
129
+
130
+	zinitkey(key, password);
131
+
132
+	/* decrypting the encryption header */
133
+	memcpy(eh, src, SIZEOF_EH);
134
+
135
+	for (i = 0; i < SIZEOF_EH; i++) {
136
+	    eh[i] ^= zdecryptbyte(key);
137
+	    zupdatekey(key, eh[i]);
138
+	}
139
+
140
+	/* verify that the password is correct */
141
+	if (LH_version > 20) { /* higher than 2.0 */
142
+	    uint16_t a = eh[SIZEOF_EH-1];
143
+
144
+	    if (LH_flags & F_USEDD) {
145
+		cli_dbgmsg("verify(%u): 0x%02x 0x%x (moddate)\n", LH_version, a, LH_mtime);
146
+		if (a == ((LH_mtime >> 8) & 0xff))
147
+		    v = 1;
148
+	    } else {
149
+		cli_dbgmsg("verify(%u): 0x%02x 0x%x (crc32)\n", LH_version, a, LH_crc32);
150
+		if (a == ((LH_crc32 >> 24) & 0xff))
151
+		    v = 1;
152
+	    }
153
+	} else {
154
+	    uint16_t a = eh[SIZEOF_EH-1], b = eh[SIZEOF_EH-2];
155
+
156
+	    if (LH_flags & F_USEDD) {
157
+		cli_dbgmsg("verify(%u): 0x0000%02x%02x 0x%x (moddate)\n", LH_version, a, b, LH_mtime);
158
+		if ((b | (a << 8)) == (LH_mtime & 0xffff))
159
+		    v = 1;
160
+	    } else {
161
+		cli_dbgmsg("verify(%u): 0x0000%02x%02x 0x%x (crc32)\n", LH_version, eh[SIZEOF_EH-1], eh[SIZEOF_EH-2], LH_crc32);
162
+		if ((b | (a << 8)) == ((LH_crc32 >> 16) & 0xffff))
163
+		    v = 1;
164
+	    }
165
+	}
166
+
167
+	if (v) {
168
+	    cli_dbgmsg("zdecrypt: password [%s] matches\n", password->name);
169
+#if 0
170
+	    /* decrypt data to new fmap -> buffer */
171
+	    
172
+	    /* call unz on decrypted output */
173
+	    ret = unz();
174
+
175
+	    /* clean-up and return */
176
+	    return ret;
177
+#else
178
+	    v = 0;
179
+#endif
180
+	}
181
+
182
+	password = password->next;
183
+    }
184
+
185
+    cli_dbgmsg("cli_unzip: lh - skipping encrypted file, no valid passwords\n");
186
+    return CL_SUCCESS;
187
+}
188
+
60 189
 static int unz(const uint8_t *src, uint32_t csize, uint32_t usize, uint16_t method, uint16_t flags, unsigned int *fu, cli_ctx *ctx, char *tmpd, zip_cb zcb) {
61 190
   char name[1024], obuf[BUFSIZ];
62 191
   char *tempfile = name;
... ...
@@ -389,7 +518,8 @@ static unsigned int lhdr(fmap_t *map, uint32_t loff,uint32_t zsize, unsigned int
389 389
 	  return 0;
390 390
       }
391 391
       if(LH_flags & F_ENCR) {
392
-	  cli_dbgmsg("cli_unzip: lh - skipping encrypted file\n");
392
+	  if(fmap_need_ptr_once(map, zip, csize))
393
+	      *ret = zdecrypt(zip, csize, usize, lh, fu, ctx, tmpd, zcb);
393 394
       } else {
394 395
 	  if(fmap_need_ptr_once(map, zip, csize))
395 396
 	      *ret = unz(zip, csize, usize, LH_method, LH_flags, fu, ctx, tmpd, zcb);
... ...
@@ -160,6 +160,8 @@ enum ALGO {
160 160
 #define CH_eattrib	((uint32_t)cli_readint32((uint8_t *)(ch)+38))
161 161
 #define CH_off  	((uint32_t)cli_readint32((uint8_t *)(ch)+42))
162 162
 #define SIZEOF_CH 46
163
+
164
+#define SIZEOF_EH 12
163 165
 #endif /* UNZIP_PRIVATE */
164 166
 
165 167
 #endif /* __UNZIP_H */