/*
 * Class: KeyFactory
 *
 * Author: Alex Usun
 *
 * Date: 31/8/2005
 */
package com.jscape.inet.ssh.util.keyreader.sshcom;

import com.jscape.inet.ssh.util.keyreader.IKeyFactory;
import com.jscape.util.Assert;

import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.security.MessageDigest;

/**
 * SSH.COM format key factory.
 *
 * @author Alex Usun
 * @version 1.0
 * @see <a href="KeyFactory.java.html">Source code</a>
 */
public class KeyFactory
        implements IKeyFactory {

    private static final String DIGEST_NAME = "MD5";
    private static final int DIGEST_SIZE = 16;

    private final byte[] passphrase;

    /**
     * Creates a new factory instance.
     *
     * @param passphrase a passphrase
     */
    public KeyFactory(String passphrase) {
        Assert.notNull(passphrase);
        this.passphrase = passphrase.getBytes(StandardCharsets.UTF_8);
    }

    /**
     * Returns current passphrase.
     *
     * @return current passphrase
     */
    public String getPassphrase() {
        return new String(this.passphrase, StandardCharsets.UTF_8);
    }

    /**
     * @see IKeyFactory#createKey(String, int)
     */
    public Key createKey(String algorithm, int keySize) {
        try {
            MessageDigest digest = MessageDigest.getInstance(DIGEST_NAME);
            byte[] hashBytes = new byte[computeHashSize(keySize)];

            for (int index = 0; index <= hashBytes.length - DIGEST_SIZE; index += DIGEST_SIZE) {
                digest.update(this.passphrase);
                digest.update(hashBytes, 0, index);
                System.arraycopy(digest.digest(), 0, hashBytes, index, DIGEST_SIZE);
            }

            return new SecretKeySpec(hashBytes, 0, keySize, algorithm);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * @see IKeyFactory#createIv(int)
     */
    public byte[] createIv(int size) {
        Assert.greater(size, 0, "bad key size.");

        return new byte[size];
    }

    /**
     * Computes needed hash size.
     *
     * @return hash size
     */
    private int computeHashSize(int keySize) {
        int hashSize = (keySize > DIGEST_SIZE) ? keySize : DIGEST_SIZE;
        int remainder = hashSize % DIGEST_SIZE;

        if (remainder != 0) {
            hashSize += DIGEST_SIZE - remainder;
        }

        return hashSize;
    }

}
