• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.nearby.fastpair.provider.crypto;
18 
19 import static com.google.common.base.Preconditions.checkArgument;
20 import static com.google.common.base.Preconditions.checkState;
21 
22 import android.annotation.SuppressLint;
23 
24 import com.google.common.collect.ImmutableSet;
25 import com.google.protobuf.ByteString;
26 
27 import java.nio.ByteBuffer;
28 import java.security.GeneralSecurityException;
29 
30 import javax.crypto.Cipher;
31 import javax.crypto.spec.SecretKeySpec;
32 
33 /** Cryptography utilities for ephemeral IDs. */
34 public final class Crypto {
35     private static final int AES_BLOCK_SIZE = 16;
36     private static final ImmutableSet<Integer> VALID_AES_KEY_SIZES = ImmutableSet.of(16, 24, 32);
37     private static final String AES_ECB_NOPADDING_ENCRYPTION_ALGO = "AES/ECB/NoPadding";
38     private static final String AES_ENCRYPTION_ALGO = "AES";
39 
40     /** Encrypts the provided data with the provided key using the AES/ECB/NoPadding algorithm. */
aesEcbNoPaddingEncrypt(ByteString key, ByteString data)41     public static ByteString aesEcbNoPaddingEncrypt(ByteString key, ByteString data) {
42         return aesEcbOperation(key, data, Cipher.ENCRYPT_MODE);
43     }
44 
45     /** Decrypts the provided data with the provided key using the AES/ECB/NoPadding algorithm. */
aesEcbNoPaddingDecrypt(ByteString key, ByteString data)46     public static ByteString aesEcbNoPaddingDecrypt(ByteString key, ByteString data) {
47         return aesEcbOperation(key, data, Cipher.DECRYPT_MODE);
48     }
49 
50     @SuppressLint("GetInstance")
aesEcbOperation(ByteString key, ByteString data, int operation)51     private static ByteString aesEcbOperation(ByteString key, ByteString data, int operation) {
52         checkArgument(VALID_AES_KEY_SIZES.contains(key.size()));
53         checkArgument(data.size() % AES_BLOCK_SIZE == 0);
54         try {
55             Cipher aesCipher = Cipher.getInstance(AES_ECB_NOPADDING_ENCRYPTION_ALGO);
56             SecretKeySpec secretKeySpec = new SecretKeySpec(key.toByteArray(), AES_ENCRYPTION_ALGO);
57             aesCipher.init(operation, secretKeySpec);
58             ByteBuffer output = ByteBuffer.allocate(data.size());
59             checkState(aesCipher.doFinal(data.asReadOnlyByteBuffer(), output) == data.size());
60             output.rewind();
61             return ByteString.copyFrom(output);
62         } catch (GeneralSecurityException e) {
63             // Should never happen.
64             throw new AssertionError(e);
65         }
66     }
67 
Crypto()68     private Crypto() {
69     }
70 }
71