123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- MODULE CryptoDSA; (** AUTHOR "G.F."; PURPOSE "Digital Signature Algorithm DSA"; *)
- IMPORT
- B := CryptoBigNumbers, SHA1 := CryptoSHA1, P := CryptoPrimes, U := CryptoUtils, Streams,
- Base64 := CryptoBase64, BIT;
- TYPE
- Number = B.BigNumber;
- Signature* = OBJECT
- VAR
- r- : Number;
- s- : Number;
- PROCEDURE & Init*( r, s: Number );
- BEGIN
- B.Copy( r, SELF.r );
- B.Copy( s, SELF.s )
- END Init;
- END Signature;
- Key* = OBJECT
- VAR
- name-: ARRAY 128 OF CHAR; (** Owner of this key. *)
- private-: BOOLEAN;
- p- : Number;
- q- : Number;
- g- : Number;
- y- : Number;
- inv, r : Number;
- PROCEDURE Sign*( CONST digest: ARRAY OF CHAR; len: INTEGER ): Signature;
- VAR m, xr, ss: Number; sig: Signature;
- BEGIN
- ASSERT( private );
- IF (len > q.len*4 ) OR (len > 50) THEN HALT( 102 ) END;
- B.AssignBin( m, digest, 0, len );
- xr := B.Mul( y, r );
- ss := B.Add( xr, m ); (* s := inv( k ) * (m + x*r) mod q *)
- IF B.Cmp( ss, q ) > 0 THEN ss := B.Sub( ss, q ) END;
- ss := B.ModMul( ss, inv, q );
- NEW( sig, r, ss );
- RETURN sig
- END Sign;
- PROCEDURE Verify*( CONST digest: ARRAY OF CHAR; dlen: INTEGER; sig: Signature ): BOOLEAN;
- VAR
- u, v, w, t1, t2: Number;
- BEGIN
- ASSERT( ~private );
- IF B.Cmp( sig.r, q ) >= 0 THEN RETURN FALSE END;
- IF B.Cmp( sig.s, q ) >= 0 THEN RETURN FALSE END;
- (* w = inv( s ) mod q *)
- w := B.ModInverse( sig.s, q );
- (* v = m * w mod q *)
- B.AssignBin( v, digest, 0, dlen );
- v := B.ModMul( v, w, q );
- (* u = r * w mod q *)
- u := B.ModMul( sig.r, w, q );
- (* v = (g^v * y^u mod p) mod q *)
- t1 := B.ModExp( g, v, p );
- t2 := B.ModExp( y, u, p);
- v := B.ModMul( t1, t2, p );
- v := B.Mod( v, q );
- RETURN B.Cmp( v, sig.r ) = 0;
- END Verify;
- END Key;
- VAR
- one: Number; (* constant *)
- PROCEDURE GenParams*( dsa: Key; bits: INTEGER; CONST seed: ARRAY OF CHAR );
- VAR
- randomseed, pfound: BOOLEAN;
- i, k, n, count: INTEGER;
- test, W, r0: Number;
- sbuf, buf, buf2, md: ARRAY 20 OF CHAR;
- h: SHA1.Hash;
- BEGIN
- IF bits < 512 THEN
- bits := 512;
- ELSE
- bits := (bits + 63) DIV 64 * 64;
- END;
- randomseed := LEN( seed ) < 20;
- IF ~randomseed THEN
- FOR i := 0 TO 19 DO sbuf[i] := seed[i] END;
- END;
- B.AssignInt( test, 1 );
- test.Shift( bits - 1 );
- NEW( h );
- pfound := FALSE;
- LOOP (* find q and p *)
- REPEAT (* find q *)
- IF randomseed THEN
- B.RandomBytes( sbuf, 0, 20 );
- END;
- buf := sbuf;
- buf2 := sbuf;
- (* precompute "SEED + 1" *)
- REPEAT
- DEC( i );
- buf[i] := CHR( ORD( buf[i] ) + 1);
- UNTIL (i = 0) OR (buf[i] # 0X );
- h.Initialize;
- h.Update( sbuf, 0, 20 );
- h.GetHash( md, 0 );
- h.Initialize;
- h.Update( buf, 0, 20 );
- h.GetHash( buf2, 0 );
- FOR i := 0 TO 19 DO (* md := md xor buf2*)
- md[ i ] := BIT.CXOR( md[ i ], buf2[ i ] );
- END;
- IF ORD( md[0] ) < 128 THEN
- md[0] := CHR( ORD( md[0] ) + 128 );
- END;
- IF ~ODD( ORD( md[19] ) ) THEN
- md[19] := CHR( ORD( md[19] ) + 1 );
- END;
- B.AssignBin( dsa.q, md, 0, 20 );
- UNTIL P.IsPrime( dsa.q, 50, randomseed );
- count := 0; n := bits DIV 160;
- LOOP (* find p *)
- B.AssignInt( W, 0 );
- (* now 'buf' contains "SEED + offset - 1" *)
- FOR k := 0 TO n DO
- (* obtain "SEED + offset + k" by incrementing: *)
- i := 20;
- REPEAT
- DEC( i );
- buf[i] := CHR( ORD( buf[i] ) + 1)
- UNTIL (i = 0) OR (buf[i] # 0X );
- h.Initialize;
- h.Update( buf, 0, 20 );
- h.GetHash( md, 0 );
- B.AssignBin( r0, md, 0, 20 );
- r0.Shift( 160*k ); W := B.Add( W, r0 );
- END;
- W.Mask( bits - 1);
- W := B.Add( W, test );
- B.Copy( dsa.q, r0 );
- r0.Shift( 1 );
- r0 := B.Mod( W, r0 );
- r0.Dec;
- dsa.p := B.Sub( W, r0 );
- IF B.Cmp( dsa.p, test ) >= 0 THEN
- IF P.IsPrime( dsa.p, 50, TRUE ) THEN
- pfound := TRUE;
- EXIT;
- END;
- END;
- INC( count );
- IF count >= 4096 THEN EXIT END;
- END; (* find p *)
- IF pfound THEN EXIT END;
- END; (* find q and p *)
- B.Copy( dsa.p, test );
- test.Dec;
- r0 := B.Div( test, dsa.q ); (* r0 := (p-1)/q *)
- B.AssignInt( test, 2 );
- LOOP (* g := test ^ r0 mod p *)
- dsa.g := B.ModExp( test, r0, dsa.p );
- IF B.Cmp( dsa.g, one ) # 0 THEN EXIT END;
- test.Inc;
- END;
- END GenParams;
- PROCEDURE MakeKeys*( bits: INTEGER; CONST seed: ARRAY OF CHAR; VAR pub, priv: Key );
- BEGIN
- NEW( priv );
- GenParams( priv, bits, seed );
- REPEAT
- priv.y := B.NewRandRange( priv.q )
- UNTIL ~priv.y.IsZero( );
- NEW( pub ); pub^ := priv^;
- pub.y := B.ModExp( priv.g, priv.y, priv.p );
- priv.private := TRUE;
- priv.r := B.Mod( pub.y, priv.q ); (* r := (g ^ k mod p) mod q *)
- priv.inv := B.ModInverse( priv.y, priv.q ) (* part of 's := inv( k ) * (m + x*r) mod q' *)
- END MakeKeys;
- (** returns a new public key with exponent e and modulus m *)
- PROCEDURE PubKey*( p, q, g, y: Number ): Key;
- VAR dsa: Key;
- BEGIN
- NEW( dsa );
- dsa.name := "unkown";
- dsa.private := FALSE;
- B.Copy( p, dsa.p );
- B.Copy( q, dsa.q );
- B.Copy( g, dsa.g );
- B.Copy( y, dsa.y );
- RETURN dsa
- END PubKey;
- PROCEDURE LoadPrivateKey*( r: Streams.Reader; CONST passwd: ARRAY OF CHAR ): Key;
- (* TODO *)
- END LoadPrivateKey;
- PROCEDURE StorePrivateKey*( w: Streams.Writer; k: Key; CONST passwd: ARRAY OF CHAR );
- (* TODO *)
- END StorePrivateKey;
- PROCEDURE StorePublicKey*( w: Streams.Writer; k: Key ); (* openssh format *)
- VAR buf, encoded: ARRAY 4096 OF CHAR; pos: LONGINT;
- BEGIN
- ASSERT( ~k.private );
- w.String( "ssh-dss " );
- pos := 0;
- U.PutString( buf, pos, "ssh-dss" );
- U.PutBigNumber( buf, pos, k.p );
- U.PutBigNumber( buf, pos, k.q );
- U.PutBigNumber( buf, pos, k.g );
- U.PutBigNumber( buf, pos, k.y );
- Base64.Encode( buf, pos, encoded );
- w.String( encoded );
- w.String( " user@Aos" )
- END StorePublicKey;
- PROCEDURE LoadPublicKey*( r: Streams.Reader ): Key;
- VAR buf: ARRAY 4096 OF CHAR; len, pos: LONGINT;
- str: ARRAY 64 OF CHAR;
- k: Key;
- BEGIN
- NEW( k ); k.private := FALSE;
- len := Base64.DecodeStream( r, buf );
- pos := 0;
- U.GetString( buf, pos, str );
- ASSERT( str = "ssh-dss" );
- U.GetBigNumber( buf, pos, k.p );
- U.GetBigNumber( buf, pos, k.q );
- U.GetBigNumber( buf, pos, k.g );
- U.GetBigNumber( buf, pos, k.y );
- RETURN k
- END LoadPublicKey;
- BEGIN
- B.AssignInt( one, 1 );
- END CryptoDSA.
- System.Free CryptoDSA ~
|