SignCommon.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 java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.util.Arrays;


/**
 *
 * @author jimsch
 */

public abstract class SignCommon extends Message {
    protected String contextString;

    byte[] computeSignature(byte[] rgbToBeSigned, OneKey cnKey) throws CoseException {
        AlgorithmID alg = AlgorithmID.FromCBOR(findAttribute(HeaderKeys.Algorithm));
        return computeSignature(alg, rgbToBeSigned, cnKey);
    }

    static byte[] computeSignature(AlgorithmID alg, byte[] rgbToBeSigned, OneKey cnKey) throws CoseException {
        String      algName = null;
        int         sigLen = 0;
        
        switch (alg) {
            case ECDSA_256:
                algName = "SHA256withECDSA";
                sigLen = 32;
                break;
            case ECDSA_384:
                algName = "SHA384withECDSA";
                sigLen = 48;
                break;
            case ECDSA_512:
                algName = "SHA512withECDSA";
                sigLen = 66;
                break;
            case EDDSA:
                algName = "NonewithEdDSA";
                break;
                
            default:
                throw new CoseException("Unsupported Algorithm Specified");
        }
        
        if (cnKey == null) {
            throw new NullPointerException();
        }
        
        PrivateKey  privKey = cnKey.AsPrivateKey();
        if (privKey == null) {
            throw new CoseException("Private key required to sign");
        }
        
        byte[]      result = null;
        try {
            Signature sig = Signature.getInstance(algName);
            sig.initSign(privKey);
            sig.update(rgbToBeSigned);
            
			// System.out.println("COSE: To be signed: " +
			// Utils.toHexString(rgbToBeSigned));
            
            result = sig.sign();
            if (sigLen > 0) {
                result = convertDerToConcat(result, sigLen);
            }
        } catch (NoSuchAlgorithmException ex) {
            throw new CoseException("Algorithm not supported", ex);
        } catch (Exception ex) {
            throw new CoseException("Signature failure", ex);
        }
                
        return result;
    }
    
    private static byte[] convertDerToConcat(byte[] der, int len) throws CoseException {
        // this is far too naive
        byte[] concat = new byte[len * 2];

        // assumes SEQUENCE is organized as "R + S"
        int kLen = 4;
        if (der[0] != 0x30) {
            throw new CoseException("Unexpected signature input");
        }
        if ((der[1] & 0x80) != 0) {
            // offset actually 4 + (7-bits of byte 1)
            kLen = 4 + (der[1] & 0x7f);
        }
        
        // calculate start/end of R
        int rOff = kLen;
        int rLen = der[rOff - 1];
        int rPad = 0;
        if (rLen > len) {
            rOff += (rLen - len);
            rLen = len;
        } else {
            rPad = (len - rLen);
        }
        // copy R
        System.arraycopy(der, rOff, concat, rPad, rLen);
        
        // calculate start/end of S
        int sOff = rOff + rLen + 2;
        int sLen = der[sOff - 1];
        int sPad = 0;
        if (sLen > len) {
            sOff += (sLen - len);
            sLen = len;
        } else {
            sPad = (len - sLen);
        }
        // copy S
        System.arraycopy(der, sOff, concat, len + sPad, sLen);
        
        return concat;
    }
    
    boolean validateSignature(byte[] rgbToBeSigned, byte[] rgbSignature, OneKey cnKey) throws CoseException {
        AlgorithmID alg = AlgorithmID.FromCBOR(findAttribute(HeaderKeys.Algorithm));
        return validateSignature(alg, rgbToBeSigned, rgbSignature, cnKey);
    }

    static boolean validateSignature(AlgorithmID alg, byte[] rgbToBeSigned, byte[] rgbSignature, OneKey cnKey) throws CoseException {
        String algName = null;
        boolean convert = false;

        switch (alg) {
        case ECDSA_256:
            algName = "SHA256withECDSA";
            convert = true;
            break;
        case ECDSA_384:
            algName = "SHA384withECDSA";
            convert = true;
            break;
        case ECDSA_512:
            algName = "SHA512withECDSA";
            convert = true;
            break;
            
        case EDDSA:
            algName = "NonewithEdDSA";
            break;

        default:
            throw new CoseException("Unsupported Algorithm Specified");
        }

        if (cnKey == null) {
            throw new NullPointerException();
        }

        PublicKey pubKey = cnKey.AsPublicKey();
        if (pubKey == null) {
            throw new CoseException("Public key required to verify");
        }

        boolean result = false;
        try {
            Signature sig = Signature.getInstance(algName);
            sig.initVerify(pubKey);
            sig.update(rgbToBeSigned);
            
			// System.out.println("COSE: To be signed (checked): " +
			// Utils.toHexString(rgbToBeSigned));

            if (convert) {
                rgbSignature = convertConcatToDer(rgbSignature);
            }
            result = sig.verify(rgbSignature);
        } catch (NoSuchAlgorithmException ex) {
            throw new CoseException("Algorithm not supported", ex);
        } catch (Exception ex) {
            throw new CoseException("Signature verification failure", ex);
        }

        return result;
    }

    private static byte[] convertConcatToDer(byte[] concat) throws CoseException {
        int len = concat.length / 2;
        byte[] r = Arrays.copyOfRange(concat, 0, len);
        byte[] s = Arrays.copyOfRange(concat, len, concat.length);

        return ASN1.EncodeSignature(r, s);
    }
}