#include <sys/param.h>
#ifdef _KERNEL
#include <sys/malloc.h>
#include <sys/systm.h>
#include <geom/geom.h>
#else
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#endif
#include <geom/eli/g_eli.h>
#ifdef _KERNEL
MALLOC_DECLARE(M_ELI);
#endif
static int
g_eli_mkey_verify(const unsigned char *mkey, const unsigned char *key)
{
const unsigned char *odhmac;
unsigned char chmac[SHA512_MDLEN];
unsigned char hmkey[SHA512_MDLEN];
g_eli_crypto_hmac(key, G_ELI_USERKEYLEN, "\x00", 1, hmkey, 0);
odhmac = mkey + G_ELI_DATAIVKEYLEN;
g_eli_crypto_hmac(hmkey, sizeof(hmkey), mkey, G_ELI_DATAIVKEYLEN,
chmac, 0);
explicit_bzero(hmkey, sizeof(hmkey));
return (!bcmp(odhmac, chmac, SHA512_MDLEN));
}
void
g_eli_mkey_hmac(unsigned char *mkey, const unsigned char *key)
{
unsigned char hmkey[SHA512_MDLEN];
unsigned char *odhmac;
g_eli_crypto_hmac(key, G_ELI_USERKEYLEN, "\x00", 1, hmkey, 0);
odhmac = mkey + G_ELI_DATAIVKEYLEN;
g_eli_crypto_hmac(hmkey, sizeof(hmkey), mkey, G_ELI_DATAIVKEYLEN,
odhmac, 0);
explicit_bzero(hmkey, sizeof(hmkey));
}
int
g_eli_mkey_decrypt(const struct g_eli_metadata *md, const unsigned char *key,
unsigned char *mkey, unsigned nkey)
{
unsigned char tmpmkey[G_ELI_MKEYLEN];
unsigned char enckey[SHA512_MDLEN];
const unsigned char *mmkey;
int bit, error;
if (nkey > G_ELI_MKEYLEN)
return (-1);
g_eli_crypto_hmac(key, G_ELI_USERKEYLEN, "\x01", 1, enckey, 0);
mmkey = md->md_mkeys + G_ELI_MKEYLEN * nkey;
bit = (1 << nkey);
if (!(md->md_keys & bit))
return (-1);
bcopy(mmkey, tmpmkey, G_ELI_MKEYLEN);
error = g_eli_crypto_decrypt(md->md_ealgo, tmpmkey,
G_ELI_MKEYLEN, enckey, md->md_keylen);
if (error != 0) {
explicit_bzero(tmpmkey, sizeof(tmpmkey));
explicit_bzero(enckey, sizeof(enckey));
return (error);
}
if (g_eli_mkey_verify(tmpmkey, key)) {
bcopy(tmpmkey, mkey, G_ELI_DATAIVKEYLEN);
explicit_bzero(tmpmkey, sizeof(tmpmkey));
explicit_bzero(enckey, sizeof(enckey));
return (0);
}
explicit_bzero(enckey, sizeof(enckey));
explicit_bzero(tmpmkey, sizeof(tmpmkey));
return (-1);
}
int
g_eli_mkey_decrypt_any(const struct g_eli_metadata *md,
const unsigned char *key, unsigned char *mkey, unsigned *nkeyp)
{
int error, nkey;
if (nkeyp != NULL)
*nkeyp = -1;
error = -1;
for (nkey = 0; nkey < G_ELI_MAXMKEYS; nkey++) {
error = g_eli_mkey_decrypt(md, key, mkey, nkey);
if (error == 0) {
if (nkeyp != NULL)
*nkeyp = nkey;
break;
} else if (error > 0) {
break;
}
}
return (error);
}
int
g_eli_mkey_encrypt(unsigned algo, const unsigned char *key, unsigned keylen,
unsigned char *mkey)
{
unsigned char enckey[SHA512_MDLEN];
int error;
g_eli_mkey_hmac(mkey, key);
g_eli_crypto_hmac(key, G_ELI_USERKEYLEN, "\x01", 1, enckey, 0);
error = g_eli_crypto_encrypt(algo, mkey, G_ELI_MKEYLEN, enckey, keylen);
explicit_bzero(enckey, sizeof(enckey));
return (error);
}
#ifdef _KERNEL
void
g_eli_mkey_propagate(struct g_eli_softc *sc, const unsigned char *mkey)
{
bcopy(mkey, sc->sc_mkey, sizeof(sc->sc_mkey));
bcopy(mkey, sc->sc_ivkey, sizeof(sc->sc_ivkey));
mkey += sizeof(sc->sc_ivkey);
if ((sc->sc_flags & G_ELI_FLAG_AUTH) != 0) {
g_eli_crypto_hmac(mkey, G_ELI_MAXKEYLEN, "\x11", 1,
sc->sc_akey, 0);
} else {
arc4rand(sc->sc_akey, sizeof(sc->sc_akey), 0);
}
g_eli_key_init(sc);
if ((sc->sc_flags & G_ELI_FLAG_AUTH) != 0) {
SHA256_Init(&sc->sc_akeyctx);
SHA256_Update(&sc->sc_akeyctx, sc->sc_akey,
sizeof(sc->sc_akey));
}
switch (sc->sc_ealgo) {
case CRYPTO_AES_XTS:
break;
default:
SHA256_Init(&sc->sc_ivctx);
SHA256_Update(&sc->sc_ivctx, sc->sc_ivkey,
sizeof(sc->sc_ivkey));
break;
}
}
#endif