Skip to content

Commit fb98094

Browse files
authored
Use only two SQL statements to read TeeTaskComputeSecret and Web2Secret during TEE session creation (#254)
1 parent 266682b commit fb98094

File tree

7 files changed

+237
-187
lines changed

7 files changed

+237
-187
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
44

55
## [[NEXT]](https://github.com/iExecBlockchainComputing/iexec-sms/releases/tag/vNEXT) 2024
66

7+
### Quality
8+
9+
- Use only two SQL statements to read `TeeTaskComputeSecret` and `Web2Secret` during TEE session creation. (#254)
10+
711
## [[8.5.0]](https://github.com/iExecBlockchainComputing/iexec-sms/releases/tag/v8.5.0) 2024-02-29
812

913
### New Features

src/main/java/com/iexec/sms/secret/compute/TeeTaskComputeSecretService.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626
import org.springframework.jdbc.core.JdbcTemplate;
2727
import org.springframework.stereotype.Service;
2828

29+
import java.util.List;
2930
import java.util.Optional;
31+
import java.util.stream.Collectors;
3032

3133
@Slf4j
3234
@Service
@@ -77,6 +79,12 @@ public Optional<TeeTaskComputeSecret> getSecret(
7779
return Optional.of(decryptedSecret);
7880
}
7981

82+
public List<TeeTaskComputeSecret> getSecretsForTeeSession(Iterable<TeeTaskComputeSecretHeader> ids) {
83+
return teeTaskComputeSecretRepository.findAllById(ids).stream()
84+
.map(secret -> secret.withValue(encryptionService.decrypt(secret.getValue())))
85+
.collect(Collectors.toList());
86+
}
87+
8088
/**
8189
* Check whether a secret exists.
8290
*

src/main/java/com/iexec/sms/secret/web2/Web2SecretHeader.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public class Web2SecretHeader implements Serializable {
3434
private String ownerAddress;
3535
private String address; //0xdataset1, aws.amazon.com, beneficiary.key.iex.ec (Kb)
3636

37-
Web2SecretHeader(String ownerAddress, String address) {
37+
public Web2SecretHeader(String ownerAddress, String address) {
3838
Objects.requireNonNull(ownerAddress, "Web2 secret owner address can't be null.");
3939
Objects.requireNonNull(address, "Web2 secret address can't be null.");
4040

src/main/java/com/iexec/sms/secret/web2/Web2SecretService.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@
2626
import org.springframework.jdbc.core.JdbcTemplate;
2727
import org.springframework.stereotype.Service;
2828

29+
import java.util.List;
2930
import java.util.Objects;
3031
import java.util.Optional;
32+
import java.util.stream.Collectors;
3133

3234
@Slf4j
3335
@Service
@@ -68,6 +70,12 @@ public Optional<String> getDecryptedValue(String ownerAddress, String secretAddr
6870
.map(secret -> encryptionService.decrypt(secret.getValue()));
6971
}
7072

73+
public List<Web2Secret> getSecretsForTeeSession(Iterable<Web2SecretHeader> ids) {
74+
return web2SecretRepository.findAllById(ids).stream()
75+
.map(secret -> secret.withValue(encryptionService.decrypt(secret.getValue())))
76+
.collect(Collectors.toList());
77+
}
78+
7179
public boolean isSecretPresent(String ownerAddress, String secretAddress) {
7280
final Web2SecretHeader key = new Web2SecretHeader(ownerAddress, secretAddress);
7381
final Boolean found = cacheSecretService.lookSecretExistenceInCache(key);

src/main/java/com/iexec/sms/tee/session/base/SecretSessionBaseService.java

Lines changed: 110 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,9 @@
2121
import com.iexec.commons.poco.task.TaskDescription;
2222
import com.iexec.commons.poco.tee.TeeEnclaveConfiguration;
2323
import com.iexec.sms.api.config.TeeServicesProperties;
24-
import com.iexec.sms.secret.compute.OnChainObjectType;
25-
import com.iexec.sms.secret.compute.SecretOwnerRole;
26-
import com.iexec.sms.secret.compute.TeeTaskComputeSecret;
27-
import com.iexec.sms.secret.compute.TeeTaskComputeSecretService;
24+
import com.iexec.sms.secret.compute.*;
25+
import com.iexec.sms.secret.web2.Web2Secret;
26+
import com.iexec.sms.secret.web2.Web2SecretHeader;
2827
import com.iexec.sms.secret.web2.Web2SecretService;
2928
import com.iexec.sms.secret.web3.Web3SecretService;
3029
import com.iexec.sms.tee.challenge.EthereumCredentials;
@@ -220,54 +219,64 @@ public SecretEnclaveBase getAppTokens(TeeSessionRequest request)
220219

221220
private Map<String, Object> getApplicationComputeSecrets(TaskDescription taskDescription) {
222221
final Map<String, Object> tokens = new HashMap<>();
223-
final String applicationAddress = taskDescription.getAppAddress();
224-
225-
if (applicationAddress != null) {
226-
final String secretIndex = "1";
227-
String appDeveloperSecret = teeTaskComputeSecretService.getSecret(
228-
OnChainObjectType.APPLICATION,
229-
applicationAddress.toLowerCase(),
230-
SecretOwnerRole.APPLICATION_DEVELOPER,
231-
"",
232-
secretIndex)
233-
.map(TeeTaskComputeSecret::getValue)
234-
.orElse(EMPTY_YML_VALUE);
235-
if (!StringUtils.isEmpty(appDeveloperSecret)) {
236-
tokens.put("IEXEC_APP_DEVELOPER_SECRET", appDeveloperSecret);
237-
tokens.put(IexecEnvUtils.IEXEC_APP_DEVELOPER_SECRET_PREFIX + secretIndex, appDeveloperSecret);
222+
final List<TeeTaskComputeSecretHeader> ids = getAppComputeSecretsHeaders(taskDescription);
223+
log.debug("TeeTaskComputeSecret looking for secrets [chainTaskId:{}, count:{}]",
224+
taskDescription.getChainTaskId(), ids.size());
225+
final List<TeeTaskComputeSecret> secrets = teeTaskComputeSecretService.getSecretsForTeeSession(ids);
226+
log.debug("TeeTaskComputeSecret objects fetched from database [chainTaskId:{}, count:{}]",
227+
taskDescription.getChainTaskId(), secrets.size());
228+
for (TeeTaskComputeSecret secret : secrets) {
229+
if (!StringUtils.isEmpty(secret.getHeader().getOnChainObjectAddress())) {
230+
tokens.put("IEXEC_APP_DEVELOPER_SECRET", secret.getValue());
231+
tokens.put(IexecEnvUtils.IEXEC_APP_DEVELOPER_SECRET_PREFIX + "1", secret.getValue());
232+
} else {
233+
final String secretKey = secret.getHeader().getKey();
234+
taskDescription.getSecrets().forEach((key, value) -> {
235+
if (value.equals(secretKey)) {
236+
tokens.put(IexecEnvUtils.IEXEC_REQUESTER_SECRET_PREFIX + key, secret.getValue());
237+
}
238+
});
238239
}
239240
}
241+
return tokens;
242+
}
240243

241-
if (taskDescription.getSecrets() == null || taskDescription.getRequester() == null) {
242-
return tokens;
244+
private List<TeeTaskComputeSecretHeader> getAppComputeSecretsHeaders(TaskDescription taskDescription) {
245+
final List<TeeTaskComputeSecretHeader> ids = new ArrayList<>();
246+
final String applicationAddress = taskDescription.getAppAddress();
247+
if (applicationAddress != null) {
248+
final String secretIndex = "1";
249+
ids.add(new TeeTaskComputeSecretHeader(
250+
OnChainObjectType.APPLICATION,
251+
applicationAddress.toLowerCase(),
252+
SecretOwnerRole.APPLICATION_DEVELOPER,
253+
"",
254+
secretIndex));
243255
}
244256

245-
final HashMap<String, String> requesterSecrets = new HashMap<>();
246-
for (Map.Entry<String, String> secretEntry : taskDescription.getSecrets().entrySet()) {
247-
try {
248-
int requesterSecretIndex = Integer.parseInt(secretEntry.getKey());
249-
if (requesterSecretIndex <= 0) {
250-
String message = "Application secret indices provided in the deal parameters must be positive numbers"
251-
+ " [providedApplicationSecretIndex:" + requesterSecretIndex + "]";
252-
log.warn(message);
253-
throw new NumberFormatException(message);
257+
if (taskDescription.getSecrets() != null && taskDescription.getRequester() != null) {
258+
for (Map.Entry<String, String> secretEntry : taskDescription.getSecrets().entrySet()) {
259+
try {
260+
int requesterSecretIndex = Integer.parseInt(secretEntry.getKey());
261+
if (requesterSecretIndex <= 0) {
262+
String message = "Application secret indices provided in the deal parameters must be positive numbers"
263+
+ " [providedApplicationSecretIndex:" + requesterSecretIndex + "]";
264+
log.warn(message);
265+
throw new NumberFormatException(message);
266+
}
267+
} catch (NumberFormatException e) {
268+
log.warn("Invalid entry found in deal parameters secrets map", e);
269+
continue;
254270
}
255-
} catch (NumberFormatException e) {
256-
log.warn("Invalid entry found in deal parameters secrets map", e);
257-
continue;
271+
ids.add(new TeeTaskComputeSecretHeader(
272+
OnChainObjectType.APPLICATION,
273+
"",
274+
SecretOwnerRole.REQUESTER,
275+
taskDescription.getRequester().toLowerCase(),
276+
secretEntry.getValue()));
258277
}
259-
String requesterSecret = teeTaskComputeSecretService.getSecret(
260-
OnChainObjectType.APPLICATION,
261-
"",
262-
SecretOwnerRole.REQUESTER,
263-
taskDescription.getRequester().toLowerCase(),
264-
secretEntry.getValue())
265-
.map(TeeTaskComputeSecret::getValue)
266-
.orElse(EMPTY_YML_VALUE);
267-
requesterSecrets.put(IexecEnvUtils.IEXEC_REQUESTER_SECRET_PREFIX + secretEntry.getKey(), requesterSecret);
268278
}
269-
tokens.putAll(requesterSecrets);
270-
return tokens;
279+
return ids;
271280
}
272281

273282
/**
@@ -287,12 +296,46 @@ public SecretEnclaveBase getPostComputeTokens(TeeSessionRequest request)
287296
if (taskDescription == null) {
288297
throw new TeeSessionGenerationException(NO_TASK_DESCRIPTION, "Task description must not be null");
289298
}
299+
300+
final List<Web2SecretHeader> ids = getPostComputeSecretHeaders(taskDescription, request.getWorkerAddress());
301+
log.debug("Web2Secret looking for secrets [chainTaskId:{}, count:{}]",
302+
taskDescription.getChainTaskId(), ids.size());
303+
final List<Web2Secret> secrets = web2SecretService.getSecretsForTeeSession(ids);
304+
log.debug("Web2Secret objects fetched from database [chainTaskId:{}, count:{}]",
305+
taskDescription.getChainTaskId(), secrets.size());
290306
// encryption
291-
Map<String, String> encryptionTokens = getPostComputeEncryptionTokens(request);
292-
tokens.putAll(encryptionTokens);
307+
final String resultEncryptionSecret = secrets.stream()
308+
.filter(secret -> IEXEC_RESULT_ENCRYPTION_PUBLIC_KEY.equals(secret.getHeader().getAddress()))
309+
.findFirst()
310+
.map(Web2Secret::getValue)
311+
.orElse("");
312+
tokens.putAll(getPostComputeEncryptionTokens(request, resultEncryptionSecret));
293313
// storage
294-
Map<String, String> storageTokens = getPostComputeStorageTokens(request);
295-
tokens.putAll(storageTokens);
314+
if (taskDescription.containsCallback()) {
315+
tokens.putAll(getPostComputeStorageTokens(request, ""));
316+
} else if (DROPBOX_RESULT_STORAGE_PROVIDER.equals(taskDescription.getResultStorageProvider())) {
317+
final String storageToken = secrets.stream()
318+
.filter(secret -> IEXEC_RESULT_DROPBOX_TOKEN.equals(secret.getHeader().getAddress()))
319+
.findFirst()
320+
.map(Web2Secret::getValue)
321+
.orElse("");
322+
tokens.putAll(getPostComputeStorageTokens(request, storageToken));
323+
} else {
324+
// TODO remove fallback on requester token when legacy Result Proxy endpoints have been removed
325+
final boolean isWorkerTokenPresent = secrets.stream()
326+
.anyMatch(secret -> IEXEC_RESULT_IEXEC_IPFS_TOKEN.equals(secret.getHeader().getAddress())
327+
&& request.getWorkerAddress().equalsIgnoreCase(secret.getHeader().getOwnerAddress()));
328+
final String tokenOwner = isWorkerTokenPresent ? request.getWorkerAddress() : taskDescription.getRequester();
329+
final String storageToken = secrets.stream()
330+
.filter(secret -> IEXEC_RESULT_IEXEC_IPFS_TOKEN.equals(secret.getHeader().getAddress()) &&
331+
tokenOwner.equalsIgnoreCase(secret.getHeader().getOwnerAddress()))
332+
.findFirst()
333+
.map(Web2Secret::getValue)
334+
.orElse("");
335+
log.debug("storage token [isWorkerTokenPresent:{}, tokenOwner:{}]",
336+
isWorkerTokenPresent, tokenOwner);
337+
tokens.putAll(getPostComputeStorageTokens(request, storageToken));
338+
}
296339
// enclave signature
297340
Map<String, String> signTokens = getPostComputeSignTokens(request);
298341
tokens.putAll(signTokens);
@@ -301,7 +344,21 @@ public SecretEnclaveBase getPostComputeTokens(TeeSessionRequest request)
301344
.build();
302345
}
303346

304-
public Map<String, String> getPostComputeEncryptionTokens(TeeSessionRequest request)
347+
List<Web2SecretHeader> getPostComputeSecretHeaders(TaskDescription taskDescription, String workerAddress) {
348+
final List<Web2SecretHeader> ids = new ArrayList<>();
349+
if (taskDescription.isResultEncryption()) {
350+
ids.add(new Web2SecretHeader(taskDescription.getBeneficiary(), IEXEC_RESULT_ENCRYPTION_PUBLIC_KEY));
351+
}
352+
if (DROPBOX_RESULT_STORAGE_PROVIDER.equals(taskDescription.getResultStorageProvider())) {
353+
ids.add(new Web2SecretHeader(taskDescription.getRequester(), IEXEC_RESULT_DROPBOX_TOKEN));
354+
} else {
355+
ids.add(new Web2SecretHeader(taskDescription.getRequester(), IEXEC_RESULT_IEXEC_IPFS_TOKEN));
356+
ids.add(new Web2SecretHeader(workerAddress, IEXEC_RESULT_IEXEC_IPFS_TOKEN));
357+
}
358+
return ids;
359+
}
360+
361+
public Map<String, String> getPostComputeEncryptionTokens(TeeSessionRequest request, String resultEncryptionKey)
305362
throws TeeSessionGenerationException {
306363
TaskDescription taskDescription = request.getTaskDescription();
307364
String taskId = taskDescription.getChainTaskId();
@@ -313,24 +370,20 @@ public Map<String, String> getPostComputeEncryptionTokens(TeeSessionRequest requ
313370
if (!shouldEncrypt) {
314371
return tokens;
315372
}
316-
Optional<String> beneficiaryResultEncryptionKeySecret = web2SecretService.getDecryptedValue(
317-
taskDescription.getBeneficiary(),
318-
IEXEC_RESULT_ENCRYPTION_PUBLIC_KEY);
319-
if (beneficiaryResultEncryptionKeySecret.isEmpty()) {
373+
if (StringUtils.isEmpty(resultEncryptionKey)) {
320374
throw new TeeSessionGenerationException(
321375
POST_COMPUTE_GET_ENCRYPTION_TOKENS_FAILED_EMPTY_BENEFICIARY_KEY,
322376
"Empty beneficiary encryption key - taskId: " + taskId);
323377
}
324-
String publicKeyValue = beneficiaryResultEncryptionKeySecret.get();
325-
tokens.put(RESULT_ENCRYPTION_PUBLIC_KEY, publicKeyValue); // base64 encoded by client
378+
tokens.put(RESULT_ENCRYPTION_PUBLIC_KEY, resultEncryptionKey); // base64 encoded by client
326379
return tokens;
327380
}
328381

329382
// TODO: We need a signature of the beneficiary to push
330383
// to the beneficiary private storage space waiting for
331384
// that feature we only allow to push to the requester
332385
// private storage space
333-
public Map<String, String> getPostComputeStorageTokens(TeeSessionRequest request)
386+
public Map<String, String> getPostComputeStorageTokens(TeeSessionRequest request, String storageToken)
334387
throws TeeSessionGenerationException {
335388
TaskDescription taskDescription = request.getTaskDescription();
336389
String taskId = taskDescription.getChainTaskId();
@@ -345,23 +398,13 @@ public Map<String, String> getPostComputeStorageTokens(TeeSessionRequest request
345398
}
346399
String storageProvider = taskDescription.getResultStorageProvider();
347400
String storageProxy = taskDescription.getResultStorageProxy();
348-
final Optional<String> storageTokenSecret;
349-
if (DROPBOX_RESULT_STORAGE_PROVIDER.equals(storageProvider)) {
350-
storageTokenSecret = web2SecretService.getDecryptedValue(taskDescription.getRequester(), IEXEC_RESULT_DROPBOX_TOKEN);
351-
} else {
352-
// TODO remove fallback on requester token when legacy Result Proxy endpoints have been removed
353-
final boolean isWorkerTokenPresent = web2SecretService.isSecretPresent(request.getWorkerAddress(), IEXEC_RESULT_IEXEC_IPFS_TOKEN);
354-
final String tokenOwner = isWorkerTokenPresent ? request.getWorkerAddress() : taskDescription.getRequester();
355-
storageTokenSecret = web2SecretService.getDecryptedValue(tokenOwner, IEXEC_RESULT_IEXEC_IPFS_TOKEN);
356-
}
357-
if (storageTokenSecret.isEmpty()) {
401+
if (StringUtils.isEmpty(storageToken)) {
358402
log.error("Failed to get storage token [taskId:{}, storageProvider:{}, requester:{}]",
359403
taskId, storageProvider, taskDescription.getRequester());
360404
throw new TeeSessionGenerationException(
361405
POST_COMPUTE_GET_STORAGE_TOKENS_FAILED,
362406
"Empty requester storage token - taskId: " + taskId);
363407
}
364-
final String storageToken = storageTokenSecret.get();
365408
tokens.put(RESULT_STORAGE_PROVIDER, storageProvider);
366409
tokens.put(RESULT_STORAGE_PROXY, storageProxy);
367410
tokens.put(RESULT_STORAGE_TOKEN, storageToken);

src/test/java/com/iexec/sms/tee/session/TeeSessionTestUtils.java

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,19 @@
1616

1717
package com.iexec.sms.tee.session;
1818

19-
import com.iexec.common.precompute.PreComputeUtils;
20-
import com.iexec.common.utils.IexecEnvUtils;
2119
import com.iexec.commons.poco.task.TaskDescription;
2220
import com.iexec.commons.poco.tee.TeeEnclaveConfiguration;
2321
import com.iexec.sms.secret.compute.OnChainObjectType;
2422
import com.iexec.sms.secret.compute.SecretOwnerRole;
2523
import com.iexec.sms.secret.compute.TeeTaskComputeSecret;
26-
import com.iexec.sms.tee.session.base.SecretSessionBaseService;
2724
import com.iexec.sms.tee.session.generic.TeeSessionRequest;
2825
import lombok.extern.slf4j.Slf4j;
2926
import org.apache.commons.lang3.ClassUtils;
3027

31-
import java.util.HashMap;
3228
import java.util.List;
3329
import java.util.Map;
3430

35-
import static com.iexec.common.worker.result.ResultUtils.*;
3631
import static com.iexec.sms.Web3jUtils.createEthereumAddress;
37-
import static com.iexec.sms.tee.session.base.SecretSessionBaseService.*;
3832
import static org.assertj.core.api.Assertions.assertThat;
3933

4034
@Slf4j
@@ -139,39 +133,6 @@ public static TaskDescription.TaskDescriptionBuilder createTaskDescription(TeeEn
139133
.botIndex(0);
140134
}
141135

142-
public static Map<String, Object> getPreComputeTokens() {
143-
return Map.of(
144-
PRE_COMPUTE_MRENCLAVE, PRE_COMPUTE_FINGERPRINT,
145-
PreComputeUtils.IS_DATASET_REQUIRED, true,
146-
PreComputeUtils.IEXEC_DATASET_KEY, DATASET_KEY.trim(),
147-
INPUT_FILE_URLS, Map.of(
148-
IexecEnvUtils.IEXEC_INPUT_FILE_URL_PREFIX + "1", INPUT_FILE_URL_1,
149-
IexecEnvUtils.IEXEC_INPUT_FILE_URL_PREFIX + "2", INPUT_FILE_URL_2));
150-
}
151-
152-
public static Map<String, Object> getAppTokens() {
153-
return Map.of(
154-
APP_MRENCLAVE, APP_FINGERPRINT,
155-
SecretSessionBaseService.INPUT_FILE_NAMES, Map.of(
156-
IexecEnvUtils.IEXEC_INPUT_FILE_NAME_PREFIX + "1", INPUT_FILE_NAME_1,
157-
IexecEnvUtils.IEXEC_INPUT_FILE_NAME_PREFIX + "2", INPUT_FILE_NAME_2));
158-
}
159-
160-
public static Map<String, Object> getPostComputeTokens() {
161-
Map<String, Object> map = new HashMap<>();
162-
map.put(POST_COMPUTE_MRENCLAVE, POST_COMPUTE_FINGERPRINT);
163-
map.put(RESULT_TASK_ID, TASK_ID);
164-
map.put(RESULT_ENCRYPTION, "yes");
165-
map.put(RESULT_ENCRYPTION_PUBLIC_KEY, ENCRYPTION_PUBLIC_KEY);
166-
map.put(RESULT_STORAGE_PROVIDER, STORAGE_PROVIDER);
167-
map.put(RESULT_STORAGE_PROXY, STORAGE_PROXY);
168-
map.put(RESULT_STORAGE_TOKEN, STORAGE_TOKEN);
169-
map.put(RESULT_STORAGE_CALLBACK, "no");
170-
map.put(RESULT_SIGN_WORKER_ADDRESS, WORKER_ADDRESS);
171-
map.put(RESULT_SIGN_TEE_CHALLENGE_PRIVATE_KEY, TEE_CHALLENGE_PRIVATE_KEY);
172-
return map;
173-
}
174-
175136
public static void assertRecursively(Object expected, Object actual) {
176137
if (expected == null ||
177138
expected instanceof String ||

0 commit comments

Comments
 (0)