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