?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Accounting and Validation: Login Password Encryption ' ??
MODULE avm$encrypt_password;

{ PURPOSE:
{
{   This module contains the procedures used to encrypt a validation password.
{
{ DESIGN:
{
{   The Password Encryption algorithms have been placed in a separate module
{ to facilitate a site defined Password Encryption algorithm.
{
{   This module contains two procedures that are used to encrypt passwords.  The
{ primary encryption procedure (AVP$ENCRYPT_PASSWORD) contains the current
{ password encryption algorithm.  The secondary encryption procedure
{ (AVP$OLD_ENCRYPT_PASSWORD) contains the previous version of the password
{ encryption algorithm.  Both procedures are identical when NOS/VE is released.
{
{   Whenever the system is verifying a password, it will first call
{ AVP$ENCRYPT_PASSWORD, to determine the encrypted value of the supplied
{ password, and compare the encrypted result with the desired encrypted value.
{ If the two do not match, AVP$OLD_ENCRYPT_PASSWORD is called the the result is
{ compared again.  Whenever the system is updating an encrypted password,
{ AVP$ENCRYPT_PASSWORD is used.
{
{   This process allows a site to move from one password encryption algorithm
{ to another by replacing the code in AVP$OLD_ENCRYPT_PASSWORD with a copy of
{ the code in AVP$ENCRYPT_PASSWORD.  AVP$ENCRYPT_PASSWORD is then modified to
{ implement the new encryption algorithm.  After this has been done, any
{ changes made to passwords will be encrypted with the new algorithm.  After
{ an appropriate amount of time, AVP$OLD_ENCRYPT_PASSWORD should be modified
{ to match AVP$ENCRYPT_PASSWORD to remove support for the old encryption
{ algorithm.
{

?? PUSH (LISTEXT := ON) ??
?? NEWTITLE := '  Declarations', EJECT ??

*copyc avt$password

*copyc ost$status
*copyc ost$user_identification

?? POP ??
?? NEWTITLE := '  [XREF] Procedures', EJECT ??

*copyc avp$encrypt

*copyc clp$trimmed_string_size
?? TITLE := '  [XDCL] avp$encrypt_password', EJECT ??

  PROCEDURE [XDCL] avp$encrypt_password
    (    user_name: ost$user_name;
         unencrypted_password: ost$name;
     VAR encrypted_password: avt$password;
     VAR status: ost$status);

{
{ NOTES:
{ 1.  This algorithm may be customized by any or all of the following methods. -
{
{     A.  Changing the value of the prime modulus, maintaining the correct range.
{
{     B.  Changing the values of the first two large non prime exponents.
{
{     C.  Changing the values of the large coefficients.
{
{   A table that may be used to determine values for the above factors can be found in:
{     Knuth, D. E. "The Art of Computer Programming Volume 2".
{
{ DESIGN:
{
{  This procedure takes as input a PASSWORD and USER_NAME.  These values are merged into
{  a single string of 62 characters which is then converted to a 48 bit integer by
{  folding the ASCII values of each character. The folding algorithm works as follows:
{
{  |-------|-------|-------|-------|-------|-------|  <-- 62-character string
{  1  add 12  sub 23  add 35  sub 46  add 58  sub 62
{
{  The integer value of each character in the string is multiplied by a power of 26 (0-11),
{  then added to or subtracted from the base depending on its position in the string.
{  The number 26 was chosen so that duplicate passwords cannot be easily generated using
{  letters of the alphabet. More values are added than subtracted so it is not easy for the
{  characters to cancel each other out, resulting in zero.
{
{  The resulting 48 bit FOLDED_RESULT is then
{  used as a factor in the following encryption algorithm.
{
{  The encryption algorithm used is a polynomial expansion, modulus a large prime
{  number, of the form -
{
{      ENCRYPTED_RESULT = SUM (COEFFICIENT(N) * FOLDED_RESULT**EXPONENT(N)) MOD PRIME
{
{      WHERE -
{              ENCRYPTED_RESULT         is the encrypted result
{              FOLDED_RESULT            is the unencrypted folded result from above
{              COEFFICIENT              is an array of integer expansion coefficients
{              EXPONENT                 is an array of integer expansion exponents
{              PRIME                    is a 48 bit prime integer
{              N                        index = 1..6
{
{      THUS  -
{              ENCRYPTED_RESULT will end up being a 48 bit integer or less.
{          The ENCRYPTED_PASSWORD is then returned as a string containing the decimal
{          representation of the ENCRYPTED_RESULT.
{
    CONST
      two_to_the_48th = 1000000000000(16);

    VAR
      base: integer,
      coefficient_table: array [1 .. 6] of integer,
      encrypted_result: integer,
      encryption_string: ost$string,
      exponent_table: array [1 .. 6] of integer,
      index: 1 .. 6,
      integer_factor: integer,
      length: integer,
      prime: integer,
      plus_or_minus_one: -1 .. 1,
      result: integer,
      string_index: integer;

    status.normal := TRUE;

    exponent_table [1] := 33554319; {(2**25) - 113}
    exponent_table [2] := 8388577; {(2**23) - 31}
    exponent_table [3] := 3;
    exponent_table [4] := 2;
    exponent_table [5] := 1;
    exponent_table [6] := 0;

    coefficient_table [1] := 1;
    coefficient_table [2] := 281474976710501; {2**48-155}
    coefficient_table [3] := 281474976710485; {2**48-171}
    coefficient_table [4] := 281474976710465; {2**48-191}
    coefficient_table [5] := 281474976710419; {2**48-237}
    coefficient_table [6] := 281474976710407; {2**48-249}

    prime := 281474976710509; {(2**48) - 147}
    base := 0;
    encrypted_result := 0;

    encryption_string.value := ' ';
    STRINGREP (encryption_string.value, length, unencrypted_password
          (1, clp$trimmed_string_size (unencrypted_password)),
          user_name (1, clp$trimmed_string_size (user_name)));
    encryption_string.size := length;

    integer_factor := 1;
    plus_or_minus_one := 1;
    FOR string_index := 1 TO encryption_string.size DO
      base := base + (plus_or_minus_one * (($INTEGER (encryption_string.value (string_index, 1)))
            * integer_factor));
      IF base < 0 THEN
        base := -base;
      IFEND;
      IF (string_index = 12) OR (string_index = 23) OR (string_index = 35) OR
            (string_index = 46) OR (string_index = 58) THEN
        integer_factor := 1;
        plus_or_minus_one := -plus_or_minus_one;
      ELSE
        integer_factor := integer_factor * 26;
      IFEND;
    FOREND;
    encryption_string.value := ' ';
    encryption_string.size := 0;

    base := base MOD two_to_the_48th;

    FOR index := 1 TO 6 DO

      avp$encrypt (base, exponent_table [index], coefficient_table [index], prime, result);
      encrypted_result := (encrypted_result + result) MOD prime;

    FOREND;

    STRINGREP (encrypted_password, length, encrypted_result: #SIZE (encrypted_password));

    base := 0;

  PROCEND avp$encrypt_password;
?? TITLE := '  [XDCL] avp$old_encrypt_password', EJECT ??

  PROCEDURE [XDCL] avp$old_encrypt_password
    (    user_name: ost$user_name;
         unencrypted_password: ost$name;
     VAR encrypted_password: avt$password;
     VAR status: ost$status);

{
{ NOTES:
{ 1.  This algorithm may be customized by any or all of the following methods. -
{
{     A.  Changing the value of the prime modulus, maintaining the correct range.
{
{     B.  Changing the values of the first two large non prime exponents.
{
{     C.  Changing the values of the large coefficients.
{
{   A table that may be used to determine values for the above factors can be found in:
{     Knuth, D. E. "The Art of Computer Programming Volume 2".
{
{ DESIGN:
{
{  This procedure takes as input a PASSWORD and USER_NAME.  These values are merged into
{  a single string of 62 characters which is then converted to a 48 bit integer by
{  folding the ASCII values of each character. The folding algorithm works as follows:
{
{  |-------|-------|-------|-------|-------|-------|  <-- 62-character string
{  1  add 12  sub 23  add 35  sub 46  add 58  sub 62
{
{  The integer value of each character in the string is multiplied by a power of 26 (0-11),
{  then added to or subtracted from the base depending on its position in the string.
{  The number 26 was chosen so that duplicate passwords cannot be easily generated using
{  letters of the alphabet. More values are added than subtracted so it is not easy for the
{  characters to cancel each other out, resulting in zero.
{
{  The resulting 48 bit FOLDED_RESULT is then
{  used as a factor in the following encryption algorithm.
{
{  The encryption algorithm used is a polynomial expansion, modulus a large prime
{  number, of the form -
{
{      ENCRYPTED_RESULT = SUM (COEFFICIENT(N) * FOLDED_RESULT**EXPONENT(N)) MOD PRIME
{
{      WHERE -
{              ENCRYPTED_RESULT         is the encrypted result
{              FOLDED_RESULT            is the unencrypted folded result from above
{              COEFFICIENT              is an array of integer expansion coefficients
{              EXPONENT                 is an array of integer expansion exponents
{              PRIME                    is a 48 bit prime integer
{              N                        index = 1..6
{
{      THUS  -
{              ENCRYPTED_RESULT will end up being a 48 bit integer or less.
{          The ENCRYPTED_PASSWORD is then returned as a string containing the decimal
{          representation of the ENCRYPTED_RESULT.
{
    CONST
      two_to_the_48th = 1000000000000(16);

    VAR
      base: integer,
      coefficient_table: array [1 .. 6] of integer,
      encrypted_result: integer,
      encryption_string: ost$string,
      exponent_table: array [1 .. 6] of integer,
      index: 1 .. 6,
      integer_factor: integer,
      length: integer,
      prime: integer,
      plus_or_minus_one: -1 .. 1,
      result: integer,
      string_index: integer;

    status.normal := TRUE;

    exponent_table [1] := 33554319; {(2**25) - 113}
    exponent_table [2] := 8388577; {(2**23) - 31}
    exponent_table [3] := 3;
    exponent_table [4] := 2;
    exponent_table [5] := 1;
    exponent_table [6] := 0;

    coefficient_table [1] := 1;
    coefficient_table [2] := 281474976710501; {2**48-155}
    coefficient_table [3] := 281474976710485; {2**48-171}
    coefficient_table [4] := 281474976710465; {2**48-191}
    coefficient_table [5] := 281474976710419; {2**48-237}
    coefficient_table [6] := 281474976710407; {2**48-249}

    prime := 281474976710509; {(2**48) - 147}
    base := 0;
    encrypted_result := 0;

    encryption_string.value := ' ';
    STRINGREP (encryption_string.value, length, unencrypted_password
          (1, clp$trimmed_string_size (unencrypted_password)),
          user_name (1, clp$trimmed_string_size (user_name)));
    encryption_string.size := length;

    integer_factor := 1;
    plus_or_minus_one := 1;
    FOR string_index := 1 TO encryption_string.size DO
      base := base + (plus_or_minus_one * (($INTEGER (encryption_string.value (string_index, 1)))
            * integer_factor));
      IF base < 0 THEN
        base := -base;
      IFEND;
      IF (string_index = 12) OR (string_index = 23) OR (string_index = 35) OR
            (string_index = 46) OR (string_index = 58) THEN
        integer_factor := 1;
        plus_or_minus_one := -plus_or_minus_one;
      ELSE
        integer_factor := integer_factor * 26;
      IFEND;
    FOREND;
    encryption_string.value := ' ';
    encryption_string.size := 0;

    base := base MOD two_to_the_48th;

    FOR index := 1 TO 6 DO

      avp$encrypt (base, exponent_table [index], coefficient_table [index], prime, result);
      encrypted_result := (encrypted_result + result) MOD prime;

    FOREND;

    STRINGREP (encrypted_password, length, encrypted_result: #SIZE (encrypted_password));

    base := 0;

  PROCEND avp$old_encrypt_password;
?? OLDTITLE, EJECT ??

{ Here is the previous version of avp$old_encrypt_password.

{ PROCEDURE [XDCL] avp$old_encrypt_password
{   (    user_name: ost$user_name;
{        unencrypted_password: ost$name;
{    VAR encrypted_password: avt$password;
{    VAR status: ost$status);
{
{
{ NOTES:
{ 1.  This algorithm may be customized by any or all of the following methods. -
{
{     A.  Changing the value of the prime modulus, maintaining the correct range.
{
{     B.  Changing the values of the first two large non prime exponents.
{
{     C.  Changing the values of the large coefficients.
{
{   A table that may be used to determine values for the above factors can be found in:
{     Knuth, D. E. "The Art of Computer Programming Volume 2".
{
{ DESIGN:
{
{  This procedure takes as input a PASSWORD and USER_NAME.  These values are merged into
{  a single string of 62 characters which is then converted to a 48 bit integer by
{  folding the ascii values of each character. The resulting 48 bit FOLDED_RESULT is then
{  used as a factor in the following encryption algorithm.
{
{  The encryption algorithm used is a polynomial expansion, modulus a large prime
{  number, of the form -
{
{      ENCRYPTED_RESULT = SUM (COEFFICIENT(N) * FOLDED_RESULT**EXPONENT(N)) MOD PRIME
{
{      WHERE -
{              ENCRYPTED_RESULT         is the encrypted result
{              FOLDED_RESULT            is the unecrypted folded result from above
{              COEFFICIENT              is an array of integer expansion coefficients
{              EXPONENT                 is an array of integer expansion exponents
{              PRIME                    is a 48 bit prime integer
{              N                        index = 1..6
{
{      THUS  -
{              ENCRYPTED_RESULT will end up being a 48 bit integer or less.
{          The ENCRYPTED_PASSWORD is then returned as a string containing the decimal
{          representation of the ENCRYPTED_RESULT.
{
{   CONST
{     two_to_the_48th = 1000000000000(16);
{
{   VAR
{     base: integer,
{     coefficient_table: array [1 .. 6] of integer,
{     encrypted_result: integer,
{     encryption_string: ost$string,
{     exponent_table: array [1 .. 6] of integer,
{     factor_counter: 1 .. 7,
{     index: 1 .. 6,
{     integer_factor: integer,
{     length: integer,
{     prime: integer,
{     result: integer,
{     string_index: integer;
{
{   status.normal := TRUE;
{
{   exponent_table [1] := 33554319; {(2**25) - 113}
{   exponent_table [2] := 8388577; {(2**23) - 31}
{   exponent_table [3] := 3;
{   exponent_table [4] := 2;
{   exponent_table [5] := 1;
{   exponent_table [6] := 0;
{
{   coefficient_table [1] := 1;
{   coefficient_table [2] := 281474976710501; {2**48-155}
{   coefficient_table [3] := 281474976710485; {2**48-171}
{   coefficient_table [4] := 281474976710465; {2**48-191}
{   coefficient_table [5] := 281474976710419; {2**48-237}
{   coefficient_table [6] := 281474976710407; {2**48-249}
{
{   prime := 281474976710509; {(2**48) - 147}
{   base := 0;
{   encrypted_result := 0;
{
{   encryption_string.value := ' ';
{   STRINGREP (encryption_string.value, length, unencrypted_password
{         (1, clp$trimmed_string_size (unencrypted_password)),
{         user_name (1, clp$trimmed_string_size (user_name)));
{   encryption_string.size := length;
{
{   integer_factor := 1;
{   factor_counter := 1;
{   FOR string_index := 1 TO encryption_string.size DO
{     base := base + (($INTEGER (encryption_string.value (string_index, 1))) * integer_factor);
{     IF factor_counter < 6 THEN
{       integer_factor := integer_factor * 256;
{       factor_counter := factor_counter + 1;
{     ELSE
{       integer_factor := 1;
{       factor_counter := 1;
{     IFEND;
{   FOREND;
{   encryption_string.value := ' ';
{   encryption_string.size := 0;
{
{   base := base MOD two_to_the_48th;
{
{   FOR index := 1 TO 6 DO
{
{     avp$encrypt (base, exponent_table [index], coefficient_table [index], prime, result);
{     encrypted_result := (encrypted_result + result) MOD prime;
{
{   FOREND;
{
{   STRINGREP (encrypted_password, length, encrypted_result: #SIZE (encrypted_password));
{
{   base := 0;
{
{ PROCEND avp$old_encrypt_password;

MODEND avm$encrypt_password;
