Skip to content

Commit

Permalink
Update/hcert kotlin lib (#82)
Browse files Browse the repository at this point in the history
* fix expiration

* hcert kotlin update

* fix backend issuing for hcert kotlin 1.0.1

* fix checkstyle
  • Loading branch information
a-trzewik committed Jun 16, 2021
1 parent d6fc2a8 commit 75524fd
Show file tree
Hide file tree
Showing 13 changed files with 191 additions and 151 deletions.
25 changes: 18 additions & 7 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
<spring.security.version>5.4.6</spring.security.version>
<lombok.version>1.18.20</lombok.version>
<liquibase.version>4.3.3</liquibase.version>
<hcert-kotlin.version>0.2.2-SNAPSHOT</hcert-kotlin.version>
<hcert-kotlin.version>1.0.1</hcert-kotlin.version>
<kotlin.version>1.5.10</kotlin.version>
<springdoc.version>1.5.7</springdoc.version>
<junit.version>5.7.1</junit.version>
<mapstruct.version>1.4.2.Final</mapstruct.version>
Expand Down Expand Up @@ -77,7 +78,7 @@
</repository>
<repository>
<id>ehd-github</id>
<url>https://maven.pkg.github.com/ehn-digital-green-development/*</url>
<url>https://maven.pkg.github.com/ehn-dcc-development/*</url>
</repository>
</repositories>

Expand Down Expand Up @@ -235,18 +236,28 @@
</dependency>
<dependency>
<groupId>ehn.techiop.hcert</groupId>
<artifactId>hcert-kotlin</artifactId>
<artifactId>hcert-kotlin-jvm</artifactId>
<version>${hcert-kotlin.version}</version>
</dependency>
<dependency>
<groupId>com.augustcellars.cose</groupId>
<artifactId>cose-java</artifactId>
<version>1.1.0</version>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-serialization-json</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>1.4.31</version>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-datetime-jvm</artifactId>
<version>0.2.1</version>
</dependency>
<dependency>
<groupId>com.augustcellars.cose</groupId>
<artifactId>cose-java</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/eu/europa/ec/dgc/issuance/config/HcertLibConfig.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package eu.europa.ec.dgc.issuance.config;

import ehn.techiop.hcert.kotlin.chain.Base45Service;
import ehn.techiop.hcert.kotlin.chain.CborService;
import ehn.techiop.hcert.kotlin.chain.CompressorService;
import ehn.techiop.hcert.kotlin.chain.ContextIdentifierService;
import ehn.techiop.hcert.kotlin.chain.CoseService;
import ehn.techiop.hcert.kotlin.chain.SchemaValidationService;
import ehn.techiop.hcert.kotlin.chain.impl.DefaultBase45Service;
import ehn.techiop.hcert.kotlin.chain.impl.DefaultCborService;
import ehn.techiop.hcert.kotlin.chain.impl.DefaultCompressorService;
import ehn.techiop.hcert.kotlin.chain.impl.DefaultContextIdentifierService;
import ehn.techiop.hcert.kotlin.chain.impl.DefaultCoseService;
import ehn.techiop.hcert.kotlin.chain.impl.DefaultSchemaValidationService;
import eu.europa.ec.dgc.issuance.service.EhdCryptoService;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
Expand Down Expand Up @@ -38,4 +42,14 @@ Base45Service base45Service() {
return new DefaultBase45Service();
}

@Bean
CborService cborService() {
return new DefaultCborService();
}

@Bean
SchemaValidationService schemaValidationService() {
return new DefaultSchemaValidationService();
}

}
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package eu.europa.ec.dgc.issuance.restapi.controller;

import ehn.techiop.hcert.data.Eudgc;
import eu.europa.ec.dgc.issuance.restapi.dto.EgdcCodeData;
import eu.europa.ec.dgc.issuance.service.DgciService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import javax.validation.Valid;
import lombok.AllArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.http.MediaType;
Expand All @@ -17,7 +15,7 @@
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/context")
@RequestMapping("/dgci")
@AllArgsConstructor
@ConditionalOnExpression("${issuance.endpoints.backendIssuing:false}")
public class DgciBackendController {
Expand All @@ -31,7 +29,7 @@ public class DgciBackendController {
@ApiResponse(responseCode = "200", description = "signed edgc qr code created"),
@ApiResponse(responseCode = "400", description = "wrong issue data")})
@PutMapping(value = "/issue", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<EgdcCodeData> createEdgc(@Valid @RequestBody Eudgc eudgc) {
public ResponseEntity<EgdcCodeData> createEdgc(@RequestBody String eudgc) {
EgdcCodeData egdcCodeData = dgciService.createEdgc(eudgc);
return ResponseEntity.ok(egdcCodeData);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
@Data
public class EgdcCodeData {
String dgci;
String qrcCode;
String qrCode;
String tan;
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package eu.europa.ec.dgc.issuance.service;

import com.upokecenter.cbor.CBORObject;
import ehn.techiop.hcert.kotlin.chain.CwtService;
import ehn.techiop.hcert.kotlin.chain.VerificationResult;
import ehn.techiop.hcert.kotlin.crypto.CwtHeaderKeys;
import ehn.techiop.hcert.kotlin.data.GreenCertificate;
import eu.europa.ec.dgc.issuance.config.IssuanceConfigProperties;
import kotlinx.serialization.json.Json;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class ConfigurableCwtService implements CwtService {
private final ExpirationService expirationService;
private final IssuanceConfigProperties issuanceConfigProperties;

@NotNull
@Override
public byte[] decode(@NotNull byte[] bytes, @NotNull VerificationResult verificationResult) {
throw new UnsupportedOperationException("decoding not supported");
}

@NotNull
@Override
public byte[] encode(@NotNull byte[] bytes) {
CBORObject cwtMap = CBORObject.NewMap();
cwtMap.Add(CwtHeaderKeys.ISSUER.getIntVal(), issuanceConfigProperties.getCountryCode());
CBORObject dcc = CBORObject.DecodeFromBytes(bytes);
GreenCertificate greenCertificate = Json.Default.decodeFromString(GreenCertificate.Companion.serializer(),
dcc.ToJSONString());
ExpirationService.CwtTimeFields cwtTimes = expirationService.calculateCwtExpiration(greenCertificate);

cwtMap.Add(CwtHeaderKeys.ISSUED_AT.getIntVal(), cwtTimes.issuedAt);
cwtMap.Add(CwtHeaderKeys.EXPIRATION.getIntVal(), cwtTimes.expiration);
CBORObject hcertMap = CBORObject.NewMap();
hcertMap.Add(CwtHeaderKeys.EUDGC_IN_HCERT.getIntVal(),dcc);
cwtMap.Add(CwtHeaderKeys.HCERT.getIntVal(), hcertMap);
return cwtMap.EncodeToBytes();
}
}
70 changes: 55 additions & 15 deletions src/main/java/eu/europa/ec/dgc/issuance/service/DgciService.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,23 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.nimbusds.jose.jwk.Curve;
import com.nimbusds.jose.jwk.ECKey;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.util.Base64URL;
import com.upokecenter.cbor.CBORObject;
import com.upokecenter.cbor.CBORType;
import ehn.techiop.hcert.data.Eudgc;
import ehn.techiop.hcert.kotlin.chain.Base45Service;
import ehn.techiop.hcert.kotlin.chain.CborService;
import ehn.techiop.hcert.kotlin.chain.Chain;
import ehn.techiop.hcert.kotlin.chain.ChainResult;
import ehn.techiop.hcert.kotlin.chain.CompressorService;
import ehn.techiop.hcert.kotlin.chain.ContextIdentifierService;
import ehn.techiop.hcert.kotlin.chain.CoseService;
import ehn.techiop.hcert.kotlin.chain.CwtService;
import ehn.techiop.hcert.kotlin.chain.SchemaValidationService;
import ehn.techiop.hcert.kotlin.data.GreenCertificate;
import eu.europa.ec.dgc.issuance.config.IssuanceConfigProperties;
import eu.europa.ec.dgc.issuance.entity.DgciEntity;
import eu.europa.ec.dgc.issuance.entity.GreenCertificateType;
Expand Down Expand Up @@ -71,6 +74,8 @@
import java.util.Base64;
import java.util.List;
import java.util.Optional;
import kotlinx.serialization.SerializationException;
import kotlinx.serialization.json.Json;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
Expand All @@ -93,9 +98,11 @@ public enum DgciStatus {
private final DgciGenerator dgciGenerator;
private final CborService cborService;
private final CoseService coseService;
private final CwtService cwtService;
private final ContextIdentifierService contextIdentifierService;
private final CompressorService compressorService;
private final Base45Service base45Service;
private final SchemaValidationService schemaValidationService;
private final ExpirationService expirationService;

private static final int MAX_CLAIM_RETRY_TAN = 3;
Expand Down Expand Up @@ -363,37 +370,42 @@ private boolean verifySignature(ClaimRequest claimRequest) {
/**
* Create edgc in backend.
*
* @param eudgc certificate
* @param dccJson certificate
* @return edgc qr code and tan
*/
public EgdcCodeData createEdgc(Eudgc eudgc) {
public EgdcCodeData createEdgc(String dccJson) {
String dgci = dgciGenerator.newDgci();
dccJson = updateCI(dccJson, dgci);

GreenCertificate eudgc;
try {
eudgc = Json.Default.decodeFromString(GreenCertificate.Companion.serializer(), dccJson);
} catch (SerializationException se) {
throw new WrongRequest(se.getMessage());
}
GreenCertificateType greenCertificateType = GreenCertificateType.Vaccination;
if (eudgc.getR() != null) {
for (val v : eudgc.getR()) {
v.setCi(dgci);
if (eudgc.getRecoveryStatements() != null) {
for (val v : eudgc.getRecoveryStatements()) {
greenCertificateType = GreenCertificateType.Recovery;
}
}
if (eudgc.getT() != null) {
for (val v : eudgc.getT()) {
v.setCi(dgci);
if (eudgc.getTests() != null) {
for (val v : eudgc.getTests()) {
greenCertificateType = GreenCertificateType.Test;
}
}
if (eudgc.getV() != null) {
for (val v : eudgc.getV()) {
v.setCi(dgci);
if (eudgc.getVaccinations() != null) {
for (val v : eudgc.getVaccinations()) {
greenCertificateType = GreenCertificateType.Vaccination;
}
}
Chain cborProcessingChain =
new Chain(cborService, coseService,
contextIdentifierService, compressorService, base45Service);
new Chain(cborService, cwtService, coseService,
contextIdentifierService, compressorService, base45Service, schemaValidationService);
ChainResult chainResult = cborProcessingChain.encode(eudgc);

EgdcCodeData egdcCodeData = new EgdcCodeData();
egdcCodeData.setQrcCode(chainResult.getStep5Prefixed());
egdcCodeData.setQrCode(chainResult.getStep5Prefixed());
egdcCodeData.setDgci(dgci);
Tan ta = Tan.create();
egdcCodeData.setTan(ta.getRawTan());
Expand All @@ -411,6 +423,34 @@ public EgdcCodeData createEdgc(Eudgc eudgc) {
return egdcCodeData;
}

private String updateCI(String dccJson, String dgci) {
// The fields can be not modified on GreenCertificate object so we need to set ci on json level
ObjectMapper mapper = new ObjectMapper();
try {
JsonNode dccTree = mapper.readTree(dccJson);
updateCI(dccTree, dgci);
return mapper.writeValueAsString(dccTree);
} catch (JsonProcessingException e) {
throw new WrongRequest(e.getMessage());
}
}

private void updateCI(JsonNode jsonNode, String dgci) {
if (jsonNode.isObject()) {
if (jsonNode.has("ci")) {
((ObjectNode)jsonNode).put("ci",dgci);
} else {
for (JsonNode value : jsonNode) {
updateCI(value, dgci);
}
}
} else if (jsonNode.isArray()) {
for (JsonNode item : jsonNode) {
updateCI(item, dgci);
}
}
}

/**
* Check if dgci exists.
*
Expand Down
Loading

0 comments on commit 75524fd

Please sign in to comment.