320 lines
6.3 KiB
C
320 lines
6.3 KiB
C
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <openssl/evp.h>
|
|
#include <openssl/pem.h>
|
|
#include <openssl/rsa.h>
|
|
#include <sodium.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#include "apk_crypto.h"
|
|
|
|
/* includes '\0', undef in the end */
|
|
#define APK_CUTE_SECRET_SIZE 113
|
|
|
|
/*
|
|
* The format is as follows:
|
|
* uint8[ 2] header = {'q', 't'}
|
|
* uint8[16] key_id
|
|
* uint8[64] secret_key
|
|
*
|
|
*/
|
|
static int secret_key_load_cute(struct apk_secret_key *sec, int fd)
|
|
{
|
|
char b64[APK_CUTE_SECRET_SIZE];
|
|
uint8_t raw[82];
|
|
size_t len;
|
|
int r = -APKE_CRYPTO_KEY_FORMAT;
|
|
|
|
if (read(fd, b64, APK_CUTE_SECRET_SIZE) != APK_CUTE_SECRET_SIZE) {
|
|
r = -errno;
|
|
goto err;
|
|
}
|
|
|
|
if (sodium_base642bin(raw, 82, b64, APK_CUTE_SECRET_SIZE, NULL, &len, NULL, sodium_base64_VARIANT_ORIGINAL)
|
|
!= 0) {
|
|
r = -APKE_CRYPTO_KEY_FORMAT;
|
|
goto err;
|
|
}
|
|
|
|
if (raw[0] != 'q' || raw[1] != 't') {
|
|
r = -APKE_CRYPTO_KEY_FORMAT;
|
|
goto err;
|
|
}
|
|
|
|
sec->version = APK_KEY_VERSION_V2;
|
|
memcpy(sec->id, raw + 2, 16);
|
|
memcpy(sec->impl, raw + 18, crypto_sign_ed25519_SECRETKEYBYTES);
|
|
|
|
r = 0;
|
|
|
|
err:
|
|
sodium_memzero(b64, sizeof b64);
|
|
sodium_memzero(raw, sizeof raw);
|
|
return r;
|
|
}
|
|
|
|
static void secret_key_free_cute(struct apk_secret_key *sec)
|
|
{
|
|
sodium_memzero(sec->impl, crypto_sign_ed25519_SECRETKEYBYTES);
|
|
sodium_free(sec->impl);
|
|
}
|
|
|
|
static int sign_cute(struct apk_secret_key *sec, struct apk_digest_ctx *dctx, void *sig, size_t *len)
|
|
{
|
|
unsigned char *sigdata = (unsigned char *) sig;
|
|
struct apk_digest digest;
|
|
|
|
if (*len < 82) {
|
|
return -APKE_SIGNATURE_FAIL;
|
|
}
|
|
|
|
if (dctx->alg != APK_DIGEST_SHA256) {
|
|
return -APKE_SIGNATURE_FAIL;
|
|
}
|
|
|
|
if (apk_digest_ctx_final(dctx, &digest) != 0) {
|
|
return -APKE_SIGNATURE_FAIL;
|
|
}
|
|
|
|
sigdata[0] = 'q';
|
|
sigdata[1] = 't';
|
|
memcpy(sigdata + 2, sec->id, 16);
|
|
crypto_sign_ed25519_detached(sigdata + 18, NULL, digest.data, digest.len, sec->impl);
|
|
*len = 82;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int secret_key_load_v2(struct apk_secret_key *sec, int fd)
|
|
{
|
|
unsigned char *raw = NULL;
|
|
struct apk_digest digest;
|
|
EVP_PKEY *pkey;
|
|
BIO *bio;
|
|
int len;
|
|
int r = -APKE_CRYPTO_KEY_FORMAT;
|
|
|
|
bio = BIO_new_fd(fd, BIO_NOCLOSE);
|
|
if (bio == NULL) {
|
|
r = -ENOMEM;
|
|
goto err_bio_new;
|
|
}
|
|
|
|
pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
|
|
if (pkey == NULL) {
|
|
r = -APKE_CRYPTO_KEY_FORMAT;
|
|
goto err_pem_read;
|
|
}
|
|
|
|
if (EVP_PKEY_id(pkey) != EVP_PKEY_RSA) {
|
|
r = -APKE_CRYPTO_KEY_FORMAT;
|
|
goto err_load_and_check_type;
|
|
}
|
|
|
|
len = i2d_PublicKey(pkey, &raw);
|
|
if (len < 0) {
|
|
r = -APKE_CRYPTO_ERROR;
|
|
goto err_load_and_check_type;
|
|
}
|
|
|
|
if (apk_digest_calc(&digest, APK_DIGEST_SHA512, raw, len) != 0) {
|
|
r = -APKE_CRYPTO_ERROR;
|
|
goto err_calculate_id_and_finalize;
|
|
}
|
|
|
|
sec->impl = pkey;
|
|
if (EVP_PKEY_up_ref(pkey) != 1) {
|
|
r = -APKE_CRYPTO_ERROR;
|
|
goto err_calculate_id_and_finalize;
|
|
}
|
|
|
|
sec->version = APK_KEY_VERSION_V2;
|
|
memcpy(sec->id, digest.data, sizeof sec->id);
|
|
|
|
r = 0;
|
|
|
|
err_calculate_id_and_finalize:
|
|
OPENSSL_free(raw);
|
|
err_load_and_check_type:
|
|
EVP_PKEY_free(pkey);
|
|
err_pem_read:
|
|
BIO_free(bio);
|
|
err_bio_new:
|
|
return r;
|
|
}
|
|
|
|
static int sign_v2(struct apk_secret_key *sec, struct apk_digest_ctx *dctx, void *sig, size_t *len)
|
|
{
|
|
struct apk_digest digest;
|
|
EVP_PKEY_CTX *pctx;
|
|
const EVP_MD *md;
|
|
int r = -APKE_SIGNATURE_FAIL;
|
|
size_t needed;
|
|
|
|
if (apk_digest_ctx_final(dctx, &digest) != 0) {
|
|
r = -APKE_SIGNATURE_FAIL;
|
|
goto err_digest_final_and_pctx_new;
|
|
}
|
|
|
|
switch (dctx->alg) {
|
|
case APK_DIGEST_SHA1:
|
|
md = EVP_sha1();
|
|
break;
|
|
case APK_DIGEST_SHA256:
|
|
md = EVP_sha256();
|
|
break;
|
|
case APK_DIGEST_SHA512:
|
|
md = EVP_sha512();
|
|
break;
|
|
case APK_DIGEST_NONE:
|
|
case APK_DIGEST_MD5:
|
|
default:
|
|
r = -APKE_SIGNATURE_FAIL;
|
|
goto err_digest_final_and_pctx_new;
|
|
}
|
|
|
|
pctx = EVP_PKEY_CTX_new(sec->impl, NULL);
|
|
if (pctx == NULL) {
|
|
r = -APKE_SIGNATURE_FAIL;
|
|
goto err_digest_final_and_pctx_new;
|
|
}
|
|
|
|
if (EVP_PKEY_sign_init(pctx) != 1) {
|
|
r = -APKE_SIGNATURE_FAIL;
|
|
goto err_pctx_setup_and_sign;
|
|
}
|
|
|
|
if (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PADDING) != 1) {
|
|
r = -APKE_SIGNATURE_FAIL;
|
|
goto err_pctx_setup_and_sign;
|
|
}
|
|
|
|
if (EVP_PKEY_CTX_set_signature_md(pctx, md) != 1) {
|
|
r = -APKE_SIGNATURE_INVALID;
|
|
goto err_pctx_setup_and_sign;
|
|
}
|
|
|
|
/* Determine minimum required buffer size */
|
|
if (EVP_PKEY_sign(pctx, NULL, &needed, digest.data, digest.len) != 1) {
|
|
r = -APKE_SIGNATURE_FAIL;
|
|
goto err_pctx_setup_and_sign;
|
|
}
|
|
|
|
if (needed < *len) {
|
|
r = -APKE_SIGNATURE_FAIL;
|
|
goto err_pctx_setup_and_sign;
|
|
}
|
|
|
|
if (EVP_PKEY_sign(pctx, sig, len, digest.data, digest.len) != 1) {
|
|
r = -APKE_SIGNATURE_FAIL;
|
|
goto err_pctx_setup_and_sign;
|
|
}
|
|
|
|
r = 0;
|
|
|
|
err_pctx_setup_and_sign:
|
|
EVP_PKEY_CTX_free(pctx);
|
|
err_digest_final_and_pctx_new:
|
|
return r;
|
|
}
|
|
|
|
static void secret_key_free_v2(struct apk_secret_key *sec)
|
|
{
|
|
EVP_PKEY_free(sec->impl);
|
|
sec->impl = NULL;
|
|
}
|
|
|
|
int apk_secret_key_load(struct apk_secret_key *sec, int dirfd, const char *fn)
|
|
{
|
|
struct stat st;
|
|
int fd, r = -APKE_CRYPTO_KEY_FORMAT;
|
|
|
|
fd = openat(dirfd, fn, O_RDONLY | O_CLOEXEC);
|
|
if (fd == -1) {
|
|
r = -errno;
|
|
goto err_openat;
|
|
}
|
|
|
|
if (fstat(fd, &st) != 0) {
|
|
r = -errno;
|
|
goto err_fstat;
|
|
}
|
|
|
|
if (st.st_size == APK_CUTE_SECRET_SIZE) {
|
|
r = secret_key_load_cute(sec, fd);
|
|
} else {
|
|
r = secret_key_load_v2(sec, fd);
|
|
}
|
|
|
|
err_fstat:
|
|
close(fd);
|
|
err_openat:
|
|
return r;
|
|
}
|
|
|
|
void apk_secret_key_free(struct apk_secret_key *sec)
|
|
{
|
|
if (sec == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (sec->impl == NULL) {
|
|
return;
|
|
}
|
|
|
|
switch (sec->version) {
|
|
case APK_KEY_VERSION_V2:
|
|
secret_key_free_v2(sec);
|
|
break;
|
|
case APK_KEY_VERSION_CUTE:
|
|
secret_key_free_cute(sec);
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
sec->impl = NULL;
|
|
}
|
|
|
|
int apk_sign_digest_start(struct apk_digest_ctx *dctx, uint16_t signature_type)
|
|
{
|
|
uint8_t digest;
|
|
|
|
switch (signature_type) {
|
|
case APK_SIGNATURE_CUTE:
|
|
case APK_SIGNATURE_RSA256:
|
|
digest = APK_DIGEST_SHA256;
|
|
break;
|
|
case APK_SIGNATURE_RSA512:
|
|
digest = APK_DIGEST_SHA512;
|
|
break;
|
|
case APK_SIGNATURE_RSA:
|
|
digest = APK_DIGEST_SHA1;
|
|
break;
|
|
default:
|
|
return -APKE_CRYPTO_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (apk_digest_ctx_reset(dctx, digest) != 0) {
|
|
return -APKE_CRYPTO_ERROR;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int apk_sign(struct apk_secret_key *sec, struct apk_digest_ctx *dctx, void *sig, size_t *len)
|
|
{
|
|
switch (sec->version) {
|
|
case APK_KEY_VERSION_V2:
|
|
return sign_v2(sec, dctx, sig, len);
|
|
case APK_KEY_VERSION_CUTE:
|
|
return sign_cute(sec, dctx, sig, len);
|
|
default:
|
|
return -APKE_CRYPTO_NOT_SUPPORTED;
|
|
}
|
|
}
|
|
|
|
#undef APK_CUTE_SECRET_SIZE
|