crypto: start seperating public/private key usage
Digests now use bearssl + libsodium instead of openssl + libsodium. It fits better with the API looking more similar and I don't think it will be that much of a bottleneck. Instead of a apk_pkey that mimicks the EVP_PKEY semantics, have seperate public and private key structures. However, apk is broken because of it and needs fixing. The rest of the code might compile but won't work as I haven't handled the cases where pkey corressponds to public/private keys in codebase properly. Also, DSA signatures are removed. Public and private keys impls' now use a static array of vtables internally. The indicies correspond to the signature scheme version and checking `< APK_*_MAX` should be enough? I tried to make so that higher values correspond to more desirable constructs, this might come in handy. Signing/verifying start is free from keys as it isn't used though it might be brought back for domain seperated hashes, ability to get timestamps involved with epheremal keys in the future etc. `apk_crypto.h` is free from openssl includes although some headers still use them for now.cute-signatures
parent
2727846361
commit
0b7d6dfbf2
@ -1,133 +0,0 @@
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
#include "apk_crypto.h"
|
||||
|
||||
int apk_digest_alg_len(uint8_t alg)
|
||||
{
|
||||
switch (alg) {
|
||||
case APK_DIGEST_MD5: return 16;
|
||||
case APK_DIGEST_SHA1: return 20;
|
||||
case APK_DIGEST_SHA256_160: return 20;
|
||||
case APK_DIGEST_SHA256: return 32;
|
||||
case APK_DIGEST_SHA512: return 64;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t apk_digest_alg_by_len(int len)
|
||||
{
|
||||
switch (len) {
|
||||
case 0: return APK_DIGEST_NONE;
|
||||
case 16: return APK_DIGEST_MD5;
|
||||
case 20: return APK_DIGEST_SHA1;
|
||||
case 32: return APK_DIGEST_SHA256;
|
||||
case 64: return APK_DIGEST_SHA512;
|
||||
default: return APK_DIGEST_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t apk_digest_alg_from_csum(int csum)
|
||||
{
|
||||
switch (csum) {
|
||||
case APK_CHECKSUM_NONE: return APK_DIGEST_NONE;
|
||||
case APK_CHECKSUM_MD5: return APK_DIGEST_MD5;
|
||||
case APK_CHECKSUM_SHA1: return APK_DIGEST_SHA1;
|
||||
default: return APK_DIGEST_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t apk_digest_from_blob(struct apk_digest *d, apk_blob_t b)
|
||||
{
|
||||
d->alg = apk_digest_alg_by_len(b.len);
|
||||
d->len = 0;
|
||||
if (d->alg != APK_DIGEST_NONE) {
|
||||
d->len = b.len;
|
||||
memcpy(d->data, b.ptr, d->len);
|
||||
}
|
||||
return d->alg;
|
||||
}
|
||||
|
||||
int apk_pkey_init(struct apk_pkey *pkey, EVP_PKEY *key)
|
||||
{
|
||||
unsigned char dig[EVP_MAX_MD_SIZE], *pub = NULL;
|
||||
unsigned int dlen = sizeof dig;
|
||||
int len;
|
||||
|
||||
if ((len = i2d_PublicKey(key, &pub)) < 0) return -APKE_CRYPTO_ERROR;
|
||||
EVP_Digest(pub, len, dig, &dlen, EVP_sha512(), NULL);
|
||||
memcpy(pkey->id, dig, sizeof pkey->id);
|
||||
OPENSSL_free(pub);
|
||||
|
||||
pkey->key = key;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void apk_pkey_free(struct apk_pkey *pkey)
|
||||
{
|
||||
EVP_PKEY_free(pkey->key);
|
||||
}
|
||||
|
||||
int apk_pkey_load(struct apk_pkey *pkey, int dirfd, const char *fn)
|
||||
{
|
||||
EVP_PKEY *key;
|
||||
BIO *bio;
|
||||
int fd;
|
||||
|
||||
fd = openat(dirfd, fn, O_RDONLY|O_CLOEXEC);
|
||||
if (fd < 0) return -errno;
|
||||
|
||||
bio = BIO_new_fp(fdopen(fd, "r"), BIO_CLOSE);
|
||||
if (!bio) return -ENOMEM;
|
||||
|
||||
key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
|
||||
if (!key) {
|
||||
(void)BIO_reset(bio);
|
||||
key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
|
||||
}
|
||||
ERR_clear_error();
|
||||
|
||||
BIO_free(bio);
|
||||
if (!key) return -APKE_CRYPTO_KEY_FORMAT;
|
||||
|
||||
apk_pkey_init(pkey, key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int apk_sign_start(struct apk_digest_ctx *dctx, struct apk_pkey *pkey)
|
||||
{
|
||||
if (EVP_MD_CTX_reset(dctx->mdctx) != 1 ||
|
||||
EVP_DigestSignInit(dctx->mdctx, NULL, EVP_sha512(), NULL, pkey->key) != 1)
|
||||
return -APKE_CRYPTO_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int apk_sign(struct apk_digest_ctx *dctx, void *sig, size_t *len)
|
||||
{
|
||||
if (EVP_DigestSignFinal(dctx->mdctx, sig, len) != 1) {
|
||||
ERR_print_errors_fp(stderr);
|
||||
return -APKE_SIGNATURE_FAIL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int apk_verify_start(struct apk_digest_ctx *dctx, struct apk_pkey *pkey)
|
||||
{
|
||||
if (EVP_MD_CTX_reset(dctx->mdctx) != 1 ||
|
||||
EVP_DigestVerifyInit(dctx->mdctx, NULL, EVP_sha512(), NULL, pkey->key) != 1)
|
||||
return -APKE_CRYPTO_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int apk_verify(struct apk_digest_ctx *dctx, void *sig, size_t len)
|
||||
{
|
||||
if (EVP_DigestVerifyFinal(dctx->mdctx, sig, len) != 1) {
|
||||
ERR_print_errors_fp(stderr);
|
||||
return -APKE_SIGNATURE_INVALID;
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,350 @@
|
||||
#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_PUBLIC_SIZE 69
|
||||
|
||||
union public_key_state {
|
||||
unsigned char cute[crypto_sign_ed25519_PUBLICKEYBYTES];
|
||||
EVP_PKEY *pkey;
|
||||
};
|
||||
|
||||
struct public_key_ops {
|
||||
int (*load)(struct apk_public_key *pub, int fd);
|
||||
int (*verify)(struct apk_public_key *pub, struct apk_digest_ctx *dctx, void *sig, size_t len);
|
||||
void (*free)(union public_key_state *state);
|
||||
};
|
||||
|
||||
static int public_key_load_v2(struct apk_public_key *pub, int fd);
|
||||
static int verify_rsa_pkcs1v15(struct apk_public_key *pub, struct apk_digest_ctx *dctx, void *sig, size_t len);
|
||||
static void public_key_free_v2(union public_key_state *state);
|
||||
|
||||
static int public_key_load_cute(struct apk_public_key *pub, int fd);
|
||||
static int verify_cute(struct apk_public_key *pub, struct apk_digest_ctx *dctx, void *sig, size_t len);
|
||||
static void public_key_free_cute(union public_key_state *state);
|
||||
|
||||
static const struct public_key_ops ops[] = {
|
||||
[APK_KEY_VERSION_V2] = {.load = public_key_load_v2, .verify = verify_rsa_pkcs1v15, .free = public_key_free_v2},
|
||||
[APK_KEY_VERSION_CUTE] = {.load = public_key_load_cute, .verify = verify_cute, .free = public_key_free_cute},
|
||||
};
|
||||
|
||||
/*
|
||||
* The format is as follows:
|
||||
* uint8[ 2] header = {'q', 't'}
|
||||
* uint8[16] key_id
|
||||
* uint8[32] public_key
|
||||
*/
|
||||
static int public_key_load_cute(struct apk_public_key *pub, int fd)
|
||||
{
|
||||
union public_key_state *state = (union public_key_state *) pub->impl;
|
||||
|
||||
char b64[APK_CUTE_PUBLIC_SIZE];
|
||||
uint8_t raw[50];
|
||||
size_t len;
|
||||
|
||||
if (read(fd, b64, APK_CUTE_PUBLIC_SIZE) != APK_CUTE_PUBLIC_SIZE) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (sodium_base642bin(raw, 50, b64, APK_CUTE_PUBLIC_SIZE, NULL, &len, NULL, sodium_base64_VARIANT_ORIGINAL)
|
||||
!= 0) {
|
||||
return -APKE_CRYPTO_KEY_FORMAT;
|
||||
}
|
||||
|
||||
if (len != 50) {
|
||||
return -APKE_CRYPTO_KEY_FORMAT;
|
||||
}
|
||||
|
||||
if (raw[0] != 'q' || raw[1] != 't') {
|
||||
return -APKE_CRYPTO_KEY_FORMAT;
|
||||
}
|
||||
|
||||
memcpy(pub->id, raw + 2, sizeof pub->id);
|
||||
memcpy(state->cute, raw + 18, crypto_sign_ed25519_PUBLICKEYBYTES);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The signature format is as follows:
|
||||
* uint8[ 2] header = {'q', 't'}
|
||||
* uint8[16] key_id
|
||||
* uint8[64] signature
|
||||
*/
|
||||
static int verify_cute(struct apk_public_key *pub, struct apk_digest_ctx *dctx, void *sig, size_t len)
|
||||
{
|
||||
union public_key_state *state = (union public_key_state *) pub->impl;
|
||||
|
||||
unsigned char *sigdata = (unsigned char *) sig;
|
||||
struct apk_digest digest;
|
||||
|
||||
if (len != 82) {
|
||||
return -APKE_SIGNATURE_INVALID;
|
||||
}
|
||||
|
||||
if (sigdata[0] != 'q' || sigdata[1] != 't') {
|
||||
return -APKE_SIGNATURE_INVALID;
|
||||
}
|
||||
|
||||
if (sodium_memcmp(pub->id, sigdata + 2, sizeof pub->id) != 0) {
|
||||
return -APKE_SIGNATURE_INVALID;
|
||||
}
|
||||
|
||||
if (dctx->alg != APK_DIGEST_SHA256) {
|
||||
return -APKE_SIGNATURE_INVALID;
|
||||
}
|
||||
|
||||
if (apk_digest_ctx_final(dctx, &digest) != 0) {
|
||||
return -APKE_SIGNATURE_INVALID;
|
||||
}
|
||||
|
||||
if (crypto_sign_ed25519_verify_detached(sigdata + 18, digest.data, digest.len, state->cute) != 0) {
|
||||
return -APKE_SIGNATURE_INVALID;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void public_key_free_cute(union public_key_state *state)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static int public_key_load_v2(struct apk_public_key *pub, int fd)
|
||||
{
|
||||
union public_key_state *state = (union public_key_state *) pub->impl;
|
||||
|
||||
unsigned char *raw = NULL;
|
||||
struct apk_digest digest;
|
||||
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;
|
||||
}
|
||||
|
||||
state->pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
|
||||
if (state->pkey == NULL) {
|
||||
r = -APKE_CRYPTO_KEY_FORMAT;
|
||||
goto err_load_and_check_type;
|
||||
}
|
||||
|
||||
if (state->pkey == NULL) {
|
||||
r = -APKE_CRYPTO_KEY_FORMAT;
|
||||
goto err_load_and_check_type;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_id(state->pkey) != EVP_PKEY_RSA) {
|
||||
r = -APKE_CRYPTO_KEY_FORMAT;
|
||||
goto err_load_and_check_type;
|
||||
}
|
||||
|
||||
len = i2d_PublicKey(state->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;
|
||||
}
|
||||
|
||||
memcpy(pub->id, digest.data, sizeof pub->id);
|
||||
|
||||
r = 0;
|
||||
|
||||
err_calculate_id:
|
||||
OPENSSL_free(raw);
|
||||
err_load_and_check_type:
|
||||
BIO_free(bio);
|
||||
err_bio_new:
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* RSA-PKCS1 v1.5 can work with different hashes.
|
||||
* Since APK v2 hasn't switched from SHA-1 yet, we still have to work with it for now.
|
||||
* However, MD5 is out of the question.
|
||||
*/
|
||||
static int verify_rsa_pkcs1v15(struct apk_public_key *pub, struct apk_digest_ctx *dctx, void *sig, size_t len)
|
||||
{
|
||||
union public_key_state *state = (union public_key_state *) pub->impl;
|
||||
|
||||
struct apk_digest digest;
|
||||
EVP_PKEY_CTX *pctx;
|
||||
const EVP_MD *md;
|
||||
int r = -APKE_SIGNATURE_INVALID;
|
||||
|
||||
if (apk_digest_ctx_final(dctx, &digest) != 0) {
|
||||
r = -APKE_SIGNATURE_INVALID;
|
||||
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_INVALID;
|
||||
goto err_digest_final_and_pctx_new;
|
||||
}
|
||||
|
||||
pctx = EVP_PKEY_CTX_new(state->pkey, NULL);
|
||||
if (pctx == NULL) {
|
||||
r = -APKE_SIGNATURE_INVALID;
|
||||
goto err_pctx_setup_and_verify;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_verify_init(pctx) != 1) {
|
||||
r = -APKE_SIGNATURE_INVALID;
|
||||
goto err_pctx_setup_and_verify;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PADDING) != 1) {
|
||||
r = -APKE_SIGNATURE_INVALID;
|
||||
goto err_pctx_setup_and_verify;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_CTX_set_signature_md(pctx, md) != 1) {
|
||||
r = -APKE_SIGNATURE_INVALID;
|
||||
goto err_pctx_setup_and_verify;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_verify(pctx, sig, len, digest.data, digest.len) != 1) {
|
||||
r = -APKE_SIGNATURE_INVALID;
|
||||
goto err_pctx_setup_and_verify;
|
||||
}
|
||||
|
||||
r = 0;
|
||||
|
||||
err_pctx_setup_and_verify:
|
||||
EVP_PKEY_CTX_free(pctx);
|
||||
err_digest_final_and_pctx_new:
|
||||
return r;
|
||||
}
|
||||
|
||||
static void public_key_free_v2(union public_key_state *state)
|
||||
{
|
||||
EVP_PKEY_free(state->pkey);
|
||||
}
|
||||
|
||||
int apk_public_key_load(struct apk_public_key *pub, int dirfd, const char *fn)
|
||||
{
|
||||
struct stat st;
|
||||
int fd = -1;
|
||||
int r = -APKE_CRYPTO_KEY_FORMAT;
|
||||
|
||||
pub->impl = malloc(sizeof(union public_key_state));
|
||||
if (pub->impl == NULL) {
|
||||
r = -ENOMEM;
|
||||
goto err_alloc;
|
||||
}
|
||||
|
||||
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_and_load;
|
||||
}
|
||||
|
||||
// guess
|
||||
if (st.st_size == APK_CUTE_PUBLIC_SIZE) {
|
||||
pub->version = APK_KEY_VERSION_CUTE;
|
||||
} else {
|
||||
pub->version = APK_KEY_VERSION_V2;
|
||||
}
|
||||
|
||||
r = ops[pub->version].load(pub, fd);
|
||||
if (r != 0) {
|
||||
goto err_fstat_and_load;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
|
||||
err_fstat_and_load:
|
||||
close(fd);
|
||||
err_openat:
|
||||
free(pub->impl);
|
||||
err_alloc:
|
||||
return r;
|
||||
}
|
||||
|
||||
void apk_public_key_free(struct apk_public_key *pub)
|
||||
{
|
||||
if (pub == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pub->impl == NULL || pub->version >= APK_KEY_VERSION_MAX) {
|
||||
return;
|
||||
}
|
||||
|
||||
ops[pub->version].free(pub->impl);
|
||||
|
||||
free(pub->impl);
|
||||
pub->impl = NULL;
|
||||
}
|
||||
|
||||
int apk_verify_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_verify(struct apk_public_key *pub, struct apk_digest_ctx *dctx, void *sig, size_t len)
|
||||
{
|
||||
if (pub->version >= APK_KEY_VERSION_MAX) {
|
||||
return -APKE_CRYPTO_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
return ops[pub->version].verify(pub, dctx, sig, len);
|
||||
}
|
||||
|
||||
#undef APK_CUTE_PUBLIC_SIZE
|
@ -0,0 +1,347 @@
|
||||
#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
|
||||
|
||||
union secret_key_state {
|
||||
unsigned char cute[crypto_sign_ed25519_SECRETKEYBYTES];
|
||||
EVP_PKEY *pkey;
|
||||
};
|
||||
|
||||
struct secret_key_ops {
|
||||
int (*load)(struct apk_secret_key *pub, int fd);
|
||||
int (*sign)(struct apk_secret_key *pub, struct apk_digest_ctx *dctx, void *sig, size_t *len);
|
||||
void (*free)(union secret_key_state *state);
|
||||
};
|
||||
|
||||
static int secret_key_load_v2(struct apk_secret_key *pub, int fd);
|
||||
static int sign_rsa_pkcs1v15(struct apk_secret_key *pub, struct apk_digest_ctx *dctx, void *sig, size_t *len);
|
||||
static void secret_key_free_v2(union secret_key_state *state);
|
||||
|
||||
static int secret_key_load_cute(struct apk_secret_key *pub, int fd);
|
||||
static int sign_cute(struct apk_secret_key *pub, struct apk_digest_ctx *dctx, void *sig, size_t *len);
|
||||
static void secret_key_free_cute(union secret_key_state *state);
|
||||
|
||||
static struct secret_key_ops ops[] = {
|
||||
[APK_KEY_VERSION_CUTE] = {.load = secret_key_load_cute, .sign = sign_cute, .free = secret_key_free_cute},
|
||||
[APK_KEY_VERSION_V2] = {.load = secret_key_load_v2, .sign = sign_rsa_pkcs1v15, .free = secret_key_free_v2},
|
||||
};
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
union secret_key_state *state = (union secret_key_state *) sec->impl;
|
||||
|
||||
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(state->cute, 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(union secret_key_state *state)
|
||||
{
|
||||
sodium_memzero(state->cute, crypto_sign_ed25519_SECRETKEYBYTES);
|
||||
}
|
||||
|
||||
static int sign_cute(struct apk_secret_key *sec, struct apk_digest_ctx *dctx, void *sig, size_t *len)
|
||||
{
|
||||
union secret_key_state *state = (union secret_key_state *) sec->impl;
|
||||
|
||||
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, state->cute);
|
||||
*len = 82;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int secret_key_load_v2(struct apk_secret_key *sec, int fd)
|
||||
{
|
||||
union secret_key_state *state = (union secret_key_state *) sec->impl;
|
||||
|
||||
struct apk_digest digest;
|
||||
unsigned char *raw = NULL;
|
||||
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;
|
||||
}
|
||||
|
||||
state->pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
|
||||
if (state->pkey == NULL) {
|
||||
r = -APKE_CRYPTO_KEY_FORMAT;
|
||||
goto err_load_and_check_type;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_id(state->pkey) != EVP_PKEY_RSA) {
|
||||
r = -APKE_CRYPTO_KEY_FORMAT;
|
||||
goto err_load_and_check_type;
|
||||
}
|
||||
|
||||
len = i2d_PublicKey(state->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_digest;
|
||||
}
|
||||
|
||||
memcpy(sec->id, digest.data, sizeof sec->id);
|
||||
|
||||
r = 0;
|
||||
|
||||
err_digest:
|
||||
OPENSSL_free(raw);
|
||||
err_load_and_check_type:
|
||||
BIO_free(bio);
|
||||
err_bio_new:
|
||||
return r;
|
||||
}
|
||||
|
||||
static int sign_rsa_pkcs1v15(struct apk_secret_key *sec, struct apk_digest_ctx *dctx, void *sig, size_t *len)
|
||||
{
|
||||
union secret_key_state *state = (union secret_key_state *) sec->impl;
|
||||
|
||||
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(state->pkey, 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(union secret_key_state *state)
|
||||
{
|
||||
EVP_PKEY_free(state->pkey);
|
||||
}
|
||||
|
||||
int apk_secret_key_load(struct apk_secret_key *sec, int dirfd, const char *fn)
|
||||
{
|
||||
struct stat st;
|
||||
int fd = -1;
|
||||
int r = -APKE_CRYPTO_KEY_FORMAT;
|
||||
|
||||
sec->impl = malloc(sizeof(union secret_key_state));
|
||||
if (sec->impl == NULL) {
|
||||
r = -ENOMEM;
|
||||
goto err_alloc;
|
||||
}
|
||||
|
||||
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) {
|
||||
sec->version = APK_KEY_VERSION_CUTE;
|
||||
} else {
|
||||
sec->version = APK_KEY_VERSION_V2;
|
||||
}
|
||||
|
||||
r = ops[sec->version].load(sec, fd);
|
||||
if (r != 0) {
|
||||
goto err_load;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
|
||||
err_load:
|
||||
err_fstat:
|
||||
close(fd);
|
||||
err_openat:
|
||||
free(sec->impl);
|
||||
err_alloc:
|
||||
return r;
|
||||
}
|
||||
|
||||
void apk_secret_key_free(struct apk_secret_key *sec)
|
||||
{
|
||||
if (sec == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sec->impl == NULL || sec->version >= APK_KEY_VERSION_MAX) {
|
||||
return;
|
||||
}
|
||||
|
||||
ops[sec->version].free(sec->impl);
|
||||
|
||||
free(sec->impl);
|
||||
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)
|
||||
{
|
||||
if (sec->version >= APK_KEY_VERSION_MAX) {
|
||||
return -APKE_CRYPTO_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
return ops[sec->version].sign(sec, dctx, sig, len);
|
||||
}
|
||||
|
||||
#undef APK_CUTE_SECRET_SIZE
|
@ -1,232 +0,0 @@
|
||||
#include <fcntl.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <sodium.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "apk_crypto.h"
|
||||
|
||||
union pkey_state {
|
||||
unsigned char cute_pk[crypto_sign_ed25519_PUBLICKEYBYTES];
|
||||
unsigned char cute_sk[crypto_sign_ed25519_SECRETKEYBYTES];
|
||||
EVP_PKEY *pkey;
|
||||
};
|
||||
|
||||
static int load_openssl(int fd, struct apk_pkey *pkey)
|
||||
{
|
||||
int r = -APKE_CRYPTO_KEY_FORMAT;
|
||||
union pkey_state *state;
|
||||
BIO *bio;
|
||||
|
||||
pkey->impl = malloc(sizeof(union pkey_state));
|
||||
if (pkey->impl == NULL) {
|
||||
r = -ENOMEM;
|
||||
goto err_alloc;
|
||||
}
|
||||
|
||||
bio = BIO_new_fd(fd, BIO_NOCLOSE);
|
||||
if (bio == NULL) {
|
||||
r = -ENOMEM;
|
||||
goto err_bio_new;
|
||||
}
|
||||
|
||||
state = (union pkey_state *) pkey->impl;
|
||||
|
||||
pkey->type = APK_PKEY_TYPE_PUBLIC;
|
||||
state->pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
|
||||
if (state->pkey == NULL) {
|
||||
pkey->type = APK_PKEY_TYPE_SECRET;
|
||||
BIO_reset(bio);
|
||||
state->pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
ERR_clear_error();
|
||||
|
||||
if (state->pkey == NULL) {
|
||||
r = -APKE_CRYPTO_KEY_FORMAT;
|
||||
goto err_load;
|
||||
}
|
||||
|
||||
BIO_free(bio);
|
||||
|
||||
return 0;
|
||||
|
||||
err_load:
|
||||
BIO_free(bio);
|
||||
err_bio_new:
|
||||
free(pkey->impl);
|
||||
err_alloc:
|
||||
return r;
|
||||
}
|
||||
|
||||
static int load_public_cute(int fd, struct apk_pkey *pkey)
|
||||
{
|
||||
char b64[APK_PKEY_CUTE_PUBLIC_SIZE];
|
||||
int r = -APKE_CRYPTO_KEY_FORMAT;
|
||||
union pkey_state *state;
|
||||
uint8_t raw[50]; // 2 + 16 + 32
|
||||
size_t len;
|
||||
|
||||
pkey->impl = malloc(sizeof(union pkey_state));
|
||||
if (pkey->impl == NULL) {
|
||||
r = -ENOMEM;
|
||||
goto err_alloc;
|
||||
}
|
||||
|
||||
state = (union pkey_state *) pkey->impl;
|
||||
|
||||
if (read(fd, b64, APK_PKEY_CUTE_PUBLIC_SIZE) != APK_PKEY_CUTE_PUBLIC_SIZE) {
|
||||
r = -APKE_CRYPTO_KEY_FORMAT;
|
||||
goto err_read;
|
||||
}
|
||||
|
||||
if (sodium_base642bin(raw,
|
||||
50,
|
||||
b64,
|
||||
APK_PKEY_CUTE_PUBLIC_SIZE,
|
||||
NULL,
|
||||
&len,
|
||||
NULL,
|
||||
sodium_base64_VARIANT_URLSAFE)
|
||||
!= 0) {
|
||||
r = -APKE_CRYPTO_KEY_FORMAT;
|
||||
goto err_b64decode;
|
||||
}
|
||||
|
||||
if (raw[0] != 'q' || raw[1] != 't') {
|
||||
r = -APKE_CRYPTO_KEY_FORMAT;
|
||||
goto err_rawheader;
|
||||
}
|
||||
|
||||
pkey->type = APK_PKEY_TYPE_PUBLIC;
|
||||
pkey->alg = APK_PKEY_ED25519;
|
||||
memcpy(pkey->id, raw + 2, 16);
|
||||
memcpy(state->cute_pk, raw + 18, crypto_sign_ed25519_PUBLICKEYBYTES);
|
||||
|
||||
return 0;
|
||||
|
||||
err_rawheader:
|
||||
err_b64decode:
|
||||
err_read:
|
||||
free(pkey->impl);
|
||||
err_alloc:
|
||||
return r;
|
||||
}
|
||||
|
||||
static int load_secret_cute(int fd, struct apk_pkey *pkey)
|
||||
{
|
||||
char b64[APK_PKEY_CUTE_SECRET_SIZE];
|
||||
int r = -APKE_CRYPTO_KEY_FORMAT;
|
||||
union pkey_state *state;
|
||||
uint8_t raw[82]; // 2 + 16 + 64
|
||||
size_t len;
|
||||
|
||||
pkey->impl = malloc(sizeof(union pkey_state));
|
||||
if (pkey->impl == NULL) {
|
||||
r = -ENOMEM;
|
||||
goto err_alloc;
|
||||
}
|
||||
|
||||
state = (union pkey_state *) pkey->impl;
|
||||
|
||||
if (read(fd, b64, APK_PKEY_CUTE_SECRET_SIZE) != APK_PKEY_CUTE_SECRET_SIZE) {
|
||||
r = -APKE_CRYPTO_KEY_FORMAT;
|
||||
goto err_read;
|
||||
}
|
||||
|
||||
if (sodium_base642bin(raw,
|
||||
82,
|
||||