#include #include #include #include #include #include #include #include #include #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; }