/*******************************************************************************/
/*                                                                             */
/*  Copyright 2004 Pascal Gloor                                                */
/*                                                                             */
/*  Licensed under the Apache License, Version 2.0 (the "License");            */
/*  you may not use this file except in compliance with the License.           */
/*  You may obtain a copy of the License at                                    */
/*                                                                             */
/*     http://www.apache.org/licenses/LICENSE-2.0                              */
/*                                                                             */
/*  Unless required by applicable law or agreed to in writing, software        */
/*  distributed under the License is distributed on an "AS IS" BASIS,          */
/*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   */
/*  See the License for the specific language governing permissions and        */
/*  limitations under the License.                                             */
/*                                                                             */
/*******************************************************************************/

/* Author   : Pascal Gloor <pascal.gloor@spale.com> */
/* Version  : 1.0                                   */
/* Revision : 02.04.2005                            */

#include <stdio.h>
#include <inttypes.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>

#include "scrypt.h"
#include "sha256.h"


#define BUFFER 16384

void sc_read(FILE *file, char *buf, size_t *len, int *end)
{
	size_t clen;

	clen = fread(buf+*len, 1, BUFFER-*len, file);

	if ( clen == 0 )
	{
		if ( feof(file) == 0 )
		{
			fprintf(stderr,"fread(): %s\n",strerror(errno));
			exit(EXIT_FAILURE);
		}
		else
		{
			*end = 1;
		}
	}
	else
	{
		*len += clen;
	}
}

void sc_send(FILE *file, char *buf, size_t *len)
{
	size_t clen;

	clen = fwrite(buf, 1, *len, file);

	if ( clen == 0 && *len > 0 )
	{
		fprintf(stderr,"fwrite(): %s\n",strerror(errno));
		exit(EXIT_FAILURE);
	}
	else
	{
		memmove(buf, buf+clen, *len-clen);
		*len -= clen;
	}
}

char sc_key(char *hash, uint32_t *cnum)
{
	uint8_t  m = *( hash + ( *cnum % 32 ) );
	uint8_t  c = (*cnum % 256);
	uint16_t check = c + m;

	return (check % 256);
}

void sc_work(char *ibuf, size_t *ilen, char *obuf, size_t *olen, char *hash, uint32_t *cnum)
{
	size_t i;

	if ( *ilen > (BUFFER-*olen) )
		return;

	for( i=0; i<*ilen; i++ )
	{
		*(obuf+*olen+i) = *(ibuf+i) ^ sc_key(hash, cnum);
		*cnum += 1;
	}
	*olen += i;
	memmove(ibuf, ibuf+i, *ilen-i);
	*ilen -= i;
}

void main_loop(char *key, FILE *in, FILE *out)
{
	unsigned char hash[32];
	char *ibuf, *obuf;
	uint32_t cnum = 0;
	size_t ilen = 0, olen = 0;
	int end = 0;

	ibuf = malloc(BUFFER);
	obuf = malloc(BUFFER);

	{
		sha256_context ctx;
		sha256_starts(&ctx);
		sha256_update(&ctx, key, strlen(key));
		sha256_finish(&ctx, hash);
	}
	
	while(end != 2)
	{
		sc_read(in, ibuf, &ilen, &end);
		sc_work(ibuf, &ilen, obuf, &olen, hash, &cnum);
		sc_send(out, obuf, &olen);

		if ( end == 1 && ilen == 0 && olen == 0 )
			end = 2;
	}

	free(ibuf);
	free(obuf);

}

int main(int argc, char *argv[])
{
	FILE *in  = stdin;
	FILE *out = stdout;

	if ( argc < 2 || argc > 4 )
	{
		printf("SCrypt v1.0 by Pascal Gloor\n");
		printf("usage: %s <key> [input] [output]\n",argv[0]);
		printf("\n");
		printf("  key    : the key used to crypt/decrypt.\n");
		printf("  input  : file to read. use '-' for stdin.\n");
		printf("  output : file to write. use '-' for stdin.\n");
		printf("\n");
		printf("  note   : if input/output are omited, stdin/stdout\n");
		printf("           will be used. this is the default behaviour.\n");
		printf("\n");
		exit(EXIT_FAILURE);
	}

	if ( argc >= 3 )
		if ( strcmp(argv[2],"-") != 0 )
			if ( ( in = fopen(argv[2],"r") ) == NULL )
			{
				fprintf(stderr,"fopen('%s') error: '%s'\n",argv[2],strerror(errno));
				exit(EXIT_FAILURE);
			}

	if ( argc == 4 )
		if ( strcmp(argv[3],"-") != 0 )
			if ( ( out = fopen(argv[3],"w") ) == NULL )
			{
				fprintf(stderr,"fopen('%s') error: '%s'\n",argv[3],strerror(errno));
				exit(EXIT_FAILURE);
			}

	main_loop(argv[1],in,out);

	fclose(in);
	fclose(out);

	return 0;
}