1 // Copyright 2023 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.errorprone.annotations.CanIgnoreReturnValue; 20 import com.google.errorprone.annotations.Immutable; 21 import java.math.BigInteger; 22 import java.security.GeneralSecurityException; 23 import java.security.InvalidAlgorithmParameterException; 24 import java.util.Objects; 25 import javax.annotation.Nullable; 26 27 /** 28 * Describes the parameters of a {@link RsaSsaPkcs1PublicKey} and {@link RsaSsaPkcs1PrivateKey}. 29 * 30 * <p>Standard: https://www.rfc-editor.org/rfc/rfc8017.txt 31 */ 32 public final class RsaSsaPkcs1Parameters extends SignatureParameters { 33 /** 34 * Describes details of the signature. 35 * 36 * <p>The usual key is used for variant "NO_PREFIX". Other variants slightly change how the 37 * signature is computed, or add a prefix to every computation depending on the key id. 38 */ 39 @Immutable 40 public static final class Variant { 41 public static final Variant TINK = new Variant("TINK"); 42 public static final Variant CRUNCHY = new Variant("CRUNCHY"); 43 public static final Variant LEGACY = new Variant("LEGACY"); 44 public static final Variant NO_PREFIX = new Variant("NO_PREFIX"); 45 46 private final String name; 47 Variant(String name)48 private Variant(String name) { 49 this.name = name; 50 } 51 52 @Override toString()53 public String toString() { 54 return name; 55 } 56 } 57 58 /** The Hash algorithm used. */ 59 @Immutable 60 public static final class HashType { 61 public static final HashType SHA256 = new HashType("SHA256"); 62 public static final HashType SHA384 = new HashType("SHA384"); 63 public static final HashType SHA512 = new HashType("SHA512"); 64 65 private final String name; 66 HashType(String name)67 private HashType(String name) { 68 this.name = name; 69 } 70 71 @Override toString()72 public String toString() { 73 return name; 74 } 75 } 76 77 public static final BigInteger F4 = BigInteger.valueOf(65537); 78 79 /** Builds a new RsaSsaPkcs1Parameters instance. */ 80 public static final class Builder { 81 @Nullable private Integer modulusSizeBits = null; 82 @Nullable private BigInteger publicExponent = F4; 83 @Nullable private HashType hashType = null; 84 private Variant variant = Variant.NO_PREFIX; 85 Builder()86 private Builder() {} 87 88 @CanIgnoreReturnValue setModulusSizeBits(int modulusSizeBits)89 public Builder setModulusSizeBits(int modulusSizeBits) { 90 this.modulusSizeBits = modulusSizeBits; 91 return this; 92 } 93 94 @CanIgnoreReturnValue setPublicExponent(BigInteger e)95 public Builder setPublicExponent(BigInteger e) { 96 this.publicExponent = e; 97 return this; 98 } 99 100 @CanIgnoreReturnValue setVariant(Variant variant)101 public Builder setVariant(Variant variant) { 102 this.variant = variant; 103 return this; 104 } 105 106 @CanIgnoreReturnValue setHashType(HashType hashType)107 public Builder setHashType(HashType hashType) { 108 this.hashType = hashType; 109 return this; 110 } 111 112 private static final BigInteger TWO = BigInteger.valueOf(2); 113 private static final BigInteger PUBLIC_EXPONENT_UPPER_BOUND = TWO.pow(256); 114 validatePublicExponent(BigInteger publicExponent)115 private void validatePublicExponent(BigInteger publicExponent) 116 throws InvalidAlgorithmParameterException { 117 // We use the validation of the public exponent as defined in 118 // https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf, B.3 119 int c = publicExponent.compareTo(F4); 120 if (c == 0) { 121 // publicExponent is F4. 122 return; 123 } 124 if (c < 0) { 125 // publicExponent is smaller than F4. 126 throw new InvalidAlgorithmParameterException("Public exponent must be at least 65537."); 127 } 128 if (publicExponent.mod(TWO).equals(BigInteger.ZERO)) { 129 // publicExponent is even. This is invalid since it is not co-prime to p-1. 130 throw new InvalidAlgorithmParameterException("Invalid public exponent"); 131 } 132 if (publicExponent.compareTo(PUBLIC_EXPONENT_UPPER_BOUND) > 0) { 133 // publicExponent is larger than PUBLIC_EXPONENT_UPPER_BOUND. 134 throw new InvalidAlgorithmParameterException( 135 "Public exponent cannot be larger than 2^256."); 136 } 137 } 138 build()139 public RsaSsaPkcs1Parameters build() throws GeneralSecurityException { 140 if (modulusSizeBits == null) { 141 throw new GeneralSecurityException("key size is not set"); 142 } 143 if (publicExponent == null) { 144 throw new GeneralSecurityException("publicExponent is not set"); 145 } 146 if (hashType == null) { 147 throw new GeneralSecurityException("hash type is not set"); 148 } 149 if (variant == null) { 150 throw new GeneralSecurityException("variant is not set"); 151 } 152 if (modulusSizeBits < 2048) { 153 throw new InvalidAlgorithmParameterException( 154 String.format( 155 "Invalid key size in bytes %d; must be at least 2048 bits", modulusSizeBits)); 156 } 157 validatePublicExponent(publicExponent); 158 return new RsaSsaPkcs1Parameters(modulusSizeBits, publicExponent, variant, hashType); 159 } 160 } 161 162 private final int modulusSizeBits; 163 private final BigInteger publicExponent; 164 private final Variant variant; 165 private final HashType hashType; 166 RsaSsaPkcs1Parameters( int modulusSizeBits, BigInteger publicExponent, Variant variant, HashType hashType)167 private RsaSsaPkcs1Parameters( 168 int modulusSizeBits, BigInteger publicExponent, Variant variant, HashType hashType) { 169 this.modulusSizeBits = modulusSizeBits; 170 this.publicExponent = publicExponent; 171 this.variant = variant; 172 this.hashType = hashType; 173 } 174 builder()175 public static Builder builder() { 176 return new Builder(); 177 } 178 getModulusSizeBits()179 public int getModulusSizeBits() { 180 return modulusSizeBits; 181 } 182 getPublicExponent()183 public BigInteger getPublicExponent() { 184 return publicExponent; 185 } 186 getVariant()187 public Variant getVariant() { 188 return variant; 189 } 190 getHashType()191 public HashType getHashType() { 192 return hashType; 193 } 194 195 @Override equals(Object o)196 public boolean equals(Object o) { 197 if (!(o instanceof RsaSsaPkcs1Parameters)) { 198 return false; 199 } 200 RsaSsaPkcs1Parameters that = (RsaSsaPkcs1Parameters) o; 201 return that.getModulusSizeBits() == getModulusSizeBits() 202 && Objects.equals(that.getPublicExponent(), getPublicExponent()) 203 && that.getVariant() == getVariant() 204 && that.getHashType() == getHashType(); 205 } 206 207 @Override hashCode()208 public int hashCode() { 209 return Objects.hash( 210 RsaSsaPkcs1Parameters.class, modulusSizeBits, publicExponent, variant, hashType); 211 } 212 213 @Override hasIdRequirement()214 public boolean hasIdRequirement() { 215 return variant != Variant.NO_PREFIX; 216 } 217 218 @Override toString()219 public String toString() { 220 return "RSA SSA PKCS1 Parameters (variant: " 221 + variant 222 + ", hashType: " 223 + hashType 224 + ", publicExponent: " 225 + publicExponent 226 + ", and " 227 + modulusSizeBits 228 + "-bit modulus)"; 229 } 230 } 231