(* Ulm's Oberon Library Copyright (C) 1989-1997 by University of Ulm, SAI, D-89069 Ulm, Germany ---------------------------------------------------------------------------- Ulm's Oberon Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Ulm's Oberon Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ---------------------------------------------------------------------------- E-mail contact: oberon@mathematik.uni-ulm.de ---------------------------------------------------------------------------- $Id: TCrypt.om,v 1.1 1997/04/02 11:54:02 borchert Exp borchert $ ---------------------------------------------------------------------------- $Log: TCrypt.om,v $ Revision 1.1 1997/04/02 11:54:02 borchert Initial revision ---------------------------------------------------------------------------- *) MODULE ulmTCrypt; (* Michael Szczuka *) (* Trautner's association method for key exchange *) IMPORT AsymmetricCiphers := ulmAsymmetricCiphers, BlockCiphers := ulmBlockCiphers, Ciphers := ulmCiphers, Conclusions := ulmConclusions, Events := ulmEvents, NetIO := ulmNetIO, PersistentObjects := ulmPersistentObjects, Random := ulmRandomGenerators, RelatedEvents := ulmRelatedEvents, Services := ulmServices, Streams := ulmStreams, SYS := SYSTEM, Types := ulmTypes; CONST M = 16; (* size of an element of CC(M) [ring of Circular Convolution] *) MaxVar = 8; (* number of variables of a polynomial *) MaxNrExp = 4; (* maxiumum number of different exponts used during initialisaton *) Dim = 2; (* dimension of the linear recursion *) Rounds = 16; (* length of the linear recursion in rounds *) LastRounds = 4; (* use the last LastRounds polynomial vectors as the composed function eta *) reg = 1; sing = 2; random = 3; LIST = TRUE; NOLIST = FALSE; MaxTerms = 1000; CONST writeSetFailed = 0; readSetFailed = 1; notRegular = 2; errorcodes = 3; TYPE (* an element out of CC(M) *) CCMElement = Types.Set; Exponent = ARRAY MaxVar OF Types.Int8; TYPE (* a polynomial with coefficients out of CC(M) *) Polynom = POINTER TO PolynomRec; PolynomRec = RECORD koeff : CCMElement; exp : Exponent; next : Polynom; END; TYPE VektorCCM = ARRAY Dim OF CCMElement; VektorPolynom = ARRAY Dim OF Polynom; MatCCM = ARRAY Dim, Dim OF CCMElement; MatPolynom = ARRAY Dim, Dim OF Polynom; ListCCM = ARRAY Rounds OF CCMElement; ListPolynom = ARRAY Rounds OF Polynom; ChainCCM = ARRAY Rounds OF VektorCCM; ChainPolynom = ARRAY Rounds OF VektorPolynom; (* to increase the performance of the algorithm there shouldn't be too many different exponents to start with *) ListExp = ARRAY MaxNrExp OF Exponent; TYPE (* this type is the input of the TCrypt method *) TCryptInput = POINTER TO TCryptInputRec; TCryptInputRec = RECORD arg : ARRAY MaxVar OF CCMElement; END; TYPE (* result type after encryption with the public key *) TCryptTmp = POINTER TO TCryptTmpRec; TCryptTmpRec = RECORD numerator : ChainCCM; denominator : ListCCM; END; TYPE (* result type of the algorithm *) TCryptRes = POINTER TO TCryptResRec; TCryptResRec = RECORD arg : ARRAY LastRounds OF VektorCCM; END; TYPE (* this type represents the public function f resp. phi *) Phi = POINTER TO PhiRec; PhiRec = RECORD num : ChainPolynom; denom : ListPolynom; END; TYPE (* the private/secret function g resp. psi consisting of an inital matrix and a permutation *) Psi = POINTER TO PsiRec; PsiRec = RECORD (* although the inital matrix consists only of elements out of CC(M) this generalization is useful since all other matrces consist of polynomials *) initialmatrix : MatCCM; (* correcting factors *) korrNum : ChainCCM; korrDenom : ListCCM; END; (* the public function h resp. eta being the composition of f/phi and g/psi *) TYPE Eta = POINTER TO EtaRec; EtaRec = RECORD p : ARRAY LastRounds OF VektorPolynom; END; TYPE (* the declaration of a basic type which PublicCipher and PrivateCipher are descendents from seems a good idea ... at least to me :) *) Cipher* = POINTER TO CipherRec; CipherRec* = RECORD (AsymmetricCiphers.CipherRec) END; (* the specific format of a public key for Trautner's technique *) PublicCipher = POINTER TO PublicCipherRec; PublicCipherRec = RECORD (CipherRec) phi : Phi; eta : Eta; END; (* the specific format of a key for Trautner's technique *) PrivateCipher = POINTER TO PrivateCipherRec; PrivateCipherRec = RECORD (CipherRec) phi : Phi; psi : Psi; eta : Eta; END; TYPE ErrorEvent = POINTER TO ErrorEventRec; ErrorEventRec = RECORD (Events.EventRec) errorcode : Types.Int8; END; VAR pubType, privType, cipherType : Services.Type; pubIf, privIf, cipherIf : PersistentObjects.Interface; NullCCM, EinsCCM : CCMElement; (* the zero and unit of CC(M) *) NullExp : Exponent; (* consists of zero exponents *) NullExpList : ListExp; (* a pseudo list for CreatePolynom *) GlobalExpList : ListExp; (* contains the exponents which should be used when calling CreatePolynom *) NullPolynom : Polynom; (* the zero polynomial *) PolFeld : ARRAY MaxTerms OF Polynom; (* used for sorting purposes *) PreEvalArg : ARRAY M OF TCryptInput; (* precomputed values to speed up evaluation of a polynomial *) k : Types.Int8; (* simple counter during initialisation *) error : Events.EventType; errormsg : ARRAY errorcodes OF Events.Message; (* ***** error handling ***** *) PROCEDURE InitErrorHandling; BEGIN Events.Define(error); errormsg[writeSetFailed] := "couldn't write set"; errormsg[readSetFailed] := "couldn't read set"; errormsg[notRegular] := "element isn't regular"; END InitErrorHandling; PROCEDURE Error(s: Streams.Stream; errorcode: Types.Int8); VAR event: ErrorEvent; BEGIN NEW(event); event.message := errormsg[errorcode]; event.type := error; event.errorcode := errorcode; RelatedEvents.Raise(s, event); END Error; (* ***** arithmetic functions for elements out of CC(M) ***** *) PROCEDURE RegulaerCCM (x: CCMElement) : BOOLEAN; (* tests x for regularity [a regular CCMElement contains an odd number of set bits]; returns TRUE when x is regular, FALSE otherwise *) VAR res, i : Types.Int8; BEGIN i := 0; res := 0; REPEAT (* counting the set bits *) IF i IN x THEN INC(res); END; INC(i); UNTIL i>=M; RETURN ((res MOD 2) = 1); END RegulaerCCM; PROCEDURE EqualCCM (x, y: CCMElement) : BOOLEAN; (* compares x and y for equality; if x and y are equal TRUE is returned, FALSE otherwise *) VAR i : Types.Int8; BEGIN i := 0; WHILE i < M DO IF ((i IN x) & (~(i IN y))) OR ((~(i IN x)) & (i IN y)) THEN RETURN FALSE; END; INC(i); END; RETURN TRUE; END EqualCCM; PROCEDURE AddCCM (x, y: CCMElement; VAR z: CCMElement); (* add x and y in CC(M) *) VAR i : Types.Int8; BEGIN z := NullCCM; i := 0; REPEAT IF ((i IN x) & (~(i IN y))) OR ((~(i IN x)) & (i IN y)) THEN z := z + {i}; END; INC(i); UNTIL i>=M; END AddCCM; PROCEDURE MulCCM (x, y: CCMElement; VAR z: CCMElement); (* multiply x and y in CC(M) *) VAR i, j, diff : Types.Int8; tmp : Types.Int32; BEGIN z := NullCCM; i := 0; REPEAT j := 0; tmp := 0; REPEAT diff := i-j; IF diff >= 0 THEN IF (j IN x) & (diff IN y) THEN INC(tmp); END; ELSE IF (j IN x) & ((M+diff) IN y) THEN INC(tmp); END; END; INC(j); UNTIL j>=M; IF (tmp MOD 2) = 1 THEN z := z + {i}; END; INC(i); UNTIL i>=M; END MulCCM; PROCEDURE PowerCCM (x: CCMElement; exp: Types.Int32; VAR z: CCMElement); (* raises x to the power exp in CC(M) *) VAR tmp : CCMElement; BEGIN (* some special cases first *) IF exp >= M THEN IF ~RegulaerCCM(x) THEN (* x is singular -> result is zero *) z := NullCCM; RETURN; END; (* x is regular -> compute the modulus of exp mod M and use this instead of exp *) exp := exp MOD M; END; IF exp = 0 THEN z := EinsCCM; RETURN; END; IF exp = 1 THEN z := x; RETURN; END; (* default case; use a "square and multiply" technique *) tmp := x; z := EinsCCM; REPEAT IF exp MOD 2 = 1 THEN MulCCM(z, tmp, z); END; exp := exp DIV 2; MulCCM(tmp, tmp, tmp); UNTIL exp < 1; END PowerCCM; PROCEDURE CreateCCM (VAR x: CCMElement; mode: Types.Int8); (* creates a random element out of CC(M) depending on mode which can be reg, sing or random; the result is in any case different from the zero *) VAR i, SetBits: Types.Int8; BEGIN x := NullCCM; REPEAT i := 0; SetBits := 0; REPEAT IF Random.Flip() THEN (* set bit *) x := x + {i}; INC(SetBits); END; INC(i); UNTIL i >= (M-1); UNTIL SetBits > 0; (* at least one bit must be set so that the result differs from zero *) CASE mode OF random: IF Random.Flip() THEN x := x + {M-1}; END; | sing: (* singular element - even # of bits *) IF (SetBits MOD 2) = 1 THEN x := x + {M-1}; END; | reg: (* regular element - odd # of bits *) IF ((SetBits + 1) MOD 2) = 1 THEN x := x + {M-1}; END; ELSE END; END CreateCCM; (* ***** arithmetic functions for polynomials over CC(M) ***** *) PROCEDURE LengthPolynom(p: Polynom) : Types.Int32; (* returns the number of terms which make up the polynomial p *) VAR i : Types.Int32; BEGIN i := 0; WHILE p # NIL DO INC(i); p := p.next; END; RETURN i; END LengthPolynom; PROCEDURE RegulaerPolynom (p: Polynom) : BOOLEAN; (* tests the regularity of a polynomial [a polynomial is regular iff the # of regular coefficients is odd] *) VAR regkoeffs : Types.Int8; BEGIN regkoeffs := 0; WHILE p # NIL DO IF RegulaerCCM(p.koeff) THEN (* count # of reg. coefficients *) INC(regkoeffs); END; p := p.next; END; RETURN (regkoeffs MOD 2) = 1; END RegulaerPolynom; PROCEDURE CmpExp (exp1, exp2: Exponent) : Types.Int8; (* compares two exponent vectors and returns 0 on equality, a positive value if exp1>exp2 and a negative value if exp1 e2 THEN cmp := 1; diff := TRUE; END; END; INC(i); UNTIL i >= MaxVar; IF sum1 < sum2 THEN RETURN -2; END; IF sum1 > sum2 THEN RETURN 2; END; RETURN cmp END CmpExp; PROCEDURE ArrangePolynom (VAR p: Polynom); (* arrange a polynomial according to the order given by CmpExp *) VAR r : Polynom; cnt : Types.Int32; PROCEDURE SortPolynom(left, right: Types.Int32); (* sort the global field PolFeld with the quicksort algorithm *) VAR mid : Types.Int32; PROCEDURE Partition(l, r: Types.Int32) : Types.Int32; VAR koeff : CCMElement; exp : Exponent; cmp : Exponent; i, j : Types.Int32; BEGIN cmp := PolFeld[(l+r) DIV 2].exp; i := l-1; j := r+1; LOOP REPEAT DEC(j); UNTIL CmpExp(PolFeld[j].exp, cmp) >= 0; REPEAT INC(i); UNTIL CmpExp(PolFeld[i].exp, cmp) <= 0; IF i < j THEN koeff := PolFeld[i].koeff; exp := PolFeld[i].exp; PolFeld[i].koeff := PolFeld[j].koeff; PolFeld[i].exp := PolFeld[j].exp; PolFeld[j].koeff := koeff; PolFeld[j].exp := exp; ELSE RETURN j; END; END; END Partition; BEGIN IF left < right THEN mid := Partition(left, right); SortPolynom(left, mid); SortPolynom(mid+1, right); END; END SortPolynom; BEGIN (* ArrangePolynom *) IF p = NIL THEN RETURN; END; r := p; cnt := 0; WHILE (p # NIL) & (cnt < MaxTerms) DO PolFeld[cnt] := p; INC(cnt); p := p.next; END; (* polynomial contains too many terms; this shouldn't happen if all parameters are set to reasonable values and MaxTerms is high enough *) ASSERT(cnt 1 THEN SortPolynom(0, cnt-1); END; p := r; END ArrangePolynom; PROCEDURE CopyPolynom (s: Polynom; VAR t: Polynom); (* copy the source polynomial s to a new target t *) VAR troot : Polynom; BEGIN IF s = NIL THEN t := NIL; RETURN; END; NEW(t); troot := t; (* save the root of t *) WHILE s # NIL DO troot.koeff := s.koeff; troot.exp := s.exp; s := s.next; IF s # NIL THEN NEW(troot.next); troot := troot.next; ELSE troot.next := NIL; END; END; END CopyPolynom; PROCEDURE AddPolynom (p, q: Polynom; VAR r: Polynom); (* add two polynomial; the polynomials must be sorted by the exponents as is the result *) VAR term1, term2 : Polynom; last : Polynom; (* the last term of the result *) tmp : Polynom; cmpres : Types.Int8; BEGIN IF (p = NIL) & (q = NIL) THEN r := NIL; RETURN; END; NEW(r); term1 := p; (* term1 runs through all terms of p *) term2 := q; (* same with term2 for q *) tmp := r; (* save the root of r *) last := tmp; REPEAT IF (term1 = NIL) OR (term2 = NIL) THEN IF term2 = NIL THEN (* no further terms in q *) WHILE term1 # NIL DO (* copy the remaining terms of p *) tmp.koeff := term1.koeff; tmp.exp := term1.exp; term1 := term1.next; IF ~EqualCCM(tmp.koeff, NullCCM) THEN last := tmp; NEW(tmp.next); tmp := tmp.next; END; END; ELSE (* no further terms in p *) WHILE term2 # NIL DO tmp.koeff := term2.koeff; tmp.exp := term2.exp; term2 := term2.next; IF ~EqualCCM(tmp.koeff, NullCCM) THEN last := tmp; NEW(tmp.next); tmp := tmp.next; END; END; END; ELSE (* both p and q still have a term *) cmpres := CmpExp(term1.exp, term2.exp); IF cmpres = 0 THEN (* add when exponents are equal *) AddCCM(term1.koeff, term2.koeff, tmp.koeff); tmp.exp := term1.exp; term1 := term1.next; term2 := term2.next; ELSE IF cmpres < 0 THEN (* exp2 > exp1 *) tmp.koeff := term2.koeff; tmp.exp := term2.exp; term2 := term2.next; ELSE (* exp1 > exp2 *) tmp.koeff := term1.koeff; tmp.exp := term1.exp; term1 := term1.next; END; END; (* zero coefficients = zero terms shouldn't occur in the result *) IF ~EqualCCM(tmp.koeff, NullCCM) THEN NEW(tmp.next); last := tmp; tmp := tmp.next; END; END; UNTIL (term1 = NIL) & (term2 = NIL); (* forget last created term *) last.next := NIL; END AddPolynom; PROCEDURE MulTerm (p, term: Polynom; VAR r: Polynom); (* multiply a polynomial with a single term; is used by MulPolynom *) VAR tmp : Polynom; last : Polynom; (* add two exponent vetors; addition is modulo M *) PROCEDURE AddExp (exp1, exp2 : Exponent; VAR res: Exponent); VAR i : Types.Int8; BEGIN i := 0; WHILE i 0 DO IF (exp MOD 2) = 1 THEN MulPolynom(res, tmp, res); END; MulPolynom(tmp, tmp, tmp); exp := exp DIV 2; END; END InvertPolynom; PROCEDURE EvalPolynom (p: Polynom; VAR res: CCMElement); (* evaluate p; a precomputed list of all the powers of the argument can be found in the global variable PreEvalArg *) VAR i : Types.Int8; pow, prod : CCMElement; BEGIN res := NullCCM; IF p = NIL THEN RETURN; END; WHILE p # NIL DO prod := PreEvalArg[p.exp[0]].arg[0]; i := 1; REPEAT pow := PreEvalArg[p.exp[i]].arg[i]; MulCCM(prod, pow, prod); INC(i); UNTIL i >= MaxVar; MulCCM(prod, p.koeff, prod); AddCCM(res, prod, res); p := p.next; END; END EvalPolynom; PROCEDURE CreateExp (VAR exp: Exponent); (* creates a random vector of exponents *) VAR i : Types.Int8; BEGIN i := 0; WHILE i 0 DO IF (kk MOD 2) = 1 THEN MulCCM(tmp, PreEvalArg[ii].arg[i], tmp); END; INC(ii,ii); kk := kk DIV 2; END; PreEvalArg[k].arg[i] := tmp; INC(k); END; INC(i); END; END PreComputeArgs; PROCEDURE EvaluatePhi (arg: TCryptInput; data: Phi) : TCryptTmp; (* evaluate the public function phi (represented by data) with argument arg *) VAR res : TCryptTmp; r, d : Types.Int8; BEGIN NEW(res); PreComputeArgs(arg); r := 0; WHILE r < Rounds DO d := 0; WHILE d < Dim DO EvalPolynom(data.num[r][d], res.numerator[r][d]); INC(d); END; EvalPolynom(data.denom[r], res.denominator[r]); INC(r); END; RETURN res; END EvaluatePhi; PROCEDURE EvaluatePsi (arg: TCryptTmp; data: Psi) : TCryptRes; (* evalute the private function psi *) VAR res : TCryptRes; mat, prev : MatCCM; num, denom, inv : CCMElement; vek : VektorCCM; A : ChainCCM; r, d : Types.Int8; BEGIN (* first correct the input with the correlating inverts *) MulCCM(arg.denominator[0], data.korrDenom[0], denom); PowerCCM(denom, M-1, inv); MulCCM(arg.numerator[0][0], data.korrNum[0][0], num); MulCCM(num, inv, vek[0]); MulCCM(arg.numerator[0][1], data.korrNum[0][1], num); MulCCM(num, inv, vek[1]); MulMatrix(data.initialmatrix, vek, A[0]); prev := data.initialmatrix; r := 1; WHILE r < Rounds DO (* the matrix for the current round of the recursion must be computed each round *) BuildMatrix(mat, prev, A[r-1]); prev := mat; MulCCM(arg.denominator[r], data.korrDenom[r], denom); PowerCCM(denom, M-1, inv); MulCCM(arg.numerator[r][0], data.korrNum[r][0], num); MulCCM(num, inv, vek[0]); MulCCM(arg.numerator[r][1], data.korrNum[r][1], num); MulCCM(num, inv, vek[1]); MulMatrix(mat, vek, A[r]); INC(r); END; NEW(res); r := 0; WHILE r < LastRounds DO d := 0; WHILE d < Dim DO res.arg[r][d] := A[Rounds-LastRounds+r][d]; INC(d); END; INC(r); END; RETURN res; END EvaluatePsi; PROCEDURE EvaluateEta (arg: TCryptInput; data: Eta) : TCryptRes; (* evaluate the public function eta (composition of phi and psi) *) VAR l, d : Types.Int8; res : TCryptRes; BEGIN NEW(res); PreComputeArgs(arg); l := 0; WHILE l < LastRounds DO d := 0; WHILE d < Dim DO EvalPolynom(data.p[l][d], res.arg[l][d]); INC(d); END; INC(l); END; RETURN res; END EvaluateEta; PROCEDURE Eof (s: Streams.Stream) : BOOLEAN; (* returns TRUE if no bytes are left to read from stream s *) VAR b : SYS.BYTE; BEGIN RETURN ~Streams.ReadByte(s, b) OR ~Streams.Back(s); END Eof; PROCEDURE Encrypt (msg: Streams.Stream; key: Ciphers.Cipher; length: Types.Int32; s: Streams.Stream) : BOOLEAN; (* interface procedure for Ciphers.Encrypt *) VAR i, j : Types.Int8; ccmarg : TCryptInput; ccmres : TCryptTmp; wholeStream : BOOLEAN; BEGIN (* check if the whole stream msg shall be encrypted or only a certain amount of bytes *) IF length <= 0 THEN wholeStream := TRUE; ELSE wholeStream := FALSE END; NEW(ccmarg); WHILE ~Eof(msg) & (wholeStream OR (length > 0)) DO i := 0; WHILE i < MaxVar DO IF ~NetIO.ReadSet(msg, ccmarg.arg[i]) THEN Error(msg, readSetFailed); RETURN FALSE; END; IF ~RegulaerCCM(ccmarg.arg[i]) THEN Error(msg, notRegular); RETURN FALSE; END; INC(i); END; IF key IS PublicCipher THEN ccmres := EvaluatePhi(ccmarg, key(PublicCipher).phi); ELSE ccmres := EvaluatePhi(ccmarg, key(PrivateCipher).phi); END; i := 0; WHILE i < Rounds DO j := 0; WHILE j < Dim DO IF ~NetIO.WriteSet(s, ccmres.numerator[i][j]) THEN Error(s, writeSetFailed); RETURN FALSE; END; INC(j); END; IF ~NetIO.WriteSet(s, ccmres.denominator[i]) THEN Error(s, writeSetFailed); RETURN FALSE; END; INC(i); END; DEC(length, MaxVar*(M DIV 8)); END; RETURN TRUE; END Encrypt; PROCEDURE Decrypt (msg: Streams.Stream; key: Ciphers.Cipher; length: Types.Int32; s: Streams.Stream) : BOOLEAN; (* interface procedure for Ciphers.Decrypt *) VAR i, j : Types.Int8; inNum, inDenom, out : ARRAY (M DIV 8) OF SYS.BYTE; ccmarg : TCryptTmp; ccmres : TCryptRes; wholeStream : BOOLEAN; BEGIN IF length < 0 THEN wholeStream := TRUE; ELSE wholeStream := FALSE; END; WITH key:PrivateCipher DO NEW(ccmarg); WHILE ~Eof(msg) & (wholeStream OR (length > 0)) DO i := 0; WHILE i < Rounds DO j := 0; WHILE j < Dim DO IF ~NetIO.ReadSet(msg, ccmarg.numerator[i][j]) THEN Error(msg, readSetFailed); RETURN FALSE; END; INC(j); END; IF ~NetIO.ReadSet(msg, ccmarg.denominator[i]) THEN Error(msg, readSetFailed); RETURN FALSE; END; INC(i); END; ccmres := EvaluatePsi(ccmarg, key.psi); i := 0; WHILE i < LastRounds DO j := 0; WHILE j < Dim DO IF ~NetIO.WriteSet(s, ccmres.arg[i][j]) THEN Error(s, writeSetFailed); RETURN FALSE; END; INC(j); END; INC(i); END; DEC (length, Rounds*Dim*(M DIV 8)); END; END; RETURN TRUE; END Decrypt; PROCEDURE ComposedEncrypt (msg: Streams.Stream; key: Ciphers.Cipher; length: Types.Int32; s: Streams.Stream) : BOOLEAN; (* interface procedure for AsymmetricCiphers.ComposedEncrypt *) VAR i, j : Types.Int8; ccmarg : TCryptInput; ccmres : TCryptRes; in, out : ARRAY (M DIV 8) OF SYS.BYTE; wholeStream : BOOLEAN; BEGIN IF length < 0 THEN wholeStream := TRUE; ELSE wholeStream := FALSE; END; NEW(ccmarg); WHILE ~Eof(msg) & (wholeStream OR (length > 0)) DO i := 0; WHILE i < MaxVar DO IF ~NetIO.ReadSet(msg, ccmarg.arg[i]) THEN Error(msg, readSetFailed); RETURN FALSE; END; INC(i); END; IF key IS PublicCipher THEN ccmres := EvaluateEta(ccmarg, key(PublicCipher).eta); ELSE ccmres := EvaluateEta(ccmarg, key(PrivateCipher).eta); END; i := 0; WHILE i < LastRounds DO j := 0; WHILE j < Dim DO IF ~NetIO.WriteSet(s, ccmres.arg[i][j]) THEN Error(s, writeSetFailed); RETURN FALSE; END; INC(j); END; INC(i); END; DEC (length, MaxVar*(M DIV 8)); END; RETURN TRUE; END ComposedEncrypt; PROCEDURE RandomStream (s: Streams.Stream); (* writes some random elements of CC(M) to the stream s which can then be used as an input for Trautner's TCRYPT *) VAR ccm : CCMElement; bytes : ARRAY M DIV 8 OF SYS.BYTE; i : Types.Int32; BEGIN i := 0; WHILE i < MaxVar DO CreateCCM(ccm, reg); IF ~NetIO.WriteSet(s, ccm) THEN Error(s, writeSetFailed); END; INC(i); END; END RandomStream; PROCEDURE PublicCipherCreate (VAR obj: PersistentObjects.Object); (* constructor for a public cipher *) VAR pub : PublicCipher; if : AsymmetricCiphers.Interface; caps : AsymmetricCiphers.CapabilitySet; BEGIN NEW(pub); NEW(pub.phi); NEW(pub.eta); PersistentObjects.Init(pub, pubType); NEW(if); if.encrypt := Encrypt; if.decrypt := NIL; if.compencrypt := ComposedEncrypt; if.split := NIL; if.randomStream := RandomStream; caps := {AsymmetricCiphers.composed}; AsymmetricCiphers.Init(pub, if, caps, M*MaxVar, M*Dim); obj := pub; END PublicCipherCreate; PROCEDURE Split (VAR public: AsymmetricCiphers.Cipher; key: AsymmetricCiphers.Cipher); (* interface procedure for asymmetric interface *) VAR pub: PublicCipher; obj: PersistentObjects.Object; BEGIN WITH key:PrivateCipher DO PublicCipherCreate(obj); pub := obj(PublicCipher); pub.phi := key.phi; pub.eta := key.eta; public := pub; END; END Split; PROCEDURE CipherCreate (VAR obj: PersistentObjects.Object); (* constructor for a private cipher *) VAR key : PrivateCipher; if : AsymmetricCiphers.Interface; caps : AsymmetricCiphers.CapabilitySet; BEGIN NEW(key); NEW(key.phi); NEW(key.psi); NEW(key.eta); PersistentObjects.Init(key, privType); NEW(if); if.encrypt := Encrypt; if.decrypt := Decrypt; if.compencrypt := ComposedEncrypt; if.split := Split; if.randomStream := RandomStream; caps := {AsymmetricCiphers.composed, AsymmetricCiphers.isPrivateKey}; AsymmetricCiphers.Init(key, if, caps, M*MaxVar, M*Dim); obj := key; END CipherCreate; PROCEDURE Create* (VAR key: Ciphers.Cipher); (* creates a cipher for the use with Trautner's TCRYPT algorithm *) VAR tmpKey : PrivateCipher; obj : PersistentObjects.Object; phi : Phi; psi : Psi; eta : Eta; BEGIN CipherCreate(obj); tmpKey := obj(PrivateCipher); CreateMaps(tmpKey.phi, tmpKey.psi, tmpKey.eta); key := tmpKey; END Create; PROCEDURE WritePolynom (s: Streams.Stream; p: Polynom) : BOOLEAN; (* writes the polynomial p onto the stream s *) CONST index = M DIV 8; VAR nrOfTerms, i : Types.Int32; bytes : ARRAY index OF SYS.BYTE; BEGIN nrOfTerms := LengthPolynom(p); IF ~NetIO.WriteInteger(s, nrOfTerms) THEN RETURN FALSE; END; WHILE nrOfTerms > 0 DO IF ~NetIO.WriteSet(s, p.koeff) THEN RETURN FALSE; END; i := 0; WHILE i < MaxVar DO IF ~NetIO.WriteShortInt(s, p.exp[i]) THEN RETURN FALSE; END; INC(i); END; p := p.next; DEC(nrOfTerms); END; RETURN TRUE; END WritePolynom; PROCEDURE ReadPolynom (s: Streams.Stream; VAR p: Polynom) : BOOLEAN; (* reads a polynomial from stream s *) CONST index = M DIV 8; VAR nrOfTerms, i : Types.Int32; pol : Polynom; bytes : ARRAY index OF SYS.BYTE; BEGIN IF ~NetIO.ReadInteger(s, nrOfTerms) THEN RETURN FALSE; END; NEW(p); pol := p; WHILE nrOfTerms > 0 DO IF ~NetIO.ReadSet(s, pol.koeff) THEN RETURN FALSE; END; i := 0; WHILE i < MaxVar DO IF ~NetIO.ReadShortInt(s, pol.exp[i]) THEN RETURN FALSE; END; INC(i); END; DEC(nrOfTerms); IF nrOfTerms > 0 THEN NEW(pol.next); pol := pol.next; END END; RETURN TRUE; END ReadPolynom; PROCEDURE PhiWrite (s: Streams.Stream; data: Phi) : BOOLEAN; (* writes the data structure for the public function phi onto a stream *) VAR r, d, k : Types.Int32; BEGIN r := 0; WHILE r < Rounds DO d := 0; WHILE d < Dim DO IF ~WritePolynom(s, data.num[r][d]) THEN RETURN FALSE; END; INC(d); END; IF ~WritePolynom(s, data.denom[r]) THEN RETURN FALSE; END; INC(r); END; RETURN TRUE; END PhiWrite; PROCEDURE PhiRead (s: Streams.Stream; VAR data: Phi) : BOOLEAN; (* reads the data structure for the public function phi from a stream *) VAR r, d, k : Types.Int32; BEGIN NEW(data); r := 0; WHILE r < Rounds DO d := 0; WHILE d < Dim DO IF ~ReadPolynom(s, data.num[r][d]) THEN RETURN FALSE; END; INC(d); END; IF ~ReadPolynom(s, data.denom[r]) THEN RETURN FALSE; END; INC(r); END; RETURN TRUE; END PhiRead; PROCEDURE PsiWrite (s: Streams.Stream; data: Psi) : BOOLEAN; (* writes the data structure for the private function psi onto a stream *) CONST index = M DIV 8; VAR dx, dy, r, d : Types.Int32; bytes : ARRAY index OF SYS.BYTE; BEGIN dy := 0; WHILE dy < Dim DO dx := 0; WHILE dx < Dim DO IF ~NetIO.WriteSet(s, data.initialmatrix[dy][dx]) THEN RETURN FALSE; END; INC(dx); END; INC(dy); END; r := 0; WHILE r < Rounds DO d := 0; WHILE d < Dim DO IF ~NetIO.WriteSet(s, data.korrNum[r][d]) THEN RETURN FALSE; END; INC(d); END; IF ~NetIO.WriteSet(s, data.korrDenom[r]) THEN RETURN FALSE; END; INC(r); END; RETURN TRUE; END PsiWrite; PROCEDURE PsiRead (s: Streams.Stream; VAR data: Psi) : BOOLEAN; (* reads the data structure for the private function psi from a stream *) CONST index = M DIV 8; VAR dy, dx, r, d : Types.Int32; bytes : ARRAY index OF SYS.BYTE; BEGIN dy := 0; WHILE dy < Dim DO dx := 0; WHILE dx < Dim DO IF ~NetIO.ReadSet(s, data.initialmatrix[dy][dx]) THEN RETURN FALSE; END; INC(dx); END; INC(dy); END; r := 0; WHILE r < Rounds DO d := 0; WHILE d < Dim DO IF ~NetIO.ReadSet(s, data.korrNum[r][d]) THEN RETURN FALSE; END; INC(d); END; IF ~NetIO.ReadSet(s, data.korrDenom[r]) THEN RETURN FALSE; END; INC(r); END; RETURN TRUE; END PsiRead; PROCEDURE EtaWrite (s: Streams.Stream; data: Eta) : BOOLEAN; (* writes the data structure for the public function eta onto a stream *) VAR l, d : Types.Int32; BEGIN l := 0; WHILE l < LastRounds DO d := 0; WHILE d < Dim DO IF ~WritePolynom(s, data.p[l][d]) THEN RETURN FALSE; END; INC(d); END; INC(l); END; RETURN TRUE; END EtaWrite; PROCEDURE EtaRead (s: Streams.Stream; VAR data: Eta) : BOOLEAN; (* reads the data structure for the public function eta from a stream *) VAR l, d : Types.Int32; BEGIN NEW(data); l := 0; WHILE l < LastRounds DO d := 0; WHILE d < Dim DO IF ~ReadPolynom(s, data.p[l][d]) THEN RETURN FALSE; END; INC(d); END; INC(l); END; RETURN TRUE; END EtaRead; PROCEDURE PubWrite (s: Streams.Stream; obj: PersistentObjects.Object) : BOOLEAN; (* interface procedure for PersistentObjects *) BEGIN WITH obj:PublicCipher DO RETURN PhiWrite(s, obj.phi) & EtaWrite(s, obj.eta); END; END PubWrite; PROCEDURE CipherWrite (s: Streams.Stream; obj: PersistentObjects.Object) : BOOLEAN; (* interface procedure for PersistentObjects *) BEGIN WITH obj:PrivateCipher DO RETURN PhiWrite(s, obj.phi) & PsiWrite(s, obj.psi) & EtaWrite(s, obj.eta); END; END CipherWrite; PROCEDURE PubRead (s: Streams.Stream; obj: PersistentObjects.Object) : BOOLEAN; (* interface procedure for PersistentObjects *) BEGIN WITH obj:PublicCipher DO IF ~PhiRead(s, obj.phi) OR ~EtaRead(s, obj.eta) THEN RETURN FALSE; END; END; RETURN TRUE; END PubRead; PROCEDURE CipherRead (s: Streams.Stream; obj: PersistentObjects.Object) : BOOLEAN; (* interface procedure for PersistentObjects *) BEGIN WITH obj:PrivateCipher DO IF ~PhiRead(s, obj.phi) OR ~PsiRead(s, obj.psi) OR ~EtaRead(s, obj.eta) THEN RETURN FALSE; END; END; RETURN TRUE; END CipherRead; BEGIN (* init of the zero and unit of CC(M) *) NullCCM := {}; EinsCCM := {0}; (* init of the zero exponent *) k := 0; WHILE k