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.util;
  27 
  28 import java.io.IOException;
  29 import java.math.BigInteger;
  30 import java.security.spec.AlgorithmParameterSpec;
  31 import java.security.spec.NamedParameterSpec;
  32 import java.util.Collections;
  33 import java.util.Map;
  34 import java.util.HashMap;
  35 import java.util.Optional;
  36 import java.util.function.Function;
  37 import java.util.function.Supplier;
  38 
  39 import sun.security.util.ObjectIdentifier;
  40 import sun.security.x509.AlgorithmId;
  41 
  42 public class XECParameters {
  43 
  44     // Naming/identification parameters
  45     private final ObjectIdentifier oid;
  46     private final String name;
  47 
  48     // Curve/field parameters
  49     private final int bits;
  50     private final BigInteger p;
  51     private final int logCofactor;
  52     private final int a24;
  53     private final byte basePoint;
  54 
  55     /**
  56      *
  57      * Construct an object holding the supplied parameters. No parameters are
  58      * checked, so this method always succeeds. This method supports
  59      * Montgomery curves of the form y^2 = x^3 + ax^2 + x.
  60      *
  61      * @param bits The number of relevant bits in a public/private key.
  62      * @param p The prime that defines the finite field.
  63      * @param a24 The value of (a - 2) / 4, where a is the second-degree curve
  64      *            coefficient.
  65      * @param basePoint The point that generates the desired group
  66      * @param logCofactor The base-2 logarithm of the cofactor of the curve
  67      * @param oid
  68      * @param name
  69      */
  70     public XECParameters(int bits, BigInteger p, int a24,
  71                          byte basePoint, int logCofactor,
  72                          ObjectIdentifier oid, String name) {
  73 
  74         this.bits = bits;
  75         this.logCofactor = logCofactor;
  76         this.p = p;
  77         this.a24 = a24;
  78         this.basePoint = basePoint;
  79         this.oid = oid;
  80         this.name = name;
  81 
  82     }
  83 
  84     public int getBits() {
  85         return bits;
  86     }
  87     public int getBytes() {
  88         return (bits + 7) / 8;
  89     }
  90     public int getLogCofactor() {
  91         return logCofactor;
  92     }
  93     public BigInteger getP() {
  94         return p;
  95     }
  96     public int getA24() {
  97         return a24;
  98     }
  99     public byte getBasePoint() {
 100         return basePoint;
 101     }
 102     public ObjectIdentifier getOid() {
 103         return oid;
 104     }
 105     public String getName() {
 106         return name;
 107     }
 108 
 109     private static final Map<Integer, XECParameters> SIZE_MAP;
 110     private static final Map<ObjectIdentifier, XECParameters> OID_MAP;
 111     private static final Map<String, XECParameters> NAME_MAP;
 112 
 113     static {
 114         final BigInteger TWO = BigInteger.valueOf(2);
 115 
 116         Map<Integer, XECParameters> bySize = new HashMap<>();
 117         Map<ObjectIdentifier, XECParameters> byOid = new HashMap<>();
 118         Map<String, XECParameters> byName = new HashMap<>();
 119 
 120         // set up X25519
 121         try {
 122             BigInteger p = TWO.pow(255).subtract(BigInteger.valueOf(19));
 123             addParameters(255, p, 121665, (byte) 0x09, 3,
 124                 new int[]{1, 3, 101, 110}, NamedParameterSpec.X25519.getName(),
 125                 bySize, byOid, byName);
 126 
 127         } catch (IOException ex) {
 128             // Unable to set X25519 parameters---it will be disabled
 129         }
 130 
 131         // set up X448
 132         try {
 133             BigInteger p = TWO.pow(448).subtract(TWO.pow(224))
 134                 .subtract(BigInteger.ONE);
 135             addParameters(448, p, 39081, (byte) 0x05, 2,
 136                 new int[]{1, 3, 101, 111}, NamedParameterSpec.X448.getName(),
 137                 bySize, byOid, byName);
 138 
 139         } catch (IOException ex) {
 140             // Unable to set X448 parameters---it will be disabled
 141         }
 142 
 143         SIZE_MAP = Collections.unmodifiableMap(bySize);
 144         OID_MAP = Collections.unmodifiableMap(byOid);
 145         NAME_MAP = Collections.unmodifiableMap(byName);
 146     }
 147 
 148     private static void addParameters(int bits, BigInteger p, int a24,
 149         byte basePoint, int logCofactor, int[] oidBytes, String name,
 150         Map<Integer, XECParameters> bySize,
 151         Map<ObjectIdentifier, XECParameters> byOid,
 152         Map<String, XECParameters> byName) throws IOException {
 153 
 154         ObjectIdentifier oid = new ObjectIdentifier(oidBytes);
 155         XECParameters params =
 156             new XECParameters(bits, p, a24, basePoint, logCofactor, oid, name);
 157         bySize.put(bits, params);
 158         byOid.put(oid, params);
 159         byName.put(name.toLowerCase(), params);
 160     }
 161 
 162     public static Optional<XECParameters> getByOid(ObjectIdentifier id) {
 163         return Optional.ofNullable(OID_MAP.get(id));
 164     }
 165     public static Optional<XECParameters> getBySize(int size) {
 166         return Optional.ofNullable(SIZE_MAP.get(size));
 167     }
 168     public static Optional<XECParameters> getByName(String name) {
 169         return Optional.ofNullable(NAME_MAP.get(name.toLowerCase()));
 170     }
 171 
 172     public boolean oidEquals(XECParameters other) {
 173         return oid.equals(other.getOid());
 174     }
 175 
 176     // Utility method that is used by the methods below to handle exception
 177     // suppliers
 178     private static
 179     <A, B> Supplier<B> apply(final Function<A, B> func, final A a) {
 180         return new Supplier<B>() {
 181             @Override
 182             public B get() {
 183                 return func.apply(a);
 184             }
 185         };
 186     }
 187 
 188     /**
 189      * Get parameters by key size, or throw an exception if no parameters are
 190      * defined for the specified key size. This method is used in several
 191      * contexts that should throw different exceptions when the parameters
 192      * are not found. The first argument is a function that produces the
 193      * desired exception.
 194      *
 195      * @param exception a function that produces an exception from a string
 196      * @param size the desired key size
 197      * @param <T> the type of exception that is thrown
 198      * @return the parameters for the specified key size
 199      * @throws T when suitable parameters do not exist
 200      */
 201     public static
 202     <T extends Throwable>
 203     XECParameters getBySize(Function<String, T> exception,
 204                             int size) throws T {
 205 
 206         Optional<XECParameters> xecParams = getBySize(size);
 207         return xecParams.orElseThrow(
 208             apply(exception, "Unsupported size: " + size));
 209     }
 210 
 211     /**
 212      * Get parameters by algorithm ID, or throw an exception if no
 213      * parameters are defined for the specified ID. This method is used in
 214      * several contexts that should throw different exceptions when the
 215      * parameters are not found. The first argument is a function that produces
 216      * the desired exception.
 217      *
 218      * @param exception a function that produces an exception from a string
 219      * @param algId the algorithm ID
 220      * @param <T> the type of exception that is thrown
 221      * @return the parameters for the specified algorithm ID
 222      * @throws T when suitable parameters do not exist
 223      */
 224     public static
 225     <T extends Throwable>
 226     XECParameters get(Function<String, T> exception,
 227                       AlgorithmId algId) throws T {
 228 
 229         Optional<XECParameters> xecParams = getByOid(algId.getOID());
 230         return xecParams.orElseThrow(
 231             apply(exception, "Unsupported OID: " + algId.getOID()));
 232     }
 233 
 234     /**
 235      * Get parameters by algorithm parameter spec, or throw an exception if no
 236      * parameters are defined for the spec. This method is used in
 237      * several contexts that should throw different exceptions when the
 238      * parameters are not found. The first argument is a function that produces
 239      * the desired exception.
 240      *
 241      * @param exception a function that produces an exception from a string
 242      * @param params the algorithm parameters spec
 243      * @param <T> the type of exception that is thrown
 244      * @return the parameters for the spec
 245      * @throws T when suitable parameters do not exist
 246      */
 247     public static
 248     <T extends Throwable>
 249     XECParameters get(Function<String, T> exception,
 250                       AlgorithmParameterSpec params) throws T {
 251 
 252         if (params instanceof NamedParameterSpec) {
 253             NamedParameterSpec namedParams = (NamedParameterSpec) params;
 254             Optional<XECParameters> xecParams =
 255                 getByName(namedParams.getName());
 256             return xecParams.orElseThrow(
 257                 apply(exception, "Unsupported name: " + namedParams.getName()));
 258         } else {
 259             throw exception.apply("Only NamedParameterSpec is supported.");
 260         }
 261     }
 262 }
 263