src/java.base/share/classes/sun/security/provider/DSA.java
changeset 47216 71c04702a3d5
parent 45990 ba8bfbb9d633
child 48080 18a4438eb690
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/provider/DSA.java	Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,659 @@
+/*
+ * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider;
+
+import java.io.*;
+import java.util.*;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+
+import java.security.*;
+import java.security.SecureRandom;
+import java.security.interfaces.*;
+
+import sun.security.util.Debug;
+import sun.security.util.DerValue;
+import sun.security.util.DerInputStream;
+import sun.security.util.DerOutputStream;
+import sun.security.jca.JCAUtil;
+
+/**
+ * The Digital Signature Standard (using the Digital Signature
+ * Algorithm), as described in fips186-3 of the National Instute of
+ * Standards and Technology (NIST), using SHA digest algorithms
+ * from FIPS180-3.
+ *
+ * This file contains both the signature implementation for the
+ * commonly used SHA1withDSA (DSS), SHA224withDSA, SHA256withDSA,
+ * as well as RawDSA, used by TLS among others. RawDSA expects
+ * the 20 byte SHA-1 digest as input via update rather than the
+ * original data like other signature implementations.
+ *
+ * @author Benjamin Renaud
+ *
+ * @since   1.1
+ *
+ * @see DSAPublicKey
+ * @see DSAPrivateKey
+ */
+abstract class DSA extends SignatureSpi {
+
+    /* Are we debugging? */
+    private static final boolean debug = false;
+
+    /* The number of bits used in exponent blinding */
+    private static final int BLINDING_BITS = 7;
+
+    /* The constant component of the exponent blinding value */
+    private static final BigInteger BLINDING_CONSTANT =
+        BigInteger.valueOf(1 << BLINDING_BITS);
+
+    /* The parameter object */
+    private DSAParams params;
+
+    /* algorithm parameters */
+    private BigInteger presetP, presetQ, presetG;
+
+    /* The public key, if any */
+    private BigInteger presetY;
+
+    /* The private key, if any */
+    private BigInteger presetX;
+
+    /* The RNG used to output a seed for generating k */
+    private SecureRandom signingRandom;
+
+    /* The message digest object used */
+    private final MessageDigest md;
+
+    /* The format. true for the IEEE P1363 format. false (default) for ASN.1 */
+    private final boolean p1363Format;
+
+    /**
+     * Construct a blank DSA object. It must be
+     * initialized before being usable for signing or verifying.
+     */
+    DSA(MessageDigest md) {
+        this(md, false);
+    }
+
+    /**
+     * Construct a blank DSA object that will use the specified
+     * signature format. {@code p1363Format} should be {@code true} to
+     * use the IEEE P1363 format. If {@code p1363Format} is {@code false},
+     * the DER-encoded ASN.1 format will used. The DSA object must be
+     * initialized before being usable for signing or verifying.
+     */
+    DSA(MessageDigest md, boolean p1363Format) {
+        super();
+        this.md = md;
+        this.p1363Format = p1363Format;
+    }
+
+    private static void checkKey(DSAParams params, int digestLen, String mdAlgo)
+        throws InvalidKeyException {
+        // FIPS186-3 states in sec4.2 that a hash function which provides
+        // a lower security strength than the (L, N) pair ordinarily should
+        // not be used.
+        int valueN = params.getQ().bitLength();
+        if (valueN > digestLen) {
+            throw new InvalidKeyException("The security strength of " +
+                mdAlgo + " digest algorithm is not sufficient for this key size");
+        }
+    }
+
+    /**
+     * Initialize the DSA object with a DSA private key.
+     *
+     * @param privateKey the DSA private key
+     *
+     * @exception InvalidKeyException if the key is not a valid DSA private
+     * key.
+     */
+    protected void engineInitSign(PrivateKey privateKey)
+            throws InvalidKeyException {
+        if (!(privateKey instanceof java.security.interfaces.DSAPrivateKey)) {
+            throw new InvalidKeyException("not a DSA private key: " +
+                                          privateKey);
+        }
+
+        java.security.interfaces.DSAPrivateKey priv =
+            (java.security.interfaces.DSAPrivateKey)privateKey;
+
+        // check for algorithm specific constraints before doing initialization
+        DSAParams params = priv.getParams();
+        if (params == null) {
+            throw new InvalidKeyException("DSA private key lacks parameters");
+        }
+
+        // check key size against hash output size for signing
+        // skip this check for verification to minimize impact on existing apps
+        if (md.getAlgorithm() != "NullDigest20") {
+            checkKey(params, md.getDigestLength()*8, md.getAlgorithm());
+        }
+
+        this.params = params;
+        this.presetX = priv.getX();
+        this.presetY = null;
+        this.presetP = params.getP();
+        this.presetQ = params.getQ();
+        this.presetG = params.getG();
+        this.md.reset();
+    }
+    /**
+     * Initialize the DSA object with a DSA public key.
+     *
+     * @param publicKey the DSA public key.
+     *
+     * @exception InvalidKeyException if the key is not a valid DSA public
+     * key.
+     */
+    protected void engineInitVerify(PublicKey publicKey)
+            throws InvalidKeyException {
+        if (!(publicKey instanceof java.security.interfaces.DSAPublicKey)) {
+            throw new InvalidKeyException("not a DSA public key: " +
+                                          publicKey);
+        }
+        java.security.interfaces.DSAPublicKey pub =
+            (java.security.interfaces.DSAPublicKey)publicKey;
+
+        // check for algorithm specific constraints before doing initialization
+        DSAParams params = pub.getParams();
+        if (params == null) {
+            throw new InvalidKeyException("DSA public key lacks parameters");
+        }
+        this.params = params;
+        this.presetY = pub.getY();
+        this.presetX = null;
+        this.presetP = params.getP();
+        this.presetQ = params.getQ();
+        this.presetG = params.getG();
+        this.md.reset();
+    }
+
+    /**
+     * Update a byte to be signed or verified.
+     */
+    protected void engineUpdate(byte b) {
+        md.update(b);
+    }
+
+    /**
+     * Update an array of bytes to be signed or verified.
+     */
+    protected void engineUpdate(byte[] data, int off, int len) {
+        md.update(data, off, len);
+    }
+
+    protected void engineUpdate(ByteBuffer b) {
+        md.update(b);
+    }
+
+
+    /**
+     * Sign all the data thus far updated. The signature format is
+     * determined by {@code p1363Format}. If {@code p1363Format} is
+     * {@code false} (the default), then the signature is formatted
+     * according to the Canonical Encoding Rules, returned as a DER
+     * sequence of Integers, r and s. If {@code p1363Format} is
+     * {@code false}, the signature is returned in the IEEE P1363
+     * format, which is the concatenation or r and s.
+     *
+     * @return a signature block formatted according to the format
+     * indicated by {@code p1363Format}
+     *
+     * @exception SignatureException if the signature object was not
+     * properly initialized, or if another exception occurs.
+     *
+     * @see sun.security.DSA#engineUpdate
+     * @see sun.security.DSA#engineVerify
+     */
+    protected byte[] engineSign() throws SignatureException {
+        BigInteger k = generateK(presetQ);
+        BigInteger r = generateR(presetP, presetQ, presetG, k);
+        BigInteger s = generateS(presetX, presetQ, r, k);
+
+        if (p1363Format) {
+            // Return the concatenation of r and s
+            byte[] rBytes = r.toByteArray();
+            byte[] sBytes = s.toByteArray();
+
+            int size = presetQ.bitLength() / 8;
+            byte[] outseq = new byte[size * 2];
+
+            int rLength = rBytes.length;
+            int sLength = sBytes.length;
+            int i;
+            for (i = rLength; i > 0 && rBytes[rLength - i] == 0; i--);
+
+            int j;
+            for (j = sLength;
+                    j > 0 && sBytes[sLength - j] == 0; j--);
+
+            System.arraycopy(rBytes, rLength - i, outseq, size - i, i);
+            System.arraycopy(sBytes, sLength - j, outseq, size * 2 - j, j);
+
+            return outseq;
+        } else {
+            // Return the DER-encoded ASN.1 form
+            try {
+                DerOutputStream outseq = new DerOutputStream(100);
+                outseq.putInteger(r);
+                outseq.putInteger(s);
+                DerValue result = new DerValue(DerValue.tag_Sequence,
+                        outseq.toByteArray());
+
+                return result.toByteArray();
+
+            } catch (IOException e) {
+                throw new SignatureException("error encoding signature");
+            }
+        }
+    }
+
+    /**
+     * Verify all the data thus far updated.
+     *
+     * @param signature the alleged signature, encoded using the
+     * Canonical Encoding Rules, as a sequence of integers, r and s.
+     *
+     * @exception SignatureException if the signature object was not
+     * properly initialized, or if another exception occurs.
+     *
+     * @see sun.security.DSA#engineUpdate
+     * @see sun.security.DSA#engineSign
+     */
+    protected boolean engineVerify(byte[] signature)
+            throws SignatureException {
+        return engineVerify(signature, 0, signature.length);
+    }
+
+    /**
+     * Verify all the data thus far updated.
+     *
+     * @param signature the alleged signature, encoded using the
+     * format indicated by {@code p1363Format}. If {@code p1363Format}
+     * is {@code false} (the default), then the signature is formatted
+     * according to the Canonical Encoding Rules, as a DER sequence of
+     * Integers, r and s. If {@code p1363Format} is {@code false},
+     * the signature is in the IEEE P1363 format, which is the
+     * concatenation or r and s.
+     *
+     * @param offset the offset to start from in the array of bytes.
+     *
+     * @param length the number of bytes to use, starting at offset.
+     *
+     * @exception SignatureException if the signature object was not
+     * properly initialized, or if another exception occurs.
+     *
+     * @see sun.security.DSA#engineUpdate
+     * @see sun.security.DSA#engineSign
+     */
+    protected boolean engineVerify(byte[] signature, int offset, int length)
+            throws SignatureException {
+
+        BigInteger r = null;
+        BigInteger s = null;
+
+        if (p1363Format) {
+            if ((length & 1) == 1) {
+                // length of signature byte array should be even
+                throw new SignatureException("invalid signature format");
+            }
+            int mid = length/2;
+            r = new BigInteger(Arrays.copyOfRange(signature, 0, mid));
+            s = new BigInteger(Arrays.copyOfRange(signature, mid, length));
+        } else {
+            // first decode the signature.
+            try {
+                // Enforce strict DER checking for signatures
+                DerInputStream in =
+                    new DerInputStream(signature, offset, length, false);
+                DerValue[] values = in.getSequence(2);
+
+                // check number of components in the read sequence
+                // and trailing data
+                if ((values.length != 2) || (in.available() != 0)) {
+                    throw new IOException("Invalid encoding for signature");
+                }
+                r = values[0].getBigInteger();
+                s = values[1].getBigInteger();
+            } catch (IOException e) {
+                throw new SignatureException("Invalid encoding for signature", e);
+            }
+        }
+
+        // some implementations do not correctly encode values in the ASN.1
+        // 2's complement format. force r and s to be positive in order to
+        // to validate those signatures
+        if (r.signum() < 0) {
+            r = new BigInteger(1, r.toByteArray());
+        }
+        if (s.signum() < 0) {
+            s = new BigInteger(1, s.toByteArray());
+        }
+
+        if ((r.compareTo(presetQ) == -1) && (s.compareTo(presetQ) == -1)) {
+            BigInteger w = generateW(presetP, presetQ, presetG, s);
+            BigInteger v = generateV(presetY, presetP, presetQ, presetG, w, r);
+            return v.equals(r);
+        } else {
+            throw new SignatureException("invalid signature: out of range values");
+        }
+    }
+
+    @Deprecated
+    protected void engineSetParameter(String key, Object param) {
+        throw new InvalidParameterException("No parameter accepted");
+    }
+
+    @Deprecated
+    protected Object engineGetParameter(String key) {
+        return null;
+    }
+
+
+    private BigInteger generateR(BigInteger p, BigInteger q, BigInteger g,
+                         BigInteger k) {
+
+        // exponent blinding to hide information from timing channel
+        SecureRandom random = getSigningRandom();
+        // start with a random blinding component
+        BigInteger blindingValue = new BigInteger(BLINDING_BITS, random);
+        // add the fixed blinding component
+        blindingValue = blindingValue.add(BLINDING_CONSTANT);
+        // replace k with a blinded value that is congruent (mod q)
+        k = k.add(q.multiply(blindingValue));
+
+        BigInteger temp = g.modPow(k, p);
+        return temp.mod(q);
+    }
+
+    private BigInteger generateS(BigInteger x, BigInteger q,
+            BigInteger r, BigInteger k) throws SignatureException {
+
+        byte[] s2;
+        try {
+            s2 = md.digest();
+        } catch (RuntimeException re) {
+            // Only for RawDSA due to its 20-byte length restriction
+            throw new SignatureException(re.getMessage());
+        }
+        // get the leftmost min(N, outLen) bits of the digest value
+        int nBytes = q.bitLength()/8;
+        if (nBytes < s2.length) {
+            s2 = Arrays.copyOfRange(s2, 0, nBytes);
+        }
+        BigInteger z = new BigInteger(1, s2);
+        BigInteger k1 = k.modInverse(q);
+
+        return x.multiply(r).add(z).multiply(k1).mod(q);
+    }
+
+    private BigInteger generateW(BigInteger p, BigInteger q,
+                         BigInteger g, BigInteger s) {
+        return s.modInverse(q);
+    }
+
+    private BigInteger generateV(BigInteger y, BigInteger p,
+             BigInteger q, BigInteger g, BigInteger w, BigInteger r)
+             throws SignatureException {
+
+        byte[] s2;
+        try {
+            s2 = md.digest();
+        } catch (RuntimeException re) {
+            // Only for RawDSA due to its 20-byte length restriction
+            throw new SignatureException(re.getMessage());
+        }
+        // get the leftmost min(N, outLen) bits of the digest value
+        int nBytes = q.bitLength()/8;
+        if (nBytes < s2.length) {
+            s2 = Arrays.copyOfRange(s2, 0, nBytes);
+        }
+        BigInteger z = new BigInteger(1, s2);
+
+        BigInteger u1 = z.multiply(w).mod(q);
+        BigInteger u2 = (r.multiply(w)).mod(q);
+
+        BigInteger t1 = g.modPow(u1,p);
+        BigInteger t2 = y.modPow(u2,p);
+        BigInteger t3 = t1.multiply(t2);
+        BigInteger t5 = t3.mod(p);
+        return t5.mod(q);
+    }
+
+    protected BigInteger generateK(BigInteger q) {
+        // Implementation defined in FIPS 186-4 AppendixB.2.1.
+        SecureRandom random = getSigningRandom();
+        byte[] kValue = new byte[(q.bitLength() + 7)/8 + 8];
+
+        random.nextBytes(kValue);
+        return new BigInteger(1, kValue).mod(
+                q.subtract(BigInteger.ONE)).add(BigInteger.ONE);
+    }
+
+    // Use the application-specified SecureRandom Object if provided.
+    // Otherwise, use our default SecureRandom Object.
+    protected SecureRandom getSigningRandom() {
+        if (signingRandom == null) {
+            if (appRandom != null) {
+                signingRandom = appRandom;
+            } else {
+                signingRandom = JCAUtil.getSecureRandom();
+            }
+        }
+        return signingRandom;
+    }
+
+    /**
+     * Return a human readable rendition of the engine.
+     */
+    public String toString() {
+        String printable = "DSA Signature";
+        if (presetP != null && presetQ != null && presetG != null) {
+            printable += "\n\tp: " + Debug.toHexString(presetP);
+            printable += "\n\tq: " + Debug.toHexString(presetQ);
+            printable += "\n\tg: " + Debug.toHexString(presetG);
+        } else {
+            printable += "\n\t P, Q or G not initialized.";
+        }
+        if (presetY != null) {
+            printable += "\n\ty: " + Debug.toHexString(presetY);
+        }
+        if (presetY == null && presetX == null) {
+            printable += "\n\tUNINIIALIZED";
+        }
+        return printable;
+    }
+
+    private static void debug(Exception e) {
+        if (debug) {
+            e.printStackTrace();
+        }
+    }
+
+    private static void debug(String s) {
+        if (debug) {
+            System.err.println(s);
+        }
+    }
+
+    /**
+     * Standard SHA224withDSA implementation as defined in FIPS186-3.
+     */
+    public static final class SHA224withDSA extends DSA {
+        public SHA224withDSA() throws NoSuchAlgorithmException {
+            super(MessageDigest.getInstance("SHA-224"));
+        }
+    }
+
+    /**
+     * SHA224withDSA implementation that uses the IEEE P1363 format.
+     */
+    public static final class SHA224withDSAinP1363Format extends DSA {
+        public SHA224withDSAinP1363Format() throws NoSuchAlgorithmException {
+            super(MessageDigest.getInstance("SHA-224"), true);
+        }
+    }
+
+    /**
+     * Standard SHA256withDSA implementation as defined in FIPS186-3.
+     */
+    public static final class SHA256withDSA extends DSA {
+        public SHA256withDSA() throws NoSuchAlgorithmException {
+            super(MessageDigest.getInstance("SHA-256"));
+        }
+    }
+
+    /**
+     * SHA256withDSA implementation that uses the IEEE P1363 format.
+     */
+    public static final class SHA256withDSAinP1363Format extends DSA {
+        public SHA256withDSAinP1363Format() throws NoSuchAlgorithmException {
+            super(MessageDigest.getInstance("SHA-256"), true);
+        }
+    }
+
+    /**
+     * Standard SHA1withDSA implementation.
+     */
+    public static final class SHA1withDSA extends DSA {
+        public SHA1withDSA() throws NoSuchAlgorithmException {
+            super(MessageDigest.getInstance("SHA-1"));
+        }
+    }
+
+    /**
+     * SHA1withDSA implementation that uses the IEEE P1363 format.
+     */
+    public static final class SHA1withDSAinP1363Format extends DSA {
+        public SHA1withDSAinP1363Format() throws NoSuchAlgorithmException {
+            super(MessageDigest.getInstance("SHA-1"), true);
+        }
+    }
+
+    /**
+     * Raw DSA.
+     *
+     * Raw DSA requires the data to be exactly 20 bytes long. If it is
+     * not, a SignatureException is thrown when sign()/verify() is called
+     * per JCA spec.
+     */
+    static class Raw extends DSA {
+        // Internal special-purpose MessageDigest impl for RawDSA
+        // Only override whatever methods used
+        // NOTE: no clone support
+        public static final class NullDigest20 extends MessageDigest {
+            // 20 byte digest buffer
+            private final byte[] digestBuffer = new byte[20];
+
+            // offset into the buffer; use Integer.MAX_VALUE to indicate
+            // out-of-bound condition
+            private int ofs = 0;
+
+            protected NullDigest20() {
+                super("NullDigest20");
+            }
+            protected void engineUpdate(byte input) {
+                if (ofs == digestBuffer.length) {
+                    ofs = Integer.MAX_VALUE;
+                } else {
+                    digestBuffer[ofs++] = input;
+                }
+            }
+            protected void engineUpdate(byte[] input, int offset, int len) {
+                if (ofs + len > digestBuffer.length) {
+                    ofs = Integer.MAX_VALUE;
+                } else {
+                    System.arraycopy(input, offset, digestBuffer, ofs, len);
+                    ofs += len;
+                }
+            }
+            protected final void engineUpdate(ByteBuffer input) {
+                int inputLen = input.remaining();
+                if (ofs + inputLen > digestBuffer.length) {
+                    ofs = Integer.MAX_VALUE;
+                } else {
+                    input.get(digestBuffer, ofs, inputLen);
+                    ofs += inputLen;
+                }
+            }
+            protected byte[] engineDigest() throws RuntimeException {
+                if (ofs != digestBuffer.length) {
+                    throw new RuntimeException
+                        ("Data for RawDSA must be exactly 20 bytes long");
+                }
+                reset();
+                return digestBuffer;
+            }
+            protected int engineDigest(byte[] buf, int offset, int len)
+                throws DigestException {
+                if (ofs != digestBuffer.length) {
+                    throw new DigestException
+                        ("Data for RawDSA must be exactly 20 bytes long");
+                }
+                if (len < digestBuffer.length) {
+                    throw new DigestException
+                        ("Output buffer too small; must be at least 20 bytes");
+                }
+                System.arraycopy(digestBuffer, 0, buf, offset, digestBuffer.length);
+                reset();
+                return digestBuffer.length;
+            }
+
+            protected void engineReset() {
+                ofs = 0;
+            }
+            protected final int engineGetDigestLength() {
+                return digestBuffer.length;
+            }
+        }
+
+        private Raw(boolean p1363Format) throws NoSuchAlgorithmException {
+            super(new NullDigest20(), p1363Format);
+        }
+
+    }
+
+    /**
+     * Standard Raw DSA implementation.
+     */
+    public static final class RawDSA extends Raw {
+        public RawDSA() throws NoSuchAlgorithmException {
+            super(false);
+        }
+    }
+
+    /**
+     * Raw DSA implementation that uses the IEEE P1363 format.
+     */
+    public static final class RawDSAinP1363Format extends Raw {
+        public RawDSAinP1363Format() throws NoSuchAlgorithmException {
+            super(true);
+        }
+    }
+}