/*
 * Copyright 2015-2024 the original author or authors
 *
 * This software is licensed under the Apache License, Version 2.0,
 * the GNU Lesser General Public License version 2 or later ("LGPL")
 * and the WTFPL.
 * You may choose either license to govern your use of this software only
 * upon the condition that you accept all of the terms of either
 * the Apache License 2.0, the LGPL 2.1+ or the WTFPL.
 */
package org.minidns.dnssec.algorithms;

import org.minidns.dnssec.DnssecValidationFailedException.DnssecInvalidKeySpecException;
import org.minidns.record.DNSKEY;
import org.minidns.record.RRSIG;
import org.minidns.dnssec.DnssecValidationFailedException.DataMalformedException;

import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.DSAPublicKeySpec;
import java.security.spec.InvalidKeySpecException;

class DsaSignatureVerifier extends JavaSecSignatureVerifier {
    private static final int LENGTH = 20;

    DsaSignatureVerifier(String algorithm) throws NoSuchAlgorithmException {
        super("DSA", algorithm);
    }

    @Override
    protected byte[] getSignature(RRSIG rrsig) throws DataMalformedException {
        DataInput dis = rrsig.getSignatureAsDataInputStream();

        ByteArrayOutputStream bos;
        try {
            // Convert RFC 2536 to ASN.1
            @SuppressWarnings("unused")
            byte t = dis.readByte();

            byte[] r = new byte[LENGTH];
            dis.readFully(r);
            int roff = 0;
            final int rlen;
            if (r[0] == 0) {
                while (roff < LENGTH && r[roff] == 0) {
                    roff++;
                }
                rlen = r.length - roff;
            } else if (r[0] < 0) {
                rlen = r.length + 1;
            } else {
                rlen = r.length;
            }

            byte[] s = new byte[LENGTH];
            dis.readFully(s);
            int soff = 0;
            final int slen;
            if (s[0] == 0) {
                while (soff < LENGTH && s[soff] == 0) {
                    soff++;
                }
                slen = s.length - soff;
            } else if (s[0] < 0) {
                slen = s.length + 1;
            } else {
                slen = s.length;
            }

            bos = new ByteArrayOutputStream();
            DataOutputStream dos = new DataOutputStream(bos);

            dos.writeByte(0x30);
            dos.writeByte(rlen + slen + 4);

            dos.writeByte(0x2);
            dos.writeByte(rlen);
            if (rlen > LENGTH)
                dos.writeByte(0);
            dos.write(r, roff, LENGTH - roff);

            dos.writeByte(0x2);
            dos.writeByte(slen);
            if (slen > LENGTH)
                dos.writeByte(0);
            dos.write(s, soff, LENGTH - soff);
        } catch (IOException e) {
            throw new DataMalformedException(e, rrsig.getSignature());
        }

        return bos.toByteArray();
    }

    @Override
    protected PublicKey getPublicKey(DNSKEY key) throws DataMalformedException, DnssecInvalidKeySpecException {
        DataInput dis = key.getKeyAsDataInputStream();
        BigInteger subPrime, prime, base, pubKey;

        try {
            int t = dis.readUnsignedByte();

            byte[] subPrimeBytes = new byte[LENGTH];
            dis.readFully(subPrimeBytes);
            subPrime = new BigInteger(1, subPrimeBytes);

            byte[] primeBytes = new byte[64 + t * 8];
            dis.readFully(primeBytes);
            prime = new BigInteger(1, primeBytes);

            byte[] baseBytes = new byte[64 + t * 8];
            dis.readFully(baseBytes);
            base = new BigInteger(1, baseBytes);

            byte[] pubKeyBytes = new byte[64 + t * 8];
            dis.readFully(pubKeyBytes);
            pubKey = new BigInteger(1, pubKeyBytes);
        } catch (IOException e) {
            throw new DataMalformedException(e, key.getKey());
        }

        try {
            return getKeyFactory().generatePublic(new DSAPublicKeySpec(pubKey, prime, subPrime, base));
        } catch (InvalidKeySpecException e) {
            throw new DnssecInvalidKeySpecException(e);
        }
    }
}
