Newsgroups: comp.databases.informix Subject: Encryption under I4GL From: dberg@informix.com (David I. Berg) Date: 6 Jan 95 21:40:08 GMT smorrow@dotrisc.cfr.usf.edu (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: alan@po.den.mmc.com (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 <76543.2600@CompuServe.COM> > 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 | / \ alan@den.mmc.com | 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: dave@cassens.com (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 #include #include /* 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 #include #include #include /* 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 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 chin@nihc.demon.co.uk ----- 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 #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 /* 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> 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= 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 -------------------------------------------