1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.conscrypt; 18 19 import java.nio.ByteBuffer; 20 import java.security.AlgorithmParameters; 21 import java.security.InvalidAlgorithmParameterException; 22 import java.security.InvalidKeyException; 23 import java.security.InvalidParameterException; 24 import java.security.NoSuchAlgorithmException; 25 import java.security.PrivateKey; 26 import java.security.ProviderException; 27 import java.security.PublicKey; 28 import java.security.SignatureException; 29 import java.security.SignatureSpi; 30 import java.security.spec.AlgorithmParameterSpec; 31 import java.security.spec.InvalidParameterSpecException; 32 import java.security.spec.MGF1ParameterSpec; 33 import java.security.spec.PSSParameterSpec; 34 35 /** 36 * Implements the subset of the JDK Signature interface needed for 37 * signature verification using OpenSSL. 38 * 39 * @hide 40 */ 41 @Internal 42 public class OpenSSLSignature extends SignatureSpi { 43 private enum EngineType { 44 RSA, EC, 45 } 46 47 private NativeRef.EVP_MD_CTX ctx; 48 49 /** 50 * The current OpenSSL key we're operating on. 51 */ 52 private OpenSSLKey key; 53 54 /** 55 * Holds the type of the Java algorithm. 56 */ 57 private final EngineType engineType; 58 59 /** 60 * Digest algorithm (reference to {@code EVP_MD}). 61 */ 62 private final long evpMdRef; 63 64 /** 65 * Holds a dummy buffer for writing single bytes to the digest. 66 */ 67 private final byte[] singleByte = new byte[1]; 68 69 /** 70 * True when engine is initialized to signing. 71 */ 72 private boolean signing; 73 74 /** 75 * Public key algorithm context (reference to {@code EVP_PKEY_CTX}). 76 */ 77 private long evpPkeyCtx; 78 79 /** 80 * Creates a new OpenSSLSignature instance for the given algorithm name. 81 * 82 * @param evpMdRef digest algorithm ({@code EVP_MD} reference). 83 */ 84 private OpenSSLSignature(long evpMdRef, EngineType engineType) { 85 this.engineType = engineType; 86 this.evpMdRef = evpMdRef; 87 } 88 89 private void resetContext() throws InvalidAlgorithmParameterException { 90 NativeRef.EVP_MD_CTX ctxLocal = new NativeRef.EVP_MD_CTX(NativeCrypto.EVP_MD_CTX_create()); 91 if (signing) { 92 evpPkeyCtx = NativeCrypto.EVP_DigestSignInit(ctxLocal, evpMdRef, key.getNativeRef()); 93 } else { 94 evpPkeyCtx = NativeCrypto.EVP_DigestVerifyInit(ctxLocal, evpMdRef, key.getNativeRef()); 95 } 96 configureEVP_PKEY_CTX(evpPkeyCtx); 97 this.ctx = ctxLocal; 98 } 99 100 /** 101 * Configures the public key algorithm context ({@code EVP_PKEY_CTX}) associated with this 102 * operation. 103 * 104 * <p>The default implementation does nothing. 105 * 106 * @param ctx reference to the context ({@code EVP_PKEY_CTX}). 107 */ 108 protected void configureEVP_PKEY_CTX(long ctx) throws InvalidAlgorithmParameterException {} 109 110 @Override 111 protected void engineUpdate(byte input) { 112 singleByte[0] = input; 113 engineUpdate(singleByte, 0, 1); 114 } 115 116 @Override 117 protected void engineUpdate(byte[] input, int offset, int len) { 118 final NativeRef.EVP_MD_CTX ctxLocal = ctx; 119 if (signing) { 120 NativeCrypto.EVP_DigestSignUpdate(ctxLocal, input, offset, len); 121 } else { 122 NativeCrypto.EVP_DigestVerifyUpdate(ctxLocal, input, offset, len); 123 } 124 } 125 126 @Override 127 protected void engineUpdate(ByteBuffer input) { 128 // Optimization: Avoid copying/allocation for direct buffers because their contents are 129 // stored as a contiguous region in memory and thus can be efficiently accessed from native 130 // code. 131 132 if (!input.hasRemaining()) { 133 return; 134 } 135 136 if (!input.isDirect()) { 137 super.engineUpdate(input); 138 return; 139 } 140 141 long baseAddress = NativeCrypto.getDirectBufferAddress(input); 142 if (baseAddress == 0) { 143 // Direct buffer's contents can't be accessed from JNI -- superclass's implementation 144 // is good enough to handle this. 145 super.engineUpdate(input); 146 return; 147 } 148 149 // Process the contents between Buffer's position and limit (remaining() number of bytes) 150 int position = input.position(); 151 if (position < 0) { 152 throw new RuntimeException("Negative position"); 153 } 154 long ptr = baseAddress + position; 155 int len = input.remaining(); 156 if (len < 0) { 157 throw new RuntimeException("Negative remaining amount"); 158 } 159 160 final NativeRef.EVP_MD_CTX ctxLocal = ctx; 161 if (signing) { 162 NativeCrypto.EVP_DigestSignUpdateDirect(ctxLocal, ptr, len); 163 } else { 164 NativeCrypto.EVP_DigestVerifyUpdateDirect(ctxLocal, ptr, len); 165 } 166 input.position(position + len); 167 } 168 169 @Deprecated 170 @Override 171 protected Object engineGetParameter(String param) throws InvalidParameterException { 172 return null; 173 } 174 175 private void checkEngineType(OpenSSLKey pkey) throws InvalidKeyException { 176 final int pkeyType = NativeCrypto.EVP_PKEY_type(pkey.getNativeRef()); 177 178 switch (engineType) { 179 case RSA: 180 if (pkeyType != NativeConstants.EVP_PKEY_RSA) { 181 throw new InvalidKeyException("Signature initialized as " + engineType 182 + " (not RSA)"); 183 } 184 break; 185 case EC: 186 if (pkeyType != NativeConstants.EVP_PKEY_EC) { 187 throw new InvalidKeyException("Signature initialized as " + engineType 188 + " (not EC)"); 189 } 190 break; 191 default: 192 throw new InvalidKeyException("Key must be of type " + engineType); 193 } 194 } 195 196 private void initInternal(OpenSSLKey newKey, boolean signing) throws InvalidKeyException { 197 checkEngineType(newKey); 198 key = newKey; 199 200 this.signing = signing; 201 try { 202 resetContext(); 203 } catch (InvalidAlgorithmParameterException e) { 204 throw new InvalidKeyException(e); 205 } 206 } 207 208 @Override 209 protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException { 210 initInternal(OpenSSLKey.fromPrivateKey(privateKey), true); 211 } 212 213 @Override 214 protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { 215 initInternal(OpenSSLKey.fromPublicKey(publicKey), false); 216 } 217 218 @Deprecated 219 @Override 220 protected void engineSetParameter(String param, Object value) throws InvalidParameterException { 221 } 222 223 @Override 224 @SuppressWarnings("Finally") 225 protected byte[] engineSign() throws SignatureException { 226 final NativeRef.EVP_MD_CTX ctxLocal = ctx; 227 try { 228 return NativeCrypto.EVP_DigestSignFinal(ctxLocal); 229 } catch (Exception ex) { 230 throw new SignatureException(ex); 231 } finally { 232 /* 233 * Java expects the digest context to be reset completely after sign 234 * calls. 235 */ 236 try { 237 resetContext(); 238 } catch (InvalidAlgorithmParameterException e) { 239 throw new AssertionError("Reset of context failed after it was successful once"); 240 } 241 } 242 } 243 244 @Override 245 @SuppressWarnings("Finally") 246 protected boolean engineVerify(byte[] sigBytes) throws SignatureException { 247 final NativeRef.EVP_MD_CTX ctxLocal = ctx; 248 try { 249 return NativeCrypto.EVP_DigestVerifyFinal(ctxLocal, sigBytes, 0, sigBytes.length); 250 } catch (Exception ex) { 251 throw new SignatureException(ex); 252 } finally { 253 /* 254 * Java expects the digest context to be reset completely after 255 * verify calls. 256 */ 257 try { 258 resetContext(); 259 } catch (InvalidAlgorithmParameterException e) { 260 throw new AssertionError("Reset of context failed after it was successful once"); 261 } 262 } 263 } 264 265 /** 266 * Returns the public key algorithm context ({@code EVP_PKEY_CTX} reference) associated with 267 * this operation or {@code 0} if operation hasn't been initialized. 268 */ 269 protected final long getEVP_PKEY_CTX() { 270 return evpPkeyCtx; 271 } 272 273 /** 274 * Base class for {@code RSASSA-PKCS1-v1_5} signatures. 275 */ 276 abstract static class RSAPKCS1Padding extends OpenSSLSignature { 277 RSAPKCS1Padding(long evpMdRef) { 278 super(evpMdRef, EngineType.RSA); 279 } 280 281 @Override 282 protected final void configureEVP_PKEY_CTX(long ctx) 283 throws InvalidAlgorithmParameterException { 284 NativeCrypto.EVP_PKEY_CTX_set_rsa_padding(ctx, NativeConstants.RSA_PKCS1_PADDING); 285 } 286 } 287 288 public static final class MD5RSA extends RSAPKCS1Padding { 289 public MD5RSA() { 290 super(EvpMdRef.MD5.EVP_MD); 291 } 292 } 293 public static final class SHA1RSA extends RSAPKCS1Padding { 294 public SHA1RSA() { 295 super(EvpMdRef.SHA1.EVP_MD); 296 } 297 } 298 public static final class SHA224RSA extends RSAPKCS1Padding { 299 public SHA224RSA() { 300 super(EvpMdRef.SHA224.EVP_MD); 301 } 302 } 303 public static final class SHA256RSA extends RSAPKCS1Padding { 304 public SHA256RSA() { 305 super(EvpMdRef.SHA256.EVP_MD); 306 } 307 } 308 public static final class SHA384RSA extends RSAPKCS1Padding { 309 public SHA384RSA() { 310 super(EvpMdRef.SHA384.EVP_MD); 311 } 312 } 313 public static final class SHA512RSA extends RSAPKCS1Padding { 314 public SHA512RSA() { 315 super(EvpMdRef.SHA512.EVP_MD); 316 } 317 } 318 319 public static final class SHA1ECDSA extends OpenSSLSignature { 320 public SHA1ECDSA() { 321 super(EvpMdRef.SHA1.EVP_MD, EngineType.EC); 322 } 323 } 324 public static final class SHA224ECDSA extends OpenSSLSignature { 325 public SHA224ECDSA() { 326 super(EvpMdRef.SHA224.EVP_MD, EngineType.EC); 327 } 328 } 329 public static final class SHA256ECDSA extends OpenSSLSignature { 330 public SHA256ECDSA() { 331 super(EvpMdRef.SHA256.EVP_MD, EngineType.EC); 332 } 333 } 334 public static final class SHA384ECDSA extends OpenSSLSignature { 335 public SHA384ECDSA() { 336 super(EvpMdRef.SHA384.EVP_MD, EngineType.EC); 337 } 338 } 339 public static final class SHA512ECDSA extends OpenSSLSignature { 340 public SHA512ECDSA() { 341 super(EvpMdRef.SHA512.EVP_MD, EngineType.EC); 342 } 343 } 344 345 /** 346 * Base class for {@code RSASSA-PSS} signatures. 347 */ 348 abstract static class RSAPSSPadding extends OpenSSLSignature { 349 private static final int TRAILER_FIELD_BC_ID = 1; 350 351 private final String contentDigestAlgorithm; 352 353 private String mgf1DigestAlgorithm; 354 private long mgf1EvpMdRef; 355 private int saltSizeBytes; 356 357 RSAPSSPadding( 358 long contentDigestEvpMdRef, String contentDigestAlgorithm, int saltSizeBytes) { 359 super(contentDigestEvpMdRef, EngineType.RSA); 360 this.contentDigestAlgorithm = contentDigestAlgorithm; 361 this.mgf1DigestAlgorithm = contentDigestAlgorithm; 362 this.mgf1EvpMdRef = contentDigestEvpMdRef; 363 this.saltSizeBytes = saltSizeBytes; 364 } 365 366 @Override 367 protected final void configureEVP_PKEY_CTX(long ctx) 368 throws InvalidAlgorithmParameterException { 369 NativeCrypto.EVP_PKEY_CTX_set_rsa_padding(ctx, NativeConstants.RSA_PKCS1_PSS_PADDING); 370 NativeCrypto.EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, mgf1EvpMdRef); 371 NativeCrypto.EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, saltSizeBytes); 372 } 373 374 @Override 375 protected final void engineSetParameter(AlgorithmParameterSpec params) 376 throws InvalidAlgorithmParameterException { 377 if (!(params instanceof PSSParameterSpec)) { 378 throw new InvalidAlgorithmParameterException( 379 "Unsupported parameter: " + params + ". Only " 380 + PSSParameterSpec.class.getName() + " supported"); 381 } 382 PSSParameterSpec spec = (PSSParameterSpec) params; 383 String specContentDigest = EvpMdRef 384 .getJcaDigestAlgorithmStandardName(spec.getDigestAlgorithm()); 385 if (specContentDigest == null) { 386 throw new InvalidAlgorithmParameterException( 387 "Unsupported content digest algorithm: " + spec.getDigestAlgorithm()); 388 } else if (!contentDigestAlgorithm.equalsIgnoreCase(specContentDigest)) { 389 throw new InvalidAlgorithmParameterException( 390 "Changing content digest algorithm not supported"); 391 } 392 393 String specMgfAlgorithm = spec.getMGFAlgorithm(); 394 if ((!EvpMdRef.MGF1_ALGORITHM_NAME.equalsIgnoreCase(specMgfAlgorithm)) 395 && (!EvpMdRef.MGF1_OID.equals(specMgfAlgorithm))) { 396 throw new InvalidAlgorithmParameterException( 397 "Unsupported MGF algorithm: " + specMgfAlgorithm + ". Only " 398 + EvpMdRef.MGF1_ALGORITHM_NAME + " supported"); 399 } 400 401 AlgorithmParameterSpec mgfSpec = spec.getMGFParameters(); 402 if (!(mgfSpec instanceof MGF1ParameterSpec)) { 403 throw new InvalidAlgorithmParameterException( 404 "Unsupported MGF parameters: " + mgfSpec + ". Only " 405 + MGF1ParameterSpec.class.getName() + " supported"); 406 } 407 MGF1ParameterSpec specMgf1Spec = (MGF1ParameterSpec) spec.getMGFParameters(); 408 409 String specMgf1Digest = EvpMdRef 410 .getJcaDigestAlgorithmStandardName(specMgf1Spec.getDigestAlgorithm()); 411 if (specMgf1Digest == null) { 412 throw new InvalidAlgorithmParameterException( 413 "Unsupported MGF1 digest algorithm: " + specMgf1Spec.getDigestAlgorithm()); 414 } 415 long specMgf1EvpMdRef; 416 try { 417 specMgf1EvpMdRef = EvpMdRef 418 .getEVP_MDByJcaDigestAlgorithmStandardName(specMgf1Digest); 419 } catch (NoSuchAlgorithmException e) { 420 throw new ProviderException("Failed to obtain EVP_MD for " + specMgf1Digest, e); 421 } 422 423 int specSaltSizeBytes = spec.getSaltLength(); 424 if (specSaltSizeBytes < 0) { 425 throw new InvalidAlgorithmParameterException( 426 "Salt length must be non-negative: " + specSaltSizeBytes); 427 } 428 429 int specTrailer = spec.getTrailerField(); 430 if (specTrailer != TRAILER_FIELD_BC_ID) { 431 throw new InvalidAlgorithmParameterException( 432 "Unsupported trailer field: " + specTrailer + ". Only " 433 + TRAILER_FIELD_BC_ID + " supported"); 434 } 435 436 this.mgf1DigestAlgorithm = specMgf1Digest; 437 this.mgf1EvpMdRef = specMgf1EvpMdRef; 438 this.saltSizeBytes = specSaltSizeBytes; 439 440 long ctx = getEVP_PKEY_CTX(); 441 if (ctx != 0) { 442 configureEVP_PKEY_CTX(ctx); 443 } 444 } 445 446 @Override 447 protected final AlgorithmParameters engineGetParameters() { 448 try { 449 AlgorithmParameters result = AlgorithmParameters.getInstance("PSS"); 450 result.init( 451 new PSSParameterSpec( 452 contentDigestAlgorithm, 453 EvpMdRef.MGF1_ALGORITHM_NAME, 454 new MGF1ParameterSpec(mgf1DigestAlgorithm), 455 saltSizeBytes, 456 TRAILER_FIELD_BC_ID)); 457 return result; 458 } catch (NoSuchAlgorithmException e) { 459 throw new ProviderException("Failed to create PSS AlgorithmParameters", e); 460 } catch (InvalidParameterSpecException e) { 461 throw new ProviderException("Failed to create PSS AlgorithmParameters", e); 462 } 463 } 464 } 465 466 public static final class SHA1RSAPSS extends RSAPSSPadding { 467 public SHA1RSAPSS() { 468 super(EvpMdRef.SHA1.EVP_MD, EvpMdRef.SHA1.JCA_NAME, EvpMdRef.SHA1.SIZE_BYTES); 469 } 470 } 471 472 public static final class SHA224RSAPSS extends RSAPSSPadding { 473 public SHA224RSAPSS() { 474 super(EvpMdRef.SHA224.EVP_MD, EvpMdRef.SHA224.JCA_NAME, EvpMdRef.SHA224.SIZE_BYTES); 475 } 476 } 477 478 public static final class SHA256RSAPSS extends RSAPSSPadding { 479 public SHA256RSAPSS() { 480 super(EvpMdRef.SHA256.EVP_MD, EvpMdRef.SHA256.JCA_NAME, EvpMdRef.SHA256.SIZE_BYTES); 481 } 482 } 483 484 public static final class SHA384RSAPSS extends RSAPSSPadding { 485 public SHA384RSAPSS() { 486 super(EvpMdRef.SHA384.EVP_MD, EvpMdRef.SHA384.JCA_NAME, EvpMdRef.SHA384.SIZE_BYTES); 487 } 488 } 489 490 public static final class SHA512RSAPSS extends RSAPSSPadding { 491 public SHA512RSAPSS() { 492 super(EvpMdRef.SHA512.EVP_MD, EvpMdRef.SHA512.JCA_NAME, EvpMdRef.SHA512.SIZE_BYTES); 493 } 494 } 495 } 496