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