All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.elastos.did.backend.IDChainRequest Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2019 Elastos Foundation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package org.elastos.did.backend;

import static com.google.common.base.Preconditions.checkArgument;

import org.elastos.did.Constants;
import org.elastos.did.DID;
import org.elastos.did.DIDDocument;
import org.elastos.did.DIDEntity;
import org.elastos.did.DIDURL;
import org.elastos.did.TransferTicket;
import org.elastos.did.crypto.Base64;
import org.elastos.did.exception.DIDResolveException;
import org.elastos.did.exception.DIDSyntaxException;
import org.elastos.did.exception.MalformedIDChainRequestException;
import org.elastos.did.exception.MalformedTransferTicketException;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.databind.JsonNode;

/**
 * The abstract super class for all ID chain transaction requests.
 *
 * @param  the type of the class modeled by this IDChainRequest object
 */
@JsonPropertyOrder({ IDChainRequest.HEADER,
	IDChainRequest.PAYLOAD,
	IDChainRequest.PROOF })
public abstract class IDChainRequest extends DIDEntity {
	/**
	 * The specification string of IDChain DID Request
	 */
	public static final String DID_SPECIFICATION = "elastos/did/1.0";

	/**
	 * The specification string of IDChain Credential Request
	 */
	public static final String CREDENTIAL_SPECIFICATION = "elastos/credential/1.0";

	protected static final String HEADER = "header";
	protected static final String PAYLOAD = "payload";
	protected static final String PROOF = "proof";

	protected static final String SPECIFICATION = "specification";
	private static final String OPERATION = "operation";
	private static final String PREVIOUS_TXID = "previousTxid";
	private static final String TICKET = "ticket";

	private static final String TYPE = "type";
	private static final String VERIFICATION_METHOD = "verificationMethod";
	private static final String SIGNATURE = "signature";

	@JsonProperty(HEADER)
	private Header header;
	@JsonProperty(PAYLOAD)
	private String payload;
	@JsonProperty(PROOF)
	private Proof proof;

	/**
	 * The IDChain Request Operations.
	 */
	public static enum Operation {
		/**
		 * Create a new DID
		 */
		CREATE(DID_SPECIFICATION),
		/**
		 * Update an exist DID
		 */
		UPDATE(DID_SPECIFICATION),
		/**
		 * Transfer the DID' ownership
		 */
		TRANSFER(DID_SPECIFICATION),
		/**
		 * Deactivate a DID
		 */
		DEACTIVATE(DID_SPECIFICATION),
		/**
		 * Declare a credential
		 */
		DECLARE(CREDENTIAL_SPECIFICATION),
		/**
		 * Revoke a credential
		 */
		REVOKE(CREDENTIAL_SPECIFICATION);

		private String specification;

		private Operation(String specification) {
			this.specification = specification;
		}

		/**
		 * Get the specification string for this operation.
		 *
		 * @return the specification string
		 */
		public String getSpecification() {
			return specification;
		}

		/**
		 * Returns the name of this enumeration constant in low case,
		 * as contained in the declaration.
		 */
		@Override
		@JsonValue
		public String toString() {
			return name().toLowerCase();
		}

		/**
		 * Returns the Status enumeration constant of the specified name.
		 * (This is a helper method for JSON deserialization)
		 *
		 * @param name the operation name
		 * @return the enumeration constant
		 */
		@JsonCreator
		public static Operation fromString(String name) {
			return valueOf(name.toUpperCase());
		}
	}

	/**
	 * Header class for the ID transaction request.
	 */
	@JsonPropertyOrder({ SPECIFICATION, OPERATION, PREVIOUS_TXID, TICKET })
	@JsonInclude(Include.NON_NULL)
	protected static class Header {
		@JsonProperty(SPECIFICATION)
		private String specification;
		@JsonProperty(OPERATION)
		private Operation operation;
		@JsonProperty(PREVIOUS_TXID)
		@JsonInclude(Include.NON_NULL)
		private String previousTxid;
		@JsonProperty(TICKET)
		@JsonInclude(Include.NON_NULL)
		private String ticket;
		private TransferTicket transferTicket;

		@JsonCreator
		private Header(@JsonProperty(value = SPECIFICATION, required = true) String spec) {
			this.specification = spec;
		}

		private Header(Operation operation, String previousTxid) {
			this(operation.getSpecification());
			this.operation = operation;
			this.previousTxid = previousTxid;
		}

		private Header(Operation operation, TransferTicket ticket) {
			this(operation.getSpecification());
			this.operation = operation;

			String json = ticket.toString(true);
			this.ticket = Base64.encodeToString(json.getBytes(),
					Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP);
			this.transferTicket = ticket;
		}

		private Header(Operation operation) {
			this(operation.getSpecification());
			this.operation = operation;
		}

		/**
		 * Get the specification of this request.
		 *
		 * @return the specification string
		 */
		public String getSpecification() {
			return specification;
		}

		/**
		 * Get the operation.
		 *
		 * @return the operation of the request
		 */
		public Operation getOperation() {
			return operation;
		}

		/**
		 * Get the previous transaction id header.
		 *
		 * 

* This header item is optional. only required by the DID update request. *

* * @return the previous transaction id or null if not exists */ public String getPreviousTxid() { return previousTxid; } /** * Get the transfer ticket header. * *

* This header item is optional. only required by the DID transfer request. *

* * @return the transfer ticket in string format or null if not exists */ public String getTicket() { return ticket; } @JsonSetter(TICKET) private void setTicket(String ticket) { checkArgument(ticket != null && !ticket.isEmpty(), "Invalid ticket"); String json = new String(Base64.decode(ticket, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP)); try { this.transferTicket = TransferTicket.parse(json); } catch (MalformedTransferTicketException e) { throw new IllegalArgumentException("Invalid ticket", e); } this.ticket = ticket; } /** * Get the transfer ticket object from this header. * * @return the transfer ticket object or null if not exists */ public TransferTicket getTransferTicket() { return transferTicket; } } /** * The proof object of ID chain transaction request. */ @JsonPropertyOrder({ TYPE, VERIFICATION_METHOD, SIGNATURE }) public static class Proof { @JsonProperty(TYPE) private String type; @JsonProperty(VERIFICATION_METHOD) private DIDURL verificationMethod; @JsonProperty(SIGNATURE) private String signature; @JsonCreator private Proof(@JsonProperty(value = TYPE) String type, @JsonProperty(value = VERIFICATION_METHOD, required = true) DIDURL verificationMethod, @JsonProperty(value = SIGNATURE, required = true) String signature) { this.type = type != null ? type : Constants.DEFAULT_PUBLICKEY_TYPE; this.verificationMethod = verificationMethod; this.signature = signature; } protected Proof(DIDURL verificationMethod, String signature) { this(null, verificationMethod, signature); } /** * Get the proof type string. the type is derived from the type of the * public key that signed this proof. * * @return get proof type */ public String getType() { return type; } /** * Get the public key id that signed this proof. * * @return the public key id */ public DIDURL getVerificationMethod() { return verificationMethod; } /** * Update the verification method to a canonical DIDURL. * * @param ref the DID context */ protected void qualifyVerificationMethod(DID ref) { // TODO: need improve the impl if (verificationMethod.getDid() == null) verificationMethod = new DIDURL(ref, verificationMethod); } /** * Get the signature string of this proof. * * @return the signature string */ public String getSignature() { return signature; } } /** * Default constructor. */ protected IDChainRequest() {} /** * Create a ID chain request with given operation. * * @param operation the operation */ protected IDChainRequest(Operation operation) { this.header = new Header(operation); } /** * Create a DID update request with given previous transaction id. * * @param operation should be UPDATE operation * @param previousTxid the previous transaction id of target DID */ protected IDChainRequest(Operation operation, String previousTxid) { this.header = new Header(operation, previousTxid); } /** * Create a DID transfer request with given ticket. * * @param operation should be TRANSFER operation * @param ticket the transfer ticket object */ protected IDChainRequest(Operation operation, TransferTicket ticket) { this.header = new Header(operation, ticket); } /** * Copy constructor. * * @param request another ID chain request object */ protected IDChainRequest(IDChainRequest request) { this.header = request.header; this.payload = request.payload; this.proof = request.proof; } /** * Get the request header object. * * @return the header object */ protected Header getHeader() { return header; } /** * Get the operation of this request. * * @return the operation enum */ public Operation getOperation() { return header.getOperation(); } /** * Get the payload of this ID chain request. * * @return the payload string */ public String getPayload() { return payload; } /** * Set the payload for this ID chain request. * * @param payload the string format payload */ protected void setPayload(String payload) { this.payload = payload; } /** * Get the proof object of this ID chain request. * * @return the proof object */ public Proof getProof() { return proof; } /** * Set the proof object for the ID chain request. * * @param proof the proof object */ protected void setProof(Proof proof) { this.proof = proof; } /** * Get the signing inputs for generating the proof signature. * * @return the array object of input byte arrays */ protected byte[][] getSigningInputs() { String prevtxid = getOperation() == Operation.UPDATE ? header.getPreviousTxid() : ""; String ticket = getOperation() == Operation.TRANSFER ? header.getTicket() : ""; byte[][] inputs = new byte[][] { header.getSpecification().getBytes(), header.getOperation().toString().getBytes(), prevtxid.getBytes(), ticket.getBytes(), payload.getBytes() }; return inputs; } /** * Abstract method to get the DIDDocument of the request signer. * * @return the signer's DIDDocument object * @throws DIDResolveException if error occurred when resolving * DID document */ protected abstract DIDDocument getSignerDocument() throws DIDResolveException; /** * Post sanitize routine after deserialization. * * @throws MalformedIDChainRequestException if the IDChainRequest * object is invalid */ @Override protected void sanitize() throws MalformedIDChainRequestException { } /** * Return whether this ID chain request is valid or not. * * @return true if valid, otherwise false * @throws DIDResolveException if if error occurred when resolving * DID document */ public boolean isValid() throws DIDResolveException { DIDURL signKey = proof.getVerificationMethod(); DIDDocument doc = getSignerDocument(); if (doc == null) return false; // if (!doc.isValid()) // Here should not check the expiration and deactivated if (!doc.isGenuine()) return false; if (getOperation() != Operation.DEACTIVATE) { if (!doc.isAuthenticationKey(signKey)) return false; } else { if (!doc.isCustomizedDid()) { // the signKey should be default key or authorization key if (!doc.getDefaultPublicKeyId().equals(signKey) && doc.getAuthorizationKey(signKey) == null) return false; } else { // the signKey should be controller's default key DIDDocument controller = doc.getControllerDocument(signKey.getDid()); if (controller == null || !controller.getDefaultPublicKeyId().equals(signKey)) return false; } } return doc.verify(proof.getVerificationMethod(), proof.getSignature(), getSigningInputs()); } /** * Parse a ID chain request object from JsonNode object. * * @param the class type of the ID chain request * @param content a JsonNode object that contains a ID chain request * @param clazz the class of the ID chain request * @return the parsed ID chain request object * * @throws DIDSyntaxException if error when parse the resolve request */ protected static> T parse(JsonNode content, Class clazz) throws DIDSyntaxException { return DIDEntity.parse(content, clazz); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy