Skip to content

Commit

Permalink
[INJICERT-434] add preliminary VC 2.0 support via DataProvider plugin…
Browse files Browse the repository at this point in the history
… & VCFormatter + VCSigner (#93)

* [INJICERT-434] add DataProvider plugin interface

Co-Authored by: Piyush7034 <[email protected]>
Signed-off-by: Harsh Vardhan <[email protected]>

* [INJICERT-434] temp add sleep to replace JARs

Signed-off-by: Harsh Vardhan <[email protected]>

* [INJICERT-434] update interface definitions

Signed-off-by: Harsh Vardhan <[email protected]>

* [INJICERT-434] add repository to fetch VC Templates

Signed-off-by: Piyush Shukla <[email protected]>

* [INJICERT-434] add prelimnary impl for VC template & sign

* Velocity Engine 1.7 used for templating
* Embedded mosip/keymanager used for key signing with RSASignature2018
* older issuance plugin(VCIssuancePlugin) compatibility is maintained w
  hack assuming VC2.0 is always generated by
  DataProvider+VCFormatter+VCSigner

Co-Authored by: Piyush7034 <[email protected]>

Signed-off-by: Harsh Vardhan <[email protected]>

* [INJICERT-434] add sample tests for templating

Signed-off-by: Harsh Vardhan <[email protected]>

* [INJICERT-434] fix TemplateRepository definition

Co-Authored by: Hitesh Jain <[email protected]>

Signed-off-by: Harsh Vardhan <[email protected]>

* [INJICERT-434] generate RSA key for signing VCs

Signed-off-by: Harsh Vardhan <[email protected]>

* [INJICERT-434] fix local setup, update templates

Co-Authored by: Hitesh Jain <[email protected]>

Signed-off-by: Harsh Vardhan <[email protected]>

* [INJICERT-497] use the correct plugin type for VCI resp

* add config to point the VCI b/w VCIssuancePlugin & DataProviderPlugin

Signed-off-by: Harsh Vardhan <[email protected]>

* [INJICERT-434] improve time handling iff DataProvider skips issuance/expiry time

Signed-off-by: Harsh Vardhan <[email protected]>

* [INJICERT-434] get public key URL from config

Signed-off-by: Harsh Vardhan <[email protected]>

* [INJICERT-434] fix integration test

Signed-off-by: Harsh Vardhan <[email protected]>

* [INJICERT-434] separate CertifyPlugin & PluginIssuer VCIssuanceService

Two use-cases are possible now:

* CertifyPlugin: Uses DataProviderPlugin + VCFormatter + VCSigner to
  generate the credential by itself. With this, DataProviderPlugin will
  share the Credential data and formatting & signing will be done by
  Certify.

* PluginIssuer: Uses VCIssuancePlugin, which will generate the
  credential all by itself and Certify will just deliver the Credential
  over the OpenID4VCI.

Signed-off-by: Harsh Vardhan <[email protected]>

* [INJICERT-434] add unit tests & refactor impl

Signed-off-by: Harsh Vardhan <[email protected]>

* [INJICERT-434] add lib for json unit-test & update javadoc

Signed-off-by: Harsh Vardhan <[email protected]>

* [INJICERT-434] add create & update TS to template_data table

Signed-off-by: Harsh Vardhan <[email protected]>

* [INJICERT-434] add mandatory issuer property for DataProvider VC

Signed-off-by: Harsh Vardhan <[email protected]>

* [INJICERT-434] optimise imports

Signed-off-by: Harsh Vardhan <[email protected]>

* [INJICERT-434] improve logging for keymanagerlib

Signed-off-by: Harsh Vardhan <[email protected]>

* [INJICERT-434] add redis pwd in cfg file

Signed-off-by: Harsh Vardhan <[email protected]>

---------

Signed-off-by: Harsh Vardhan <[email protected]>
Signed-off-by: Piyush Shukla <[email protected]>
Co-authored-by: Piyush Shukla <[email protected]>
  • Loading branch information
vharsh and Piyush7034 authored Oct 17, 2024
1 parent 0272972 commit e87f210
Show file tree
Hide file tree
Showing 40 changed files with 1,318 additions and 19 deletions.
35 changes: 35 additions & 0 deletions certify-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,41 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- templating dependencies -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>1.7</version>
</dependency>
<dependency>
<groupId>org.apache.velocity.tools</groupId>
<artifactId>velocity-tools-generic</artifactId>
<version>3.1</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,7 @@ public class Constants {
public static final String CLIENT_ID = "client_id";
public static final String CERTIFY_PARTNER_APP_ID = "CERTIFY_PARTNER";
public static final String CERTIFY_SERVICE_APP_ID = "CERTIFY_SERVICE";
public static final String CERTIFY_MOCK_RSA = "CERTIFY_MOCK_RSA";
public static final String ROOT_KEY = "ROOT";
public static final String EMPTY_REF_ID = "";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.mosip.certify.core.constants;

/**
* SignatureAlg is the constants file of supported VC sign algorithms.
* TODO(later): convert this into a structure such that it enables
* consumers to choose VC sign algos with
*/
public class SignatureAlg {
// LinkedDataSignature Algorithms
public static final String RSA_SIGNATURE_SUITE = "RsaSignature2018";

public static final String ED25519_SIGNATURE_SUITE = "Ed25519Signature2018";

// RS256, PS256, ES256 --> JWSAlgorithm.RS256.getName();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package io.mosip.certify.core.constants;

public class VCDM1Constants {
public static final String ISSUANCE_DATE = "issuanceDate";
public static final String EXPIRATION_DATE = "expirationDate";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.mosip.certify.core.constants;

/**
* VC Data Model 2.0 Constants are some constant fields for the (draft)
* VC DataModel 2.0.
* ref:
*/
public class VCDM2Constants {
public static final String VALID_UNITL = "validUntil";
public static final String VALID_FROM = "validFrom";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.mosip.certify.core.constants;

public class VCDMConstants {

// the below assertionMethods are common b/w VC 1.1 & VC 2.0
public static final String ASSERTION_METHOD = "assertionMethod";
public static final String PROOF = "proof";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.mosip.certify.core.entity;


import jakarta.persistence.Entity;
import jakarta.persistence.*;
import lombok.*;
import jakarta.validation.constraints.NotBlank;
import java.time.LocalDateTime;

@Entity
@NoArgsConstructor
@IdClass(TemplateId.class)
public class TemplateData {
@NotBlank(message = "Template is mandatory")
@Getter
@Setter
private String template;
@Id
@Getter
@Setter
private String context;
@Id
@Getter
@Setter
private String credentialType;

@NotBlank
@Column(name = "cr_dtimes")
private LocalDateTime createdTimes;

@Column(name = "upd_dtimes")
private LocalDateTime updatedTimes;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.mosip.certify.core.entity;

import lombok.*;

import java.io.Serializable;
import java.util.Objects;

@NoArgsConstructor
@AllArgsConstructor
public class TemplateId implements Serializable {
@Getter
@Setter
private String context;
@Getter
@Setter
private String credentialType;

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof TemplateId that)) return false;
return Objects.equals(context, that.context) && Objects.equals(credentialType, that.credentialType);
}

@Override
public int hashCode() {
return Objects.hash(context, credentialType);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.mosip.certify.core.repository;

import io.mosip.certify.core.entity.TemplateData;
import io.mosip.certify.core.entity.TemplateId;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public interface TemplateRepository extends JpaRepository<TemplateData, TemplateId> {
Optional<TemplateData> findByCredentialTypeAndContext(String credentialType, String context);
// NOTE: best practice? .save()
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package io.mosip.certify.core.validators;

import io.mosip.certify.core.constants.VCFormats;
import io.mosip.certify.core.dto.CredentialDefinition;
import io.mosip.certify.core.dto.CredentialRequest;
import org.junit.Before;
import org.junit.Test;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.junit.Assert.*;

public class CredentialRequestValidatorFactoryTest {

CredentialRequestValidatorFactory factory;

@Before
public void setUp() {
factory = new CredentialRequestValidatorFactory();
}

@Test
public void isValid_invalidFormat() {
CredentialRequest cr = new CredentialRequest();
cr.setFormat("fake-format");
assertFalse(factory.isValid(cr));
}

@Test
public void isValid_LDP_true() {
CredentialRequest cr = new CredentialRequest();
cr.setFormat(VCFormats.LDP_VC);
cr.setCredential_definition(new CredentialDefinition());
assertTrue(factory.isValid(cr));
}

@Test
public void isValid_mDoc_true() {
CredentialRequest cr = new CredentialRequest();
cr.setFormat(VCFormats.MSO_MDOC);
cr.setDoctype("mDoc-doctype-fake");
cr.setClaims(Map.of("isAge", "21"));
CredentialDefinition cd = new CredentialDefinition();
cd.setType(List.of("VerifiableCredential", "MockDrivingLicense"));
cd.setContext(List.of("https://example.context.page.sh"));
cr.setCredential_definition(new CredentialDefinition());
assertTrue(factory.isValid(cr));
}
}
38 changes: 38 additions & 0 deletions certify-core/src/test/resources/SchoolTemplate.vm
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#set($validFrom = $validFrom) ## Assume $validFrom might be null or undefined
#set($validUntil = $validUntil) ## Assume $validFrom might be null or undefined

{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"${context}"
],
"type": [
"VerifiableCredential",
"SchoolCertificate"
],
"validFrom": "${validFrom}",
"issuer": "${issuer}",
#if($validUntil)
"validUntil": "${validUntil}",
#end
"credentialSubject": {
"foundingDate": "${dob}",
"city": "${city}",
#if($amenities)
"amenities": #if($amenities)$amenities#else#set($amenities = '"[]"') $amenities#end,
#end
"name": "${name}",
"principalName": "${principalName}",
"schoolType": "${schoolType}",
"country": "${country}",
"schoolRegistrationNumber": "${policyName}"
},
"renderMethod": [{
"id": "https://vharsh.github.io/DID/insurance_svg_template.svg",
"type": "SvgRenderingTemplate",
"name": "Portrait Mode",
"css3MediaQuery": "@media (orientation: portrait)",
"digestMultibase": "zQmAPdhyxzznFCwYxAp2dRerWC85Wg6wFl9G270iEu5h6JqW"
}]
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.mosip.certify.api.exception;

import io.mosip.certify.api.util.ErrorConstants;

/**
* {@link DataProviderExchangeException} is thrown when the DataProvider
* plugin fails to return user data against a set of claims generated by
* an Authentication provider.
*/
public class DataProviderExchangeException extends Exception {
private String errorCode;

public DataProviderExchangeException() {
super(ErrorConstants.VCI_DATAFETCH_FAILED);
this.errorCode = ErrorConstants.VCI_DATAFETCH_FAILED;
}

public DataProviderExchangeException(String errorCode) {
super(errorCode);
this.errorCode = errorCode;
}

public DataProviderExchangeException(String errorCode, String errorMessage) {
super(errorCode + " -> " + errorMessage);
this.errorCode = errorCode;
}

public String getErrorCode() {
return errorCode;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package io.mosip.certify.api.spi;

import io.mosip.certify.api.exception.DataProviderExchangeException;

import java.util.Map;

/**
* DataProviderPlugin is implemented by type#2 of identity plugin
* implementors to fetch data for Certify to template into a VC
* format of choice using {@link VCFormatter}.
*/
public interface DataProviderPlugin {
Map<String, Object> fetchData(Map<String, Object> identityDetails) throws DataProviderExchangeException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.mosip.certify.api.spi;


import java.util.Map;
/**
* VCDataModelFormatter is a templating engine which takes @param templateInput and returns a templated VC.
* Some implementations include
* - VC 2.0 data model templating engine
*/
public interface VCFormatter {
// TODO: Should it be changed to JSONObject?
String format(Map<String, Object> templateInput, Map<String, Object> defaultSettings);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.mosip.certify.api.spi;

import io.mosip.certify.api.dto.VCResult;
import java.util.Map;

/**
* VCSigner can sign any VC provided a vcHash & Signer inputs
*/
public interface VCSigner {
VCResult<?> perform(String templatedVC, Map<String, String> params);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ public class ErrorConstants {

public static final String NOT_IMPLEMENTED = "not_implemented";
public static final String VCI_EXCHANGE_FAILED = "vci_exchange_failed";
public static final String VCI_DATAFETCH_FAILED = "vci_datafetch_failed";
}
5 changes: 4 additions & 1 deletion certify-service/configure_start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ download_and_extract() {

if [ "$enable_certify_artifactory" = "true" ]; then
download_and_extract "${artifactory_url_env}/artifactory/libs-release-local/certify/certify-plugin.zip" "${loader_path_env}"
echo "Please patch plugin JAR now"
sleep 60
echo "Plugin JAR patching not posssible now"
fi

#installs the pkcs11 libraries.
Expand Down Expand Up @@ -67,4 +70,4 @@ if [ "$download_hsm_client" = "true" ]; then
fi
cd $work_dir

exec "$@"
exec "$@"
6 changes: 6 additions & 0 deletions certify-service/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>net.javacrumbs.json-unit</groupId>
<artifactId>json-unit-assertj</artifactId>
<version>3.4.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>info.weboftrust</groupId>
<artifactId>ld-signatures-java</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.scheduling.annotation.EnableAsync;

@EnableAsync
@EnableCaching
@SpringBootApplication(scanBasePackages = "io.mosip.certify,"+
"io.mosip.certify.core.*," +
"io.mosip.kernel.crypto," +
"io.mosip.kernel.keymanager.hsm," +
"io.mosip.kernel.cryptomanager," +
Expand Down
Loading

0 comments on commit e87f210

Please sign in to comment.