Sign1Message.java

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package org.eclipse.californium.cose;

import com.upokecenter.cbor.CBORObject;
import com.upokecenter.cbor.CBORType;

/**
 * The Sign1Message class implements the COSE_Sign1 object.
 * This provides for a single signature where the signer is normally implicitly known.
 * {@link SignMessage} need to be used in those cases where more than one signature is needed on a content.
 * There is no way to convert a signed message between the two formats.
 * <p>
 * Create a Sign1Message object for a new message, when processing an existing message use Message.DecodeFromBytes to create the Sign1Message object.
 * <p>
 * There are examples of using this class:
 * <br> Simple Example
 * 
 * @author jimsch
 */
public class Sign1Message extends SignCommon {
    byte[] rgbSignature;
    
    /**
     * Create a signed message object for which there will be a leading tag and the content will be included.
     */
    public Sign1Message() {
        this(true, true);
    }
    
    /**
     * Create a signed message object.  Emission of the leading tag is controlled by the parameter.
     * 
     * @param emitTag emit leading tag when message is serialized
     */
    public Sign1Message(boolean emitTag) {
        this(emitTag, true);
    }
    
    /**
     * Create a signed message object for which the emission of the leading tag and content is controlled by the parameters.
     * 
     * @param emitTag emit leading tag when message is serialized
     * @param emitContent emit the content as part of the message
     * @since COSE 0.9.1
     */
    public Sign1Message(boolean emitTag, boolean emitContent) {
        this.emitTag = emitTag;
        this.contextString = "Signature1";
        this.emitContent = emitContent;
        this.messageTag = MessageTag.Sign1;
    }
    
    /**
     * Create a signature for the message if one does not exist.
     * 
     * @param key key to use to sign the message 
     * @exception CoseException Errors generated by the COSE module
    */
    public void sign(OneKey key) throws CoseException {
        if (rgbContent == null) throw new CoseException("No Content Specified");
        if (rgbSignature != null) return;

        if (rgbProtected == null) {
            if (objProtected.size() > 0) rgbProtected = objProtected.EncodeToBytes();
            else rgbProtected = new byte[0];
        }
                
        CBORObject obj = CBORObject.NewArray();
        obj.Add(contextString);
        obj.Add(rgbProtected);
        obj.Add(externalData);
        obj.Add(rgbContent);
        
        rgbSignature = computeSignature(obj.EncodeToBytes(), key);
        
        ProcessCounterSignatures();
    }
    
    /**
     * Validate the signature on the message using the passed in key.
     * 
     * @param cnKey key to use for validation
     * @return true if the signature validates
     * @throws CoseException Errors generated by the COSE module
     */

    public boolean validate(OneKey cnKey) throws CoseException {
        CBORObject obj = CBORObject.NewArray();
        obj.Add(contextString);
        if (objProtected.size() > 0) obj.Add(rgbProtected);
        else obj.Add(CBORObject.FromObject(new byte[0]));
        obj.Add(externalData);
        obj.Add(rgbContent);
        return validateSignature(obj.EncodeToBytes(), rgbSignature, cnKey);
    }
    
    /**
     * Internal function used in creating a Sign1Message object from a byte string.
     * 
     * @param messageObject COSE_Sign1 encoded object.
     * @throws CoseException Errors generated by the COSE module
     */
    @Override
    protected void DecodeFromCBORObject(CBORObject messageObject) throws CoseException {
        if (messageObject.size() != 4) throw new CoseException("Invalid Sign1 structure");
        
        if (messageObject.get(0).getType() == CBORType.ByteString) {
            rgbProtected = messageObject.get(0).GetByteString();
            if (messageObject.get(0).GetByteString().length == 0) objProtected = CBORObject.NewMap();
            else {
                objProtected = CBORObject.DecodeFromBytes(rgbProtected);
                if (objProtected.size() == 0) rgbProtected = new byte[0];
            }
        }
        else throw new CoseException("Invalid Sign1 structure");
        
        if (messageObject.get(1).getType() == CBORType.Map) {
            objUnprotected = messageObject.get(1);
        }
        else throw new CoseException("Invalid Sign1 structure");
        
        if (messageObject.get(2).getType() == CBORType.ByteString) rgbContent = messageObject.get(2).GetByteString();
        else if (!messageObject.get(2).isNull()) throw new CoseException("Invalid Sign1 structure");
        
        if (messageObject.get(3).getType() == CBORType.ByteString) rgbSignature = messageObject.get(3).GetByteString();
        else throw new CoseException("Invalid Sign1 structure");
    }

    /**
     * Internal function used to create a serialization of a COSE_Sign1 message
     * 
     * @return CBOR object which can be encoded.
     * @throws CoseException Errors generated by the COSE module
     */
    @Override
    protected CBORObject EncodeCBORObject() throws CoseException {
        if (rgbSignature == null) throw new CoseException("sign function not called");
        if (rgbProtected == null) throw new CoseException("Internal Error");
        
        CBORObject obj = CBORObject.NewArray();
        obj.Add(rgbProtected);        
        obj.Add(objUnprotected);
        if (emitContent) obj.Add(rgbContent);
        else obj.Add(null);
        obj.Add(rgbSignature);
        
        return obj;
    }
}