summaryrefslogtreecommitdiff
path: root/src/adb_trust.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/adb_trust.c')
-rw-r--r--src/adb_trust.c266
1 files changed, 266 insertions, 0 deletions
diff --git a/src/adb_trust.c b/src/adb_trust.c
new file mode 100644
index 0000000..41c904f
--- /dev/null
+++ b/src/adb_trust.c
@@ -0,0 +1,266 @@
+#include <errno.h>
+#include <stdio.h>
+#include <openssl/bio.h>
+#include <openssl/pem.h>
+#include <openssl/err.h>
+#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,
+};