• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.crypto.tink.AccessesPartialKey;
20 import com.google.crypto.tink.InsecureSecretKeyAccess;
21 import com.google.crypto.tink.Key;
22 import com.google.crypto.tink.util.SecretBigInteger;
23 import com.google.errorprone.annotations.CanIgnoreReturnValue;
24 import com.google.errorprone.annotations.RestrictedApi;
25 import java.math.BigInteger;
26 import java.security.GeneralSecurityException;
27 import javax.annotation.Nullable;
28 
29 /**
30  * Represents a private key for RSA SSA PKCS1 signatures.
31  *
32  * <p>Standard: https://www.rfc-editor.org/rfc/rfc8017#section-3.2.
33  */
34 public final class RsaSsaPkcs1PrivateKey extends SignaturePrivateKey {
35   private final RsaSsaPkcs1PublicKey publicKey;
36   private final SecretBigInteger d;
37   private final SecretBigInteger p;
38   private final SecretBigInteger q;
39   private final SecretBigInteger dP;
40   private final SecretBigInteger dQ;
41   private final SecretBigInteger qInv;
42 
43   /** Builder for RsaSsaPkcs1PrivateKey. */
44   public static class Builder {
45     @Nullable private RsaSsaPkcs1PublicKey publicKey = null;
46     @Nullable private SecretBigInteger d = null;
47     @Nullable private SecretBigInteger p = null;
48     @Nullable private SecretBigInteger q = null;
49     @Nullable private SecretBigInteger dP = null;
50     @Nullable private SecretBigInteger dQ = null;
51     @Nullable private SecretBigInteger qInv = null;
52 
Builder()53     private Builder() {}
54 
55     /**
56      * Sets the public key, which includes the parameters.
57      *
58      * <p>This is required.
59      */
60     @CanIgnoreReturnValue
setPublicKey(RsaSsaPkcs1PublicKey publicKey)61     public Builder setPublicKey(RsaSsaPkcs1PublicKey publicKey) {
62       this.publicKey = publicKey;
63       return this;
64     }
65 
66     /**
67      * Sets the prime factors p and q.
68      *
69      * <p>This is required.
70      */
71     @CanIgnoreReturnValue
setPrimes(SecretBigInteger p, SecretBigInteger q)72     public Builder setPrimes(SecretBigInteger p, SecretBigInteger q) {
73       this.p = p;
74       this.q = q;
75       return this;
76     }
77 
78     /**
79      * Sets the private exponent d.
80      *
81      * <p>This is required.
82      */
83     @CanIgnoreReturnValue
setPrivateExponent(SecretBigInteger d)84     public Builder setPrivateExponent(SecretBigInteger d) {
85       this.d = d;
86       return this;
87     }
88 
89     /**
90      * Sets the prime exponents dP and dQ.
91      *
92      * <p>See https://www.rfc-editor.org/rfc/rfc8017#section-3.2.
93      *
94      * <p>This is required.
95      */
96     @CanIgnoreReturnValue
setPrimeExponents(SecretBigInteger dP, SecretBigInteger dQ)97     public Builder setPrimeExponents(SecretBigInteger dP, SecretBigInteger dQ) {
98       this.dP = dP;
99       this.dQ = dQ;
100       return this;
101     }
102 
103     /**
104      * Sets the CRT coefficient qInv.
105      *
106      * <p>See https://www.rfc-editor.org/rfc/rfc8017#section-3.2.
107      *
108      * <p>This is required.
109      */
110     @CanIgnoreReturnValue
setCrtCoefficient(SecretBigInteger qInv)111     public Builder setCrtCoefficient(SecretBigInteger qInv) {
112       this.qInv = qInv;
113       return this;
114     }
115 
116     private static final int PRIME_CERTAINTY = 10;
117 
118     @AccessesPartialKey
build()119     public RsaSsaPkcs1PrivateKey build() throws GeneralSecurityException {
120       if (publicKey == null) {
121         throw new GeneralSecurityException("Cannot build without a RSA SSA PKCS1 public key");
122       }
123       if (p == null || q == null) {
124         throw new GeneralSecurityException("Cannot build without prime factors");
125       }
126       if (d == null) {
127         throw new GeneralSecurityException("Cannot build without private exponent");
128       }
129       if (dP == null || dQ == null) {
130         throw new GeneralSecurityException("Cannot build without prime exponents");
131       }
132       if (qInv == null) {
133         throw new GeneralSecurityException("Cannot build without CRT coefficient");
134       }
135       BigInteger e = publicKey.getParameters().getPublicExponent();
136       BigInteger n = publicKey.getModulus();
137 
138       BigInteger ip = this.p.getBigInteger(InsecureSecretKeyAccess.get());
139       BigInteger iq = this.q.getBigInteger(InsecureSecretKeyAccess.get());
140       BigInteger id = this.d.getBigInteger(InsecureSecretKeyAccess.get());
141       BigInteger idP = this.dP.getBigInteger(InsecureSecretKeyAccess.get());
142       BigInteger idQ = this.dQ.getBigInteger(InsecureSecretKeyAccess.get());
143       BigInteger iqInv = this.qInv.getBigInteger(InsecureSecretKeyAccess.get());
144 
145       if (!ip.isProbablePrime(PRIME_CERTAINTY)) {
146         throw new GeneralSecurityException("p is not a prime");
147       }
148       if (!iq.isProbablePrime(PRIME_CERTAINTY)) {
149         throw new GeneralSecurityException("q is not a prime");
150       }
151       if (!ip.multiply(iq).equals(n)) {
152         throw new GeneralSecurityException(
153             "Prime p times prime q is not equal to the public key's modulus");
154       }
155       // lambda = LCM(p-1, q-1)
156       BigInteger pMinusOne = ip.subtract(BigInteger.ONE);
157       BigInteger qMinusOne = iq.subtract(BigInteger.ONE);
158       BigInteger lambda = pMinusOne.divide(pMinusOne.gcd(qMinusOne)).multiply(qMinusOne);
159       if (!e.multiply(id).mod(lambda).equals(BigInteger.ONE)) {
160         throw new GeneralSecurityException("D is invalid.");
161       }
162       if (!e.multiply(idP).mod(pMinusOne).equals(BigInteger.ONE)) {
163         throw new GeneralSecurityException("dP is invalid.");
164       }
165       if (!e.multiply(idQ).mod(qMinusOne).equals(BigInteger.ONE)) {
166         throw new GeneralSecurityException("dQ is invalid.");
167       }
168       if (!iq.multiply(iqInv).mod(ip).equals(BigInteger.ONE)) {
169         throw new GeneralSecurityException("qInv is invalid.");
170       }
171       return new RsaSsaPkcs1PrivateKey(publicKey, p, q, d, dP, dQ, qInv);
172     }
173   }
174 
RsaSsaPkcs1PrivateKey( RsaSsaPkcs1PublicKey publicKey, SecretBigInteger p, SecretBigInteger q, SecretBigInteger d, SecretBigInteger dP, SecretBigInteger dQ, SecretBigInteger qInv)175   private RsaSsaPkcs1PrivateKey(
176       RsaSsaPkcs1PublicKey publicKey,
177       SecretBigInteger p,
178       SecretBigInteger q,
179       SecretBigInteger d,
180       SecretBigInteger dP,
181       SecretBigInteger dQ,
182       SecretBigInteger qInv) {
183     this.publicKey = publicKey;
184     this.p = p;
185     this.q = q;
186     this.d = d;
187     this.dP = dP;
188     this.dQ = dQ;
189     this.qInv = qInv;
190   }
191 
192   @RestrictedApi(
193       explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey",
194       link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys",
195       allowedOnPath = ".*Test\\.java",
196       allowlistAnnotations = {AccessesPartialKey.class})
builder()197   public static Builder builder() {
198     return new Builder();
199   }
200 
201   /** Returns the key parameters. */
202   @Override
getParameters()203   public RsaSsaPkcs1Parameters getParameters() {
204     return publicKey.getParameters();
205   }
206 
207   /** Returns the public key. */
208   @Override
getPublicKey()209   public RsaSsaPkcs1PublicKey getPublicKey() {
210     return publicKey;
211   }
212 
213   /** Returns the prime factor p. */
214   @RestrictedApi(
215       explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey",
216       link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys",
217       allowedOnPath = ".*Test\\.java",
218       allowlistAnnotations = {AccessesPartialKey.class})
getPrimeP()219   public SecretBigInteger getPrimeP() {
220     return p;
221   }
222 
223   /** Returns the prime factor q. */
224   @RestrictedApi(
225       explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey",
226       link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys",
227       allowedOnPath = ".*Test\\.java",
228       allowlistAnnotations = {AccessesPartialKey.class})
getPrimeQ()229   public SecretBigInteger getPrimeQ() {
230     return q;
231   }
232 
233   /** Returns the private exponent d. */
getPrivateExponent()234   public SecretBigInteger getPrivateExponent() {
235     return d;
236   }
237 
238   /** Returns the prime exponent dP. */
getPrimeExponentP()239   public SecretBigInteger getPrimeExponentP() {
240     return dP;
241   }
242 
243   /** Returns the prime exponent dQ. */
getPrimeExponentQ()244   public SecretBigInteger getPrimeExponentQ() {
245     return dQ;
246   }
247 
248   /** Returns the CRT coefficient qInv. */
getCrtCoefficient()249   public SecretBigInteger getCrtCoefficient() {
250     return qInv;
251   }
252 
253   @Override
equalsKey(Key o)254   public boolean equalsKey(Key o) {
255     if (!(o instanceof RsaSsaPkcs1PrivateKey)) {
256       return false;
257     }
258     RsaSsaPkcs1PrivateKey that = (RsaSsaPkcs1PrivateKey) o;
259     return that.publicKey.equalsKey(publicKey)
260         && p.equalsSecretBigInteger(that.p)
261         && q.equalsSecretBigInteger(that.q)
262         && d.equalsSecretBigInteger(that.d)
263         && dP.equalsSecretBigInteger(that.dP)
264         && dQ.equalsSecretBigInteger(that.dQ)
265         && qInv.equalsSecretBigInteger(that.qInv);
266   }
267 }
268