File: cipher.c

package info (click to toggle)
dump 0.4b41-3
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 1,416 kB
  • ctags: 1,987
  • sloc: ansic: 17,610; sh: 3,185; makefile: 210; sed: 5
file content (131 lines) | stat: -rw-r--r-- 3,503 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <syslog.h>
#include <strings.h>
#include <errno.h>
#include <openssl/evp.h>

/*
 * Encrypt or decrypt buf, returning a pointer to the transformed data,
 * or NULL on error.
 * The returned data is the same size as the input data,
 * which means we must turn off cipher padding, and require that buflen
 * be a multiple of the cipher block size (8 for Blowfish).
 * To keep things simple, the return value is a malloc'd
 * buffer that is overwritten on each call.
 *
 * Ken Lalonde <ken@globalremit.com>, 2003
 */
char *
cipher(char *buf, int buflen, int do_encrypt)
{
	static EVP_CIPHER_CTX ctx;
	static char *out = NULL;	/* return value, grown as necessary */
	static int outlen = 0;
	static int init = 0, which, blocksize;
	int n;

	if (!init) {
		static const EVP_CIPHER *cipher;
		unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH];
		// Read key from $HOME/.ermt.key
		char *keyfile = ".ermt.key";
		unsigned char buf[128];
		FILE *fp;
		int i;

		openlog("ermt", LOG_PID, LOG_DAEMON);
		// Not needed: OpenSSL_add_all_algorithms();
		// Not needed: ERR_load_crypto_strings();
		cipher = EVP_bf_cbc();	// or: EVP_aes_{128,192,256}_cbc()
		// We want the ability to decrypt the output
		// using "openssl enc -d -kfile K -nopad -nosalt", which
		// means we must read the key file the same way
		// openssl does.  But be careful: if the key contains
		// \0 or \r or \n, the effective size will be reduced.
		if ((fp = fopen(keyfile, "r")) == NULL) {
			syslog(LOG_ERR, "Can't open key file %s: %m", keyfile);
			return NULL;
		}
		buf[0] = '\0';
		fgets(buf, sizeof buf, fp);
		fclose(fp);
		i = strlen(buf);
		if ((i > 0) &&
			((buf[i-1] == '\n') || (buf[i-1] == '\r')))
			buf[--i]='\0';
		if ((i > 0) &&
			((buf[i-1] == '\n') || (buf[i-1] == '\r')))
			buf[--i]='\0';
		if (i < 1) {
			syslog(LOG_ERR, "zero length key");
			errno = EINVAL;
			return NULL;
		}
		EVP_BytesToKey(cipher, EVP_md5(), NULL,
			buf, strlen(buf), 1, key, iv);
		EVP_CIPHER_CTX_init(&ctx);
		EVP_CipherInit_ex(&ctx, cipher, NULL, key, iv, do_encrypt);
		EVP_CIPHER_CTX_set_padding(&ctx, 0);	// -nopad
		OPENSSL_cleanse(buf, sizeof buf);
		OPENSSL_cleanse(key, sizeof key);
		OPENSSL_cleanse(iv, sizeof iv);
		blocksize = EVP_CIPHER_CTX_block_size(&ctx);
		which = do_encrypt;
		init = 1;
	}
	if (which != do_encrypt) {
		syslog(LOG_ERR, "Cannot switch modes");
		errno = EINVAL;
		return NULL;
	}
	if ((buflen % blocksize) != 0) {
		syslog(LOG_ERR, "Buffer size is not a multiple of cipher block size");
		errno = EINVAL;
		return NULL;
	}
	if (outlen < buflen+blocksize) {
		outlen = (buflen+blocksize) * 2;
		out = realloc(out, outlen);
	}
	if (!EVP_CipherUpdate(&ctx, out, &n, buf, buflen)) {
		syslog(LOG_ERR, "EVP_CipherUpdate failed");
		errno = EINVAL;
		return NULL;
	}
	if (n != buflen) {
		syslog(LOG_ERR, "EVP_CipherUpdate: %d != %d", n, buflen);
		errno = EINVAL;
		return NULL;
	}
	// assert(ctx->buf_len == 0);
	return out;
}

/* Decrypt stdin to stdout, and exit */
int
decrypt()
{
	char buf[8*1024], *cp;
	int n;

	while ((n = fread(buf, 1, sizeof buf, stdin)) > 0) {
		if ((cp = cipher(buf, n, 0)) == NULL) {
			fprintf(stderr, "ermt: Error decoding input; see daemon.err syslog\n");
			exit(1);
		}
		fwrite(cp, 1, n, stdout);
	}
	if (ferror(stdin)) {
		perror("ermt: stdin: read");
		exit(1);
	}
	if (fflush(stdout)) {
		perror("ermt: stdout: write");
		exit(1);
	}
	exit(0);
}