xref: /aosp_15_r20/external/tink/java_src/src/main/java/com/google/crypto/tink/integration/gcpkms/FakeCloudKms.java (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
1 // Copyright 2022 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.integration.gcpkms;
17 
18 import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
19 import com.google.api.client.http.HttpTransport;
20 import com.google.api.client.http.LowLevelHttpRequest;
21 import com.google.api.client.json.gson.GsonFactory;
22 import com.google.api.services.cloudkms.v1.CloudKMS;
23 import com.google.api.services.cloudkms.v1.model.DecryptRequest;
24 import com.google.api.services.cloudkms.v1.model.DecryptResponse;
25 import com.google.api.services.cloudkms.v1.model.EncryptRequest;
26 import com.google.api.services.cloudkms.v1.model.EncryptResponse;
27 import com.google.crypto.tink.Aead;
28 import com.google.crypto.tink.KeyTemplates;
29 import com.google.crypto.tink.KeysetHandle;
30 import java.io.IOException;
31 import java.security.GeneralSecurityException;
32 import java.util.HashMap;
33 import java.util.List;
34 import java.util.Map;
35 
36 /**
37  * A partial, fake implementation of {@link com.google.api.services.cloudkms.v1.CloudKMS}.
38  *
39  * <p>It creates a new AEAD for every valid key ID. CryptoKeys use them to encrypt and decrypt.
40  */
41 final class FakeCloudKms extends CloudKMS {
42   private final Map<String, Aead> aeads = new HashMap<>();
43 
FakeCloudKms(List<String> validKeyIds)44   public FakeCloudKms(List<String> validKeyIds)
45       throws GeneralSecurityException {
46     super(
47         new HttpTransport() {
48           @Override
49           protected LowLevelHttpRequest buildRequest(String method, String url) throws IOException {
50             throw new IOException("Test should not have interacted with HttpTransport.");
51           }
52         },
53         new GsonFactory(),
54         new GoogleCredential());
55     for (String keyId : validKeyIds) {
56       Aead aead = KeysetHandle.generateNew(KeyTemplates.get("AES128_GCM")).getPrimitive(Aead.class);
57       aeads.put(keyId, aead);
58     }
59   }
60 
61   private final Projects projects = new Projects();
62 
63   @Override
projects()64   public Projects projects() {
65     return projects;
66   }
67 
68   final class Projects extends CloudKMS.Projects {
69 
70     private final Locations locations = new Locations();
71 
72     @Override
locations()73     public Locations locations() {
74       return locations;
75     }
76 
77     final class Locations extends CloudKMS.Projects.Locations {
78 
79       private final KeyRings keyRings = new KeyRings();
80 
81       @Override
keyRings()82       public KeyRings keyRings() {
83         return keyRings;
84       }
85 
86       final class KeyRings extends CloudKMS.Projects.Locations.KeyRings {
87 
88         private final CryptoKeys cryptoKeys = new CryptoKeys();
89 
90         @Override
cryptoKeys()91         public CryptoKeys cryptoKeys() {
92           return cryptoKeys;
93         }
94 
95         final class CryptoKeys extends CloudKMS.Projects.Locations.KeyRings.CryptoKeys {
96           @Override
encrypt(String name, EncryptRequest request)97           public Encrypt encrypt(String name, EncryptRequest request) {
98             return new Encrypt(name, request);
99           }
100 
101           @Override
decrypt(String name, DecryptRequest request)102           public Decrypt decrypt(String name, DecryptRequest request) {
103             return new Decrypt(name, request);
104           }
105 
106           final class Encrypt extends CloudKMS.Projects.Locations.KeyRings.CryptoKeys.Encrypt {
107             String name;
108             EncryptRequest request;
109 
Encrypt(String name, EncryptRequest request)110             Encrypt(String name, EncryptRequest request) {
111               super(name, request);
112               this.name = name;
113               this.request = request;
114             }
115 
116             @Override
execute()117             public EncryptResponse execute() throws IOException {
118               if (!aeads.containsKey(name)) {
119                 throw new IOException(
120                     "Unknown key ID : " + name + " is not in " + aeads.keySet());
121               }
122               try {
123                 Aead aead = aeads.get(name);
124                 byte[] ciphertext =
125                     aead.encrypt(
126                         request.decodePlaintext(), request.decodeAdditionalAuthenticatedData());
127                 return new EncryptResponse().encodeCiphertext(ciphertext);
128               } catch (GeneralSecurityException e) {
129                 throw new IOException(e.getMessage());
130               }
131             }
132           }
133 
134           final class Decrypt extends CloudKMS.Projects.Locations.KeyRings.CryptoKeys.Decrypt {
135             String name;
136             DecryptRequest request;
137 
Decrypt(String name, DecryptRequest request)138             Decrypt(String name, DecryptRequest request) {
139               super(name, request);
140               this.name = name;
141               this.request = request;
142             }
143 
144             @Override
execute()145             public DecryptResponse execute() throws IOException {
146               if (!aeads.containsKey(name)) {
147                 throw new IOException("Unknown key ID : " + name + " is not in " + aeads.keySet());
148               }
149               try {
150                 Aead aead = aeads.get(name);
151                 byte[] plaintext =
152                     aead.decrypt(
153                         request.decodeCiphertext(), request.decodeAdditionalAuthenticatedData());
154                 if (plaintext.length == 0) {
155                   // The real CloudKMS also returns null in this case.
156                   return new DecryptResponse().encodePlaintext(null);
157                 } else {
158                   return new DecryptResponse().encodePlaintext(plaintext);
159                 }
160               } catch (GeneralSecurityException e) {
161                 throw new IOException(e.getMessage());
162               }
163             }
164           }
165         }
166       }
167     }
168   }
169 }
170