/* $OpenBSD: key.c,v 1.131 2017/05/30 14:16:41 markus Exp $ */
/*
 * placed in the public domain
 */

#include "includes.h"

#include <sys/types.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <limits.h>

#define SSH_KEY_NO_DEFINE
#include "key.h"

#include "compat.h"
#include "sshkey.h"
#include "ssherr.h"
#include "log.h"
#include "authfile.h"

static void
fatal_on_fatal_errors(int r, const char *func, int extra_fatal)
{
	if (r == SSH_ERR_INTERNAL_ERROR ||
	    r == SSH_ERR_ALLOC_FAIL ||
	    (extra_fatal != 0 && r == extra_fatal))
		fatal("%s: %s", func, ssh_err(r));
}

Key *
key_from_blob(const u_char *blob, u_int blen)
{
	int r;
	Key *ret = NULL;

	if ((r = sshkey_from_blob(blob, blen, &ret)) != 0) {
		fatal_on_fatal_errors(r, __func__, 0);
		error("%s: %s", __func__, ssh_err(r));
		return NULL;
	}
	return ret;
}

int
key_to_blob(const Key *key, u_char **blobp, u_int *lenp)
{
	u_char *blob;
	size_t blen;
	int r;

	if (blobp != NULL)
		*blobp = NULL;
	if (lenp != NULL)
		*lenp = 0;
	if ((r = sshkey_to_blob(key, &blob, &blen)) != 0) {
		fatal_on_fatal_errors(r, __func__, 0);
		error("%s: %s", __func__, ssh_err(r));
		return 0;
	}
	if (blen > INT_MAX)
		fatal("%s: giant len %zu", __func__, blen);
	if (blobp != NULL)
		*blobp = blob;
	if (lenp != NULL)
		*lenp = blen;
	return blen;
}

int
key_sign(const Key *key, u_char **sigp, u_int *lenp,
    const u_char *data, u_int datalen, const char *alg)
{
	int r;
	u_char *sig;
	size_t siglen;

	if (sigp != NULL)
		*sigp = NULL;
	if (lenp != NULL)
		*lenp = 0;
	if ((r = sshkey_sign(key, &sig, &siglen,
	    data, datalen, alg, datafellows)) != 0) {
		fatal_on_fatal_errors(r, __func__, 0);
		error("%s: %s", __func__, ssh_err(r));
		return -1;
	}
	if (siglen > INT_MAX)
		fatal("%s: giant len %zu", __func__, siglen);
	if (sigp != NULL)
		*sigp = sig;
	if (lenp != NULL)
		*lenp = siglen;
	return 0;
}

int
key_verify(const Key *key, const u_char *signature, u_int signaturelen,
    const u_char *data, u_int datalen)
{
	int r;

	if ((r = sshkey_verify(key, signature, signaturelen,
	    data, datalen, datafellows)) != 0) {
		fatal_on_fatal_errors(r, __func__, 0);
		error("%s: %s", __func__, ssh_err(r));
		return r == SSH_ERR_SIGNATURE_INVALID ? 0 : -1;
	}
	return 1;
}

Key *
key_demote(const Key *k)
{
	int r;
	Key *ret = NULL;

	if ((r = sshkey_demote(k, &ret)) != 0)
		fatal("%s: %s", __func__, ssh_err(r));
	return ret;
}

int
key_drop_cert(Key *k)
{
	int r;

	if ((r = sshkey_drop_cert(k)) != 0) {
		fatal_on_fatal_errors(r, __func__, 0);
		error("%s: %s", __func__, ssh_err(r));
		return -1;
	}
	return 0;
}

int
key_cert_check_authority(const Key *k, int want_host, int require_principal,
    const char *name, const char **reason)
{
	int r;

	if ((r = sshkey_cert_check_authority(k, want_host, require_principal,
	    name, reason)) != 0) {
		fatal_on_fatal_errors(r, __func__, 0);
		error("%s: %s", __func__, ssh_err(r));
		return -1;
	}
	return 0;
}

/* authfile.c */

Key *
key_load_cert(const char *filename)
{
	int r;
	Key *ret = NULL;

	if ((r = sshkey_load_cert(filename, &ret)) != 0) {
		fatal_on_fatal_errors(r, __func__, SSH_ERR_LIBCRYPTO_ERROR);
		/* Old authfile.c ignored all file errors. */
		if (r == SSH_ERR_SYSTEM_ERROR)
			debug("%s: %s", __func__, ssh_err(r));
		else
			error("%s: %s", __func__, ssh_err(r));
		return NULL;
	}
	return ret;

}

Key *
key_load_public(const char *filename, char **commentp)
{
	int r;
	Key *ret = NULL;

	if ((r = sshkey_load_public(filename, &ret, commentp)) != 0) {
		fatal_on_fatal_errors(r, __func__, SSH_ERR_LIBCRYPTO_ERROR);
		/* Old authfile.c ignored all file errors. */
		if (r == SSH_ERR_SYSTEM_ERROR)
			debug("%s: %s", __func__, ssh_err(r));
		else
			error("%s: %s", __func__, ssh_err(r));
		return NULL;
	}
	return ret;
}

Key *
key_load_private(const char *path, const char *passphrase,
    char **commentp)
{
	int r;
	Key *ret = NULL;

	if ((r = sshkey_load_private(path, passphrase, &ret, commentp)) != 0) {
		fatal_on_fatal_errors(r, __func__, SSH_ERR_LIBCRYPTO_ERROR);
		/* Old authfile.c ignored all file errors. */
		if (r == SSH_ERR_SYSTEM_ERROR ||
		    r == SSH_ERR_KEY_WRONG_PASSPHRASE)
			debug("%s: %s", __func__, ssh_err(r));
		else
			error("%s: %s", __func__, ssh_err(r));
		return NULL;
	}
	return ret;
}

Key *
key_load_private_cert(int type, const char *filename, const char *passphrase,
    int *perm_ok)
{
	int r;
	Key *ret = NULL;

	if ((r = sshkey_load_private_cert(type, filename, passphrase,
	    &ret, perm_ok)) != 0) {
		fatal_on_fatal_errors(r, __func__, SSH_ERR_LIBCRYPTO_ERROR);
		/* Old authfile.c ignored all file errors. */
		if (r == SSH_ERR_SYSTEM_ERROR ||
		    r == SSH_ERR_KEY_WRONG_PASSPHRASE)
			debug("%s: %s", __func__, ssh_err(r));
		else
			error("%s: %s", __func__, ssh_err(r));
		return NULL;
	}
	return ret;
}

Key *
key_load_private_type(int type, const char *filename, const char *passphrase,
    char **commentp, int *perm_ok)
{
	int r;
	Key *ret = NULL;

	if ((r = sshkey_load_private_type(type, filename, passphrase,
	    &ret, commentp, perm_ok)) != 0) {
		fatal_on_fatal_errors(r, __func__, SSH_ERR_LIBCRYPTO_ERROR);
		/* Old authfile.c ignored all file errors. */
		if (r == SSH_ERR_SYSTEM_ERROR ||
		    (r == SSH_ERR_KEY_WRONG_PASSPHRASE))
			debug("%s: %s", __func__, ssh_err(r));
		else
			error("%s: %s", __func__, ssh_err(r));
		return NULL;
	}
	return ret;
}
