Skip to content

Commit 4653838

Browse files
committed
[ACC-2372] Address PR remarks
1 parent 45a24e8 commit 4653838

File tree

6 files changed

+130
-99
lines changed

6 files changed

+130
-99
lines changed

contentgrid-appserver-contentstore-impl-encryption/src/main/java/com/contentgrid/appserver/contentstore/impl/encryption/engine/AlfrescoCompatibleEncryptionEngine.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public class AlfrescoCompatibleEncryptionEngine implements ContentEncryptionEngi
4141
public boolean supports(DataEncryptionAlgorithm algorithm) {
4242
boolean supported = true;
4343

44-
// alfresco-simple-content-stores supports arbitrary algorithms, though only symmetric onces
44+
// alfresco-simple-content-stores supports arbitrary algorithms, though only symmetric ones
4545
// check Java support of algorithm (which may include mode + padding)
4646
// and also check key algorithm for being symmetric
4747
String algorithmValue = algorithm.getValue();
@@ -101,8 +101,8 @@ private Cipher initializeCipher(EncryptionParameters parameters)
101101
// Same padding logic as in alfresco encrypted storage plugin
102102
if (MODES_AND_PADDINGS_BY_ALGORITHM.containsKey(algorithm)) {
103103
algorithm = algorithm + "/" + MODES_AND_PADDINGS_BY_ALGORITHM.get(algorithm);
104+
cipher = Cipher.getInstance(algorithm);
104105
}
105-
cipher = Cipher.getInstance(algorithm);
106106
// Always use zero IV because we will always read from the start.
107107
// This is fine because this decryption engine is strictly a migration tool.
108108
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(new byte[blockSize]));

contentgrid-appserver-contentstore-impl-encryption/src/test/java/com/contentgrid/appserver/contentstore/impl/encryption/engine/AlfrescoCompatibleEncryptionEngineTest.java

Lines changed: 37 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,31 @@
33
import static org.assertj.core.api.Assertions.assertThat;
44
import static org.assertj.core.api.Assertions.assertThatThrownBy;
55

6-
import com.contentgrid.appserver.contentstore.api.ContentReader;
76
import com.contentgrid.appserver.contentstore.api.ContentReference;
87
import com.contentgrid.appserver.contentstore.api.range.ResolvedContentRange;
98
import com.contentgrid.appserver.contentstore.impl.encryption.engine.ContentEncryptionEngine.EncryptionParameters;
109
import com.contentgrid.appserver.contentstore.impl.encryption.keys.KeyBytes;
10+
import com.contentgrid.appserver.contentstore.impl.encryption.testing.ResourceContentReader;
1111

12-
import java.io.IOException;
1312
import java.io.InputStream;
14-
import java.util.Arrays;
13+
import java.util.ArrayList;
1514
import java.util.HexFormat;
1615
import java.util.List;
1716

1817
import org.junit.jupiter.api.Test;
1918
import org.junit.jupiter.params.ParameterizedTest;
2019
import org.junit.jupiter.params.provider.Arguments;
2120
import org.junit.jupiter.params.provider.FieldSource;
22-
import org.junit.jupiter.params.provider.ValueSource;
2321

2422
class AlfrescoCompatibleEncryptionEngineTest
2523
{
2624

27-
// KEYS, RESOURCES, SIZES all correlate to another and must have consistent order
25+
// the following static variables correlate to another and must have consistent order
2826
// KEYS = symmetric encryption keys generated in an alfresco-simple-content-stores ACS instance
2927
// RESOURCES = encrypted/decrypted resources (lorem ipsum-like)
30-
// SIZES = unencrypted resource sizes
28+
// ALGORITHMS = encryption algorithms used for resources
29+
// DECRYPTED_SIZES = unencrypted resource sizes
30+
// ENCRYPTED_SIZES = encrypted resource sizes
3131
private static final String[] KEYS = { "156c8bc259cd3e4ad1d9c38cf6361847", "566cf243d7ee9cf69df6ec004bf5f35c",
3232
"2405ae5a16b3909abb9ece58833c08bc", "72c51fcefd0a6da181dc4c98bbcc08e5", "89cf56ab800a394d95c10548065aae5d", "e39d23642ca81c68",
3333
"13320e7520e60e26133ef8372a0b7aad9e9e8a0bba5bf2ab" };
@@ -42,14 +42,27 @@ class AlfrescoCompatibleEncryptionEngineTest
4242
private static final long[] DECRYPTED_SIZES = { 6094l, 4240l, 4117l, 4162l, 1001l, 2859l, 5853l };
4343

4444
private static final long[] ENCRYPTED_SIZES = { 6096l, 4256l, 4128l, 4176l, 1008l, 2864l, 5856l };
45+
46+
private static final long[] START_BYTES = { 128l, 3096l, 1234l, 3210l, 512l, 1536l, 4096l };
47+
48+
private static final List<Arguments> fullComparison;
49+
private static final List<Arguments> rangedComparison;
50+
static {
51+
fullComparison = new ArrayList<>();
52+
rangedComparison = new ArrayList<>();
53+
for (int i = 0; i < KEYS.length; i++)
54+
{
55+
fullComparison.add(Arguments.of(ALGORITHMS[i], KEYS[i], RESOURCES[i], DECRYPTED_SIZES[i]));
56+
rangedComparison.add(Arguments.of(ALGORITHMS[i], KEYS[i], RESOURCES[i], DECRYPTED_SIZES[i], ENCRYPTED_SIZES[i], START_BYTES[i]));
57+
}
58+
}
4559

46-
private static final List<Arguments> RANGED_ARGUMENTS = Arrays.asList(Arguments.of(0, 128l), Arguments.of(1, 3096l),
47-
Arguments.of(2, 1234l), Arguments.of(3, 3210l), Arguments.of(4, 512l), Arguments.of(5, 1536l), Arguments.of(6, 4096l));
60+
// engine is stateless
61+
private final AlfrescoCompatibleEncryptionEngine engine = new AlfrescoCompatibleEncryptionEngine();
4862

4963
@Test
5064
void decryptionOnly() throws Exception
5165
{
52-
var engine = new AlfrescoCompatibleEncryptionEngine();
5366
assertThatThrownBy(engine::createNewParameters).isInstanceOf(UnsupportedOperationException.class);
5467

5568
var params = new EncryptionParameters(DataEncryptionAlgorithm.of("Alfresco-AES"), KeyBytes.adopt(HexFormat.of().parseHex(KEYS[0])),
@@ -65,8 +78,6 @@ void supportCheck()
6578
{
6679
// test various general algorithms and explicit modes
6780
// supported + unsupported
68-
69-
var engine = new AlfrescoCompatibleEncryptionEngine();
7081
assertThat(engine.supports(DataEncryptionAlgorithm.of("AES"))).isFalse();
7182
assertThat(engine.supports(DataEncryptionAlgorithm.of("Alfresco-AES"))).isTrue();
7283
assertThat(engine.supports(DataEncryptionAlgorithm.of("Alfresco-AES/CTR/PKCS5Padding"))).isFalse();
@@ -108,35 +119,32 @@ public long getContentSize()
108119
}
109120

110121
@ParameterizedTest
111-
@ValueSource(ints = { 0, 1, 2, 3, 4 })
112-
void fullComparison(int resIdx) throws Exception
122+
@FieldSource
123+
void fullComparison(String alg, String keyHex, String resource, long decryptedSize) throws Exception
113124
{
114-
var engine = new AlfrescoCompatibleEncryptionEngine();
115-
var alg = ALGORITHMS[resIdx];
116-
var params = new EncryptionParameters(DataEncryptionAlgorithm.of(alg), KeyBytes.adopt(HexFormat.of().parseHex(KEYS[resIdx])),
125+
var params = new EncryptionParameters(DataEncryptionAlgorithm.of(alg), KeyBytes.adopt(HexFormat.of().parseHex(keyHex)),
117126
new byte[0]);
118127

119-
var encryptedResource = "/alfresco/encrypted/" + RESOURCES[resIdx];
120-
var decryptedResource = "/alfresco/decrypted/" + RESOURCES[resIdx];
128+
var encryptedResource = "/alfresco/encrypted/" + resource;
129+
var decryptedResource = "/alfresco/decrypted/" + resource;
121130

122131
var reader = engine.decrypt(r -> new ResourceContentReader(encryptedResource), params,
123-
ResolvedContentRange.fullRange(DECRYPTED_SIZES[resIdx]));
132+
ResolvedContentRange.fullRange(decryptedSize));
124133

125134
assertThat(reader.getContentInputStream())
126135
.hasSameContentAs(AlfrescoCompatibleEncryptionEngineTest.class.getResourceAsStream(decryptedResource));
127136
}
128137

129138
@ParameterizedTest
130-
@FieldSource("RANGED_ARGUMENTS")
131-
void rangedComparison(int resIdx, long startByte) throws Exception
139+
@FieldSource
140+
void rangedComparison(String alg, String keyHex, String resource, long decryptedSize, long encryptedSize, long startByte)
141+
throws Exception
132142
{
133-
var engine = new AlfrescoCompatibleEncryptionEngine();
134-
var alg = ALGORITHMS[resIdx];
135-
var params = new EncryptionParameters(DataEncryptionAlgorithm.of(alg), KeyBytes.adopt(HexFormat.of().parseHex(KEYS[resIdx])),
143+
var params = new EncryptionParameters(DataEncryptionAlgorithm.of(alg), KeyBytes.adopt(HexFormat.of().parseHex(keyHex)),
136144
new byte[0]);
137145

138-
var encryptedResource = "/alfresco/encrypted/" + RESOURCES[resIdx];
139-
var decryptedResource = "/alfresco/decrypted/" + RESOURCES[resIdx];
146+
var encryptedResource = "/alfresco/encrypted/" + resource;
147+
var decryptedResource = "/alfresco/decrypted/" + resource;
140148

141149
ResolvedContentRange contentRange = new ResolvedContentRange()
142150
{
@@ -151,17 +159,17 @@ public long getStartByte()
151159
@Override
152160
public long getEndByteInclusive()
153161
{
154-
return DECRYPTED_SIZES[resIdx] - 1;
162+
return decryptedSize - 1;
155163
}
156164

157165
@Override
158166
public long getContentSize()
159167
{
160-
return DECRYPTED_SIZES[resIdx];
168+
return decryptedSize;
161169
}
162170
};
163171
var reader = engine.decrypt(r -> new ResourceContentReader(encryptedResource), params, contentRange);
164-
assertThat(reader.getContentSize()).isEqualTo(ENCRYPTED_SIZES[resIdx]);
172+
assertThat(reader.getContentSize()).isEqualTo(encryptedSize);
165173
assertThat(reader.getReference()).isEqualTo(ContentReference.of(encryptedResource));
166174
assertThat(reader.getDescription()).isEqualTo("Decrypted resource file " + encryptedResource);
167175

@@ -173,56 +181,4 @@ public long getContentSize()
173181
assertThat(is2).hasSameContentAs(is1);
174182
}
175183
}
176-
177-
private static class ResourceContentReader implements ContentReader
178-
{
179-
180-
private final String resourceName;
181-
182-
private final long contentSize;
183-
184-
ResourceContentReader(String resourceName)
185-
{
186-
this.resourceName = resourceName;
187-
long size = 0;
188-
try (InputStream is = getContentInputStream())
189-
{
190-
byte[] buf = new byte[1024];
191-
int bytesRead = 0;
192-
while ((bytesRead = is.read(buf)) != -1)
193-
{
194-
size += bytesRead;
195-
}
196-
}
197-
catch (IOException ioex)
198-
{
199-
throw new RuntimeException(ioex);
200-
}
201-
this.contentSize = size;
202-
}
203-
204-
@Override
205-
public ContentReference getReference()
206-
{
207-
return ContentReference.of(this.resourceName);
208-
}
209-
210-
@Override
211-
public String getDescription()
212-
{
213-
return "resource file " + this.resourceName;
214-
}
215-
216-
@Override
217-
public long getContentSize()
218-
{
219-
return this.contentSize;
220-
}
221-
222-
@Override
223-
public InputStream getContentInputStream()
224-
{
225-
return AlfrescoCompatibleEncryptionEngineTest.class.getResourceAsStream(resourceName);
226-
}
227-
}
228184
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# test files should be treated as binary (no crlf magic!)
2+
*.bin binary
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.contentgrid.appserver.contentstore.impl.encryption.testing;
2+
3+
import com.contentgrid.appserver.contentstore.api.ContentReader;
4+
import com.contentgrid.appserver.contentstore.api.ContentReference;
5+
6+
import java.io.IOException;
7+
import java.io.InputStream;
8+
9+
public class ResourceContentReader implements ContentReader
10+
{
11+
12+
private final String resourceName;
13+
14+
private final long contentSize;
15+
16+
public ResourceContentReader(String resourceName)
17+
{
18+
this.resourceName = resourceName;
19+
long size = 0;
20+
try (InputStream is = getContentInputStream())
21+
{
22+
byte[] buf = new byte[1024];
23+
int bytesRead = 0;
24+
while ((bytesRead = is.read(buf)) != -1)
25+
{
26+
size += bytesRead;
27+
}
28+
}
29+
catch (IOException ioex)
30+
{
31+
throw new RuntimeException(ioex);
32+
}
33+
this.contentSize = size;
34+
}
35+
36+
@Override
37+
public ContentReference getReference()
38+
{
39+
return ContentReference.of(this.resourceName);
40+
}
41+
42+
@Override
43+
public String getDescription()
44+
{
45+
return "resource file " + this.resourceName;
46+
}
47+
48+
@Override
49+
public long getContentSize()
50+
{
51+
return this.contentSize;
52+
}
53+
54+
@Override
55+
public InputStream getContentInputStream()
56+
{
57+
return ResourceContentReader.class.getResourceAsStream(resourceName);
58+
}
59+
}

contentgrid-appserver-integration-test/src/test/java/com/contentgrid/appserver/integration/test/content/EncryptedAlfCompatibilityTest.java

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,11 @@
3737
import java.io.InputStream;
3838
import java.nio.charset.StandardCharsets;
3939
import java.time.Duration;
40+
import java.util.ArrayList;
4041
import java.util.Arrays;
4142
import java.util.HexFormat;
4243
import java.util.List;
44+
import java.util.UUID;
4345

4446
import org.junit.jupiter.api.BeforeEach;
4547
import org.junit.jupiter.api.Test;
@@ -111,10 +113,12 @@ class EncryptedAlfCompatibilityTest {
111113

112114
private static final String FILENAME = "hello.txt";
113115

114-
// KEYS, RESOURCES, SIZES all correlate to another and must have consistent order
116+
// the following static variables correlate to another and must have consistent order
115117
// KEYS = symmetric encryption keys generated in an alfresco-simple-content-stores ACS instance
116118
// RESOURCES = encrypted/decrypted resources (lorem ipsum-like)
117-
// SIZES = unencrypted resource sizes
119+
// ALGORITHMS = encryption algorithms used for resources
120+
// DECRYPTED_SIZES = unencrypted resource sizes
121+
// ENCRYPTED_SIZES = encrypted resource sizes
118122
private static final String[] KEYS = { "156c8bc259cd3e4ad1d9c38cf6361847", "566cf243d7ee9cf69df6ec004bf5f35c",
119123
"2405ae5a16b3909abb9ece58833c08bc", "72c51fcefd0a6da181dc4c98bbcc08e5", "89cf56ab800a394d95c10548065aae5d", "e39d23642ca81c68",
120124
"13320e7520e60e26133ef8372a0b7aad9e9e8a0bba5bf2ab" };
@@ -124,15 +128,22 @@ class EncryptedAlfCompatibilityTest {
124128
"bc49e9ad-fcfc-403e-ba3e-2280d602a53e.bin", "ba50f10d-4df4-4ba0-9161-3038be5fab80.bin",
125129
"05efb43b-2808-4a1f-b25a-83f1c24f8bcf.bin" };
126130

127-
private static final String[] ALGORITHMS = { "Alfresco-AES", "Alfresco-AES", "Alfresco-AES", "Alfresco-AES", "Alfresco-AES",
128-
"Alfresco-DES", "Alfresco-DESede" };
131+
private static final String[] ALGORITHMS = { "Alfresco-AES", "Alfresco-AES", "Alfresco-AES", "Alfresco-AES", "Alfresco-AES", "Alfresco-DES", "Alfresco-DESede" };
129132

130133
private static final long[] DECRYPTED_SIZES = { 6094l, 4240l, 4117l, 4162l, 1001l, 2859l, 5853l };
131134

132135
private static final long[] ENCRYPTED_SIZES = { 6096l, 4256l, 4128l, 4176l, 1008l, 2864l, 5856l };
133-
134-
private static final List<Arguments> RANGED_ARGUMENTS = Arrays.asList(Arguments.of(0, 128l), Arguments.of(1, 3096l),
135-
Arguments.of(2, 1234l), Arguments.of(3, 3210l), Arguments.of(4, 512l), Arguments.of(5, 1536l), Arguments.of(6, 4096l));
136+
137+
private static final long[] START_BYTES = { 128l, 3096l, 1234l, 3210l, 512l, 1536l, 4096l };
138+
139+
private static final List<Arguments> simulateContentMigrationAndAccess;
140+
static {
141+
simulateContentMigrationAndAccess = new ArrayList<>();
142+
for (int i = 0; i < KEYS.length; i++)
143+
{
144+
simulateContentMigrationAndAccess.add(Arguments.of(ALGORITHMS[i], KEYS[i], RESOURCES[i], DECRYPTED_SIZES[i], ENCRYPTED_SIZES[i], START_BYTES[i]));
145+
}
146+
}
136147

137148
@LocalServerPort
138149
private int port;
@@ -195,15 +206,16 @@ void createNewContentWhileInCompatibilityMode() {
195206
}
196207

197208
@ParameterizedTest
198-
@FieldSource("RANGED_ARGUMENTS")
199-
void simulateContentMigrationAndAccess(int resIdx, long startByte) throws Exception
209+
@FieldSource
210+
void simulateContentMigrationAndAccess(String alg, String keyHex, String resource, long decryptedSize, long encryptedSize,
211+
long startByte) throws Exception
200212
{
201213
// it is not possible to use ReST API to create migrated content
202214
// migration scripts (as far as I know) use direct DB load + store access
203215

204-
var encryptedResource = "/alfresco/encrypted/" + RESOURCES[resIdx];
205-
var decryptedResource = "/alfresco/decrypted/" + RESOURCES[resIdx];
206-
var keyBytes = KeyBytes.adopt(HexFormat.of().parseHex(KEYS[resIdx]));
216+
var encryptedResource = "/alfresco/encrypted/" + resource;
217+
var decryptedResource = "/alfresco/decrypted/" + resource;
218+
var keyBytes = KeyBytes.adopt(HexFormat.of().parseHex(keyHex));
207219

208220
// write file as stored in Alfresco
209221
ContentAccessor written;
@@ -214,18 +226,18 @@ void simulateContentMigrationAndAccess(int resIdx, long startByte) throws Except
214226
// record content-associated key
215227
dkeAccessor.addKey(written.getReference(),
216228
new StoredDataEncryptionKey(
217-
DataEncryptionAlgorithm.of(ALGORITHMS[resIdx]),
229+
DataEncryptionAlgorithm.of(alg),
218230
WrappingKeyId.unwrapped(), keyBytes, new byte[0]));
219231

220232
// create entity creation data (typically done in DatamodelApi)
221233
// need to save the encrypted size (actual length is technically unknown internally)
222-
String fileName = "test" + resIdx;
234+
String fileName = "test-" + UUID.randomUUID().toString();
223235
var compositeContent = CompositeAttributeData.builder()
224236
.name(AttributeName.of("file"))
225237
.attribute(new SimpleAttributeData<>(AttributeName.of("id"), written.getReference().getValue()))
226238
.attribute(new SimpleAttributeData<>(AttributeName.of("filename"), fileName))
227239
.attribute(new SimpleAttributeData<>(AttributeName.of("mimetype"), "text/plain"))
228-
.attribute(new SimpleAttributeData<>(AttributeName.of("length"), ENCRYPTED_SIZES[resIdx]))
240+
.attribute(new SimpleAttributeData<>(AttributeName.of("length"), encryptedSize))
229241
.build();
230242
var entity = EntityCreateData.builder()
231243
.entityName(EntityName.of("employee"))
@@ -239,7 +251,7 @@ void simulateContentMigrationAndAccess(int resIdx, long startByte) throws Except
239251

240252
// use RestAPI to read
241253
var start = startByte;
242-
var end = startByte + (DECRYPTED_SIZES[resIdx] - startByte) / 2;
254+
var end = startByte + (decryptedSize - startByte) / 2;
243255

244256
var expected = new byte[(int)(end - start + 1)];
245257
try (InputStream is = EncryptedAlfCompatibilityTest.class.getResourceAsStream(decryptedResource)) {

0 commit comments

Comments
 (0)