Skip to content
This repository has been archived by the owner on Nov 7, 2023. It is now read-only.

Commit

Permalink
Rename TransferblockTO to EBLEnvelopeTO
Browse files Browse the repository at this point in the history
Signed-off-by: Niels Thykier <[email protected]>
  • Loading branch information
nt-gt committed Jun 2, 2023
1 parent 65ca6c8 commit ac3cc64
Show file tree
Hide file tree
Showing 10 changed files with 56 additions and 55 deletions.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ The rendered Open API specification for the is available on the [DCSA Swaggerhub
See [the contributing guide](CONTRIBUTING.md) for detailed instructions on how to get started with our project.

# Transferring BL control to another platform
Transferring the BL control of possession is accomplished by sending a _**"Transferblock"**_ from the sending platform to the receiving platform.
Transferring the BL control of possession is accomplished by sending a _**"EBLEnvelope"**_ from the sending platform to the receiving platform.

## Overview of a `transferblock`
A `transferblock` is a single JSON structure described [here](https://app.swaggerhub.com/apis/dcsaorg/DCSA_EEC/0.12-alpha#/transferblock).
The `transferblock` has the following structure:
## Overview of an `EBLEnvelope`
A `eblEnvelope` is a single JSON structure described [here](https://app.swaggerhub.com/apis/dcsaorg/DCSA_EEC/0.12-alpha#/eblEnvelope).
The `eblEnvelope` has the following structure:
* data of the B/L according to the [DCSA transportDocument specification](https://app.swaggerhub.com/domains/dcsaorg/DOCUMENTATION_DOMAIN/2.1.0#/components/schemas/transportDocument)
* The complete endorsement chain as signed endorsementChainEntries transferred between platforms
* signature of the endorsementChainEntry - signed with the private key of the sending platform
Expand Down Expand Up @@ -68,8 +68,8 @@ This results in the following JSON structure:

## Process of transferring

Transferring a B/L between platforms consists of creating and signing the ebl Envelope and wrap it in a `transferblock`.
This `transferblock` is being sent via an HTTP PUT to the receiving platform. Which is depicted in this diagram:
Transferring a B/L between platforms consists of creating and signing the endorsement chain entry and wrap it in a `eblEnvelope`.
This `eblEnvelope` is being sent via an HTTP PUT to the receiving platform. Which is depicted in this diagram:

![cross platform B/L transfer](specifications/plantuml/transfer-sequence-diagram.png)

Expand Down
6 changes: 3 additions & 3 deletions SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,10 @@ keytool -genkeypair -alias dcsa-kid -keyalg RSA -keystore dcsa-jwk.jks -storepas
```

### Generating keystore for verifying signatures
Verifying the signatures of either incoming transferblocks or verifying the signature in the response of an outgoing transferblock requires a JKS.
Verifying the signatures of either incoming eblEnvelopes or verifying the signature in the response of an outgoing eblEnvelopes requires a JKS.
This JKS can contain multiple public certificates of each platform connected. The reference implementations uses the **_'CN'_** in the certificate to determine which public key to use for verification.
For incoming transferblocks the CN is matched with the 'platformHost' field in the transaction.
For verifying responses of outgoing transferblocks the CN is matched to the URL the transferblock is being sent to.
For incoming eblEnvelopes the CN is matched with the 'platformHost' field in the transaction.
For verifying responses of outgoing eblEnvelopes the CN is matched to the URL the eblEnvelopes is being sent to.
So when generating the key material make sure to take note of the CN used.

To generate the keystore:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import lombok.RequiredArgsConstructor;
import org.dcsa.endorsementchain.service.ImportService;
import org.dcsa.endorsementchain.transferobjects.TransferblockTO;
import org.dcsa.endorsementchain.transferobjects.EBLEnvelopeTO;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PutMapping;
Expand All @@ -17,11 +17,11 @@ public class ImportController {

private final ImportService importService;

@PutMapping(value = "/transferblocks",
@PutMapping(value = "/transferblocks", // TODO: Rename when we are ready to change the API
produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> importTransferblock(@RequestBody @Valid TransferblockTO transferblock) {
return importService.importEbl(transferblock)
public ResponseEntity<String> importEBLEnvelope(@RequestBody @Valid EBLEnvelopeTO eblEnvelopeTO) {
return importService.importEbl(eblEnvelopeTO)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.badRequest().build());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import org.dcsa.endorsementchain.persistence.entity.TransportDocument;
import org.dcsa.endorsementchain.transferobjects.EndorsementChainEntryTO;
import org.dcsa.endorsementchain.transferobjects.SignedEndorsementChainEntryTO;
import org.dcsa.endorsementchain.transferobjects.TransferblockTO;
import org.dcsa.endorsementchain.transferobjects.EBLEnvelopeTO;
import org.dcsa.endorsementchain.unofficial.service.TransactionService;
import org.dcsa.skernel.errors.exceptions.ConcreteRequestErrorMessageException;
import org.erdtman.jcs.JsonCanonicalizer;
Expand Down Expand Up @@ -59,36 +59,36 @@ public String exportEbl(String transferee, String documentHash) {
List<SignedEndorsementChainEntryTO> toBeExportedEndorsementChainEntries =
Stream.concat(previousSignedEndorsementChainEntries.stream(), Stream.of(signedEndorsementChainEntryTO)).toList();

TransferblockTO transferblock =
TransferblockTO.builder()
EBLEnvelopeTO eblEnvelopeTO =
EBLEnvelopeTO.builder()
.endorsementChain(toBeExportedEndorsementChainEntries)
.document(transportDocument.getTransportDocumentJson())
.build();

URI platformURL = transfereeToPlatformHost(transferee);

String signatureResponse = sendTransferBlock(platformURL, transferblock);
String signatureResponse = sendEBLEnvelope(platformURL, eblEnvelopeTO);

return endorsementChainEntryService.verifyEndorsementChainEntryResponseSignature(
platformURL.getHost() + ":" + platformURL.getPort(),
signedEndorsementChainEntryTO.envelopeHash(),
signatureResponse);
}

private String sendTransferBlock(URI platformUrl, TransferblockTO transferblock) {
private String sendEBLEnvelope(URI platformUrl, EBLEnvelopeTO eblEnvelopeTO) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(List.of(MediaType.APPLICATION_JSON));

ResponseEntity<String> transferblockResponse =
ResponseEntity<String> eblEnvelopeResponse =
restTemplate.exchange(
platformUrl, HttpMethod.PUT, new HttpEntity<>(transferblock, headers), String.class);
platformUrl, HttpMethod.PUT, new HttpEntity<>(eblEnvelopeTO, headers), String.class);

if (transferblockResponse.getStatusCode().isError()) {
if (eblEnvelopeResponse.getStatusCode().isError()) {
throw ConcreteRequestErrorMessageException.internalServerError("Transfer failed.");
}

return Optional.ofNullable(transferblockResponse.getBody())
return Optional.ofNullable(eblEnvelopeResponse.getBody())
.orElseThrow(
() ->
ConcreteRequestErrorMessageException.internalServerError(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import org.dcsa.endorsementchain.persistence.entity.EndorsementChainEntry;
import org.dcsa.endorsementchain.transferobjects.EndorsementChainEntryTO;
import org.dcsa.endorsementchain.transferobjects.SignedEndorsementChainEntryTO;
import org.dcsa.endorsementchain.transferobjects.TransferblockTO;
import org.dcsa.endorsementchain.transferobjects.EBLEnvelopeTO;
import org.dcsa.endorsementchain.unofficial.service.TransportDocumentService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -19,32 +19,32 @@ public class ImportService {
private final TransportDocumentService transportDocumentService;

@Transactional
public Optional<String> importEbl(TransferblockTO transferblock) {
List<EndorsementChainEntry> parsedEndorsementChainEntries = parseEndorsementChainEntries(transferblock);
public Optional<String> importEbl(EBLEnvelopeTO eblEnvelope) {
List<EndorsementChainEntry> parsedEndorsementChainEntries = parseEndorsementChainEntries(eblEnvelope);
return Optional.ofNullable(endorsementChainEntryService.saveEndorsementEntries(parsedEndorsementChainEntries));
}

private List<EndorsementChainEntry> parseEndorsementChainEntries(TransferblockTO transferblock) {
private List<EndorsementChainEntry> parseEndorsementChainEntries(EBLEnvelopeTO eblEnvelope) {

List<EndorsementChainEntry> endorsementChainEntryList = transferblock.endorsementChain().stream()
List<EndorsementChainEntry> endorsementChainEntryList = eblEnvelope.endorsementChain().stream()
.map(signedEndorsementChainEntryTO ->
validate(transferblock, signedEndorsementChainEntryTO))
validate(eblEnvelope, signedEndorsementChainEntryTO))
.toList();

verifyTransportDocument(transferblock.document(), endorsementChainEntryList);
verifyTransportDocument(eblEnvelope.document(), endorsementChainEntryList);

return endorsementChainEntryList;
}

private EndorsementChainEntry validate(TransferblockTO transferblock, SignedEndorsementChainEntryTO signedEndorsementChainEntryTO) {
private EndorsementChainEntry validate(EBLEnvelopeTO eblEnvelope, SignedEndorsementChainEntryTO signedEndorsementChainEntryTO) {
EndorsementChainEntryTO parsedEndorsementChainEntry = endorsementChainEntryService.verifyEndorsementChainSignature(signedEndorsementChainEntryTO.signature());

// Since the platformhost is the host of the originating transaction and all transactions within
// an EBL envelope are from the same platform we can take any of the transactions to retrieve
// the platformhost.
String platformHost = parsedEndorsementChainEntry.transactions().get(0).platformHost();

return endorsementChainEntryService.signedEndorsementEntryToEndorsementChainEntry(signedEndorsementChainEntryTO, parsedEndorsementChainEntry, transferblock.document(), platformHost);
return endorsementChainEntryService.signedEndorsementEntryToEndorsementChainEntry(signedEndorsementChainEntryTO, parsedEndorsementChainEntry, eblEnvelope.document(), platformHost);
}

private void verifyTransportDocument(String transferDocument, List<EndorsementChainEntry> endorsementChainEntryList) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package org.dcsa.endorsementchain.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.dcsa.endorsementchain.datafactories.TransferblockTODataFactory;
import org.dcsa.endorsementchain.datafactories.EBLEnvelopeTODataFactory;
import org.dcsa.endorsementchain.service.ImportService;
import org.dcsa.endorsementchain.transferobjects.TransferblockTO;
import org.dcsa.endorsementchain.transferobjects.EBLEnvelopeTO;
import org.dcsa.skernel.errors.infrastructure.ConcreteRequestErrorMessageExceptionHandler;
import org.dcsa.skernel.errors.infrastructure.FallbackExceptionHandler;
import org.dcsa.skernel.errors.infrastructure.JakartaValidationExceptionHandler;
Expand Down Expand Up @@ -38,12 +38,12 @@ class ImportControllerTest {

@Test
void testImportController() throws Exception {
TransferblockTO transferblock = TransferblockTODataFactory.transferblockTO();
String rawTransferBlock = mapper.writeValueAsString(transferblock);
EBLEnvelopeTO eblEnvelope = EBLEnvelopeTODataFactory.eblEnvelopeTO();
String rawEBLEnvelope = mapper.writeValueAsString(eblEnvelope);

when(service.importEbl(isNotNull())).thenReturn(Optional.of("Dummy signature"));

String response = mockMvc.perform(put("/transferblocks").contentType(MediaType.APPLICATION_JSON).content(rawTransferBlock))
String response = mockMvc.perform(put("/transferblocks").contentType(MediaType.APPLICATION_JSON).content(rawEBLEnvelope))
.andDo(print())
.andExpect(status().isOk())
.andReturn()
Expand All @@ -55,12 +55,12 @@ void testImportController() throws Exception {

@Test
void testImportControllerError() throws Exception {
TransferblockTO transferblock = TransferblockTODataFactory.transferblockTO();
String rawTransferBlock = mapper.writeValueAsString(transferblock);
EBLEnvelopeTO eblEnvelope = EBLEnvelopeTODataFactory.eblEnvelopeTO();
String rawEBLEnvelope = mapper.writeValueAsString(eblEnvelope);

when(service.importEbl(isNotNull())).thenReturn(Optional.empty());

mockMvc.perform(put("/transferblocks").contentType(MediaType.APPLICATION_JSON).content(rawTransferBlock))
mockMvc.perform(put("/transferblocks").contentType(MediaType.APPLICATION_JSON).content(rawEBLEnvelope))
.andDo(print())
.andExpect(status().isBadRequest());

Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
package org.dcsa.endorsementchain.datafactories;

import lombok.experimental.UtilityClass;
import org.dcsa.endorsementchain.transferobjects.TransferblockTO;
import org.dcsa.endorsementchain.transferobjects.EBLEnvelopeTO;
import org.dcsa.endorsementchain.unofficial.datafactories.TransportDocumentDataFactory;

@UtilityClass
public class TransferblockTODataFactory {
public class EBLEnvelopeTODataFactory {

public TransferblockTO transferblockTO() {
return TransferblockTO.builder()
public EBLEnvelopeTO eblEnvelopeTO() {
return EBLEnvelopeTO.builder()
.document(TransportDocumentDataFactory.transportDocumentEntityWithoutTransactions().getTransportDocumentJson())
.endorsementChain(
SignedEndorsementChainEntryTODataFactory.signedEndorsementChainEntryTOList())
.build();
}

public TransferblockTO transferblockTO(String rawEnvelope, String secondRawEnvelope) {
return TransferblockTO.builder()
public EBLEnvelopeTO eblEnvelopeTO(String rawEnvelope, String secondRawEnvelope) {
// TODO: Both parameters are unused!?
return EBLEnvelopeTO.builder()
.document(TransportDocumentDataFactory.transportDocumentEntityWithoutTransactions().getTransportDocumentJson())
.endorsementChain(
SignedEndorsementChainEntryTODataFactory.signedEndorsementChainEntryTOList())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
import org.dcsa.endorsementchain.datafactories.EndorsementChainEntryDataFactory;
import org.dcsa.endorsementchain.datafactories.EndorsementChainEntryTODataFactory;
import org.dcsa.endorsementchain.datafactories.SignedEndorsementChainEntryTODataFactory;
import org.dcsa.endorsementchain.datafactories.TransferblockTODataFactory;
import org.dcsa.endorsementchain.datafactories.EBLEnvelopeTODataFactory;
import org.dcsa.endorsementchain.persistence.entity.EndorsementChainEntry;
import org.dcsa.endorsementchain.transferobjects.EndorsementChainEntryTO;
import org.dcsa.endorsementchain.transferobjects.SignedEndorsementChainEntryTO;
import org.dcsa.endorsementchain.transferobjects.TransferblockTO;
import org.dcsa.endorsementchain.transferobjects.EBLEnvelopeTO;
import org.dcsa.endorsementchain.unofficial.service.TransportDocumentService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
Expand Down Expand Up @@ -48,30 +48,30 @@ void testImportEbl() {
}).toList();
List<SignedEndorsementChainEntryTO> signedEndorsementChainEntries = SignedEndorsementChainEntryTODataFactory.signedEndorsementChainEntryTOList();

TransferblockTO transferblock = TransferblockTODataFactory.transferblockTO(rawEndorsementChainEntries.get(0), rawEndorsementChainEntries.get(1));
EBLEnvelopeTO eblEnvelope = EBLEnvelopeTODataFactory.eblEnvelopeTO(rawEndorsementChainEntries.get(0), rawEndorsementChainEntries.get(1));
List<EndorsementChainEntryTO> endorsementChainEntryTOs = EndorsementChainEntryTODataFactory.endorsementChainEntryTOList();

when(endorsementChainEntryService.verifyEndorsementChainSignature(signedEndorsementChainEntries.get(0).signature()))
.thenReturn(endorsementChainEntryTOs.get(0));
when(endorsementChainEntryService.verifyEndorsementChainSignature(signedEndorsementChainEntries.get(1).signature()))
.thenReturn(endorsementChainEntryTOs.get(1));
when(endorsementChainEntryService.signedEndorsementEntryToEndorsementChainEntry(signedEndorsementChainEntries.get(0), endorsementChainEntryTOs.get(0), transferblock.document(), "localhost:8443")).thenReturn(endorsementChainEntries.get(0));
when(endorsementChainEntryService.signedEndorsementEntryToEndorsementChainEntry(signedEndorsementChainEntries.get(1), endorsementChainEntryTOs.get(1), transferblock.document(), "localhost:8443")).thenReturn(endorsementChainEntries.get(1));
when(endorsementChainEntryService.signedEndorsementEntryToEndorsementChainEntry(signedEndorsementChainEntries.get(0), endorsementChainEntryTOs.get(0), eblEnvelope.document(), "localhost:8443")).thenReturn(endorsementChainEntries.get(0));
when(endorsementChainEntryService.signedEndorsementEntryToEndorsementChainEntry(signedEndorsementChainEntries.get(1), endorsementChainEntryTOs.get(1), eblEnvelope.document(), "localhost:8443")).thenReturn(endorsementChainEntries.get(1));
when((endorsementChainEntryService.saveEndorsementEntries(endorsementChainEntries))).thenReturn("dummyHash");

Optional<String> signedResponse = importService.importEbl(transferblock);
Optional<String> signedResponse = importService.importEbl(eblEnvelope);
assertTrue(signedResponse.isPresent());
assertEquals("dummyHash", signedResponse.get());
}

@Test
void testImportEblWithoutEndorsementChain() {
TransferblockTO transferblock = TransferblockTO.builder()
EBLEnvelopeTO eblEnvelope = EBLEnvelopeTO.builder()
.document("Test document")
.endorsementChain(Collections.emptyList())
.build();

Optional<String> signedResponse = importService.importEbl(transferblock);
Optional<String> signedResponse = importService.importEbl(eblEnvelope);
assertTrue(signedResponse.isEmpty());
}

Expand Down
4 changes: 2 additions & 2 deletions test-certificates/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ This directory contains a set of key material to ease a local setup and should n

All keys containing in this directory are generated on a local machine and self-signed certificates.

* verification-private-key.pem - the private key of the party sending the transferblock to the test application. This key is used to generate a signature that can be verified by this reference application.
* verification-private-key.pem - the private key of the party sending the eblEnvelope to the test application. This key is used to generate a signature that can be verified by this reference application.
* dcsa-jwk-verify.jks - the JKS containing the public certificate used by the reference implementation to perform a verification of the signature. Contains the public certificate of the keypair of which verification-private-key.pem is the private key.
* dcsa-jwk.jks - contains the private key and public key and certificate used by the reference implementation to sign an outgoing (exporting transferblock). The public key material is available via the endpoint: https://localhost:8443/v1/unofficial/.well-known/jwks.json
* dcsa-jwk.jks - contains the private key and public key and certificate used by the reference implementation to sign an outgoing (exporting eblEnvelope). The public key material is available via the endpoint: https://localhost:8443/v1/unofficial/.well-known/jwks.json
* springboot-https.p12 - pkcs12 keystore containing the key material for setting up TLS. Since this is a self-signed certificate it requires disabling certificate verification checks.

## Matching public key used for verifying incoming signatures
Expand Down

0 comments on commit ac3cc64

Please sign in to comment.