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 17 package com.google.crypto.tink.aead; 18 19 import com.google.crypto.tink.Aead; 20 import com.google.crypto.tink.CryptoFormat; 21 import com.google.crypto.tink.aead.internal.LegacyFullAead; 22 import com.google.crypto.tink.internal.LegacyProtoKey; 23 import com.google.crypto.tink.internal.MonitoringClient; 24 import com.google.crypto.tink.internal.MonitoringKeysetInfo; 25 import com.google.crypto.tink.internal.MonitoringUtil; 26 import com.google.crypto.tink.internal.MutableMonitoringRegistry; 27 import com.google.crypto.tink.internal.MutablePrimitiveRegistry; 28 import com.google.crypto.tink.internal.PrimitiveConstructor; 29 import com.google.crypto.tink.internal.PrimitiveRegistry; 30 import com.google.crypto.tink.internal.PrimitiveSet; 31 import com.google.crypto.tink.internal.PrimitiveWrapper; 32 import java.security.GeneralSecurityException; 33 import java.util.Arrays; 34 import java.util.List; 35 36 /** 37 * AeadWrapper is the implementation of SetWrapper for the Aead primitive. 38 * 39 * <p>Key rotation works as follows: each ciphertext is prefixed with the keyId. When decrypting, we 40 * first try all primitives whose keyId starts with the prefix of the ciphertext. If none of these 41 * succeed, we try the raw primitives. If any succeeds, we return the ciphertext, otherwise we 42 * simply throw a GeneralSecurityException. 43 */ 44 public class AeadWrapper implements PrimitiveWrapper<Aead, Aead> { 45 46 private static final AeadWrapper WRAPPER = new AeadWrapper(); 47 private static final PrimitiveConstructor<LegacyProtoKey, Aead> 48 LEGACY_FULL_AEAD_PRIMITIVE_CONSTRUCTOR = 49 PrimitiveConstructor.create(LegacyFullAead::create, LegacyProtoKey.class, Aead.class); 50 51 private static class WrappedAead implements Aead { 52 private final PrimitiveSet<Aead> pSet; 53 private final MonitoringClient.Logger encLogger; 54 private final MonitoringClient.Logger decLogger; 55 WrappedAead(PrimitiveSet<Aead> pSet)56 private WrappedAead(PrimitiveSet<Aead> pSet) { 57 this.pSet = pSet; 58 if (pSet.hasAnnotations()) { 59 MonitoringClient client = MutableMonitoringRegistry.globalInstance().getMonitoringClient(); 60 MonitoringKeysetInfo keysetInfo = MonitoringUtil.getMonitoringKeysetInfo(pSet); 61 this.encLogger = client.createLogger(keysetInfo, "aead", "encrypt"); 62 this.decLogger = client.createLogger(keysetInfo, "aead", "decrypt"); 63 } else { 64 this.encLogger = MonitoringUtil.DO_NOTHING_LOGGER; 65 this.decLogger = MonitoringUtil.DO_NOTHING_LOGGER; 66 } 67 } 68 69 @Override encrypt(final byte[] plaintext, final byte[] associatedData)70 public byte[] encrypt(final byte[] plaintext, final byte[] associatedData) 71 throws GeneralSecurityException { 72 try { 73 byte[] result = pSet.getPrimary().getFullPrimitive().encrypt(plaintext, associatedData); 74 encLogger.log(pSet.getPrimary().getKeyId(), plaintext.length); 75 return result; 76 } catch (GeneralSecurityException e) { 77 encLogger.logFailure(); 78 throw e; 79 } 80 } 81 82 @Override decrypt(final byte[] ciphertext, final byte[] associatedData)83 public byte[] decrypt(final byte[] ciphertext, final byte[] associatedData) 84 throws GeneralSecurityException { 85 if (ciphertext.length > CryptoFormat.NON_RAW_PREFIX_SIZE) { 86 byte[] prefix = Arrays.copyOf(ciphertext, CryptoFormat.NON_RAW_PREFIX_SIZE); 87 List<PrimitiveSet.Entry<Aead>> entries = pSet.getPrimitive(prefix); 88 for (PrimitiveSet.Entry<Aead> entry : entries) { 89 try { 90 byte[] result = entry.getFullPrimitive().decrypt(ciphertext, associatedData); 91 decLogger.log(entry.getKeyId(), ciphertext.length); 92 return result; 93 } catch (GeneralSecurityException ignored) { 94 // ignore and continue trying 95 } 96 } 97 } 98 99 // Let's try all RAW keys. 100 List<PrimitiveSet.Entry<Aead>> entries = pSet.getRawPrimitives(); 101 for (PrimitiveSet.Entry<Aead> entry : entries) { 102 try { 103 byte[] result = entry.getFullPrimitive().decrypt(ciphertext, associatedData); 104 decLogger.log(entry.getKeyId(), ciphertext.length); 105 return result; 106 } catch (GeneralSecurityException ignored) { 107 // ignore and continue trying 108 } 109 } 110 decLogger.logFailure(); 111 // nothing works. 112 throw new GeneralSecurityException("decryption failed"); 113 } 114 } 115 AeadWrapper()116 AeadWrapper() {} 117 118 @Override wrap(final PrimitiveSet<Aead> pset)119 public Aead wrap(final PrimitiveSet<Aead> pset) throws GeneralSecurityException { 120 return new WrappedAead(pset); 121 } 122 123 @Override getPrimitiveClass()124 public Class<Aead> getPrimitiveClass() { 125 return Aead.class; 126 } 127 128 @Override getInputPrimitiveClass()129 public Class<Aead> getInputPrimitiveClass() { 130 return Aead.class; 131 } 132 register()133 public static void register() throws GeneralSecurityException { 134 MutablePrimitiveRegistry.globalInstance().registerPrimitiveWrapper(WRAPPER); 135 MutablePrimitiveRegistry.globalInstance() 136 .registerPrimitiveConstructor(LEGACY_FULL_AEAD_PRIMITIVE_CONSTRUCTOR); 137 } 138 139 /** 140 * registerToInternalPrimitiveRegistry is a non-public method (it takes an argument of an 141 * internal-only type) registering an instance of {@code AeadWrapper} to the provided {@code 142 * PrimitiveRegistry.Builder}. 143 */ registerToInternalPrimitiveRegistry( PrimitiveRegistry.Builder primitiveRegistryBuilder)144 public static void registerToInternalPrimitiveRegistry( 145 PrimitiveRegistry.Builder primitiveRegistryBuilder) throws GeneralSecurityException { 146 primitiveRegistryBuilder.registerPrimitiveWrapper(WRAPPER); 147 } 148 } 149