1 // Copyright 2017 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 package com.google.crypto.tink.hybrid; 17 18 import com.google.crypto.tink.CryptoFormat; 19 import com.google.crypto.tink.HybridDecrypt; 20 import com.google.crypto.tink.hybrid.internal.LegacyFullHybridDecrypt; 21 import com.google.crypto.tink.internal.LegacyProtoKey; 22 import com.google.crypto.tink.internal.MonitoringClient; 23 import com.google.crypto.tink.internal.MonitoringKeysetInfo; 24 import com.google.crypto.tink.internal.MonitoringUtil; 25 import com.google.crypto.tink.internal.MutableMonitoringRegistry; 26 import com.google.crypto.tink.internal.MutablePrimitiveRegistry; 27 import com.google.crypto.tink.internal.PrimitiveConstructor; 28 import com.google.crypto.tink.internal.PrimitiveRegistry; 29 import com.google.crypto.tink.internal.PrimitiveSet; 30 import com.google.crypto.tink.internal.PrimitiveWrapper; 31 import java.security.GeneralSecurityException; 32 import java.util.Arrays; 33 import java.util.List; 34 35 /** 36 * The implementation of {@code PrimitiveWrapper<HybridDecrypt>}. 37 * 38 * <p>The returned primitive works with a keyset (rather than a single key). To decrypt, the 39 * primitive uses the prefix of the ciphertext to efficiently select the right key in the set. If 40 * the keys associated with the prefix do not work, the primitive tries all keys with {@link 41 * com.google.crypto.tink.proto.OutputPrefixType#RAW}. 42 */ 43 public class HybridDecryptWrapper implements PrimitiveWrapper<HybridDecrypt, HybridDecrypt> { 44 45 private static final HybridDecryptWrapper WRAPPER = new HybridDecryptWrapper(); 46 private static final PrimitiveConstructor<LegacyProtoKey, HybridDecrypt> 47 LEGACY_PRIMITIVE_CONSTRUCTOR = 48 PrimitiveConstructor.create( 49 LegacyFullHybridDecrypt::create, LegacyProtoKey.class, HybridDecrypt.class); 50 51 private static class WrappedHybridDecrypt implements HybridDecrypt { 52 private final PrimitiveSet<HybridDecrypt> primitives; 53 54 private final MonitoringClient.Logger decLogger; 55 WrappedHybridDecrypt(final PrimitiveSet<HybridDecrypt> primitives)56 public WrappedHybridDecrypt(final PrimitiveSet<HybridDecrypt> primitives) { 57 this.primitives = primitives; 58 if (primitives.hasAnnotations()) { 59 MonitoringClient client = MutableMonitoringRegistry.globalInstance().getMonitoringClient(); 60 MonitoringKeysetInfo keysetInfo = MonitoringUtil.getMonitoringKeysetInfo(primitives); 61 this.decLogger = client.createLogger(keysetInfo, "hybrid_decrypt", "decrypt"); 62 } else { 63 this.decLogger = MonitoringUtil.DO_NOTHING_LOGGER; 64 } 65 } 66 67 @Override decrypt(final byte[] ciphertext, final byte[] contextInfo)68 public byte[] decrypt(final byte[] ciphertext, final byte[] contextInfo) 69 throws GeneralSecurityException { 70 if (ciphertext.length > CryptoFormat.NON_RAW_PREFIX_SIZE) { 71 byte[] prefix = Arrays.copyOfRange(ciphertext, 0, CryptoFormat.NON_RAW_PREFIX_SIZE); 72 List<PrimitiveSet.Entry<HybridDecrypt>> entries = primitives.getPrimitive(prefix); 73 for (PrimitiveSet.Entry<HybridDecrypt> entry : entries) { 74 try { 75 byte[] output = entry.getFullPrimitive().decrypt(ciphertext, contextInfo); 76 decLogger.log(entry.getKeyId(), ciphertext.length); 77 return output; 78 } catch (GeneralSecurityException e) { 79 continue; 80 } 81 } 82 } 83 // Let's try all RAW keys. 84 List<PrimitiveSet.Entry<HybridDecrypt>> entries = primitives.getRawPrimitives(); 85 for (PrimitiveSet.Entry<HybridDecrypt> entry : entries) { 86 try { 87 byte[] output = entry.getFullPrimitive().decrypt(ciphertext, contextInfo); 88 decLogger.log(entry.getKeyId(), ciphertext.length); 89 return output; 90 } catch (GeneralSecurityException e) { 91 continue; 92 } 93 } 94 // nothing works. 95 decLogger.logFailure(); 96 throw new GeneralSecurityException("decryption failed"); 97 } 98 } 99 HybridDecryptWrapper()100 HybridDecryptWrapper() {} 101 102 @Override wrap(final PrimitiveSet<HybridDecrypt> primitives)103 public HybridDecrypt wrap(final PrimitiveSet<HybridDecrypt> primitives) { 104 return new WrappedHybridDecrypt(primitives); 105 } 106 107 @Override getPrimitiveClass()108 public Class<HybridDecrypt> getPrimitiveClass() { 109 return HybridDecrypt.class; 110 } 111 112 @Override getInputPrimitiveClass()113 public Class<HybridDecrypt> getInputPrimitiveClass() { 114 return HybridDecrypt.class; 115 } 116 117 /** 118 * Register the wrapper within the registry. 119 * 120 * <p>This is required for calls to {@link Keyset.getPrimitive} with a {@link HybridDecrypt} 121 * argument. 122 */ register()123 public static void register() throws GeneralSecurityException { 124 MutablePrimitiveRegistry.globalInstance().registerPrimitiveWrapper(WRAPPER); 125 MutablePrimitiveRegistry.globalInstance() 126 .registerPrimitiveConstructor(LEGACY_PRIMITIVE_CONSTRUCTOR); 127 } 128 129 /** 130 * registerToInternalPrimitiveRegistry is a non-public method (it takes an argument of an 131 * internal-only type) registering an instance of {@code HybridDecryptWrapper} to the provided 132 * {@code PrimitiveRegistry.Builder}. 133 */ registerToInternalPrimitiveRegistry( PrimitiveRegistry.Builder primitiveRegistryBuilder)134 public static void registerToInternalPrimitiveRegistry( 135 PrimitiveRegistry.Builder primitiveRegistryBuilder) throws GeneralSecurityException { 136 primitiveRegistryBuilder.registerPrimitiveWrapper(WRAPPER); 137 } 138 } 139