Subject: Encryption under I4GL
Newsgroups: comp.databases.informix

From: [email-masked] (David I. Berg)
Date: 6 Jan 95 21:40:08 GMT

[email-masked] (Steve Morrow) writes:

>We have a Fourgen screen which prompts for a PIN number, and the value
>entered is referenced against a value in a table for validation.

>I am interested in encrypting this PIN number.  Since such a function
>doesn't exist (yet) in 4GL, has anybody written such a routine, or has
>anyone made use of the UNIX encrypt() call from within I4GL?

>Any sample code or pointers would be appreciated.

>Thanks!

Following is a simple encryption function based on a variation of ROT13
that I wrote for password encryption. You could probably use a variation
of this scheme to do what you want.
  ___                   ___              Senior Consultant
  /  ) __      . __/    /_ ) _    _  __  Informix Software Inc. (303) 850-0210
_/__/ (_(_ (/ / (_(_  _/__) (-' ~/ '(_-  5299 DTC Blvd #740 Englewood CO 80111

#----------------------------------------------------------------------
FUNCTION encrypt_fg(l_password)
# ARGUMENTS: CHAR(8) or less to which to apply password en-/de-cryption
# PURPOSE:   Encrypt or decrypt a user password
# RETURNS:   En- or De-crypted value (NULL indicates invalid value passed in.)
#----------------------------------------------------------------------
DEFINE l_password       CHAR(8)
DEFINE l_encrypt        CHAR(8)
DEFINE l_char_set       CHAR(62)
DEFINE l_encrypt_set    CHAR(62)
DEFINE i, j, k          SMALLINT

LET l_char_set =
    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
LET l_encrypt_set =
    "NoPqRsTuVwXyZAbCdEfGhIjKlMnOpQrStUvWxYzaBcDeFgHiJkLm5678901234"
INITIALIZE l_encrypt TO NULL
IF LENGTH(l_password) > 0 THEN
    LET j = 1
    LET k = LENGTH(l_password)
    FOR i = 1 TO LENGTH(l_password)
        FOR j = 1 TO LENGTH(l_char_set)
            IF l_password[i,i] = l_char_set[j,j] THEN
                LET l_encrypt[k,k] = l_encrypt_set[j,j]
                LET k = k - 1
                EXIT FOR
            END IF
        END FOR
        IF j > LENGTH(l_char_set) THEN
            LET l_encrypt = NULL
            EXIT FOR
        END IF
    END FOR
END IF

RETURN l_encrypt

END FUNCTION # encrypt_fg

From: [email-masked] (Alan Popiel)
Date: 27 Jan 1995 11:58:09 -0500

> Subject: Column Encryption
> Date: 27 Jan 1995 04:53:35 GMT
> Reply-To: Michael L. Gonzales <[email-masked]>
> Organization: The Focus Group, Ltd.
>
> I need a method to take entered data (passwords), encrypt the
> value, and store the value in a column.  Obviously, I would need
> to decipher the stored value for future validations.  The purpose
> of this is to merely keep any individual from scanning the
> database for passwords.
>
> Any recommendations on encrypting data?
>
> Mike.

Within the past couple of weeks, someone posted a very nice encryption /
decryption routine to c.d.i.  I saved it somewhere; now if I could only
find it!

However, I consider it poor security to store passwords in a format that
can be deciphered.  For password verification it is enough, and better
security, to encrypt the newly entered password and then compare the
encrypted form to the stored encrypted form.  This is in contrast to your
suggestion of deciphering the stored password and comparing the passwords
in clear-text format.

I have used variations on the following password encryption algorithm in
several languages:

DEFINE	cypher	INTEGER		{ other definitions as needed }
LET cypher = 0
FOR j = 1 to LENGTH ( password_text )
  LET cypher = cypher * 7 + ord ( password_text[j] ) + 3
  IF cypher > 32767 THEN	{ these three lines are optional }
    LET cypher = cypher - 32767
  END IF
END FOR

Especially with the optional lines, this is a "lossy" algorithm, meaning
that information is lost, so that encrypted values cannot be decrypted
uniquely.  The chance of two different passwords colliding to the same
encrypted value and giving a false validation can be reduced by increasing
32767 to some larger value.

A disadvantage to doing this in 4GL is that 4GL does not have an ord function.
However, I include one below for your use.

Regards,
Alan                   ___________________________
______________________| R. Alan Popiel            |__________________________
\  Internet:          | Martin Marietta, SLS      |                         /
 \   [email-masked] | P.O. Box 179, M/S 3810    | Std disclaimers apply. /
  )Voice:             | Denver, CO 80201-0179 USA |                       (
 /   303-977-9998     |___________________________|  (But you knew that!)  \
/________________________)                     (____________________________\

----------- begin included source code ---------------
/* function: ord - return numeric ASCII code for character
 * author:   Alan Popiel
 * date:     27 Oct 1993
 *
 * ord()  returns the numeric value of the ASCII code for the first character
 *        of the string passed to it by an Informix 4GL routine.
 *
 * usage:
 *    CALL ord(str) RETURNING number
 *    LET number = ord(str)
 *
 * CALL argument:
 *    str -- 4GL character string, normally of length 1.  If length is greater
 *           than 1, the the code for the first character is returned.
 *
 * RETURNING argument:
 *    integer number -- numeric value of the ASCII code;
 *    Note: negative values are returned for ASCII codes > 127.  To fix this:
 *          IF number < 0 THEN
 *             LET number = number + 256
 *          END IF
 */
int ord(nargs)
   int nargs;
{
   char str[513];               /* Input from stack: Allow for long strings. */

                                /* Pop calling argument from the stack. */
   popquote( str, sizeof(str) );

                                /* Push return argument to the stack. */
   retint( (unsigned)str[0] );  /* ASCII code of first character. */
   return(1);                   /* Number of arguments pushed. */
}
------------ end included source code ----------------

From: [email-masked] (Dave Adams)
Date: 15 Feb 95 16:27:12 GMT

Mike,

I'm not a C programmer, but unix-style encrypted passwords may be used within
a 4GL program with the help of some C routines I've written for the purpose.
The examples below contain a 4GL program that creates and stores crypt(3)
encrypted passwords within a Informix table, two C functions to encrypt and
validate passwords, and another C function to generate salt values for the
encryption routine.  The examples were tested under Online 5.02 running on
an HP/9000 Series 800 with HP-UX.  The column encryption is actually done
completely within the crypt(3) routine, supplied with most *nix operating
systems sold in the United States, subject to ITAR regulations.

#-------------------------------------------------------------------------------
# cryptval.4gl:
#-------------------------------------------------------------------------------

DATABASE stores2

MAIN

   DEFINE username, plaintext,salt,cryptval CHAR(16),
   checkcode INTEGER,
   knt SMALLINT

   #
   #--- Create new table in stores database to hold passwords ---#
   #
   SELECT COUNT(*) INTO knt FROM SYSTABLES WHERE tabname = 'passwd'

   IF knt IS NULL OR knt = 0 THEN
      CREATE TABLE passwd(userid CHAR(16), passwd CHAR(16))
   END IF

   #
   #--- Generate new salt value based on time and program name ---#
   #
   CALL mksalt(arg_val(0)) RETURNING salt

   #
   #--- Generate new password given plaintext and salt values ---#
   #
   DISPLAY "--- Creating a new password entry for a user ---"
   PROMPT "username: " FOR username
   SELECT COUNT(*) INTO knt FROM passwd WHERE passwd.userid = username
   IF knt IS NOT NULL AND knt > 0 THEN
      DELETE FROM passwd WHERE userid = username
      LET knt = SQLCA.sqlerrd[3]
      IF knt IS NULL OR knt = 0 THEN
         DISPLAY "%%% cannot delete passwd entry for ", username CLIPPED, "!"
         EXIT PROGRAM 1
      END IF
   END IF
   PROMPT "password: " FOR plaintext ATTRIBUTE(INVISIBLE)
   CALL cryptwd(plaintext,salt) RETURNING cryptval
   INSERT INTO passwd VALUES(username,cryptval)
   LET knt = SQLCA.sqlerrd[3]
   IF knt IS NULL OR knt = 0 THEN
      DISPLAY "%%% cannot create passwd entry for ", username CLIPPED, "!"
      EXIT PROGRAM 1
   END IF
   DISPLAY " "
   DISPLAY " "

   #
   #--- Validate password given plaintext and encrypted values ---#
   #
   DISPLAY "--- Validating a password entry for a user ---"
   PROMPT "username: " FOR username
   SELECT passwd.passwd INTO cryptval FROM passwd WHERE passwd.userid = username
   LET knt = SQLCA.sqlerrd[3]
   IF knt IS NULL OR knt = 0 THEN
      DISPLAY "%%% cannot locate passwd entry for ", username CLIPPED, "!"
      EXIT PROGRAM 1
   END IF
   PROMPT "password: " FOR plaintext ATTRIBUTE(INVISIBLE)
   CALL checkwd(plaintext, cryptval) RETURNING checkcode
   IF checkcode THEN
      DISPLAY "correct."
   ELSE
      DISPLAY "incorrect!"
   END IF

END MAIN

#-------------------------------------------------------------------------------
# cryptwd.c:
#-------------------------------------------------------------------------------

#include <stdio.h>
#include <strings.h>
#include <unistd.h>

/* return a crypt(3) encrypted password given a plaintext value and salt */

cryptwd(int nargs)
{
 char *usage="Usage: call cryptwd(plaintext,salt) returning cryptval";
 char *cryptval,*crypt();
 char plaintext[16];
 char salt[16];
 char junk[512];
 int i;

 if(nargs != 2) {
   fprintf(stderr, "cryptwd: wrong number of parameters (%d)\n", nargs);
   fprintf(stderr, "%s\n", usage);
   for( i=0; i<nargs;i++ ) { popquote(junk,512); }
   cryptval = '\0';
   retquote(cryptval);
   return(1); }

 popquote(salt,16);
 popquote(plaintext,16);
 cryptval = crypt(plaintext,salt);
 retquote(cryptval);
 return(1);
}

/* validate a crypt(3) encrypted password given plaintext, encrypted values */

checkwd(int nargs)
{
 char *usage="Usage: call checkwd(plaintext,cryptwd) returning checkcode";
 char *cryptval,*crypt();
 char plaintext[16];
 char cryptwd[16];
 char junk[512];
 int checkcode;
 int i;

 if(nargs != 2) {
   fprintf(stderr, "checkwd: wrong number of parameters (%d)\n", nargs);
   fprintf(stderr, "%s\n", usage);
   for( i=0; i<nargs;i++ ) { popquote(junk,512); }
   checkcode = 0;
   retint(checkcode);
   return(1); }

 popquote(cryptwd,16);
 for( i=0; i<16 && cryptwd[i] != ' ' ;i++ ) { }
 cryptwd[i]='\0';
 popquote(plaintext,16);
 cryptval = crypt(plaintext,cryptwd);
 checkcode = 0;
 i = strcmp(cryptval,cryptwd);
 if(i == 0) { checkcode = 1; }
 retint(checkcode);
 return(1);
}

#-------------------------------------------------------------------------------
# mksalt.c:
#-------------------------------------------------------------------------------

#include <stdio.h>
#include <strings.h>
#include <unistd.h>
#include <time.h>

/* Create a new salt value for crypt(3), using a seed value and time of day */

mksalt(int nargs)
{
 char *usage="Usage: call mksalt(seed) returning salt";
 char alphas[64]=
   "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
 char *cryptval,*crypt();
 char junk[512];
 char seed[16];
 char salt[2];
 int i;

 struct {
   unsigned long   tv_sec;    /* seconds since Jan. 1, 1970 */
   long            tv_usec;   /* and microseconds */
}  tp;

 struct {
   int     tz_minuteswest;    /* of UTC */
   int     tz_dsttime;        /* type of DST correction to apply */
}  tzp;

  if(nargs != 1) {
    fprintf(stderr, "mksalt: wrong number of parameters (%d)\n", nargs);
    fprintf(stderr, "%s\n", usage);
    for( i=0; i<nargs;i++ ) { popquote(junk,512); }
    cryptval = '\0';
    retquote(cryptval);
    return(1); }

  popquote(seed,16);
  gettimeofday(&tp, &tzp);

  i = tp.tv_sec % 64;
  salt[1] = alphas[i];
  i = tp.tv_usec % 64;
  salt[0] = alphas[i];
  cryptval = crypt(seed,salt);
  salt[0] = cryptval[11];
  salt[1] = cryptval[12];

  retquote(salt);
  return(1);
}

Dave Adams            //dna//                                [email-masked]
-------------------------------------------------------------------------------
"Let's show this prehistoric bitch how we do things downtown!"
-- The Ghostbusters

DISCLAIMER:  Not FCC Approved.

From: Chin <[email-masked]>
Date: 4 Jun 1995 02:37:38 +0100

I developed an EPOS system for the licenced 'pub' trade which required
the company name & program licence expiry date to be stored in an INFORMIX
database.

For obvious reasons I wanted these fields & certain others to be encrypted.
Encrypting the strings & writing them to the database did not work, so I have
written some C functions to do this which you may find useful.
It works as follows:

The source string is sent from 4GL to a C funtion which just does a simple XOR
encryption, (you could use a more elaborate DES type algorithm) , the resulting
encrypted string (8-bit) is them sent to a function that uuencodes it to
another string which is returned to the 4GL app'. The string returned from
cmencode() is 7 bit ASCII & can be written to the database without problem.
The decoding routine just does the reverse.

It's really just a hack of codon & uuencode for mail, altered to read & write
strings instead of files.

( An ASCII string is sent to the encodeing function & it returns an ASCII
string roughly 135% the size of the original - so be careful about string &
field sizes, they should be 35% larger that normal. )

Feel free to E-mail me with any comments at
[email-masked]

----- CUT HERE ---  test.4gl ------------------------------------------------

MAIN
	DEFINE stringtoencode,encodedstring,decodedstring,tmp CHAR(200)
	DATABASE pub   ### change to your database

{######
	Just link 4glcrypt.c with this to test & change stringtoencode to
	whatever you like.
#######}

	WHENEVER ERROR CONTINUE

	DROP TABLE testtable

	WHENEVER ERROR STOP

	CREATE TABLE testtable

	(
		licence CHAR(300) ## Be careful, encodedstring will be about 35%
			 ## lager (sorry larger - too much beer ) than
			 ## stringtoencode
	)

	LET stringtoencode="HEY HO LETS GO"

		DISPLAY "STRING TO ENCODE <",stringtoencode clipped,">"

	LET encodedstring=cmencode(stringtoencode ,LENGTH(stringtoencode)+1)

	### Now store encdodedstring in database

	INSERT INTO  testtable VALUES (encodedstring )

			#### WOW licence field now looks really weird !!
			#### now get field from table & decode it

	SELECT licence INTO tmp FROM testtable

	LET decodedstring=cmdecode(tmp ,LENGTH(tmp)+1)

		DISPLAY "DECODED STRING<",decodedstring CLIPPED,">"

END MAIN

--------- CUT END OF test.4gl --------------------------------------------

--------- CUT HERE 4glcrypt.c ---------------------------------------------

/* 4glcrypt.c

#   @(#) 4glcrypt.c 1.0   Sat Jun 03 20:15:40 BST 1995

#   4glcrypt.c - generates encrypted/decrypted strings for INFORMIX 4GL.
#   Copyright (C) 1995  Christopher Moore.
#
#  This program is offered 'as is'; the author accepts no responsibility
#  for any consequences of its use.
#  You may freely distribute and use this code your own programs as long as this
#  comment remains. You may NOT sell it or use it in
#  software that is sold , and you must not claim you wrote it.
#  If you modify the source code & distribute it to other people you must
#  include change comments indicating the author of the change, & a description
#  of the modification & the date of the change directly after this comment.
*/

/******
	To encode a string, place the string in stringtoencode &;

	LET encodedstring=cmencode(stringtoencode,length(stringtoencode)+1)
	
	Then just write encodedstring to a database field.		

	--------------------------------------------------------

	To decode the string;

	Select stringtodecode from database field, then;

	LET decodedstring=cmdecode(stringtodecode,length(stringtodecode)+1)

*******/

#include <stdio.h>
#define MAXSTRINGLEN 200 /*** Change this to suit 4GL var, should be at least
				35% lager than the 4GL var you want to encode
			*****/

void uuencode(),outdec(),outdecd();

int cmencode(n)
int n;
	{
		int len, i,cc;
		char s[MAXSTRINGLEN];
		unsigned char c;
		char new[MAXSTRINGLEN];
		static char uunew[MAXSTRINGLEN];
		c = 0;
		i = 150; /* this is the XOR seed can be anything. Someone
			    wanting to decrypt will have to know this or
			    try every combo. But the fact that it is uuencoded
			    as well makes this more difficult. This must match
			    the seed in cmdecode()
			*/

		popint(&len); 		/*** get length of source string ****/
		popquote(s,len); 	/*** get string from 4GL to encode ***/

	/*** Encryption routine - this is simple XOR but could be better,using
		DES crypt or GNU crypt from libufc.a
	****/

		new[0]=NULL;
		for (cc=0;cc<=len;cc++){
			c=s[cc];
			new[cc]=c ^ i;
			i = c;
		}
		new[cc]=NULL;

		/******
			OK we now have an encrypted string, lets uuencode it
			to produce uunew, which is sent back to 4GL.
		******/

		uuencode(new,len,uunew);

		retquote(uunew);
		return(1); /*** send it back to 4gl ****/
	}

int cmdecode(n)
int n;
{

	int len, i,cc;
	char s[MAXSTRINGLEN];
	static char new[MAXSTRINGLEN];
	char uunew[MAXSTRINGLEN];
	unsigned char c;
	c = 0;
	i = 150; /* This must match the seed used to encrypt */

	popint(&len);
	popquote(s,len); /*** Get uuencoded string from 4GL ****/

	len=uudecode(s,uunew); /*** uudecode back to encrypted string **/

	/******
		Simple XOR decryption routine
	*******/

	new[0]=NULL;
	for (cc=0;cc<=len;cc++){
		c=uunew[cc];
		c = c ^ i;
		new[cc]=c;
		i = c;
	}
	new[cc]=NULL;

	/**** Ok got original 7 bit ASCII string send back to 4GL *****/

	retquote(new);
	return(1);
}

/*
 * cmencode.c
 *
 * Encode a string so it can be filed to a database
 */

#include <stdio.h>

/* ENC is the basic 1 character encoding function to make a char printing */
#define ENC(c) (((c) & 077) + ' ')
#define DEC(c)	(((c) - ' ') & 077)
/***#define buildout(c,outt) (outt[*(outpos)++]=c)**/

void uuencode(in,inlen, out)
char *in;
char *out;
int inlen;
{
	char buf[80];
	int i, n;
	int inpos,outpos;

	inpos=0; /*** Position in source string ****/
	outpos=0; /*** Position in object string ****/

	for (;;) {
		/* 1 (up to) 45 character line */
		n = breakstring(in, buf, 45,&inpos,inlen);
		out[outpos++]=ENC(n); /*** length of chunk returned in buf **/

		for (i=0; i<n; i += 3)
			outdec(&buf[i], out,&outpos); /** uuencode data ***/

		out[outpos++]='\n';   /**** terminate chunk with newline ***/

		if (n <= 0)
			break;
	}
	out[outpos++]='\0';   /*** OK all chunks processed NULL terminate ***/
}

/*
 * output one group of 3 bytes, pointed at by p, on file f.
 */
void outdec(p, f,outpos)
char *p;
char *f;
int *outpos;
{
	int c1, c2, c3, c4;

	c1 = *p >> 2;
	c2 = (*p << 4) & 060 | (p[1] >> 4) & 017;
	c3 = (p[1] << 2) & 074 | (p[2] >> 6) & 03;
	c4 = p[2] & 077;
	f[(*outpos)++]=ENC(c1);
	f[(*outpos)++]=ENC(c2);
	f[(*outpos)++]=ENC(c3);
	f[(*outpos)++]=ENC(c4);
}

/***    breaks the string pointed to by 'in' into 45 byte chunks ****/
int breakstring(in, buf, cnt,inpos,inlen)
char *in;
char *buf;
int *inpos;
int cnt,inlen;
{
	int c, i;

	for (i=0; i<cnt; i++) {
		if (*inpos >= inlen)
			return(i);
		buf[i] = in[(*inpos)++];
	}
	return (cnt);
}

/*
 * copy from in to out, decoding as you go along.
 */
int uudecode(in, out)
char *in;
char *out;
{
	char buf[80];
	char *bp;
	int n;
	int outpos,inpos,inlen;

	outpos=0;
	inpos=0;
	inlen=strlen(in);

	for (;;) {
		/* for each input line */
		if (getline(buf, in,&inpos,inlen) == 0 ) {
			printf("Short string\n");
			/**exit(10);***/
			return (0);
		}
		n = DEC(buf[0]); /*** get chunk size ****/
		if (n <= 0)
			break;

		bp = &buf[1];
		while (n > 0) {
			outdecd(bp, out, n,&outpos); /*** decode data ****/
			bp += 4;
			n -= 3;
		}
	}
return(outpos);
}

/*
 * output a group of 3 bytes (4 input characters).
 * the input chars are pointed to by p, they are to
 * be output to file f.  n is used to tell us not to
 * output all of them at the end of the file.
 */
void outdecd(p, f, n,outpos)
char *p;
char *f;
int *outpos;
{
	int c1, c2, c3;

	c1 = DEC(*p) << 2 | DEC(p[1]) >> 4;
	c2 = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
	c3 = DEC(p[2]) << 6 | DEC(p[3]);
	if (n >= 1)

f[(*outpos)++]=c1;
	if (n >= 2)
f[(*outpos)++]=c2;
	if (n >= 3)
f[(*outpos)++]=c3;
}

/** get chunks (newline delimited strings )****/
int getline(buf,inn,inpos,inlen)
char *buf,*inn;
int *inpos,inlen;
{
	int c,f,found;

	c=0;
	found=0;

	for (f= *inpos;f<=inlen;f++){
		
		if (inn[f]=='\n'){
			found=1;
			break;
		}

		buf[c++]=inn[f];

	}
	if (!found) return(0);

	buf[c]='\0';
	*inpos = ++f;

return (1);
}

--------- CUT END OF 4glcrypt.c -------------------------------------------