Skip to content

Commit 1bbd355

Browse files
committed
Use AES key per stream
Switched to use AES key for each stream which brings key auto-rotation Signed-off-by: Andrey Pleskach <[email protected]>
1 parent 65b8297 commit 1bbd355

File tree

8 files changed

+53
-75
lines changed

8 files changed

+53
-75
lines changed

src/main/java/org/opensearch/repository/encrypted/EncryptedRepository.java

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,6 @@ public class EncryptedRepository extends BlobStoreRepository {
6060

6161
private final EncryptedRepositorySettings encryptedRepositorySettings;
6262

63-
private final Cache<String, EncryptionData> encryptionDataCache;
64-
6563
private final EncryptionDataGenerator encryptionDataGenerator;
6664

6765
public EncryptedRepository(final RepositoryMetadata metadata,
@@ -71,27 +69,11 @@ public EncryptedRepository(final RepositoryMetadata metadata,
7169
final NamedXContentRegistry namedXContentRegistry,
7270
final ClusterService clusterService,
7371
final RecoverySettings recoverySettings) {
74-
this(metadata, encryptedRepositorySettings,
75-
blobStorageRepositoryType, blobStorageRepository,
76-
namedXContentRegistry, clusterService,
77-
CacheBuilder.<String, EncryptionData>builder().build(),
78-
recoverySettings);
79-
}
80-
81-
public EncryptedRepository(final RepositoryMetadata metadata,
82-
final EncryptedRepositorySettings encryptedRepositorySettings,
83-
final String blobStorageRepositoryType,
84-
final BlobStoreRepository blobStorageRepository,
85-
final NamedXContentRegistry namedXContentRegistry,
86-
final ClusterService clusterService,
87-
final Cache<String, EncryptionData> encryptionDataCache,
88-
final RecoverySettings recoverySettings) {
8972
super(metadata, COMPRESS_SETTING.get(metadata.settings()),
9073
namedXContentRegistry, clusterService, recoverySettings);
9174
this.encryptedRepositorySettings = encryptedRepositorySettings;
9275
this.blobStorageRepositoryType = blobStorageRepositoryType;
9376
this.blobStorageRepository = blobStorageRepository;
94-
this.encryptionDataCache = encryptionDataCache;
9577
this.encryptionDataGenerator = new EncryptionDataGenerator();
9678
}
9779

@@ -119,14 +101,12 @@ protected void doStart() {
119101
@Override
120102
protected void doStop() {
121103
super.doStop();
122-
encryptionDataCache.invalidateAll();
123104
blobStorageRepository.stop();
124105
}
125106

126107
@Override
127108
protected void doClose() {
128109
super.doClose();
129-
encryptionDataCache.invalidateAll();
130110
blobStorageRepository.close();
131111
}
132112

@@ -135,10 +115,8 @@ protected BlobStore createBlobStore() throws Exception {
135115
return new EncryptedBlobStore(
136116
blobStorageRepository.blobStore(),
137117
new CryptoIO(
138-
encryptionDataCache.computeIfAbsent(
139-
settingsKey(metadata.settings()),
140-
this::createOrRestoreEncryptionData
141-
)
118+
new EncryptionDataSerializer(encryptedRepositorySettings.rsaKeyPair(settingsKey(metadata.settings()))),
119+
encryptionDataGenerator
142120
)
143121
);
144122
}

src/main/java/org/opensearch/repository/encrypted/EncryptedRepositorySettings.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
import java.util.Map;
2828
import java.util.Set;
2929

30-
class EncryptedRepositorySettings {
30+
public class EncryptedRepositorySettings {
3131

3232
private static final Logger LOGGER = LogManager.getLogger(EncryptedRepositorySettings.class);
3333

src/main/java/org/opensearch/repository/encrypted/security/CryptoIO.java

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99

1010
import javax.crypto.Cipher;
1111
import javax.crypto.CipherInputStream;
12-
import javax.crypto.SecretKey;
1312
import javax.crypto.spec.GCMParameterSpec;
1413
import java.io.BufferedInputStream;
1514
import java.io.ByteArrayInputStream;
@@ -26,34 +25,29 @@ public class CryptoIO implements Encryptor, Decryptor {
2625

2726
public static final int GCM_ENCRYPTED_BLOCK_LENGTH = 128;
2827

29-
public static final int GCM_IV_LENGTH = 12;
30-
3128
public static final String CIPHER_TRANSFORMATION = "AES/GCM/NoPadding";
3229

33-
private final SecretKey secretKey;
34-
35-
private final byte[] aad;
30+
private final EncryptionDataSerializer encryptionDataSerializer;
3631

37-
private final SecureRandom secureRandom;
32+
private final EncryptionDataGenerator encryptionDataGenerator;
3833

39-
public CryptoIO(final EncryptionData encryptionData) {
40-
this.secretKey = encryptionData.encryptionKey();
41-
this.aad = encryptionData.aad();
42-
this.secureRandom = new SecureRandom();
34+
public CryptoIO(final EncryptionDataSerializer encryptionDataSerializer,
35+
final EncryptionDataGenerator encryptionDataGenerator) {
36+
this.encryptionDataSerializer = encryptionDataSerializer;
37+
this.encryptionDataGenerator = encryptionDataGenerator;
4338
}
4439

4540
public InputStream encrypt(final InputStream in) throws IOException {
4641
return Permissions.doPrivileged(() -> {
47-
final byte[] iv = new byte[GCM_IV_LENGTH];
48-
secureRandom.nextBytes(iv);
42+
final var encryptionData = encryptionDataGenerator.generate();
4943
final Cipher cipher = createEncryptingCipher(
50-
secretKey,
51-
new GCMParameterSpec(GCM_ENCRYPTED_BLOCK_LENGTH, iv),
44+
encryptionData.encryptionKey(),
45+
new GCMParameterSpec(GCM_ENCRYPTED_BLOCK_LENGTH, encryptionData.iv()),
5246
CIPHER_TRANSFORMATION);
53-
cipher.updateAAD(aad);
47+
cipher.updateAAD(encryptionData.aad());
5448
return new BufferedInputStream(
5549
new SequenceInputStream(
56-
new ByteArrayInputStream(iv),
50+
new ByteArrayInputStream(encryptionDataSerializer.serialize(encryptionData)),
5751
new CipherInputStream(in, cipher)
5852
), BUFFER_SIZE
5953
);
@@ -62,17 +56,18 @@ public InputStream encrypt(final InputStream in) throws IOException {
6256

6357
public InputStream decrypt(final InputStream in) throws IOException {
6458
return Permissions.doPrivileged(() -> {
59+
final var encryptionData = encryptionDataSerializer.deserialize(in.readNBytes(EncryptionDataSerializer.ENC_DATA_SIZE));
6560
final Cipher cipher = createDecryptingCipher(
66-
secretKey,
67-
new GCMParameterSpec(GCM_ENCRYPTED_BLOCK_LENGTH, in.readNBytes(GCM_IV_LENGTH)),
61+
encryptionData.encryptionKey(),
62+
new GCMParameterSpec(GCM_ENCRYPTED_BLOCK_LENGTH, encryptionData.iv()),
6863
CIPHER_TRANSFORMATION);
69-
cipher.updateAAD(aad);
64+
cipher.updateAAD(encryptionData.aad());
7065
return new BufferedInputStream(new CipherInputStream(in, cipher), BUFFER_SIZE);
7166
});
7267
}
7368

7469
public long encryptedStreamSize(final long originSize) {
75-
return originSize + GCM_TAG_LENGTH + GCM_IV_LENGTH;
70+
return originSize + GCM_TAG_LENGTH + EncryptionDataSerializer.ENC_DATA_SIZE;
7671
}
7772

7873
}

src/main/java/org/opensearch/repository/encrypted/security/EncryptionData.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@ public final class EncryptionData {
1515

1616
private final byte[] aad;
1717

18-
public EncryptionData(final SecretKey encryptionKey, final byte[] aad) {
18+
private final byte[] iv;
19+
20+
public EncryptionData(final SecretKey encryptionKey, final byte[] aad, final byte[] iv) {
1921
this.encryptionKey = encryptionKey;
2022
this.aad = aad;
23+
this.iv = iv;
2124
}
2225

2326
public SecretKey encryptionKey() {
@@ -28,18 +31,23 @@ public byte[] aad() {
2831
return aad;
2932
}
3033

34+
public byte[] iv() {
35+
return iv;
36+
}
37+
3138
@Override
3239
public boolean equals(Object o) {
3340
if (this == o) return true;
3441
if (o == null || getClass() != o.getClass()) return false;
35-
final EncryptionData that = (EncryptionData) o;
36-
return Objects.equals(encryptionKey, that.encryptionKey) && Arrays.equals(aad, that.aad);
42+
EncryptionData that = (EncryptionData) o;
43+
return Objects.equals(encryptionKey, that.encryptionKey) && Arrays.equals(aad, that.aad) && Arrays.equals(iv, that.iv);
3744
}
3845

3946
@Override
4047
public int hashCode() {
4148
int result = Objects.hash(encryptionKey);
4249
result = 31 * result + Arrays.hashCode(aad);
50+
result = 31 * result + Arrays.hashCode(iv);
4351
return result;
4452
}
4553
}

src/main/java/org/opensearch/repository/encrypted/security/EncryptionDataGenerator.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,15 @@ public final class EncryptionDataGenerator {
1515

1616
private static final int AAD_SIZE = 32;
1717

18+
public static final int GCM_IV_LENGTH = 12;
19+
1820
private final KeyGenerator aesKeyGenerator;
1921

20-
private final SecureRandom random;
22+
private final SecureRandom random = new SecureRandom();
2123

2224
public EncryptionDataGenerator() {
2325
try {
2426
this.aesKeyGenerator = KeyGenerator.getInstance("AES");
25-
this.random = new SecureRandom();
2627
this.aesKeyGenerator.init(KEY_SIZE, this.random);
2728
} catch (NoSuchAlgorithmException e) {
2829
throw new RuntimeException("Couldn't create AES key generator", e);
@@ -31,8 +32,10 @@ public EncryptionDataGenerator() {
3132

3233
public EncryptionData generate() {
3334
final byte[] aad = new byte[AAD_SIZE];
35+
final byte[] iv = new byte[GCM_IV_LENGTH];
3436
random.nextBytes(aad);
35-
return new EncryptionData(aesKeyGenerator.generateKey(), aad);
37+
random.nextBytes(iv);
38+
return new EncryptionData(aesKeyGenerator.generateKey(), aad, iv);
3639
}
3740

3841
}

src/main/java/org/opensearch/repository/encrypted/security/EncryptionDataSerializer.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public class EncryptionDataSerializer implements Encryptor, Decryptor {
3939

4040
public static final int ENCRYPTED_AAD_SIZE = 256;
4141

42-
public static final int ENC_DATA_SIZE = ENCRYPTED_KEY_SIZE + ENCRYPTED_AAD_SIZE + SIGNATURE_SIZE + Integer.BYTES;
42+
public static final int ENC_DATA_SIZE = EncryptionDataGenerator.GCM_IV_LENGTH + ENCRYPTED_KEY_SIZE + ENCRYPTED_AAD_SIZE + SIGNATURE_SIZE + Integer.BYTES;
4343

4444
public EncryptionDataSerializer(final KeyPair rsaKeyPair) {
4545
this.rsaKeyPair = rsaKeyPair;
@@ -52,15 +52,18 @@ public byte[] serialize(final EncryptionData encryptionData) throws IOException
5252
}
5353
final byte[] key = encryptionData.encryptionKey().getEncoded();
5454
final byte[] aad = encryptionData.aad();
55+
final byte[] iv = encryptionData.iv();
5556
final byte[] signature = sign(
56-
ByteBuffer.allocate(key.length + aad.length)
57+
ByteBuffer.allocate(key.length + aad.length + iv.length)
5758
.put(key)
5859
.put(aad)
60+
.put(iv)
5961
.array()
6062
);
6163
final byte[] encryptedKey = encrypt(key, "Couldn't encrypt " + KEY_ALGORITHM + " key");
6264
final byte[] encryptedAad = encrypt(aad, "Couldn't encrypt AAD");
6365
return ByteBuffer.allocate(ENC_DATA_SIZE)
66+
.put(iv)
6467
.put(encryptedKey)
6568
.put(encryptedAad)
6669
.put(signature)
@@ -75,6 +78,8 @@ public EncryptionData deserialize(final byte[] metadata) throws IOException {
7578
final byte[] encryptedKey = new byte[256];
7679
final byte[] encryptedAad = new byte[256];
7780
final byte[] signature = new byte[256];
81+
final byte[] iv = new byte[EncryptionDataGenerator.GCM_IV_LENGTH];
82+
buffer.get(iv);
7883
buffer.get(encryptedKey);
7984
buffer.get(encryptedAad);
8085
buffer.get(signature);
@@ -83,12 +88,13 @@ public EncryptionData deserialize(final byte[] metadata) throws IOException {
8388
final byte[] decryptedAdd = decrypt(encryptedAad, "Couldn't decrypt AAD");
8489
verifySignature(
8590
signature,
86-
ByteBuffer.allocate(decryptedKey.length + decryptedAdd.length)
91+
ByteBuffer.allocate(decryptedKey.length + decryptedAdd.length + iv.length)
8792
.put(decryptedKey)
8893
.put(decryptedAdd)
94+
.put(iv)
8995
.array()
9096
);
91-
return new EncryptionData(new SecretKeySpec(decryptedKey, KEY_ALGORITHM), decryptedAdd);
97+
return new EncryptionData(new SecretKeySpec(decryptedKey, KEY_ALGORITHM), decryptedAdd, iv);
9298
});
9399
}
94100

src/test/java/org/opensearch/repository/encrypted/EncryptedRepositoryTests.java

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,13 @@
99
import org.opensearch.cluster.metadata.RepositoryMetadata;
1010
import org.opensearch.cluster.service.ClusterApplierService;
1111
import org.opensearch.cluster.service.ClusterService;
12-
import org.opensearch.common.cache.Cache;
1312
import org.opensearch.common.settings.Settings;
1413
import org.opensearch.core.xcontent.NamedXContentRegistry;
1514
import org.opensearch.indices.recovery.RecoverySettings;
1615
import org.opensearch.repositories.blobstore.BlobStoreRepository;
17-
import org.opensearch.repository.encrypted.security.EncryptionData;
1816
import org.opensearch.test.OpenSearchTestCase;
1917

2018
import static org.mockito.Mockito.mock;
21-
import static org.mockito.Mockito.reset;
2219
import static org.mockito.Mockito.verify;
2320
import static org.mockito.Mockito.when;
2421

@@ -35,9 +32,6 @@ public class EncryptedRepositoryTests extends OpenSearchTestCase {
3532

3633
final RecoverySettings mockedRecoverySettings = mock(RecoverySettings.class);
3734

38-
final Cache<String, EncryptionData> mockedCache =
39-
(Cache<String, EncryptionData>) mock(Cache.class);
40-
4135
@Before
4236
public void setupMocks() throws Exception {
4337
when(mockedClusterService.getClusterApplierService()).thenReturn(mock(ClusterApplierService.class));
@@ -52,21 +46,16 @@ public void testBlobStorageLifecycle() throws Exception {
5246
mockedBlobStoreRepository,
5347
mockedNamedXContentRegistry,
5448
mockedClusterService,
55-
mockedCache,
5649
mockedRecoverySettings);
5750

5851
repository.start();
5952
verify(mockedBlobStoreRepository).start();
6053

6154
repository.stop();
6255
verify(mockedBlobStoreRepository).stop();
63-
verify(mockedCache).invalidateAll();
64-
65-
reset(mockedCache);
6656

6757
repository.close();
6858
verify(mockedBlobStoreRepository).close();
69-
verify(mockedCache).invalidateAll();
7059

7160
repository.stats();
7261
verify(mockedBlobStoreRepository).stats();

src/test/java/org/opensearch/repository/encrypted/security/CryptoIOTests.java

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,26 @@
77

88
import org.bouncycastle.jce.provider.BouncyCastleProvider;
99
import org.junit.BeforeClass;
10+
import org.opensearch.repository.encrypted.RsaKeyAwareTest;
1011
import org.opensearch.test.OpenSearchTestCase;
1112

1213
import java.io.ByteArrayInputStream;
1314
import java.io.IOException;
1415
import java.io.InputStream;
1516
import java.security.Security;
1617

17-
public class CryptoIOTests extends OpenSearchTestCase {
18+
public class CryptoIOTests extends RsaKeyAwareTest {
1819

19-
private static final int MAX_BYES_SIZE = 18_192;
20+
private static final int MAX_BYES_SIZE = 1_800_192;
2021

21-
private final EncryptionData encData = new EncryptionDataGenerator().generate();
22+
private final EncryptionDataGenerator encryptionDataGenerator = new EncryptionDataGenerator();
2223

23-
@BeforeClass
24-
static void setupProvider() {
24+
static {
2525
Security.addProvider(new BouncyCastleProvider());
2626
}
2727

2828
public void testEncryptAndDecrypt() throws IOException {
29-
final CryptoIO cryptoIo = new CryptoIO(encData);
29+
final CryptoIO cryptoIo = new CryptoIO(new EncryptionDataSerializer(rsaKeyPair), encryptionDataGenerator);
3030
final byte [] sequence = randomByteArrayOfLength(randomInt(MAX_BYES_SIZE));
3131

3232
try (InputStream encIn = cryptoIo.encrypt(new ByteArrayInputStream(sequence))) {
@@ -35,11 +35,10 @@ public void testEncryptAndDecrypt() throws IOException {
3535
assertArrayEquals(sequence, decIn.readAllBytes());
3636
}
3737
}
38-
3938
}
4039

4140
public void testEncryptedStreamSize() throws IOException {
42-
final CryptoIO cryptoIo = new CryptoIO(encData);
41+
final CryptoIO cryptoIo = new CryptoIO(new EncryptionDataSerializer(rsaKeyPair), encryptionDataGenerator);
4342
final byte [] sequence = randomByteArrayOfLength(randomInt(MAX_BYES_SIZE));
4443

4544
try (InputStream encIn = cryptoIo.encrypt(new ByteArrayInputStream(sequence))) {

0 commit comments

Comments
 (0)