#include <stdio.h>
#include <errno.h>
#include "cryptlib.h"
#include <openssl/buffer.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
static int ok_write(BIO *h,char *buf,int num);
static int ok_read(BIO *h,char *buf,int size);
static long ok_ctrl(BIO *h,int cmd,long arg1,char *arg2);
static int ok_new(BIO *h);
static int ok_free(BIO *data);
static long ok_callback_ctrl(BIO *h,int cmd,void (*fp)());
static void sig_out(BIO* b);
static void sig_in(BIO* b);
static void block_out(BIO* b);
static void block_in(BIO* b);
#define OK_BLOCK_SIZE (1024*4)
#define OK_BLOCK_BLOCK 4
#define IOBS (OK_BLOCK_SIZE+ OK_BLOCK_BLOCK+ 3*EVP_MAX_MD_SIZE)
#define WELLKNOWN "The quick brown fox jumped over the lazy dog's back."
#ifndef L_ENDIAN
#define swapem(x) \
((unsigned long int)((((unsigned long int)(x) & 0x000000ffU) << 24) | \
(((unsigned long int)(x) & 0x0000ff00U) << 8) | \
(((unsigned long int)(x) & 0x00ff0000U) >> 8) | \
(((unsigned long int)(x) & 0xff000000U) >> 24)))
#else
#define swapem(x) (x)
#endif
typedef struct ok_struct
{
int buf_len;
int buf_off;
int buf_len_save;
int buf_off_save;
int cont;
int finished;
EVP_MD_CTX md;
int blockout;
int sigio;
char buf[IOBS];
} BIO_OK_CTX;
static BIO_METHOD methods_ok=
{
BIO_TYPE_CIPHER,"reliable",
ok_write,
ok_read,
NULL,
NULL,
ok_ctrl,
ok_new,
ok_free,
ok_callback_ctrl,
};
BIO_METHOD *BIO_f_reliable(void)
{
return(&methods_ok);
}
static int ok_new(BIO *bi)
{
BIO_OK_CTX *ctx;
ctx=(BIO_OK_CTX *)Malloc(sizeof(BIO_OK_CTX));
if (ctx == NULL) return(0);
ctx->buf_len=0;
ctx->buf_off=0;
ctx->buf_len_save=0;
ctx->buf_off_save=0;
ctx->cont=1;
ctx->finished=0;
ctx->blockout= 0;
ctx->sigio=1;
bi->init=0;
bi->ptr=(char *)ctx;
bi->flags=0;
return(1);
}
static int ok_free(BIO *a)
{
if (a == NULL) return(0);
memset(a->ptr,0,sizeof(BIO_OK_CTX));
Free(a->ptr);
a->ptr=NULL;
a->init=0;
a->flags=0;
return(1);
}
static int ok_read(BIO *b, char *out, int outl)
{
int ret=0,i,n;
BIO_OK_CTX *ctx;
if (out == NULL) return(0);
ctx=(BIO_OK_CTX *)b->ptr;
if ((ctx == NULL) || (b->next_bio == NULL) || (b->init == 0)) return(0);
while(outl > 0)
{
if (ctx->blockout)
{
i=ctx->buf_len-ctx->buf_off;
if (i > outl) i=outl;
memcpy(out,&(ctx->buf[ctx->buf_off]),i);
ret+=i;
out+=i;
outl-=i;
ctx->buf_off+=i;
if (ctx->buf_len == ctx->buf_off)
{
ctx->buf_off=0;
if(ctx->buf_len_save- ctx->buf_off_save > 0)
{
ctx->buf_len= ctx->buf_len_save- ctx->buf_off_save;
memmove(ctx->buf, &(ctx->buf[ctx->buf_off_save]),
ctx->buf_len);
}
else
{
ctx->buf_len=0;
}
ctx->blockout= 0;
}
}
if (outl == 0) break;
n=IOBS- ctx->buf_len;
i=BIO_read(b->next_bio,&(ctx->buf[ctx->buf_len]),n);
if (i <= 0) break;
ctx->buf_len+= i;
if (ctx->sigio == 1) sig_in(b);
if (ctx->sigio == 0) block_in(b);
if (ctx->cont <= 0) break;
}
BIO_clear_retry_flags(b);
BIO_copy_next_retry(b);
return(ret);
}
static int ok_write(BIO *b, char *in, int inl)
{
int ret=0,n,i;
BIO_OK_CTX *ctx;
ctx=(BIO_OK_CTX *)b->ptr;
ret=inl;
if ((ctx == NULL) || (b->next_bio == NULL) || (b->init == 0)) return(0);
if(ctx->sigio) sig_out(b);
do{
BIO_clear_retry_flags(b);
n=ctx->buf_len-ctx->buf_off;
while (ctx->blockout && n > 0)
{
i=BIO_write(b->next_bio,&(ctx->buf[ctx->buf_off]),n);
if (i <= 0)
{
BIO_copy_next_retry(b);
if(!BIO_should_retry(b))
ctx->cont= 0;
return(i);
}
ctx->buf_off+=i;
n-=i;
}
ctx->blockout= 0;
if (ctx->buf_len == ctx->buf_off)
{
ctx->buf_len=OK_BLOCK_BLOCK;
ctx->buf_off=0;
}
if ((in == NULL) || (inl <= 0)) return(0);
n= (inl+ ctx->buf_len > OK_BLOCK_SIZE+ OK_BLOCK_BLOCK) ?
OK_BLOCK_SIZE+ OK_BLOCK_BLOCK- ctx->buf_len : inl;
memcpy((unsigned char *)(&(ctx->buf[ctx->buf_len])),(unsigned char *)in,n);
ctx->buf_len+= n;
inl-=n;
in+=n;
if(ctx->buf_len >= OK_BLOCK_SIZE+ OK_BLOCK_BLOCK)
{
block_out(b);
}
}while(inl > 0);
BIO_clear_retry_flags(b);
BIO_copy_next_retry(b);
return(ret);
}
static long ok_ctrl(BIO *b, int cmd, long num, char *ptr)
{
BIO_OK_CTX *ctx;
EVP_MD *md;
const EVP_MD **ppmd;
long ret=1;
int i;
ctx=(BIO_OK_CTX *)b->ptr;
switch (cmd)
{
case BIO_CTRL_RESET:
ctx->buf_len=0;
ctx->buf_off=0;
ctx->buf_len_save=0;
ctx->buf_off_save=0;
ctx->cont=1;
ctx->finished=0;
ctx->blockout= 0;
ctx->sigio=1;
ret=BIO_ctrl(b->next_bio,cmd,num,ptr);
break;
case BIO_CTRL_EOF:
if (ctx->cont <= 0)
ret=1;
else
ret=BIO_ctrl(b->next_bio,cmd,num,ptr);
break;
case BIO_CTRL_PENDING:
case BIO_CTRL_WPENDING:
ret=ctx->blockout ? ctx->buf_len-ctx->buf_off : 0;
if (ret <= 0)
ret=BIO_ctrl(b->next_bio,cmd,num,ptr);
break;
case BIO_CTRL_FLUSH:
if(ctx->blockout == 0)
block_out(b);
while (ctx->blockout)
{
i=ok_write(b,NULL,0);
if (i < 0)
{
ret=i;
break;
}
}
ctx->finished=1;
ctx->buf_off=ctx->buf_len=0;
ctx->cont=(int)ret;
ret=BIO_ctrl(b->next_bio,cmd,num,ptr);
break;
case BIO_C_DO_STATE_MACHINE:
BIO_clear_retry_flags(b);
ret=BIO_ctrl(b->next_bio,cmd,num,ptr);
BIO_copy_next_retry(b);
break;
case BIO_CTRL_INFO:
ret=(long)ctx->cont;
break;
case BIO_C_SET_MD:
md=(EVP_MD *)ptr;
EVP_DigestInit(&(ctx->md),md);
b->init=1;
break;
case BIO_C_GET_MD:
if (b->init)
{
ppmd=(const EVP_MD **)ptr;
*ppmd=ctx->md.digest;
}
else
ret=0;
break;
default:
ret=BIO_ctrl(b->next_bio,cmd,num,ptr);
break;
}
return(ret);
}
static long ok_callback_ctrl(BIO *b, int cmd, void (*fp)())
{
long ret=1;
if (b->next_bio == NULL) return(0);
switch (cmd)
{
default:
ret=BIO_callback_ctrl(b->next_bio,cmd,fp);
break;
}
return(ret);
}
static void longswap(void *_ptr, int len)
{
#ifndef L_ENDIAN
int i;
char *ptr=_ptr;
for(i= 0;i < len;i+= 4){
*((unsigned long *)&(ptr[i]))= swapem(*((unsigned long *)&(ptr[i])));
}
#endif
}
static void sig_out(BIO* b)
{
BIO_OK_CTX *ctx;
EVP_MD_CTX *md;
ctx=(BIO_OK_CTX *)b->ptr;
md= &(ctx->md);
if(ctx->buf_len+ 2* md->digest->md_size > OK_BLOCK_SIZE) return;
EVP_DigestInit(md, md->digest);
RAND_pseudo_bytes(&(md->md.base[0]), md->digest->md_size);
memcpy(&(ctx->buf[ctx->buf_len]), &(md->md.base[0]), md->digest->md_size);
longswap(&(ctx->buf[ctx->buf_len]), md->digest->md_size);
ctx->buf_len+= md->digest->md_size;
EVP_DigestUpdate(md, WELLKNOWN, strlen(WELLKNOWN));
md->digest->final(&(ctx->buf[ctx->buf_len]), &(md->md.base[0]));
ctx->buf_len+= md->digest->md_size;
ctx->blockout= 1;
ctx->sigio= 0;
}
static void sig_in(BIO* b)
{
BIO_OK_CTX *ctx;
EVP_MD_CTX *md;
unsigned char tmp[EVP_MAX_MD_SIZE];
int ret= 0;
ctx=(BIO_OK_CTX *)b->ptr;
md= &(ctx->md);
if(ctx->buf_len- ctx->buf_off < 2* md->digest->md_size) return;
EVP_DigestInit(md, md->digest);
memcpy(&(md->md.base[0]), &(ctx->buf[ctx->buf_off]), md->digest->md_size);
longswap(&(md->md.base[0]), md->digest->md_size);
ctx->buf_off+= md->digest->md_size;
EVP_DigestUpdate(md, WELLKNOWN, strlen(WELLKNOWN));
md->digest->final(tmp, &(md->md.base[0]));
ret= memcmp(&(ctx->buf[ctx->buf_off]), tmp, md->digest->md_size) == 0;
ctx->buf_off+= md->digest->md_size;
if(ret == 1)
{
ctx->sigio= 0;
if(ctx->buf_len != ctx->buf_off)
{
memmove(ctx->buf, &(ctx->buf[ctx->buf_off]), ctx->buf_len- ctx->buf_off);
}
ctx->buf_len-= ctx->buf_off;
ctx->buf_off= 0;
}
else
{
ctx->cont= 0;
}
}
static void block_out(BIO* b)
{
BIO_OK_CTX *ctx;
EVP_MD_CTX *md;
unsigned long tl;
ctx=(BIO_OK_CTX *)b->ptr;
md= &(ctx->md);
tl= ctx->buf_len- OK_BLOCK_BLOCK;
tl= swapem(tl);
memcpy(ctx->buf, &tl, OK_BLOCK_BLOCK);
tl= swapem(tl);
EVP_DigestUpdate(md, (unsigned char*) &(ctx->buf[OK_BLOCK_BLOCK]), tl);
md->digest->final(&(ctx->buf[ctx->buf_len]), &(md->md.base[0]));
ctx->buf_len+= md->digest->md_size;
ctx->blockout= 1;
}
static void block_in(BIO* b)
{
BIO_OK_CTX *ctx;
EVP_MD_CTX *md;
long tl= 0;
unsigned char tmp[EVP_MAX_MD_SIZE];
ctx=(BIO_OK_CTX *)b->ptr;
md= &(ctx->md);
memcpy(&tl, ctx->buf, OK_BLOCK_BLOCK);
tl= swapem(tl);
if (ctx->buf_len < tl+ OK_BLOCK_BLOCK+ md->digest->md_size) return;
EVP_DigestUpdate(md, (unsigned char*) &(ctx->buf[OK_BLOCK_BLOCK]), tl);
md->digest->final(tmp, &(md->md.base[0]));
if(memcmp(&(ctx->buf[tl+ OK_BLOCK_BLOCK]), tmp, md->digest->md_size) == 0)
{
ctx->buf_off_save= tl+ OK_BLOCK_BLOCK+ md->digest->md_size;
ctx->buf_len_save= ctx->buf_len;
ctx->buf_off= OK_BLOCK_BLOCK;
ctx->buf_len= tl+ OK_BLOCK_BLOCK;
ctx->blockout= 1;
}
else
{
ctx->cont= 0;
}
}