diff --git a/src/adb.c b/src/adb.c index a687aba..277aba6 100644 --- a/src/adb.c +++ b/src/adb.c @@ -198,12 +198,13 @@ static int __adb_m_stream(struct adb *db, struct apk_istream *is, uint32_t expec r = -APKE_ADB_BLOCK; break; } - sz = adb_block_size(&blk) - sizeof blk; + + sz = adb_block_length(&blk); switch (type) { case ADB_BLOCK_ADB: allowed = BIT(ADB_BLOCK_SIG) | BIT(ADB_BLOCK_DATA) | BIT(ADB_BLOCK_DATAX); db->adb.ptr = malloc(sz); - db->adb.len = adb_block_length(&blk); + db->adb.len = sz; if (db->adb.len < 16) { r = -APKE_ADB_BLOCK; goto err; @@ -215,7 +216,7 @@ static int __adb_m_stream(struct adb *db, struct apk_istream *is, uint32_t expec } r = cb(db, &blk, apk_istream_from_blob(&seg.is, db->adb)); if (r < 0) goto err; - continue; + goto skip_padding; case ADB_BLOCK_SIG: sig = apk_istream_peek(is, sz); if (IS_ERR(sig)) { @@ -223,7 +224,7 @@ static int __adb_m_stream(struct adb *db, struct apk_istream *is, uint32_t expec goto err; } if (!trusted && - adb_trust_verify_signature(t, db, &vfy, APK_BLOB_PTR_LEN(sig, adb_block_length(&blk))) == 0) + adb_trust_verify_signature(t, db, &vfy, APK_BLOB_PTR_LEN(sig, sz)) == 0) trusted = 1; break; case ADB_BLOCK_DATA: @@ -237,8 +238,11 @@ static int __adb_m_stream(struct adb *db, struct apk_istream *is, uint32_t expec apk_istream_segment(&seg, is, sz, 0); r = cb(db, &blk, &seg.is); + r = apk_istream_close_error(&seg.is, r); if (r < 0) break; - r = apk_istream_close(&seg.is); + + skip_padding: + r = apk_istream_read(is, 0, adb_block_padding(&blk)); if (r < 0) break; } while (1); err: diff --git a/src/apk_crypto.h b/src/apk_crypto.h index d3ce24b..4eab130 100644 --- a/src/apk_crypto.h +++ b/src/apk_crypto.h @@ -82,9 +82,9 @@ static inline int apk_digest_calc(struct apk_digest *d, uint8_t alg, const void } static inline int apk_digest_ctx_init(struct apk_digest_ctx *dctx, uint8_t alg) { + dctx->alg = alg; dctx->mdctx = EVP_MD_CTX_new(); if (!dctx->mdctx) return -ENOMEM; - dctx->alg = alg; #ifdef EVP_MD_CTX_FLAG_FINALISE EVP_MD_CTX_set_flags(dctx->mdctx, EVP_MD_CTX_FLAG_FINALISE); #endif diff --git a/src/apk_io.h b/src/apk_io.h index 3ad0790..762f9e0 100644 --- a/src/apk_io.h +++ b/src/apk_io.h @@ -135,6 +135,14 @@ struct apk_segment_istream { }; struct apk_istream *apk_istream_segment(struct apk_segment_istream *sis, struct apk_istream *is, size_t len, time_t mtime); +struct apk_digest_istream { + struct apk_istream is; + struct apk_istream *pis; + struct apk_digest *digest; + struct apk_digest_ctx dctx; +}; +struct apk_istream *apk_istream_verify(struct apk_digest_istream *dis, struct apk_istream *is, struct apk_digest *d); + #define APK_ISTREAM_TEE_COPY_META 1 #define APK_ISTREAM_TEE_OPTIONAL 2 diff --git a/src/app_extract.c b/src/app_extract.c index dd410e5..57d40fe 100644 --- a/src/app_extract.c +++ b/src/app_extract.c @@ -16,22 +16,15 @@ #include "apk_applet.h" #include "apk_print.h" -#include "apk_adb.h" -#include "apk_pathbuilder.h" #include "apk_extract.h" struct extract_ctx { const char *destination; unsigned int extract_flags; + struct apk_extract_ctx ectx; struct apk_ctx *ac; - struct adb db; int root_fd; - - struct adb_obj pkg, paths, path, files, file; - unsigned int cur_path, cur_file; - - struct apk_pathbuilder pb; }; @@ -63,22 +56,6 @@ static const struct apk_option_group optgroup_applet = { .parse = option_parse_applet, }; -static void apk_extract_acl(struct apk_file_info *fi, struct adb_obj *o, struct apk_id_cache *idc) -{ - fi->mode = adb_ro_int(o, ADBI_ACL_MODE); - fi->uid = apk_id_cache_resolve_uid(idc, adb_ro_blob(o, ADBI_ACL_USER), 65534); - fi->gid = apk_id_cache_resolve_gid(idc, adb_ro_blob(o, ADBI_ACL_GROUP), 65534); -} - -static const char *uvol_detect(struct apk_ctx *ac, const char *path) -{ - if (!apk_ctx_get_uvol(ac)) return 0; - if (strncmp(path, "uvol", 4) != 0) return 0; - if (path[4] == 0) return path; - if (path[4] == '/') return &path[5]; - return 0; -} - static int uvol_run(struct apk_ctx *ac, char *action, const char *volname, char *arg1, char *arg2) { struct apk_out *out = &ac->out; @@ -103,7 +80,7 @@ static int uvol_run(struct apk_ctx *ac, char *action, const char *volname, char return 0; } -static int uvol_extract(struct apk_ctx *ac, const char *volname, char *arg1, off_t sz, struct apk_istream *is, struct apk_digest_ctx *dctx) +static int uvol_extract(struct apk_ctx *ac, const char *volname, char *arg1, off_t sz, struct apk_istream *is) { struct apk_out *out = &ac->out; struct apk_ostream *os; @@ -124,7 +101,7 @@ static int uvol_extract(struct apk_ctx *ac, const char *volname, char *arg1, off } close(pipefds[0]); os = apk_ostream_to_fd(pipefds[1]); - apk_stream_copy(is, os, sz, 0, 0, dctx); + apk_stream_copy(is, os, sz, 0, 0, 0); r = apk_ostream_close(os); if (r != 0) { if (r >= 0) r = -APKE_UVOL; @@ -141,7 +118,7 @@ static int uvol_extract(struct apk_ctx *ac, const char *volname, char *arg1, off return 0; } -static int apk_extract_volume(struct apk_ctx *ac, struct apk_file_info *fi, struct apk_istream *is, struct apk_digest_ctx *dctx) +static int apk_extract_volume(struct apk_ctx *ac, const struct apk_file_info *fi, struct apk_istream *is) { char size[64]; int r; @@ -149,195 +126,39 @@ static int apk_extract_volume(struct apk_ctx *ac, struct apk_file_info *fi, stru snprintf(size, sizeof size, "%ju", fi->size); r = uvol_run(ac, "create", fi->uvol_name, size, "ro"); if (r != 0) return r; - return uvol_extract(ac, fi->uvol_name, size, fi->size, is, dctx); -} -static int apk_extract_adb_file(struct extract_ctx *ctx, off_t sz, struct apk_istream *is) -{ - struct apk_ctx *ac = ctx->ac; - struct apk_out *out = &ac->out; - const char *path_name = apk_pathbuilder_cstr(&ctx->pb); - struct apk_file_info fi = { - .name = path_name, - .uvol_name = uvol_detect(ac, path_name), - .size = adb_ro_int(&ctx->file, ADBI_FI_SIZE), - .mtime = adb_ro_int(&ctx->file, ADBI_FI_MTIME), - }; - struct adb_obj acl; - struct apk_digest_ctx dctx; - struct apk_digest d; - apk_blob_t target; - int r; - - apk_extract_acl(&fi, adb_ro_obj(&ctx->file, ADBI_FI_ACL, &acl), apk_ctx_get_id_cache(ctx->ac)); - - target = adb_ro_blob(&ctx->file, ADBI_FI_TARGET); - if (!APK_BLOB_IS_NULL(target)) { - char *target_path; - uint16_t mode; - - if (target.len < 2) return -APKE_ADB_SCHEMA; - mode = *(uint16_t*)target.ptr; - target.ptr += 2; - target.len -= 2; - switch (mode) { - case S_IFBLK: - case S_IFCHR: - case S_IFIFO: - if (target.len != sizeof(uint64_t)) return -APKE_ADB_SCHEMA; - struct unaligned64 { - uint64_t value; - } __attribute__((packed)); - fi.device = ((struct unaligned64 *)target.ptr)->value; - break; - case S_IFLNK: - target_path = alloca(target.len + 1); - memcpy(target_path, target.ptr, target.len); - target_path[target.len] = 0; - fi.link_target = target_path; - break; - default: - return -APKE_ADB_SCHEMA; - } - fi.mode |= mode; - return apk_extract_file( - ctx->root_fd, &fi, 0, 0, is, 0, 0, 0, - ctx->extract_flags, out); - } - - apk_digest_from_blob(&fi.digest, adb_ro_blob(&ctx->file, ADBI_FI_HASHES)); - if (fi.digest.alg == APK_DIGEST_NONE) return -APKE_ADB_SCHEMA; - - fi.mode |= S_IFREG; - apk_digest_ctx_init(&dctx, fi.digest.alg); - if (fi.uvol_name) { - r = apk_extract_volume(ac, &fi, is, &dctx); - } else { - r = apk_extract_file( - ctx->root_fd, &fi, 0, 0, is, 0, 0, &dctx, - ctx->extract_flags, out); - if (r < 0) return r; - } - apk_digest_ctx_final(&dctx, &d); - apk_digest_ctx_free(&dctx); - if (r == 0 && apk_digest_cmp(&fi.digest, &d) != 0) - r = -APKE_FILE_INTEGRITY; - if (fi.uvol_name) { - if (r == 0) - r = uvol_run(ac, "up", fi.uvol_name, 0, 0); - else - uvol_run(ac, "remove", fi.uvol_name, 0, 0); - } else if (r != 0) - unlinkat(ctx->root_fd, fi.name, 0); + r = uvol_extract(ac, fi->uvol_name, size, fi->size, is); + if (r == 0) r = uvol_run(ac, "up", fi->uvol_name, 0, 0); + if (r != 0) uvol_run(ac, "remove", fi->uvol_name, 0, 0); return r; } -static int apk_extract_directory(struct extract_ctx *ctx) +static int extract_v3_meta(struct apk_extract_ctx *ectx, struct adb *db) { - struct apk_ctx *ac = ctx->ac; - struct apk_out *out = &ac->out; - struct apk_file_info fi = { - .name = apk_pathbuilder_cstr(&ctx->pb), - }; - struct adb_obj acl; - - if (uvol_detect(ac, fi.name)) return 0; - - apk_extract_acl(&fi, adb_ro_obj(&ctx->path, ADBI_DI_ACL, &acl), apk_ctx_get_id_cache(ctx->ac)); - fi.mode |= S_IFDIR; - - return apk_extract_file( - ctx->root_fd, &fi, 0, 0, 0, 0, 0, 0, - ctx->extract_flags, out); + return 0; } -static int apk_extract_next_file(struct extract_ctx *ctx) +static int extract_file(struct apk_extract_ctx *ectx, const struct apk_file_info *fi, struct apk_istream *is) { - apk_blob_t target; + struct extract_ctx *ctx = container_of(ectx, struct extract_ctx, ectx); int r; - if (!ctx->cur_path) { - // one time init - ctx->cur_path = ADBI_FIRST; - ctx->cur_file = 0; - adb_r_rootobj(&ctx->db, &ctx->pkg, &schema_package); - adb_ro_obj(&ctx->pkg, ADBI_PKG_PATHS, &ctx->paths); - adb_ro_obj(&ctx->paths, ctx->cur_path, &ctx->path); - adb_ro_obj(&ctx->path, ADBI_DI_FILES, &ctx->files); - } + if (fi->uvol_name) return apk_extract_volume(ectx->ac, fi, is); - do { - ctx->cur_file++; - while (ctx->cur_file > adb_ra_num(&ctx->files)) { - ctx->cur_path++; - ctx->cur_file = ADBI_FIRST; - if (ctx->cur_path > adb_ra_num(&ctx->paths)) return 1; - adb_ro_obj(&ctx->paths, ctx->cur_path, &ctx->path); - apk_pathbuilder_setb(&ctx->pb, adb_ro_blob(&ctx->path, ADBI_DI_NAME)); - adb_ro_obj(&ctx->path, ADBI_DI_FILES, &ctx->files); - r = apk_extract_directory(ctx); - if (r != 0) return r; - } - adb_ro_obj(&ctx->files, ctx->cur_file, &ctx->file); - apk_pathbuilder_setb(&ctx->pb, adb_ro_blob(&ctx->path, ADBI_DI_NAME)); - apk_pathbuilder_pushb(&ctx->pb, adb_ro_blob(&ctx->file, ADBI_FI_NAME)); - target = adb_ro_blob(&ctx->file, ADBI_FI_TARGET); - if (adb_ro_int(&ctx->file, ADBI_FI_SIZE) != 0 && - APK_BLOB_IS_NULL(target)) { - return 0; - } - r = apk_extract_adb_file(ctx, 0, 0); - if (r != 0) return r; - } while (1); -} + r = apk_extract_file(ctx->root_fd, fi, 0, 0, is, 0, 0, 0, + ctx->extract_flags, &ectx->ac->out); + r = apk_istream_close_error(is, r); -static int apk_extract_data_block(struct adb *db, struct adb_block *b, struct apk_istream *is) -{ - struct extract_ctx *ctx = container_of(db, struct extract_ctx, db); - struct adb_data_package *hdr; - size_t sz = adb_block_length(b); - int r; - - if (adb_block_type(b) != ADB_BLOCK_DATA) return 0; - - r = apk_extract_next_file(ctx); - if (r != 0) { - if (r > 0) r = -APKE_ADB_BLOCK; - return r; - } - - hdr = apk_istream_get(is, sizeof *hdr); - sz -= sizeof *hdr; - if (IS_ERR(hdr)) return PTR_ERR(hdr); - - if (hdr->path_idx != ctx->cur_path || - hdr->file_idx != ctx->cur_file || - sz != adb_ro_int(&ctx->file, ADBI_FI_SIZE)) { - // got data for some unexpected file - return -APKE_ADB_BLOCK; - } - - return apk_extract_adb_file(ctx, sz, is); -} - -static int apk_extract_pkg(struct extract_ctx *ctx, const char *fn) -{ - struct apk_ctx *ac = ctx->ac; - struct apk_trust *trust = apk_ctx_get_trust(ac); - int r; - - r = adb_m_process(&ctx->db, - adb_decompress(apk_istream_from_fd_url(AT_FDCWD, fn, apk_ctx_since(ac, 0)), 0), - ADB_SCHEMA_PACKAGE, trust, apk_extract_data_block); - if (r == 0) { - r = apk_extract_next_file(ctx); - if (r == 0) r = -APKE_ADB_BLOCK; - if (r == 1) r = 0; - } - adb_free(&ctx->db); + if (r != 0) unlinkat(ctx->root_fd, fi->name, 0); return r; } +static const struct apk_extract_ops extract_ops = { + .v2meta = apk_extract_v2_meta, + .v3meta = extract_v3_meta, + .file = extract_file, +}; + static int extract_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *args) { struct extract_ctx *ctx = pctx; @@ -356,9 +177,10 @@ static int extract_main(void *pctx, struct apk_ctx *ac, struct apk_string_array return r; } + apk_extract_init(&ctx->ectx, ac, &extract_ops); foreach_array_item(parg, args) { apk_out(out, "Extracting %s...", *parg); - r = apk_extract_pkg(ctx, *parg); + r = apk_extract(&ctx->ectx, apk_istream_from_fd_url(AT_FDCWD, *parg, apk_ctx_since(ac, 0))); if (r != 0) { apk_err(out, "%s: %s", *parg, apk_error_str(r)); break; diff --git a/src/extract_v3.c b/src/extract_v3.c index c6e80df..156bcbc 100644 --- a/src/extract_v3.c +++ b/src/extract_v3.c @@ -7,10 +7,210 @@ * SPDX-License-Identifier: GPL-2.0-only */ +#include + #include "apk_context.h" #include "apk_extract.h" +#include "apk_adb.h" +#include "apk_pathbuilder.h" + +struct apk_extract_v3_ctx { + struct apk_extract_ctx *ectx; + struct adb db; + struct adb_obj pkg, paths, path, files, file; + unsigned int cur_path, cur_file; + struct apk_pathbuilder pb; +}; + +static const char *uvol_detect(struct apk_ctx *ac, const char *path) +{ + if (!apk_ctx_get_uvol(ac)) return 0; + if (strncmp(path, "uvol", 4) != 0) return 0; + if (path[4] == 0) return path; + if (path[4] == '/') return &path[5]; + return 0; +} + +static void apk_extract_v3_acl(struct apk_file_info *fi, struct adb_obj *o, struct apk_id_cache *idc) +{ + fi->mode = adb_ro_int(o, ADBI_ACL_MODE); + fi->uid = apk_id_cache_resolve_uid(idc, adb_ro_blob(o, ADBI_ACL_USER), 65534); + fi->gid = apk_id_cache_resolve_gid(idc, adb_ro_blob(o, ADBI_ACL_GROUP), 65534); +} + +static int apk_extract_v3_file(struct apk_extract_ctx *ectx, off_t sz, struct apk_istream *is) +{ + struct apk_extract_v3_ctx *ctx = ectx->pctx; + struct apk_ctx *ac = ectx->ac; + const char *path_name = apk_pathbuilder_cstr(&ctx->pb); + struct apk_file_info fi = { + .name = path_name, + .uvol_name = uvol_detect(ac, path_name), + .size = adb_ro_int(&ctx->file, ADBI_FI_SIZE), + .mtime = adb_ro_int(&ctx->file, ADBI_FI_MTIME), + }; + struct adb_obj acl; + struct apk_digest_istream dis; + apk_blob_t target; + int r; + + apk_extract_v3_acl(&fi, adb_ro_obj(&ctx->file, ADBI_FI_ACL, &acl), apk_ctx_get_id_cache(ectx->ac)); + + target = adb_ro_blob(&ctx->file, ADBI_FI_TARGET); + if (!APK_BLOB_IS_NULL(target)) { + char *target_path; + uint16_t mode; + + if (target.len < 2) return -APKE_ADB_SCHEMA; + mode = *(uint16_t*)target.ptr; + target.ptr += 2; + target.len -= 2; + switch (mode) { + case S_IFBLK: + case S_IFCHR: + case S_IFIFO: + if (target.len != sizeof(uint64_t)) return -APKE_ADB_SCHEMA; + struct unaligned64 { + uint64_t value; + } __attribute__((packed)); + fi.device = ((struct unaligned64 *)target.ptr)->value; + break; + case S_IFLNK: + target_path = alloca(target.len + 1); + memcpy(target_path, target.ptr, target.len); + target_path[target.len] = 0; + fi.link_target = target_path; + break; + default: + return -APKE_ADB_SCHEMA; + } + fi.mode |= mode; + return ectx->ops->file(ectx, &fi, is); + } + + apk_digest_from_blob(&fi.digest, adb_ro_blob(&ctx->file, ADBI_FI_HASHES)); + if (fi.digest.alg == APK_DIGEST_NONE) return -APKE_ADB_SCHEMA; + + fi.mode |= S_IFREG; + r = ectx->ops->file(ectx, &fi, apk_istream_verify(&dis, is, &fi.digest)); + return apk_istream_close_error(&dis.is, r); +} + +static int apk_extract_v3_directory(struct apk_extract_ctx *ectx) +{ + struct apk_extract_v3_ctx *ctx = ectx->pctx; + struct apk_ctx *ac = ectx->ac; + struct apk_file_info fi = { + .name = apk_pathbuilder_cstr(&ctx->pb), + }; + struct adb_obj acl; + + if (uvol_detect(ac, fi.name)) return 0; + + apk_extract_v3_acl(&fi, adb_ro_obj(&ctx->path, ADBI_DI_ACL, &acl), apk_ctx_get_id_cache(ectx->ac)); + fi.mode |= S_IFDIR; + + return ectx->ops->file(ectx, &fi, 0); +} + +static int apk_extract_v3_next_file(struct apk_extract_ctx *ectx) +{ + struct apk_extract_v3_ctx *ctx = ectx->pctx; + apk_blob_t target; + int r; + + if (!ctx->cur_path) { + r = ectx->ops->v3meta(ectx, &ctx->db); + if (r < 0) return r; + + // one time init + ctx->cur_path = ADBI_FIRST; + ctx->cur_file = 0; + adb_r_rootobj(&ctx->db, &ctx->pkg, &schema_package); + adb_ro_obj(&ctx->pkg, ADBI_PKG_PATHS, &ctx->paths); + adb_ro_obj(&ctx->paths, ctx->cur_path, &ctx->path); + adb_ro_obj(&ctx->path, ADBI_DI_FILES, &ctx->files); + if (!ectx->ops->file) return -ECANCELED; + } + + do { + ctx->cur_file++; + while (ctx->cur_file > adb_ra_num(&ctx->files)) { + ctx->cur_path++; + ctx->cur_file = ADBI_FIRST; + if (ctx->cur_path > adb_ra_num(&ctx->paths)) return 1; + adb_ro_obj(&ctx->paths, ctx->cur_path, &ctx->path); + apk_pathbuilder_setb(&ctx->pb, adb_ro_blob(&ctx->path, ADBI_DI_NAME)); + adb_ro_obj(&ctx->path, ADBI_DI_FILES, &ctx->files); + r = apk_extract_v3_directory(ectx); + if (r != 0) return r; + } + adb_ro_obj(&ctx->files, ctx->cur_file, &ctx->file); + apk_pathbuilder_setb(&ctx->pb, adb_ro_blob(&ctx->path, ADBI_DI_NAME)); + apk_pathbuilder_pushb(&ctx->pb, adb_ro_blob(&ctx->file, ADBI_FI_NAME)); + target = adb_ro_blob(&ctx->file, ADBI_FI_TARGET); + if (adb_ro_int(&ctx->file, ADBI_FI_SIZE) != 0 && + APK_BLOB_IS_NULL(target)) { + return 0; + } + r = apk_extract_v3_file(ectx, 0, 0); + if (r != 0) return r; + } while (1); +} + +static int apk_extract_v3_data_block(struct adb *db, struct adb_block *b, struct apk_istream *is) +{ + struct apk_extract_v3_ctx *ctx = container_of(db, struct apk_extract_v3_ctx, db); + struct apk_extract_ctx *ectx = ctx->ectx; + struct adb_data_package *hdr; + size_t sz = adb_block_length(b); + int r; + + if (adb_block_type(b) != ADB_BLOCK_DATA) return 0; + + r = apk_extract_v3_next_file(ectx); + if (r != 0) { + if (r > 0) r = -APKE_ADB_BLOCK; + return r; + } + + hdr = apk_istream_get(is, sizeof *hdr); + sz -= sizeof *hdr; + if (IS_ERR(hdr)) return PTR_ERR(hdr); + + if (hdr->path_idx != ctx->cur_path || + hdr->file_idx != ctx->cur_file || + sz != adb_ro_int(&ctx->file, ADBI_FI_SIZE)) { + // got data for some unexpected file + return -APKE_ADB_BLOCK; + } + + return apk_extract_v3_file(ectx, sz, is); +} int apk_extract_v3(struct apk_extract_ctx *ectx, struct apk_istream *is) { - return apk_istream_close_error(is, -APKE_FORMAT_NOT_SUPPORTED); + struct apk_ctx *ac = ectx->ac; + struct apk_trust *trust = apk_ctx_get_trust(ac); + struct apk_extract_v3_ctx ctx = { + .ectx = ectx, + }; + int r; + + if (IS_ERR(is)) return PTR_ERR(is); + if (!ectx->ops || !ectx->ops->v3meta) + return apk_istream_close_error(is, -APKE_FORMAT_NOT_SUPPORTED); + + ectx->pctx = &ctx; + r = adb_m_process(&ctx.db, adb_decompress(is, 0), + ADB_SCHEMA_PACKAGE, trust, apk_extract_v3_data_block); + if (r == 0) { + r = apk_extract_v3_next_file(ectx); + if (r == 0) r = -APKE_ADB_BLOCK; + if (r == 1) r = 0; + } + if (r == -ECANCELED) r = 0; + adb_free(&ctx.db); + ectx->pctx = 0; + return r; } diff --git a/src/io.c b/src/io.c index d78959b..218e008 100644 --- a/src/io.c +++ b/src/io.c @@ -302,6 +302,61 @@ struct apk_istream *apk_istream_segment(struct apk_segment_istream *sis, struct return &sis->is; } +static void digest_get_meta(struct apk_istream *is, struct apk_file_meta *meta) +{ + struct apk_digest_istream *dis = container_of(is, struct apk_digest_istream, is); + return apk_istream_get_meta(dis->pis, meta); +} + +static ssize_t digest_read(struct apk_istream *is, void *ptr, size_t size) +{ + struct apk_digest_istream *dis = container_of(is, struct apk_digest_istream, is); + ssize_t r; + + r = dis->pis->ops->read(dis->pis, ptr, size); + if (r > 0) apk_digest_ctx_update(&dis->dctx, ptr, r); + return r; +} + +static int digest_close(struct apk_istream *is) +{ + struct apk_digest_istream *dis = container_of(is, struct apk_digest_istream, is); + + if (dis->digest) { + struct apk_digest res; + apk_digest_ctx_final(&dis->dctx, &res); + if (apk_digest_cmp(&res, dis->digest) != 0) + apk_istream_error(is, -APKE_FILE_INTEGRITY); + dis->digest = 0; + } + apk_digest_ctx_free(&dis->dctx); + + return is->err < 0 ? is->err : 0; +} + +static const struct apk_istream_ops digest_istream_ops = { + .get_meta = digest_get_meta, + .read = digest_read, + .close = digest_close, +}; + +struct apk_istream *apk_istream_verify(struct apk_digest_istream *dis, struct apk_istream *is, struct apk_digest *d) +{ + *dis = (struct apk_digest_istream) { + .is.ops = &digest_istream_ops, + .is.buf = is->buf, + .is.buf_size = is->buf_size, + .is.ptr = is->ptr, + .is.end = is->end, + .pis = is, + .digest = d, + }; + apk_digest_ctx_init(&dis->dctx, d->alg); + if (dis->is.ptr != dis->is.end) + apk_digest_ctx_update(&dis->dctx, dis->is.ptr, dis->is.end - dis->is.ptr); + return &dis->is; +} + struct apk_tee_istream { struct apk_istream is; struct apk_istream *inner_is;