(**************************************************************************)
(*                                                                        *)
(*  Encryption library                                                    *)
(*  Copyright (C) 2018   Peter Moylan                                     *)
(*                                                                        *)
(*  This program is free software: you can redistribute it and/or modify  *)
(*  it under the terms of the GNU General Public License as published by  *)
(*  the Free Software Foundation, either version 3 of the License, or     *)
(*  (at your option) any later version.                                   *)
(*                                                                        *)
(*  This program is distributed in the hope that it will be useful,       *)
(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
(*  GNU General Public License for more details.                          *)
(*                                                                        *)
(*  You should have received a copy of the GNU General Public License     *)
(*  along with this program.  If not, see <http://www.gnu.org/licenses/>. *)
(*                                                                        *)
(*  To contact author:   http://www.pmoylan.org   peter@pmoylan.org       *)
(*                                                                        *)
(**************************************************************************)

<* WOFF316+ *>

IMPLEMENTATION MODULE RC4;

        (********************************************************)
        (*                                                      *)
        (*                  STREAM CIPHER RC4                   *)
        (*                                                      *)
        (*   This is now deprecated as an encryption method,    *)
        (*   because of known attacks, but it is included in    *)
        (*          this package for completeness.              *)
        (*                                                      *)
        (*  Programmer:         P. Moylan                       *)
        (*  Started:            17 April 2018                   *)
        (*  Last edited:        18 April 2018                   *)
        (*  Status:             Passes all tests                *)
        (*                                                      *)
        (********************************************************)

(************************************************************************)
(*                                                                      *)
(* The algorithm used in this module is based on the information in     *)
(*              https://en.wikipedia.org/wiki/RC4                       *)
(*                                                                      *)
(************************************************************************)

FROM SYSTEM IMPORT LOC, CARD8, CAST;

FROM LowLevel IMPORT
    (* proc *)  IXORB, BlockFill, EVAL;

FROM Storage IMPORT
    (* proc *)  ALLOCATE, DEALLOCATE;

(********************************************************************************)

TYPE
    StateArray = ARRAY [0..255] OF CARD8;

    StateRecord =   RECORD
                        i, j: CARD8;
                        S: StateArray;
                    END (*RECORD*);

    RC4state = POINTER TO StateRecord;

(********************************************************************************)
(*                            MISCELLANEOUS UTILITIES                           *)
(********************************************************************************)

PROCEDURE Swap2 (VAR (*INOUT*) v1, v2: CARD8);

    (* Swaps two CARD8 values.  *)

    VAR temp: CARD8;

    BEGIN
        temp := v1;  v1 := v2;  v2 := temp;
    END Swap2;

(********************************************************************************)
(*                           KEY-SCHEDULING ALGORITHM                           *)
(********************************************************************************)

PROCEDURE KSA (state: RC4state;  key: ARRAY OF LOC;  keylength: CARDINAL);

    (* Creates an initial state, a permutation of the range 0..255.  *)

    VAR i, j, keyval: CARD8;

    BEGIN
        FOR i := 0 TO 255 DO
            state^.S[i] := i;
        END (*FOR*);
        j := 0;
        FOR i := 0 TO 255 DO
            keyval := CAST (CARD8, key[i MOD keylength]);
            j := (j + state^.S[i] + keyval) MOD 256;
            Swap2 (state^.S[i],  state^.S[j]);
        END (*FOR*);
    END KSA;

(********************************************************************************)
(*                     PSEUDO-RANDOM GENERATION ALGORITHM                       *)
(********************************************************************************)

PROCEDURE PRGA (state: RC4state): CARD8;

    (* Produces the next pseudorandom number to be used in producing a result. *)

    VAR K: CARD8;

    BEGIN
        WITH state^ DO
            i := (i + 1) MOD 256;
            j := (j + S[i]) MOD 256;
            Swap2 (S[i], S[j]);
            K := S[(S[i] + S[j]) MOD 256];
            RETURN K;
        END (*WITH*);
    END PRGA;

(********************************************************************************)

PROCEDURE Encrypt (state: RC4state;  length: CARDINAL;
                                        VAR (*IN*) input: ARRAY OF LOC;
                                        VAR (*OUT*) output: ARRAY OF LOC);

    (* Transforms input to output.  This same procedure is used either for  *)
    (* encryption or decryption.                                            *)

    VAR k, ival: CARDINAL;

    BEGIN
        FOR k := 0 TO length - 1 DO
            ival := CAST(CARD8, input[k]);
            output[k] := CAST(LOC, IXORB (ival, PRGA(state)));
        END (*FOR*);
    END Encrypt;

(********************************************************************************)

PROCEDURE RC4Init (offset: CARDINAL;  key: ARRAY OF LOC;  keylength: CARDINAL)
                                                                       : RC4state;

    (* Starts a new encryption or decryption operation.  The offset is the  *)
    (* initial number of bytes we discard.                                  *)

    VAR state: RC4state;  j: CARDINAL;

    BEGIN
        NEW (state);
        KSA (state, key, keylength);
        state^.i := 0;
        state^.j := 0;
        IF offset > 0 THEN
            FOR j := 0 TO offset-1 DO
                EVAL (PRGA(state));
            END (*FOR*);
        END (*IF*);
        RETURN state;
    END RC4Init;

(********************************************************************************)

PROCEDURE RC4Close (VAR (*INOUT*) state: RC4state);

    (* Terminates the operation. *)

    BEGIN
        BlockFill (state, SIZE(StateRecord), 0);
        DISPOSE (state);
    END RC4Close;

(********************************************************************************)

END RC4.

