Home | History | Annotate | Download | only in signers
      1 package org.bouncycastle.crypto.signers;
      2 
      3 import java.math.BigInteger;
      4 import java.security.SecureRandom;
      5 
      6 import org.bouncycastle.crypto.CipherParameters;
      7 import org.bouncycastle.crypto.DSA;
      8 import org.bouncycastle.crypto.params.DSAKeyParameters;
      9 import org.bouncycastle.crypto.params.DSAParameters;
     10 import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
     11 import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
     12 import org.bouncycastle.crypto.params.ParametersWithRandom;
     13 
     14 /**
     15  * The Digital Signature Algorithm - as described in "Handbook of Applied
     16  * Cryptography", pages 452 - 453.
     17  */
     18 public class DSASigner
     19     implements DSA
     20 {
     21     private final DSAKCalculator kCalculator;
     22 
     23     private DSAKeyParameters key;
     24     private SecureRandom    random;
     25 
     26     /**
     27      * Default configuration, random K values.
     28      */
     29     public DSASigner()
     30     {
     31         this.kCalculator = new RandomDSAKCalculator();
     32     }
     33 
     34     /**
     35      * Configuration with an alternate, possibly deterministic calculator of K.
     36      *
     37      * @param kCalculator a K value calculator.
     38      */
     39     public DSASigner(DSAKCalculator kCalculator)
     40     {
     41         this.kCalculator = kCalculator;
     42     }
     43 
     44     public void init(
     45         boolean                 forSigning,
     46         CipherParameters        param)
     47     {
     48         SecureRandom providedRandom = null;
     49 
     50         if (forSigning)
     51         {
     52             if (param instanceof ParametersWithRandom)
     53             {
     54                 ParametersWithRandom rParam = (ParametersWithRandom)param;
     55 
     56                 this.key = (DSAPrivateKeyParameters)rParam.getParameters();
     57                 providedRandom = rParam.getRandom();
     58             }
     59             else
     60             {
     61                 this.key = (DSAPrivateKeyParameters)param;
     62             }
     63         }
     64         else
     65         {
     66             this.key = (DSAPublicKeyParameters)param;
     67         }
     68 
     69         this.random = initSecureRandom(forSigning && !kCalculator.isDeterministic(), providedRandom);
     70     }
     71 
     72     /**
     73      * generate a signature for the given message using the key we were
     74      * initialised with. For conventional DSA the message should be a SHA-1
     75      * hash of the message of interest.
     76      *
     77      * @param message the message that will be verified later.
     78      */
     79     public BigInteger[] generateSignature(
     80         byte[] message)
     81     {
     82         DSAParameters   params = key.getParameters();
     83         BigInteger      q = params.getQ();
     84         BigInteger      m = calculateE(q, message);
     85         BigInteger      x = ((DSAPrivateKeyParameters)key).getX();
     86 
     87         if (kCalculator.isDeterministic())
     88         {
     89             kCalculator.init(q, x, message);
     90         }
     91         else
     92         {
     93             kCalculator.init(q, random);
     94         }
     95 
     96         BigInteger  k = kCalculator.nextK();
     97 
     98         // the randomizer is to conceal timing information related to k and x.
     99         BigInteger  r = params.getG().modPow(k.add(getRandomizer(q, random)), params.getP()).mod(q);
    100 
    101         k = k.modInverse(q).multiply(m.add(x.multiply(r)));
    102 
    103         BigInteger  s = k.mod(q);
    104 
    105         return new BigInteger[]{ r, s };
    106     }
    107 
    108     /**
    109      * return true if the value r and s represent a DSA signature for
    110      * the passed in message for standard DSA the message should be a
    111      * SHA-1 hash of the real message to be verified.
    112      */
    113     public boolean verifySignature(
    114         byte[]      message,
    115         BigInteger  r,
    116         BigInteger  s)
    117     {
    118         DSAParameters   params = key.getParameters();
    119         BigInteger      q = params.getQ();
    120         BigInteger      m = calculateE(q, message);
    121         BigInteger      zero = BigInteger.valueOf(0);
    122 
    123         if (zero.compareTo(r) >= 0 || q.compareTo(r) <= 0)
    124         {
    125             return false;
    126         }
    127 
    128         if (zero.compareTo(s) >= 0 || q.compareTo(s) <= 0)
    129         {
    130             return false;
    131         }
    132 
    133         BigInteger  w = s.modInverse(q);
    134 
    135         BigInteger  u1 = m.multiply(w).mod(q);
    136         BigInteger  u2 = r.multiply(w).mod(q);
    137 
    138         BigInteger p = params.getP();
    139         u1 = params.getG().modPow(u1, p);
    140         u2 = ((DSAPublicKeyParameters)key).getY().modPow(u2, p);
    141 
    142         BigInteger  v = u1.multiply(u2).mod(p).mod(q);
    143 
    144         return v.equals(r);
    145     }
    146 
    147     private BigInteger calculateE(BigInteger n, byte[] message)
    148     {
    149         if (n.bitLength() >= message.length * 8)
    150         {
    151             return new BigInteger(1, message);
    152         }
    153         else
    154         {
    155             byte[] trunc = new byte[n.bitLength() / 8];
    156 
    157             System.arraycopy(message, 0, trunc, 0, trunc.length);
    158 
    159             return new BigInteger(1, trunc);
    160         }
    161     }
    162 
    163     protected SecureRandom initSecureRandom(boolean needed, SecureRandom provided)
    164     {
    165         return !needed ? null : (provided != null) ? provided : new SecureRandom();
    166     }
    167 
    168     private BigInteger getRandomizer(BigInteger q, SecureRandom provided)
    169     {
    170         // Calculate a random multiple of q to add to k. Note that g^q = 1 (mod p), so adding multiple of q to k does not change r.
    171         int randomBits = 7;
    172 
    173         return new BigInteger(randomBits, provided != null ? provided : new SecureRandom()).add(BigInteger.valueOf(128)).multiply(q);
    174     }
    175 }
    176