Creating A Stream To Read An Encrypted File

This example creates an input stream to read from a file encrypted using a simple encryption algorithm. The key is stored in the first byte of the file. A character code stored in the file is the result of a logical exclusive-or operation of the output character and the key. The decryption of the input file is done in the bottom layer read function.

The input stream created only permits seeking to a previous read position. Notice the bottom layer read function defined (decrypt_read()) buffers more than one record. By doing this, the Prolog input/output system will maintain a correct line count and line position based on the new line character (\n). There are also two user-defined error numbers used in this example (DECRYPT_NO_KEY and DECRYPT_TTY_FILE).

                                 
decrypt.pl
foreign(open_decrypt, c, open_decrypt(+string, -integer, [-address])). foreign_file('decrypt', [open_decrypt]). :- load_foreign_files(['decrypt'],['-lc']). open_decrypt_stream(FileName, PrologStream) :- open_decrypt(FileName, ErrorNum, CStream), ( CStream =:= 0 -> raise_exception(existence_error( open_decrypt_stream(FileName, PrologStream), 1, file, FileName, errno(ErrorNum))) ; stream_code(PrologStream, CStream) ).
                                  
decrypt.c
#include <fcntl.h> #include <quintus/quintus.h> extern int errno; #define Buffer_Size 8192 typedef unsigned char Key_Type; typedef struct { QP_stream qpinfo; int fd; /* file descriptor */ int last_rdsize; /* size of last record */ int left_size; /* char. left unread */ unsigned char *left_ptr; /* pointer to the unread */ unsigned char buffer[Buffer_Size+3]; Key_Type key; /* decryption key */ } DecryptStream; #define CoerceDecryptStream(qpstream) \ ((DecryptStream *)(qpstream)) /* define user-defined error number */ #define DECRYPT_NO_KEY QP_END_ECODE+1 #define DECRYPT_TTY_FILE QP_END_ECODE+2
                                  
decrypt.c
/* To enable the Prolog system to maintain correct line count and line position, a whole buffer is read but only a line in the buffer is returned every time. The characters in the buffer are decrypted at once. The buffer is maintained as follows: <- left_size -> +---------------+-------------+--+-------+ | has been read | to be read |\n| empty | +---------------+-------------+--+-------+ ^ left_ptr ^ <pad '\n' character> */ static int decrypt_read(qpstream, bufptr, sizeptr) QP_stream *qpstream; unsigned char **bufptr; size_t *sizeptr; { register DecryptStream *stream = CoerceDecryptStream(qpstream); register int n; register unsigned char *s, *s1; /* magic is the beginning byte offset of return record */ qpstream->magic.byteno += stream->last_rdsize; if (stream->left_size <= 0) { register Key_Type *kp, *kq, key; /* read a new buffer of input and decrypt characters*/ n = read(stream->fd, (char *) stream->buffer, Buffer_Size); if (n > 0) { kp=(Key_Type *) stream->buffer; kq=(Key_Type *) &stream->buffer[n]; for (key = stream->key; kp < kq ; ) /* decrypt */ *kp++ ^= key; stream->left_size = n; stream->left_ptr = stream->buffer; } else if (n == 0) { stream->last_rdsize = stream->left_size = 0; *bufptr = stream->left_ptr = stream->buffer; *sizeptr = stream->last_rdsize = 0; return QP_EOF; } else { qpstream->errno = errno; stream->last_rdsize = stream->left_size = 0; return QP_ERROR; } }
                                  
decrypt.c
/* make next line of data available */ s = stream->left_ptr; se = s + stream->left_size; while (s < se) { if (*s++ == '\n') { /* found end of record */ break; } } *bufptr = stream->left_ptr; *sizeptr = stream->last_rdsize = s - stream->left_ptr; stream->left_ptr = s; stream->left_size = se - s; return (*--s == '\n') ? QP_FULL : QP_PART; } /* Only QP_SEEK_PREVIOUS is allowed for the file, so 'whence' specified can only be QP_BEGINNING. '*sizeptr' should always be set to 0 since there is only input stream. */ static int decrypt_seek(qpstream, qpmagic, whence, bufptr, sizeptr) QP_stream *qpstream; union QP_cookie *qpmagic; int whence; unsigned char **bufptr; size_t *sizeptr; { DecryptStream *stream = CoerceDecryptStream(qpstream); off_t offset; switch (whence) { case QP_BEGINNING: if ((offset = lseek(stream->fd,qpmagic->byteno,L_SET)) == -1) { qpstream->errno = errno; return QP_ERROR; } qpstream->magic.byteno = offset; *bufptr = stream->buffer; *sizeptr = 0; stream->left_ptr = stream->buffer; stream->left_size = stream->last_rdsize = 0; return QP_SUCCESS; case QP_CURRENT: case QP_END: default: qpstream->errno = QP_E_INVAL; return QP_ERROR; } }
                                  
decrypt.c
static int decrypt_close(qpstream) QP_stream *qpstream; { DecryptStream *stream = CoerceDecryptStream(qpstream); int fd = stream->fd; QP_free((char *)stream); if (close(fd) < 0) { qpstream->errno = errno; return QP_ERROR; } return QP_SUCCESS; }
                                  
decrypt.c
/* open_crypt_stream: open the specified non-tty 'filename' for reading. The file is a simple crypted file with the first byte as the key. It is crypted by logical exclusive-or operation of the key with every character in the file. Upon success, the opened stream is returned. Upon failure, QP_NULL_STREAM is returned and the error code is stored in the parameter 'error_num'. */ QP_stream * open_decrypt(filename, error_num) char *filename; int *error_num; { int fd; Key_Type key; DecryptStream *stream; QP_stream *option; if ((fd = open(filename, O_RDONLY)) < 0) { *error_num = errno; return QP_NULL_STREAM; } if (isatty(fd)) { /* tty file is not accepted */ (void) close(fd); *error_num = DECRYPT_TTY_FILE; } switch (read(fd, (char *) &key, sizeof(key)) ) { case sizeof(key): break; case 0: *error_num = DECRYPT_NO_KEY; (void) close(fd); return QP_NULL_STREAM;; default: *error_num = errno; (void) close(fd); return QP_NULL_STREAM; }
                                  
decrypt.c
if (! (stream = (DecryptStream *) QP_malloc(sizeof(*stream))) ) { (void) close(fd); *error_num = QP_errno; return QP_NULL_STREAM; } stream->fd = fd; stream->last_rdsize = 0; stream->left_size = 0; stream->key = key; option = &stream->qpinfo; QU_stream_param(filename, QP_READ, QP_DELIM_LF, option); option->max_reclen = Buffer_Size; /* Record the current byte offset in the file */ option->magic.byteno = sizeof(key); option->read = decrypt_read; option->seek = decrypt_seek; option->close = decrypt_close; QP_prepare_stream(&stream->qpinfo, stream->buffer); if (QP_register_stream(&stream->qpinfo) == QP_ERROR) { (void) stream->qpinfo.close(&stream->qpinfo); *error_num = QP_errno; return QP_NULL_STREAM; } return (QP_stream *) stream; }