xref: /plan9/sys/src/cmd/auth/secstore/aescbc.c (revision eed6406f36d695b1749e6b99b0ebfb624403dfc7)
1 /* encrypt file by writing
2 	v2hdr,
3 	16byte initialization vector,
4 	AES-CBC(key, random | file),
5     HMAC_SHA1(md5(key), AES-CBC(random | file))
6 */
7 #include <u.h>
8 #include <libc.h>
9 #include <bio.h>
10 #include <mp.h>
11 #include <libsec.h>
12 #include <authsrv.h>
13 
14 extern char* getpassm(char*);
15 
16 enum{ CHK = 16, BUF = 4096 };
17 
18 uchar v2hdr[AESbsize+1] = "AES CBC SHA1  2\n";
19 Biobuf bin;
20 Biobuf bout;
21 
22 void
safewrite(uchar * buf,int n)23 safewrite(uchar *buf, int n)
24 {
25 	if(Bwrite(&bout, buf, n) != n)
26 		sysfatal("write error");
27 }
28 
29 void
saferead(uchar * buf,int n)30 saferead(uchar *buf, int n)
31 {
32 	if(Bread(&bin, buf, n) != n)
33 		sysfatal("read error");
34 }
35 
36 int
main(int argc,char ** argv)37 main(int argc, char **argv)
38 {
39 	int encrypt = 0;  /* 0=decrypt, 1=encrypt */
40 	int n, nkey, pass_stdin = 0, pass_nvram = 0;
41 	char *pass;
42 	uchar key[AESmaxkey], key2[SHA1dlen];
43 	uchar buf[BUF+SHA1dlen];    /* assumption: CHK <= SHA1dlen */
44 	AESstate aes;
45 	DigestState *dstate;
46 	Nvrsafe nvr;
47 
48 	ARGBEGIN{
49 	case 'e':
50 		encrypt = 1;
51 		break;
52 	case 'i':
53 		pass_stdin = 1;
54 		break;
55 	case 'n':
56 		pass_nvram = 1;
57 		break;
58 	}ARGEND;
59 	if(argc!=0){
60 		fprint(2,"usage: %s -d < cipher.aes > clear.txt\n", argv0);
61 		fprint(2,"   or: %s -e < clear.txt > cipher.aes\n", argv0);
62 		exits("usage");
63 	}
64 	Binit(&bin, 0, OREAD);
65 	Binit(&bout, 1, OWRITE);
66 
67 	if(pass_stdin){
68 		n = readn(3, buf, (sizeof buf)-1);
69 		if(n < 1)
70 			exits("usage: echo password |[3=1] auth/aescbc -i ...");
71 		buf[n] = 0;
72 		while(buf[n-1] == '\n')
73 			buf[--n] = 0;
74 	}else if(pass_nvram){
75 		if(readnvram(&nvr, 0) < 0)
76 			exits("readnvram: %r");
77 		strecpy((char*)buf, (char*)buf+sizeof buf, (char*)nvr.config);
78 		n = strlen((char*)buf);
79 	}else{
80 		pass = getpassm("aescbc key:");
81 		n = strlen(pass);
82 		if(n >= BUF)
83 			exits("key too long");
84 		strcpy((char*)buf, pass);
85 		memset(pass, 0, n);
86 		free(pass);
87 	}
88 	if(n <= 0)
89 		sysfatal("no key");
90 	dstate = sha1((uchar*)"aescbc file", 11, nil, nil);
91 	sha1(buf, n, key2, dstate);
92 	memcpy(key, key2, 16);
93 	nkey = 16;
94 	md5(key, nkey, key2, 0);  /* so even if HMAC_SHA1 is broken, encryption key is protected */
95 
96 	if(encrypt){
97 		safewrite(v2hdr, AESbsize);
98 		genrandom(buf,2*AESbsize); /* CBC is semantically secure if IV is unpredictable. */
99 		setupAESstate(&aes, key, nkey, buf);  /* use first AESbsize bytes as IV */
100 		aesCBCencrypt(buf+AESbsize, AESbsize, &aes);  /* use second AESbsize bytes as initial plaintext */
101 		safewrite(buf, 2*AESbsize);
102 		dstate = hmac_sha1(buf+AESbsize, AESbsize, key2, MD5dlen, 0, 0);
103 		for(;;){
104 			n = Bread(&bin, buf, BUF);
105 			if(n < 0)
106 				sysfatal("read error");
107 			aesCBCencrypt(buf, n, &aes);
108 			safewrite(buf, n);
109 			dstate = hmac_sha1(buf, n, key2, MD5dlen, 0, dstate);
110 			if(n < BUF)
111 				break; /* EOF */
112 		}
113 		hmac_sha1(0, 0, key2, MD5dlen, buf, dstate);
114 		safewrite(buf, SHA1dlen);
115 	}else{ /* decrypt */
116 		saferead(buf, AESbsize);
117 		if(memcmp(buf, v2hdr, AESbsize) == 0){
118 			saferead(buf, 2*AESbsize);  /* read IV and random initial plaintext */
119 			setupAESstate(&aes, key, nkey, buf);
120 			dstate = hmac_sha1(buf+AESbsize, AESbsize, key2, MD5dlen, 0, 0);
121 			aesCBCdecrypt(buf+AESbsize, AESbsize, &aes);
122 			saferead(buf, SHA1dlen);
123 			while((n = Bread(&bin, buf+SHA1dlen, BUF)) > 0){
124 				dstate = hmac_sha1(buf, n, key2, MD5dlen, 0, dstate);
125 				aesCBCdecrypt(buf, n, &aes);
126 				safewrite(buf, n);
127 				memmove(buf, buf+n, SHA1dlen);  /* these bytes are not yet decrypted */
128 			}
129 			hmac_sha1(0, 0, key2, MD5dlen, buf+SHA1dlen, dstate);
130 			if(memcmp(buf, buf+SHA1dlen, SHA1dlen) != 0)
131 				sysfatal("decrypted file failed to authenticate");
132 		}else{ /* compatibility with past mistake */
133 			// if file was encrypted with bad aescbc use this:
134 			//         memset(key, 0, AESmaxkey);
135 			//    else assume we're decrypting secstore files
136 			setupAESstate(&aes, key, AESbsize, buf);
137 			saferead(buf, CHK);
138 			aesCBCdecrypt(buf, CHK, &aes);
139 			while((n = Bread(&bin, buf+CHK, BUF)) > 0){
140 				aesCBCdecrypt(buf+CHK, n, &aes);
141 				safewrite(buf, n);
142 				memmove(buf, buf+n, CHK);
143 			}
144 			if(memcmp(buf, "XXXXXXXXXXXXXXXX", CHK) != 0)
145 				sysfatal("decrypted file failed to authenticate");
146 		}
147 	}
148 	exits("");
149 	return 1;  /* keep  other compilers happy */
150 }
151