1 /*
   2  * Copyright (c) 2019, 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 package sun.security.ssl;
  26 
  27 import java.io.IOException;
  28 import java.math.BigInteger;
  29 import java.security.*;
  30 import java.security.interfaces.XECPublicKey;
  31 import java.security.spec.*;
  32 import sun.security.ssl.NamedGroup.NamedGroupSpec;
  33 import sun.security.util.*;
  34 
  35 /**
  36  * Specifics for XEC/XDH Keys/Exchanges
  37  */
  38 final class XDHKeyExchange {
  39 
  40     static final SSLKeyAgreementGenerator xdheKAGenerator
  41             = new XDHEKAGenerator();
  42 
  43     static final class XDHECredentials implements NamedGroupCredentials {
  44 
  45         final XECPublicKey popPublicKey;
  46         final NamedGroup namedGroup;
  47 
  48         XDHECredentials(XECPublicKey popPublicKey, NamedGroup namedGroup) {
  49             this.popPublicKey = popPublicKey;
  50             this.namedGroup = namedGroup;
  51         }
  52 
  53         @Override
  54         public PublicKey getPublicKey() {
  55             return popPublicKey;
  56         }
  57 
  58         @Override
  59         public NamedGroup getNamedGroup() {
  60             return namedGroup;
  61         }
  62 
  63         /**
  64          * Parse the encoded Point into the XDHECredentials using the
  65          * namedGroup.
  66          */
  67         static XDHECredentials valueOf(NamedGroup namedGroup,
  68                 byte[] encodedPoint) throws IOException,
  69                 GeneralSecurityException {
  70 
  71             if (namedGroup.spec != NamedGroupSpec.NAMED_GROUP_XDH) {
  72                 throw new RuntimeException(
  73                         "Credentials decoding:  Not XDH named group");
  74             }
  75 
  76             if (encodedPoint == null || encodedPoint.length == 0) {
  77                 return null;
  78             }
  79 
  80             byte[] uBytes = encodedPoint.clone();
  81             Utilities.reverseBytes(uBytes);
  82             BigInteger u = new BigInteger(1, uBytes);
  83 
  84             XECPublicKeySpec xecPublicKeySpec = new XECPublicKeySpec(
  85                     new NamedParameterSpec(namedGroup.name), u);
  86             KeyFactory factory = JsseJce.getKeyFactory(namedGroup.algorithm);
  87             XECPublicKey publicKey = (XECPublicKey) factory.generatePublic(
  88                     xecPublicKeySpec);
  89 
  90             return new XDHECredentials(publicKey, namedGroup);
  91         }
  92     }
  93 
  94     static final class XDHEPossession implements NamedGroupPossession {
  95 
  96         final PrivateKey privateKey;
  97         final XECPublicKey publicKey;
  98         final NamedGroup namedGroup;
  99 
 100         XDHEPossession(NamedGroup namedGroup, SecureRandom random) {
 101             try {
 102                 KeyPairGenerator kpg
 103                         = JsseJce.getKeyPairGenerator(namedGroup.algorithm);
 104                 kpg.initialize(namedGroup.keAlgParamSpec, random);
 105                 KeyPair kp = kpg.generateKeyPair();
 106                 privateKey = kp.getPrivate();
 107                 publicKey = (XECPublicKey) kp.getPublic();
 108             } catch (GeneralSecurityException e) {
 109                 throw new RuntimeException(
 110                         "Could not generate XDH keypair", e);
 111             }
 112 
 113             this.namedGroup = namedGroup;
 114         }
 115 
 116         @Override
 117         public byte[] encode() {
 118 
 119             byte[] uBytes = ECUtil.trimZeroes(publicKey.getU().toByteArray());
 120 
 121             int expLength;
 122             switch (namedGroup) {
 123                 case X25519:
 124                     expLength = 32;
 125                     break;
 126                 case X448:
 127                     expLength = 56;
 128                     break;
 129                 default:
 130                     throw new RuntimeException("Invalid XDH group");
 131             }
 132 
 133             if (uBytes.length > expLength) {
 134                 throw new RuntimeException("Encoded XDH key too large");
 135             }
 136 
 137             if (uBytes.length != expLength) {
 138                 byte[] tmp = new byte[expLength];
 139                 System.arraycopy(uBytes, 0, tmp,
 140                         expLength - uBytes.length, uBytes.length);
 141                 uBytes = tmp;
 142             }
 143 
 144             Utilities.reverseBytes(uBytes);
 145             return (uBytes);
 146         }
 147 
 148         @Override
 149         public PublicKey getPublicKey() {
 150             return publicKey;
 151         }
 152 
 153         @Override
 154         public NamedGroup getNamedGroup() {
 155             return namedGroup;
 156         }
 157 
 158         @Override
 159         public PrivateKey getPrivateKey() {
 160             return privateKey;
 161         }
 162     }
 163 
 164     private static final class XDHEKAGenerator
 165             implements SSLKeyAgreementGenerator {
 166 
 167         // Prevent instantiation of this class.
 168         private XDHEKAGenerator() {
 169             // blank
 170         }
 171 
 172         @Override
 173         public SSLKeyDerivation createKeyDerivation(
 174                 HandshakeContext context) throws IOException {
 175             XDHEPossession xdhePossession = null;
 176             XDHECredentials xdheCredentials = null;
 177             for (SSLPossession poss : context.handshakePossessions) {
 178                 if (!(poss instanceof XDHEPossession)) {
 179                     continue;
 180                 }
 181 
 182                 NamedGroup ng = ((XDHEPossession) poss).namedGroup;
 183                 for (SSLCredentials cred : context.handshakeCredentials) {
 184                     if (!(cred instanceof XDHECredentials)) {
 185                         continue;
 186                     }
 187                     if (ng.equals(((XDHECredentials) cred).namedGroup)) {
 188                         xdheCredentials = (XDHECredentials) cred;
 189                         break;
 190                     }
 191                 }
 192 
 193                 if (xdheCredentials != null) {
 194                     xdhePossession = (XDHEPossession) poss;
 195                     break;
 196                 }
 197             }
 198 
 199             if (xdhePossession == null || xdheCredentials == null) {
 200                 context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
 201                         "No sufficient XDHE key agreement "
 202                         + "parameters negotiated");
 203             }
 204 
 205             return new KAKeyDerivation("XDH", context,
 206                     xdhePossession.privateKey, xdheCredentials.popPublicKey);
 207         }
 208     }
 209 }