apk-tools/src/crypto_sign.c

233 lines
4.5 KiB
C

#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,
b64,
APK_PKEY_CUTE_PUBLIC_SIZE,
NULL,
&len,
NULL,
sodium_base64_VARIANT_URLSAFE)
!= 0) {
r = -APKE_CRYPTO_KEY_FORMAT;
goto err_b64decode;
}
if (len != 82) {
r = -APKE_CRYPTO_KEY_FORMAT;
goto err_decodelen;
}
if (raw[0] != 'q' || raw[1] != 't') {
r = -APKE_CRYPTO_KEY_FORMAT;
goto err_rawheader;
}
pkey->type = APK_PKEY_TYPE_SECRET;
pkey->alg = APK_PKEY_ED25519;
memcpy(pkey->id, raw + 2, 16);
memcpy(state->cute_sk, raw + 18, crypto_sign_ed25519_SECRETKEYBYTES);
return 0;
err_rawheader:
err_decodelen:
err_b64decode:
err_read:
free(pkey->impl);
err_alloc:
return r;
}
void apk_pkey_free(struct apk_pkey *pkey)
{
union pkey_state *state = (union pkey_state *) pkey->impl;
switch (pkey->alg) {
case APK_PKEY_ED25519:
break;
case APK_PKEY_RSAPKCS1v15:
EVP_PKEY_free(state->pkey);
break;
default:
/* invalid state, don't touch impl */
return;
}
free(pkey->impl);
pkey->impl = NULL;
}
int apk_pkey_load(struct apk_pkey *pkey, int dirfd, const char *fn)
{
struct stat st;
int fd, r = 0;
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;
}
switch (st.st_size) {
case APK_PKEY_CUTE_SECRET_SIZE:
r = load_secret_cute(fd, pkey);
break;
case APK_PKEY_CUTE_PUBLIC_SIZE:
r = load_public_cute(fd, pkey);
break;
default:
r = load_openssl(fd, pkey);
break;
}
err_fstat:
close(fd);
err_openat:
return r;
}