1 /* 2 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.security.ec; 27 28 import java.security.InvalidAlgorithmParameterException; 29 import java.security.InvalidKeyException; 30 import java.security.NoSuchAlgorithmException; 31 import java.security.Key; 32 import java.security.PublicKey; 33 import java.security.PrivateKey; 34 import java.security.SecureRandom; 35 import java.security.interfaces.XECPrivateKey; 36 import java.security.interfaces.XECPublicKey; 37 import java.security.spec.InvalidKeySpecException; 38 import javax.crypto.KeyAgreementSpi; 39 import javax.crypto.SecretKey; 40 import javax.crypto.ShortBufferException; 41 import java.security.spec.AlgorithmParameterSpec; 42 43 public class XDHKeyAgreement extends KeyAgreementSpi { 44 45 private byte[] privateKey; 46 private byte[] secret; 47 private XECOperations ops; 48 49 @Override 50 protected void engineInit(Key key, SecureRandom random) 51 throws InvalidKeyException { 52 53 initImpl(key); 54 } 55 56 @Override 57 protected void engineInit(Key key, final AlgorithmParameterSpec params, 58 SecureRandom random) throws InvalidKeyException, 59 InvalidAlgorithmParameterException { 60 61 initImpl(key); 62 63 // the private key parameters must match params 64 XECParameters xecParams = XECParameters.get( 65 InvalidAlgorithmParameterException::new, params); 66 if (!xecParams.oidEquals(this.ops.getParameters())) { 67 throw new InvalidKeyException( 68 "Incorrect private key parameters" 69 ); 70 } 71 } 72 73 private void initImpl(Key key) throws InvalidKeyException { 74 75 if (!(key instanceof XECPrivateKey)) { 76 throw new InvalidKeyException 77 ("Unsupported key type"); 78 } 79 XECPrivateKey privateKey = (XECPrivateKey) key; 80 XECParameters xecParams = XECParameters.get( 81 InvalidKeyException::new, privateKey.getParams()); 82 83 this.ops = new XECOperations(xecParams); 84 this.privateKey = privateKey.getScalar().orElseThrow( 85 () -> new InvalidKeyException("No private key value") 86 ); 87 secret = null; 88 } 89 90 @Override 91 protected Key engineDoPhase(Key key, boolean lastPhase) 92 throws InvalidKeyException, IllegalStateException { 93 94 if (this.privateKey == null) { 95 throw new IllegalStateException("Not initialized"); 96 } 97 if (this.secret != null) { 98 throw new IllegalStateException("Phase already executed"); 99 } 100 if (!lastPhase) { 101 throw new IllegalStateException 102 ("Only two party agreement supported, lastPhase must be true"); 103 } 104 if (!(key instanceof XECPublicKey)) { 105 throw new InvalidKeyException 106 ("Unsupported key type"); 107 } 108 109 XECPublicKey publicKey = (XECPublicKey) key; 110 111 // Ensure public key parameters are compatible with private key 112 XECParameters xecParams = XECParameters.get(InvalidKeyException::new, 113 publicKey.getParams()); 114 if (!ops.getParameters().oidEquals(xecParams)) { 115 throw new InvalidKeyException( 116 "Public key parameters are not compatible with private key."); 117 } 118 119 // The privateKey may be modified to a value that is equivalent for 120 // the purposes of this algorithm. 121 byte[] computedSecret = ops.encodedPointMultiply( 122 this.privateKey, 123 publicKey.getU()); 124 125 // test for contributory behavior 126 if (allZero(computedSecret)) { 127 throw new InvalidKeyException("Point has small order"); 128 } 129 130 this.secret = computedSecret; 131 132 return null; 133 } 134 135 /* 136 * Constant-time check for an all-zero array 137 */ 138 private boolean allZero(byte[] arr) { 139 byte orValue = (byte) 0; 140 for (int i = 0; i < arr.length; i++) { 141 orValue |= arr[i]; 142 } 143 144 return orValue == (byte) 0; 145 } 146 147 @Override 148 protected byte[] engineGenerateSecret() throws IllegalStateException { 149 if (secret == null) { 150 throw new IllegalStateException("Not initialized correctly"); 151 } 152 153 return secret.clone(); 154 } 155 156 @Override 157 protected int engineGenerateSecret(byte[] sharedSecret, int offset) 158 throws IllegalStateException, ShortBufferException { 159 160 if (secret == null) { 161 throw new IllegalStateException("Not initialized correctly"); 162 } 163 int secretLen = this.secret.length; 164 if (offset + secretLen > sharedSecret.length) { 165 throw new ShortBufferException("Need " + secretLen 166 + " bytes, only " + (sharedSecret.length - offset) 167 + " available"); 168 } 169 170 System.arraycopy(this.secret, 0, sharedSecret, offset, secretLen); 171 return secret.length; 172 } 173 174 @Override 175 protected SecretKey engineGenerateSecret(String algorithm) 176 throws IllegalStateException, NoSuchAlgorithmException, 177 InvalidKeyException { 178 179 throw new NoSuchAlgorithmException("Not supported"); 180 } 181 }