├── examples ├── appnet-api-server │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ ├── .ratpack │ │ │ ├── simplelogger.properties │ │ │ └── driving-license-schema.json │ │ │ └── java │ │ │ └── com │ │ │ └── hedera │ │ │ └── hashgraph │ │ │ └── identity │ │ │ └── hcs │ │ │ └── example │ │ │ └── appnet │ │ │ ├── dto │ │ │ ├── package-info.java │ │ │ ├── DidResolutionRequest.java │ │ │ ├── ErrorResponse.java │ │ │ ├── DrivingLicenseRequest.java │ │ │ └── VerifiableCredentialStatus.java │ │ │ ├── vc │ │ │ ├── package-info.java │ │ │ ├── CredentialSchema.java │ │ │ ├── LinkedDataProof.java │ │ │ ├── DrivingLicense.java │ │ │ ├── DrivingLicenseDocument.java │ │ │ └── Ed25519CredentialProof.java │ │ │ ├── handlers │ │ │ ├── package-info.java │ │ │ └── AppnetHandler.java │ │ │ ├── package-info.java │ │ │ ├── CommonHeaders.java │ │ │ └── AppnetStorageProperties.java │ ├── .gitignore │ ├── build.gradle │ └── README.md ├── testnet.json ├── previewnet.json ├── mainnet.json └── .env.sample ├── docs ├── sdk-javadocs │ ├── resources │ │ ├── x.png │ │ └── glass.png │ ├── type-search-index.zip │ ├── member-search-index.zip │ ├── package-search-index.zip │ ├── jquery │ │ ├── images │ │ │ ├── ui-icons_222222_256x240.png │ │ │ ├── ui-icons_2e83ff_256x240.png │ │ │ ├── ui-icons_454545_256x240.png │ │ │ ├── ui-icons_888888_256x240.png │ │ │ ├── ui-icons_cd0a0a_256x240.png │ │ │ ├── ui-bg_glass_55_fbf9ee_1x400.png │ │ │ ├── ui-bg_glass_65_dadada_1x400.png │ │ │ ├── ui-bg_glass_75_dadada_1x400.png │ │ │ ├── ui-bg_glass_75_e6e6e6_1x400.png │ │ │ ├── ui-bg_glass_95_fef1ec_1x400.png │ │ │ └── ui-bg_highlight-soft_75_cccccc_1x100.png │ │ ├── jszip-utils │ │ │ └── dist │ │ │ │ ├── jszip-utils-ie.min.js │ │ │ │ ├── jszip-utils.min.js │ │ │ │ ├── jszip-utils-ie.js │ │ │ │ └── jszip-utils.js │ │ ├── jquery-ui.structure.min.css │ │ └── jquery-ui.structure.css │ ├── element-list │ ├── package-search-index.js │ ├── overview-summary.html │ ├── type-search-index.js │ ├── deprecated-list.html │ ├── script.js │ └── index.html ├── vc-message.schema.json └── id-network-user-guide.md ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties └── config │ ├── checkstyle │ └── checkstyle-suppressions.xml │ ├── spotbugs │ └── exclude.xml │ ├── codenarc │ └── codenarc.xml │ └── cpd │ └── cpdhtml.xslt ├── settings.gradle ├── src ├── main │ ├── java │ │ └── com │ │ │ └── hedera │ │ │ └── hashgraph │ │ │ └── identity │ │ │ ├── package-info.java │ │ │ ├── utils │ │ │ ├── package-info.java │ │ │ ├── JsonUtils.java │ │ │ ├── Iso8601InstantTypeAdapter.java │ │ │ ├── MirrorNodeAddress.java │ │ │ ├── Validator.java │ │ │ ├── SingleToArrayTypeAdapterFactory.java │ │ │ └── InstantTypeAdapter.java │ │ │ ├── hcs │ │ │ ├── vc │ │ │ │ ├── package-info.java │ │ │ │ ├── CredentialSubject.java │ │ │ │ ├── HcsVcOperation.java │ │ │ │ ├── Issuer.java │ │ │ │ ├── HcsVcDocumentJsonProperties.java │ │ │ │ ├── HcsVcDocumentHashBase.java │ │ │ │ ├── IssuerTypeAdapterFactory.java │ │ │ │ ├── HcsVcTransaction.java │ │ │ │ ├── HcsVcStatusResolver.java │ │ │ │ ├── HcsVcTopicListener.java │ │ │ │ └── HcsVcMessage.java │ │ │ ├── did │ │ │ │ ├── package-info.java │ │ │ │ ├── HcsDidTopicListener.java │ │ │ │ ├── HcsDidRootKey.java │ │ │ │ ├── HcsDidResolver.java │ │ │ │ └── HcsDidTransaction.java │ │ │ ├── package-info.java │ │ │ ├── MessageMode.java │ │ │ ├── InvalidMessageException.java │ │ │ ├── Message.java │ │ │ ├── SerializableMirrorConsensusResponse.java │ │ │ ├── AddressBook.java │ │ │ └── HcsIdentityNetworkBuilder.java │ │ │ ├── DidMethodOperation.java │ │ │ ├── DidDocumentJsonProperties.java │ │ │ ├── DidParser.java │ │ │ ├── HederaDid.java │ │ │ ├── DidSyntax.java │ │ │ └── DidDocumentBase.java │ └── resources │ │ └── did-document-context-w3org-did-v1.1.jsonld └── test │ └── java │ └── com │ └── hedera │ └── hashgraph │ └── identity │ └── hcs │ ├── vc │ ├── DemoVerifiableCredentialDocument.java │ ├── DemoAccessCredential.java │ ├── HcsVcDocumentOperationsTest.java │ └── HcsVcDocumentBaseTest.java │ ├── AesEncryptionUtil.java │ └── did │ └── HcsDidRootKeyTest.java ├── .gitattributes ├── .env.sample ├── testnet.json ├── previewnet.json ├── .github └── ISSUE_TEMPLATE │ ├── question.md │ ├── feature_request.md │ └── bug_report.md ├── mainnet.json ├── Dockerfile ├── .gitignore ├── gradlew.bat ├── gradlew └── CONTRIBUTING.md /examples/appnet-api-server/src/main/resources/.ratpack: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/sdk-javadocs/resources/x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hashgraph/did-sdk-java/HEAD/docs/sdk-javadocs/resources/x.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hashgraph/did-sdk-java/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /docs/sdk-javadocs/resources/glass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hashgraph/did-sdk-java/HEAD/docs/sdk-javadocs/resources/glass.png -------------------------------------------------------------------------------- /docs/sdk-javadocs/type-search-index.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hashgraph/did-sdk-java/HEAD/docs/sdk-javadocs/type-search-index.zip -------------------------------------------------------------------------------- /docs/sdk-javadocs/member-search-index.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hashgraph/did-sdk-java/HEAD/docs/sdk-javadocs/member-search-index.zip -------------------------------------------------------------------------------- /docs/sdk-javadocs/package-search-index.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hashgraph/did-sdk-java/HEAD/docs/sdk-javadocs/package-search-index.zip -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include 'appnet-api-server' 2 | project(":appnet-api-server").projectDir = file("examples/appnet-api-server") 3 | 4 | rootProject.name = 'did-sdk-java' 5 | -------------------------------------------------------------------------------- /docs/sdk-javadocs/jquery/images/ui-icons_222222_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hashgraph/did-sdk-java/HEAD/docs/sdk-javadocs/jquery/images/ui-icons_222222_256x240.png -------------------------------------------------------------------------------- /docs/sdk-javadocs/jquery/images/ui-icons_2e83ff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hashgraph/did-sdk-java/HEAD/docs/sdk-javadocs/jquery/images/ui-icons_2e83ff_256x240.png -------------------------------------------------------------------------------- /docs/sdk-javadocs/jquery/images/ui-icons_454545_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hashgraph/did-sdk-java/HEAD/docs/sdk-javadocs/jquery/images/ui-icons_454545_256x240.png -------------------------------------------------------------------------------- /docs/sdk-javadocs/jquery/images/ui-icons_888888_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hashgraph/did-sdk-java/HEAD/docs/sdk-javadocs/jquery/images/ui-icons_888888_256x240.png -------------------------------------------------------------------------------- /docs/sdk-javadocs/jquery/images/ui-icons_cd0a0a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hashgraph/did-sdk-java/HEAD/docs/sdk-javadocs/jquery/images/ui-icons_cd0a0a_256x240.png -------------------------------------------------------------------------------- /docs/sdk-javadocs/jquery/images/ui-bg_glass_55_fbf9ee_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hashgraph/did-sdk-java/HEAD/docs/sdk-javadocs/jquery/images/ui-bg_glass_55_fbf9ee_1x400.png -------------------------------------------------------------------------------- /docs/sdk-javadocs/jquery/images/ui-bg_glass_65_dadada_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hashgraph/did-sdk-java/HEAD/docs/sdk-javadocs/jquery/images/ui-bg_glass_65_dadada_1x400.png -------------------------------------------------------------------------------- /docs/sdk-javadocs/jquery/images/ui-bg_glass_75_dadada_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hashgraph/did-sdk-java/HEAD/docs/sdk-javadocs/jquery/images/ui-bg_glass_75_dadada_1x400.png -------------------------------------------------------------------------------- /docs/sdk-javadocs/jquery/images/ui-bg_glass_75_e6e6e6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hashgraph/did-sdk-java/HEAD/docs/sdk-javadocs/jquery/images/ui-bg_glass_75_e6e6e6_1x400.png -------------------------------------------------------------------------------- /docs/sdk-javadocs/jquery/images/ui-bg_glass_95_fef1ec_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hashgraph/did-sdk-java/HEAD/docs/sdk-javadocs/jquery/images/ui-bg_glass_95_fef1ec_1x400.png -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * The root package of Hedera Hashgraph Identity framework SDK. 3 | */ 4 | package com.hedera.hashgraph.identity; -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # 2 | # https://help.github.com/articles/dealing-with-line-endings/ 3 | # 4 | # These are explicitly windows files and should use crlf 5 | *.bat text eol=crlf 6 | 7 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/utils/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Various utility classes used by Hedera identity frameworks. 3 | */ 4 | package com.hedera.hashgraph.identity.utils; -------------------------------------------------------------------------------- /docs/sdk-javadocs/jquery/images/ui-bg_highlight-soft_75_cccccc_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hashgraph/did-sdk-java/HEAD/docs/sdk-javadocs/jquery/images/ui-bg_highlight-soft_75_cccccc_1x100.png -------------------------------------------------------------------------------- /.env.sample: -------------------------------------------------------------------------------- 1 | OPERATOR_ID=0.0.xxxx 2 | OPERATOR_KEY=302... 3 | # testnet, previewnet, mainnet 4 | NETWORK=testnet 5 | # hedera, kabuto (note kabuto not available on previewnet) 6 | MIRROR_PROVIDER=hedera 7 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/hcs/vc/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Verifiable Credentials implementation on top of Hedera HCS DID method. 3 | */ 4 | package com.hedera.hashgraph.identity.hcs.vc; -------------------------------------------------------------------------------- /examples/appnet-api-server/src/main/java/com/hedera/hashgraph/identity/hcs/example/appnet/dto/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Data transfer objects. 3 | */ 4 | package com.hedera.hashgraph.identity.hcs.example.appnet.dto; -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/hcs/did/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Hedera DID Method Specification implementation on top of Hedera Consensus Service. 3 | */ 4 | package com.hedera.hashgraph.identity.hcs.did; -------------------------------------------------------------------------------- /docs/sdk-javadocs/element-list: -------------------------------------------------------------------------------- 1 | com.hedera.hashgraph.identity 2 | com.hedera.hashgraph.identity.hcs 3 | com.hedera.hashgraph.identity.hcs.did 4 | com.hedera.hashgraph.identity.hcs.vc 5 | com.hedera.hashgraph.identity.utils 6 | -------------------------------------------------------------------------------- /examples/appnet-api-server/src/main/java/com/hedera/hashgraph/identity/hcs/example/appnet/vc/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Verifiable credential example. 3 | */ 4 | package com.hedera.hashgraph.identity.hcs.example.appnet.vc; -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/hcs/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Hedera Identity framework implementation based on Hedera Consensus Service DID method specification. 3 | */ 4 | package com.hedera.hashgraph.identity.hcs; -------------------------------------------------------------------------------- /examples/appnet-api-server/src/main/java/com/hedera/hashgraph/identity/hcs/example/appnet/handlers/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Appnet REST API request handlers. 3 | */ 4 | package com.hedera.hashgraph.identity.hcs.example.appnet.handlers; -------------------------------------------------------------------------------- /testnet.json: -------------------------------------------------------------------------------- 1 | { 2 | "network": { 3 | "0.0.3" : "0.testnet.hedera.com:50211", 4 | "0.0.4" : "1.testnet.hedera.com:50211", 5 | "0.0.5" : "2.testnet.hedera.com:50211", 6 | "0.0.6" : "3.testnet.hedera.com:50211" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/testnet.json: -------------------------------------------------------------------------------- 1 | { 2 | "network": { 3 | "0.0.3" : "0.testnet.hedera.com:50211", 4 | "0.0.4" : "1.testnet.hedera.com:50211", 5 | "0.0.5" : "2.testnet.hedera.com:50211", 6 | "0.0.6" : "3.testnet.hedera.com:50211" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /previewnet.json: -------------------------------------------------------------------------------- 1 | { 2 | "network": { 3 | "0.0.3" : "0.previewnet.hedera.com:50211", 4 | "0.0.4" : "1.previewnet.hedera.com:50211", 5 | "0.0.5" : "2.previewnet.hedera.com:50211", 6 | "0.0.6" : "3.previewnet.hedera.com:50211" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/previewnet.json: -------------------------------------------------------------------------------- 1 | { 2 | "network": { 3 | "0.0.3" : "0.previewnet.hedera.com:50211", 4 | "0.0.4" : "1.previewnet.hedera.com:50211", 5 | "0.0.5" : "2.previewnet.hedera.com:50211", 6 | "0.0.6" : "3.previewnet.hedera.com:50211" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/appnet-api-server/src/main/java/com/hedera/hashgraph/identity/hcs/example/appnet/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Example appnet application that utilizes HCS Identity SDK for DID and VC handling. 3 | */ 4 | package com.hedera.hashgraph.identity.hcs.example.appnet; -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Aug 21 09:20:18 CEST 2020 2 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.0-all.zip 3 | distributionBase=GRADLE_USER_HOME 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradle/config/checkstyle/checkstyle-suppressions.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /docs/sdk-javadocs/package-search-index.js: -------------------------------------------------------------------------------- 1 | packageSearchIndex = [{"l":"All Packages","url":"allpackages-index.html"},{"l":"com.hedera.hashgraph.identity"},{"l":"com.hedera.hashgraph.identity.hcs"},{"l":"com.hedera.hashgraph.identity.hcs.did"},{"l":"com.hedera.hashgraph.identity.hcs.vc"},{"l":"com.hedera.hashgraph.identity.utils"}] -------------------------------------------------------------------------------- /examples/appnet-api-server/src/main/resources/simplelogger.properties: -------------------------------------------------------------------------------- 1 | org.slf4j.simpleLogger.defaultLogLevel=error 2 | org.slf4j.simpleLogger.log.com.hedera.hashgraph.identity.hcs.example=info 3 | org.slf4j.simpleLogger.showDateTime=true 4 | org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS 5 | org.slf4j.simpleLogger.showThreadName=false 6 | org.slf4j.simpleLogger.showShortLogName=true -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Get an answer to your question 4 | title: '' 5 | labels: question 6 | assignees: '' 7 | 8 | --- 9 | 10 | Please ask questions you have in our [Discord]https://hedera.com/discord) group first. You can also search GitHub to see if the issue has been reported already. 11 | 12 | Otherwise, please feel free to ask your question here. 13 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/DidMethodOperation.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | /** 6 | * The operation type to be performed on the DID document. 7 | */ 8 | public enum DidMethodOperation { 9 | @SerializedName("create") 10 | CREATE, 11 | 12 | @SerializedName("update") 13 | UPDATE, 14 | 15 | @SerializedName("delete") 16 | DELETE, 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/hcs/MessageMode.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | /** 6 | * The mode in which HCS message with DID document is submitted. 7 | * It can be a plain with document encoded with Base64 or encrypted with custom encryption algorithm. 8 | */ 9 | public enum MessageMode { 10 | @SerializedName("plain") 11 | PLAIN, 12 | 13 | @SerializedName("encrypted") 14 | ENCRYPTED 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/hcs/vc/CredentialSubject.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs.vc; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import com.google.gson.annotations.SerializedName; 5 | 6 | public abstract class CredentialSubject { 7 | @Expose(serialize = true, deserialize = true) 8 | @SerializedName(HcsVcDocumentJsonProperties.ID) 9 | protected String id; 10 | 11 | public String getId() { 12 | return id; 13 | } 14 | 15 | public void setId(final String id) { 16 | this.id = id; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /mainnet.json: -------------------------------------------------------------------------------- 1 | { 2 | "network": { 3 | "0.0.3": "35.237.200.180:50211", 4 | "0.0.4": "35.186.191.247:50211", 5 | "0.0.5": "35.192.2.25:50211", 6 | "0.0.6": "35.199.161.108:50211", 7 | "0.0.7": "35.203.82.240:50211", 8 | "0.0.8": "35.236.5.219:50211", 9 | "0.0.9": "35.197.192.225:50211", 10 | "0.0.10": "35.242.233.154:50211", 11 | "0.0.11": "35.240.118.96:50211", 12 | "0.0.12": "35.204.86.32:50211", 13 | "0.0.13": "51.140.102.228:50211", 14 | "0.0.14": "52.8.21.141:50211", 15 | "0.0.15": "40.114.107.85:50211" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/appnet-api-server/src/main/java/com/hedera/hashgraph/identity/hcs/example/appnet/dto/DidResolutionRequest.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs.example.appnet.dto; 2 | 3 | import com.google.gson.annotations.Expose; 4 | 5 | /** 6 | * DTO that represents a request body sent from clients asking for DID resolution. 7 | */ 8 | public class DidResolutionRequest { 9 | 10 | @Expose 11 | private String did; 12 | 13 | public String getDid() { 14 | return did; 15 | } 16 | 17 | public void setDid(final String did) { 18 | this.did = did; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/mainnet.json: -------------------------------------------------------------------------------- 1 | { 2 | "network": { 3 | "0.0.3": "35.237.200.180:50211", 4 | "0.0.4": "35.186.191.247:50211", 5 | "0.0.5": "35.192.2.25:50211", 6 | "0.0.6": "35.199.161.108:50211", 7 | "0.0.7": "35.203.82.240:50211", 8 | "0.0.8": "35.236.5.219:50211", 9 | "0.0.9": "35.197.192.225:50211", 10 | "0.0.10": "35.242.233.154:50211", 11 | "0.0.11": "35.240.118.96:50211", 12 | "0.0.12": "35.204.86.32:50211", 13 | "0.0.13": "51.140.102.228:50211", 14 | "0.0.14": "52.8.21.141:50211", 15 | "0.0.15": "40.114.107.85:50211" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/hcs/vc/HcsVcOperation.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs.vc; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import java.io.Serializable; 5 | 6 | /** 7 | * The operation type to be performed on the DID document. 8 | */ 9 | public enum HcsVcOperation implements Serializable { 10 | @SerializedName("issue") 11 | ISSUE, 12 | 13 | @SerializedName("revoke") 14 | REVOKE, 15 | 16 | @SerializedName("suspend") 17 | SUSPEND, 18 | 19 | @SerializedName("resume") 20 | RESUME; 21 | } 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Problem** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Solution** 14 | A clear and concise description of what you want to happen to address the problem. 15 | 16 | **Alternatives** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional Context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /src/test/java/com/hedera/hashgraph/identity/hcs/vc/DemoVerifiableCredentialDocument.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs.vc; 2 | 3 | import com.google.gson.annotations.Expose; 4 | 5 | /** 6 | * Custom VC document for tests. 7 | */ 8 | class DemoVerifiableCredentialDocument extends HcsVcDocumentBase { 9 | 10 | @Expose(serialize = true, deserialize = true) 11 | private String customProperty; 12 | 13 | public String getCustomProperty() { 14 | return customProperty; 15 | } 16 | 17 | public void setCustomProperty(String customProperty) { 18 | this.customProperty = customProperty; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/appnet-api-server/src/main/java/com/hedera/hashgraph/identity/hcs/example/appnet/vc/CredentialSchema.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs.example.appnet.vc; 2 | 3 | import com.google.gson.annotations.Expose; 4 | 5 | /** 6 | * An object representing a simple credential schema reference in a verifiable document. 7 | */ 8 | public class CredentialSchema { 9 | @Expose 10 | public String id; 11 | 12 | @Expose 13 | public String type; 14 | 15 | public CredentialSchema(final String id, final String type) { 16 | this.type = type; 17 | this.id = id; 18 | } 19 | 20 | public String getId() { 21 | return id; 22 | } 23 | 24 | public String getType() { 25 | return type; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /docs/sdk-javadocs/overview-summary.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | did-sdk-java 1.0.0 API 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 |
17 | 20 |

index.html

21 |
22 | 23 | 24 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/hcs/vc/Issuer.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs.vc; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import com.google.gson.annotations.SerializedName; 5 | 6 | public class Issuer { 7 | @Expose(serialize = true, deserialize = true) 8 | @SerializedName(HcsVcDocumentJsonProperties.ID) 9 | protected String id; 10 | 11 | @Expose(serialize = true, deserialize = true) 12 | protected String name; 13 | 14 | public Issuer(final String id, final String name) { 15 | this.id = id; 16 | this.name = name; 17 | } 18 | 19 | public Issuer(final String id) { 20 | this(id, null); 21 | } 22 | 23 | public String getId() { 24 | return id; 25 | } 26 | 27 | public String getName() { 28 | return name; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/hcs/InvalidMessageException.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs; 2 | 3 | import com.hedera.hashgraph.sdk.TopicMessage; 4 | 5 | /** 6 | * Exception thrown when the message coming from the mirror node fails validation. 7 | */ 8 | public class InvalidMessageException extends Exception { 9 | private static final long serialVersionUID = -2493756740875603220L; 10 | private TopicMessage mirrorResponse; 11 | 12 | public InvalidMessageException(final String reason) { 13 | super(reason); 14 | } 15 | 16 | public InvalidMessageException(final TopicMessage response, final String reason) { 17 | super(reason); 18 | this.mirrorResponse = response; 19 | } 20 | 21 | public TopicMessage getMirrorResponse() { 22 | return mirrorResponse; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/DidDocumentJsonProperties.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity; 2 | 3 | /** 4 | * Key property names in DID document standard. 5 | */ 6 | public final class DidDocumentJsonProperties { 7 | public static final String CONTEXT = "@context"; 8 | public static final String ID = "id"; 9 | public static final String AUTHENTICATION = "authentication"; 10 | public static final String PUBLIC_KEY = "publicKey"; 11 | public static final String SERVICE = "service"; 12 | public static final String CREATED = "created"; 13 | public static final String UPDATED = "updated"; 14 | public static final String PROOF = "proof"; 15 | 16 | /** 17 | * This class is not to be instantiated. 18 | */ 19 | private DidDocumentJsonProperties() { 20 | // Empty on purpose. 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/utils/JsonUtils.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.utils; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import org.threeten.bp.Instant; 6 | 7 | /** 8 | * JSON utilities for Hedera identity. 9 | */ 10 | public final class JsonUtils { 11 | 12 | /** 13 | * This is a utility class, never to be instantiated. 14 | */ 15 | private JsonUtils() { 16 | // Empty on purpose. 17 | } 18 | 19 | /** 20 | * Builds {@link Gson} instance with default configuration for Hedera identity. 21 | * 22 | * @return {@link Gson} instance. 23 | */ 24 | public static Gson getGson() { 25 | return new GsonBuilder() 26 | .disableHtmlEscaping() 27 | .excludeFieldsWithoutExposeAnnotation() 28 | .registerTypeAdapter(Instant.class, Iso8601InstantTypeAdapter.getInstance()) 29 | .create(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/hcs/Message.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import com.hedera.hashgraph.identity.utils.JsonUtils; 5 | import java.io.Serializable; 6 | import org.threeten.bp.Instant; 7 | 8 | public abstract class Message implements Serializable { 9 | 10 | @Expose(serialize = false, deserialize = false) 11 | private static final long serialVersionUID = 1L; 12 | 13 | @Expose(serialize = true, deserialize = true) 14 | protected final Instant timestamp; 15 | 16 | protected Message() { 17 | this.timestamp = Instant.now(); 18 | } 19 | 20 | public Instant getTimestamp() { 21 | return timestamp; 22 | } 23 | 24 | /** 25 | * Converts this message into JSON string. 26 | * 27 | * @return The JSON representation of this message. 28 | */ 29 | public String toJson() { 30 | return JsonUtils.getGson().toJson(this); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/appnet-api-server/src/main/java/com/hedera/hashgraph/identity/hcs/example/appnet/handlers/AppnetHandler.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs.example.appnet.handlers; 2 | 3 | import com.hedera.hashgraph.identity.hcs.HcsIdentityNetwork; 4 | import com.hedera.hashgraph.identity.hcs.example.appnet.AppnetStorage; 5 | 6 | /** 7 | * A base class for all appnet handlers that require access to local storage and identity network. 8 | */ 9 | public abstract class AppnetHandler { 10 | 11 | protected HcsIdentityNetwork identityNetwork; 12 | protected AppnetStorage storage; 13 | 14 | /** 15 | * Creates a new appnet request handler instance. 16 | * 17 | * @param identityNetwork The Hedera Identity network. 18 | * @param storage The appnet's local storage. 19 | */ 20 | protected AppnetHandler(final HcsIdentityNetwork identityNetwork, final AppnetStorage storage) { 21 | this.identityNetwork = identityNetwork; 22 | this.storage = storage; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # build in a temporary container 3 | # 4 | 5 | FROM adoptopenjdk:12-jdk-hotspot AS build 6 | 7 | COPY ./ /opt/hedera-did 8 | 9 | WORKDIR /opt/hedera-did 10 | 11 | #RUN ./gradlew --no-daemon 12 | 13 | RUN ./gradlew --no-daemon assemble 14 | 15 | # 16 | # run in a fresh container, copying files from build container above 17 | # 18 | 19 | FROM adoptopenjdk:12-jre-hotspot 20 | 21 | # make a place to put our built JAR and copy it to there 22 | WORKDIR /srv 23 | COPY --from=build /opt/hedera-did/examples/appnet-api-server/build/libs/appnet-api-server.jar /srv/appnet-api-server.jar 24 | COPY --from=build /opt/hedera-did/examples/.env /srv/.env 25 | COPY --from=build /opt/hedera-did/previewnet.json /srv/previewnet.json 26 | COPY --from=build /opt/hedera-did/testnet.json /srv/testnet.json 27 | COPY --from=build /opt/hedera-did/mainnet.json /srv/mainnet.json 28 | 29 | VOLUME /srv/data 30 | 31 | # run the micro service 32 | CMD java "-jar" "appnet-api-server.jar" 33 | 34 | EXPOSE 5050 35 | -------------------------------------------------------------------------------- /examples/appnet-api-server/src/main/java/com/hedera/hashgraph/identity/hcs/example/appnet/CommonHeaders.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs.example.appnet; 2 | 3 | import com.hedera.hashgraph.identity.hcs.example.appnet.handlers.DemoHandler; 4 | import ratpack.handling.Context; 5 | import ratpack.handling.Handler; 6 | import ratpack.http.MutableHeaders; 7 | 8 | /** 9 | * A helper class that configures REST server's response headers. 10 | */ 11 | public class CommonHeaders implements Handler { 12 | 13 | @Override 14 | public void handle(final Context ctx) throws Exception { 15 | ctx.getResponse().noCompress(); 16 | ctx.getResponse().contentType("application/json"); 17 | MutableHeaders headers = ctx.getResponse().getHeaders(); 18 | headers.set("Access-Control-Allow-Origin", "*"); 19 | headers.set("Access-Control-Allow-Headers", DemoHandler.HEADER_PRIVATE_KEY + ", Content-Type"); 20 | headers.set("Access-Control-Allow-Methods", "*"); 21 | ctx.next(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/hcs/vc/HcsVcDocumentJsonProperties.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs.vc; 2 | 3 | /** 4 | * Key property names in VC document standard. 5 | */ 6 | public final class HcsVcDocumentJsonProperties { 7 | public static final String CONTEXT = "@context"; 8 | public static final String FIRST_CONTEXT_ENTRY = "https://www.w3.org/2018/credentials/v1"; 9 | 10 | public static final String ID = "id"; 11 | public static final String CREDENTIAL_SUBJECT = "credentialSubject"; 12 | 13 | public static final String TYPE = "type"; 14 | public static final String VERIFIABLE_CREDENTIAL_TYPE = "VerifiableCredential"; 15 | 16 | public static final String ISSUER = "issuer"; 17 | public static final String ISSUANCE_DATE = "issuanceDate"; 18 | public static final String CREDENTIAL_STATUS = "credentialStatus"; 19 | public static final String PROOF = "proof"; 20 | 21 | /** 22 | * This class is not to be instantiated. 23 | */ 24 | private HcsVcDocumentJsonProperties() { 25 | // Empty on purpose. 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | 15 | 16 | **Detailed Description** 17 | A clear and concise description of what the bug is. 18 | 19 | **Actual Behavior** 20 | Steps to reproduce the behavior: 21 | 1. Go to '...' 22 | 2. Click on '....' 23 | 3. Scroll down to '....' 24 | 25 | **Expected Behavior** 26 | A clear and concise description of what you expected to happen. 27 | 28 | **Environment:** 29 | - Java: [e.g. OpenJDK 11.0.4] 30 | - Node: [e.g. v12.9.1-x86] 31 | - OS: [e.g. Ubuntu 18.04] 32 | - Version: [e.g. 1.0.1] 33 | 34 | **Additional Context** 35 | Add any other context about the problem here. Attach any logs here, if applicable. 36 | -------------------------------------------------------------------------------- /gradle/config/spotbugs/exclude.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /examples/.env.sample: -------------------------------------------------------------------------------- 1 | OPERATOR_ID=0.0.xxxx 2 | OPERATOR_KEY=302... 3 | # testnet, previewnet, mainnet 4 | NETWORK=testnet 5 | # hedera, kabuto (note kabuto not available on previewnet) 6 | MIRROR_PROVIDER=hedera 7 | 8 | # how frequently should DiDs be persisted to file 9 | DID_PERSIST_INTERVAL=1 10 | # how frequently should VCs be persisted to file 11 | VC_PERSIST_INTERVAL=1 12 | 13 | # Topic and File IDs 14 | # If neither of the below is provided, a new Hedera file containing the address book and two topic IDs will be created. 15 | # If the file id alone is provided, it will be used to fetch the VC and DiD topic IDs from the Hedera Network 16 | #EXISTING_ADDRESS_BOOK_FILE_ID=0.0.19087 17 | # If the below is provided, topic IDs won't be fetched from the network, saving the cost of a file query 18 | #EXISTING_ADDRESS_BOOK_JSON={"appnetName":"Example appnet using Hedera Identity SDK","didTopicId":"0.0.19085","vcTopicId":"0.0.19086","appnetDidServers":["http://localhost:5050/"]} 19 | 20 | # how frequently should DiDs be persisted to file 21 | DID_PERSIST_INTERVAL=10 22 | # how frequently should VCs be persisted to file 23 | VC_PERSIST_INTERVAL=10 24 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/DidParser.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity; 2 | 3 | import com.hedera.hashgraph.identity.hcs.did.HcsDid; 4 | 5 | /** 6 | * DID parser for Hedera DIDs. 7 | */ 8 | public final class DidParser { 9 | 10 | /** 11 | * Private default constructor. 12 | */ 13 | private DidParser() { 14 | // This constructor is intentionally empty. Nothing special is needed here. 15 | } 16 | 17 | /** 18 | * Parses the given DID string into it's corresponding Hedera DID object. 19 | * 20 | * @param didString DID string. 21 | * @return {@link HederaDid} instance. 22 | */ 23 | public static HederaDid parse(final String didString) { 24 | final int methodIndex = DidSyntax.DID_PREFIX.length() + 1; 25 | if (didString == null || didString.length() <= methodIndex) { 26 | throw new IllegalArgumentException("DID string cannot be null"); 27 | } 28 | 29 | if (didString.startsWith(HcsDid.DID_METHOD.toString() + DidSyntax.DID_METHOD_SEPARATOR, methodIndex)) { 30 | return HcsDid.fromString(didString); 31 | } else { 32 | throw new IllegalArgumentException("DID string is invalid."); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/HederaDid.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity; 2 | 3 | import com.hedera.hashgraph.identity.DidSyntax.Method; 4 | 5 | /** 6 | * Hedera Decentralized Identifier. 7 | */ 8 | public interface HederaDid { 9 | 10 | /** 11 | * Parses the given DID string into a {@link HederaDid} object. 12 | * 13 | * @param didString The DID. 14 | * @return {@link HederaDid} object. 15 | */ 16 | static HederaDid fromString(final String didString) { 17 | return DidParser.parse(didString); 18 | } 19 | 20 | /** 21 | * Converts the {@link HederaDid} object into a DID string. 22 | * 23 | * @return The DID string. 24 | */ 25 | String toDid(); 26 | 27 | /** 28 | * Generates a basic DID document for this identity. 29 | * 30 | * @return The DID document as a {@link DidDocumentBase}. 31 | */ 32 | DidDocumentBase generateDidDocument(); 33 | 34 | /** 35 | * Returns a {@link String} for which the DID is created. 36 | * 37 | * @return A Hedera network for which the DID is created. 38 | */ 39 | String getNetwork(); 40 | 41 | /** 42 | * Returns a DID method of this DID. 43 | * 44 | * @return A DID method. 45 | */ 46 | Method getMethod(); 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/hcs/vc/HcsVcDocumentHashBase.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs.vc; 2 | 3 | import com.google.common.collect.Lists; 4 | import com.google.gson.annotations.Expose; 5 | import com.google.gson.annotations.JsonAdapter; 6 | import com.google.gson.annotations.SerializedName; 7 | import java.util.List; 8 | import org.threeten.bp.Instant; 9 | 10 | /** 11 | * The part of the VC document that is used for hash calculation. 12 | */ 13 | abstract class HcsVcDocumentHashBase { 14 | @Expose(serialize = true, deserialize = true) 15 | @SerializedName(HcsVcDocumentJsonProperties.ID) 16 | protected String id; 17 | 18 | @Expose(serialize = true, deserialize = true) 19 | @SerializedName(HcsVcDocumentJsonProperties.TYPE) 20 | protected List type; 21 | 22 | @Expose(serialize = true, deserialize = true) 23 | @SerializedName(HcsVcDocumentJsonProperties.ISSUER) 24 | @JsonAdapter(IssuerTypeAdapterFactory.class) 25 | protected Issuer issuer; 26 | 27 | @Expose(serialize = true, deserialize = true) 28 | @SerializedName(HcsVcDocumentJsonProperties.ISSUANCE_DATE) 29 | protected Instant issuanceDate; 30 | 31 | /** 32 | * Creates a new VC document instance. 33 | */ 34 | protected HcsVcDocumentHashBase() { 35 | this.type = Lists.newArrayList(HcsVcDocumentJsonProperties.VERIFIABLE_CREDENTIAL_TYPE); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/com/hedera/hashgraph/identity/hcs/vc/DemoAccessCredential.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs.vc; 2 | 3 | import com.google.gson.annotations.Expose; 4 | 5 | /** 6 | * Example Credential. 7 | */ 8 | class DemoAccessCredential extends CredentialSubject { 9 | public static final String ACCESS_GRANTED = "granted"; 10 | public static final String ACCESS_DENIED = "denied"; 11 | 12 | @Expose 13 | private String blueLevel; 14 | @Expose 15 | private String greenLevel; 16 | @Expose 17 | private String redLevel; 18 | 19 | /** 20 | * Creates a new credential instance. 21 | * 22 | * @param did Credential Subject DID. 23 | * @param blue Access to blue level granted or denied. 24 | * @param green Access to green level granted or denied. 25 | * @param red Access to red level granted or denied. 26 | */ 27 | public DemoAccessCredential(String did, boolean blue, boolean green, boolean red) { 28 | this.id = did; 29 | this.blueLevel = blue ? ACCESS_GRANTED : ACCESS_DENIED; 30 | this.greenLevel = green ? ACCESS_GRANTED : ACCESS_DENIED; 31 | this.redLevel = red ? ACCESS_GRANTED : ACCESS_DENIED; 32 | } 33 | 34 | public String getBlueLevel() { 35 | return blueLevel; 36 | } 37 | 38 | public String getGreenLevel() { 39 | return greenLevel; 40 | } 41 | 42 | public String getRedLevel() { 43 | return redLevel; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/hcs/SerializableMirrorConsensusResponse.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs; 2 | 3 | import com.hedera.hashgraph.sdk.TopicMessage; 4 | import java.io.Serializable; 5 | import java.util.Arrays; 6 | import org.threeten.bp.Instant; 7 | 8 | /** 9 | * This class is a serializable copy of the MirrorConsensusResponse class from the Java SDK. 10 | */ 11 | public class SerializableMirrorConsensusResponse implements Serializable { 12 | 13 | private static final long serialVersionUID = 1L; 14 | 15 | public final Instant consensusTimestamp; 16 | 17 | public final byte[] message; 18 | 19 | public final byte[] runningHash; 20 | 21 | public final long sequenceNumber; 22 | 23 | SerializableMirrorConsensusResponse(final TopicMessage response) { 24 | this.consensusTimestamp = response.consensusTimestamp; 25 | this.message = response.contents; 26 | this.runningHash = response.runningHash; 27 | this.sequenceNumber = response.sequenceNumber; 28 | } 29 | 30 | // TODO: Use a standard debug serialization 31 | @Override 32 | public String toString() { 33 | return "ConsensusMessage{" 34 | + "consensusTimestamp=" + consensusTimestamp 35 | + ", message=" + Arrays.toString(message) 36 | + ", runningHash=" + Arrays.toString(runningHash) 37 | + ", sequenceNumber=" + sequenceNumber 38 | + '}'; 39 | } 40 | } 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/utils/Iso8601InstantTypeAdapter.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.utils; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import org.threeten.bp.Instant; 5 | import org.threeten.bp.format.DateTimeFormatter; 6 | 7 | 8 | /** 9 | * Gson type adapter for serialization/deserialization between {@link Instant} and ISO 8601 date format. 10 | */ 11 | public final class Iso8601InstantTypeAdapter extends InstantTypeAdapter { 12 | 13 | public static final DateTimeFormatter DEFAULT_OUTPUT_FORMATTER = DateTimeFormatter.ISO_INSTANT; 14 | 15 | public static final ImmutableList DEFAULT_INPUT_PARSERS = ImmutableList.of( 16 | DateTimeFormatter.ISO_INSTANT, 17 | DateTimeFormatter.ISO_OFFSET_DATE_TIME, 18 | DateTimeFormatter.ISO_DATE_TIME, 19 | DateTimeFormatter.ISO_LOCAL_DATE_TIME); 20 | 21 | private static final Iso8601InstantTypeAdapter DEFAULT_INSTANCE = new Iso8601InstantTypeAdapter(); 22 | 23 | public Iso8601InstantTypeAdapter() { 24 | super(DEFAULT_OUTPUT_FORMATTER, DEFAULT_INPUT_PARSERS); 25 | } 26 | 27 | /** 28 | * Returns the statically defined instance constructed with the default formatter. 29 | * 30 | * @return the instance 31 | * @see #DEFAULT_OUTPUT_FORMATTER 32 | * @see #DEFAULT_INPUT_PARSERS 33 | */ 34 | public static Iso8601InstantTypeAdapter getInstance() { 35 | return DEFAULT_INSTANCE; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /examples/appnet-api-server/src/main/java/com/hedera/hashgraph/identity/hcs/example/appnet/dto/ErrorResponse.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs.example.appnet.dto; 2 | 3 | import java.io.PrintWriter; 4 | import java.io.StringWriter; 5 | 6 | /** 7 | * DTO that represents a response body in case request processing failed. 8 | */ 9 | public class ErrorResponse { 10 | 11 | private String errorMessage; 12 | private String errorDetails; 13 | 14 | /** 15 | * Creates a new response body object. 16 | * 17 | * @param message Error message. 18 | */ 19 | public ErrorResponse(final String message) { 20 | this.errorMessage = message; 21 | } 22 | 23 | /** 24 | * Creates a new response body object. 25 | * 26 | * @param error Exception object, error message will be extracted from this instance. 27 | */ 28 | public ErrorResponse(final Throwable error) { 29 | this(error.getMessage(), error); 30 | } 31 | 32 | /** 33 | * Creates a new response body object. 34 | * 35 | * @param message Error message. 36 | * @param error Exception object to provide more details about the error. 37 | */ 38 | public ErrorResponse(final String message, final Throwable error) { 39 | this.errorMessage = message; 40 | 41 | StringWriter sw = new StringWriter(); 42 | PrintWriter pw = new PrintWriter(sw); 43 | error.printStackTrace(pw); 44 | 45 | this.errorDetails = sw.toString(); 46 | pw.close(); 47 | } 48 | 49 | public String getErrorMessage() { 50 | return errorMessage; 51 | } 52 | 53 | public String getErrorDetails() { 54 | return errorDetails; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /examples/appnet-api-server/src/main/java/com/hedera/hashgraph/identity/hcs/example/appnet/dto/DrivingLicenseRequest.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs.example.appnet.dto; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import java.util.List; 5 | 6 | /** 7 | * DTO that represents a request body sent from clients to generate new driving license. 8 | */ 9 | public class DrivingLicenseRequest { 10 | @Expose 11 | private String issuer; 12 | 13 | @Expose 14 | private String owner; 15 | 16 | @Expose 17 | private String firstName; 18 | 19 | @Expose 20 | private String lastName; 21 | 22 | @Expose 23 | private List drivingLicenseCategories; 24 | 25 | public String getIssuer() { 26 | return issuer; 27 | } 28 | 29 | public void setIssuer(final String issuer) { 30 | this.issuer = issuer; 31 | } 32 | 33 | public String getOwner() { 34 | return owner; 35 | } 36 | 37 | public void setOwner(final String owner) { 38 | this.owner = owner; 39 | } 40 | 41 | public String getFirstName() { 42 | return firstName; 43 | } 44 | 45 | public void setFirstName(final String firstName) { 46 | this.firstName = firstName; 47 | } 48 | 49 | public String getLastName() { 50 | return lastName; 51 | } 52 | 53 | public void setLastName(final String lastName) { 54 | this.lastName = lastName; 55 | } 56 | 57 | public List getDrivingLicenseCategories() { 58 | return drivingLicenseCategories; 59 | } 60 | 61 | public void setDrivingLicenseCategories(final List drivingLicenseCategories) { 62 | this.drivingLicenseCategories = drivingLicenseCategories; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /docs/sdk-javadocs/jquery/jszip-utils/dist/jszip-utils-ie.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | JSZipUtils - A collection of cross-browser utilities to go along with JSZip. 4 | 5 | 6 | (c) 2014 Stuart Knightley, David Duponchel 7 | Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip-utils/master/LICENSE.markdown. 8 | 9 | */ 10 | !function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};b[g][0].call(j.exports,function(a){var c=b[g][1][a];return e(c?c:a)},j,j.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g\r\n";document.write(b),a.JSZipUtils._getBinaryFromXHR=function(a){for(var b=a.responseBody,c={},d=0;256>d;d++)for(var e=0;256>e;e++)c[String.fromCharCode(d+(e<<8))]=String.fromCharCode(d)+String.fromCharCode(e);var f=IEBinaryToArray_ByteStr(b),g=IEBinaryToArray_ByteStr_Last(b);return f.replace(/[\s\S]/g,function(a){return c[a]})+g}},{}]},{},[1]); 11 | -------------------------------------------------------------------------------- /examples/appnet-api-server/.gitignore: -------------------------------------------------------------------------------- 1 | ### Java ### 2 | *.class 3 | 4 | # Package Files # 5 | *.jar 6 | *.war 7 | *.ear 8 | 9 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 10 | hs_err_pid* 11 | 12 | 13 | ### Gradle ### 14 | .gradle 15 | 16 | ### Output directories ### 17 | /build/ 18 | /bin/ 19 | /target/ 20 | 21 | ### IDE Settings ### 22 | .settings 23 | 24 | # Ignore Gradle GUI config 25 | gradle-app.setting 26 | 27 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 28 | !gradle-wrapper.jar 29 | 30 | # Cache of project 31 | .gradletasknamecache 32 | 33 | # Environment variables file 34 | .env 35 | .env.sample 36 | 37 | .metadata 38 | bin/ 39 | tmp/ 40 | *.tmp 41 | *.bak 42 | *.swp 43 | *~.nib 44 | local.properties 45 | .settings/ 46 | .loadpath 47 | .recommenders 48 | 49 | # External tool builders 50 | .externalToolBuilders/ 51 | 52 | # Locally stored "Eclipse launch configurations" 53 | *.launch 54 | 55 | # PyDev specific (Python IDE for Eclipse) 56 | *.pydevproject 57 | 58 | # CDT-specific (C/C++ Development Tooling) 59 | .cproject 60 | 61 | # CDT- autotools 62 | .autotools 63 | 64 | # Java annotation processor (APT) 65 | .factorypath 66 | 67 | # PDT-specific (PHP Development Tools) 68 | .buildpath 69 | 70 | # sbteclipse plugin 71 | .target 72 | 73 | # Tern plugin 74 | .tern-project 75 | 76 | # TeXlipse plugin 77 | .texlipse 78 | 79 | # STS (Spring Tool Suite) 80 | .springBeans 81 | 82 | # Code Recommenders 83 | .recommenders/ 84 | 85 | # Annotation Processing 86 | .apt_generated/ 87 | .apt_generated_test/ 88 | 89 | # Scala IDE specific (Scala & Java development for Eclipse) 90 | .cache-main 91 | .scala_dependencies 92 | .worksheet 93 | 94 | .project 95 | .checkstyle 96 | .classpath -------------------------------------------------------------------------------- /docs/sdk-javadocs/jquery/jquery-ui.structure.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.12.1 - 2018-12-06 2 | * http://jqueryui.com 3 | * Copyright jQuery Foundation and other contributors; Licensed MIT */ 4 | 5 | .ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important;pointer-events:none}.ui-icon{display:inline-block;vertical-align:middle;margin-top:-.25em;position:relative;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-icon-block{left:50%;margin-left:-8px;display:block}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:0}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{margin:0;cursor:pointer;list-style-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")}.ui-menu .ui-menu-item-wrapper{position:relative;padding:3px 1em 3px .4em}.ui-menu .ui-menu-divider{margin:5px 0;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-state-focus,.ui-menu .ui-state-active{margin:-1px}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item-wrapper{padding-left:2em}.ui-menu .ui-icon{position:absolute;top:0;bottom:0;left:.2em;margin:auto 0}.ui-menu .ui-menu-icon{left:auto;right:0} -------------------------------------------------------------------------------- /examples/appnet-api-server/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | } 5 | dependencies { 6 | classpath group: 'io.ratpack', name: 'ratpack-gradle', version: '1.8.0' 7 | } 8 | } 9 | 10 | plugins { 11 | id "java" 12 | id "application" 13 | id "com.github.johnrengelman.shadow" 14 | } 15 | 16 | apply plugin: "io.ratpack.ratpack-java" 17 | apply plugin: "ru.vyarus.quality" 18 | 19 | 20 | repositories { 21 | jcenter() 22 | mavenLocal() 23 | mavenCentral() 24 | } 25 | 26 | dependencies { 27 | implementation rootProject 28 | implementation group: 'com.hedera.hashgraph', name: 'sdk-jdk7', version: '2.0.5' 29 | implementation 'com.google.code.gson:gson:2.8.6' 30 | implementation group: 'org.bitcoinj', name: 'bitcoinj-core', version: '0.15.6' 31 | implementation group: 'io.github.cdimascio', name: 'java-dotenv', version: '5.1.4' 32 | implementation group: 'io.grpc', name: 'grpc-okhttp', version: '1.28.0' 33 | implementation group: 'com.github.jsonld-java', name: 'jsonld-java', version: '0.12.3' 34 | implementation group: 'com.nimbusds', name: 'nimbus-jose-jwt', version: '8.14.1' 35 | implementation group: 'org.slf4j', name: 'slf4j-simple', version: '1.7.30' 36 | } 37 | 38 | mainClassName = "com.hedera.hashgraph.identity.hcs.example.appnet.AppnetServer" 39 | 40 | jar { 41 | manifest { 42 | attributes "Main-Class": mainClassName 43 | } 44 | } 45 | 46 | shadowJar { 47 | // by default, this plugin generates a new `-all` jar 48 | // the empty string makes it override the non-all jar 49 | archiveClassifier.set('') 50 | } 51 | 52 | quality { 53 | checkstyleVersion = '8.29' 54 | spotbugsVersion = '4.0.1' 55 | checkstyle = true 56 | spotbugs = true 57 | pmd = true 58 | } 59 | -------------------------------------------------------------------------------- /docs/sdk-javadocs/jquery/jszip-utils/dist/jszip-utils.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | JSZipUtils - A collection of cross-browser utilities to go along with JSZip. 4 | 5 | 6 | (c) 2014 Stuart Knightley, David Duponchel 7 | Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip-utils/master/LICENSE.markdown. 8 | 9 | */ 10 | !function(a){"object"==typeof exports?module.exports=a():"function"==typeof define&&define.amd?define(a):"undefined"!=typeof window?window.JSZipUtils=a():"undefined"!=typeof global?global.JSZipUtils=a():"undefined"!=typeof self&&(self.JSZipUtils=a())}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};b[g][0].call(j.exports,function(a){var c=b[g][1][a];return e(c?c:a)},j,j.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g TypeAdapter create(final Gson gson, final TypeToken type) { 16 | if (type.getRawType() != Issuer.class) { 17 | return null; 18 | } 19 | return (TypeAdapter) new IssuerTypeAdapter(gson.getAdapter(Issuer.class), gson.getAdapter(String.class)); 20 | } 21 | 22 | private static final class IssuerTypeAdapter extends TypeAdapter { 23 | private final TypeAdapter delegateAdapter; 24 | private final TypeAdapter elementAdapter; 25 | 26 | IssuerTypeAdapter(final TypeAdapter delegateAdapter, final TypeAdapter elementAdapter) { 27 | this.delegateAdapter = delegateAdapter; 28 | this.elementAdapter = elementAdapter; 29 | } 30 | 31 | @Override 32 | public Issuer read(final JsonReader reader) throws IOException { 33 | if (reader.peek() == JsonToken.STRING) { 34 | return new Issuer(elementAdapter.read(reader)); 35 | } 36 | return delegateAdapter.read(reader); 37 | } 38 | 39 | @Override 40 | public void write(final JsonWriter writer, final Issuer value) 41 | throws IOException { 42 | if (value != null && value.getName() == null) { 43 | elementAdapter.write(writer, value.getId()); 44 | } else { 45 | delegateAdapter.write(writer, value); 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Java ### 2 | *.class 3 | 4 | # Package Files # 5 | *.jar 6 | *.war 7 | *.ear 8 | 9 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 10 | hs_err_pid* 11 | 12 | 13 | ### Gradle ### 14 | .gradle 15 | 16 | ### Output directories ### 17 | /build/ 18 | /bin/ 19 | /target/ 20 | 21 | ### IDE Settings ### 22 | .settings 23 | 24 | # Ignore Gradle GUI config 25 | gradle-app.setting 26 | 27 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 28 | !gradle-wrapper.jar 29 | 30 | # Cache of project 31 | .gradletasknamecache 32 | 33 | # Environment variables file 34 | .env 35 | 36 | .metadata 37 | bin/ 38 | tmp/ 39 | *.tmp 40 | *.bak 41 | *.swp 42 | *~.nib 43 | local.properties 44 | .settings/ 45 | .loadpath 46 | .recommenders 47 | 48 | # External tool builders 49 | .externalToolBuilders/ 50 | 51 | # Locally stored "Eclipse launch configurations" 52 | *.launch 53 | 54 | # PyDev specific (Python IDE for Eclipse) 55 | *.pydevproject 56 | 57 | # CDT-specific (C/C++ Development Tooling) 58 | .cproject 59 | 60 | # CDT- autotools 61 | .autotools 62 | 63 | # Java annotation processor (APT) 64 | .factorypath 65 | 66 | # PDT-specific (PHP Development Tools) 67 | .buildpath 68 | 69 | # sbteclipse plugin 70 | .target 71 | 72 | # Tern plugin 73 | .tern-project 74 | 75 | # TeXlipse plugin 76 | .texlipse 77 | 78 | # STS (Spring Tool Suite) 79 | .springBeans 80 | 81 | # Code Recommenders 82 | .recommenders/ 83 | 84 | # Annotation Processing 85 | .apt_generated/ 86 | .apt_generated_test/ 87 | 88 | # Scala IDE specific (Scala & Java development for Eclipse) 89 | .cache-main 90 | .scala_dependencies 91 | .worksheet 92 | 93 | .project 94 | .checkstyle 95 | .classpath 96 | .idea/ 97 | examples/persistedCredentialIssuers.ser 98 | examples/persistedDiDs.ser 99 | examples/persistedSignatures.ser 100 | examples/persistedVCs.ser 101 | gradle.properties 102 | data/ 103 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/utils/Validator.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.utils; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.function.Consumer; 6 | 7 | /** 8 | * {@link Validator} allows implementing classes to run validation checks and gather all errors. 9 | */ 10 | public class Validator { 11 | protected List validationErrors; 12 | 13 | /** 14 | * Adds a validation error to the errors list. 15 | * 16 | * @param errorMessage The error message. 17 | */ 18 | public void addValidationError(final String errorMessage) { 19 | if (validationErrors == null) { 20 | validationErrors = new ArrayList<>(); 21 | } 22 | 23 | validationErrors.add(errorMessage); 24 | } 25 | 26 | /** 27 | * Checks if there are any validation errors and if yes throws {@link IllegalStateException}. 28 | * 29 | * @param prologue A prologue before the errors list. 30 | * @param validationFunction Validation function that defines all check conditions. 31 | */ 32 | public void checkValidationErrors(final String prologue, final Consumer validationFunction) { 33 | validationErrors = null; 34 | 35 | validationFunction.accept(this); 36 | 37 | if (validationErrors == null) { 38 | return; 39 | } 40 | 41 | List errors = validationErrors; 42 | validationErrors = null; 43 | 44 | throw new IllegalStateException(prologue + ":\n" + String.join("\n", errors)); 45 | } 46 | 47 | /** 48 | * Checks if required condition is true and if not adds the given error message to the list. 49 | * 50 | * @param condition The condition to be met. 51 | * @param errorMessage The error message in case condition is not met. 52 | */ 53 | public final void require(final boolean condition, final String errorMessage) { 54 | if (!condition) { 55 | addValidationError(errorMessage); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /examples/appnet-api-server/src/main/resources/driving-license-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema", 3 | "$id": "http://localhost:5050/driving-license-schema.json", 4 | "type": "object", 5 | "title": "The Example Driving License Schema", 6 | "description": "The root schema comprises the entire JSON document.", 7 | "default": {}, 8 | "additionalProperties": true, 9 | "required": [ 10 | "id", 11 | "firstName", 12 | "lastName", 13 | "drivingLicenseCategories" 14 | ], 15 | "properties": { 16 | "id": { 17 | "$id": "#/properties/id", 18 | "type": "string", 19 | "title": "DID of the owner", 20 | "default": "", 21 | "examples": [ 22 | "did:hedera:testnet:DnK9ApZ4GHyAyJY4BL6PKQCJdq1T6jbFpt2bJbRvc63k;hedera:testnet:fid=0.0.1" 23 | ] 24 | }, 25 | "firstName": { 26 | "$id": "#/properties/firstName", 27 | "type": "string", 28 | "title": "Owner's first name", 29 | "default": "", 30 | "examples": [ 31 | "John" 32 | ] 33 | }, 34 | "lastName": { 35 | "$id": "#/properties/lastName", 36 | "type": "string", 37 | "title": "Owner's last name", 38 | "default": "", 39 | "examples": [ 40 | "Doe" 41 | ] 42 | }, 43 | "drivingLicenseCategories": { 44 | "$id": "#/properties/drivingLicenseCategories", 45 | "type": "array", 46 | "title": "Array of driving license categories", 47 | "default": [], 48 | "examples": [ 49 | [ 50 | "A", 51 | "B1", 52 | "T" 53 | ] 54 | ], 55 | "additionalItems": true, 56 | "items": { 57 | "$id": "#/properties/drivingLicenseCategories/items", 58 | "type": "string", 59 | "title": "A single driving license category", 60 | "default": "", 61 | "examples": [ 62 | "A", 63 | "B1", 64 | "T" 65 | ] 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /examples/appnet-api-server/src/main/java/com/hedera/hashgraph/identity/hcs/example/appnet/vc/LinkedDataProof.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs.example.appnet.vc; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import org.threeten.bp.Instant; 5 | 6 | /** 7 | * A simple DTO for manually constructed example of a linked data proof. 8 | */ 9 | public class LinkedDataProof { 10 | @Expose 11 | private String type; 12 | @Expose 13 | private String creator; 14 | @Expose 15 | private Instant created; 16 | @Expose 17 | private String domain; 18 | @Expose 19 | private String nonce; 20 | @Expose 21 | private String proofPurpose; 22 | @Expose 23 | private String verificationMethod; 24 | @Expose 25 | private String jws; 26 | 27 | public String getCreator() { 28 | return creator; 29 | } 30 | 31 | public void setCreator(final String creator) { 32 | this.creator = creator; 33 | } 34 | 35 | public Instant getCreated() { 36 | return created; 37 | } 38 | 39 | public void setCreated(final Instant created) { 40 | this.created = created; 41 | } 42 | 43 | public String getDomain() { 44 | return domain; 45 | } 46 | 47 | public void setDomain(final String domain) { 48 | this.domain = domain; 49 | } 50 | 51 | public String getNonce() { 52 | return nonce; 53 | } 54 | 55 | public void setNonce(final String nonce) { 56 | this.nonce = nonce; 57 | } 58 | 59 | public String getProofPurpose() { 60 | return proofPurpose; 61 | } 62 | 63 | public void setProofPurpose(final String proofPurpose) { 64 | this.proofPurpose = proofPurpose; 65 | } 66 | 67 | public String getVerificationMethod() { 68 | return verificationMethod; 69 | } 70 | 71 | public void setVerificationMethod(final String verificationMethod) { 72 | this.verificationMethod = verificationMethod; 73 | } 74 | 75 | public String getType() { 76 | return type; 77 | } 78 | 79 | public void setType(final String type) { 80 | this.type = type; 81 | } 82 | 83 | public String getJws() { 84 | return jws; 85 | } 86 | 87 | public void setJws(final String jws) { 88 | this.jws = jws; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/test/java/com/hedera/hashgraph/identity/hcs/AesEncryptionUtil.java: -------------------------------------------------------------------------------- 1 | 2 | package com.hedera.hashgraph.identity.hcs; 3 | 4 | import java.nio.charset.StandardCharsets; 5 | import java.security.MessageDigest; 6 | import java.security.NoSuchAlgorithmException; 7 | import java.util.Arrays; 8 | import javax.crypto.Cipher; 9 | import javax.crypto.spec.SecretKeySpec; 10 | 11 | public class AesEncryptionUtil { 12 | 13 | private static SecretKeySpec secretToKey(String secret) throws NoSuchAlgorithmException { 14 | MessageDigest sha = MessageDigest.getInstance("SHA-1"); 15 | byte[] key = sha.digest(secret.getBytes(StandardCharsets.UTF_8)); 16 | key = Arrays.copyOf(key, 16); 17 | return new SecretKeySpec(key, "AES"); 18 | } 19 | 20 | /** 21 | * Encrypts the given message with AES using a defined secret phrase. 22 | * 23 | * @param messageToEncrypt Message to encrypt. 24 | * @param secret The encryption secret. 25 | * @return The encrypted message. 26 | */ 27 | public static byte[] encrypt(byte[] messageToEncrypt, String secret) { 28 | try { 29 | SecretKeySpec secretKey = secretToKey(secret); 30 | Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); 31 | cipher.init(Cipher.ENCRYPT_MODE, secretKey); 32 | 33 | return cipher.doFinal(messageToEncrypt); 34 | } catch (Exception e) { 35 | throw new RuntimeException(e); 36 | } 37 | } 38 | 39 | /** 40 | * Decrypts the given message encrypted with AES and defined secret phrase. 41 | * 42 | * @param messageToDecrypt Message to decrypt. 43 | * @param secret The encryption secret. 44 | * @return The decrypted message. 45 | */ 46 | public static byte[] decrypt(byte[] messageToDecrypt, String secret) { 47 | try { 48 | SecretKeySpec secretKey = secretToKey(secret); 49 | Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING"); 50 | cipher.init(Cipher.DECRYPT_MODE, secretKey); 51 | 52 | return cipher.doFinal(messageToDecrypt); 53 | } catch (Exception e) { 54 | throw new RuntimeException(e); 55 | } 56 | } 57 | 58 | private AesEncryptionUtil() { 59 | // Empty on purpose 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /examples/appnet-api-server/src/main/java/com/hedera/hashgraph/identity/hcs/example/appnet/dto/VerifiableCredentialStatus.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs.example.appnet.dto; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import com.hedera.hashgraph.identity.hcs.vc.HcsVcMessage; 5 | import com.hedera.hashgraph.identity.hcs.vc.HcsVcOperation; 6 | import org.threeten.bp.Instant; 7 | 8 | /** 9 | * A status of verifiable credential in VC registry. 10 | * Includes consensus timestamp of the latest valid message. 11 | */ 12 | public class VerifiableCredentialStatus { 13 | 14 | @Expose 15 | private HcsVcOperation operation; 16 | 17 | @Expose 18 | private String credentialHash; 19 | 20 | @Expose 21 | private Instant timestamp; 22 | 23 | @Expose 24 | private Instant updated; 25 | 26 | /** 27 | * Creates a new instance of this DTO from the given VC message and its consensus timestamp. 28 | * 29 | * @param message VC message received from the mirror node. 30 | * @param consensusTimestamp Consensus timestamp of the message. 31 | * @return The DTO instance. 32 | */ 33 | public static VerifiableCredentialStatus fromHcsVcMessage(final HcsVcMessage message, 34 | final Instant consensusTimestamp) { 35 | VerifiableCredentialStatus result = new VerifiableCredentialStatus(); 36 | 37 | result.setOperation(message.getOperation()); 38 | result.setCredentialHash(message.getCredentialHash()); 39 | result.setTimestamp(message.getTimestamp()); 40 | result.setUpdated(consensusTimestamp); 41 | 42 | return result; 43 | } 44 | 45 | public Instant getTimestamp() { 46 | return timestamp; 47 | } 48 | 49 | public void setTimestamp(final Instant timestamp) { 50 | this.timestamp = timestamp; 51 | } 52 | 53 | public Instant getUpdated() { 54 | return updated; 55 | } 56 | 57 | public void setUpdated(final Instant updated) { 58 | this.updated = updated; 59 | } 60 | 61 | public HcsVcOperation getOperation() { 62 | return operation; 63 | } 64 | 65 | public void setOperation(final HcsVcOperation operation) { 66 | this.operation = operation; 67 | } 68 | 69 | public String getCredentialHash() { 70 | return credentialHash; 71 | } 72 | 73 | public void setCredentialHash(final String credentialHash) { 74 | this.credentialHash = credentialHash; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/test/java/com/hedera/hashgraph/identity/hcs/did/HcsDidRootKeyTest.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs.did; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertNotNull; 5 | import static org.junit.jupiter.api.Assertions.assertThrows; 6 | 7 | import com.hedera.hashgraph.sdk.PrivateKey; 8 | import com.hedera.hashgraph.sdk.PublicKey; 9 | import com.hedera.hashgraph.sdk.FileId; 10 | import io.github.cdimascio.dotenv.Dotenv; 11 | import org.bitcoinj.core.Base58; 12 | import org.junit.jupiter.api.Test; 13 | import org.junit.jupiter.api.TestInstance; 14 | 15 | import java.util.Objects; 16 | 17 | /** 18 | * Tests {@link HcsDidRootKey} generation and parsing operations. 19 | */ 20 | @TestInstance(TestInstance.Lifecycle.PER_CLASS) 21 | public class HcsDidRootKeyTest { 22 | private Dotenv dotenv = Dotenv.configure().ignoreIfMissing().ignoreIfMalformed().load(); 23 | // Grab the network to use from environment variables 24 | private String network = Objects.requireNonNull(dotenv.get("NETWORK")); 25 | 26 | @Test 27 | void testGenerate() { 28 | final String addressBook = "0.0.1"; 29 | 30 | // Generate pair of HcsDid root keys 31 | PrivateKey privateKey = HcsDid.generateDidRootKey(); 32 | 33 | // Generate HcsDid 34 | HcsDid did = new HcsDid(network, privateKey.getPublicKey(), FileId.fromString(addressBook)); 35 | 36 | assertThrows(IllegalArgumentException.class, () -> HcsDidRootKey.fromHcsIdentity(null, null)); 37 | assertThrows(IllegalArgumentException.class, () -> HcsDidRootKey.fromHcsIdentity(did, null)); 38 | assertThrows(IllegalArgumentException.class, () -> HcsDidRootKey.fromHcsIdentity(null, privateKey.getPublicKey())); 39 | 40 | PublicKey differentPublicKey = HcsDid.generateDidRootKey().getPublicKey(); 41 | assertThrows(IllegalArgumentException.class, () -> HcsDidRootKey.fromHcsIdentity(did, differentPublicKey)); 42 | 43 | HcsDidRootKey didRootKey = HcsDidRootKey.fromHcsIdentity(did, privateKey.getPublicKey()); 44 | assertNotNull(didRootKey); 45 | 46 | assertEquals(didRootKey.getType(), "Ed25519VerificationKey2018"); 47 | assertEquals(didRootKey.getId(), did.toDid() + HcsDidRootKey.DID_ROOT_KEY_NAME); 48 | assertEquals(didRootKey.getController(), did.toDid()); 49 | assertEquals(didRootKey.getPublicKeyBase58(), Base58.encode(privateKey.getPublicKey().toBytes())); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/utils/SingleToArrayTypeAdapterFactory.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.utils; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.TypeAdapter; 5 | import com.google.gson.TypeAdapterFactory; 6 | import com.google.gson.reflect.TypeToken; 7 | import com.google.gson.stream.JsonReader; 8 | import com.google.gson.stream.JsonToken; 9 | import com.google.gson.stream.JsonWriter; 10 | import java.io.IOException; 11 | import java.lang.reflect.ParameterizedType; 12 | import java.lang.reflect.Type; 13 | import java.util.Collections; 14 | import java.util.List; 15 | 16 | public final class SingleToArrayTypeAdapterFactory implements TypeAdapterFactory { 17 | @SuppressWarnings("unchecked") 18 | @Override 19 | public TypeAdapter create(final Gson gson, final TypeToken type) { 20 | if (type.getRawType() != List.class) { 21 | return null; 22 | } 23 | 24 | Type elementType = ((ParameterizedType) type.getType()).getActualTypeArguments()[0]; 25 | Type listType = TypeToken.get(List.class).getType(); 26 | TypeToken> listTypeToken = (TypeToken>) TypeToken.getParameterized(listType, elementType); 27 | 28 | TypeAdapter> delegateAdapter = gson.getAdapter(listTypeToken); 29 | TypeAdapter elementAdapter = (TypeAdapter) gson.getAdapter(TypeToken.get(elementType)); 30 | 31 | return (TypeAdapter) new SingleToArrayTypeAdapter<>(delegateAdapter, elementAdapter); 32 | } 33 | 34 | private final class SingleToArrayTypeAdapter extends TypeAdapter> { 35 | private final TypeAdapter> delegateAdapter; 36 | private final TypeAdapter elementAdapter; 37 | 38 | SingleToArrayTypeAdapter(final TypeAdapter> delegateAdapter, 39 | final TypeAdapter elementAdapter) { 40 | this.delegateAdapter = delegateAdapter; 41 | this.elementAdapter = elementAdapter; 42 | } 43 | 44 | @Override 45 | public List read(final JsonReader reader) throws IOException { 46 | if (reader.peek() != JsonToken.BEGIN_ARRAY) { 47 | return Collections.singletonList(elementAdapter.read(reader)); 48 | } 49 | return delegateAdapter.read(reader); 50 | } 51 | 52 | @Override 53 | public void write(final JsonWriter writer, final List value) 54 | throws IOException { 55 | if (value.size() == 1) { 56 | elementAdapter.write(writer, value.get(0)); 57 | } else { 58 | delegateAdapter.write(writer, value); 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/DidSyntax.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * Hedera DID method syntax. 7 | */ 8 | public final class DidSyntax { 9 | /** 10 | * DID prefix as defined by W3C DID specification. 11 | */ 12 | public static final String DID_PREFIX = "did"; 13 | 14 | public static final String DID_DOCUMENT_CONTEXT = "https://www.w3.org/ns/did/v1"; 15 | public static final String DID_METHOD_SEPARATOR = ":"; 16 | public static final String DID_PARAMETER_SEPARATOR = ";"; 17 | public static final String DID_PARAMETER_VALUE_SEPARATOR = "="; 18 | 19 | /** 20 | * This class is not to be instantiated. 21 | */ 22 | private DidSyntax() { 23 | // Empty on purpose. 24 | } 25 | 26 | /** 27 | * Hedera DID Method. 28 | */ 29 | public enum Method { 30 | HEDERA_HCS("hedera"); 31 | 32 | /** 33 | * Method name. 34 | */ 35 | private String method; 36 | 37 | /** 38 | * Creates Method instance. 39 | * 40 | * @param methodName The name of the Hedera DID method. 41 | */ 42 | Method(final String methodName) { 43 | this.method = methodName; 44 | } 45 | 46 | /** 47 | * Resolves method name from string to {@link Method} type. 48 | * 49 | * @param methodName The name of the Hedera method. 50 | * @return {@link Method} type instance value for the given string. 51 | */ 52 | public static Method get(final String methodName) { 53 | return Arrays.stream(values()) 54 | .filter(m -> m.method.equals(methodName)).findFirst() 55 | .orElseThrow(() -> new IllegalArgumentException("Invalid DID method name: " + methodName)); 56 | } 57 | 58 | @Override 59 | public String toString() { 60 | return method; 61 | } 62 | } 63 | 64 | /** 65 | * Hedera DID method-specific URL Parameters. 66 | */ 67 | public static final class MethodSpecificParameter { 68 | /** 69 | * MethodSpecificParameter name for the FileId of appnet's address book. 70 | */ 71 | public static final String ADDRESS_BOOK_FILE_ID = "fid"; 72 | 73 | /** 74 | * MethodSpecificParameter name for the TopicId of appnet's DID topic. 75 | */ 76 | public static final String DID_TOPIC_ID = "tid"; 77 | 78 | /** 79 | * This class is not to be instantiated. 80 | */ 81 | private MethodSpecificParameter() { 82 | // Empty on purpose. 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/hcs/did/HcsDidTopicListener.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs.did; 2 | 3 | import com.hedera.hashgraph.identity.hcs.MessageEnvelope; 4 | import com.hedera.hashgraph.identity.hcs.MessageListener; 5 | import com.hedera.hashgraph.sdk.TopicId; 6 | import com.hedera.hashgraph.sdk.TopicMessage; 7 | import java8.util.function.BiFunction; 8 | import org.threeten.bp.Instant; 9 | 10 | /** 11 | * A listener of confirmed {@link HcsDidMessage} messages from a DID topic. 12 | * Messages are received from a given mirror node, parsed and validated. 13 | */ 14 | public class HcsDidTopicListener extends MessageListener { 15 | 16 | /** 17 | * Creates a new instance of a DID topic listener for the given consensus topic. 18 | * By default, invalid messages are ignored and errors are not. 19 | * 20 | * @param didTopicId The DID consensus topic ID. 21 | */ 22 | public HcsDidTopicListener(final TopicId didTopicId) { 23 | super(didTopicId); 24 | } 25 | 26 | @Override 27 | protected MessageEnvelope extractMessage(final TopicMessage response) { 28 | MessageEnvelope result = null; 29 | try { 30 | result = MessageEnvelope.fromMirrorResponse(response, HcsDidMessage.class); 31 | } catch (Exception err) { 32 | handleError(err); 33 | } 34 | 35 | return result; 36 | } 37 | 38 | @Override 39 | protected boolean isMessageValid(final MessageEnvelope envelope, 40 | final TopicMessage response) { 41 | try { 42 | BiFunction msgDecrypter = decrypter == null ? null 43 | : HcsDidMessage.getDecrypter(decrypter); 44 | 45 | HcsDidMessage message = envelope.open(msgDecrypter); 46 | if (message == null) { 47 | reportInvalidMessage(response, "Empty message received when opening envelope"); 48 | return false; 49 | } 50 | 51 | if (!envelope.isSignatureValid(e -> message.extractDidRootKey())) { 52 | reportInvalidMessage(response, "Signature validation failed"); 53 | return false; 54 | } 55 | 56 | if (!message.isValid(topicId)) { 57 | reportInvalidMessage(response, "Message content validation failed."); 58 | return false; 59 | } 60 | 61 | return true; 62 | } catch (Exception err) { 63 | handleError(err); 64 | reportInvalidMessage(response, "Exception while validating message: " + err.getMessage()); 65 | return false; 66 | } 67 | 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/hcs/did/HcsDidRootKey.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs.did; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import com.hedera.hashgraph.sdk.PublicKey; 5 | import org.bitcoinj.core.Base58; 6 | 7 | /** 8 | * Represents a root key of HCS Identity DID. 9 | * That is a public key of type Ed25519VerificationKey2018 compatible with a single publicKey entry of a DID Document. 10 | */ 11 | public class HcsDidRootKey { 12 | public static final String DID_ROOT_KEY_NAME = "#did-root-key"; 13 | public static final String DID_ROOT_KEY_TYPE = "Ed25519VerificationKey2018"; 14 | 15 | @Expose(serialize = true, deserialize = true) 16 | private String id; 17 | 18 | @Expose(serialize = true, deserialize = true) 19 | private String type; 20 | 21 | @Expose(serialize = true, deserialize = true) 22 | private String controller; 23 | 24 | @Expose(serialize = true, deserialize = true) 25 | private String publicKeyBase58; 26 | 27 | /** 28 | * Creates a {@link HcsDidRootKey} object from the given {@link HcsDid} DID and it's root public key. 29 | * 30 | * @param did The {@link HcsDid} DID object. 31 | * @param didRootKey The public key from which the DID was derived. 32 | * @return The {@link HcsDidRootKey} object. 33 | */ 34 | public static HcsDidRootKey fromHcsIdentity(final HcsDid did, final PublicKey didRootKey) { 35 | if (did == null) { 36 | throw new IllegalArgumentException("DID cannot be null"); 37 | } 38 | 39 | if (didRootKey == null) { 40 | throw new IllegalArgumentException("DID root key cannot be null"); 41 | } 42 | 43 | // Validate if hcsIdentity is derived from the given root key 44 | if (!HcsDid.publicKeyToIdString(didRootKey).equals(did.getIdString())) { 45 | throw new IllegalArgumentException("The specified DID does not correspond to the given DID root key"); 46 | } 47 | 48 | HcsDidRootKey result = new HcsDidRootKey(); 49 | result.controller = did.toDid(); 50 | result.id = result.controller + DID_ROOT_KEY_NAME; 51 | result.publicKeyBase58 = Base58.encode(didRootKey.toBytes()); 52 | result.type = DID_ROOT_KEY_TYPE; 53 | 54 | return result; 55 | } 56 | 57 | public String getId() { 58 | return id; 59 | } 60 | 61 | public String getType() { 62 | return type; 63 | } 64 | 65 | public String getController() { 66 | return controller; 67 | } 68 | 69 | public String getPublicKeyBase58() { 70 | return publicKeyBase58; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /docs/sdk-javadocs/jquery/jszip-utils/dist/jszip-utils-ie.js: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | JSZipUtils - A collection of cross-browser utilities to go along with JSZip. 4 | 5 | 6 | (c) 2014 Stuart Knightley, David Duponchel 7 | Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip-utils/master/LICENSE.markdown. 8 | 9 | */ 10 | ;(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o\r\n"+ 18 | "\r\n"; 32 | 33 | // inject VBScript 34 | document.write(IEBinaryToArray_ByteStr_Script); 35 | 36 | global.JSZipUtils._getBinaryFromXHR = function (xhr) { 37 | var binary = xhr.responseBody; 38 | var byteMapping = {}; 39 | for ( var i = 0; i < 256; i++ ) { 40 | for ( var j = 0; j < 256; j++ ) { 41 | byteMapping[ String.fromCharCode( i + (j << 8) ) ] = 42 | String.fromCharCode(i) + String.fromCharCode(j); 43 | } 44 | } 45 | var rawBytes = IEBinaryToArray_ByteStr(binary); 46 | var lastChr = IEBinaryToArray_ByteStr_Last(binary); 47 | return rawBytes.replace(/[\s\S]/g, function( match ) { 48 | return byteMapping[match]; 49 | }) + lastChr; 50 | }; 51 | 52 | // enforcing Stuk's coding style 53 | // vim: set shiftwidth=4 softtabstop=4: 54 | 55 | },{}]},{},[1]) 56 | ; 57 | -------------------------------------------------------------------------------- /docs/sdk-javadocs/type-search-index.js: -------------------------------------------------------------------------------- 1 | typeSearchIndex = [{"p":"com.hedera.hashgraph.identity.hcs","l":"AddressBook"},{"l":"All Classes","url":"allclasses-index.html"},{"p":"com.hedera.hashgraph.identity.hcs.vc","l":"CredentialSubject"},{"p":"com.hedera.hashgraph.identity","l":"DidDocumentBase"},{"p":"com.hedera.hashgraph.identity","l":"DidDocumentJsonProperties"},{"p":"com.hedera.hashgraph.identity","l":"DidMethodOperation"},{"p":"com.hedera.hashgraph.identity","l":"DidParser"},{"p":"com.hedera.hashgraph.identity","l":"DidSyntax"},{"p":"com.hedera.hashgraph.identity.hcs.did","l":"HcsDid"},{"p":"com.hedera.hashgraph.identity.hcs.did","l":"HcsDidMessage"},{"p":"com.hedera.hashgraph.identity.hcs.did","l":"HcsDidResolver"},{"p":"com.hedera.hashgraph.identity.hcs.did","l":"HcsDidRootKey"},{"p":"com.hedera.hashgraph.identity.hcs.did","l":"HcsDidTopicListener"},{"p":"com.hedera.hashgraph.identity.hcs.did","l":"HcsDidTransaction"},{"p":"com.hedera.hashgraph.identity.hcs","l":"HcsIdentityNetwork"},{"p":"com.hedera.hashgraph.identity.hcs","l":"HcsIdentityNetworkBuilder"},{"p":"com.hedera.hashgraph.identity.hcs.vc","l":"HcsVcDocumentBase"},{"p":"com.hedera.hashgraph.identity.hcs.vc","l":"HcsVcDocumentJsonProperties"},{"p":"com.hedera.hashgraph.identity.hcs.vc","l":"HcsVcMessage"},{"p":"com.hedera.hashgraph.identity.hcs.vc","l":"HcsVcOperation"},{"p":"com.hedera.hashgraph.identity.hcs.vc","l":"HcsVcStatusResolver"},{"p":"com.hedera.hashgraph.identity.hcs.vc","l":"HcsVcTopicListener"},{"p":"com.hedera.hashgraph.identity.hcs.vc","l":"HcsVcTransaction"},{"p":"com.hedera.hashgraph.identity","l":"HederaDid"},{"p":"com.hedera.hashgraph.identity.utils","l":"InstantTypeAdapter"},{"p":"com.hedera.hashgraph.identity.hcs","l":"InvalidMessageException"},{"p":"com.hedera.hashgraph.identity.utils","l":"Iso8601InstantTypeAdapter"},{"p":"com.hedera.hashgraph.identity.hcs.vc","l":"Issuer"},{"p":"com.hedera.hashgraph.identity.hcs.vc","l":"IssuerTypeAdapterFactory"},{"p":"com.hedera.hashgraph.identity.utils","l":"JsonUtils"},{"p":"com.hedera.hashgraph.identity.hcs","l":"Message"},{"p":"com.hedera.hashgraph.identity.hcs","l":"MessageEnvelope"},{"p":"com.hedera.hashgraph.identity.hcs","l":"MessageListener"},{"p":"com.hedera.hashgraph.identity.hcs","l":"MessageMode"},{"p":"com.hedera.hashgraph.identity.hcs","l":"MessageResolver"},{"p":"com.hedera.hashgraph.identity.hcs","l":"MessageTransaction"},{"p":"com.hedera.hashgraph.identity","l":"DidSyntax.Method"},{"p":"com.hedera.hashgraph.identity","l":"DidSyntax.MethodSpecificParameter"},{"p":"com.hedera.hashgraph.identity.utils","l":"MirrorNodeAddress"},{"p":"com.hedera.hashgraph.identity.hcs","l":"SerializableMirrorConsensusResponse"},{"p":"com.hedera.hashgraph.identity.utils","l":"SingleToArrayTypeAdapterFactory"},{"p":"com.hedera.hashgraph.identity.utils","l":"Validator"}] -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/hcs/did/HcsDidResolver.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs.did; 2 | 3 | import com.hedera.hashgraph.identity.DidMethodOperation; 4 | import com.hedera.hashgraph.identity.hcs.MessageEnvelope; 5 | import com.hedera.hashgraph.identity.hcs.MessageListener; 6 | import com.hedera.hashgraph.identity.hcs.MessageResolver; 7 | import com.hedera.hashgraph.sdk.TopicId; 8 | import java.util.Set; 9 | 10 | /** 11 | * Resolves the DID from Hedera network. 12 | */ 13 | public class HcsDidResolver extends MessageResolver { 14 | 15 | /** 16 | * Instantiates a new DID resolver for the given DID topic. 17 | * 18 | * @param topicId The HCS DID topic ID. 19 | */ 20 | public HcsDidResolver(final TopicId topicId) { 21 | super(topicId); 22 | } 23 | 24 | /** 25 | * Adds a DID to resolve. 26 | * 27 | * @param did The DID string. 28 | * @return This resolver instance. 29 | */ 30 | public HcsDidResolver addDid(final String did) { 31 | if (did != null) { 32 | results.put(did, null); 33 | } 34 | return this; 35 | } 36 | 37 | /** 38 | * Adds multiple DIDs to resolve. 39 | * 40 | * @param dids The set of DID strings. 41 | * @return This resolver instance. 42 | */ 43 | public HcsDidResolver addDids(final Set dids) { 44 | if (dids != null) { 45 | dids.forEach(d -> addDid(d)); 46 | } 47 | 48 | return this; 49 | } 50 | 51 | @Override 52 | protected boolean matchesSearchCriteria(final HcsDidMessage message) { 53 | return results.containsKey(message.getDid()); 54 | } 55 | 56 | @Override 57 | protected void processMessage(final MessageEnvelope envelope) { 58 | HcsDidMessage message = envelope.open(); 59 | // Also skip messages that are older than the once collected or if we already have a DELETE message 60 | MessageEnvelope existing = results.get(message.getDid()); 61 | if (existing != null 62 | && (envelope.getConsensusTimestamp().isBefore(existing.getConsensusTimestamp()) 63 | || (DidMethodOperation.DELETE.equals(existing.open().getOperation()) 64 | && !DidMethodOperation.DELETE.equals(message.getOperation())))) { 65 | return; 66 | } 67 | 68 | // Preserve created and updated timestamps 69 | message.setUpdated(envelope.getConsensusTimestamp()); 70 | if (DidMethodOperation.CREATE.equals(message.getOperation())) { 71 | message.setCreated(envelope.getConsensusTimestamp()); 72 | } else if (existing != null) { 73 | message.setCreated(existing.open().getCreated()); 74 | } 75 | 76 | // Add valid message to the results 77 | results.put(message.getDid(), envelope); 78 | } 79 | 80 | @Override 81 | protected MessageListener supplyMessageListener() { 82 | return new HcsDidTopicListener(topicId); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /examples/appnet-api-server/src/main/java/com/hedera/hashgraph/identity/hcs/example/appnet/vc/DrivingLicense.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs.example.appnet.vc; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.JsonElement; 5 | import com.google.gson.JsonObject; 6 | import com.google.gson.annotations.Expose; 7 | import com.hedera.hashgraph.identity.hcs.vc.CredentialSubject; 8 | import com.hedera.hashgraph.identity.utils.JsonUtils; 9 | import java.util.LinkedHashMap; 10 | import java.util.List; 11 | 12 | /** 13 | * A simple, manual example of a credential subject in verifiable credential - a driving license. 14 | */ 15 | public class DrivingLicense extends CredentialSubject { 16 | private static final String[] JSON_PROPERTIES_ORDER = {"id", "firstName", "lastName", "drivingLicenseCategories"}; 17 | 18 | @Expose 19 | private final String firstName; 20 | 21 | @Expose 22 | private final String lastName; 23 | 24 | @Expose 25 | private final List drivingLicenseCategories; 26 | 27 | /** 28 | * Builds a new driving license credential. 29 | * 30 | * @param did Driving license owner's DID. 31 | * @param firstName Owner's first name. 32 | * @param lastName Owner's last name. 33 | * @param drivingLicenseCategories A list of categories granted to the owner. 34 | */ 35 | public DrivingLicense(final String did, final String firstName, final String lastName, 36 | final List drivingLicenseCategories) { 37 | this.id = did; 38 | this.firstName = firstName; 39 | this.lastName = lastName; 40 | this.drivingLicenseCategories = drivingLicenseCategories; 41 | } 42 | 43 | /** 44 | * Note: this is a manual implementation of ordered JSON items for this simple non-nested schema. 45 | * In a real-world application it is recommended to use a JSON-LD compatible library to handle normalization. 46 | * However at this point the only available one in Java support JSON-LD version 1.0, but 1.1 is required by W3C 47 | * Verifiable Credentials. 48 | * 49 | * @return A normalized JSON Element representation of this document. 50 | */ 51 | public JsonElement toNormalizedJsonElement() { 52 | Gson gson = JsonUtils.getGson(); 53 | 54 | // First turn to normal JSON 55 | JsonObject root = gson.toJsonTree(this).getAsJsonObject(); 56 | // Then put JSON properties in ordered map 57 | LinkedHashMap map = new LinkedHashMap<>(); 58 | 59 | for (String property : JSON_PROPERTIES_ORDER) { 60 | if (root.has(property)) { 61 | map.put(property, root.get(property)); 62 | } 63 | } 64 | // Turn map to JSON 65 | return gson.toJsonTree(map); 66 | } 67 | 68 | public String getFirstName() { 69 | return firstName; 70 | } 71 | 72 | public String getLastName() { 73 | return lastName; 74 | } 75 | 76 | public List getDrivingLicenseCategories() { 77 | return drivingLicenseCategories; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/hcs/did/HcsDidTransaction.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs.did; 2 | 3 | import com.google.common.base.Strings; 4 | import com.hedera.hashgraph.identity.DidMethodOperation; 5 | import com.hedera.hashgraph.identity.hcs.MessageEnvelope; 6 | import com.hedera.hashgraph.identity.hcs.MessageListener; 7 | import com.hedera.hashgraph.identity.hcs.MessageTransaction; 8 | import com.hedera.hashgraph.identity.utils.Validator; 9 | import com.hedera.hashgraph.sdk.TopicId; 10 | import java.util.function.UnaryOperator; 11 | 12 | /** 13 | * The DID document creation, update or deletion transaction. 14 | * Builds a correct {@link HcsDidMessage} and send it to HCS DID topic. 15 | */ 16 | public class HcsDidTransaction extends MessageTransaction { 17 | private final DidMethodOperation operation; 18 | private String didDocument; 19 | 20 | /** 21 | * Instantiates a new transaction object. 22 | * 23 | * @param operation The operation to be performed on a DID document. 24 | * @param topicId The HCS DID topic ID where message will be submitted. 25 | */ 26 | public HcsDidTransaction(final DidMethodOperation operation, final TopicId topicId) { 27 | super(topicId); 28 | this.operation = operation; 29 | } 30 | 31 | /** 32 | * Instantiates a new transaction object from a message that was already prepared. 33 | * 34 | * @param topicId The HCS DID topic ID where message will be submitted. 35 | * @param message The message envelope. 36 | */ 37 | public HcsDidTransaction(final MessageEnvelope message, final TopicId topicId) { 38 | super(topicId, message); 39 | this.operation = null; 40 | } 41 | 42 | /** 43 | * Sets a DID document as JSON string that will be submitted to HCS. 44 | * 45 | * @param didDocument The didDocument to be published. 46 | * @return This transaction instance. 47 | */ 48 | public HcsDidTransaction setDidDocument(final String didDocument) { 49 | this.didDocument = didDocument; 50 | return this; 51 | } 52 | 53 | @Override 54 | protected void validate(final Validator validator) { 55 | super.validate(validator); 56 | // if message was not provided, check if we have all information to build it. 57 | validator.require(!Strings.isNullOrEmpty(didDocument) || message != null, "DID document is mandatory."); 58 | validator.require(operation != null || message != null, "DID method operation is not defined."); 59 | } 60 | 61 | @Override 62 | protected MessageEnvelope buildMessage() { 63 | return HcsDidMessage.fromDidDocumentJson(didDocument, operation); 64 | } 65 | 66 | @Override 67 | protected MessageListener provideTopicListener(final TopicId topicIdToListen) { 68 | return new HcsDidTopicListener(topicIdToListen); 69 | } 70 | 71 | @Override 72 | protected UnaryOperator provideMessageEncrypter(final UnaryOperator encryptionFunction) { 73 | return HcsDidMessage.getEncrypter(encryptionFunction); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /docs/vc-message.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema", 3 | "$id": "http://example.com/example.json", 4 | "type": "object", 5 | "title": "HCS VC message schema", 6 | "description": "The schema of a VC message", 7 | "default": {}, 8 | "required": [ 9 | "mode", 10 | "message", 11 | "signature" 12 | ], 13 | "additionalProperties": true, 14 | "properties": { 15 | "mode": { 16 | "$id": "#/properties/mode", 17 | "type": "string", 18 | "title": "The mode in which this message is sent", 19 | "default": "", 20 | "examples": [ 21 | "plain", 22 | "encrypted" 23 | ] 24 | }, 25 | "message": { 26 | "$id": "#/properties/message", 27 | "type": "object", 28 | "title": "The VC message", 29 | "default": {}, 30 | "examples": [ 31 | { 32 | "timestamp": "2020-05-06T16:45:20.006Z", 33 | "credentialHash": "GmcA3ut3tM7d51tRjkxvGZNaigtzwFAVunkimQC573Mv", 34 | "operation": "issue" 35 | } 36 | ], 37 | "required": [ 38 | "operation", 39 | "credentialHash", 40 | "timestamp" 41 | ], 42 | "additionalProperties": false, 43 | "properties": { 44 | "operation": { 45 | "$id": "#/properties/message/properties/operation", 46 | "type": "string", 47 | "title": "The type of operation on VC status", 48 | "default": "", 49 | "examples": [ 50 | "issue", "suspend", "resume", "revoke" 51 | ] 52 | }, 53 | "credentialHash": { 54 | "$id": "#/properties/message/properties/credentialHash", 55 | "type": "string", 56 | "title": "The hash of a verifiable credential document", 57 | "default": "", 58 | "examples": [ 59 | "GmcA3ut3tM7d51tRjkxvGZNaigtzwFAVunkimQC573Mv" 60 | ] 61 | }, 62 | "timestamp": { 63 | "$id": "#/properties/message/properties/timestamp", 64 | "type": "string", 65 | "title": "The message creation timestamp", 66 | "default": "", 67 | "examples": [ 68 | "2020-05-06T16:45:20.006Z" 69 | ] 70 | } 71 | } 72 | }, 73 | "signature": { 74 | "$id": "#/properties/signature", 75 | "type": "string", 76 | "title": "The signature of a message JSON with DID root key", 77 | "default": "", 78 | "examples": [ 79 | "8sCN9zIUcIL17R9KILvPoAkYkh8XFOCGL+uIYZw+nfDZuuEEb0JZHMZ/78Anr1QrKx9KLBxd3c6Xay8gNZ//AA==" 80 | ] 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /src/main/resources/did-document-context-w3org-did-v1.1.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": { 3 | "@version": 1.1, 4 | "id": "@id", 5 | "type": "@type", 6 | 7 | "dc": "http://purl.org/dc/terms/", 8 | "schema": "http://schema.org/", 9 | "sec": "https://w3id.org/security#", 10 | "didv": "https://w3id.org/did#", 11 | "xsd": "http://www.w3.org/2001/XMLSchema#", 12 | 13 | "EcdsaSecp256k1Signature2019": "sec:EcdsaSecp256k1Signature2019", 14 | "EcdsaSecp256k1VerificationKey2019": "sec:EcdsaSecp256k1VerificationKey2019", 15 | "Ed25519Signature2018": "sec:Ed25519Signature2018", 16 | "Ed25519VerificationKey2018": "sec:Ed25519VerificationKey2018", 17 | "RsaSignature2018": "sec:RsaSignature2018", 18 | "RsaVerificationKey2018": "sec:RsaVerificationKey2018", 19 | "SchnorrSecp256k1Signature2019": "sec:SchnorrSecp256k1Signature2019", 20 | "SchnorrSecp256k1VerificationKey2019": "sec:SchnorrSecp256k1VerificationKey2019", 21 | "ServiceEndpointProxyService": "didv:ServiceEndpointProxyService", 22 | 23 | "allowedAction": "sec:allowedAction", 24 | "assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"}, 25 | "authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"}, 26 | "capability": {"@id": "sec:capability", "@type": "@id"}, 27 | "capabilityAction": "sec:capabilityAction", 28 | "capabilityChain": {"@id": "sec:capabilityChain", "@type": "@id", "@container": "@list"}, 29 | "capabilityDelegation": {"@id": "sec:capabilityDelegationMethod", "@type": "@id", "@container": "@set"}, 30 | "capabilityInvocation": {"@id": "sec:capabilityInvocationMethod", "@type": "@id", "@container": "@set"}, 31 | "capabilityStatusList": {"@id": "sec:capabilityStatusList", "@type": "@id"}, 32 | "canonicalizationAlgorithm": "sec:canonicalizationAlgorithm", 33 | "caveat": {"@id": "sec:caveat", "@type": "@id", "@container": "@set"}, 34 | "challenge": "sec:challenge", 35 | "controller": {"@id": "sec:controller", "@type": "@id"}, 36 | "created": {"@id": "dc:created", "@type": "xsd:dateTime"}, 37 | "creator": {"@id": "dc:creator", "@type": "@id"}, 38 | "delegator": {"@id": "sec:delegator", "@type": "@id"}, 39 | "domain": "sec:domain", 40 | "expirationDate": {"@id": "sec:expiration", "@type": "xsd:dateTime"}, 41 | "invocationTarget": {"@id": "sec:invocationTarget", "@type": "@id"}, 42 | "invoker": {"@id": "sec:invoker", "@type": "@id"}, 43 | "jws": "sec:jws", 44 | "keyAgreement": {"@id": "sec:keyAgreementMethod", "@type": "@id", "@container": "@set"}, 45 | "nonce": "sec:nonce", 46 | "owner": {"@id": "sec:owner", "@type": "@id"}, 47 | "proof": {"@id": "sec:proof", "@type": "@id", "@container": "@graph"}, 48 | "proofPurpose": {"@id": "sec:proofPurpose", "@type": "@vocab"}, 49 | "proofValue": "sec:proofValue", 50 | "publicKey": {"@id": "sec:publicKey", "@type": "@id", "@container": "@set"}, 51 | "publicKeyBase58": "sec:publicKeyBase58", 52 | "publicKeyPem": "sec:publicKeyPem", 53 | "revoked": {"@id": "sec:revoked", "@type": "xsd:dateTime"}, 54 | "service": {"@id": "didv:service", "@type": "@id", "@container": "@set"}, 55 | "serviceEndpoint": {"@id": "didv:serviceEndpoint", "@type": "@id"}, 56 | "verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"} 57 | } 58 | } -------------------------------------------------------------------------------- /docs/sdk-javadocs/jquery/jquery-ui.structure.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI CSS Framework 1.12.1 3 | * http://jqueryui.com 4 | * 5 | * Copyright jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/category/theming/ 10 | */ 11 | /* Layout helpers 12 | ----------------------------------*/ 13 | .ui-helper-hidden { 14 | display: none; 15 | } 16 | .ui-helper-hidden-accessible { 17 | border: 0; 18 | clip: rect(0 0 0 0); 19 | height: 1px; 20 | margin: -1px; 21 | overflow: hidden; 22 | padding: 0; 23 | position: absolute; 24 | width: 1px; 25 | } 26 | .ui-helper-reset { 27 | margin: 0; 28 | padding: 0; 29 | border: 0; 30 | outline: 0; 31 | line-height: 1.3; 32 | text-decoration: none; 33 | font-size: 100%; 34 | list-style: none; 35 | } 36 | .ui-helper-clearfix:before, 37 | .ui-helper-clearfix:after { 38 | content: ""; 39 | display: table; 40 | border-collapse: collapse; 41 | } 42 | .ui-helper-clearfix:after { 43 | clear: both; 44 | } 45 | .ui-helper-zfix { 46 | width: 100%; 47 | height: 100%; 48 | top: 0; 49 | left: 0; 50 | position: absolute; 51 | opacity: 0; 52 | filter:Alpha(Opacity=0); /* support: IE8 */ 53 | } 54 | 55 | .ui-front { 56 | z-index: 100; 57 | } 58 | 59 | 60 | /* Interaction Cues 61 | ----------------------------------*/ 62 | .ui-state-disabled { 63 | cursor: default !important; 64 | pointer-events: none; 65 | } 66 | 67 | 68 | /* Icons 69 | ----------------------------------*/ 70 | .ui-icon { 71 | display: inline-block; 72 | vertical-align: middle; 73 | margin-top: -.25em; 74 | position: relative; 75 | text-indent: -99999px; 76 | overflow: hidden; 77 | background-repeat: no-repeat; 78 | } 79 | 80 | .ui-widget-icon-block { 81 | left: 50%; 82 | margin-left: -8px; 83 | display: block; 84 | } 85 | 86 | /* Misc visuals 87 | ----------------------------------*/ 88 | 89 | /* Overlays */ 90 | .ui-widget-overlay { 91 | position: fixed; 92 | top: 0; 93 | left: 0; 94 | width: 100%; 95 | height: 100%; 96 | } 97 | .ui-autocomplete { 98 | position: absolute; 99 | top: 0; 100 | left: 0; 101 | cursor: default; 102 | } 103 | .ui-menu { 104 | list-style: none; 105 | padding: 0; 106 | margin: 0; 107 | display: block; 108 | outline: 0; 109 | } 110 | .ui-menu .ui-menu { 111 | position: absolute; 112 | } 113 | .ui-menu .ui-menu-item { 114 | margin: 0; 115 | cursor: pointer; 116 | /* support: IE10, see #8844 */ 117 | list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"); 118 | } 119 | .ui-menu .ui-menu-item-wrapper { 120 | position: relative; 121 | padding: 3px 1em 3px .4em; 122 | } 123 | .ui-menu .ui-menu-divider { 124 | margin: 5px 0; 125 | height: 0; 126 | font-size: 0; 127 | line-height: 0; 128 | border-width: 1px 0 0 0; 129 | } 130 | .ui-menu .ui-state-focus, 131 | .ui-menu .ui-state-active { 132 | margin: -1px; 133 | } 134 | 135 | /* icon support */ 136 | .ui-menu-icons { 137 | position: relative; 138 | } 139 | .ui-menu-icons .ui-menu-item-wrapper { 140 | padding-left: 2em; 141 | } 142 | 143 | /* left-aligned */ 144 | .ui-menu .ui-icon { 145 | position: absolute; 146 | top: 0; 147 | bottom: 0; 148 | left: .2em; 149 | margin: auto 0; 150 | } 151 | 152 | /* right-aligned */ 153 | .ui-menu .ui-menu-icon { 154 | left: auto; 155 | right: 0; 156 | } 157 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 34 | 35 | @rem Find java.exe 36 | if defined JAVA_HOME goto findJavaFromJavaHome 37 | 38 | set JAVA_EXE=java.exe 39 | %JAVA_EXE% -version >NUL 2>&1 40 | if "%ERRORLEVEL%" == "0" goto init 41 | 42 | echo. 43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 44 | echo. 45 | echo Please set the JAVA_HOME variable in your environment to match the 46 | echo location of your Java installation. 47 | 48 | goto fail 49 | 50 | :findJavaFromJavaHome 51 | set JAVA_HOME=%JAVA_HOME:"=% 52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 53 | 54 | if exist "%JAVA_EXE%" goto init 55 | 56 | echo. 57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 58 | echo. 59 | echo Please set the JAVA_HOME variable in your environment to match the 60 | echo location of your Java installation. 61 | 62 | goto fail 63 | 64 | :init 65 | @rem Get command-line arguments, handling Windows variants 66 | 67 | if not "%OS%" == "Windows_NT" goto win9xME_args 68 | 69 | :win9xME_args 70 | @rem Slurp the command line arguments. 71 | set CMD_LINE_ARGS= 72 | set _SKIP=2 73 | 74 | :win9xME_args_slurp 75 | if "x%~1" == "x" goto execute 76 | 77 | set CMD_LINE_ARGS=%* 78 | 79 | :execute 80 | @rem Setup the command line 81 | 82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 83 | 84 | @rem Execute Gradle 85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 86 | 87 | :end 88 | @rem End local scope for the variables with windows NT shell 89 | if "%ERRORLEVEL%"=="0" goto mainEnd 90 | 91 | :fail 92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 93 | rem the _cmd.exe /c_ return code! 94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 95 | exit /b 1 96 | 97 | :mainEnd 98 | if "%OS%"=="Windows_NT" endlocal 99 | 100 | :omega 101 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/utils/InstantTypeAdapter.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.utils; 2 | 3 | import static com.google.common.base.Preconditions.checkArgument; 4 | import static com.google.common.base.Preconditions.checkNotNull; 5 | 6 | import com.google.common.collect.ImmutableList; 7 | import com.google.gson.TypeAdapter; 8 | import com.google.gson.stream.JsonReader; 9 | import com.google.gson.stream.JsonToken; 10 | import com.google.gson.stream.JsonWriter; 11 | import java.io.IOException; 12 | import java.util.Iterator; 13 | import org.threeten.bp.Instant; 14 | import org.threeten.bp.format.DateTimeFormatter; 15 | import org.threeten.bp.format.DateTimeParseException; 16 | import org.threeten.bp.temporal.TemporalAccessor; 17 | 18 | /** 19 | * Gson type adapter for {@link Instant} type and configurable date/time string format. 20 | */ 21 | public class InstantTypeAdapter extends TypeAdapter { 22 | 23 | private final DateTimeFormatter outputDateTimeFormatter; 24 | private final DateTimeFormatter firstParser; 25 | private final ImmutableList otherParsers; 26 | 27 | /** 28 | * Creates a new type adapter instance. 29 | * 30 | * @param dateTimeFormatter Formatter. 31 | * @param inputDateTimeParsers Date/time parsers list. 32 | */ 33 | public InstantTypeAdapter(final DateTimeFormatter dateTimeFormatter, 34 | final Iterable inputDateTimeParsers) { 35 | this.outputDateTimeFormatter = checkNotNull(dateTimeFormatter); 36 | Iterator parsers = inputDateTimeParsers.iterator(); 37 | checkArgument(parsers.hasNext(), "input parsers list must be nonempty"); 38 | this.firstParser = parsers.next(); 39 | this.otherParsers = ImmutableList.copyOf(parsers); 40 | } 41 | 42 | @Override 43 | public void write(final JsonWriter out, final Instant value) throws IOException { 44 | if (value == null) { 45 | out.nullValue(); 46 | } else { 47 | String instantStr = outputDateTimeFormatter.format(value); 48 | out.value(instantStr); 49 | } 50 | } 51 | 52 | @Override 53 | public Instant read(final JsonReader in) throws IOException { 54 | JsonToken token = in.peek(); 55 | if (token != JsonToken.STRING) { 56 | in.skipValue(); 57 | return null; 58 | } 59 | return parse(in.nextString()); 60 | } 61 | 62 | /** 63 | * Parses the given string into {@link Instant} object. 64 | * 65 | * @param instantStr Instant as string. 66 | * @return {@link Instant} object. 67 | * @throws IllegalArgumentException In case parsing fails. 68 | */ 69 | protected Instant parse(final String instantStr) { 70 | if (instantStr == null) { 71 | return null; 72 | } 73 | 74 | TemporalAccessor accessor = null; 75 | try { 76 | accessor = firstParser.parse(instantStr); 77 | } catch (DateTimeParseException ignore) { 78 | for (DateTimeFormatter parser : otherParsers) { 79 | try { 80 | accessor = parser.parse(instantStr); 81 | } catch (DateTimeParseException ignoreToo) { 82 | // ignore the error and move to the next parser 83 | continue; 84 | } 85 | } 86 | } 87 | 88 | if (accessor == null) { 89 | throw new IllegalArgumentException("Input string does not match any parsing formats used by this adapter"); 90 | } 91 | 92 | return Instant.from(accessor); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/hcs/vc/HcsVcTransaction.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs.vc; 2 | 3 | import com.hedera.hashgraph.identity.hcs.MessageEnvelope; 4 | import com.hedera.hashgraph.identity.hcs.MessageListener; 5 | import com.hedera.hashgraph.identity.hcs.MessageTransaction; 6 | import com.hedera.hashgraph.identity.hcs.did.HcsDidMessage; 7 | import com.hedera.hashgraph.identity.utils.Validator; 8 | import com.hedera.hashgraph.sdk.PublicKey; 9 | import com.hedera.hashgraph.sdk.TopicId; 10 | import java.util.function.UnaryOperator; 11 | import java8.util.Lists; 12 | import org.apache.commons.lang3.StringUtils; 13 | 14 | /** 15 | * The DID document creation, update or deletion transaction. 16 | * Builds a correct {@link HcsDidMessage} and send it to HCS DID topic. 17 | */ 18 | public class HcsVcTransaction extends MessageTransaction { 19 | private final HcsVcOperation operation; 20 | private final String credentialHash; 21 | private final PublicKey signerPublicKey; 22 | 23 | /** 24 | * Instantiates a new transaction object. 25 | * 26 | * @param topicId The HCS VC topic ID where message will be submitted. 27 | * @param operation The operation to be performed on a verifiable credential. 28 | * @param credentialHash The hash of a credential. 29 | * @param signerPublicKey Public key of the signer of this operation. 30 | */ 31 | public HcsVcTransaction(final TopicId topicId, final HcsVcOperation operation, 32 | final String credentialHash, final PublicKey signerPublicKey) { 33 | super(topicId); 34 | this.operation = operation; 35 | this.credentialHash = credentialHash; 36 | this.signerPublicKey = signerPublicKey; 37 | } 38 | 39 | /** 40 | * Instantiates a new transaction object from a message that was already prepared. 41 | * 42 | * @param topicId The HCS VC topic ID where message will be submitted. 43 | * @param message The message envelope. 44 | * @param signerPublicKey Public key of the signer of this operation. 45 | */ 46 | public HcsVcTransaction(final TopicId topicId, final MessageEnvelope message, 47 | final PublicKey signerPublicKey) { 48 | super(topicId, message); 49 | this.signerPublicKey = signerPublicKey; 50 | this.operation = null; 51 | this.credentialHash = null; 52 | } 53 | 54 | @Override 55 | protected void validate(final Validator validator) { 56 | super.validate(validator); 57 | 58 | // If built message was provided credential hash and operation are not mandatory 59 | validator.require(!StringUtils.isEmpty(credentialHash) || message != null, 60 | "Verifiable credential hash is null or empty."); 61 | validator.require(operation != null || message != null, "Operation on verifiable credential is not defined."); 62 | } 63 | 64 | @Override 65 | protected MessageEnvelope buildMessage() { 66 | return HcsVcMessage.fromCredentialHash(credentialHash, operation); 67 | } 68 | 69 | @Override 70 | protected MessageListener provideTopicListener(final TopicId topicIdToListen) { 71 | return new HcsVcTopicListener(topicIdToListen, s -> Lists.of(signerPublicKey)); 72 | } 73 | 74 | @Override 75 | protected UnaryOperator provideMessageEncrypter(final UnaryOperator encryptionFunction) { 76 | return HcsVcMessage.getEncrypter(encryptionFunction); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/hcs/vc/HcsVcStatusResolver.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs.vc; 2 | 3 | import com.hedera.hashgraph.identity.hcs.MessageEnvelope; 4 | import com.hedera.hashgraph.identity.hcs.MessageListener; 5 | import com.hedera.hashgraph.identity.hcs.MessageResolver; 6 | import com.hedera.hashgraph.sdk.PublicKey; 7 | import com.hedera.hashgraph.sdk.TopicId; 8 | import java.util.Collection; 9 | import java.util.Set; 10 | import java.util.function.Function; 11 | 12 | /** 13 | * Resolves the VC status from Hedera network. 14 | */ 15 | public class HcsVcStatusResolver extends MessageResolver { 16 | 17 | /** 18 | * A function providing a collection of public keys accepted for a given credential hash. 19 | * If the function is not supplied, the listener will not validate signatures. 20 | */ 21 | private Function> publicKeysProvider; 22 | 23 | /** 24 | * Instantiates a new status resolver for the given VC topic. 25 | * 26 | * @param topicId The HCS VC topic ID. 27 | */ 28 | public HcsVcStatusResolver(final TopicId topicId) { 29 | this(topicId, null); 30 | } 31 | 32 | /** 33 | * Instantiates a new status resolver for the given VC topic with signature validation. 34 | * 35 | * @param topicId The VC consensus topic ID. 36 | * @param publicKeysProvider Provider of a public keys acceptable for a given VC hash. 37 | */ 38 | public HcsVcStatusResolver(final TopicId topicId, 39 | final Function> publicKeysProvider) { 40 | super(topicId); 41 | this.publicKeysProvider = publicKeysProvider; 42 | } 43 | 44 | /** 45 | * Adds a credential hash to resolve its status. 46 | * 47 | * @param credentialHash The credential hash string. 48 | * @return This resolver instance. 49 | */ 50 | public HcsVcStatusResolver addCredentialHash(final String credentialHash) { 51 | if (credentialHash != null) { 52 | results.put(credentialHash, null); 53 | } 54 | return this; 55 | } 56 | 57 | /** 58 | * Adds multiple VC hashes to resolve. 59 | * 60 | * @param hashes The set of VC hash strings. 61 | * @return This resolver instance. 62 | */ 63 | public HcsVcStatusResolver addCredentialHashes(final Set hashes) { 64 | if (hashes != null) { 65 | hashes.forEach(d -> addCredentialHash(d)); 66 | } 67 | 68 | return this; 69 | } 70 | 71 | @Override 72 | protected boolean matchesSearchCriteria(final HcsVcMessage message) { 73 | return results.containsKey(message.getCredentialHash()); 74 | } 75 | 76 | @Override 77 | protected MessageListener supplyMessageListener() { 78 | return new HcsVcTopicListener(topicId, publicKeysProvider); 79 | } 80 | 81 | @Override 82 | protected void processMessage(final MessageEnvelope envelope) { 83 | HcsVcMessage message = envelope.open(); 84 | 85 | // Skip messages that are older than the once collected or if we already have a REVOKED message 86 | MessageEnvelope existing = results.get(message.getCredentialHash()); 87 | if (existing != null 88 | && (envelope.getConsensusTimestamp().isBefore(existing.getConsensusTimestamp()) 89 | || (HcsVcOperation.REVOKE.equals(existing.open().getOperation()) 90 | && !HcsVcOperation.REVOKE.equals(message.getOperation())))) { 91 | return; 92 | } 93 | 94 | // Add valid message to the results 95 | results.put(message.getCredentialHash(), envelope); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /docs/id-network-user-guide.md: -------------------------------------------------------------------------------- 1 | # Identity Network - User Guide 2 | 3 | --- 4 | 5 | - [Identity Network - User Guide](#identity-network---user-guide) 6 | - [Creation](#creation) 7 | - [Existing Network Instantiation](#existing-network-instantiation) 8 | 9 | --- 10 | 11 | ## Creation 12 | 13 | An identity appnet should have the following artifacts created on the Hedera mainnet: 14 | 15 | - [Address book][address-book] for the members of the appnet stored as a file in Hedera File Service 16 | - Hedera Consensus Service topic for DID Document messages. HCS messages creating, updating, or deleting DID Documents are submitted to this topic. 17 | - Hedera Consensus Service topic for Verifiable Credentials messages. HCS messages issuing, suspending, or revoking Verifiable Credentials are submitted to this topic. 18 | 19 | These could be set up manually by appnet administrators or can be created using `HcsIdentityNetworkBuilder` as follows: 20 | 21 | ```java 22 | PublicKey myPublicKey = ...; 23 | Client client = Client.forTestnet(); 24 | 25 | HcsIdentityNetwork identityNetwork = new HcsIdentityNetworkBuilder() 26 | .setNetwork("testnet") 27 | .setAppnetName("MyIdentityAppnet") 28 | .addAppnetDidServer("https://appnet-did-server-url:port/path-to-did-api") 29 | .setPublicKey(publicKey) 30 | .setMaxTransactionFee(new Hbar(2)) 31 | .setDidTopicMemo("MyIdentityAppnet DID topic") 32 | .setVCTopicMemo("MyIdentityAppnet VC topic") 33 | .execute(client); 34 | ``` 35 | 36 | `FileCreateTransaction` for address book file creation and `ConsensusTopicCreateTransaction` for DID and VC topic creation can be configured in a standard way as specified in Hedera Java SDK. 37 | 38 | ## Existing Network Instantiation 39 | 40 | Once the above identity network artifacts have been created, appnets will require `HcsIdentityNetwork` instance to interact with identity network. 41 | It can be initialized in multiple ways: 42 | 43 | - from an existing address book file stored by appnet - which will not require querying Hedera File Service: 44 | 45 | ```java 46 | // Read address book JSON from a local file (or another appnet's source) 47 | Path pathToAddressBookFile = Paths.get(""); 48 | String addressBookJson = new String(Files.readAllBytes(pathToAddressBookFile), StandardCharsets.UTF_8); 49 | FileId addressBookFileId = FileId.fromString(""); 50 | 51 | AddressBook addressBook = AddressBook.fromJson(addressBookJson, addressBookFileId); 52 | 53 | HcsIdentityNetwork identityNetwork = HcsIdentityNetwork.fromAddressBook(HederaNetwork.TESTNET, addressBook); 54 | ``` 55 | 56 | - from address book FileId - which will query Hedera File Service and read the content of the address book file: 57 | 58 | ```java 59 | Client client = Client.forTestnet(); 60 | Hbar maxFileQueryPayment = new Hbar(2); 61 | FileId addressBookFileId = FileId.fromString(""); 62 | 63 | HcsIdentityNetwork identityNetwork = HcsIdentityNetwork.fromAddressBookFile(client, HederaNetwork.TESTNET, addressBookFileId, maxFileQueryPayment); 64 | ``` 65 | 66 | - from a Hedera DID string that will extract `fid` parameter and query address book file content from Hedera File Service: 67 | 68 | ```java 69 | // Initialize network from this DID, reading address book file from Hedera File Service 70 | String did = "did:hedera:testnet:7c38oC4ytrYDGCqsaZ1AXt7ZPQ8etzfwaxoKjfJNzfoc;hedera:testnet:fid=0.0.1"; 71 | Client client = Client.forTestnet(); 72 | Hbar maxFileQueryPayment = new Hbar(2); 73 | 74 | HcsIdentityNetwork identityNetwork = HcsIdentityNetwork.fromHcsDid(client, HcsDid.fromString(did), maxFileQueryPayment); 75 | ``` 76 | 77 | [address-book]: https://github.com/hashgraph/did-method/blob/master/did-method-specification.md#appnet-address-book 78 | -------------------------------------------------------------------------------- /examples/appnet-api-server/src/main/java/com/hedera/hashgraph/identity/hcs/example/appnet/vc/DrivingLicenseDocument.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs.example.appnet.vc; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.JsonArray; 5 | import com.google.gson.JsonElement; 6 | import com.google.gson.JsonObject; 7 | import com.google.gson.annotations.Expose; 8 | import com.hedera.hashgraph.identity.hcs.vc.HcsVcDocumentBase; 9 | import com.hedera.hashgraph.identity.utils.JsonUtils; 10 | import java.util.LinkedHashMap; 11 | import java.util.UUID; 12 | 13 | /** 14 | * A simple, manually constructed example of a driving license verifiable credential document. 15 | */ 16 | public class DrivingLicenseDocument extends HcsVcDocumentBase { 17 | public static final String CREDENTIAL_SCHEMA_TYPE = "JsonSchemaValidator2018"; 18 | private static final String DOCUMENT_TYPE = "DrivingLicense"; 19 | private static final String EXAMPLE_ID_PREFIX = "https://example.appnet.com/driving-license/"; 20 | private static final String JSON_PROPERTY_CREDENTIAL_SUBJECT = "credentialSubject"; 21 | private static final String JSON_PROPERTY_PROOF = "proof"; 22 | private static final String[] JSON_PROPERTIES_ORDER = {"@context", "id", "type", "credentialSchema", 23 | "credentialSubject", "issuer", "issuanceDate", "proof"}; 24 | @Expose 25 | private CredentialSchema credentialSchema; 26 | 27 | @Expose 28 | private Ed25519CredentialProof proof; 29 | 30 | /** 31 | * Creates a new verifiable credential document instance with predefined types and auto-generated ID. 32 | */ 33 | public DrivingLicenseDocument() { 34 | super(); 35 | addType(DOCUMENT_TYPE); 36 | 37 | // Generate a unique identifier for this credential 38 | this.id = EXAMPLE_ID_PREFIX + UUID.randomUUID(); 39 | } 40 | 41 | /** 42 | * Note: this is a manual implementation of order JSON items. 43 | * In a real-world application it is recommended to use a JSON-LD compatible library to handle normalization. 44 | * However at this point the only available one in Java support JSON-LD version 1.0, but 1.1 is required by W3C 45 | * Verifiable Credentials. 46 | * 47 | * @param withoutProof Will skip 'proof' attribute if True. 48 | * @return A normalized JSON string representation of this document. 49 | */ 50 | public String toNormalizedJson(final boolean withoutProof) { 51 | Gson gson = JsonUtils.getGson(); 52 | 53 | // First turn to normal JSON 54 | JsonObject root = gson.toJsonTree(this).getAsJsonObject(); 55 | // Then put JSON properties in ordered map 56 | LinkedHashMap map = new LinkedHashMap<>(); 57 | 58 | JsonArray credentialSubjectsArray = new JsonArray(); 59 | for (String property : JSON_PROPERTIES_ORDER) { 60 | if (JSON_PROPERTY_CREDENTIAL_SUBJECT.equals(property) && getCredentialSubject() != null) { 61 | for (DrivingLicense dl : getCredentialSubject()) { 62 | credentialSubjectsArray.add(dl.toNormalizedJsonElement()); 63 | } 64 | 65 | map.put(property, credentialSubjectsArray); 66 | } else if (JSON_PROPERTY_PROOF.equals(property) && withoutProof) { 67 | continue; 68 | } else if (root.has(property)) { 69 | map.put(property, root.get(property)); 70 | } 71 | } 72 | // Turn map to JSON 73 | return gson.toJson(map); 74 | } 75 | 76 | public CredentialSchema getCredentialSchema() { 77 | return credentialSchema; 78 | } 79 | 80 | public void setCredentialSchema(final CredentialSchema credentialSchema) { 81 | this.credentialSchema = credentialSchema; 82 | } 83 | 84 | public Ed25519CredentialProof getProof() { 85 | return proof; 86 | } 87 | 88 | public void setProof(final Ed25519CredentialProof proof) { 89 | this.proof = proof; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/hcs/AddressBook.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import com.hedera.hashgraph.identity.utils.JsonUtils; 5 | import com.hedera.hashgraph.sdk.FileId; 6 | import java.util.List; 7 | import javax.annotation.Nullable; 8 | 9 | /** 10 | * Appent's address book for HCS identity network. 11 | */ 12 | public final class AddressBook { 13 | @Expose(serialize = false, deserialize = false) 14 | private FileId fileId; 15 | 16 | @Expose(serialize = true, deserialize = true) 17 | private String appnetName; 18 | 19 | @Expose(serialize = true, deserialize = true) 20 | private String didTopicId; 21 | 22 | @Expose(serialize = true, deserialize = true) 23 | private String vcTopicId; 24 | 25 | @Expose(serialize = true, deserialize = true) 26 | private List appnetDidServers; 27 | 28 | /** 29 | * Default constructor. 30 | */ 31 | private AddressBook() { 32 | } 33 | 34 | /** 35 | * Converts an address book JSON string into address book object. 36 | * 37 | * @param json Address book JSON file. 38 | * @param addressBookFileId FileId of this address book in Hedera File Service. 39 | * @return The {@link AddressBook}. 40 | */ 41 | public static AddressBook fromJson(final String json, final @Nullable FileId addressBookFileId) { 42 | AddressBook result = JsonUtils.getGson().fromJson(json, AddressBook.class); 43 | result.setFileId(addressBookFileId); 44 | 45 | return result; 46 | } 47 | 48 | /** 49 | * Creates a new {@link AddressBook} instance. Does not create the file on Hedera File Service!. 50 | * 51 | * @param appnetName Name of the appnet. 52 | * @param didTopicId TopicID of the DID topic. 53 | * @param vcTopicId Topic ID of the Verifiable Credentials topic. 54 | * @param appnetDidServers List of appnet API servers. 55 | * @return The {@link AddressBook}. 56 | */ 57 | public static AddressBook create( 58 | final String appnetName, 59 | final String didTopicId, 60 | final String vcTopicId, 61 | final @Nullable List appnetDidServers) { 62 | AddressBook result = new AddressBook(); 63 | result.appnetDidServers = appnetDidServers; 64 | result.didTopicId = didTopicId; 65 | result.vcTopicId = vcTopicId; 66 | result.appnetName = appnetName; 67 | 68 | return result; 69 | } 70 | 71 | /** 72 | * Converts this address book file into JSON string. 73 | * 74 | * @return The JSON representation of this address book. 75 | */ 76 | public String toJson() { 77 | return JsonUtils.getGson().toJson(this); 78 | } 79 | 80 | public String getAppnetName() { 81 | return appnetName; 82 | } 83 | 84 | public void setAppnetName(final String appnetName) { 85 | this.appnetName = appnetName; 86 | } 87 | 88 | public String getDidTopicId() { 89 | return didTopicId; 90 | } 91 | 92 | public void setDidTopicId(final String didTopicId) { 93 | this.didTopicId = didTopicId; 94 | } 95 | 96 | public String getVcTopicId() { 97 | return vcTopicId; 98 | } 99 | 100 | public void setVcTopicId(final String vcTopicId) { 101 | this.vcTopicId = vcTopicId; 102 | } 103 | 104 | public List getAppnetDidServers() { 105 | return appnetDidServers; 106 | } 107 | 108 | public void setAppnetDidServers(final List appnetDidServers) { 109 | this.appnetDidServers = appnetDidServers; 110 | } 111 | 112 | public FileId getFileId() { 113 | return fileId; 114 | } 115 | 116 | public void setFileId(final FileId fileId) { 117 | this.fileId = fileId; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /examples/appnet-api-server/README.md: -------------------------------------------------------------------------------- 1 | # Example Appnet Implementation 2 | 3 | --- 4 | - [Example Appnet Implementation](#example-appnet-implementation) 5 | - [About](#about) 6 | - [Usage](#usage) 7 | - [Configuration](#configuration) 8 | 9 | --- 10 | 11 | ## About 12 | 13 | This project is an example of how appnets can build identity networks on top of Hedera and utilize Hedera DID Method and Verifiable Credentials Registry. It is not a production-grade reference implementation, as this appnet exchanges and exposes private keys of issuers and credential owners via its API interface for demonstration purposes. It also does not use any persistent storage, clears all its data upon shutdown and listens to DID and VC topics from startup, ignoring past messages - so that each execution has a 'clean' demo state. 14 | 15 | ## Usage 16 | 17 | The appnet server can be started by the following command directly from the root folder of this repository: 18 | 19 | ```cmd 20 | gradle :appnet-api-server:run 21 | ``` 22 | 23 | The appnet runs on localhost port 5050 be default. It does not expose any user interface, instead there is a collection of [Postman][postman] requests available [here](/examples/appnet-api-server/postman-example-requests/e2e-flow.postman_collection) that demonstrate a full end-to-end flow of DID documents generation, publishing, update and deletion, as well as verifiable credential generation, issuance and revocation. 24 | 25 | There are three types of requests in this collection: 26 | 27 | - `DID` - reference requests of Appnet Relay Service as defined in [Hedera DID Method Specification][did-method-spec] 28 | - `VC` - reference requests of Appnet Relay Service as defined in [Verifiable Credentials Registry](/docs/vc-specification.md) 29 | - `DEMO` - additional requests for demonstration purposes. These in real-world application would be implemented differently, in secure environment (e.g. in a client wallet application). 30 | 31 | The collection is ready to be run end-to-end and automatically captures responses from previous requests as input for the next. 32 | 33 | ## Configuration 34 | 35 | The following environment variables are required to be set up before running the application: 36 | 37 | - `OPERATOR_ID` - Your testnet account ID. 38 | - `OPERATOR_KEY` - Your testnet account ID private key 39 | - `MIRROR_NODE_ADDRESS` - Address of the mirror node this application should connect to 40 | 41 | 42 | Additionally the following configuration of already initialized identity network can be provided. 43 | If it is missing, the application will create identity network artifacts upon startup and print them into system console. A new Hedera file containing the address book and two topic IDs will be created. 44 | 45 | If the file id below alone is provided, it will be used to fetch the VC and DiD topic IDs from the Hedera Network 46 | - `EXISTING_ADDRESS_BOOK_FILE_ID` (e.g. 0.0.19087) 47 | 48 | If the below is provided, topic IDs won't be fetched from the network, saving the cost of a file query 49 | - `EXISTING_ADDRESS_BOOK_JSON` (e.g. {"appnetName":"Example appnet using Hedera Identity SDK","didTopicId":"0.0.19085","vcTopicId":"0.0.19086","appnetDidServers":["http://localhost:5050/"]}) 50 | 51 | Finally, the following two optional parameters may be set (they will default to 10 if unset). They determine how frequently DiDs and VCs are persisted to file. If for example the value is 5, then every 5 VC operations, the state of the VCs will be persisted. 52 | Providing a large number may improve performance due to fewer file operations, however upon restart, catching up with mirror node to rebuild state to its latest may take longer. 53 | 54 | - `DID_PERSIST_INTERVAL` - how frequently should DiDs be persisted to file 55 | - `VC_PERSIST_INTERVAL` - how frequently should VCs be persisted to file 56 | 57 | Persisted data resides in the `persistedCredentialIssuers.ser`, `persistedDiDs.ser`, `persistedSignatures.ser` and `persistedVCs.ser` of the application's folder. They are binary files and not human readable. 58 | 59 | [did-method-spec]: https://github.com/hashgraph/did-method 60 | [postman]: https://www.postman.com/ 61 | -------------------------------------------------------------------------------- /src/test/java/com/hedera/hashgraph/identity/hcs/vc/HcsVcDocumentOperationsTest.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs.vc; 2 | 3 | import com.google.common.collect.Lists; 4 | import com.hedera.hashgraph.identity.hcs.MessageEnvelope; 5 | import com.hedera.hashgraph.identity.hcs.NetworkReadyTestBase; 6 | import com.hedera.hashgraph.identity.hcs.did.HcsDid; 7 | import com.hedera.hashgraph.sdk.PrivateKey; 8 | import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; 9 | import org.junit.jupiter.api.Order; 10 | import org.junit.jupiter.api.Test; 11 | import org.junit.jupiter.api.TestInstance; 12 | import org.junit.jupiter.api.TestMethodOrder; 13 | import org.threeten.bp.Instant; 14 | 15 | import static org.junit.jupiter.api.Assertions.*; 16 | 17 | /** 18 | * Tests operations on verifiable credentials and their status resolution. 19 | */ 20 | @TestInstance(TestInstance.Lifecycle.PER_CLASS) 21 | @TestMethodOrder(OrderAnnotation.class) 22 | class HcsVcDocumentOperationsTest extends NetworkReadyTestBase { 23 | private HcsDid issuer; 24 | private HcsDid owner; 25 | private HcsVcDocumentBase vc; 26 | private String credentialHash; 27 | private PrivateKey issuersPrivateKey; 28 | 29 | @Override 30 | protected void beforeAll() { 31 | issuer = didNetwork.generateDid(false); 32 | issuersPrivateKey = issuer.getPrivateDidRootKey().get(); 33 | 34 | owner = didNetwork.generateDid(false); 35 | 36 | // For tests only we do not need to submit DID documents, as we will not validate them. 37 | // final DidMethodOperation op = DidMethodOperation.CREATE; 38 | // sendDidTransaction(issuer, issuer.generateDidDocument().toJson(), op, EXPECT_NO_ERROR); 39 | // sendDidTransaction(owner, owner.generateDidDocument().toJson(), op, EXPECT_NO_ERROR); 40 | 41 | // Create an example Verifiable Credential. 42 | vc = new HcsVcDocumentBase(); 43 | vc.setIssuer(issuer); 44 | vc.setIssuanceDate(Instant.now()); 45 | vc.addCredentialSubject(new DemoAccessCredential(owner.toDid(), true, false, false)); 46 | 47 | credentialHash = vc.toCredentialHash(); 48 | } 49 | 50 | private void testVcOperation(HcsVcOperation op) { 51 | MessageEnvelope envelope = sendVcTransaction(op, credentialHash, issuersPrivateKey, EXPECT_NO_ERROR); 52 | assertNotNull(envelope); 53 | 54 | HcsVcMessage msg = envelope.open(); 55 | 56 | // Check results 57 | assertNotNull(msg); 58 | assertTrue(msg.isValid()); 59 | assertEquals(op, msg.getOperation()); 60 | assertEquals(credentialHash, msg.getCredentialHash()); 61 | } 62 | 63 | private void testVcStatusResolution(HcsVcOperation expectedOperation) { 64 | MessageEnvelope envelope = resolveVcStatus( 65 | credentialHash, 66 | m -> Lists.newArrayList(issuersPrivateKey.getPublicKey()), 67 | EXPECT_NO_ERROR); 68 | 69 | assertNotNull(envelope); 70 | 71 | HcsVcMessage msg = envelope.open(); 72 | 73 | assertNotNull(msg); 74 | assertTrue(msg.isValid()); 75 | assertEquals(credentialHash, msg.getCredentialHash()); 76 | assertEquals(expectedOperation, msg.getOperation()); 77 | } 78 | 79 | @Test 80 | @Order(1) 81 | void testIssue() { 82 | testVcOperation(HcsVcOperation.ISSUE); 83 | testVcStatusResolution(HcsVcOperation.ISSUE); 84 | } 85 | 86 | @Test 87 | @Order(2) 88 | void testSuspend() { 89 | testVcOperation(HcsVcOperation.SUSPEND); 90 | testVcStatusResolution(HcsVcOperation.SUSPEND); 91 | } 92 | 93 | @Test 94 | @Order(3) 95 | void testResume() { 96 | testVcOperation(HcsVcOperation.RESUME); 97 | testVcStatusResolution(HcsVcOperation.RESUME); 98 | } 99 | 100 | @Test 101 | @Order(4) 102 | void testRevoke() { 103 | testVcOperation(HcsVcOperation.REVOKE); 104 | testVcStatusResolution(HcsVcOperation.REVOKE); 105 | } 106 | 107 | @Test 108 | @Order(5) 109 | void testInvalidResumeAfterRevoke() { 110 | testVcOperation(HcsVcOperation.RESUME); 111 | // Status should still be revoked 112 | testVcStatusResolution(HcsVcOperation.REVOKE); 113 | 114 | testVcOperation(HcsVcOperation.SUSPEND); 115 | // Status should still be revoked 116 | testVcStatusResolution(HcsVcOperation.REVOKE); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /gradle/config/codenarc/codenarc.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/hcs/vc/HcsVcTopicListener.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs.vc; 2 | 3 | import com.hedera.hashgraph.identity.hcs.MessageEnvelope; 4 | import com.hedera.hashgraph.identity.hcs.MessageListener; 5 | import com.hedera.hashgraph.sdk.PublicKey; 6 | import com.hedera.hashgraph.sdk.TopicId; 7 | import com.hedera.hashgraph.sdk.TopicMessage; 8 | import java.util.Collection; 9 | import java.util.function.Function; 10 | import java8.util.function.BiFunction; 11 | import org.threeten.bp.Instant; 12 | 13 | /** 14 | * A listener of confirmed {@link HcsVcMessage} messages from a VC topic. 15 | * Messages are received from a given mirror node, parsed and validated. 16 | */ 17 | public class HcsVcTopicListener extends MessageListener { 18 | 19 | /** 20 | * A function providing a collection of public keys accepted for a given credential hash. 21 | * If the function is not supplied, the listener will not validate signatures. 22 | */ 23 | private Function> publicKeysProvider; 24 | 25 | /** 26 | * Creates a new instance of a VC topic listener for the given consensus topic. 27 | * By default, invalid messages are ignored and errors are not. 28 | * Listener without a public key provider will not validate message signatures. 29 | * 30 | * @param vcTopicId The VC consensus topic ID. 31 | */ 32 | public HcsVcTopicListener(final TopicId vcTopicId) { 33 | this(vcTopicId, null); 34 | } 35 | 36 | /** 37 | * Creates a new instance of a VC topic listener for the given consensus topic. 38 | * By default, invalid messages are ignored and errors are not. 39 | * 40 | * @param vcTopicId The VC consensus topic ID. 41 | * @param publicKeysProvider Provider of a public keys acceptable for a given VC hash. 42 | */ 43 | public HcsVcTopicListener(final TopicId vcTopicId, 44 | final Function> publicKeysProvider) { 45 | super(vcTopicId); 46 | this.publicKeysProvider = publicKeysProvider; 47 | } 48 | 49 | @Override 50 | protected MessageEnvelope extractMessage(final TopicMessage response) { 51 | MessageEnvelope result = null; 52 | try { 53 | result = MessageEnvelope.fromMirrorResponse(response, HcsVcMessage.class); 54 | } catch (Exception err) { 55 | handleError(err); 56 | } 57 | 58 | return result; 59 | } 60 | 61 | @Override 62 | protected boolean isMessageValid(final MessageEnvelope envelope, 63 | final TopicMessage response) { 64 | try { 65 | BiFunction msgDecrypter = decrypter == null ? null 66 | : HcsVcMessage.getDecrypter(decrypter); 67 | 68 | HcsVcMessage message = envelope.open(msgDecrypter); 69 | if (message == null) { 70 | reportInvalidMessage(response, "Empty message received when opening envelope"); 71 | return false; 72 | } 73 | 74 | if (!message.isValid()) { 75 | reportInvalidMessage(response, "Message content validation failed."); 76 | return false; 77 | } 78 | 79 | // Validate signature only if public key provider has been supplied. 80 | if (publicKeysProvider != null && !isSignatureAccepted(envelope)) { 81 | reportInvalidMessage(response, "Signature validation failed"); 82 | return false; 83 | } 84 | 85 | return true; 86 | } catch (Exception err) { 87 | handleError(err); 88 | reportInvalidMessage(response, "Exception while validating message: " + err.getMessage()); 89 | return false; 90 | } 91 | } 92 | 93 | /** 94 | * Checks if the signature on the envelope is accepted by any public key supplied for the credential hash. 95 | * 96 | * @param envelope The message envelope. 97 | * @return True if signature is accepted, false otherwise. 98 | */ 99 | private boolean isSignatureAccepted(final MessageEnvelope envelope) { 100 | if (publicKeysProvider == null) { 101 | return false; 102 | } 103 | 104 | Collection acceptedKeys = publicKeysProvider.apply(envelope.open().getCredentialHash()); 105 | if (acceptedKeys == null || acceptedKeys.isEmpty()) { 106 | return false; 107 | } 108 | 109 | for (PublicKey publicKey : acceptedKeys) { 110 | if (envelope.isSignatureValid(e -> publicKey)) { 111 | return true; 112 | } 113 | } 114 | 115 | return false; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/hcs/vc/HcsVcMessage.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs.vc; 2 | 3 | import com.google.common.base.Strings; 4 | import com.google.gson.annotations.Expose; 5 | import com.hedera.hashgraph.identity.hcs.Message; 6 | import com.hedera.hashgraph.identity.hcs.MessageEnvelope; 7 | import java.nio.charset.StandardCharsets; 8 | import java.util.Base64; 9 | import java.util.function.UnaryOperator; 10 | import java8.util.function.BiFunction; 11 | import org.threeten.bp.Instant; 12 | 13 | /** 14 | * The Verifiable Credential message. 15 | */ 16 | public class HcsVcMessage extends Message { 17 | @Expose(serialize = true, deserialize = true) 18 | private final HcsVcOperation operation; 19 | 20 | @Expose(serialize = true, deserialize = true) 21 | private final String credentialHash; 22 | 23 | /** 24 | * Creates a new message instance. 25 | * 26 | * @param operation Operation type. 27 | * @param credentialHash Credential hash. 28 | */ 29 | protected HcsVcMessage(final HcsVcOperation operation, final String credentialHash) { 30 | super(); 31 | this.operation = operation; 32 | this.credentialHash = credentialHash; 33 | } 34 | 35 | /** 36 | * Provides an encryption operator that converts an {@link HcsVcMessage} into encrypted one. 37 | * 38 | * @param encryptionFunction The encryption function to use for encryption of single attributes. 39 | * @return The encryption operator instance. 40 | */ 41 | public static UnaryOperator getEncrypter(final UnaryOperator encryptionFunction) { 42 | if (encryptionFunction == null) { 43 | throw new IllegalArgumentException("Encryption function is missing or null."); 44 | } 45 | 46 | return message -> { 47 | 48 | // Encrypt the credential hash 49 | byte[] encryptedHash = encryptionFunction.apply(message.getCredentialHash().getBytes(StandardCharsets.UTF_8)); 50 | String hash = new String(Base64.getEncoder().encode(encryptedHash), StandardCharsets.UTF_8); 51 | 52 | return new HcsVcMessage(message.getOperation(), hash); 53 | }; 54 | } 55 | 56 | /** 57 | * Provides a decryption function that converts {@link HcsVcMessage} in encrypted for into a plain form. 58 | * 59 | * @param decryptionFunction The decryption function to use for decryption of single attributes. 60 | * @return The decryption function for the {@link HcsVcMessage} 61 | */ 62 | public static BiFunction getDecrypter( 63 | final BiFunction decryptionFunction) { 64 | if (decryptionFunction == null) { 65 | throw new IllegalArgumentException("Decryption function is missing or null."); 66 | } 67 | 68 | return (encryptedMsg, consensusTimestamp) -> { 69 | 70 | // Decrypt DID string 71 | String decryptedHash = encryptedMsg.getCredentialHash(); 72 | if (decryptedHash != null) { 73 | byte[] hashBytes = Base64.getDecoder().decode(decryptedHash.getBytes(StandardCharsets.UTF_8)); 74 | hashBytes = decryptionFunction.apply(hashBytes, consensusTimestamp); 75 | decryptedHash = new String(hashBytes, StandardCharsets.UTF_8); 76 | } 77 | 78 | return new HcsVcMessage(encryptedMsg.getOperation(), decryptedHash); 79 | }; 80 | } 81 | 82 | /** 83 | * Creates a new VC message for submission to HCS topic. 84 | * 85 | * @param credentialHash VC hash. 86 | * @param operation The operation on a VC document. 87 | * @return The HCS message wrapped in an envelope for the given VC and operation. 88 | */ 89 | public static MessageEnvelope fromCredentialHash(final String credentialHash, 90 | final HcsVcOperation operation) { 91 | HcsVcMessage message = new HcsVcMessage(operation, credentialHash); 92 | return new MessageEnvelope<>(message); 93 | } 94 | 95 | /** 96 | * Checks if the message is valid from content point of view. 97 | * Does not verify hash nor any signatures. 98 | * 99 | * @return True if the message is valid and False otherwise. 100 | */ 101 | public boolean isValid() { 102 | return !Strings.isNullOrEmpty(credentialHash) && operation != null; 103 | } 104 | 105 | public HcsVcOperation getOperation() { 106 | return operation; 107 | } 108 | 109 | public String getCredentialHash() { 110 | return credentialHash; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /docs/sdk-javadocs/deprecated-list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Deprecated List (did-sdk-java 1.0.0 API) 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 17 | 18 | 19 | 20 | 32 | 35 |
36 | 92 |
93 |
94 |
95 |

Deprecated API

96 |

Contents

97 |
98 |
99 |
100 | 143 |
144 | 145 | 146 | -------------------------------------------------------------------------------- /gradle/config/cpd/cpdhtml.xslt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 11 | 12 | 13 | 14 | 15 | 31 | 40 | 41 | 42 |

Summary of duplicated code

43 | This page summarizes the code fragments that have been found to be 44 | replicated in the code. 45 |

46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 57 | 60 | 63 | 67 | 68 |
# duplicationsTotal linesTotal tokensApprox # bytes
55 | 56 | 58 | 59 | 61 | 62 | 64 | 66 |
69 |

70 | You expand and collapse the code fragments using the + buttons. You 71 | can also navigate to the source code by clicking 72 | on the file names. 73 |

74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 107 | 111 | 112 | 113 | 115 | 136 | 137 | 138 | 141 | 142 | 143 |
IDFilesLines
85 | 86 | 88 | 89 | 90 | 91 | 99 | 103 | 104 | 105 |
92 | 93 | ../src/.html# 96 | 97 | 98 | 100 | line 101 | 102 |
106 |
108 | # lines : 109 | 110 |
114 | 116 | 117 | 118 | 125 | 133 | 134 |
119 | 124 | 126 | 132 |
135 |
139 |
140 |
144 | 145 | 146 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /docs/sdk-javadocs/jquery/jszip-utils/dist/jszip-utils.js: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | JSZipUtils - A collection of cross-browser utilities to go along with JSZip. 4 | 5 | 6 | (c) 2014 Stuart Knightley, David Duponchel 7 | Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip-utils/master/LICENSE.markdown. 8 | 9 | */ 10 | !function(e){"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define(e):"undefined"!=typeof window?window.JSZipUtils=e():"undefined"!=typeof global?global.JSZipUtils=e():"undefined"!=typeof self&&(self.JSZipUtils=e())}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o vc = new HcsVcDocumentBase(); 32 | 33 | // Should fail as no issuer is set. 34 | assertFalse(vc.isComplete()); 35 | 36 | vc.setIssuer(issuer); 37 | 38 | // Should fail as no issuance date is set. 39 | assertFalse(vc.isComplete()); 40 | 41 | vc.setIssuanceDate(Instant.now()); 42 | 43 | // Should fail as no credential subject is set. 44 | assertFalse(vc.isComplete()); 45 | 46 | // Default VC type should be set. 47 | assertNotNull(vc.getType()); 48 | assertEquals(1, vc.getType().size()); 49 | 50 | // Add a custom type 51 | vc.addType("TestVC"); 52 | assertEquals(2, vc.getType().size()); 53 | 54 | // Default context should be set 55 | assertNotNull(vc.getContext()); 56 | assertEquals(1, vc.getContext().size()); 57 | 58 | // Add a custom context 59 | vc.addContext("https://www.example.com/testContext"); 60 | assertEquals(2, vc.getContext().size()); 61 | 62 | // Add a credential subject. 63 | assertNull(vc.getCredentialSubject()); 64 | DemoAccessCredential credential = new DemoAccessCredential(owner.toDid(), true, false, false); 65 | vc.addCredentialSubject(credential); 66 | 67 | // Make sure it's there 68 | assertNotNull(vc.getCredentialSubject()); 69 | assertEquals(1, vc.getCredentialSubject().size()); 70 | 71 | // Now all mandatory fields should be set 72 | assertTrue(vc.isComplete()); 73 | } 74 | 75 | @Test 76 | void testVcJsonConversion() { 77 | HcsVcDocumentBase vc = new HcsVcDocumentBase(); 78 | vc.setId("example:test:vc:id"); 79 | vc.setIssuer(new Issuer(issuer.toDid(), "My Company Ltd.")); 80 | vc.setIssuanceDate(Instant.now()); 81 | 82 | DemoAccessCredential subject = new DemoAccessCredential(owner.toDid(), true, false, false); 83 | vc.addCredentialSubject(subject); 84 | 85 | // Convert to JSON 86 | String json = vc.toJson(); 87 | assertFalse(Strings.isNullOrEmpty(json)); 88 | 89 | // Convert back to VC document and compare 90 | HcsVcDocumentBase vcFromJson = HcsVcDocumentBase.fromJson(json, DemoAccessCredential.class); 91 | // Test simple properties 92 | assertNotNull(vcFromJson); 93 | assertEquals(vc.getType(), vcFromJson.getType()); 94 | assertEquals(vc.getContext(), vcFromJson.getContext()); 95 | assertEquals(vc.getIssuanceDate(), vcFromJson.getIssuanceDate()); 96 | assertEquals(vc.getId(), vcFromJson.getId()); 97 | 98 | // Test issuer object 99 | assertNotNull(vcFromJson.getIssuer()); 100 | assertEquals(vc.getIssuer().getId(), vcFromJson.getIssuer().getId()); 101 | assertEquals(vc.getIssuer().getName(), vcFromJson.getIssuer().getName()); 102 | 103 | // Test credential subject 104 | assertNotNull(vcFromJson.getCredentialSubject()); 105 | 106 | DemoAccessCredential subjectFromJson = vcFromJson.getCredentialSubject().get(0); 107 | assertEquals(subject.getId(), subjectFromJson.getId()); 108 | assertEquals(subject.getBlueLevel(), subjectFromJson.getBlueLevel()); 109 | assertEquals(subject.getGreenLevel(), subjectFromJson.getGreenLevel()); 110 | assertEquals(subject.getRedLevel(), subjectFromJson.getRedLevel()); 111 | } 112 | 113 | @Test 114 | void testCredentialHash() { 115 | DemoVerifiableCredentialDocument vc = new DemoVerifiableCredentialDocument(); 116 | vc.setId("example:test:vc:id"); 117 | vc.setIssuer(issuer); 118 | vc.setIssuanceDate(Instant.now()); 119 | vc.addCredentialSubject(new DemoAccessCredential(owner.toDid(), true, false, false)); 120 | vc.setCustomProperty("Custom property value 1"); 121 | 122 | String credentialHash = vc.toCredentialHash(); 123 | assertFalse(Strings.isNullOrEmpty(credentialHash)); 124 | 125 | // Recalculation should give the same value 126 | assertEquals(credentialHash, vc.toCredentialHash()); 127 | 128 | // Hash shall not change if we don't change anything in the document 129 | vc.setCustomProperty("Another value for custom property"); 130 | vc.addCredentialSubject(new DemoAccessCredential(owner.toDid(), false, false, true)); 131 | 132 | assertEquals(credentialHash, vc.toCredentialHash()); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/DidDocumentBase.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.JsonArray; 5 | import com.google.gson.JsonElement; 6 | import com.google.gson.JsonObject; 7 | import com.google.gson.JsonParser; 8 | import com.google.gson.annotations.Expose; 9 | import com.google.gson.annotations.SerializedName; 10 | import com.hedera.hashgraph.identity.hcs.did.HcsDidRootKey; 11 | import com.hedera.hashgraph.identity.utils.JsonUtils; 12 | import java.util.Iterator; 13 | 14 | /** 15 | * The base for a DID document generation in JSON-LD format. 16 | * DID documents according to W3C draft specification must be compatible with JSON-LD version 1.1 Up until now there is 17 | * no Java implementation library of JSON-LD version 1.1. For that reason this object represents only the most basic and 18 | * mandatory attributes from the DID specification and Hedera HCS DID method specification point of view. Applications 19 | * shall extend it with any DID document properties or custom properties they require. 20 | */ 21 | public class DidDocumentBase { 22 | 23 | @Expose(serialize = true, deserialize = false) 24 | @SerializedName(DidDocumentJsonProperties.CONTEXT) 25 | protected String context; 26 | 27 | @Expose(serialize = true, deserialize = true) 28 | @SerializedName(DidDocumentJsonProperties.ID) 29 | protected String id; 30 | 31 | @Expose(serialize = false, deserialize = false) 32 | protected HcsDidRootKey didRootKey; 33 | 34 | /** 35 | * Creates a new DID Document for the specified DID string. 36 | * 37 | * @param did The DID string. 38 | */ 39 | public DidDocumentBase(final String did) { 40 | this.id = did; 41 | this.context = DidSyntax.DID_DOCUMENT_CONTEXT; 42 | } 43 | 44 | /** 45 | * Converts a DID document in JSON format into a {@link DidDocumentBase} object. 46 | * Please note this conversion respects only the fields of the base DID document. All other fields are ignored. 47 | * 48 | * @param json The DID document as JSON string.f 49 | * @return The {@link DidDocumentBase}. 50 | */ 51 | public static DidDocumentBase fromJson(final String json) { 52 | Gson gson = JsonUtils.getGson(); 53 | 54 | DidDocumentBase result = null; 55 | 56 | try { 57 | JsonObject root = JsonParser.parseString(json).getAsJsonObject(); 58 | result = gson.fromJson(root, DidDocumentBase.class); 59 | 60 | if (root.has(DidDocumentJsonProperties.PUBLIC_KEY)) { 61 | Iterator itr = root.getAsJsonArray(DidDocumentJsonProperties.PUBLIC_KEY).iterator(); 62 | while (itr.hasNext()) { 63 | JsonObject publicKeyObj = itr.next().getAsJsonObject(); 64 | if (publicKeyObj.has(DidDocumentJsonProperties.ID) 65 | && publicKeyObj.get(DidDocumentJsonProperties.ID).getAsString() 66 | .equals(result.getId() + HcsDidRootKey.DID_ROOT_KEY_NAME)) { 67 | result.setDidRootKey(gson.fromJson(publicKeyObj, HcsDidRootKey.class)); 68 | break; 69 | } 70 | } 71 | } 72 | } catch (Exception e) { 73 | throw new IllegalArgumentException("Given JSON string is not a valid DID document", e); 74 | } 75 | 76 | return result; 77 | } 78 | 79 | /** 80 | * Converts this DID document into JSON string. 81 | * 82 | * @return The JSON representation of this document. 83 | */ 84 | public String toJson() { 85 | Gson gson = JsonUtils.getGson(); 86 | 87 | JsonElement jsonElement = gson.toJsonTree(this); 88 | JsonObject rootObject = jsonElement.getAsJsonObject(); 89 | 90 | addDidRootKeyToPublicKeys(rootObject); 91 | addDidRootKeyToAuthentication(rootObject); 92 | 93 | return gson.toJson(jsonElement); 94 | } 95 | 96 | /** 97 | * Adds #did-root-key to authentication section of the DID document if it is not defined. 98 | * 99 | * @param rootObject The root object of DID Document as JsonObject. 100 | */ 101 | private void addDidRootKeyToAuthentication(final JsonObject rootObject) { 102 | if (!rootObject.has(DidDocumentJsonProperties.AUTHENTICATION)) { 103 | rootObject.add(DidDocumentJsonProperties.AUTHENTICATION, new JsonArray()); 104 | } 105 | 106 | JsonElement authElement = rootObject.get(DidDocumentJsonProperties.AUTHENTICATION); 107 | if (authElement.isJsonArray() && authElement.getAsJsonArray().size() == 0) { 108 | authElement.getAsJsonArray().add(didRootKey.getId()); 109 | } 110 | } 111 | 112 | /** 113 | * Adds a #did-root-key to public keys of the DID document. 114 | * 115 | * @param rootObject The root object of DID Document as JsonObject. 116 | */ 117 | protected void addDidRootKeyToPublicKeys(final JsonObject rootObject) { 118 | JsonArray publicKeys = null; 119 | if (rootObject.has(DidDocumentJsonProperties.PUBLIC_KEY)) { 120 | publicKeys = rootObject.getAsJsonArray(DidDocumentJsonProperties.PUBLIC_KEY); 121 | } else { 122 | publicKeys = new JsonArray(1); 123 | rootObject.add(DidDocumentJsonProperties.PUBLIC_KEY, publicKeys); 124 | } 125 | 126 | publicKeys.add(JsonUtils.getGson().toJsonTree(didRootKey)); 127 | } 128 | 129 | public String getContext() { 130 | return context; 131 | } 132 | 133 | public String getId() { 134 | return id; 135 | } 136 | 137 | public HcsDidRootKey getDidRootKey() { 138 | return didRootKey; 139 | } 140 | 141 | public void setDidRootKey(final HcsDidRootKey didRootKey) { 142 | this.didRootKey = didRootKey; 143 | } 144 | 145 | } 146 | -------------------------------------------------------------------------------- /src/main/java/com/hedera/hashgraph/identity/hcs/HcsIdentityNetworkBuilder.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs; 2 | 3 | import com.google.common.base.Charsets; 4 | import com.hedera.hashgraph.sdk.Client; 5 | import com.hedera.hashgraph.sdk.FileCreateTransaction; 6 | import com.hedera.hashgraph.sdk.FileId; 7 | import com.hedera.hashgraph.sdk.Hbar; 8 | import com.hedera.hashgraph.sdk.PrecheckStatusException; 9 | import com.hedera.hashgraph.sdk.PublicKey; 10 | import com.hedera.hashgraph.sdk.ReceiptStatusException; 11 | import com.hedera.hashgraph.sdk.TopicCreateTransaction; 12 | import com.hedera.hashgraph.sdk.TopicId; 13 | import com.hedera.hashgraph.sdk.TransactionReceipt; 14 | import com.hedera.hashgraph.sdk.TransactionResponse; 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | import java.util.concurrent.TimeoutException; 18 | 19 | /** 20 | * The builder used to create new appnet identity networks based on Hedera HCS DID method specification. 21 | */ 22 | public class HcsIdentityNetworkBuilder { 23 | private String appnetName; 24 | private String network; 25 | private List didServers; 26 | private PublicKey publicKey; 27 | private Hbar maxTransactionFee = Hbar.from(2); 28 | private String didTopicMemo = ""; 29 | private String vcTopicMemo = ""; 30 | 31 | /** 32 | * Creates a new identity network for the given appnet. 33 | * This will initialize an address book file on Hedera File Service. 34 | * Then DID and VC topics on Hedera network will be created unless already existing topics are provided. 35 | * 36 | * @param client Hedera client 37 | * @return The new identity network. 38 | * @throws ReceiptStatusException in the event the receipt contains an error 39 | * @throws PrecheckStatusException in the event the transaction isn't validated by the network 40 | * @throws TimeoutException in the event the client fails to communicate with the network in a timely fashion 41 | */ 42 | public HcsIdentityNetwork execute(final Client client) 43 | throws ReceiptStatusException, PrecheckStatusException, TimeoutException { 44 | 45 | TopicCreateTransaction didTopicCreateTransaction = new TopicCreateTransaction() 46 | .setMaxTransactionFee(maxTransactionFee) 47 | .setTopicMemo(didTopicMemo); 48 | if (publicKey != null) { 49 | didTopicCreateTransaction.setAdminKey(publicKey); 50 | } 51 | 52 | TransactionResponse didTxId = didTopicCreateTransaction 53 | .execute(client); 54 | TopicId didTopicId = didTxId.getReceipt(client).topicId; 55 | 56 | TopicCreateTransaction vcTopicCreateTransaction = new TopicCreateTransaction() 57 | .setMaxTransactionFee(maxTransactionFee) 58 | .setTopicMemo(vcTopicMemo); 59 | if (publicKey != null) { 60 | vcTopicCreateTransaction.setAdminKey(publicKey); 61 | } 62 | 63 | TransactionResponse vcTxId = vcTopicCreateTransaction.execute(client); 64 | TopicId vcTopicId = vcTxId.getReceipt(client).topicId; 65 | 66 | AddressBook addressBook = AddressBook 67 | .create(appnetName, didTopicId.toString(), vcTopicId.toString(), didServers); 68 | 69 | FileCreateTransaction fileCreateTx = new FileCreateTransaction() 70 | .setContents(addressBook.toJson().getBytes(Charsets.UTF_8)); 71 | 72 | TransactionResponse response = fileCreateTx.execute(client); 73 | TransactionReceipt receipt = response.getReceipt(client); 74 | FileId fileId = receipt.fileId; 75 | 76 | addressBook.setFileId(fileId); 77 | 78 | return HcsIdentityNetwork.fromAddressBook(network, addressBook); 79 | } 80 | 81 | /** 82 | * Adds an appnet server URL that hosts DID REST API as specified by Hedera HCS DID method. 83 | * 84 | * @param serverUrl The URL to the appnet's server DID REST service. 85 | * @return This identity network builder instance. 86 | */ 87 | public HcsIdentityNetworkBuilder addAppnetDidServer(final String serverUrl) { 88 | if (didServers == null) { 89 | didServers = new ArrayList<>(1); 90 | } 91 | 92 | if (!didServers.contains(serverUrl)) { 93 | didServers.add(serverUrl); 94 | } 95 | 96 | return this; 97 | } 98 | 99 | /** 100 | * Defines the name of the appnet for identity network address book. 101 | * 102 | * @param appnetName The name of the appnet. 103 | * @return This identity network builder instance. 104 | */ 105 | public HcsIdentityNetworkBuilder setAppnetName(final String appnetName) { 106 | this.appnetName = appnetName; 107 | return this; 108 | } 109 | 110 | /** 111 | * Defines the memo for the DiD Topic. 112 | * 113 | * @param didTopicMemo The memo for the DiD Topic 114 | * @return This identity network builder instance. 115 | */ 116 | public HcsIdentityNetworkBuilder setDidTopicMemo(final String didTopicMemo) { 117 | this.didTopicMemo = didTopicMemo; 118 | return this; 119 | } 120 | 121 | /** 122 | * Defines the memo for the VC Topic. 123 | * 124 | * @param vcTopicMemo The memo for the VC Topic 125 | * @return This identity network builder instance. 126 | */ 127 | public HcsIdentityNetworkBuilder setVCTopicMemo(final String vcTopicMemo) { 128 | this.vcTopicMemo = vcTopicMemo; 129 | return this; 130 | } 131 | 132 | /** 133 | * Sets the max fee for transactions. 134 | * 135 | * @param maxTransactionFee The max transaction fee in hBar 136 | * @return This identity network builder instance. 137 | */ 138 | public HcsIdentityNetworkBuilder setMaxTransactionFee(final Hbar maxTransactionFee) { 139 | this.maxTransactionFee = maxTransactionFee; 140 | return this; 141 | } 142 | 143 | /** 144 | * Sets the key for topic submission. 145 | * 146 | * @param publicKey The publicKey to use. 147 | * @return This identity network builder instance. 148 | */ 149 | public HcsIdentityNetworkBuilder setPublicKey(final PublicKey publicKey) { 150 | this.publicKey = publicKey; 151 | return this; 152 | } 153 | 154 | /** 155 | * Defines the Hedera Network on which the identities and credentials are registered. 156 | * 157 | * @param network The network to set. 158 | * @return This identity network builder instance. 159 | */ 160 | public HcsIdentityNetworkBuilder setNetwork(final String network) { 161 | this.network = network; 162 | return this; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin or MSYS, switch paths to Windows format before running java 129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=`expr $i + 1` 158 | done 159 | case $i in 160 | 0) set -- ;; 161 | 1) set -- "$args0" ;; 162 | 2) set -- "$args0" "$args1" ;; 163 | 3) set -- "$args0" "$args1" "$args2" ;; 164 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=`save "$@"` 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | exec "$JAVACMD" "$@" 184 | -------------------------------------------------------------------------------- /docs/sdk-javadocs/script.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | var moduleSearchIndex; 27 | var packageSearchIndex; 28 | var typeSearchIndex; 29 | var memberSearchIndex; 30 | var tagSearchIndex; 31 | function loadScripts(doc, tag) { 32 | createElem(doc, tag, 'jquery/jszip/dist/jszip.js'); 33 | createElem(doc, tag, 'jquery/jszip-utils/dist/jszip-utils.js'); 34 | if (window.navigator.userAgent.indexOf('MSIE ') > 0 || window.navigator.userAgent.indexOf('Trident/') > 0 || 35 | window.navigator.userAgent.indexOf('Edge/') > 0) { 36 | createElem(doc, tag, 'jquery/jszip-utils/dist/jszip-utils-ie.js'); 37 | } 38 | createElem(doc, tag, 'search.js'); 39 | 40 | $.get(pathtoroot + "module-search-index.zip") 41 | .done(function() { 42 | JSZipUtils.getBinaryContent(pathtoroot + "module-search-index.zip", function(e, data) { 43 | JSZip.loadAsync(data).then(function(zip){ 44 | zip.file("module-search-index.json").async("text").then(function(content){ 45 | moduleSearchIndex = JSON.parse(content); 46 | }); 47 | }); 48 | }); 49 | }); 50 | $.get(pathtoroot + "package-search-index.zip") 51 | .done(function() { 52 | JSZipUtils.getBinaryContent(pathtoroot + "package-search-index.zip", function(e, data) { 53 | JSZip.loadAsync(data).then(function(zip){ 54 | zip.file("package-search-index.json").async("text").then(function(content){ 55 | packageSearchIndex = JSON.parse(content); 56 | }); 57 | }); 58 | }); 59 | }); 60 | $.get(pathtoroot + "type-search-index.zip") 61 | .done(function() { 62 | JSZipUtils.getBinaryContent(pathtoroot + "type-search-index.zip", function(e, data) { 63 | JSZip.loadAsync(data).then(function(zip){ 64 | zip.file("type-search-index.json").async("text").then(function(content){ 65 | typeSearchIndex = JSON.parse(content); 66 | }); 67 | }); 68 | }); 69 | }); 70 | $.get(pathtoroot + "member-search-index.zip") 71 | .done(function() { 72 | JSZipUtils.getBinaryContent(pathtoroot + "member-search-index.zip", function(e, data) { 73 | JSZip.loadAsync(data).then(function(zip){ 74 | zip.file("member-search-index.json").async("text").then(function(content){ 75 | memberSearchIndex = JSON.parse(content); 76 | }); 77 | }); 78 | }); 79 | }); 80 | $.get(pathtoroot + "tag-search-index.zip") 81 | .done(function() { 82 | JSZipUtils.getBinaryContent(pathtoroot + "tag-search-index.zip", function(e, data) { 83 | JSZip.loadAsync(data).then(function(zip){ 84 | zip.file("tag-search-index.json").async("text").then(function(content){ 85 | tagSearchIndex = JSON.parse(content); 86 | }); 87 | }); 88 | }); 89 | }); 90 | if (!moduleSearchIndex) { 91 | createElem(doc, tag, 'module-search-index.js'); 92 | } 93 | if (!packageSearchIndex) { 94 | createElem(doc, tag, 'package-search-index.js'); 95 | } 96 | if (!typeSearchIndex) { 97 | createElem(doc, tag, 'type-search-index.js'); 98 | } 99 | if (!memberSearchIndex) { 100 | createElem(doc, tag, 'member-search-index.js'); 101 | } 102 | if (!tagSearchIndex) { 103 | createElem(doc, tag, 'tag-search-index.js'); 104 | } 105 | $(window).resize(function() { 106 | $('.navPadding').css('padding-top', $('.fixedNav').css("height")); 107 | }); 108 | } 109 | 110 | function createElem(doc, tag, path) { 111 | var script = doc.createElement(tag); 112 | var scriptElement = doc.getElementsByTagName(tag)[0]; 113 | script.src = pathtoroot + path; 114 | scriptElement.parentNode.insertBefore(script, scriptElement); 115 | } 116 | 117 | function show(type) { 118 | count = 0; 119 | for (var key in data) { 120 | var row = document.getElementById(key); 121 | if ((data[key] & type) !== 0) { 122 | row.style.display = ''; 123 | row.className = (count++ % 2) ? rowColor : altColor; 124 | } 125 | else 126 | row.style.display = 'none'; 127 | } 128 | updateTabs(type); 129 | } 130 | 131 | function updateTabs(type) { 132 | for (var value in tabs) { 133 | var sNode = document.getElementById(tabs[value][0]); 134 | var spanNode = sNode.firstChild; 135 | if (value == type) { 136 | sNode.className = activeTableTab; 137 | spanNode.innerHTML = tabs[value][1]; 138 | } 139 | else { 140 | sNode.className = tableTab; 141 | spanNode.innerHTML = "" + tabs[value][1] + ""; 142 | } 143 | } 144 | } 145 | 146 | function updateModuleFrame(pFrame, cFrame) { 147 | top.packageFrame.location = pFrame; 148 | top.classFrame.location = cFrame; 149 | } 150 | -------------------------------------------------------------------------------- /examples/appnet-api-server/src/main/java/com/hedera/hashgraph/identity/hcs/example/appnet/vc/Ed25519CredentialProof.java: -------------------------------------------------------------------------------- 1 | package com.hedera.hashgraph.identity.hcs.example.appnet.vc; 2 | 3 | import com.google.common.hash.Hashing; 4 | import com.google.gson.Gson; 5 | import com.google.gson.JsonElement; 6 | import com.google.gson.JsonObject; 7 | import com.hedera.hashgraph.identity.hcs.did.HcsDidRootKey; 8 | import com.hedera.hashgraph.identity.utils.JsonUtils; 9 | import com.hedera.hashgraph.sdk.PrivateKey; 10 | import com.nimbusds.jose.JWSAlgorithm; 11 | import com.nimbusds.jose.JWSHeader; 12 | import com.nimbusds.jose.util.Base64URL; 13 | import java.nio.charset.StandardCharsets; 14 | import java.util.Collections; 15 | import java.util.LinkedHashMap; 16 | import org.threeten.bp.Instant; 17 | 18 | /** 19 | * A simple, manually constructed example of a linked data proof of type Ed25519Signature2018. 20 | * Implementation is a simplified version for this example based on: 21 | * https://github.com/WebOfTrustInfo/ld-signatures-java/. 22 | * In a real-world application it is recommended to use a JSON-LD compatible library to handle normalization. 23 | * However at this point the only available one in Java support JSON-LD version 1.0, but 1.1 is required by W3C 24 | * Verifiable Credentials. 25 | */ 26 | public class Ed25519CredentialProof extends LinkedDataProof { 27 | public static final String PROOF_TYPE = "Ed25519Signature2018"; 28 | public static final String VC_VERIFICATION_METHOD = "Ed25519Signature2018"; 29 | public static final String VC_PROOF_PURPOSE = "assertionMethod"; 30 | private static final String[] JSON_PROPERTIES_ORDER = {"type", "creator", "created", "domain", "nonce", 31 | "proofPurpose", "verificationMethod", "jws"}; 32 | private static final String JSON_PROPERTY_JWS = "jws"; 33 | 34 | /** 35 | * Constructs a new proof document - without signature. 36 | * 37 | * @param issuerDid DID of a credential issuer. 38 | */ 39 | public Ed25519CredentialProof(final String issuerDid) { 40 | this(issuerDid, null, null); 41 | } 42 | 43 | /** 44 | * Constructs a new proof document - without signature. 45 | * 46 | * @param issuerDid DID of a credential issuer. 47 | * @param nonce The variable nonce. 48 | */ 49 | public Ed25519CredentialProof(final String issuerDid, final String nonce) { 50 | this(issuerDid, null, nonce); 51 | } 52 | 53 | /** 54 | * Constructs a new proof document - without signature. 55 | * 56 | * @param issuerDid DID of a credential issuer. 57 | * @param domain The domain. 58 | * @param nonce The variable nonce. 59 | */ 60 | public Ed25519CredentialProof(final String issuerDid, final String domain, final String nonce) { 61 | setType(PROOF_TYPE); 62 | setProofPurpose(VC_PROOF_PURPOSE); 63 | setVerificationMethod(issuerDid + HcsDidRootKey.DID_ROOT_KEY_NAME); 64 | setCreator(issuerDid); 65 | setCreated(Instant.now()); 66 | setDomain(domain); 67 | setNonce(nonce); 68 | } 69 | 70 | /** 71 | * Creates a signing input in JWS form. 72 | * 73 | * @param header The JWS header. 74 | * @param signingInput The signing input. 75 | * @return The singing input in JWS form. 76 | */ 77 | private static byte[] getJwsSigningInput(final JWSHeader header, final byte[] signingInput) { 78 | byte[] encodedHeader; 79 | 80 | if (header.getParsedBase64URL() != null) { 81 | encodedHeader = header.getParsedBase64URL().toString().getBytes(StandardCharsets.UTF_8); 82 | } else { 83 | encodedHeader = header.toBase64URL().toString().getBytes(StandardCharsets.UTF_8); 84 | } 85 | 86 | byte[] jwsSigningInput = new byte[encodedHeader.length + 1 + signingInput.length]; 87 | System.arraycopy(encodedHeader, 0, jwsSigningInput, 0, encodedHeader.length); 88 | jwsSigningInput[encodedHeader.length] = (byte) '.'; 89 | System.arraycopy(signingInput, 0, jwsSigningInput, encodedHeader.length + 1, signingInput.length); 90 | 91 | return jwsSigningInput; 92 | } 93 | 94 | /** 95 | * Note: this is a manual implementation of ordered JSON items. 96 | * In a real-world application it is recommended to use a JSON-LD compatible library to handle normalization. 97 | * However at this point the only available one in Java support JSON-LD version 1.0, but 1.1 is required by W3C 98 | * Verifiable Credentials. 99 | * 100 | * @param withoutSignature Will skip signature value ('jwk' attribute) if True. 101 | * @return A normalized JSON string representation of this proof. 102 | */ 103 | public JsonElement toNormalizedJsonElement(final boolean withoutSignature) { 104 | Gson gson = JsonUtils.getGson(); 105 | 106 | // First turn to normal JSON 107 | JsonObject root = gson.toJsonTree(this).getAsJsonObject(); 108 | // Then put JSON properties in ordered map 109 | LinkedHashMap map = new LinkedHashMap<>(); 110 | 111 | for (String property : JSON_PROPERTIES_ORDER) { 112 | if (JSON_PROPERTY_JWS.equals(property) && withoutSignature) { 113 | continue; 114 | } else if (root.has(property)) { 115 | map.put(property, root.get(property)); 116 | } 117 | } 118 | // Turn map to JSON 119 | return gson.toJsonTree(map); 120 | } 121 | 122 | /** 123 | * Creates a linked data proof of type 124 | * Implementation is a simplified version for this example based on 125 | * https://github.com/WebOfTrustInfo/ld-signatures-java/. 126 | * 127 | * @param signingKey Private key of the signing subject. 128 | * @param documentToSign The canonicalized JSON string of a verifiable credential document. 129 | */ 130 | public void sign(final PrivateKey signingKey, final String documentToSign) { 131 | byte[] inputForSigning = new byte[64]; 132 | String normalizedProof = JsonUtils.getGson().toJson(toNormalizedJsonElement(true)); 133 | 134 | byte[] normalizedDocHash = Hashing.sha256().hashBytes(documentToSign.getBytes(StandardCharsets.UTF_8)) 135 | .asBytes(); 136 | byte[] normalizedProofHash = Hashing.sha256().hashBytes(normalizedProof.getBytes(StandardCharsets.UTF_8)) 137 | .asBytes(); 138 | 139 | System.arraycopy(normalizedProofHash, 0, inputForSigning, 0, 32); 140 | System.arraycopy(normalizedDocHash, 0, inputForSigning, 32, 32); 141 | 142 | JWSHeader jwsHeader = new JWSHeader.Builder(JWSAlgorithm.EdDSA).customParam("b64", Boolean.FALSE) 143 | .criticalParams(Collections.singleton("b64")).build(); 144 | byte[] jwsSigningInput = getJwsSigningInput(jwsHeader, inputForSigning); 145 | 146 | Base64URL signature = Base64URL.encode(signingKey.sign(jwsSigningInput)); 147 | setJws(jwsHeader.toBase64URL().toString() + '.' + '.' + signature.toString()); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /docs/sdk-javadocs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Overview (did-sdk-java 1.0.0 API) 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 17 | 18 | 19 | 20 | 32 |

JavaScript is disabled on your browser.
34 | 35 |
36 | 92 |
93 |
94 |

did-sdk-java 1.0.0 API

95 |
96 |
97 |
98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 110 | 111 | 112 | 113 | 116 | 117 | 118 | 119 | 122 | 123 | 124 | 125 | 128 | 129 | 130 | 131 | 134 | 135 | 136 |
Packages 
PackageDescription
com.hedera.hashgraph.identity 108 |
The root package of Hedera Hashgraph Identity framework SDK.
109 |
com.hedera.hashgraph.identity.hcs 114 |
Hedera Identity framework implementation based on Hedera Consensus Service DID method specification.
115 |
com.hedera.hashgraph.identity.hcs.did 120 |
Hedera DID Method Specification implementation on top of Hedera Consensus Service.
121 |
com.hedera.hashgraph.identity.hcs.vc 126 |
Verifiable Credentials implementation on top of Hedera HCS DID method.
127 |
com.hedera.hashgraph.identity.utils 132 |
Various utility classes used by Hedera identity frameworks.
133 |
137 |
138 |
139 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to the Hedera™ Hashgraph DID - Java SDK 2 | 3 | Thank you for your interest in contributing the Hedera Hashgraph DID SDK for Java! 4 | 5 | We appreciate your interest in helping us and the rest of our community. We welcome bug reports, feature requests, and code contributions. 6 | 7 | __Jump To:__ 8 | 9 | * [Bug Reports](#bug-reports) 10 | * [Feature Requests](#feature-requests) 11 | * [Code Contributions](#code-contributions) 12 | 13 | ## Bug Reports 14 | 15 | Bug reports are accepted through the [Issues][issues] page. 16 | 17 | The [bug][label-bug] label is used to track bugs. 18 | 19 | ### Before Submitting a Bug Report 20 | 21 | Before submitting a bug report, please do the following: 22 | 23 | 1. Do a search through the existing issues to make sure it has not already been reported. If you find that the bug has already been raised, please give it a +1 to help us to decide which issues we prioritise. 24 | 25 | 2. If possible, upgrade to the latest release of the SDK. It's possible the bug has already been fixed in the latest version. We will do our utmost to maintain backwards compatibility between patch version releases, so that you can be confident that your application will continue to work as expected with the newer version. 26 | 27 | If you have completed these steps and you need to submit a bug report, please read the guidelines below. 28 | 29 | ### Submitting a Bug Report 30 | 31 | Please ensure that your bug report contains the following: 32 | 33 | * A short, descriptive title. Other community members should be able to understand the nature of the issue by reading this title. 34 | * A succinct, detailed description of the problem you're experiencing. This should include: 35 | * Expected behaviour of the SDK and the actual behaviour exhibited. 36 | * Any details of your application development environment that may be relevant. 37 | * If applicable, the exception stack-trace. 38 | * If you are able to create one, include a [Minimal Working Example][mwe] that reproduces the issue. 39 | * [Markdown][markdown] formatting as appropriate to make the report easier to read; for example use code blocks when pasting a code snippet or exception stack-trace. 40 | 41 | ## Feature Requests 42 | 43 | Feature requests are also submitted through the [Issues][issues] page. 44 | 45 | As with Bug Reports, please do a search of the open requests first before submitting a new one to avoid duplicates. If you do find a a feature request that represents your suggestion, please give it a +1. 46 | 47 | __NOTE:__ If you intend to implement this feature, please submit the feature request *before* working on any code changes. This will allow members on the SDK team to assess the idea, discuss the design with you and ensure that it makes sense to include such a feature in the SDK. 48 | 49 | Feature requests are labeled as [enhancements][label-enhancement]. 50 | 51 | ### Submitting a Feature Request 52 | 53 | Open an [issue][issues] with the following: 54 | 55 | * A short, descriptive title. Other community members should be able to understand the nature of the issue by reading this title. 56 | * A detailed description of the the proposed feature. Explain why you believe it should be added to the SDK. Illustrative example code may also be provided to help explain how the feature should work. 57 | * [Markdown][markdown] formatting as appropriate to make the request easier to read. 58 | * If you plan to implement this feature yourself, please let us know that you'd like to the issue to be assigned to you. 59 | 60 | ## Code Contributions 61 | 62 | Code contributions to the SDK are handled using [Pull Requests][pull-requests]. Please keep the following in mind when considering a code contribution: 63 | 64 | * The SDK is released under the [Apache 2.0 License][license]. 65 | 66 | Any code you submit will be released under this license. 67 | 68 | * For anything other than small or quick changes, you should always start by reviewing the [Issues][issues] page to ensure that the nobody else is already working on the same issue. 69 | 70 | If you're working on a bug fix, check to see whether the bug has already been reported. If it has but no one is assigned to it, ask one of the maintainers to assign it to you before beginning work. If you're confident the bug hasn't been reported yet, create a new [Bug Report](#bug-reports) and ask us to assign it to you. 71 | 72 | If you are thinking about adding entirely new functionality, open a [Feature Request](#feature-requests) or get in touch with us on [Discord](discord) to ask for feedback first before beginning work; this is to ensure that nobody else is already working on the feature (or another similar feature) and to confirm that it makes sense for such functionality to be included in the SDK. 73 | * All code contributions must be accompanied with new or modified tests that verify that the code works as expected; i.e. that the issue has been fixed or that the functionality works as intended. 74 | 75 | ### Pull Request Readiness 76 | 77 | Before submitting your pull request, refer to the pull request readiness checklist below: 78 | 79 | * [ ] Includes tests to exercise the new behaviour 80 | * [ ] Code is documented, especially public and user-facing constructs 81 | * [ ] Local run of `gradle check` succeed 82 | * [ ] Git commit message is detailed and includes context behind the change 83 | * [ ] If the change is related to an existing Bug Report or Feature Request, please include its issue number 84 | 85 | To contribute, please fork the GitHub repository and submit a pull request to the `master` branch. 86 | 87 | ### Getting Your Pull Request Merged 88 | 89 | All Pull Requests must be approved by at least one member of the SDK team before it can be merged in. The members only have limited bandwidth to review Pull Requests so it's not unusual for a Pull Request to go unreviewed for a few days, especially if it's a large or complex one. After a couple of weeks, if you haven't received any feedback regarding your Pull Request from the SDK team, feel free to contact us on [Discord](discord) to ask for a review. 90 | 91 | ## Getting in Contact 92 | 93 | * Join us in our [Discord](discord) channel; there you can engage with the Hedera team and other developers and enthusiasts. 94 | 95 | [license]: https://github.com/hashgraph/did-sdk-java/blob/master/LICENSE 96 | [mwe]: https://en.wikipedia.org/wiki/Minimal_Working_Example 97 | [markdown]: https://guides.github.com/features/mastering-markdown/ 98 | [issues]: https://github.com/hashgraph/did-sdk-java/issues 99 | [pull-requests]: https://github.com/hashgraph/did-sdk-java/pulls 100 | [label-bug]: https://github.com/hashgraph/did-sdk-java/labels/bug 101 | [label-enhancement]: https://github.com/hashgraph/did-sdk-java/labels/enhancement 102 | [discord]: https://hedera.com/discord --------------------------------------------------------------------------------