xref: /aosp_15_r20/external/tink/java_src/src/main/java/com/google/crypto/tink/hybrid/internal/HpkeContext.java (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
1 // Copyright 2021 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.hybrid.internal;
18 
19 import com.google.crypto.tink.internal.BigIntegerEncoding;
20 import com.google.crypto.tink.proto.HpkePublicKey;
21 import com.google.crypto.tink.subtle.Bytes;
22 import java.math.BigInteger;
23 import java.security.GeneralSecurityException;
24 import javax.annotation.concurrent.GuardedBy;
25 import javax.annotation.concurrent.ThreadSafe;
26 
27 /**
28  * Hybrid Public Key Encryption (HPKE) context for either a sender or a recipient.
29  *
30  * <p>https://www.rfc-editor.org/rfc/rfc9180.html#name-creating-the-encryption-con
31  */
32 @ThreadSafe
33 final class HpkeContext {
34   private static final byte[] EMPTY_IKM = new byte[0];
35 
36   private final HpkeAead aead;
37   private final BigInteger maxSequenceNumber;
38   private final byte[] key;
39   private final byte[] baseNonce;
40   private final byte[] encapsulatedKey;
41 
42   @GuardedBy("this")
43   private BigInteger sequenceNumber;
44 
HpkeContext( byte[] encapsulatedKey, byte[] key, byte[] baseNonce, BigInteger maxSequenceNumber, HpkeAead aead)45   private HpkeContext(
46       byte[] encapsulatedKey,
47       byte[] key,
48       byte[] baseNonce,
49       BigInteger maxSequenceNumber,
50       HpkeAead aead) {
51     this.encapsulatedKey = encapsulatedKey;
52     this.key = key;
53     this.baseNonce = baseNonce;
54     this.sequenceNumber = BigInteger.ZERO;
55     this.maxSequenceNumber = maxSequenceNumber;
56     this.aead = aead;
57   }
58 
59   /** Helper function factored out to facilitate unit testing. */
createContext( byte[] mode, byte[] encapsulatedKey, byte[] sharedSecret, HpkeKem kem, HpkeKdf kdf, HpkeAead aead, byte[] info)60   static HpkeContext createContext(
61       byte[] mode,
62       byte[] encapsulatedKey,
63       byte[] sharedSecret,
64       HpkeKem kem,
65       HpkeKdf kdf,
66       HpkeAead aead,
67       byte[] info)
68       throws GeneralSecurityException {
69     byte[] suiteId = HpkeUtil.hpkeSuiteId(kem.getKemId(), kdf.getKdfId(), aead.getAeadId());
70     byte[] pskIdHash = kdf.labeledExtract(HpkeUtil.EMPTY_SALT, EMPTY_IKM, "psk_id_hash", suiteId);
71     byte[] infoHash = kdf.labeledExtract(HpkeUtil.EMPTY_SALT, info, "info_hash", suiteId);
72     byte[] keyScheduleContext = Bytes.concat(mode, pskIdHash, infoHash);
73     byte[] secret = kdf.labeledExtract(sharedSecret, EMPTY_IKM, "secret", suiteId);
74 
75     byte[] key = kdf.labeledExpand(secret, keyScheduleContext, "key", suiteId, aead.getKeyLength());
76     byte[] baseNonce =
77         kdf.labeledExpand(secret, keyScheduleContext, "base_nonce", suiteId, aead.getNonceLength());
78     BigInteger maxSeqNo = maxSequenceNumber(aead.getNonceLength());
79 
80     return new HpkeContext(encapsulatedKey, key, baseNonce, maxSeqNo, aead);
81   }
82 
83   /**
84    * Creates HPKE sender context according to KeySchedule() defined in
85    * https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1-9.
86    *
87    * @param recipientPublicKey recipient's public key (pkR)
88    * @param kem key encapsulation mechanism primitive
89    * @param kdf key derivation function primitive
90    * @param aead authenticated encryption with associated data primitive
91    * @param info application-specific information parameter to influence key generation
92    */
createSenderContext( HpkePublicKey recipientPublicKey, HpkeKem kem, HpkeKdf kdf, HpkeAead aead, byte[] info)93   static HpkeContext createSenderContext(
94       HpkePublicKey recipientPublicKey, HpkeKem kem, HpkeKdf kdf, HpkeAead aead, byte[] info)
95       throws GeneralSecurityException {
96     HpkeKemEncapOutput encapOutput =
97         kem.encapsulate(recipientPublicKey.getPublicKey().toByteArray());
98     byte[] encapsulatedKey = encapOutput.getEncapsulatedKey();
99     byte[] sharedSecret = encapOutput.getSharedSecret();
100     return createContext(HpkeUtil.BASE_MODE, encapsulatedKey, sharedSecret, kem, kdf, aead, info);
101   }
102 
103   /**
104    * Creates HPKE sender context with authentication according to KeySchedule() defined in
105    * https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1.3.
106    *
107    * @param recipientPublicKey recipient's public key (pkR)
108    * @param kem key encapsulation mechanism primitive
109    * @param kdf key derivation function primitive
110    * @param aead authenticated encryption with associated data primitive
111    * @param info application-specific information parameter to influence key generation
112    * @param senderPrivateKey sender's private key (skS)
113    */
createAuthSenderContext( HpkePublicKey recipientPublicKey, HpkeKem kem, HpkeKdf kdf, HpkeAead aead, byte[] info, HpkeKemPrivateKey senderPrivateKey)114   static HpkeContext createAuthSenderContext(
115       HpkePublicKey recipientPublicKey,
116       HpkeKem kem,
117       HpkeKdf kdf,
118       HpkeAead aead,
119       byte[] info,
120       HpkeKemPrivateKey senderPrivateKey)
121       throws GeneralSecurityException {
122     HpkeKemEncapOutput encapOutput =
123         kem.authEncapsulate(recipientPublicKey.getPublicKey().toByteArray(), senderPrivateKey);
124     byte[] encapsulatedKey = encapOutput.getEncapsulatedKey();
125     byte[] sharedSecret = encapOutput.getSharedSecret();
126     return createContext(HpkeUtil.AUTH_MODE, encapsulatedKey, sharedSecret, kem, kdf, aead, info);
127   }
128 
129   /**
130    * Creates HPKE sender recipient context according to KeySchedule() defined in
131    * https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1-9.
132    *
133    * @param encapsulatedKey encapsulated key (enc)
134    * @param recipientPrivateKey recipient's private key (skR)
135    * @param kem key encapsulation mechanism primitive
136    * @param kdf key derivation function primitive
137    * @param aead authenticated encryption with associated data primitive
138    * @param info application-specific information parameter to influence key generation
139    */
createRecipientContext( byte[] encapsulatedKey, HpkeKemPrivateKey recipientPrivateKey, HpkeKem kem, HpkeKdf kdf, HpkeAead aead, byte[] info)140   static HpkeContext createRecipientContext(
141       byte[] encapsulatedKey,
142       HpkeKemPrivateKey recipientPrivateKey,
143       HpkeKem kem,
144       HpkeKdf kdf,
145       HpkeAead aead,
146       byte[] info)
147       throws GeneralSecurityException {
148     byte[] sharedSecret = kem.decapsulate(encapsulatedKey, recipientPrivateKey);
149     return createContext(HpkeUtil.BASE_MODE, encapsulatedKey, sharedSecret, kem, kdf, aead, info);
150   }
151 
152   /**
153    * Creates HPKE recipient context with authentication according to KeySchedule() defined in
154    * https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1.3.
155    *
156    * @param encapsulatedKey encapsulated key (enc)
157    * @param recipientPrivateKey recipient's private key (skR)
158    * @param kem key encapsulation mechanism primitive
159    * @param kdf key derivation function primitive
160    * @param aead authenticated encryption with associated data primitive
161    * @param info application-specific information parameter to influence key generation
162    * @param senderPublicKey sender's public key (pkS)
163    */
createAuthRecipientContext( byte[] encapsulatedKey, HpkeKemPrivateKey recipientPrivateKey, HpkeKem kem, HpkeKdf kdf, HpkeAead aead, byte[] info, HpkePublicKey senderPublicKey)164   static HpkeContext createAuthRecipientContext(
165       byte[] encapsulatedKey,
166       HpkeKemPrivateKey recipientPrivateKey,
167       HpkeKem kem,
168       HpkeKdf kdf,
169       HpkeAead aead,
170       byte[] info,
171       HpkePublicKey senderPublicKey)
172       throws GeneralSecurityException {
173     byte[] sharedSecret =
174         kem.authDecapsulate(
175             encapsulatedKey, recipientPrivateKey, senderPublicKey.getPublicKey().toByteArray());
176     return createContext(HpkeUtil.AUTH_MODE, encapsulatedKey, sharedSecret, kem, kdf, aead, info);
177   }
178 
maxSequenceNumber(int nonceLength)179   private static BigInteger maxSequenceNumber(int nonceLength) {
180     return BigInteger.ONE.shiftLeft(8 * nonceLength).subtract(BigInteger.ONE);
181   }
182 
183   @GuardedBy("this")
incrementSequenceNumber()184   private void incrementSequenceNumber() throws GeneralSecurityException {
185     if (sequenceNumber.compareTo(maxSequenceNumber) >= 0) {
186       throw new GeneralSecurityException("message limit reached");
187     }
188     sequenceNumber = sequenceNumber.add(BigInteger.ONE);
189   }
190 
191   /** ComputeNonce() from https://www.rfc-editor.org/rfc/rfc9180.html#section-5.2-11. */
192   @GuardedBy("this")
computeNonce()193   private byte[] computeNonce() throws GeneralSecurityException {
194     return Bytes.xor(
195         baseNonce,
196         BigIntegerEncoding.toBigEndianBytesOfFixedLength(sequenceNumber, aead.getNonceLength()));
197   }
198 
199   /** Returns the next nonce to use for seal/open. Also, increments the sequence number. */
computeNonceAndIncrementSequenceNumber()200   private synchronized byte[] computeNonceAndIncrementSequenceNumber()
201       throws GeneralSecurityException {
202     byte[] nonce = computeNonce();
203     incrementSequenceNumber();
204     return nonce;
205   }
206 
getKey()207   byte[] getKey() {
208     return key;
209   }
210 
getBaseNonce()211   byte[] getBaseNonce() {
212     return baseNonce;
213   }
214 
getEncapsulatedKey()215   byte[] getEncapsulatedKey() {
216     return encapsulatedKey;
217   }
218 
219   /**
220    * Performs AEAD encryption of {@code plaintext} with {@code associatedData} according to
221    * ContextS.Seal() defined in https://www.rfc-editor.org/rfc/rfc9180.html#section-5.2-8.
222    *
223    * @return ciphertext
224    */
seal(byte[] plaintext, byte[] associatedData)225   byte[] seal(byte[] plaintext, byte[] associatedData) throws GeneralSecurityException {
226     byte[] nonce = computeNonceAndIncrementSequenceNumber();
227     return aead.seal(key, nonce, plaintext, associatedData);
228   }
229 
230   /**
231    * Performs AEAD decryption of {@code ciphertext} with {@code associatedData} according to
232    * ContextR.Open() defined in https://www.rfc-editor.org/rfc/rfc9180.html#section-5.2-10.
233    *
234    * @return plaintext
235    */
open(byte[] ciphertext, byte[] associatedData)236   byte[] open(byte[] ciphertext, byte[] associatedData) throws GeneralSecurityException {
237     byte[] nonce = computeNonceAndIncrementSequenceNumber();
238     return aead.open(key, nonce, ciphertext, associatedData);
239   }
240 }
241