The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Licensed to the Apache Software Foundation (ASF) under one or more |
| 3 | * contributor license agreements. See the NOTICE file distributed with |
| 4 | * this work for additional information regarding copyright ownership. |
| 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 |
| 6 | * (the "License"); you may not use this file except in compliance with |
| 7 | * the License. You may obtain a copy of the License at |
| 8 | * |
| 9 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | * |
| 11 | * Unless required by applicable law or agreed to in writing, software |
| 12 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | * See the License for the specific language governing permissions and |
| 15 | * limitations under the License. |
| 16 | */ |
| 17 | |
| 18 | package org.apache.harmony.security.provider.crypto; |
| 19 | |
| 20 | import java.math.BigInteger; |
Jesse Wilson | 7365de1 | 2010-08-11 15:21:19 -0700 | [diff] [blame] | 21 | import java.security.InvalidKeyException; |
| 22 | import java.security.InvalidParameterException; |
| 23 | import java.security.MessageDigest; |
| 24 | import java.security.NoSuchAlgorithmException; |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 25 | import java.security.PrivateKey; |
| 26 | import java.security.PublicKey; |
| 27 | import java.security.SecureRandom; |
| 28 | import java.security.Signature; |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 29 | import java.security.SignatureException; |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 30 | import java.security.interfaces.DSAKey; |
Jesse Wilson | 7365de1 | 2010-08-11 15:21:19 -0700 | [diff] [blame] | 31 | import java.security.interfaces.DSAParams; |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 32 | import java.security.interfaces.DSAPrivateKey; |
| 33 | import java.security.interfaces.DSAPublicKey; |
| 34 | |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 35 | public class SHA1withDSA_SignatureImpl extends Signature { |
| 36 | |
| 37 | private MessageDigest msgDigest; |
| 38 | |
| 39 | private DSAKey dsaKey; |
| 40 | |
| 41 | /** |
| 42 | * The solo constructor. |
| 43 | */ |
| 44 | public SHA1withDSA_SignatureImpl() throws NoSuchAlgorithmException { |
| 45 | |
Elliott Hughes | f33eae7 | 2010-05-13 12:36:25 -0700 | [diff] [blame] | 46 | super("SHA1withDSA"); |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 47 | |
Elliott Hughes | f33eae7 | 2010-05-13 12:36:25 -0700 | [diff] [blame] | 48 | msgDigest = MessageDigest.getInstance("SHA1"); |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 49 | } |
| 50 | |
| 51 | /** |
| 52 | * Deprecated method. |
| 53 | * |
| 54 | * @return |
| 55 | * null |
| 56 | */ |
| 57 | protected Object engineGetParameter(String param) |
| 58 | throws InvalidParameterException { |
| 59 | if (param == null) { |
Kenny Root | 86acc04 | 2012-09-12 10:32:58 -0700 | [diff] [blame] | 60 | throw new NullPointerException("param == null"); |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 61 | } |
| 62 | return null; |
| 63 | } |
| 64 | |
| 65 | /** |
Elliott Hughes | f33eae7 | 2010-05-13 12:36:25 -0700 | [diff] [blame] | 66 | * Initializes this signature object with PrivateKey object |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 67 | * passed as argument to the method. |
| 68 | * |
| 69 | * @params |
| 70 | * privateKey DSAPrivateKey object |
| 71 | * @throws |
| 72 | * InvalidKeyException if privateKey is not DSAPrivateKey object |
| 73 | */ |
| 74 | protected void engineInitSign(PrivateKey privateKey) |
| 75 | throws InvalidKeyException { |
| 76 | |
| 77 | DSAParams params; |
| 78 | |
| 79 | // parameters and private key |
Elliott Hughes | 2f9e468 | 2009-10-09 17:21:46 -0700 | [diff] [blame] | 80 | BigInteger p, q, x; |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 81 | |
| 82 | int n; |
| 83 | |
| 84 | if (privateKey == null || !(privateKey instanceof DSAPrivateKey)) { |
Elliott Hughes | 897538a | 2010-05-28 20:00:47 -0700 | [diff] [blame] | 85 | throw new InvalidKeyException(); |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 86 | } |
| 87 | |
| 88 | params = ((DSAPrivateKey) privateKey).getParams(); |
| 89 | p = params.getP(); |
| 90 | q = params.getQ(); |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 91 | x = ((DSAPrivateKey) privateKey).getX(); |
| 92 | |
| 93 | // checks described in DSA standard |
| 94 | n = p.bitLength(); |
Elliott Hughes | 897538a | 2010-05-28 20:00:47 -0700 | [diff] [blame] | 95 | if (p.compareTo(BigInteger.valueOf(1)) != 1 || n < 512 || n > 1024 || (n & 077) != 0) { |
| 96 | throw new InvalidKeyException("bad p"); |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 97 | } |
| 98 | if (q.signum() != 1 && q.bitLength() != 160) { |
Elliott Hughes | 897538a | 2010-05-28 20:00:47 -0700 | [diff] [blame] | 99 | throw new InvalidKeyException("bad q"); |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 100 | } |
| 101 | if (x.signum() != 1 || x.compareTo(q) != -1) { |
Elliott Hughes | 897538a | 2010-05-28 20:00:47 -0700 | [diff] [blame] | 102 | throw new InvalidKeyException("x <= 0 || x >= q"); |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 103 | } |
| 104 | |
| 105 | dsaKey = (DSAKey) privateKey; |
| 106 | |
| 107 | msgDigest.reset(); |
| 108 | } |
| 109 | |
| 110 | /** |
Elliott Hughes | f33eae7 | 2010-05-13 12:36:25 -0700 | [diff] [blame] | 111 | * Initializes this signature object with PublicKey object |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 112 | * passed as argument to the method. |
| 113 | * |
| 114 | * @params |
| 115 | * publicKey DSAPublicKey object |
| 116 | * @throws |
| 117 | * InvalidKeyException if publicKey is not DSAPublicKey object |
| 118 | */ |
| 119 | protected void engineInitVerify(PublicKey publicKey) |
| 120 | throws InvalidKeyException { |
| 121 | |
| 122 | // parameters and public key |
Elliott Hughes | 2f9e468 | 2009-10-09 17:21:46 -0700 | [diff] [blame] | 123 | BigInteger p, q, y; |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 124 | |
| 125 | int n1; |
| 126 | |
| 127 | if (publicKey == null || !(publicKey instanceof DSAPublicKey)) { |
Elliott Hughes | 897538a | 2010-05-28 20:00:47 -0700 | [diff] [blame] | 128 | throw new InvalidKeyException("publicKey is not an instance of DSAPublicKey"); |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 129 | } |
| 130 | |
| 131 | DSAParams params = ((DSAPublicKey) publicKey).getParams(); |
| 132 | p = params.getP(); |
| 133 | q = params.getQ(); |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 134 | y = ((DSAPublicKey) publicKey).getY(); |
| 135 | |
| 136 | // checks described in DSA standard |
| 137 | n1 = p.bitLength(); |
Elliott Hughes | 897538a | 2010-05-28 20:00:47 -0700 | [diff] [blame] | 138 | if (p.compareTo(BigInteger.valueOf(1)) != 1 || n1 < 512 || n1 > 1024 || (n1 & 077) != 0) { |
| 139 | throw new InvalidKeyException("bad p"); |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 140 | } |
| 141 | if (q.signum() != 1 || q.bitLength() != 160) { |
Elliott Hughes | 897538a | 2010-05-28 20:00:47 -0700 | [diff] [blame] | 142 | throw new InvalidKeyException("bad q"); |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 143 | } |
| 144 | if (y.signum() != 1) { |
Elliott Hughes | 897538a | 2010-05-28 20:00:47 -0700 | [diff] [blame] | 145 | throw new InvalidKeyException("y <= 0"); |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 146 | } |
| 147 | |
| 148 | dsaKey = (DSAKey) publicKey; |
| 149 | |
| 150 | msgDigest.reset(); |
| 151 | } |
| 152 | |
| 153 | /* |
| 154 | * Deprecated method. |
| 155 | * |
| 156 | * @throws |
| 157 | * InvalidParameterException |
| 158 | */ |
Elliott Hughes | 897538a | 2010-05-28 20:00:47 -0700 | [diff] [blame] | 159 | protected void engineSetParameter(String param, Object value) throws InvalidParameterException { |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 160 | if (param == null) { |
Elliott Hughes | 897538a | 2010-05-28 20:00:47 -0700 | [diff] [blame] | 161 | throw new NullPointerException("param == null"); |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 162 | } |
Elliott Hughes | 897538a | 2010-05-28 20:00:47 -0700 | [diff] [blame] | 163 | throw new InvalidParameterException("invalid parameter for this engine"); |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 164 | } |
| 165 | |
| 166 | /** |
Elliott Hughes | f33eae7 | 2010-05-13 12:36:25 -0700 | [diff] [blame] | 167 | * Returns signature bytes as byte array containing |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 168 | * ASN1 representation for two BigInteger objects |
| 169 | * which is SEQUENCE of two INTEGERS. |
| 170 | * Length of sequence varies from less than 46 to 48. |
| 171 | * |
Elliott Hughes | f33eae7 | 2010-05-13 12:36:25 -0700 | [diff] [blame] | 172 | * Resets object to the state it was in |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 173 | * when previous call to either "initSign" method was called. |
| 174 | * |
| 175 | * @return |
| 176 | * byte array containing signature in ASN1 representation |
| 177 | * @throws |
Elliott Hughes | f33eae7 | 2010-05-13 12:36:25 -0700 | [diff] [blame] | 178 | * SignatureException if object's state is not SIGN or |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 179 | * signature algorithm cannot process data |
| 180 | */ |
| 181 | |
| 182 | protected byte[] engineSign() throws SignatureException { |
| 183 | |
| 184 | // names of below BigIntegers are the same as they are defined in DSA standard |
| 185 | BigInteger r = null; |
| 186 | BigInteger s = null; |
| 187 | BigInteger k = null; |
| 188 | |
| 189 | // parameters and private key |
| 190 | BigInteger p, q, g, x; |
| 191 | |
Elliott Hughes | f33eae7 | 2010-05-13 12:36:25 -0700 | [diff] [blame] | 192 | // BigInteger for message digest |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 193 | BigInteger digestBI; |
| 194 | |
| 195 | // various byte array being used in computing signature |
Elliott Hughes | 171dc20 | 2010-09-02 11:06:55 -0700 | [diff] [blame] | 196 | byte[] randomBytes; |
| 197 | byte[] rBytes; |
| 198 | byte[] sBytes; |
| 199 | byte[] signature; |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 200 | |
| 201 | int n, n1, n2; |
| 202 | |
| 203 | DSAParams params; |
| 204 | |
| 205 | if (appRandom == null) { |
| 206 | appRandom = new SecureRandom(); |
| 207 | } |
| 208 | |
| 209 | params = dsaKey.getParams(); |
| 210 | p = params.getP(); |
| 211 | q = params.getQ(); |
| 212 | g = params.getG(); |
| 213 | x = ((DSAPrivateKey) dsaKey).getX(); |
| 214 | |
| 215 | // forming signature according algorithm described in chapter 5 of DSA standard |
| 216 | |
| 217 | digestBI = new BigInteger(1, msgDigest.digest()); |
| 218 | |
| 219 | randomBytes = new byte[20]; |
| 220 | |
| 221 | for (;;) { |
| 222 | |
| 223 | appRandom.nextBytes(randomBytes); |
| 224 | |
| 225 | k = new BigInteger(1, randomBytes); |
| 226 | if (k.compareTo(q) != -1) { |
| 227 | continue; |
| 228 | } |
| 229 | r = g.modPow(k, p).mod(q); |
| 230 | if (r.signum() == 0) { |
| 231 | continue; |
| 232 | } |
| 233 | |
| 234 | s = k.modInverse(q).multiply(digestBI.add(x.multiply(r)).mod(q)) |
| 235 | .mod(q); |
| 236 | |
| 237 | if (s.signum() != 0) { |
| 238 | break; |
| 239 | } |
| 240 | } |
| 241 | |
| 242 | // forming signature's ASN1 representation which is SEQUENCE of two INTEGERs |
Elliott Hughes | f33eae7 | 2010-05-13 12:36:25 -0700 | [diff] [blame] | 243 | // |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 244 | rBytes = r.toByteArray(); |
| 245 | n1 = rBytes.length; |
| 246 | if ((rBytes[0] & 0x80) != 0) { |
| 247 | n1++; |
| 248 | } |
| 249 | sBytes = s.toByteArray(); |
| 250 | n2 = sBytes.length; |
| 251 | if ((sBytes[0] & 0x80) != 0) { |
| 252 | n2++; |
| 253 | } |
| 254 | |
| 255 | signature = new byte[6 + n1 + n2]; // 48 is max. possible length of signature |
| 256 | signature[0] = (byte) 0x30; // ASN1 SEQUENCE tag |
| 257 | signature[1] = (byte) (4 + n1 + n2); // total length of two INTEGERs |
| 258 | signature[2] = (byte) 0x02; // ASN1 INTEGER tag |
| 259 | signature[3] = (byte) n1; // length of r |
| 260 | signature[4 + n1] = (byte) 0x02; // ASN1 INTEGER tag |
| 261 | signature[5 + n1] = (byte) n2; // length of s |
| 262 | |
| 263 | if (n1 == rBytes.length) { |
| 264 | n = 4; |
| 265 | } else { |
| 266 | n = 5; |
| 267 | } |
| 268 | System.arraycopy(rBytes, 0, signature, n, rBytes.length); |
| 269 | |
| 270 | if (n2 == sBytes.length) { |
| 271 | n = 6 + n1; |
| 272 | } else { |
| 273 | n = 7 + n1; |
| 274 | } |
| 275 | System.arraycopy(sBytes, 0, signature, n, sBytes.length); |
| 276 | |
| 277 | return signature; |
| 278 | } |
| 279 | |
| 280 | /** |
| 281 | * Updates data to sign or to verify. |
| 282 | * |
| 283 | * @params |
| 284 | * b byte to update |
| 285 | * @throws |
| 286 | * SignatureException if object was not initialized for signing or verifying |
| 287 | */ |
| 288 | protected void engineUpdate(byte b) throws SignatureException { |
| 289 | |
| 290 | msgDigest.update(b); |
| 291 | } |
| 292 | |
| 293 | /** |
| 294 | * Updates data to sign or to verify. |
| 295 | * |
| 296 | * @params |
| 297 | * b byte array containing bytes to update |
| 298 | * @params |
| 299 | * off offset in byte array to start from |
| 300 | * @params |
| 301 | * len number of bytes to use for updating |
| 302 | * @throws |
| 303 | * SignatureException if object was not initialized for signing or verifying |
| 304 | */ |
| 305 | protected void engineUpdate(byte[] b, int off, int len) |
| 306 | throws SignatureException { |
| 307 | |
| 308 | msgDigest.update(b, off, len); |
| 309 | } |
| 310 | |
| 311 | private boolean checkSignature(byte[] sigBytes, int offset, int length) |
| 312 | throws SignatureException { |
| 313 | |
| 314 | // names of below BigIntegers are the same as they are defined in DSA standard |
| 315 | BigInteger r, s, w; |
| 316 | BigInteger u1, u2, v; |
| 317 | |
| 318 | // parameters and public key |
| 319 | BigInteger p, q, g, y; |
| 320 | |
| 321 | DSAParams params; |
| 322 | |
| 323 | int n1, n2; |
| 324 | |
Elliott Hughes | 171dc20 | 2010-09-02 11:06:55 -0700 | [diff] [blame] | 325 | byte[] bytes; |
| 326 | byte[] digest; |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 327 | |
| 328 | // checking up on signature's ASN1 |
| 329 | try { |
| 330 | byte dummy; |
| 331 | n1 = sigBytes[offset + 3]; |
| 332 | n2 = sigBytes[offset + n1 + 5]; |
| 333 | |
| 334 | if (sigBytes[offset + 0] != 0x30 || sigBytes[offset + 2] != 2 |
| 335 | || sigBytes[offset + n1 + 4] != 2 |
| 336 | || sigBytes[offset + 1] != (n1 + n2 + 4) || n1 > 21 |
| 337 | || n2 > 21 |
| 338 | || (length != 0 && (sigBytes[offset + 1] + 2) > length)) { |
Elliott Hughes | 897538a | 2010-05-28 20:00:47 -0700 | [diff] [blame] | 339 | throw new SignatureException("signature bytes have invalid encoding"); |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 340 | } |
| 341 | |
| 342 | dummy = sigBytes[5 + n1 + n2]; // to check length of sigBytes |
| 343 | } catch (ArrayIndexOutOfBoundsException e) { |
Elliott Hughes | 897538a | 2010-05-28 20:00:47 -0700 | [diff] [blame] | 344 | throw new SignatureException("bad argument: byte[] is too small"); |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 345 | } |
| 346 | |
| 347 | digest = msgDigest.digest(); |
| 348 | |
| 349 | bytes = new byte[n1]; |
| 350 | System.arraycopy(sigBytes, offset + 4, bytes, 0, n1); |
| 351 | r = new BigInteger(bytes); |
| 352 | |
| 353 | bytes = new byte[n2]; |
| 354 | System.arraycopy(sigBytes, offset + 6 + n1, bytes, 0, n2); |
| 355 | s = new BigInteger(bytes); |
| 356 | |
| 357 | params = dsaKey.getParams(); |
| 358 | p = params.getP(); |
| 359 | q = params.getQ(); |
| 360 | g = params.getG(); |
| 361 | y = ((DSAPublicKey) dsaKey).getY(); |
| 362 | |
| 363 | // forming signature according algorithm described in chapter 6 of DSA standard |
| 364 | |
| 365 | if (r.signum() != 1 || r.compareTo(q) != -1 || s.signum() != 1 |
| 366 | || s.compareTo(q) != -1) { |
| 367 | return false; |
| 368 | } |
| 369 | |
| 370 | w = s.modInverse(q); |
| 371 | |
| 372 | u1 = (new BigInteger(1, digest)).multiply(w).mod(q); |
| 373 | u2 = r.multiply(w).mod(q); |
| 374 | |
| 375 | v = g.modPow(u1, p).multiply(y.modPow(u2, p)).mod(p).mod(q); |
| 376 | |
| 377 | if (v.compareTo(r) != 0) { |
| 378 | return false; |
| 379 | } |
| 380 | return true; |
| 381 | } |
| 382 | |
| 383 | /** |
| 384 | * Verifies the signature bytes. |
| 385 | * |
| 386 | * @params |
| 387 | * sigBytes byte array with signature bytes to verify. |
| 388 | * @return |
| 389 | * true if signature bytes were verified, false otherwise |
| 390 | * @throws |
| 391 | * SignatureException if object's state is not VERIFY or |
| 392 | * signature format is not ASN1 representation or |
| 393 | * signature algorithm cannot process data |
| 394 | */ |
| 395 | protected boolean engineVerify(byte[] sigBytes) throws SignatureException { |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 396 | if (sigBytes == null) { |
Elliott Hughes | 897538a | 2010-05-28 20:00:47 -0700 | [diff] [blame] | 397 | throw new NullPointerException("sigBytes == null"); |
The Android Open Source Project | adc854b | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 398 | } |
| 399 | |
| 400 | return checkSignature(sigBytes, 0, 0); |
| 401 | } |
| 402 | |
| 403 | /** |
| 404 | * Verifies the signature bytes. |
| 405 | * |
| 406 | * @params |
| 407 | * sigBytes byte array with signature bytes to verify. |
| 408 | * @params |
| 409 | * offset index in sigBytes to start from |
| 410 | * @params |
| 411 | * length number of bytes allotted for signature |
| 412 | * @return |
| 413 | * true if signature bytes were verified, false otherwise |
| 414 | * @throws |
| 415 | * SignatureException if object's state is not VERIFY or |
| 416 | * signature format is not ASN1 representation or |
| 417 | * signature algorithm cannot process data |
| 418 | */ |
| 419 | protected boolean engineVerify(byte[] sigBytes, int offset, int length) |
| 420 | throws SignatureException { |
| 421 | return checkSignature(sigBytes, offset, length); |
| 422 | } |
| 423 | } |