123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920 |
- MODULE CryptoBigNumbers; (* g.f. 2001.10.07 *)
- (* 2002.08.12 g.f. added neg. numbers, GCD and ModInverse *)
- (* 2002.09.24 g.f. inceased digit size from 8 bit to 32 bit *)
- (* 2002.10.04 g.f. faster version of ModExp (uses montgomery multiplications now) *)
- (* 2005.07.07 g.f. Fabian Nart's enhancements incorporated *)
- (* 2010.01.12 g.f. interface cleanup, most procedures got funtions *)
- IMPORT S := SYSTEM, Streams, Random, Kernel, Out := KernelLog;
- CONST
- BufferPoolSize = 16;
- TYPE
- BNdigit = UNSIGNED32;
- digits = POINTER TO ARRAY OF BNdigit;
- BigNumber* = OBJECT
- VAR
- len-: LONGINT; (** number of significant 'digits' *)
- neg-: BOOLEAN;
- d-: digits;
- PROCEDURE & Init( bitsize: LONGINT );
- VAR n: LONGINT;
- BEGIN
- IF bitsize # 0 THEN
- n := (bitsize + 31) DIV 32;
- INC( n, (-n) MOD 16 );
- NEW( d, n );
- END;
- len := 0; neg := FALSE
- END Init;
- PROCEDURE Mask*( bits: LONGINT );
- VAR w, b: LONGINT;
- BEGIN
- w := bits DIV 32; b := bits MOD 32; len := w;
- IF b # 0 THEN INC( len );
- d[w] := S.VAL( UNSIGNED32, S.VAL( SET32, d[w] ) * {0..b} )
- END
- END Mask;
- PROCEDURE IsZero*( ): BOOLEAN;
- BEGIN
- RETURN (len = 0) OR ((len = 1) & (d[0] = 0))
- END IsZero;
- PROCEDURE EQ* ( b: BigNumber ): BOOLEAN;
- BEGIN
- RETURN Cmp( SELF, b ) = 0
- END EQ;
- PROCEDURE NEQ* ( b: BigNumber ): BOOLEAN;
- BEGIN
- RETURN Cmp( SELF, b ) # 0
- END NEQ;
- PROCEDURE GT* ( b: BigNumber ): BOOLEAN;
- BEGIN
- RETURN Cmp( SELF, b ) > 0
- END GT;
- PROCEDURE GEQ* ( b: BigNumber ): BOOLEAN;
- BEGIN
- RETURN Cmp( SELF, b ) >= 0
- END GEQ;
- PROCEDURE Shift*( n: LONGINT );
- VAR right: BOOLEAN; w, bits, i, l: LONGINT; a, b: BNdigit;
- BEGIN
- IF len = 0 THEN RETURN END;
- IF n < 0 THEN right := TRUE; n := ABS( n ) ELSE right := FALSE END;
- w := n DIV 32; bits := n MOD 32;
- IF ~right THEN
- adjust( len + w + 1 );
- IF w > 0 THEN
- FOR i := len - 1 TO 0 BY -1 DO d[i + w] := d[i] END;
- FOR i := 0 TO w - 1 DO d[i] := 0 END;
- INC( len, w )
- END;
- IF bits > 0 THEN
- d[len] := 0;
- FOR i := len TO 0 BY -1 DO
- a := d[i];
- IF i > 0 THEN b := d[i - 1] ELSE b := 0 END;
- d[i] := LSH( a, bits ) + LSH( b, -32 + bits )
- END;
- IF d[len] # 0 THEN INC( len ) END;
- END
- ELSE
- IF w > 0 THEN
- FOR i := 0 TO len - w - 1 DO d[i] := d[i + w] END;
- DEC( len, w )
- END;
- IF bits > 0 THEN
- l := len;
- FOR i := 0 TO l - 1 DO a := d[i];
- IF i < l - 1 THEN b := d[i + 1] ELSE b := 0 END;
- d[i] := LSH( a, -bits ) + LSH( b, 32 - bits )
- END;
- IF d[l - 1] = 0 THEN DEC( len ) END;
- END
- END;
- END Shift;
- PROCEDURE Dec*;
- VAR i: LONGINT;
- BEGIN
- i := 0;
- IF IsZero( ) THEN len := 1; neg := TRUE; d[0] := 1
- ELSIF neg THEN
- WHILE (d[i] = -1) & (i < len) DO d[i] := 0; INC( i ) END;
- IF i = len THEN d[i] := 1; INC( len ) ELSE INC( d[i] ) END
- ELSE
- WHILE d[i] = 0 DO d[i] := -1; INC( i ) END;
- DEC( d[i] ); fixlen( d, len )
- END
- END Dec;
- PROCEDURE Inc*;
- VAR i: LONGINT;
- BEGIN
- i := 0;
- IF ~neg THEN
- WHILE (d[i] = -1) & (i < len) DO d[i] := 0; INC( i ) END;
- IF i = len THEN d[i] := 1; INC( len ) ELSE INC( d[i] ) END
- ELSE
- WHILE d[i] = 0 DO d[i] := -1; INC( i ) END;
- DEC( d[i] ); fixlen( d, len );
- IF len = 0 THEN neg := FALSE END
- END
- END Inc;
- PROCEDURE Negate*;
- BEGIN
- IF ~IsZero( ) THEN neg := ~neg END
- END Negate;
- PROCEDURE BitSize*( ): LONGINT;
- VAR n: LONGINT; t: BNdigit;
- BEGIN
- IF len = 0 THEN RETURN 0
- ELSE n := (len - 1) * 32
- END;
- t := d[len - 1];
- WHILE t # 0 DO INC( n ); t := LSH( t, -1 ) END;
- RETURN n
- END BitSize;
- PROCEDURE BitSet*( n: LONGINT ): BOOLEAN;
- VAR w, bit: LONGINT;
- BEGIN
- w := n DIV 32; bit := n MOD 32;
- IF w >= len THEN RETURN FALSE
- ELSE RETURN bit IN S.VAL( SET32, d[w] )
- END
- END BitSet;
- PROCEDURE adjust( newlen: LONGINT );
- VAR n, i: LONGINT; nd: digits;
- BEGIN
- n := 16;
- WHILE n < newlen DO INC( n, 16 ) END;
- IF LEN( d ) < n THEN
- NEW( nd, n );
- FOR i := 0 TO LEN( d^ ) - 1 DO nd[i] := d[i] END;
- d := nd
- END;
- END adjust;
- END BigNumber;
- dig2 = ARRAY 2 OF BNdigit;
- dig3 = ARRAY 3 OF BNdigit;
- Montgomery = OBJECT
- VAR
- bits: LONGINT; (* of R *)
- r, n, t1, t2: BigNumber;
- PROCEDURE & Init( x: BigNumber );
- BEGIN
- Copy( x, n ); bits := x.len*32;
- AssignInt( r, 1 ); r.Shift( bits ); (* r := R *)
- r := Sub( r, ModInverse( n, r ) ); (* r := R - (1/n) (mod R) *)
- n.adjust( 2*x.len ); r.adjust( 2*x.len );
- NEW( t1, 2*bits ); NEW( t2, 2*bits );
- END Init;
- PROCEDURE Convert( VAR val: BigNumber ); (* val := val ^ R mod n *)
- VAR i: LONGINT;
- BEGIN
- FOR i := 0 TO bits - 1 DO
- val.Shift( 1 );
- IF ucmp( val, n ) >= 0 THEN val := Sub( val, n ) END
- END
- END Convert;
- PROCEDURE Reduce( VAR val: BigNumber ); (* val := val ^ (1/R) mod n *)
- BEGIN
- Copy( val, t1 ); t1.Mask( bits - 1 ); (* val mod R *)
- mul( t1.d, r.d, t2.d, t1.len, r.len, t2.len ); t2.Mask( bits - 1 ); (* mod R *)
- mul( t2.d, n.d, t1.d, t2.len, n.len, t1.len );
- add( t1.d, val.d, val.d, t1.len, val.len, val.len ); val.Shift( -bits ); (* div R *)
- IF ucmp( val, n ) >= 0 THEN sub( val.d, n.d, val.d, val.len, n.len, val.len ) END;
- END Reduce;
- PROCEDURE Mult( a, b: BigNumber ): BigNumber;
- VAR c: BigNumber;
- BEGIN
- NEW( c, 0 );
- mul( a.d, b.d, c.d, a.len, b.len, c.len );
- Reduce( c );
- RETURN c
- END Mult;
- END Montgomery;
- VAR
- bufferPool: ARRAY BufferPoolSize OF digits;
- nextFreeBuffer: LONGINT;
- randomgenerator: Random.Generator;
- PROCEDURE RandomBytes*( VAR buf: ARRAY OF CHAR; p: LONGINT; n: LONGINT );
- VAR i: LONGINT;
- BEGIN
- FOR i := 0 TO n - 1 DO buf[p + i] := CHR( ENTIER( randomgenerator.Uniform()*256 ) ) END
- END RandomBytes;
- PROCEDURE adjust( VAR d: digits; dl, len: LONGINT );
- VAR n, i: LONGINT; nd: digits;
- BEGIN
- ASSERT( d # NIL );
- n := 16;
- WHILE n < len DO INC( n, 16) END;
- IF LEN( d ) < n THEN
- NEW( nd, n );
- FOR i := 0 TO dl - 1 DO nd[i] := d[i] END;
- d := nd
- END;
- END adjust;
- (** random number with len 'bits' *)
- PROCEDURE NewRand*( bits: LONGINT; top, bottom: SHORTINT ): BigNumber;
- VAR n, len, i, topbit: LONGINT; topword: SET32; b: BigNumber;
- BEGIN
- len := bits; INC( len, (-len) MOD 32 );
- NEW( b, len );
- n := len DIV 32;
- FOR i := 0 TO n -1 DO
- b.d[i] := randomgenerator.Integer()
- END;
- b.len := (bits + 31) DIV 32;
- topbit := (bits - 1) MOD 32;
- topword := SET32( S.VAL( SET32, b.d[b.len - 1] ) * {0..topbit} );
- IF top > 0 THEN INCL( topword, topbit ) END;
- b.d[b.len - 1] := S.VAL( LONGINT, topword );
- IF (bottom > 0) & ~ODD( b.d[0] ) THEN INC( b.d[0] ) END;
- RETURN b
- END NewRand;
- PROCEDURE NewRandRange*( range: BigNumber ): BigNumber; (** 0 < b < range DIV 2 - 1*)
- VAR b: BigNumber;
- BEGIN
- b := NewRand( range.BitSize( ) - 1, 0, 0 );
- b.Dec;
- RETURN b
- END NewRandRange;
- PROCEDURE fixlen( VAR d: digits; VAR len: LONGINT );
- BEGIN
- WHILE (len > 0) & (d[len - 1] = 0) DO DEC( len ) END;
- END fixlen;
- PROCEDURE h2i( c: CHAR ): LONGINT;
- VAR v: LONGINT;
- BEGIN
- CASE c OF
- | '0'..'9': v := ORD( c ) - ORD( '0' )
- | 'a'..'f': v := ORD( c ) - ORD( 'a' ) + 10
- | 'A'..'F': v := ORD( c ) - ORD( 'A' ) + 10
- ELSE HALT( 99 )
- END;
- RETURN v
- END h2i;
- PROCEDURE AssignHex*( VAR b: BigNumber; CONST hex: ARRAY OF CHAR; len: LONGINT );
- VAR n, w, pos: LONGINT;
- BEGIN
- ASSERT( len <= LEN( hex ) - 1);
- NEW( b, 4*len ); b.len := (4*len + 31) DIV 32;
- n := b.len - 1; w := 0; pos := 0;
- WHILE len > 0 DO
- w := w*16 + h2i( hex[pos] ); INC( pos ); DEC( len );
- IF len MOD 8 = 0 THEN b.d[n] := w; w := 0; DEC( n ) END;
- END;
- fixlen( b.d, b.len )
- END AssignHex;
- PROCEDURE AssignBin*( VAR b: BigNumber; CONST buf: ARRAY OF CHAR; pos, len: LONGINT );
- VAR n, w: LONGINT;
- BEGIN
- ASSERT( (pos + len) <= LEN( buf ) );
- NEW( b, 8*len ); b.len := SHORT( (8*len + 31) DIV 32 );
- n := b.len - 1; w := 0;
- WHILE len > 0 DO
- w := w*256 + ORD( buf[pos] ); INC( pos ); DEC( len );
- IF len MOD 4 = 0 THEN b.d[n] := w; w := 0; DEC( n ) END;
- END;
- fixlen( b.d, b.len )
- END AssignBin;
- (** Returns the value of b as a binary string 'data' starting at ofs.
- The Length of 'data' must be longer or equal to 4*b.len + ofs. *)
- PROCEDURE GetBinaryValue*( VAR b: BigNumber; VAR data: ARRAY OF CHAR; ofs: LONGINT );
- VAR j, n: LONGINT; tmp: BNdigit;
- BEGIN
- ASSERT( LEN( data ) >= 4 * b.len + ofs );
- FOR n := b.len-1 TO 0 BY -1 DO
- tmp := b.d[n];
- FOR j := 3 TO 0 BY - 1 DO
- data[ ofs + j ] := CHR( tmp MOD 256 );
- tmp := tmp DIV 256
- END;
- INC( ofs, 4 )
- END
- END GetBinaryValue;
- PROCEDURE AssignInt*( VAR b: BigNumber; val: LONGINT );
- BEGIN
- NEW( b, 64 );
- IF val < 0 THEN b.neg := TRUE; val := ABS( val ) END;
- IF val # 0 THEN b.len := 1; b.d[0] := val ELSE b.len := 0 END
- END AssignInt;
- PROCEDURE cmpd( VAR a, b: digits; len: LONGINT ): SHORTINT;
- VAR i: LONGINT;
- BEGIN
- i := len - 1;
- WHILE (i >= 0) & (a[i] = b[i]) DO DEC( i ) END;
- IF i < 0 THEN RETURN 0
- ELSE
- IF b[i] < a[i] THEN RETURN 1 ELSE RETURN -1 END
- END
- END cmpd;
- PROCEDURE ucmp( VAR a, b: BigNumber ): SHORTINT; (* 1: |a| > |b|; 0: a = b; -1: |a| < |b| *)
- BEGIN
- IF a.len > b.len THEN RETURN 1
- ELSIF a.len < b.len THEN RETURN -1
- ELSE RETURN cmpd( a.d, b.d, a.len )
- END
- END ucmp;
- PROCEDURE Cmp*( a, b: BigNumber ): SHORTINT; (** 1: a > b; 0: a = b; -1: a < b *)
- BEGIN
- IF a.neg # b.neg THEN
- IF a.neg THEN RETURN -1 ELSE RETURN 1 END
- ELSIF a.neg THEN RETURN ucmp( a, b ) * (-1)
- ELSE RETURN ucmp( a, b )
- END
- END Cmp;
- PROCEDURE copy( a, b: digits; len: LONGINT );
- VAR i: LONGINT;
- BEGIN
- FOR i := 0 TO len - 1 DO b[i] := a[i] END
- END copy;
- PROCEDURE Copy*( VAR a, b: BigNumber ); (** b := a *)
- BEGIN
- ASSERT( (a # NIL) & (ADDRESSOF( a ) # ADDRESSOF( b )) );
- IF (b = NIL) OR (LEN( b.d^ ) < a.len) THEN NEW( b, a.len*32 ) END;
- copy( a.d, b.d, a.len ); b.len := a.len
- END Copy;
- PROCEDURE Invert( x: BNdigit ): BNdigit;
- BEGIN
- RETURN S.VAL( BNdigit, -S.VAL( SET32, x ) )
- END Invert;
- PROCEDURE add( a, b: digits; VAR c: digits; al, bl: LONGINT; VAR cl: LONGINT );
- VAR i, n: LONGINT; A, B, x: BNdigit; carry: BOOLEAN;
- BEGIN
- n := MAX( al, bl ); carry := FALSE;
- IF LEN( c^ ) < (n + 1) THEN adjust( c, cl, n + 1 ) END;
- FOR i := 0 TO n - 1 DO
- IF i >= al THEN A := 0 ELSE A := a[i] END;
- IF i >= bl THEN B := 0 ELSE B := b[i] END;
- x := A + B;
- IF carry THEN INC( x ); carry := Invert(A) <= B ELSE carry := x < B END;
- c[i]:= x
- END;
- IF carry THEN c[n] := 1; INC( n ) END;
- cl := n
- END add;
- PROCEDURE sub( a, b: digits; VAR c: digits; al, bl: LONGINT; VAR cl: LONGINT );
- VAR i, n: LONGINT; A, B, x: BNdigit; borrow: BOOLEAN;
- BEGIN
- n := MAX( al, bl ); borrow := FALSE;
- IF LEN( c^ ) < n THEN adjust( c, cl, n ) END;
- FOR i := 0 TO n - 1 DO
- IF i >= al THEN A := 0 ELSE A := a[i] END;
- IF i >= bl THEN B := 0 ELSE B := b[i] END;
- x := A - B;
- IF borrow THEN DEC( x ); borrow := A <= B ELSE borrow := A < B END;
- c[i]:= x
- END;
- ASSERT( ~borrow );
- WHILE (n > 0) & (c[n - 1] = 0) DO DEC( n ) END;
- cl := n
- END sub;
- PROCEDURE Add*( a, b: BigNumber ): BigNumber; (** a + b *)
- VAR sd: digits; l, sl: LONGINT; c: BigNumber;
- BEGIN
- ASSERT( (a # NIL) & (b # NIL) );
- l := MAX( a.len, b.len ) + 1;
- NEW( c, l*32 ); sd := c.d;
- IF a.neg = b.neg THEN add( a.d, b.d, sd, a.len, b.len, sl ); c.neg := a.neg
- ELSE
- IF ucmp( a, b ) >= 0 THEN sub( a.d, b.d, sd, a.len, b.len, sl ); c.neg := a.neg
- ELSE sub( b.d, a.d, sd, b.len, a.len, sl ); c.neg := ~a.neg
- END
- END;
- IF sd # c.d THEN adjust( c.d, 0, sl ); copy( sd, c.d, sl ) END;
- c.len := sl;
- IF c.IsZero( ) THEN c.neg := FALSE END;
- RETURN c
- END Add;
- PROCEDURE Sub*( a, b: BigNumber ): BigNumber; (** a - b *)
- VAR sd: digits; l, sl: LONGINT; c: BigNumber;
- BEGIN
- ASSERT( (a # NIL) & (b # NIL) );
- l := MAX( a.len, b.len ) + 1;
- NEW( c, l*32 ); sd := c.d;
- IF a.neg # b.neg THEN add( a.d, b.d, sd, a.len, b.len, sl ); c.neg := a.neg
- ELSE
- IF ucmp( a, b ) >= 0 THEN sub( a.d, b.d, sd, a.len, b.len, sl ); c.neg := a.neg
- ELSE sub( b.d, a.d, sd, b.len, a.len, sl ); c.neg := ~a.neg
- END
- END;
- IF sd # c.d THEN adjust( c.d, 0, sl ); copy( sd, c.d, sl ) END;
- c.len := sl;
- IF c.IsZero( ) THEN c.neg := FALSE END;
- RETURN c
- END Sub;
- PROCEDURE mul( a, b: digits; VAR c: digits; al, bl: LONGINT; VAR cl: LONGINT ); (* c = a*b *)
- VAR
- prod, sum, tmp, mulc: BNdigit; addc: BOOLEAN; i, j: LONGINT; pl: LONGINT;
- p: digits; tmp64: UNSIGNED64;
- BEGIN
- pl := 0; NEW( p, al + bl + 2 );
- FOR i := 0 TO al + bl + 1 DO p[i] := 0 END; (* clear acc *)
- FOR i := 0 TO bl - 1 DO
- mulc := 0; addc := FALSE; pl := i;
- FOR j := 0 TO al - 1 DO
- tmp := p[pl];
- tmp64 := UNSIGNED64( a[j] )*b[i] + mulc;
- prod := BNdigit( tmp64 MOD 100000000H );
- mulc := BNdigit( tmp64 DIV 100000000H );
- sum := prod + tmp;
- IF addc THEN INC( sum ); addc := Invert(prod) <= tmp
- ELSE addc := sum < tmp
- END;
- p[pl] := sum; INC( pl );
- END;
- IF addc OR (mulc # 0) THEN
- IF addc THEN INC( mulc ) END;
- p[pl] := mulc; INC( pl )
- END;
- END;
- c := p; cl := pl; fixlen( c, cl );
- END mul;
- PROCEDURE muls( a: digits; b: BNdigit; c: digits; al: LONGINT; VAR cl: LONGINT ); (* c = a*b *)
- VAR carry: BNdigit; tmp64: UNSIGNED64; i: LONGINT;
- BEGIN
- carry := 0; cl := al;
- FOR i := 0 TO al - 1 DO
- tmp64 := UNSIGNED64( a[i] )*b + carry;
- c[i] := BNdigit( tmp64 MOD 100000000H );
- carry := BNdigit( tmp64 DIV 100000000H );
- END;
- IF carry # 0 THEN c[cl] := carry; INC( cl ) END
- END muls;
- PROCEDURE Mul*( a, b: BigNumber ): BigNumber; (** a * b *)
- VAR pd: digits; pl: LONGINT; c: BigNumber;
- BEGIN
- ASSERT( (a # NIL) & (b # NIL) );
- IF (a.len = 0) OR (b.len = 0) THEN AssignInt( c, 0 ); RETURN c END;
- NEW( c, 32 );
- IF a.len >= b.len THEN
- mul( a.d, b.d, pd, a.len, b.len, pl )
- ELSE
- mul( b.d, a.d, pd, b.len, a.len, pl )
- END;
- c.d := pd; c.len := pl; c.neg := a.neg # b.neg;
- RETURN c
- END Mul;
- PROCEDURE div64( CONST a: dig2; VAR b: BNdigit ): LONGINT; (* a div b *)
- VAR bit, q: LONGINT; r: BNdigit; overflow: BOOLEAN;
- BEGIN
- IF a[1] = 0 THEN
- IF (a[0] < 80000000H) & (b < 80000000H ) THEN RETURN LONGINT( a[0] DIV b )
- ELSIF a[0] < b THEN RETURN 0
- ELSIF a[0] = b THEN RETURN 1
- END;
- bit := 31
- ELSIF a[1] = b THEN RETURN -1
- ELSE bit := 63
- END;
- q := 0; r := 0;
- WHILE (bit >= 0) & ~(bit MOD 32 IN S.VAL( SET32, a[bit DIV 32]) ) DO DEC( bit ) END;
- WHILE bit >= 0 DO
- overflow := 31 IN S.VAL( SET32, r ); r := ASH( r, 1 );
- IF bit MOD 32 IN S.VAL( SET32, a[bit DIV 32] ) THEN INC( r ) END;
- IF overflow OR (b <= r) THEN r := r - b;
- IF bit < 32 THEN INCL( S.VAL( SET32, q ), bit ) ELSE q := -1 END;
- END;
- DEC( bit )
- END;
- RETURN q
- END div64;
- PROCEDURE div96( CONST a: dig3; CONST b: dig2 ): LONGINT; (* a div b *)
- VAR bit: LONGINT; r: dig2; q: LONGINT; overflow, borrow: BOOLEAN;
- PROCEDURE ge( CONST a, b: dig2 ): BOOLEAN;
- BEGIN
- IF a[1] = b[1] THEN RETURN a[0] >= b[0]
- ELSE RETURN a[1] >= b[1]
- END
- END ge;
- PROCEDURE shift( VAR x: dig2 );
- BEGIN
- overflow := 31 IN S.VAL( SET32, x[1] ); x[1] := ASH( x[1], 1 );
- IF 31 IN S.VAL( SET32, x[0] ) THEN INC( x[1] ) END;
- x[0] := ASH( x[0], 1 );
- END shift;
- BEGIN
- IF a[2] = 0 THEN
- IF a[1] < b[1] THEN RETURN 0 END;
- bit := 63
- ELSE bit := 95
- END;
- q := 0; r[0] := 0; r[1] := 0;
- WHILE (bit >= 0) & ~(bit MOD 32 IN S.VAL( SET32, a[bit DIV 32]) ) DO DEC( bit ) END;
- WHILE bit >= 0 DO
- shift( r ); (* r := r*2 *)
- IF bit MOD 32 IN S.VAL( SET32, a[bit DIV 32] ) THEN INC( r[0] ) END;
- IF overflow OR ge( r, b ) THEN
- borrow := r[0] <= b[0]; r[0] := r[0] - b[0]; r[1] := r[1] - b[1];
- IF borrow THEN DEC( r[1] ) END;
- IF bit < 32 THEN INCL( S.VAL( SET32, q ), bit ) ELSE q := -1 END;
- END;
- DEC( bit )
- END;
- RETURN q
- END div96;
- PROCEDURE Div2*( a, b: BigNumber; VAR q, r: BigNumber ); (** q = a div b; r = a mod b *)
- VAR td, sd, bd, qd: digits; x, i, tail, bl, tl, sl, ql, qi: LONGINT;
- t3: dig3; t2, d0: dig2;
- aq, ar: ADDRESS;
- BEGIN
- aq := ADDRESSOF( q ); ar := ADDRESSOF( r );
- ASSERT( (a # NIL) & (b # NIL) & ~b.IsZero( ) & ~b.neg & (aq # ar) );
- NEW( q, a.len*32 ); qd := q.d;
- x := ucmp( a, b );
- IF x < 0 THEN AssignInt( q, 0 ); Copy( a, r )
- ELSIF x = 0 THEN AssignInt( q, 1 ); AssignInt( r, 0 )
- ELSE
- td := GetBuffer();
- sd := GetBuffer();
- bd := b.d; bl := b.len; d0[1] := bd[bl - 1];
- IF bl > 1 THEN d0[0] := bd[bl - 2] ELSE d0[0] := 0 END;
- FOR i := 1 TO bl DO td[bl - i] := a.d[a.len - i] END;
- tl := bl; tail := a.len - bl; ql := tail + 1; qi := ql;
- LOOP
- IF tl < bl THEN x := 0;
- ELSE i := tl - 1;
- IF d0[0] = 0 THEN
- IF tl > bl THEN t2[1] := td[i]; DEC( i ) ELSE t2[1] := 0 END;
- t2[0] := td[i];
- x := div64( t2, d0[1] );
- ELSE
- IF tl > bl THEN t3[2] := td[i]; DEC( i ) ELSE t3[2] := 0 END;
- t3[1] := td[i];
- IF i > 0 THEN t3[0] := td[i - 1] ELSE t3[0] := 0 END;
- x := div96( t3, d0 );
- END
- END;
- IF x # 0 THEN muls( bd, x, sd, bl, sl );
- WHILE (sl > tl) OR ((sl = tl) & (cmpd( sd, td, sl ) > 0)) DO
- sub( sd, bd, sd, sl, bl, sl ); DEC( x );
- END;
- sub( td, sd, td, tl, sl, tl );
- END;
- IF (qi = ql) & (x = 0) THEN DEC( ql ); DEC( qi ) ELSE DEC( qi ); qd[qi] := x END;
- IF tail = 0 THEN EXIT END;
- DEC( tail );
- FOR i := tl TO 1 BY -1 DO td[i] := td[i - 1] END;
- td[0] := a.d[tail]; INC( tl );
- END;
- q.len := ql;
- NEW( r, tl*32 ); copy( td, r.d, tl ); r.len := tl;
- RecycleBuffer( td );
- RecycleBuffer( sd )
- END;
- IF q.len = 0 THEN q.neg := FALSE ELSE q.neg := a.neg END;
- IF (r.len # 0) & a.neg THEN q.Dec; r := Sub( b, r ) END;
- END Div2;
- PROCEDURE ModWord*( VAR a: BigNumber; b: BNdigit ): BNdigit; (** a mod b *)
- VAR x: BNdigit; td, sd, bd: digits; tail, tl, sl, bl: LONGINT; t2: dig2;
- BEGIN
- ASSERT( a # NIL );
- td := GetBuffer();
- sd := GetBuffer();
- bd := GetBuffer();
- bd[0] := b; bl := 1; td[0] := a.d[a.len - 1]; tl := 1; tail := a.len - 1;
- LOOP
- IF tl > 1 THEN t2[1] := td[1] ELSE t2[1] := 0 END;
- t2[0] := td[0];
- x := div64( t2, b );
- IF x # 0 THEN muls( bd, x, sd, bl, sl );
- WHILE (sl > tl) OR ((sl = tl) & (cmpd( sd, td, sl ) > 0)) DO
- sub( sd, bd, sd, sl, bl, sl ); DEC( x );
- END;
- sub( td, sd, td, tl, sl, tl );
- END;
- IF tail <= 0 THEN EXIT END;
- DEC( tail );
- IF td[0] = 0 THEN tl := 1 ELSE td[1] := td[0]; tl := 2 END;
- td[0] := a.d[tail];
- END;
- x := td[0];
- RecycleBuffer( td );
- RecycleBuffer( sd );
- RecycleBuffer( bd );
- RETURN x
- END ModWord;
- PROCEDURE Div*( a, b: BigNumber ): BigNumber; (** a DIV b *)
- VAR dummy, q: BigNumber;
- BEGIN
- Div2( a, b, q, dummy );
- RETURN q
- END Div;
- PROCEDURE Mod*( a, b: BigNumber ): BigNumber; (** a MOD b *)
- VAR dummy, r: BigNumber;
- BEGIN
- Div2( a, b, dummy, r );
- RETURN r
- END Mod;
- PROCEDURE Exp*( a, b: BigNumber ): BigNumber; (** a ^ b *)
- VAR v: digits; i: LONGINT; vl: LONGINT; e: BigNumber;
- BEGIN
- NEW( e, 8192 );
- NEW( v, 256 );
- copy( a.d, v, a.len ); vl := a.len;
- IF ODD( b.d[0] ) THEN copy( a.d, e.d, a.len ); e.len := a.len ELSE e.len := 1; e.d[0] := 1 END;
- FOR i := 1 TO b.BitSize( ) - 1 DO
- mul( v, v, v, vl, vl, vl );
- IF b.BitSet( i ) THEN mul( v, e.d, e.d, vl, e.len, e.len ) END;
- END;
- fixlen( e.d, e.len );
- RETURN e
- END Exp;
- PROCEDURE ModMul*( a, b, m: BigNumber ): BigNumber; (** (a*b) mod m *)
- VAR p, r: BigNumber;
- BEGIN
- p := Mul( a, b ); r := Mod( p, m );
- RETURN r
- END ModMul;
- PROCEDURE wbits( exp: BigNumber ): LONGINT;
- VAR b, w: LONGINT;
- BEGIN
- (* window bits for exponent size, for sliding window ModExp functions *)
- b := exp.BitSize( );
- IF b <= 23 THEN w := 1
- ELSIF b <= 79 THEN w := 3
- ELSIF b <= 239 THEN w := 4
- ELSIF b <= 671 THEN w := 5
- ELSE w := 6
- END;
- RETURN w
- END wbits;
- PROCEDURE ModExp*( a, b, m: BigNumber ): BigNumber; (** a ^ b mod m *)
- VAR
- a0: ARRAY 32 OF BigNumber; res, d: BigNumber;
- wsize, v, wstart, e, i, j: LONGINT;
- mg: Montgomery;
- BEGIN
- ASSERT( (a # NIL) & (b # NIL) & (m # NIL) );
- IF b.IsZero( ) THEN
- IF a.IsZero( ) THEN HALT( 100 ) END;
- AssignInt( res, 1 ); RETURN res
- END;
- IF m.IsZero( ) THEN HALT( 101 ) END;
- IF m.neg THEN HALT( 102 ) END;
- NEW( mg, m );
- a0[0] := Mod( a, m ); mg.Convert( a0[0] );
- wsize := wbits( b );
- IF wsize > 1 THEN (* precompute window multipliers *)
- d := mg.Mult( a0[0], a0[0] ); j := ASH( 1, wsize - 1 );
- FOR i := 1 TO j - 1 DO a0[i] := mg.Mult( a0[i - 1], d ) END;
- END;
- Copy( a0[0], res ); wstart := b.BitSize( ) - 2;
- WHILE wstart >= 0 DO res := mg.Mult( res, res );
- IF b.BitSet( wstart ) THEN
- v := 1; e := 0; i := 1;
- WHILE (i < wsize) & (wstart - i >= 0) DO
- IF b.BitSet( wstart - i ) THEN v := ASH( v, i - e ) + 1; e := i END;
- INC( i )
- END;
- FOR i := 1 TO e DO res := mg.Mult( res, res ) END;
- res := mg.Mult( res, a0[v DIV 2] ); (* v will be an odd number < 2^wsize *)
- DEC( wstart, e + 1 );
- ELSE DEC( wstart )
- END
- END;
- mg.Reduce( res );
- RETURN res
- END ModExp;
- PROCEDURE GCD*( a, b: BigNumber ): BigNumber; (** gcd( a, b ) *)
- VAR x, y, r: BigNumber;
- BEGIN
- ASSERT( ~a.neg & ~b.neg );
- Copy( a, x ); Copy( b, y );
- LOOP
- IF Cmp( x, y ) > 0 THEN x := Mod( x, y );
- IF x.IsZero( ) THEN Copy( y, r ); EXIT END
- ELSE y := Mod( y, x ) ;
- IF y.IsZero( ) THEN Copy( x, r ); EXIT END
- END;
- END;
- RETURN r
- END GCD;
- PROCEDURE ModInverse*( a, m: BigNumber ): BigNumber; (** Return x so that (x * a) mod m = 1 *)
- VAR
- q, t, x: BigNumber; g, v: ARRAY 3 OF BigNumber; p, i, s, tmp, n: LONGINT;
- BEGIN
- FOR i := 0 TO 2 DO AssignInt( g[i], 0 ); AssignInt( v[i], 0 ) END;
- Copy( a, g[0] ); Copy( m, g[1] ); AssignInt( v[0], 1 ); AssignInt( v[1], 0 );
- p := 0; i := 1; s := 2; n := 0;
- LOOP
- Div2( g[p], g[i], q, g[s] ); t := Mul( q, v[i] ); v[s] := Add( v[p], t ); INC( n );
- IF g[s].IsZero( ) THEN EXIT END;
- tmp := p; p := i; i := s; s := tmp;
- END;
- IF (g[i].len = 1) & (g[i].d[0] = 1) THEN
- IF ODD( n ) THEN v[i] := Sub( m, v[i] ) END;
- x := Mod( v[i], m )
- ELSE AssignInt( x, 0 )
- END;
- RETURN x
- END ModInverse;
- (*--------------------------- Text I/O ---------------------------------*)
- PROCEDURE TextWrite*( w: Streams.Writer; b: BigNumber );
- VAR i: LONGINT;
- BEGIN
- IF b.neg THEN w.String( "-" ) END;
- IF b.len = 0 THEN w.String( " 00000000" )
- ELSE i := b.len;
- WHILE i > 0 DO
- DEC( i ); w.Hex( b.d[i], -8 );
- IF i > 0 THEN
- IF i MOD 6 = 0 THEN w.Ln
- ELSE w.String( " " )
- END
- END
- END
- END;
- w.Char( '.' );
- END TextWrite;
- (** writes a hexadecimal representation of b to the standard output *)
- PROCEDURE Print*( b: BigNumber );
- VAR i: LONGINT;
- BEGIN
- IF b.neg THEN Out.String( "-" ) END;
- IF b.len = 0 THEN Out.String( "00000000" )
- ELSE i := b.len;
- WHILE i > 0 DO
- DEC( i ); Out.Hex( b.d[i], -8 );
- IF i > 0 THEN
- IF i MOD 6 = 0 THEN Out.Ln
- ELSE Out.String( " " )
- END
- END
- END
- END;
- Out.Char( '.' ); Out.Ln
- END Print;
- PROCEDURE nibble( r: Streams.Reader ): CHAR;
- VAR c: CHAR;
- BEGIN
- REPEAT
- REPEAT r.Char( c ) UNTIL (c > ' ') OR (r.Available() = 0);
- UNTIL (r.Available() = 0) OR
- (c >= '0') & (c <= '9') OR
- (c >= 'A') & (c <= 'F') OR
- (c >= 'a') & (c <= 'f') OR (c = '.');
- RETURN c
- END nibble;
- PROCEDURE TextRead*( r: Streams.Reader; VAR b: BigNumber );
- VAR buf: ARRAY 2048 OF CHAR; i: LONGINT; n: CHAR;
- BEGIN
- i := 0; n := nibble( r );
- WHILE n # '.' DO buf[i] := n; INC( i ); n := nibble( r ) END;
- AssignHex( b, buf, i );
- END TextRead;
- (*--------------------------- File I/O ---------------------------------*)
- PROCEDURE FileRead*( r: Streams.Reader; VAR b: BigNumber );
- VAR i, j, v: LONGINT;
- BEGIN
- r.RawLInt( j );
- NEW( b, 32 * j );
- b.len := j;
- FOR i := 0 TO j - 1 DO r.RawLInt( v ); b.d[ i ] := v END
- END FileRead;
- PROCEDURE FileWrite*( w: Streams.Writer; b: BigNumber );
- VAR i, j, v: LONGINT;
- BEGIN
- j := b.len;
- w.RawLInt( j );
- FOR i := 0 TO j - 1 DO w.RawLInt( v ); b.d[ i ] := v END
- END FileWrite;
- (* ------------ buffer pooling to make this module thread-save (F.N.) -----------------------*)
- PROCEDURE GetBuffer( ): digits;
- VAR d: digits;
- BEGIN {EXCLUSIVE}
- IF nextFreeBuffer > -1 THEN
- d := bufferPool[ nextFreeBuffer ];
- DEC( nextFreeBuffer )
- ELSE
- NEW( d, 256 )
- END;
- RETURN d
- END GetBuffer;
- PROCEDURE RecycleBuffer( d: digits );
- BEGIN {EXCLUSIVE}
- IF nextFreeBuffer < BufferPoolSize - 1 THEN
- INC( nextFreeBuffer );
- bufferPool[ nextFreeBuffer ] := d
- END
- END RecycleBuffer;
- PROCEDURE InitRandomgenerator;
- BEGIN
- NEW( randomgenerator );
- randomgenerator.InitSeed( Kernel.GetTicks() );
- END InitRandomgenerator;
- BEGIN
- ASSERT( S.VAL( LONGINT, {0}) = 1 ); (* little endian SETs! *)
- FOR nextFreeBuffer := 0 TO BufferPoolSize - 1 DO
- NEW( bufferPool[nextFreeBuffer], 256 )
- END;
- nextFreeBuffer := BufferPoolSize-1;
- InitRandomgenerator();
- END CryptoBigNumbers.
|