diff --git a/iexec-sms-library/src/main/java/com/iexec/sms/api/config/TdxServicesProperties.java b/iexec-sms-library/src/main/java/com/iexec/sms/api/config/TdxServicesProperties.java new file mode 100644 index 00000000..41ee3725 --- /dev/null +++ b/iexec-sms-library/src/main/java/com/iexec/sms/api/config/TdxServicesProperties.java @@ -0,0 +1,30 @@ +/* + * Copyright 2025 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.iexec.sms.api.config; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.iexec.commons.poco.tee.TeeFramework; + +public class TdxServicesProperties extends TeeServicesProperties { + @JsonCreator + public TdxServicesProperties(@JsonProperty("teeFrameworkVersion") final String teeFrameworkVersion, + @JsonProperty("preComputeProperties") final TeeAppProperties preComputeProperties, + @JsonProperty("postComputeProperties") final TeeAppProperties postComputeProperties) { + super(TeeFramework.TDX, teeFrameworkVersion, preComputeProperties, postComputeProperties); + } +} diff --git a/iexec-sms-library/src/main/java/com/iexec/sms/api/config/TeeServicesProperties.java b/iexec-sms-library/src/main/java/com/iexec/sms/api/config/TeeServicesProperties.java index efa9df42..78118896 100644 --- a/iexec-sms-library/src/main/java/com/iexec/sms/api/config/TeeServicesProperties.java +++ b/iexec-sms-library/src/main/java/com/iexec/sms/api/config/TeeServicesProperties.java @@ -26,10 +26,10 @@ @AllArgsConstructor @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "teeFramework", visible = true) @JsonSubTypes({ + @JsonSubTypes.Type(name = "TDX", value = TdxServicesProperties.class), @JsonSubTypes.Type(name = "SCONE", value = SconeServicesProperties.class), @JsonSubTypes.Type(name = "GRAMINE", value = GramineServicesProperties.class) }) -// TODO upgrade to sealed class in Java 17 public abstract class TeeServicesProperties { private final TeeFramework teeFramework; private final String teeFrameworkVersion; diff --git a/iexec-sms-library/src/test/java/com/iexec/sms/api/config/TdxServicesPropertiesTests.java b/iexec-sms-library/src/test/java/com/iexec/sms/api/config/TdxServicesPropertiesTests.java new file mode 100644 index 00000000..511f0ae7 --- /dev/null +++ b/iexec-sms-library/src/test/java/com/iexec/sms/api/config/TdxServicesPropertiesTests.java @@ -0,0 +1,60 @@ +/* + * Copyright 2025 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.iexec.sms.api.config; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class TdxServicesPropertiesTests { + private final ObjectMapper mapper = new ObjectMapper(); + private final String frameworkVersion = "v0.1"; + + private final TeeServicesProperties tdxVersionProperties = new TdxServicesProperties( + frameworkVersion, createTeeAppProperties("tdx-pre-compute"), createTeeAppProperties("tdx-post-compute")); + + private TeeAppProperties createTeeAppProperties(final String name) { + return TeeAppProperties.builder() + .image(name) + .fingerprint(name + "-fingerprint") + .entrypoint(name + "-entrypoint") + .heapSizeInBytes(1024 * 1024 * 1024L) + .build(); + } + + @Test + void shouldSerializeAndDeserialize() throws JsonProcessingException { + final String jsonString = mapper.writeValueAsString(tdxVersionProperties); + final TeeServicesProperties deserializedProperties = mapper.readValue(jsonString, TdxServicesProperties.class); + + assertThat(deserializedProperties) + .usingRecursiveComparison() + .isEqualTo(tdxVersionProperties); + } + + @Test + void shouldPreserveFrameworkVersion() throws JsonProcessingException { + final String jsonString = mapper.writeValueAsString(tdxVersionProperties); + final TeeServicesProperties deserializedProperties = mapper.readValue(jsonString, TdxServicesProperties.class); + + assertThat(deserializedProperties.getTeeFrameworkVersion()) + .isEqualTo(frameworkVersion); + } + +} diff --git a/src/main/java/com/iexec/sms/tee/OnTeeFrameworkCondition.java b/src/main/java/com/iexec/sms/tee/OnTeeFrameworkCondition.java index b2f5be60..5c9d5418 100644 --- a/src/main/java/com/iexec/sms/tee/OnTeeFrameworkCondition.java +++ b/src/main/java/com/iexec/sms/tee/OnTeeFrameworkCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2023 IEXEC BLOCKCHAIN TECH + * Copyright 2022-2025 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. @@ -50,7 +50,7 @@ public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeM } if (attributes == null) { - log.warn("No attribute for bean annotation, won't be loaded [bean:{}", + log.warn("No attribute for bean annotation, won't be loaded [bean:{}]", beanClassName); return new ConditionOutcome( false, diff --git a/src/main/java/com/iexec/sms/tee/TeeController.java b/src/main/java/com/iexec/sms/tee/TeeController.java index 19cab899..6b568788 100644 --- a/src/main/java/com/iexec/sms/tee/TeeController.java +++ b/src/main/java/com/iexec/sms/tee/TeeController.java @@ -63,10 +63,10 @@ public class TeeController { private final TeeServicesProperties teeServicesProperties; public TeeController( - AuthorizationService authorizationService, - TeeChallengeService teeChallengeService, - TeeSessionService teeSessionService, - @Value("${tee.worker.pipelines[0].version}") String version) { + final AuthorizationService authorizationService, + final TeeChallengeService teeChallengeService, + final TeeSessionService teeSessionService, + @Value("${tee.worker.pipelines[0].version}") final String version) { this.authorizationService = authorizationService; this.teeChallengeService = teeChallengeService; this.teeSessionService = teeSessionService; diff --git a/src/main/java/com/iexec/sms/tee/config/TeeWorkerInternalConfiguration.java b/src/main/java/com/iexec/sms/tee/config/TeeWorkerInternalConfiguration.java index e858b581..966b32fd 100644 --- a/src/main/java/com/iexec/sms/tee/config/TeeWorkerInternalConfiguration.java +++ b/src/main/java/com/iexec/sms/tee/config/TeeWorkerInternalConfiguration.java @@ -17,6 +17,7 @@ package com.iexec.sms.tee.config; import com.iexec.commons.poco.tee.TeeFramework; +import com.iexec.sms.api.config.TdxServicesProperties; import com.iexec.sms.api.config.TeeServicesProperties; import com.iexec.sms.tee.ConditionalOnTeeFramework; import jakarta.validation.constraints.NotBlank; @@ -60,4 +61,19 @@ public Map sconeServicesPropertiesMap( )); } + @Bean + @ConditionalOnTeeFramework(frameworks = TeeFramework.TDX) + public Map tdxServicesPropertiesMap( + final TeeWorkerPipelineConfiguration pipelineConfig) { + return pipelineConfig.getPipelines().stream() + .map(pipeline -> new TdxServicesProperties( + pipeline.version(), + pipeline.preCompute().toTeeAppProperties(), + pipeline.postCompute().toTeeAppProperties())) + .collect(Collectors.toUnmodifiableMap( + TeeServicesProperties::getTeeFrameworkVersion, + Function.identity() + )); + } + } diff --git a/src/main/java/com/iexec/sms/tee/session/tdx/TdxSessionHandlerService.java b/src/main/java/com/iexec/sms/tee/session/tdx/TdxSessionHandlerService.java new file mode 100644 index 00000000..188b10c1 --- /dev/null +++ b/src/main/java/com/iexec/sms/tee/session/tdx/TdxSessionHandlerService.java @@ -0,0 +1,44 @@ +/* + * Copyright 2025 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.iexec.sms.tee.session.tdx; + +import com.iexec.commons.poco.tee.TeeFramework; +import com.iexec.sms.api.TeeSessionGenerationError; +import com.iexec.sms.tee.ConditionalOnTeeFramework; +import com.iexec.sms.tee.session.generic.TeeSessionGenerationException; +import com.iexec.sms.tee.session.generic.TeeSessionHandler; +import com.iexec.sms.tee.session.generic.TeeSessionRequest; +import org.springframework.stereotype.Service; + +@Service +@ConditionalOnTeeFramework(frameworks = TeeFramework.TDX) +public class TdxSessionHandlerService implements TeeSessionHandler { + /** + * Build and post secret session on secret provisioning service. + * + * @param request tee session generation request + * @return String secret provisioning service url + * @throws TeeSessionGenerationException if an error occurs + */ + @Override + public String buildAndPostSession(final TeeSessionRequest request) throws TeeSessionGenerationException { + throw new TeeSessionGenerationException( + TeeSessionGenerationError.SECURE_SESSION_STORAGE_CALL_FAILED, + "Not implemented yet" + ); + } +} diff --git a/src/main/resources/application-tdx.yaml b/src/main/resources/application-tdx.yaml new file mode 100644 index 00000000..a41396df --- /dev/null +++ b/src/main/resources/application-tdx.yaml @@ -0,0 +1,14 @@ +tee: + worker: + pipelines: + - version: # v0.1 + pre-compute: + image: # e.g.: iexechub/tee-worker-pre-compute-rust: + fingerprint: + heap-size: # 1GB + entrypoint: # /app/tee-worker-pre-compute + post-compute: + image: # e.g.: iexechub/tee-worker-post-compute-rust: + fingerprint: + heap-size: # 1GB + entrypoint: # /app/tee-worker-post-compute diff --git a/src/test/java/com/iexec/sms/tee/config/TeeWorkerInternalConfigurationTests.java b/src/test/java/com/iexec/sms/tee/config/TeeWorkerInternalConfigurationTests.java index c7915342..e85f7b8b 100644 --- a/src/test/java/com/iexec/sms/tee/config/TeeWorkerInternalConfigurationTests.java +++ b/src/test/java/com/iexec/sms/tee/config/TeeWorkerInternalConfigurationTests.java @@ -16,19 +16,15 @@ package com.iexec.sms.tee.config; -import com.iexec.commons.poco.tee.TeeFramework; -import com.iexec.sms.api.config.GramineServicesProperties; -import com.iexec.sms.api.config.SconeServicesProperties; -import com.iexec.sms.api.config.TeeAppProperties; -import com.iexec.sms.api.config.TeeServicesProperties; +import com.iexec.sms.api.config.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.util.unit.DataSize; -import java.util.Arrays; +import java.util.List; import java.util.Map; -import static org.junit.jupiter.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; class TeeWorkerInternalConfigurationTests { private static final String PRE_IMAGE = "preComputeImage"; @@ -65,7 +61,7 @@ void setUp() { final TeeWorkerPipelineConfiguration.Pipeline additionalPipeline = getPipeline("v6"); pipelineConfig = new TeeWorkerPipelineConfiguration( - Arrays.asList(pipeline, additionalPipeline) + List.of(pipeline, additionalPipeline) ); } @@ -96,17 +92,13 @@ void shouldBuildGramineServicesPropertiesMap() { final Map multiPropertiesMap = teeWorkerInternalConfiguration.gramineServicesPropertiesMap(pipelineConfig); - assertNotNull(multiPropertiesMap); - assertEquals(2, multiPropertiesMap.size()); + assertThat(multiPropertiesMap).isNotNull() + .extracting(Map::size) + .isEqualTo(2); - final TeeServicesProperties properties = multiPropertiesMap.get(VERSION); - assertNotNull(properties); - assertInstanceOf(GramineServicesProperties.class, properties); - - final GramineServicesProperties gramineProperties = (GramineServicesProperties) properties; - assertEquals(TeeFramework.GRAMINE, gramineProperties.getTeeFramework()); - assertEquals(preComputeProperties, gramineProperties.getPreComputeProperties()); - assertEquals(postComputeProperties, gramineProperties.getPostComputeProperties()); + assertThat(multiPropertiesMap.get(VERSION)) + .usingRecursiveComparison() + .isEqualTo(new GramineServicesProperties(VERSION, preComputeProperties, postComputeProperties)); } @Test @@ -114,18 +106,27 @@ void shouldBuildSconeServicesPropertiesMap() { final Map multiPropertiesMap = teeWorkerInternalConfiguration.sconeServicesPropertiesMap(pipelineConfig, LAS_IMAGE); - assertNotNull(multiPropertiesMap); - assertEquals(2, multiPropertiesMap.size()); + assertThat(multiPropertiesMap).isNotNull() + .extracting(Map::size) + .isEqualTo(2); + + assertThat(multiPropertiesMap.get(VERSION)) + .usingRecursiveComparison() + .isEqualTo(new SconeServicesProperties(VERSION, preComputeProperties, postComputeProperties, LAS_IMAGE)); + } + + @Test + void shouldBuildTdxServicesPropertiesMap() { + final Map multiPropertiesMap = + teeWorkerInternalConfiguration.tdxServicesPropertiesMap(pipelineConfig); - final TeeServicesProperties properties = multiPropertiesMap.get(VERSION); - assertNotNull(properties); - assertInstanceOf(SconeServicesProperties.class, properties); + assertThat(multiPropertiesMap).isNotNull() + .extracting(Map::size) + .isEqualTo(2); - final SconeServicesProperties sconeProperties = (SconeServicesProperties) properties; - assertEquals(TeeFramework.SCONE, sconeProperties.getTeeFramework()); - assertEquals(preComputeProperties, sconeProperties.getPreComputeProperties()); - assertEquals(postComputeProperties, sconeProperties.getPostComputeProperties()); - assertEquals(LAS_IMAGE, sconeProperties.getLasImage()); + assertThat(multiPropertiesMap.get(VERSION)) + .usingRecursiveComparison() + .isEqualTo(new TdxServicesProperties(VERSION, preComputeProperties, postComputeProperties)); } } diff --git a/src/test/java/com/iexec/sms/tee/session/tdx/TdxSessionHandlerServiceTests.java b/src/test/java/com/iexec/sms/tee/session/tdx/TdxSessionHandlerServiceTests.java new file mode 100644 index 00000000..e59a5795 --- /dev/null +++ b/src/test/java/com/iexec/sms/tee/session/tdx/TdxSessionHandlerServiceTests.java @@ -0,0 +1,36 @@ +/* + * Copyright 2025 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.iexec.sms.tee.session.tdx; + +import com.iexec.sms.api.TeeSessionGenerationError; +import com.iexec.sms.tee.session.generic.TeeSessionGenerationException; +import com.iexec.sms.tee.session.generic.TeeSessionRequest; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class TdxSessionHandlerServiceTests { + private final TdxSessionHandlerService sessionHandlerService = new TdxSessionHandlerService(); + + @Test + void shouldThrow() { + assertThatThrownBy(() -> sessionHandlerService.buildAndPostSession(TeeSessionRequest.builder().build())) + .isInstanceOf(TeeSessionGenerationException.class) + .hasMessage("Not implemented yet") + .hasFieldOrPropertyWithValue("error", TeeSessionGenerationError.SECURE_SESSION_STORAGE_CALL_FAILED); + } +} diff --git a/src/test/java/com/iexec/sms/version/VersionControllerTests.java b/src/test/java/com/iexec/sms/version/VersionControllerTests.java index deeb469c..23e2a1ed 100644 --- a/src/test/java/com/iexec/sms/version/VersionControllerTests.java +++ b/src/test/java/com/iexec/sms/version/VersionControllerTests.java @@ -17,10 +17,7 @@ package com.iexec.sms.version; import com.iexec.commons.poco.tee.TeeFramework; -import com.iexec.sms.api.config.GramineServicesProperties; -import com.iexec.sms.api.config.SconeServicesProperties; -import com.iexec.sms.api.config.TeeAppProperties; -import com.iexec.sms.api.config.TeeServicesProperties; +import com.iexec.sms.api.config.*; import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.Metrics; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; @@ -64,7 +61,7 @@ void afterEach() { @Test void testVersionController() { - TeeServicesProperties properties = new SconeServicesProperties( + final TeeServicesProperties properties = new SconeServicesProperties( teeFrameworkVersion, preComputeProperties, postComputeProperties, @@ -76,23 +73,13 @@ void testVersionController() { } @ParameterizedTest - @EnumSource(value = TeeFramework.class, names = {"SCONE", "GRAMINE"}) + @EnumSource(value = TeeFramework.class) void shouldReturnInfoGauge(TeeFramework teeFramework) { - TeeServicesProperties properties; - if (teeFramework == TeeFramework.SCONE) { - properties = new SconeServicesProperties( - teeFrameworkVersion, - preComputeProperties, - postComputeProperties, - "lasImage" - ); - } else { - properties = new GramineServicesProperties( - teeFrameworkVersion, - preComputeProperties, - postComputeProperties - ); - } + final TeeServicesProperties properties = switch (teeFramework) { + case TDX -> new TdxServicesProperties(teeFrameworkVersion, preComputeProperties, postComputeProperties); + case SCONE -> new SconeServicesProperties(teeFrameworkVersion, preComputeProperties, postComputeProperties, "lasImage"); + case GRAMINE -> new GramineServicesProperties(teeFrameworkVersion, preComputeProperties, postComputeProperties); + }; final Map sconePropertiesMap = Map.of(teeFrameworkVersion, properties); versionController = new VersionController(buildProperties, sconePropertiesMap); versionController.initializeGaugeVersion();