/*
 * Copyright (C)2009 - SSHJ Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.schmizz.sshj.signature;

import java.math.BigInteger;
import java.security.SignatureException;

import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.common.SSHRuntimeException;

/** ECDSA {@link Signature} */
public class SignatureECDSA
        extends AbstractSignature {

    /** A named factory for ECDSA signature */
    public static class Factory
            implements net.schmizz.sshj.common.Factory.Named<Signature> {

        @Override
        public Signature create() {
            return new SignatureECDSA();
        }

        @Override
        public String getName() {
            return KeyType.ECDSA.toString();
        }

    }

    public SignatureECDSA() {
        super("SHA256withECDSA");
    }

    @Override
    public byte[] encode(byte[] sig) {
        int rIndex = 3;
        int rLen = sig[rIndex++] & 0xff;
        byte[] r = new byte[rLen];
        System.arraycopy(sig, rIndex, r, 0, r.length);

        int sIndex = rIndex + rLen + 1;
        int sLen = sig[sIndex++] & 0xff;
        byte[] s = new byte[sLen];
        System.arraycopy(sig, sIndex, s, 0, s.length);

        System.arraycopy(sig, 4, r, 0, rLen);
        System.arraycopy(sig, 6 + rLen, s, 0, sLen);

        Buffer buf = new Buffer.PlainBuffer();
        buf.putMPInt(new BigInteger(r));
        buf.putMPInt(new BigInteger(s));

        return buf.getCompactData();
    }

    @Override
    public boolean verify(byte[] sig) {
        byte[] r;
        byte[] s;
        try {
            Buffer sigbuf = new Buffer.PlainBuffer(sig);
            final String algo = new String(sigbuf.readBytes());
            if (!"ecdsa-sha2-nistp256".equals(algo)) {
                throw new SSHRuntimeException(String.format("Signature :: ecdsa-sha2-nistp256 expected, got %s", algo));
            }
            final int rsLen = sigbuf.readUInt32AsInt();
            if (!(sigbuf.available() == rsLen)) {
                throw new SSHRuntimeException("Invalid key length");
            }
            r = sigbuf.readBytes();
            s = sigbuf.readBytes();
        } catch (Exception e) {
            throw new SSHRuntimeException(e);
        }

        int rLen = r.length;
        int sLen = s.length;

        /* We can't have the high bit set, so add an extra zero at the beginning if so. */
        if ((r[0] & 0x80) != 0) {
            rLen++;
        }
        if ((s[0] & 0x80) != 0) {
            sLen++;
        }

        /* Calculate total output length */
        int length = 6 + rLen + sLen;
        byte[] asn1 = new byte[length];

        /* ASN.1 SEQUENCE tag */
        asn1[0] = (byte) 0x30;

        /* Size of SEQUENCE */
        asn1[1] = (byte) (4 + rLen + sLen);

        /* ASN.1 INTEGER tag */
        asn1[2] = (byte) 0x02;

        /* "r" INTEGER length */
        asn1[3] = (byte) rLen;

        /* Copy in the "r" INTEGER */
        System.arraycopy(r, 0, asn1, 4, rLen);

        /* ASN.1 INTEGER tag */
        asn1[rLen + 4] = (byte) 0x02;

        /* "s" INTEGER length */
        asn1[rLen + 5] = (byte) sLen;

        /* Copy in the "s" INTEGER */
        System.arraycopy(s, 0, asn1, (6 + rLen), sLen);


        try {
            return signature.verify(asn1);
        } catch (SignatureException e) {
            throw new SSHRuntimeException(e);
        }
    }
}
