mobile/android/base/browserid/DSACryptoImplementation.java

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 package org.mozilla.gecko.browserid;
     7 import java.math.BigInteger;
     8 import java.security.GeneralSecurityException;
     9 import java.security.KeyFactory;
    10 import java.security.KeyPair;
    11 import java.security.KeyPairGenerator;
    12 import java.security.NoSuchAlgorithmException;
    13 import java.security.Signature;
    14 import java.security.interfaces.DSAParams;
    15 import java.security.interfaces.DSAPrivateKey;
    16 import java.security.interfaces.DSAPublicKey;
    17 import java.security.spec.DSAPrivateKeySpec;
    18 import java.security.spec.DSAPublicKeySpec;
    19 import java.security.spec.InvalidKeySpecException;
    20 import java.security.spec.KeySpec;
    22 import org.mozilla.gecko.sync.ExtendedJSONObject;
    23 import org.mozilla.gecko.sync.NonObjectJSONException;
    24 import org.mozilla.gecko.sync.Utils;
    26 public class DSACryptoImplementation {
    27   public static final String SIGNATURE_ALGORITHM = "SHA1withDSA";
    28   public static final int SIGNATURE_LENGTH_BYTES = 40; // DSA signatures are always 40 bytes long.
    30   /**
    31    * Parameters are serialized as hex strings. Hex-versus-decimal was
    32    * reverse-engineered from what the Persona public verifier accepted. We
    33    * expect to follow the JOSE/JWT spec as it solidifies, and that will probably
    34    * mean unifying this base.
    35    */
    36   protected static final int SERIALIZATION_BASE = 16;
    38   protected static class DSAVerifyingPublicKey implements VerifyingPublicKey {
    39     protected final DSAPublicKey publicKey;
    41     public DSAVerifyingPublicKey(DSAPublicKey publicKey) {
    42       this.publicKey = publicKey;
    43     }
    45     /**
    46      * Serialize to a JSON object.
    47      * <p>
    48      * Parameters are serialized as hex strings. Hex-versus-decimal was
    49      * reverse-engineered from what the Persona public verifier accepted.
    50      */
    51     @Override
    52     public ExtendedJSONObject toJSONObject() {
    53       DSAParams params = publicKey.getParams();
    54       ExtendedJSONObject o = new ExtendedJSONObject();
    55       o.put("algorithm", "DS");
    56       o.put("y", publicKey.getY().toString(SERIALIZATION_BASE));
    57       o.put("g", params.getG().toString(SERIALIZATION_BASE));
    58       o.put("p", params.getP().toString(SERIALIZATION_BASE));
    59       o.put("q", params.getQ().toString(SERIALIZATION_BASE));
    60       return o;
    61     }
    63     @Override
    64     public boolean verifyMessage(byte[] bytes, byte[] signature)
    65         throws GeneralSecurityException {
    66       if (bytes == null) {
    67         throw new IllegalArgumentException("bytes must not be null");
    68       }
    69       if (signature == null) {
    70         throw new IllegalArgumentException("signature must not be null");
    71       }
    72       if (signature.length != SIGNATURE_LENGTH_BYTES) {
    73         return false;
    74       }
    75       byte[] first = new byte[signature.length / 2];
    76       byte[] second = new byte[signature.length / 2];
    77       System.arraycopy(signature, 0, first, 0, first.length);
    78       System.arraycopy(signature, first.length, second, 0, second.length);
    79       BigInteger r = new BigInteger(Utils.byte2Hex(first), 16);
    80       BigInteger s = new BigInteger(Utils.byte2Hex(second), 16);
    81       // This is awful, but encoding an extra 0 byte works better on devices.
    82       byte[] encoded = ASNUtils.encodeTwoArraysToASN1(
    83           Utils.hex2Byte(r.toString(16), 1 + SIGNATURE_LENGTH_BYTES / 2),
    84           Utils.hex2Byte(s.toString(16), 1 + SIGNATURE_LENGTH_BYTES / 2));
    86       final Signature signer = Signature.getInstance(SIGNATURE_ALGORITHM);
    87       signer.initVerify(publicKey);
    88       signer.update(bytes);
    89       return signer.verify(encoded);
    90     }
    91   }
    93   protected static class DSASigningPrivateKey implements SigningPrivateKey {
    94     protected final DSAPrivateKey privateKey;
    96     public DSASigningPrivateKey(DSAPrivateKey privateKey) {
    97       this.privateKey = privateKey;
    98     }
   100     @Override
   101     public String getAlgorithm() {
   102       return "DS" + (privateKey.getParams().getP().bitLength() + 7)/8;
   103     }
   105     /**
   106      * Serialize to a JSON object.
   107      * <p>
   108      * Parameters are serialized as decimal strings. Hex-versus-decimal was
   109      * reverse-engineered from what the Persona public verifier accepted.
   110      */
   111     @Override
   112     public ExtendedJSONObject toJSONObject() {
   113       DSAParams params = privateKey.getParams();
   114       ExtendedJSONObject o = new ExtendedJSONObject();
   115       o.put("algorithm", "DS");
   116       o.put("x", privateKey.getX().toString(SERIALIZATION_BASE));
   117       o.put("g", params.getG().toString(SERIALIZATION_BASE));
   118       o.put("p", params.getP().toString(SERIALIZATION_BASE));
   119       o.put("q", params.getQ().toString(SERIALIZATION_BASE));
   120       return o;
   121     }
   123     @Override
   124     public byte[] signMessage(byte[] bytes)
   125         throws GeneralSecurityException {
   126       if (bytes == null) {
   127         throw new IllegalArgumentException("bytes must not be null");
   128       }
   129       final Signature signer = Signature.getInstance(SIGNATURE_ALGORITHM);
   130       signer.initSign(privateKey);
   131       signer.update(bytes);
   132       final byte[] signature = signer.sign();
   134       final byte[][] arrays = ASNUtils.decodeTwoArraysFromASN1(signature);
   135       BigInteger r = new BigInteger(arrays[0]);
   136       BigInteger s = new BigInteger(arrays[1]);
   137       // This is awful, but signatures are always 40 bytes long.
   138       byte[] decoded = Utils.concatAll(
   139           Utils.hex2Byte(r.toString(16), SIGNATURE_LENGTH_BYTES / 2),
   140           Utils.hex2Byte(s.toString(16), SIGNATURE_LENGTH_BYTES / 2));
   141       return decoded;
   142     }
   143   }
   145   public static BrowserIDKeyPair generateKeyPair(int keysize)
   146       throws NoSuchAlgorithmException {
   147     final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");
   148     keyPairGenerator.initialize(keysize);
   149     final KeyPair keyPair = keyPairGenerator.generateKeyPair();
   150     DSAPrivateKey privateKey = (DSAPrivateKey) keyPair.getPrivate();
   151     DSAPublicKey publicKey = (DSAPublicKey) keyPair.getPublic();
   152     return new BrowserIDKeyPair(new DSASigningPrivateKey(privateKey), new DSAVerifyingPublicKey(publicKey));
   153   }
   155   public static SigningPrivateKey createPrivateKey(BigInteger x, BigInteger p, BigInteger q, BigInteger g) throws NoSuchAlgorithmException, InvalidKeySpecException {
   156     if (x == null) {
   157       throw new IllegalArgumentException("x must not be null");
   158     }
   159     if (p == null) {
   160       throw new IllegalArgumentException("p must not be null");
   161     }
   162     if (q == null) {
   163       throw new IllegalArgumentException("q must not be null");
   164     }
   165     if (g == null) {
   166       throw new IllegalArgumentException("g must not be null");
   167     }
   168     KeySpec keySpec = new DSAPrivateKeySpec(x, p, q, g);
   169     KeyFactory keyFactory = KeyFactory.getInstance("DSA");
   170     DSAPrivateKey privateKey = (DSAPrivateKey) keyFactory.generatePrivate(keySpec);
   171     return new DSASigningPrivateKey(privateKey);
   172   }
   174   public static VerifyingPublicKey createPublicKey(BigInteger y, BigInteger p, BigInteger q, BigInteger g) throws NoSuchAlgorithmException, InvalidKeySpecException {
   175     if (y == null) {
   176       throw new IllegalArgumentException("n must not be null");
   177     }
   178     if (p == null) {
   179       throw new IllegalArgumentException("p must not be null");
   180     }
   181     if (q == null) {
   182       throw new IllegalArgumentException("q must not be null");
   183     }
   184     if (g == null) {
   185       throw new IllegalArgumentException("g must not be null");
   186     }
   187     KeySpec keySpec = new DSAPublicKeySpec(y, p, q, g);
   188     KeyFactory keyFactory = KeyFactory.getInstance("DSA");
   189     DSAPublicKey publicKey = (DSAPublicKey) keyFactory.generatePublic(keySpec);
   190     return new DSAVerifyingPublicKey(publicKey);
   191   }
   193   public static SigningPrivateKey createPrivateKey(ExtendedJSONObject o) throws InvalidKeySpecException, NoSuchAlgorithmException {
   194     String algorithm = o.getString("algorithm");
   195     if (!"DS".equals(algorithm)) {
   196       throw new InvalidKeySpecException("algorithm must equal DS, was " + algorithm);
   197     }
   198     try {
   199       BigInteger x = new BigInteger(o.getString("x"), SERIALIZATION_BASE);
   200       BigInteger p = new BigInteger(o.getString("p"), SERIALIZATION_BASE);
   201       BigInteger q = new BigInteger(o.getString("q"), SERIALIZATION_BASE);
   202       BigInteger g = new BigInteger(o.getString("g"), SERIALIZATION_BASE);
   203       return createPrivateKey(x, p, q, g);
   204     } catch (NullPointerException e) {
   205       throw new InvalidKeySpecException("x, p, q, and g must be integers encoded as strings, base " + SERIALIZATION_BASE);
   206     } catch (NumberFormatException e) {
   207       throw new InvalidKeySpecException("x, p, q, and g must be integers encoded as strings, base " + SERIALIZATION_BASE);
   208     }
   209   }
   211   public static VerifyingPublicKey createPublicKey(ExtendedJSONObject o) throws InvalidKeySpecException, NoSuchAlgorithmException {
   212     String algorithm = o.getString("algorithm");
   213     if (!"DS".equals(algorithm)) {
   214       throw new InvalidKeySpecException("algorithm must equal DS, was " + algorithm);
   215     }
   216     try {
   217       BigInteger y = new BigInteger(o.getString("y"), SERIALIZATION_BASE);
   218       BigInteger p = new BigInteger(o.getString("p"), SERIALIZATION_BASE);
   219       BigInteger q = new BigInteger(o.getString("q"), SERIALIZATION_BASE);
   220       BigInteger g = new BigInteger(o.getString("g"), SERIALIZATION_BASE);
   221       return createPublicKey(y, p, q, g);
   222     } catch (NullPointerException e) {
   223       throw new InvalidKeySpecException("y, p, q, and g must be integers encoded as strings, base " + SERIALIZATION_BASE);
   224     } catch (NumberFormatException e) {
   225       throw new InvalidKeySpecException("y, p, q, and g must be integers encoded as strings, base " + SERIALIZATION_BASE);
   226     }
   227   }
   229   public static BrowserIDKeyPair fromJSONObject(ExtendedJSONObject o) throws InvalidKeySpecException, NoSuchAlgorithmException {
   230     try {
   231       ExtendedJSONObject privateKey = o.getObject(BrowserIDKeyPair.JSON_KEY_PRIVATEKEY);
   232       ExtendedJSONObject publicKey = o.getObject(BrowserIDKeyPair.JSON_KEY_PUBLICKEY);
   233       if (privateKey == null) {
   234         throw new InvalidKeySpecException("privateKey must not be null");
   235       }
   236       if (publicKey == null) {
   237         throw new InvalidKeySpecException("publicKey must not be null");
   238       }
   239       return new BrowserIDKeyPair(createPrivateKey(privateKey), createPublicKey(publicKey));
   240     } catch (NonObjectJSONException e) {
   241       throw new InvalidKeySpecException("privateKey and publicKey must be JSON objects");
   242     }
   243   }
   244 }

mercurial