1 // Copyright 2022 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 //////////////////////////////////////////////////////////////////////////////// 16 17 package com.google.crypto.tink.signature; 18 19 import com.google.crypto.tink.internal.EllipticCurvesUtil; 20 import com.google.errorprone.annotations.CanIgnoreReturnValue; 21 import com.google.errorprone.annotations.Immutable; 22 import java.security.GeneralSecurityException; 23 import java.security.spec.ECParameterSpec; 24 import java.util.Objects; 25 26 /** Describes the parameters of an ECDSA signature primitive. */ 27 public final class EcdsaParameters extends SignatureParameters { 28 /** 29 * Describes details of the ECDSA signature computation. 30 * 31 * <p>The standard ECDSA key is used for variant "NO_PREFIX". Other variants slightly change how 32 * the signature is computed, or add a prefix to every computation depending on the key id. 33 */ 34 @Immutable 35 public static final class Variant { 36 public static final Variant TINK = new Variant("TINK"); 37 public static final Variant CRUNCHY = new Variant("CRUNCHY"); 38 public static final Variant LEGACY = new Variant("LEGACY"); 39 public static final Variant NO_PREFIX = new Variant("NO_PREFIX"); 40 41 private final String name; 42 Variant(String name)43 private Variant(String name) { 44 this.name = name; 45 } 46 47 @Override toString()48 public String toString() { 49 return name; 50 } 51 } 52 53 /** The encoding used in the signature. */ 54 @Immutable 55 public static final class SignatureEncoding { 56 public static final SignatureEncoding IEEE_P1363 = new SignatureEncoding("IEEE_P1363"); 57 public static final SignatureEncoding DER = new SignatureEncoding("DER"); 58 59 private final String name; 60 SignatureEncoding(String name)61 private SignatureEncoding(String name) { 62 this.name = name; 63 } 64 65 @Override toString()66 public String toString() { 67 return name; 68 } 69 } 70 71 /** The elliptic curve and its parameters. */ 72 @Immutable 73 public static final class CurveType { 74 public static final CurveType NIST_P256 = 75 new CurveType("NIST_P256", EllipticCurvesUtil.NIST_P256_PARAMS); 76 public static final CurveType NIST_P384 = 77 new CurveType("NIST_P384", EllipticCurvesUtil.NIST_P384_PARAMS); 78 public static final CurveType NIST_P521 = 79 new CurveType("NIST_P521", EllipticCurvesUtil.NIST_P521_PARAMS); 80 81 private final String name; 82 @SuppressWarnings("Immutable") // ECParameterSpec is immutable 83 private final ECParameterSpec spec; 84 CurveType(String name, ECParameterSpec spec)85 private CurveType(String name, ECParameterSpec spec) { 86 this.name = name; 87 this.spec = spec; 88 } 89 90 @Override toString()91 public String toString() { 92 return name; 93 } 94 toParameterSpec()95 public ECParameterSpec toParameterSpec() { 96 return spec; 97 } 98 fromParameterSpec(ECParameterSpec spec)99 public static CurveType fromParameterSpec(ECParameterSpec spec) 100 throws GeneralSecurityException { 101 if (EllipticCurvesUtil.isSameEcParameterSpec(spec, NIST_P256.toParameterSpec())) { 102 return NIST_P256; 103 } 104 if (EllipticCurvesUtil.isSameEcParameterSpec(spec, NIST_P384.toParameterSpec())) { 105 return NIST_P384; 106 } 107 if (EllipticCurvesUtil.isSameEcParameterSpec(spec, NIST_P521.toParameterSpec())) { 108 return NIST_P521; 109 } 110 throw new GeneralSecurityException("unknown ECParameterSpec"); 111 } 112 } 113 114 /** The Hash algorithm used. */ 115 @Immutable 116 public static final class HashType { 117 public static final HashType SHA256 = new HashType("SHA256"); 118 public static final HashType SHA384 = new HashType("SHA384"); 119 public static final HashType SHA512 = new HashType("SHA512"); 120 121 private final String name; 122 HashType(String name)123 private HashType(String name) { 124 this.name = name; 125 } 126 127 @Override toString()128 public String toString() { 129 return name; 130 } 131 } 132 133 /** Builds a new EcdsaParameters instance. */ 134 public static final class Builder { 135 private SignatureEncoding signatureEncoding = null; 136 private CurveType curveType = null; 137 private HashType hashType = null; 138 private Variant variant = Variant.NO_PREFIX; 139 Builder()140 private Builder() {} 141 142 @CanIgnoreReturnValue setSignatureEncoding(SignatureEncoding signatureEncoding)143 public Builder setSignatureEncoding(SignatureEncoding signatureEncoding) { 144 this.signatureEncoding = signatureEncoding; 145 return this; 146 } 147 148 @CanIgnoreReturnValue setCurveType(CurveType curveType)149 public Builder setCurveType(CurveType curveType) { 150 this.curveType = curveType; 151 return this; 152 } 153 154 @CanIgnoreReturnValue setHashType(HashType hashType)155 public Builder setHashType(HashType hashType) { 156 this.hashType = hashType; 157 return this; 158 } 159 160 @CanIgnoreReturnValue setVariant(Variant variant)161 public Builder setVariant(Variant variant) { 162 this.variant = variant; 163 return this; 164 } 165 build()166 public EcdsaParameters build() throws GeneralSecurityException { 167 if (signatureEncoding == null) { 168 throw new GeneralSecurityException("signature encoding is not set"); 169 } 170 if (curveType == null) { 171 throw new GeneralSecurityException("EC curve type is not set"); 172 } 173 if (hashType == null) { 174 throw new GeneralSecurityException("hash type is not set"); 175 } 176 if (variant == null) { 177 throw new GeneralSecurityException("variant is not set"); 178 } 179 180 if (curveType == CurveType.NIST_P256) { 181 if (hashType != HashType.SHA256) { 182 throw new GeneralSecurityException("NIST_P256 requires SHA256"); 183 } 184 } 185 if (curveType == CurveType.NIST_P384) { 186 if (hashType != HashType.SHA384 && hashType != HashType.SHA512) { 187 throw new GeneralSecurityException("NIST_P384 requires SHA384 or SHA512"); 188 } 189 } 190 if (curveType == CurveType.NIST_P521) { 191 if (hashType != HashType.SHA512) { 192 throw new GeneralSecurityException("NIST_P521 requires SHA512"); 193 } 194 } 195 return new EcdsaParameters(signatureEncoding, curveType, hashType, variant); 196 } 197 } 198 199 private final SignatureEncoding signatureEncoding; 200 private final CurveType curveType; 201 private final HashType hashType; 202 private final Variant variant; 203 EcdsaParameters( SignatureEncoding signatureEncoding, CurveType curveType, HashType hashType, Variant variant)204 private EcdsaParameters( 205 SignatureEncoding signatureEncoding, 206 CurveType curveType, 207 HashType hashType, 208 Variant variant) { 209 this.signatureEncoding = signatureEncoding; 210 this.curveType = curveType; 211 this.hashType = hashType; 212 this.variant = variant; 213 } 214 builder()215 public static Builder builder() { 216 return new Builder(); 217 } 218 getSignatureEncoding()219 public SignatureEncoding getSignatureEncoding() { 220 return signatureEncoding; 221 } 222 getCurveType()223 public CurveType getCurveType() { 224 return curveType; 225 } 226 getHashType()227 public HashType getHashType() { 228 return hashType; 229 } 230 getVariant()231 public Variant getVariant() { 232 return variant; 233 } 234 235 @Override equals(Object o)236 public boolean equals(Object o) { 237 if (!(o instanceof EcdsaParameters)) { 238 return false; 239 } 240 EcdsaParameters that = (EcdsaParameters) o; 241 return that.getSignatureEncoding() == getSignatureEncoding() 242 && that.getCurveType() == getCurveType() 243 && that.getHashType() == getHashType() 244 && that.getVariant() == getVariant(); 245 } 246 247 @Override hashCode()248 public int hashCode() { 249 return Objects.hash(EcdsaParameters.class, signatureEncoding, curveType, hashType, variant); 250 } 251 252 @Override hasIdRequirement()253 public boolean hasIdRequirement() { 254 return variant != Variant.NO_PREFIX; 255 } 256 257 @Override toString()258 public String toString() { 259 return "ECDSA Parameters (variant: " 260 + variant 261 + ", hashType: " 262 + hashType 263 + ", encoding: " 264 + signatureEncoding 265 + ", curve: " 266 + curveType 267 + ")"; 268 } 269 } 270