#include #include #include #include #include #include "apk_defines.h" #include "adb.h" struct adb_trust_key { struct list_head key_node; struct adb_pkey key; }; /* Trust */ int adb_pkey_init(struct adb_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 -EIO; 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 adb_pkey_free(struct adb_pkey *pkey) { EVP_PKEY_free(pkey->key); } int adb_pkey_load(struct adb_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) { BIO_reset(bio); key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); } ERR_clear_error(); BIO_free(bio); if (!key) return -EBADMSG; adb_pkey_init(pkey, key); return 0; } static struct adb_trust_key *adb_trust_load_key(int dirfd, const char *filename) { struct adb_trust_key *key; int r; key = calloc(1, sizeof *key); if (!key) return ERR_PTR(-ENOMEM); r = adb_pkey_load(&key->key, dirfd, filename); if (r) { free(key); return ERR_PTR(-ENOKEY); } list_init(&key->key_node); return key; } static int __adb_trust_load_pubkey(void *pctx, int dirfd, const char *filename) { struct adb_trust *trust = pctx; struct adb_trust_key *key = adb_trust_load_key(dirfd, filename); if (!IS_ERR(key)) list_add_tail(&key->key_node, &trust->trusted_key_list); return 0; } int adb_trust_init(struct adb_trust *trust, int dirfd, struct apk_string_array *pkey_files) { char **fn; *trust = (struct adb_trust){ .mdctx = EVP_MD_CTX_new(), }; if (!trust->mdctx) return -ENOMEM; EVP_MD_CTX_set_flags(trust->mdctx, EVP_MD_CTX_FLAG_FINALISE); list_init(&trust->trusted_key_list); list_init(&trust->private_key_list); apk_dir_foreach_file(dirfd, __adb_trust_load_pubkey, trust); foreach_array_item(fn, pkey_files) { struct adb_trust_key *key = adb_trust_load_key(AT_FDCWD, *fn); if (IS_ERR(key)) return PTR_ERR(key); list_add_tail(&key->key_node, &trust->private_key_list); } return 0; } static void __adb_trust_free_keys(struct list_head *h) { struct adb_trust_key *tkey, *n; list_for_each_entry_safe(tkey, n, h, key_node) { list_del(&tkey->key_node); adb_pkey_free(&tkey->key); free(tkey); } } void adb_trust_free(struct adb_trust *trust) { if (!trust->mdctx) return; __adb_trust_free_keys(&trust->trusted_key_list); __adb_trust_free_keys(&trust->private_key_list); EVP_MD_CTX_free(trust->mdctx); } static int adb_verify_ctx_calc(struct adb_verify_ctx *vfy, unsigned int hash_alg, apk_blob_t data, apk_blob_t *pmd) { const EVP_MD *evp; apk_blob_t md; switch (hash_alg) { case ADB_HASH_SHA512: evp = EVP_sha512(); *pmd = md = APK_BLOB_BUF(vfy->sha512); break; default: return -ENOTSUP; } if (!(vfy->calc & (1 << hash_alg))) { unsigned int sz = md.len; if (APK_BLOB_IS_NULL(data)) return -ENOMSG; if (EVP_Digest(data.ptr, data.len, (unsigned char*) md.ptr, &sz, evp, NULL) != 1 || sz != md.len) return -EIO; vfy->calc |= (1 << hash_alg); } return 0; } int adb_trust_write_signatures(struct adb_trust *trust, struct adb *db, struct adb_verify_ctx *vfy, struct apk_ostream *os) { union { struct adb_sign_hdr hdr; struct adb_sign_v0 v0; unsigned char buf[8192]; } sig; struct adb_trust_key *tkey; apk_blob_t md; size_t siglen; int r; if (!vfy) { vfy = alloca(sizeof *vfy); memset(vfy, 0, sizeof *vfy); } r = adb_verify_ctx_calc(vfy, ADB_HASH_SHA512, db->adb, &md); if (r) return r; list_for_each_entry(tkey, &trust->private_key_list, key_node) { sig.v0 = (struct adb_sign_v0) { .hdr.sign_ver = 0, .hdr.hash_alg = ADB_HASH_SHA512, }; memcpy(sig.v0.id, tkey->key.id, sizeof(sig.v0.id)); siglen = sizeof sig.buf - sizeof sig.v0; EVP_MD_CTX_set_pkey_ctx(trust->mdctx, NULL); if (EVP_DigestSignInit(trust->mdctx, NULL, EVP_sha512(), NULL, tkey->key.key) != 1 || EVP_DigestUpdate(trust->mdctx, &db->hdr, sizeof db->hdr) != 1 || EVP_DigestUpdate(trust->mdctx, &sig.hdr.sign_ver, sizeof sig.hdr.sign_ver) != 1 || EVP_DigestUpdate(trust->mdctx, &sig.hdr.hash_alg, sizeof sig.hdr.hash_alg) != 1 || EVP_DigestUpdate(trust->mdctx, md.ptr, md.len) != 1 || EVP_DigestSignFinal(trust->mdctx, sig.v0.sig, &siglen) != 1) { ERR_print_errors_fp(stdout); goto err_io; } r = adb_c_block(os, ADB_BLOCK_SIG, APK_BLOB_PTR_LEN((char*) &sig, sizeof(sig.v0) + siglen)); if (r < 0) goto err; } return 0; err_io: r = -EIO; err: apk_ostream_cancel(os, r); return r; } int adb_trust_verify_signature(struct adb_trust *trust, struct adb *db, struct adb_verify_ctx *vfy, apk_blob_t sigb) { struct adb_trust_key *tkey; struct adb_sign_hdr *sig; struct adb_sign_v0 *sig0; apk_blob_t md; if (APK_BLOB_IS_NULL(db->adb)) return -ENOMSG; if (sigb.len < sizeof(struct adb_sign_hdr)) return -EBADMSG; sig = (struct adb_sign_hdr *) sigb.ptr; sig0 = (struct adb_sign_v0 *) sigb.ptr; if (sig->sign_ver != 0) return -ENOSYS; list_for_each_entry(tkey, &trust->trusted_key_list, key_node) { if (memcmp(sig0->id, tkey->key.id, sizeof sig0->id) != 0) continue; if (adb_verify_ctx_calc(vfy, sig->hash_alg, db->adb, &md) != 0) continue; EVP_MD_CTX_set_pkey_ctx(trust->mdctx, NULL); if (EVP_DigestVerifyInit(trust->mdctx, NULL, EVP_sha512(), NULL, tkey->key.key) != 1 || EVP_DigestUpdate(trust->mdctx, &db->hdr, sizeof db->hdr) != 1 || EVP_DigestUpdate(trust->mdctx, &sig->sign_ver, sizeof sig->sign_ver) != 1 || EVP_DigestUpdate(trust->mdctx, &sig->hash_alg, sizeof sig->hash_alg) != 1 || EVP_DigestUpdate(trust->mdctx, md.ptr, md.len) != 1 || EVP_DigestVerifyFinal(trust->mdctx, sig0->sig, sigb.len - sizeof(*sig0)) != 1) { ERR_clear_error(); continue; } return 0; } return -EKEYREJECTED; } /* Command group for signing */ #include "apk_applet.h" #define SIGNING_OPTIONS(OPT) \ OPT(OPT_SIGN_sign_key, APK_OPT_ARG "sign-key") APK_OPT_GROUP(options_signing, "Signing", SIGNING_OPTIONS); static int option_parse_signing(void *ctx, struct apk_db_options *dbopts, int optch, const char *optarg) { switch (optch) { case OPT_SIGN_sign_key: *apk_string_array_add(&dbopts->private_keys) = (char*) optarg; break; default: return -ENOTSUP; } return 0; } const struct apk_option_group optgroup_signing = { .desc = options_signing, .parse = option_parse_signing, };