Skip to content

Commit

Permalink
Merge pull request #257 from iExecBlockchainComputing/release/8.5.1
Browse files Browse the repository at this point in the history
Release/8.5.1
  • Loading branch information
jbern0rd authored Apr 2, 2024
2 parents ec9f8cc + af0f6d4 commit 4c7de65
Show file tree
Hide file tree
Showing 13 changed files with 399 additions and 266 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

All notable changes to this project will be documented in this file.

## [[8.5.1]](https://github.com/iExecBlockchainComputing/iexec-sms/releases/tag/v8.5.1) 2024-04-02

### New Features

- Add `Authorization` header on `/tee/challenges/{chainTaskId}` endpoint. (#255 #256)

### Quality

- Use only two SQL statements to read `TeeTaskComputeSecret` and `Web2Secret` during TEE session creation. (#254)

## [[8.5.0]](https://github.com/iExecBlockchainComputing/iexec-sms/releases/tag/v8.5.0) 2024-02-29

### New Features
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version=8.5.0
version=8.5.1
iexecCommonVersion=8.4.0
iexecCommonsPocoVersion=3.2.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,18 @@ String setWeb3Secret(
// endregion

// region TEE

/**
* @deprecated use {@link SmsClient#generateTeeChallenge(String, String)}
*/
@Deprecated(forRemoval = true)
@RequestLine("POST /tee/challenges/{chainTaskId}")
String generateTeeChallenge(@Param("chainTaskId") String chainTaskId);

@Headers("Authorization: {authorization}")
@RequestLine("POST /tee/challenges/{chainTaskId}")
String generateTeeChallenge(@Param("authorization") String authorization, @Param("chainTaskId") String chainTaskId);

@RequestLine("POST /tee/sessions")
@Headers("Authorization: {authorization}")
ApiResponseBody<TeeSessionGenerationResponse, TeeSessionGenerationError> generateTeeSession(
Expand Down
64 changes: 30 additions & 34 deletions src/main/java/com/iexec/sms/authorization/AuthorizationService.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package com.iexec.sms.authorization;


import com.iexec.commons.poco.chain.ChainDeal;
import com.iexec.commons.poco.chain.ChainTask;
import com.iexec.commons.poco.chain.ChainTaskStatus;
Expand Down Expand Up @@ -49,52 +48,49 @@ public AuthorizationService(IexecHubService iexecHubService) {

/**
* Checks whether this execution is authorized.
* If not authorized, return the reason.
* Otherwise, returns an empty {@link Optional}.
*
* @param workerpoolAuthorization The workerpool authorization to check
* @return {@code Optional.empty()} if all checks passed, the failure reason otherwise
*/
public Optional<AuthorizationError> isAuthorizedOnExecutionWithDetailedIssue(WorkerpoolAuthorization workerpoolAuthorization, boolean isTeeTask) {
public Optional<AuthorizationError> isAuthorizedOnExecutionWithDetailedIssue(WorkerpoolAuthorization workerpoolAuthorization) {
if (workerpoolAuthorization == null || StringUtils.isEmpty(workerpoolAuthorization.getChainTaskId())) {
log.error("Not authorized with empty params");
return Optional.of(EMPTY_PARAMS_UNAUTHORIZED);
}

String chainTaskId = workerpoolAuthorization.getChainTaskId();
Optional<ChainTask> optionalChainTask = iexecHubService.getChainTask(chainTaskId);
if (optionalChainTask.isEmpty()) {
final String chainTaskId = workerpoolAuthorization.getChainTaskId();
final ChainTask chainTask = iexecHubService.getChainTask(chainTaskId).orElse(null);
if (chainTask == null) {
log.error("Could not get chainTask [chainTaskId:{}]", chainTaskId);
return Optional.of(GET_CHAIN_TASK_FAILED);
}
ChainTask chainTask = optionalChainTask.get();
ChainTaskStatus taskStatus = chainTask.getStatus();
String chainDealId = chainTask.getDealid();
final String chainDealId = chainTask.getDealid();

if (taskStatus != ChainTaskStatus.ACTIVE) {
log.error("Task not active onchain [chainTaskId:{}, status:{}]",
chainTaskId, taskStatus);
if (chainTask.getStatus() != ChainTaskStatus.ACTIVE) {
log.error("Task not active on chain [chainTaskId:{}, status:{}]",
chainTaskId, chainTask.getStatus());
return Optional.of(TASK_NOT_ACTIVE);
}

Optional<ChainDeal> optionalChainDeal = iexecHubService.getChainDeal(chainDealId);
if (optionalChainDeal.isEmpty()) {
final ChainDeal chainDeal = iexecHubService.getChainDeal(chainDealId).orElse(null);
if (chainDeal == null) {
log.error("isAuthorizedOnExecution failed (getChainDeal failed) [chainTaskId:{}]", chainTaskId);
return Optional.of(GET_CHAIN_DEAL_FAILED);
}
ChainDeal chainDeal = optionalChainDeal.get();

boolean isTeeTaskOnchain = TeeUtils.isTeeTag(chainDeal.getTag());
if (isTeeTask != isTeeTaskOnchain) {
log.error("Could not match onchain task type [isTeeTask:{}, isTeeTaskOnchain:{}, chainTaskId:{}, walletAddress:{}]",
isTeeTask, isTeeTaskOnchain, chainTaskId, workerpoolAuthorization.getWorkerWallet());
final boolean isTeeTaskOnchain = TeeUtils.isTeeTag(chainDeal.getTag());
if (!isTeeTaskOnchain) {
log.error("Could not match onchain task type [isTeeTaskOnchain:{}, chainTaskId:{}]",
isTeeTaskOnchain, chainTaskId);
return Optional.of(NO_MATCH_ONCHAIN_TYPE);
}

String workerpoolAddress = chainDeal.getPoolOwner();
boolean isSignerByWorkerpool = isSignedByHimself(workerpoolAuthorization.getHash(),
workerpoolAuthorization.getSignature().getValue(), workerpoolAddress);
final boolean isSignedByWorkerpool = isSignedByHimself(workerpoolAuthorization.getHash(),
workerpoolAuthorization.getSignature().getValue(), chainDeal.getPoolOwner());

if (!isSignerByWorkerpool) {
log.error("isAuthorizedOnExecution failed (invalid signature) [chainTaskId:{}, isWorkerpoolSignatureValid:{}]",
chainTaskId, isSignerByWorkerpool);
if (!isSignedByWorkerpool) {
log.error("isAuthorizedOnExecution failed (invalid signature) [chainTaskId:{}, isSignedByWorkerpool:{}]",
chainTaskId, isSignedByWorkerpool);
return Optional.of(INVALID_SIGNATURE);
}

Expand All @@ -110,14 +106,6 @@ public boolean isSignedByOwner(String message, String signature, String address)
String owner = iexecHubService.getOwner(address);
return !owner.isEmpty() && isSignedByHimself(message, signature, owner);
}

public String getChallengeForSetWeb3Secret(String secretAddress,
String secretValue) {
return HashUtils.concatenateAndHash(
Hash.sha3String(DOMAIN),
secretAddress,
Hash.sha3String(secretValue));
}
// endregion

// region challenges
Expand Down Expand Up @@ -152,6 +140,14 @@ public String getChallengeForSetWeb2Secret(String ownerAddress,
Hash.sha3String(secretValue));
}

public String getChallengeForSetWeb3Secret(String secretAddress,
String secretValue) {
return HashUtils.concatenateAndHash(
Hash.sha3String(DOMAIN),
secretAddress,
Hash.sha3String(secretValue));
}

public String getChallengeForWorker(WorkerpoolAuthorization workerpoolAuthorization) {
return HashUtils.concatenateAndHash(
workerpoolAuthorization.getWorkerWallet(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Slf4j
@Service
Expand Down Expand Up @@ -77,6 +79,12 @@ public Optional<TeeTaskComputeSecret> getSecret(
return Optional.of(decryptedSecret);
}

public List<TeeTaskComputeSecret> getSecretsForTeeSession(Iterable<TeeTaskComputeSecretHeader> ids) {
return teeTaskComputeSecretRepository.findAllById(ids).stream()
.map(secret -> secret.withValue(encryptionService.decrypt(secret.getValue())))
.collect(Collectors.toList());
}

/**
* Check whether a secret exists.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class Web2SecretHeader implements Serializable {
private String ownerAddress;
private String address; //0xdataset1, aws.amazon.com, beneficiary.key.iex.ec (Kb)

Web2SecretHeader(String ownerAddress, String address) {
public Web2SecretHeader(String ownerAddress, String address) {
Objects.requireNonNull(ownerAddress, "Web2 secret owner address can't be null.");
Objects.requireNonNull(address, "Web2 secret address can't be null.");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

@Slf4j
@Service
Expand Down Expand Up @@ -68,6 +70,12 @@ public Optional<String> getDecryptedValue(String ownerAddress, String secretAddr
.map(secret -> encryptionService.decrypt(secret.getValue()));
}

public List<Web2Secret> getSecretsForTeeSession(Iterable<Web2SecretHeader> ids) {
return web2SecretRepository.findAllById(ids).stream()
.map(secret -> secret.withValue(encryptionService.decrypt(secret.getValue())))
.collect(Collectors.toList());
}

public boolean isSecretPresent(String ownerAddress, String secretAddress) {
final Web2SecretHeader key = new Web2SecretHeader(ownerAddress, secretAddress);
final Boolean found = cacheSecretService.lookSecretExistenceInCache(key);
Expand Down
47 changes: 32 additions & 15 deletions src/main/java/com/iexec/sms/tee/TeeController.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020-2023 IEXEC BLOCKCHAIN TECH
* Copyright 2020-2024 IEXEC BLOCKCHAIN TECH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,13 +19,13 @@

import com.iexec.common.web.ApiResponseBody;
import com.iexec.commons.poco.chain.WorkerpoolAuthorization;
import com.iexec.commons.poco.security.Signature;
import com.iexec.commons.poco.tee.TeeFramework;
import com.iexec.sms.api.TeeSessionGenerationError;
import com.iexec.sms.api.TeeSessionGenerationResponse;
import com.iexec.sms.api.config.TeeServicesProperties;
import com.iexec.sms.authorization.AuthorizationError;
import com.iexec.sms.authorization.AuthorizationService;
import com.iexec.sms.tee.challenge.TeeChallenge;
import com.iexec.sms.tee.challenge.TeeChallengeService;
import com.iexec.sms.tee.session.TeeSessionService;
import com.iexec.sms.tee.session.generic.TeeSessionGenerationException;
Expand Down Expand Up @@ -73,6 +73,7 @@ public TeeController(

/**
* Return which TEE framework this SMS is configured to use.
*
* @return TEE framework this SMS is configured to use.
*/
@GetMapping("/framework")
Expand Down Expand Up @@ -100,15 +101,29 @@ public ResponseEntity<TeeServicesProperties> getTeeServicesProperties(
}

/**
* Called by the core, not the worker
* Generates an enclave challenge for a PoCo task.
* <p>
* This method is called by the scheduler.
*
* @param authorization Authorization to check the query legitimacy
* @param chainTaskId ID of the task the challenge will be produced for
* @return The Ethereum address enclave challenge for the provided
*/
@PostMapping("/challenges/{chainTaskId}")
public ResponseEntity<String> generateTeeChallenge(@PathVariable String chainTaskId) {
Optional<TeeChallenge> executionChallenge =
teeChallengeService.getOrCreate(chainTaskId, false);
return executionChallenge
.map(teeChallenge -> ResponseEntity
.ok(teeChallenge.getCredentials().getAddress()))
public ResponseEntity<String> generateTeeChallenge(@RequestHeader String authorization, @PathVariable String chainTaskId) {
log.debug("generateTeeChallenge [authorization:{}, chainTaskId:{}]", authorization, chainTaskId);
final Optional<AuthorizationError> authorizationError = authorizationService.isAuthorizedOnExecutionWithDetailedIssue(
WorkerpoolAuthorization.builder()
.chainTaskId(chainTaskId)
.enclaveChallenge("")
.workerWallet("")
.signature(new Signature(authorization))
.build());
if (authorizationError.isPresent()) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
return teeChallengeService.getOrCreate(chainTaskId, false)
.map(teeChallenge -> ResponseEntity.ok(teeChallenge.getCredentials().getAddress()))
.orElseGet(() -> ResponseEntity.notFound().build());
}

Expand All @@ -118,10 +133,12 @@ public ResponseEntity<String> generateTeeChallenge(@PathVariable String chainTas
* to the enclave so the latter can talk to the CAS and get
* the needed secrets.
*
* @return
* 200 OK with the session id if success,
* 404 NOT_FOUND if the task is not found,
* 500 INTERNAL_SERVER_ERROR otherwise.
* @return result
* <ul>
* <li>200 OK with the session id if success.
* <li>404 NOT_FOUND if the task is not found.
* <li>500 INTERNAL_SERVER_ERROR otherwise.
* </ul>
*/
@PostMapping("/sessions")
public ResponseEntity<ApiResponseBody<TeeSessionGenerationResponse, TeeSessionGenerationError>> generateTeeSession(
Expand All @@ -140,7 +157,7 @@ public ResponseEntity<ApiResponseBody<TeeSessionGenerationResponse, TeeSessionGe
.body(body);
}
final Optional<AuthorizationError> authorizationError =
authorizationService.isAuthorizedOnExecutionWithDetailedIssue(workerpoolAuthorization, true);
authorizationService.isAuthorizedOnExecutionWithDetailedIssue(workerpoolAuthorization);
if (authorizationError.isPresent()) {
final TeeSessionGenerationError teeSessionGenerationError =
authorizationToGenerationError.get(authorizationError.get());
Expand Down Expand Up @@ -170,7 +187,7 @@ public ResponseEntity<ApiResponseBody<TeeSessionGenerationResponse, TeeSessionGe
return ResponseEntity.ok(ApiResponseBody.<TeeSessionGenerationResponse, TeeSessionGenerationError>builder()
.data(teeSessionGenerationResponse)
.build());
} catch(TeeSessionGenerationException e) {
} catch (TeeSessionGenerationException e) {
log.error("Failed to generate secure session [taskId:{}, workerAddress:{}]",
taskId, workerAddress, e);
final ApiResponseBody<TeeSessionGenerationResponse, TeeSessionGenerationError> body =
Expand Down
Loading

0 comments on commit 4c7de65

Please sign in to comment.