aboutsummaryrefslogtreecommitdiff
path: root/bigcrypt.c
blob: 18024dce454323ae6ed4c91306731592f7ad4c2b (plain)
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
/*
 * This function implements the "bigcrypt" algorithm specifically for
 * Linux-PAM.
 *  
 * This algorithm is algorithm 0 (default) shipped with the C2 secure
 * implementation of Digital UNIX.
 * 
 * Disclaimer: This work is not based on the source code to Digital
 * UNIX, nor am I connected to Digital Equipment Corp, in any way
 * other than as a customer. This code is based on published
 * interfaces and reasonable guesswork.
 * 
 * Description: The cleartext is divided into blocks of SEGMENT_SIZE=8
 * characters or less. Each block is encrypted using the standard UNIX
 * libc crypt function. The result of the encryption for one block
 * provides the salt for the suceeding block.
 * 
 * Restrictions: The buffer used to hold the encrypted result is
 * statically allocated. (see MAX_PASS_LEN below).  This is necessary,
 * as the returned pointer points to "static data that are overwritten
 * by each call", (XPG3: XSI System Interface + Headers pg 109), and
 * this is a drop in replacement for crypt();
 *
 * Andy Phillips <atp@mssl.ucl.ac.uk>
 */

#define _XOPEN_SOURCE 700
#include <unistd.h>
#include <string.h>

#include "bigcrypt.h"

/*
 * Max cleartext password length in segments of 8 characters this
 * function can deal with (16 segments of 8 chars= max 128 character
 * password).
 */

#define MAX_SEGMENTS       16
#define SEGMENT_SIZE       8
#define SALT_SIZE          2
#define ESEGMENT_SIZE      11

char *bigcrypt(char const * key, char const * salt) {
	static char outbuf[MAX_SEGMENTS * ESEGMENT_SIZE + SALT_SIZE + 1];	/* static storage area */

	unsigned char n_seg, seg;
	char * outptr;

	/* ensure NUL-termination */
	memset(outbuf, 0, sizeof(outbuf));

	if (strlen(salt) == (SALT_SIZE + ESEGMENT_SIZE)) /* conventional crypt */
		n_seg = 1;
	else if (key[0] == '\0')
		n_seg = 1;
	else
		n_seg = (strnlen(key, MAX_SEGMENTS * SEGMENT_SIZE) + SEGMENT_SIZE - 1) / SEGMENT_SIZE;

	/* first block is special and just traditional crypt() */
	outptr = outbuf;
	strncpy(outptr, crypt(key, salt), SALT_SIZE + ESEGMENT_SIZE);

	for (seg = 1, outptr += SALT_SIZE; seg < n_seg; ++seg) {
		/* subsequent blocks use the previous output block for salt input */
		salt = outptr;
		key += SEGMENT_SIZE;
		outptr += ESEGMENT_SIZE;
		/* and omit the salt on output */
		strncpy(outptr, crypt(key, salt) + SALT_SIZE, ESEGMENT_SIZE);
	}

	return outbuf;
}