• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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