267 lines
6.6 KiB
C
267 lines
6.6 KiB
C
#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,
|
|
};
|