001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2016 ForgeRock AS. 015 */ 016 017package org.forgerock.json.jose.jws; 018 019import java.math.BigInteger; 020import java.security.interfaces.ECKey; 021import java.security.spec.ECFieldFp; 022import java.security.spec.ECParameterSpec; 023import java.security.spec.ECPoint; 024import java.security.spec.EllipticCurve; 025import java.util.Objects; 026import javax.xml.bind.DatatypeConverter; 027 028/** 029 * Enumerates all supported elliptic curve parameters for ESXXX signature formats. 030 */ 031public enum SupportedEllipticCurve { 032 /** NIST P-256. */ 033 P256("P-256", StandardCurve.P_256, 64, JwsAlgorithm.ES256), 034 /** NIST P-384. */ 035 P384("P-384", StandardCurve.P_384, 96, JwsAlgorithm.ES384), 036 /** NIST P-521. Please note that this is not a typo: ES512 uses curve <em>P-521</em>, which produces a 132-octet 037 * signature value. */ 038 P521("P-521", StandardCurve.P_521, 132, JwsAlgorithm.ES512); 039 040 041 private final ECParameterSpec parameters; 042 private final String standardName; 043 private final int signatureSize; 044 private final JwsAlgorithm jwsAlgorithm; 045 046 SupportedEllipticCurve(String standardName, ECParameterSpec curve, int signatureSize, JwsAlgorithm jwsAlgorithm) { 047 this.parameters = curve; 048 this.standardName = standardName; 049 this.signatureSize = signatureSize; 050 this.jwsAlgorithm = jwsAlgorithm; 051 } 052 053 /** 054 * Returns the parameters for the given elliptic curve. 055 * 056 * @return the elliptic curve algorithm parameters. 057 */ 058 public ECParameterSpec getParameters() { 059 return parameters; 060 } 061 062 /** 063 * Return the name of the curve as used for the "crv" claim in a JWK. 064 * 065 * @return the standard JWA name for the curve. 066 */ 067 public String getStandardName() { 068 return standardName; 069 } 070 071 /** 072 * Returns the size of the signature produced by this curve in octets. 073 * 074 * @return the number of octets (bytes) required to hold a signature of this curve. 075 */ 076 public int getSignatureSize() { 077 return signatureSize; 078 } 079 080 /** 081 * Returns the JwsAlgorithm that corresponds to this elliptic curve. 082 * 083 * @return the corresponding JwsAlgorithm. 084 */ 085 public JwsAlgorithm getJwsAlgorithm() { 086 return jwsAlgorithm; 087 } 088 089 /** 090 * Returns the curve parameters for the given standard curve name (crv claim in a JWK). 091 * 092 * @param curveName the curve name. 093 * @return the curve parameters for the name. 094 * @throws IllegalArgumentException if the curve name is not supported. 095 */ 096 public static SupportedEllipticCurve forName(final String curveName) { 097 for (SupportedEllipticCurve candidate : values()) { 098 if (candidate.getStandardName().equals(curveName)) { 099 return candidate; 100 } 101 } 102 throw new IllegalArgumentException("Unsupported curve: " + curveName); 103 } 104 105 /** 106 * Determines the standard curve that matches the given (private or public) key. This is done by comparing the 107 * key parameters for an <em>exact</em> match against one of the standard curves. All parameters much match for a 108 * match to succeed. 109 * 110 * @param key the private or public key to determine the curve for. 111 * @return the matching supported curve parameters. 112 * @throws IllegalArgumentException if the key does not match any supported curve parameters. 113 */ 114 public static SupportedEllipticCurve forKey(final ECKey key) { 115 final ECParameterSpec params = key.getParams(); 116 for (SupportedEllipticCurve supported : values()) { 117 final ECParameterSpec candidateParams = supported.getParameters(); 118 if (candidateParams.getCofactor() == params.getCofactor() 119 && Objects.equals(candidateParams.getCurve(), params.getCurve()) 120 && Objects.equals(candidateParams.getGenerator(), params.getGenerator()) 121 && Objects.equals(candidateParams.getOrder(), params.getOrder())) { 122 return supported; 123 } 124 } 125 throw new IllegalArgumentException("Unsupported ECKey parameters"); 126 } 127 128 /** 129 * Determines the supported curve parameters for the given signature. This is done purely based on the length of 130 * the signature and the behaviour is not specified if multiple curves could have produced this signature. 131 * 132 * @param signature the signature to match. 133 * @return the curve that produced this signature. 134 * @throws IllegalArgumentException if the signature does not match any supported curve parameters. 135 */ 136 public static SupportedEllipticCurve forSignature(byte[] signature) { 137 for (SupportedEllipticCurve candidate : values()) { 138 if (signature.length == candidate.getSignatureSize()) { 139 return candidate; 140 } 141 } 142 throw new IllegalArgumentException("Unsupported signature size: " + signature.length); 143 } 144 145 /** 146 * NIST standard elliptic curve parameters as specified in the JSON Web Algorithms (JWA) spec and defined in 147 * <a href="http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf">FIPS 186-4</a> section D.1.2.3 (P-256), 148 * D.1.2.4 (P-384) and D.1.2.5 (P-521). Defined as a separate inner class to avoid illegal forward-reference 149 * problems when constructing the elements of the SupportedEllipticCurve enum. 150 * 151 * <p> 152 * ECDSA uses an elliptic curve defined by all the points from a finite field that satisfy the equation: 153 * <em>y</em><sup>2</sup> = <em>x</em><sup>3</sup> + <em>ax</em> + <em>b</em> where <em>a</em> and <em>b</em> are 154 * the coefficients. For the curves we are interested in for JWA, the finite fields are produced by the integers 155 * modulo some large prime <em>p</em>, so the arithmetic for the above equation is all done modulo <em>p</em>. 156 * In addition, we define a base point on the curve, known as the <em>generator</em> and denoted <em>G</em> (with 157 * components <em>G<sub>x</sub></em> and <em>G<sub>y</sub></em>), such that the <em>order</em> (number of 158 * elements) of the resulting curve is a large prime, <em>n</em>. The number of points on the curve is actually 159 * given by <em>hn</em> where <em>h</em> is the cofactor, but h is fixed to be 1 for all NIST curves so we 160 * ignore it here. 161 * 162 * <p> 163 * The names of the curves (e.g. P-256) are given by the length of the prime modulus, <em>p</em>, in bits. So for 164 * P-256 the prime is 256 bits long when written in binary, etc. 165 * 166 * <p> 167 * The Java {@link ECParameterSpec} expects parameters in a slightly different format from how they are defined 168 * in the NIST specification: 169 * <table> 170 * <thead> 171 * <tr><th>NIST Parameter</th><th>Java Parameter</th><th>Description</th></tr> 172 * </thead> 173 * <tbody> 174 * <tr><td><em>p</em></td><td><code>p</code></td><td>The prime modulus</td></tr> 175 * <tr><td><em>n</em></td><td><code>n</code></td><td>The order of the field</td></tr> 176 * <tr><td><em>SEED</em></td><td><code>seed</code></td><td>Seed value to SHA-1 used to generate 177 * the coefficients of the curve as per the algorithm in <a 178 * href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.202.2977&rep=rep1&type=pdf">ANSI 179 * X9.62</a> Annex A.3.3. This can be used to verify that the coefficients have been generated 180 * pseudo-randomly via the algorithm in A.3.4.2.</td> 181 * </tr> 182 * <tr><td><em>c</em></td><td>n/a</td><td>The output of the SHA-1 curve generation algorithm (this is 183 * called W in the ANSI X9.62 algorithm linked above). It should hold that <em>c</em> * 184 * <em>b</em><sup>2</sup> = <em>a</em><sup>3</sup> (mod p).</td></tr> 185 * <tr><td>n/a</td><td><code>a</code></td><td>The first coefficient of the curve equation. For all the NIST 186 * standard prime curves this is fixed as -3 (mod <em>p</em>).</td>/tr> 187 * <tr><td><em>b</em></td><td><code>b</code></td><td>The second coefficient of the curve equation.</td></tr> 188 * <tr><td><em>G<sub>x</sub></em></td><td><code>x</code></td><td>The x-coordinate of the generator point G. 189 * </td></tr> 190 * <tr><td><em>G<sub>y</sub></em></td><td><code>y</code></td><td>The y-coordinate of the generator point G. 191 * </td></tr> 192 * </tbody> 193 * </table> 194 * <p> 195 * Note that the <em>seed</em> and <em>c</em> values are not required after the coefficients <em>a</em> and 196 * <em>b</em> have been generated. They can be used to verify that the coefficients were pseudo-randomly 197 * generated and not picked by hand (which might indicate a backdoor). We include the seed value for completeness 198 * of the algorithm parameters (ECParameterSpec does not have the ability to specify <em>c</em>, but it can be 199 * derived from the seed and the coefficients). 200 */ 201 private static class StandardCurve { 202 private static final int H = 1; 203 204 /** 205 * The P-256 curve. 206 */ 207 private static final ECParameterSpec P_256 = new ECParameterSpec( 208 new EllipticCurve( 209 p("115792089210356248762697446949407573530086143415290314195533631308867097853951"), 210 a("115792089210356248762697446949407573530086143415290314195533631308867097853948"), 211 b("41058363725152142129326129780047268409114441015993725554835256314039467401291"), 212 seed("c49d3608 86e70493 6a6678e1 139d26b7 819f7e90")), 213 new ECPoint(x("48439561293906451759052585252797914202762949526041747995844080717082404635286"), 214 y("36134250956749795798585127919587881956611106672985015071877198253568414405109")), 215 n("115792089210356248762697446949407573529996955224135760342422259061068512044369"), H); 216 217 /** 218 * The P-384 curve. 219 */ 220 private static final ECParameterSpec P_384 = new ECParameterSpec( 221 new EllipticCurve( 222 p("3940200619639447921227904010014361380507973927046544666794829340424572177149687032904726" 223 + "6088258938001861606973112319"), 224 a("39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088" 225 + "258938001861606973112316"), 226 b("27580193559959705877849011840389048093056905856361568521428707301988689241309860865136260764" 227 + "883745107765439761230575"), 228 seed("a335926a a319a27a 1d00896a 6773a482 7acdac73")), 229 new ECPoint( 230 x("26247035095799689268623156744566981891852923491109213387815615900925518854738050089022388053" 231 + "975719786650872476732087"), 232 y("83257109614890299855467512895201081792878530488613155947092059024805031998844192244386437603" 233 + "92947333078086511627871")), 234 n("3940200619639447921227904010014361380507973927046544666794690527962765939911326356939895630815229491" 235 + "3554433653942643"), H); 236 237 /** 238 * The P-521 curve. 239 */ 240 private static final ECParameterSpec P_521 = new ECParameterSpec( 241 new EllipticCurve( 242 p("68647976601306097149819007990813932172694353001433054093944634591855431833976560521225596406" 243 + "61454554977296311391480858037121987999716643812574028291115057151"), 244 a("68647976601306097149819007990813932172694353001433054093944634591855431833976560521225596406" 245 + "61454554977296311391480858037121987999716643812574028291115057148"), 246 b("10938490380737342745111123907668055699362075989516837489945863944959531161507350160137087375" 247 + "73759623248592132296706313309438452531591012912142327488478985984"), 248 seed("d09e8800 291cb853 96cc6717 393284aa a0da64ba")), 249 new ECPoint( 250 x("26617408020502170632287687167233609607298591687569731477066713684188029449964278084915450806" 251 + "27771902352094241225065558662157113545570916814161637315895999846"), 252 y("37571800257700204635455072244911836035944551347697624866945677796155444774405563166912344050" 253 + "12945539562144444537289428522585666729196580810124344277578376784")), 254 n("6864797660130609714981900799081393217269435300143305409394463459185543183397655394245057746333217197" 255 + "532963996371363321113864768612440380340372808892707005449"), H); 256 257 258 private static ECFieldFp p(final String value) { 259 return new ECFieldFp(new BigInteger(value)); 260 } 261 262 private static BigInteger a(final String value) { 263 return new BigInteger(value); 264 } 265 266 private static BigInteger b(final String value) { 267 return new BigInteger(value); 268 } 269 270 private static BigInteger x(final String value) { 271 return new BigInteger(value); 272 } 273 274 private static BigInteger y(final String value) { 275 return new BigInteger(value); 276 } 277 278 private static BigInteger n(final String value) { 279 return new BigInteger(value); 280 } 281 282 private static byte[] seed(final String hex) { 283 return DatatypeConverter.parseHexBinary(hex.replaceAll("\\s+", "")); 284 } 285 } 286}