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.SecureRandom; 33 import java.security.ProviderException; 34 import java.security.interfaces.XECPrivateKey; 35 import java.security.interfaces.XECPublicKey; 36 import java.security.spec.AlgorithmParameterSpec; 37 import java.security.spec.NamedParameterSpec; 38 import javax.crypto.KeyAgreementSpi; 39 import javax.crypto.SecretKey; 40 import javax.crypto.ShortBufferException; 41 import java.util.function.Function; 42 43 public class XDHKeyAgreement extends KeyAgreementSpi { 44 45 private byte[] privateKey; 46 private byte[] secret; 47 private XECOperations ops; 48 private XECParameters lockedParams = null; 49 50 XDHKeyAgreement() { 51 // do nothing 52 } 53 54 XDHKeyAgreement(AlgorithmParameterSpec paramSpec) { 55 lockedParams = XECParameters.get(ProviderException::new, paramSpec); 56 } 57 58 @Override 59 protected void engineInit(Key key, SecureRandom random) 60 throws InvalidKeyException { 61 62 initImpl(key); 63 } 64 65 @Override 66 protected void engineInit(Key key, final AlgorithmParameterSpec params, 67 SecureRandom random) throws InvalidKeyException, 68 InvalidAlgorithmParameterException { 69 70 initImpl(key); 71 72 // the private key parameters must match params, if present 73 if (params != null) { 74 XECParameters xecParams = XECParameters.get( 75 InvalidAlgorithmParameterException::new, params); 76 if (!xecParams.oidEquals(this.ops.getParameters())) { 77 throw new InvalidKeyException( 78 "Incorrect private key parameters" 79 ); 80 } 81 } 82 } 83 84 private 85 <T extends Throwable> 86 void checkLockedParams(Function<String, T> exception, 87 XECParameters params) throws T { 88 89 if (lockedParams != null && lockedParams != params) { 90 throw exception.apply("Parameters must be " + 91 lockedParams.getName()); 92 } 93 } 94 95 private void initImpl(Key key) throws InvalidKeyException { 96 97 if (!(key instanceof XECPrivateKey)) { 98 throw new InvalidKeyException 99 ("Unsupported key type"); 100 } 101 XECPrivateKey privateKey = (XECPrivateKey) key; 102 XECParameters xecParams = XECParameters.get( 103 InvalidKeyException::new, privateKey.getParams()); 104 checkLockedParams(InvalidKeyException::new, xecParams); 105 106 this.ops = new XECOperations(xecParams); 107 this.privateKey = privateKey.getScalar().orElseThrow( 108 () -> new InvalidKeyException("No private key value") 109 ); 110 secret = null; 111 } 112 113 @Override 114 protected Key engineDoPhase(Key key, boolean lastPhase) 115 throws InvalidKeyException, IllegalStateException { 116 117 if (this.privateKey == null) { 118 throw new IllegalStateException("Not initialized"); 119 } 120 if (this.secret != null) { 121 throw new IllegalStateException("Phase already executed"); 122 } 123 if (!lastPhase) { 124 throw new IllegalStateException 125 ("Only two party agreement supported, lastPhase must be true"); 126 } 127 if (!(key instanceof XECPublicKey)) { 128 throw new InvalidKeyException 129 ("Unsupported key type"); 130 } 131 132 XECPublicKey publicKey = (XECPublicKey) key; 133 134 // Ensure public key parameters are compatible with private key 135 XECParameters xecParams = XECParameters.get(InvalidKeyException::new, 136 publicKey.getParams()); 137 if (!ops.getParameters().oidEquals(xecParams)) { 138 throw new InvalidKeyException( 139 "Public key parameters are not compatible with private key."); 140 } 141 142 // The privateKey may be modified to a value that is equivalent for 143 // the purposes of this algorithm. 144 byte[] computedSecret = ops.encodedPointMultiply( 145 this.privateKey, 146 publicKey.getU()); 147 148 // test for contributory behavior 149 if (allZero(computedSecret)) { 150 throw new InvalidKeyException("Point has small order"); 151 } 152 153 this.secret = computedSecret; 154 155 return null; 156 } 157 158 /* 159 * Constant-time check for an all-zero array 160 */ 161 private boolean allZero(byte[] arr) { 162 byte orValue = (byte) 0; 163 for (int i = 0; i < arr.length; i++) { 164 orValue |= arr[i]; 165 } 166 167 return orValue == (byte) 0; 168 } 169 170 @Override 171 protected byte[] engineGenerateSecret() throws IllegalStateException { 172 if (secret == null) { 173 throw new IllegalStateException("Not initialized correctly"); 174 } 175 176 byte[] result = secret; 177 secret = null; 178 return result; 179 } 180 181 @Override 182 protected int engineGenerateSecret(byte[] sharedSecret, int offset) 183 throws IllegalStateException, ShortBufferException { 184 185 if (secret == null) { 186 throw new IllegalStateException("Not initialized correctly"); 187 } 188 int secretLen = this.secret.length; 189 if (secretLen > sharedSecret.length - offset) { 190 throw new ShortBufferException("Need " + secretLen 191 + " bytes, only " + (sharedSecret.length - offset) 192 + " available"); 193 } 194 195 System.arraycopy(this.secret, 0, sharedSecret, offset, secretLen); 196 secret = null; 197 return secretLen; 198 } 199 200 @Override 201 protected SecretKey engineGenerateSecret(String algorithm) 202 throws IllegalStateException, NoSuchAlgorithmException, 203 InvalidKeyException { 204 205 throw new NoSuchAlgorithmException("Not supported"); 206 } 207 208 static class X25519 extends XDHKeyAgreement { 209 210 public X25519() { 211 super(NamedParameterSpec.X25519); 212 } 213 } 214 215 static class X448 extends XDHKeyAgreement { 216 217 public X448() { 218 super(NamedParameterSpec.X448); 219 } 220 } 221 }