Cygnus's libkstream, from cryptosrc-us.

This commit is contained in:
thorpej 2000-06-17 06:24:28 +00:00
parent 2a744b7dce
commit a6ab2c35f2
4 changed files with 828 additions and 0 deletions

12
lib/libkstream/Makefile Normal file
View File

@ -0,0 +1,12 @@
# $NetBSD: Makefile,v 1.1.1.1 2000/06/17 06:24:28 thorpej Exp $
LIB= kstream
SRCS= kstream.c kstream-des.c
COPTS+= -g
SHLIB_MAJOR?= 1
SHLIB_MINOR?= 0
.include <bsd.lib.mk>

View File

@ -0,0 +1,339 @@
/* $NetBSD: kstream-des.c,v 1.1.1.1 2000/06/17 06:24:28 thorpej Exp $ */
/* DES-encrypted-stream implementation for MIT Kerberos.
Written by Ken Raeburn (Raeburn@Cygnus.COM), based on algorithms
in the original MIT Kerberos code.
Copyright (C) 1991, 1992 by Cygnus Support.
This file is distributed under the same terms as Kerberos.
For copying and distribution information, please see the file
<kerberosIV/mit-copyright.h>.
from: kstream-des.c,v 1.9 1996/06/02 07:37:27 ghudson Exp $
*/
#include <assert.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <des.h>
#include <kerberosIV/kstream.h>
typedef struct {
union {
long align_me;
double align_me_harder;
Key_schedule sched;
} u;
des_cblock ivec;
} kstream_des_init_block;
typedef struct {
kstream_des_init_block x;
int no_right_justify;
int protect_rlogin_oob;
char *buf1, *buf2;
size_t len1, len2;
} priv;
typedef struct kstream_data_block ksdb;
/* The data stream consists of four bytes representing a net-order
integer, followed by enough data to produce that many
cleartext bytes. This means the size of that data must be rounded
up to a multiple of 8, even though the cleartext may only be one
byte. For blocks of less than eight bytes, most software (well,
exactly half of the two already-existing programs, and all of the
ones we write using this library :-) pads on the *left* with
random values.
Some existing software that we have to be compatible with may send
blocks of data that decrypt to more than 8 bytes, but not an exact
multiple. In that case, the padding is on the right, as would be
considered "normal". This software currently will not generate such
a sequence, but a future version could. Don't break compatibility
with that mode. */
#ifdef sun
static kstream_ptr losing_realloc (old_ptr, new_size)
kstream_ptr old_ptr;
size_t new_size;
{
return old_ptr ? realloc (old_ptr, new_size) : malloc (new_size);
}
#define realloc losing_realloc
#endif
/* Do the actual encryption work. This routine will handle chunks of
any size up to 16 bytes, or any multiple of 8 over that. It makes
the padding a little easier to write it this way. Handling sizes
between 8 and 16 is an annoyance, but rlogin actually relies on being
able to send 12 bytes in one chunk. Bleah! */
static void
do_encrypt (out, inp, p)
ksdb *out;
ksdb *inp;
priv *p;
{
union {
char buf[16];
int junk[16 / sizeof (int)];
} u;
ksdb in;
char *ptr;
static int seeded;
if (!seeded) {
srandom ((int) time ((time_t *) 0));
seeded = 1;
}
in = *inp;
if (in.length < 8)
{
if (! p->no_right_justify)
{
u.junk[0] = random ();
memcpy (u.buf + 8 - in.length, in.ptr, in.length);
}
else
{
u.junk[(sizeof (u.junk[0]) + 7) / sizeof (u.junk[0]) - 1] = random ();
memcpy (u.buf, in.ptr, in.length);
}
in.ptr = u.buf;
in.length = 8;
}
else if (in.length == 8)
{
memcpy (u.buf, in.ptr, 8);
in.ptr = u.buf;
}
else if (in.length < sizeof (u.buf))
{
if (in.length % 8 == 0)
abort ();
u.junk[(sizeof (u.junk) / sizeof (u.junk[0])) - 1] = random ();
memcpy (u.buf, in.ptr, in.length);
in.ptr = u.buf;
}
else if (in.length % 8 != 0)
abort ();
{
unsigned long x;
x = inp->length; /* not in.length! */
ptr = (char *) out->ptr;
ptr[3] = x & 0xff; x >>= 8;
ptr[2] = x & 0xff; x >>= 8;
ptr[1] = x & 0xff; x >>= 8;
ptr[0] = x & 0xff; x >>= 8;
ptr += 4;
if (x)
abort ();
}
des_pcbc_encrypt ((des_cblock *)in.ptr, (des_cblock *)ptr, in.length,
p->x.u.sched, (des_cblock *)p->x.ivec, ENCRYPT);
out->ptr = ptr + ((in.length + 7) & ~7);
}
static int
encrypt (outp, inp, k)
ksdb *outp;
ksdb *inp;
kstream k;
{
const int small_block_size = 16;
priv *p = (priv *) k->data;
if (inp->length > small_block_size && inp->length % 8 != 0)
{
/* do two */
ksdb in, out;
size_t sz;
in.ptr = inp->ptr;
in.length = inp->length & ~7;
sz = in.length + 4; /* first block */
sz += 8 + 4; /* second block */
outp->length = sz;
out.ptr = outp->ptr = p->buf1 = realloc (p->buf1, sz);
out.length = sz;
assert (out.ptr != 0 || out.length == 0);
do_encrypt (&out, &in, p);
in.ptr = (char *) in.ptr + in.length;
in.length = inp->length - in.length;
do_encrypt (&out, &in, p);
return inp->length;
}
else
{
size_t sz = (inp->length + 7) & ~7;
sz += 4;
outp->length = sz;
outp->ptr = p->buf1 = realloc (p->buf1, sz);
assert (outp->ptr != 0 || outp->length == 0);
do_encrypt (outp, inp, p);
outp->ptr = p->buf1;
return inp->length;
}
}
int _kstream_des_debug_OOB = 0;
static int
decrypt (outp, inp, k)
ksdb *outp;
ksdb *inp;
kstream k;
{
char *ptr = inp->ptr;
unsigned long x = 0;
int error_count = 0;
size_t sz;
priv *p = (priv *) k->data;
if(inp->length < 1) return -12; /* make sure we have at least one byte */
if (p->protect_rlogin_oob) {
/* here's where we handle an attack. The first byte ends up being the
highest. If it's not zero, skip it. If it is zero, we can't detect it,
and we still lose... */
x = *ptr & 0xff; /* get the first char */
while (x) {
if(_kstream_des_debug_OOB) fprintf(stderr,"BAD BYTE %02x\n\r", x);
error_count++; /* count the bad byte */
ptr++; /* and skip it */
if(inp->length == error_count) {
return -12; /* we've used up all of the input */
}
x = *ptr & 0xff; /* get the next potentially first char */
}
ptr++;
} else {
x <<= 8; x += *ptr++ & 0xff;
}
/* If we've got four bytes, we can at least determine the correct
amount that still needs to be read. If not, we can assume a
minimum of 12 good bytes (4-byte length plus one 8-byte block)
and we know how many of the ones we've got are bad. */
if (inp->length < 4 + error_count)
return inp->length - error_count - 12;
/* x <<= 8; x += *ptr++ & 0xff; */ /* x already has first byte loaded */
x <<= 8; x += *ptr++ & 0xff;
x <<= 8; x += *ptr++ & 0xff;
x <<= 8; x += *ptr++ & 0xff;
sz = (x + 7) & ~7;
if (inp->length < sz + 4 + error_count)
return - (sz + 4 + error_count - inp->length);
assert (sz <= sizeof (k->in_crypt.data));
if (p->buf1)
p->buf1 = realloc (p->buf1, sz);
else
p->buf1 = malloc (sz);
assert (p->buf1 != 0 || sz == 0);
outp->ptr = p->buf1;
outp->length = x;
pcbc_encrypt ((des_cblock *)ptr, (des_cblock *)outp->ptr, sz, p->x.u.sched,
(des_cblock *)p->x.ivec, DECRYPT);
if (p->no_right_justify == 0
&& x < 8)
outp->ptr = p->buf1 + 8 - x;
return sz + 4 + error_count;
}
static int
init (k, data)
kstream k;
void *data;
{
priv *p;
p = (priv *) malloc (sizeof (priv));
k->data = (kstream_ptr) p;
if (!p)
return errno;
p->buf1 = p->buf2 = 0;
p->len1 = p->len2 = 0;
p->no_right_justify = 0;
p->protect_rlogin_oob = 0;
p->x = * (kstream_des_init_block *) data;
return 0;
}
static int
rcp_init (k, data)
kstream k;
void *data;
{
int x = init (k, data);
((priv *)(k->data))->no_right_justify = 1;
return x;
}
static int
rlogin_init (k, data)
kstream k;
void *data;
{
int x = init (k, data);
((priv *)(k->data))->protect_rlogin_oob = 1;
return x;
}
static void
destroy (k)
kstream k;
{
priv *p = (priv *) k->data;
if (p->buf1)
free (p->buf1);
memset (p, '\\', sizeof (*p)); /* scribble to make sure it's gone */
free (p);
k->data = 0;
}
static const struct kstream_crypt_ctl_block kstream_des_ccb = {
encrypt, decrypt, rlogin_init, destroy
};
static const struct kstream_crypt_ctl_block kstream_des_rcp_ccb = {
encrypt, decrypt, rcp_init, destroy
};
kstream
kstream_create_rlogin_from_fd (fd, P_sched, ivec)
int fd;
kstream_ptr P_sched;
des_cblock (*ivec);
{
Key_schedule *sched = (Key_schedule *) P_sched;
kstream_des_init_block x;
kstream k;
memcpy (&x.u.sched, sched, sizeof (Key_schedule));
memcpy (&x.ivec, ivec, sizeof (des_cblock));
k = kstream_create_from_fd (fd, &kstream_des_ccb, &x);
memset (&x, '\\', sizeof (x));
return k;
}
kstream
kstream_create_rcp_from_fd (fd, P_sched, ivec)
int fd;
kstream_ptr P_sched;
des_cblock (*ivec);
{
Key_schedule *sched = (Key_schedule *) P_sched;
kstream_des_init_block x;
kstream k;
memcpy (&x.u.sched, sched, sizeof (Key_schedule));
memcpy (&x.ivec, ivec, sizeof (des_cblock));
k = kstream_create_from_fd (fd, &kstream_des_rcp_ccb, &x);
memset (&x, '\\', sizeof (x));
return k;
}

377
lib/libkstream/kstream.c Normal file
View File

@ -0,0 +1,377 @@
/* $NetBSD: kstream.c,v 1.1.1.1 2000/06/17 06:24:28 thorpej Exp $ */
/* Encrypted-stream implementation for MIT Kerberos.
Written by Ken Raeburn (Raeburn@Cygnus.COM).
Copyright (C) 1991, 1992 by Cygnus Support.
This file is distributed under the same terms as Kerberos.
For copying and distribution information, please see the file
<kerberosIV/mit-copyright.h>.
from: kstream.c,v 1.7 1996/06/02 07:37:27 ghudson Exp $
*/
/* Current assumptions:
* the encryption/decryption routine may change the size of the data
significantly, in either direction, and need not be consistent
about it. (e.g., a pipe to "compress" could be used.)
* encryption/decryption may not consume all characters passed, but
will consume some, or will return a negative number indicating how
many more characters should be read before it will be able to do
anything with the data. (A return value of -1 is always safe; so
is calling the crypt routine again without enough data.)
* the file descriptor used is not set to non-blocking.
* no other routines will do i/o on that file descriptor while this
library is using it. (out-of-band messages are probably okay.)
Bugs:
* if the encryption package cannot handle arbitrarily small data,
flushing the outgoing stream may not work.
ToDo: Lots.
* completion of the code...
* begin testing
* buffering (efficiency)
* reduce data copying for large buffers that get sent immediately
* error handling
* consistency in error reporting conventions
*/
#include <assert.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
/* Only use alloca if we've got gcc 2 or better */
#ifdef __GNUC__
#if __GNUC__ >= 2
#define alloca __builtin_alloca
#define HAS_ALLOCA
#endif
#endif
#include <kerberosIV/kstream.h>
#ifdef __STDC__
int krb_net_write (int, char *, int);
int krb_net_read (int, char *, int);
#else
int krb_net_write ();
int krb_net_read ();
void abort ();
char *memmove ();
void *memcpy ();
char *malloc ();
char *realloc ();
void free ();
#endif
extern int errno;
#ifdef sun
#ifndef solaris20
/* SunOS has no memmove, but bcopy overlaps correctly */
#define memmove(dest,src,size) bcopy(src,dest,size)
#endif
#endif
static void fifo__init (this)
fifo *this;
{
this->next_write = this->next_read = 0;
memset (this->data, 0, sizeof (this->data));
}
static char *fifo__data_start (this)
fifo *this;
{
return this->data + this->next_read;
}
static size_t fifo__bytes_available (this)
fifo *this;
{
return this->next_write - this->next_read;
}
static size_t fifo__space_available (this)
fifo *this;
{
return sizeof (this->data) - fifo__bytes_available (this);
}
static int fifo__append (this, ptr, len)
fifo *this;
const char *ptr;
size_t len;
{
if (len > fifo__space_available (this))
len = fifo__space_available (this);
if (sizeof (this->data) - this->next_write < len)
{
memmove (this->data, this->data + this->next_read,
this->next_write - this->next_read);
this->next_write -= this->next_read;
this->next_read = 0;
}
memcpy (this->data + this->next_write, ptr, len);
this->next_write += len;
return len;
}
static int fifo__extract (this, ptr, len)
fifo *this;
char *ptr;
size_t len;
{
size_t n = fifo__bytes_available (this);
if (len > n)
len = n;
if (ptr)
memcpy (ptr, this->data + this->next_read, len);
this->next_read += len;
if (this->next_read == this->next_write)
this->next_read = this->next_write = 0;
return len;
}
static void kstream_rec__init (this)
kstream_rec *this;
{
fifo__init (&this->in_crypt);
fifo__init (&this->in_clear);
fifo__init (&this->out_clear);
}
kstream
kstream_create_from_fd (fd, ctl, data)
int fd;
const struct kstream_crypt_ctl_block *ctl;
void *data;
{
kstream k;
k = (kstream) malloc (sizeof (kstream_rec));
if (!k)
return 0;
kstream_rec__init (k);
k->ctl = ctl;
k->data = 0;
k->fd = fd;
k->buffering = 1; /* why not? */
if (ctl && ctl->init && (ctl->init) (k, data) != 0)
{
free (k);
return 0;
}
return k;
}
int
kstream_destroy (k)
kstream k;
{
int x = kstream_flush (k);
if (k->ctl && k->ctl->destroy)
(k->ctl->destroy) (k);
free (k);
return x;
}
void
kstream_set_buffer_mode (k, mode)
kstream k;
int mode;
{
k->buffering = mode;
}
int
kstream_write (k, p_data, p_len)
kstream k;
kstream_ptr p_data;
size_t p_len;
{
size_t len = p_len;
char *data = p_data;
int x = 0;
fifo *out = &k->out_clear;
assert (k != 0);
while (len)
{
x = fifo__append (out, data, len);
assert (x >= 0);
data += x;
len -= x;
if (len == 0 && k->buffering != 0)
return p_len;
x = kstream_flush (k);
if (x < 0)
return x;
}
return p_len;
}
int
kstream_flush (k)
kstream k;
{
int x, n;
fifo *out = &k->out_clear;
struct kstream_data_block kd_out, kd_in;
assert (k != 0);
if (k->ctl == 0)
{
int n = fifo__bytes_available (out);
x = krb_net_write (k->fd, fifo__data_start (out), n);
if (x < 0)
return x;
else if (x != n)
abort ();
fifo__extract (out, 0, n);
return 0;
}
n = fifo__bytes_available (out);
kd_in.length = n;
kd_in.ptr = fifo__data_start (out);
kd_out.ptr = 0;
kd_out.length = 0;
while (fifo__bytes_available (out))
{
x = (k->ctl->encrypt) (&kd_out, &kd_in, k);
if (x < 0)
return x;
else if (x == 0)
return -1;
/* x is number of input characters processed */
fifo__extract (out, 0, x);
x = krb_net_write (k->fd, kd_out.ptr, kd_out.length);
if (x < 0)
return x;
else if (x != kd_out.length)
abort ();
}
return 0;
}
int
kstream_read (k, p_data, p_len)
kstream k;
kstream_ptr p_data;
size_t p_len;
{
char *data = p_data;
size_t len = p_len;
int n;
fifo *in = &k->in_clear, *crypt;
struct kstream_data_block kd_out, kd_in;
assert (k != 0);
read_clear:
if (k->ctl == 0)
return read (k->fd, data, len);
if (fifo__bytes_available (in) > 0)
return fifo__extract (in, data, len);
crypt = &k->in_crypt;
try_2:
kd_out.ptr = 0;
kd_out.length = 0;
kd_in.length = fifo__bytes_available (crypt);
kd_in.ptr = fifo__data_start (crypt);
if (kd_in.length == 0)
{
n = -1;
goto read_source;
}
n = (k->ctl->decrypt) (&kd_out, &kd_in, k);
if (n > 0)
{
/* Succeeded in decrypting some data. */
fifo__extract (crypt, 0, n);
{
int n2;
n = kd_out.length;
n2 = fifo__append (in, kd_out.ptr, kd_out.length);
assert (n == n2);
}
goto read_clear;
}
else if (n == 0)
assert (n != 0); /* not handling errors yet */
read_source:
{
size_t sz;
static char *buf;
sz = -n;
if (sz > fifo__space_available (crypt))
{
#ifdef DEBUG
fprintf (stderr, "insufficient space avail (%d, want %d)\n",
fifo__space_available (crypt), sz);
#endif
errno = ENOMEM;
return -1;
}
#ifdef HAS_ALLOCA
buf = alloca (sz);
#else
if (buf)
buf = realloc (buf, sz);
else
buf = malloc (sz);
assert(buf);
#endif
while (sz > 0)
{
int n2;
n2 = krb_net_read (k->fd, buf, sz);
if (n2 < 0)
{
#ifdef DEBUG
perror ("kstream_read: krb_net_read");
#endif
return n2;
}
else if (n2 == 0)
return 0;
fifo__append (crypt, buf, n2);
sz -= n2;
}
goto try_2;
}
}
#if 0
extern "C" {
/* simple rotate */
typedef struct kstream_data_block DB;
static int
move (DB *out, DB *in, kstream k, int direction)
{
int i, x;
static char *ptr;
char *inp = in->ptr;
if (in->length == 0) return -1;
/* Sun's realloc loses on null pointers. */
ptr = ptr ? realloc (ptr, in->length) : malloc (in->length);
out->ptr = ptr;
if (!ptr) return 0;
out->length = in->length;
x = direction ? +1 : -1;
for (i = in->length; i-- > 0; )
ptr[i] = inp[i] + x;
return in->length;
}
static int emove (DB *out, DB *in, kstream k) { return move (out, in, k, 0); }
static int dmove (DB *out, DB *in, kstream k) { return move (out, in, k, 1); }
extern const struct kstream_crypt_ctl_block rot1_ccb = {
emove, dmove, 0, 0
};
}
#endif

100
lib/libkstream/kstream.h Normal file
View File

@ -0,0 +1,100 @@
/* Header file for encrypted-stream library.
Written by Ken Raeburn (Raeburn@Cygnus.COM).
Copyright (C) 1991, 1992 by Cygnus Support.
This file is distributed under the same terms as Kerberos.
For copying and distribution information, please see the file
<mit-copyright.h>.
$NetBSD: kstream.h,v 1.1.1.1 2000/06/17 06:24:28 thorpej Exp $
*/
#include <sys/types.h> /* for size_t */
/* Each stream is set up for two-way communication; if buffering is
requested, output will be flushed before input is read, or when
kstream_flush is called. */
#if defined (__STDC__) || defined (__cplusplus)
typedef void *kstream_ptr;
#else
typedef char *kstream_ptr;
#endif
typedef struct kstream_rec *kstream;
struct kstream_data_block {
kstream_ptr ptr;
size_t length;
};
struct kstream_crypt_ctl_block {
/* Don't rely on anything in this structure.
It is almost guaranteed to change.
Right now, it's just a hack so we can bang out the interface
in some form that lets us run both rcp and rlogin. This is also
the only reason the contents of this structure are public. */
#if defined (__STDC__) || defined (__cplusplus)
int (*encrypt) (struct kstream_data_block *, /* output -- written */
struct kstream_data_block*, /* input */
kstream str
); /* ret val = # input bytes used */
int (*decrypt) (struct kstream_data_block *, /* output -- written */
struct kstream_data_block*, /* input */
kstream str
); /* ret val = # input bytes used */
int (*init) (kstream str, kstream_ptr data);
void (*destroy) (kstream str);
#else
int (*encrypt) (), (*decrypt) (), (*init) ();
void (*destroy) ();
#endif
};
/* ctl==0 means no encryption. data is specific to crypt functions */
#if defined (__STDC__) || defined (__cplusplus)
kstream kstream_create_from_fd (int fd,
const struct kstream_crypt_ctl_block *ctl,
kstream_ptr data);
/* There should be a "standard" DES mode used here somewhere.
These differ, and I haven't chosen one over the other (yet). */
kstream kstream_create_rlogin_from_fd (int fd, void* sched,
unsigned char (*ivec)[8]);
kstream kstream_create_rcp_from_fd (int fd, void* sched,
unsigned char (*ivec)[8]);
int kstream_write (kstream, void*, size_t);
int kstream_read (kstream, void*, size_t);
int kstream_flush (kstream);
int kstream_destroy (kstream);
void kstream_set_buffer_mode (kstream, int);
#else
kstream kstream_create_from_fd (),
kstream_create_rlogin_from_fd (),
kstream_create_rcp_from_fd ();
void kstream_set_buffer_mode ();
#endif
#if 0 /* Perhaps someday... */
kstream kstream_create (principal, host, port, ...);
#endif
#if !defined (__STDC__) && !defined (__cplusplus) && !defined (const)
#define const /* empty */
#endif
typedef struct fifo {
char data[10*1024];
size_t next_write, next_read;
} fifo;
typedef struct kstream_rec {
const struct kstream_crypt_ctl_block *ctl;
int fd;
int buffering : 2;
kstream_ptr data;
/* These should be made pointers as soon as code has been
written to reallocate them. Also, it would be more efficient
to use pointers into the buffers, rather than continually shifting
them down so unprocessed data starts at index 0. */
/* incoming */
fifo in_crypt, in_clear;
/* outgoing */
fifo out_clear;
} kstream_rec;