xref: /aosp_15_r20/external/tink/java_src/src/main/java/com/google/crypto/tink/hybrid/internal/HpkeDecrypt.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.HybridDecrypt;
20 import com.google.crypto.tink.proto.HpkeParams;
21 import com.google.crypto.tink.proto.HpkePrivateKey;
22 import com.google.errorprone.annotations.Immutable;
23 import java.security.GeneralSecurityException;
24 import java.util.Arrays;
25 
26 /**
27  * Hybrid Public Key Encryption (HPKE) decryption.
28  *
29  * <p>HPKE RFC: https://www.rfc-editor.org/rfc/rfc9180.html
30  */
31 @Immutable
32 final class HpkeDecrypt implements HybridDecrypt {
33   private static final byte[] EMPTY_ASSOCIATED_DATA = new byte[0];
34 
35   private final HpkeKemPrivateKey recipientPrivateKey;
36   private final HpkeKem kem;
37   private final HpkeKdf kdf;
38   private final HpkeAead aead;
39   private final int encapsulatedKeyLength;
40 
HpkeDecrypt( HpkeKemPrivateKey recipientPrivateKey, HpkeKem kem, HpkeKdf kdf, HpkeAead aead, int encapsulatedKeyLength)41   private HpkeDecrypt(
42       HpkeKemPrivateKey recipientPrivateKey,
43       HpkeKem kem,
44       HpkeKdf kdf,
45       HpkeAead aead,
46       int encapsulatedKeyLength) {
47     this.recipientPrivateKey = recipientPrivateKey;
48     this.kem = kem;
49     this.kdf = kdf;
50     this.aead = aead;
51     this.encapsulatedKeyLength = encapsulatedKeyLength;
52   }
53 
54   /**
55    * Returns the encapsulated key length (in bytes) for the specified {@code kemProtoEnum}. This
56    * value corresponds to the 'Nenc' column in the following table.
57    *
58    * <p>https://www.rfc-editor.org/rfc/rfc9180.html#name-key-encapsulation-mechanism.
59    */
encodingSizeInBytes(com.google.crypto.tink.proto.HpkeKem kemProtoEnum)60   private static int encodingSizeInBytes(com.google.crypto.tink.proto.HpkeKem kemProtoEnum) {
61     switch (kemProtoEnum) {
62       case DHKEM_X25519_HKDF_SHA256:
63         return 32;
64       case DHKEM_P256_HKDF_SHA256:
65         return 65;
66       case DHKEM_P384_HKDF_SHA384:
67         return 97;
68       case DHKEM_P521_HKDF_SHA512:
69         return 133;
70       default:
71         throw new IllegalArgumentException(
72             "Unable to determine KEM-encoding length for " + kemProtoEnum.name());
73     }
74   }
75 
76   /** Returns an HPKE decryption primitive created from {@code recipientPrivateKey} */
createHpkeDecrypt(HpkePrivateKey recipientPrivateKey)77   static HpkeDecrypt createHpkeDecrypt(HpkePrivateKey recipientPrivateKey)
78       throws GeneralSecurityException {
79     if (!recipientPrivateKey.hasPublicKey()) {
80       throw new IllegalArgumentException("HpkePrivateKey is missing public_key field.");
81     }
82     if (!recipientPrivateKey.getPublicKey().hasParams()) {
83       throw new IllegalArgumentException("HpkePrivateKey.public_key is missing params field.");
84     }
85     if (recipientPrivateKey.getPrivateKey().isEmpty()) {
86       throw new IllegalArgumentException("HpkePrivateKey.private_key is empty.");
87     }
88     HpkeParams params = recipientPrivateKey.getPublicKey().getParams();
89     HpkeKem kem = HpkePrimitiveFactory.createKem(params);
90     HpkeKdf kdf = HpkePrimitiveFactory.createKdf(params);
91     HpkeAead aead = HpkePrimitiveFactory.createAead(params);
92     int encapsulatedKeyLength = encodingSizeInBytes(params.getKem());
93     HpkeKemPrivateKey recipientKemPrivateKey = HpkeKemKeyFactory.createPrivate(recipientPrivateKey);
94     return new HpkeDecrypt(recipientKemPrivateKey, kem, kdf, aead, encapsulatedKeyLength);
95   }
96 
97   @Override
decrypt(final byte[] ciphertext, final byte[] contextInfo)98   public byte[] decrypt(final byte[] ciphertext, final byte[] contextInfo)
99       throws GeneralSecurityException {
100     if (ciphertext.length < encapsulatedKeyLength) {
101       throw new GeneralSecurityException("Ciphertext is too short.");
102     }
103     byte[] info = contextInfo;
104     if (info == null) {
105       info = new byte[0];
106     }
107     byte[] encapsulatedKey = Arrays.copyOf(ciphertext, encapsulatedKeyLength);
108     byte[] aeadCiphertext =
109         Arrays.copyOfRange(ciphertext, encapsulatedKeyLength, ciphertext.length);
110     HpkeContext context =
111         HpkeContext.createRecipientContext(
112             encapsulatedKey, recipientPrivateKey, kem, kdf, aead, info);
113     return context.open(aeadCiphertext, EMPTY_ASSOCIATED_DATA);
114   }
115 }
116