From 3c993f0273c753653aee21a36d1deab4ee17f07f Mon Sep 17 00:00:00 2001 From: Andrew Bettison Date: Mon, 28 Sep 2015 17:24:15 +0930 Subject: [PATCH] Add fromhere=2 to Rhizome list output Need a way for the client to distinguish between authenticated (certain) and unauthenticated (likely) author SIDs in the context of a bundle list, since the bundle list does not verify manifest signatures for performance and battery life reasons. --- doc/Servald-REST-API.md | 60 ++++++++++++++++++++++++++++++----------- rhizome_bundle.c | 8 +++--- rhizome_cli.c | 16 ++++++++--- rhizome_restful.c | 18 ++++++++++--- 4 files changed, 76 insertions(+), 26 deletions(-) diff --git a/doc/Servald-REST-API.md b/doc/Servald-REST-API.md index 882bc505..103e51c8 100644 --- a/doc/Servald-REST-API.md +++ b/doc/Servald-REST-API.md @@ -551,11 +551,23 @@ convey optional fields that are present in the bundle's manifest: Serval-Rhizome-Bundle-Name: Serval-Rhizome-Bundle-Date: -If the bundle's author, as verified by its signature, is present in the keyring, -then the following HTTP header is present: +All single-bundle operations, unless otherwise specified, attempt to deduce the +bundle's author by finding whether the manifest's signature could be re-created +using a Rhizome Secret from a currently unlocked identity in the keyring. If +the manifest `sender` field is present or the author has been cached in the +Rhizome database, then only that identity is tried, otherwise every single +identity in the keyring is tested. If a signing identity is found, then the +following HTTP header is present: Serval-Rhizome-Bundle-Author: +(Note that there is no manifest “author” field, and the “sender” field is +optional, in order to support anonymous bundles. This is why the author must +be deduced in this fashion. Serval DNA caches the authors it discovers, to +avoid redundant re-testing of all keyring identities, but cached authors are +not automatically treated as verified when read from the Rhizome database, +because the database can be altered by external means.) + If the bundle's secret is known, either because it was supplied in the request or was deduced from the manifest's Bundle Key (BK) field and the author's Rhizome Secret (RS), then the following HTTP header is present: @@ -681,21 +693,37 @@ table](#json-table) format with the following columns: store. This field is created using the local system clock, so comparisons with the `date` field cannot be relied upon as having any meaning. -* `.author` - the [SID][] of the local (unlocked) identity that (allegedly) - created the bundle; either a string containing 64 hexadecimal digits, or - *null* if the author cannot be deduced (the manifest lacks a *BK* field) or - is not an [unlocked identity](#get-restful-keyring-identities-json). For - performance reasons bundle authorship is not verified when listing bundles, - because that would impose unreasonable CPU and battery load (regenerating - the cryptographic signature of every single manifest in the list), so the - [bundle fetch request](#get-restful-rhizome-bid.rhm) and similar - single-bundle requests which do perform a signature authorship check may - return a different `Serval-Rhizome-Bundle-Author` header; in particular if - the manifest was not signed by the alleged author then that header will be - absent. +* `.author` - the [SID][] of the local (unlocked) identity that created the + bundle; either a string containing 64 hexadecimal digits, or *null* if the + author cannot be deduced (the manifest lacks a *BK* field) or is not an + [unlocked identity](#get-restful-keyring-identities-json). In the case of + *null*, the `.fromhere` field will be 0 (“not authored here”). In the case + of a SID, the `.fromhere` indicates whether authorship was absent, likely or + certain. -* `.fromhere` - a boolean flag that is set if the `.author` field is - non-*null*; an integer either 1 or 0. +* `.fromhere` - an integer flag that indicates whether the bundle was authored + on the local device: + + * `0` (“absent”) means the bundle was not authored by any identity on this + device, which could be because either: + * the author's identity is not unlocked in the local keyring, or + * the author's identity is in the local keyring but does not verify + cryptographically as the author. + + * `1` (“likely”) means the author whose [SID][] is given in the `.author` + field is present in the local keyring but authorship (the manifest's + signature) has not been cryptographically verified, so attempting to + update this bundle may yet fail. This is the usual value for most + bundles in a list because cryptographic verification is not performed + while listing bundles, since it is slow and costly in CPU and battery. + + * `2` (“certain”) means the author whose [SID][] is given in the `.author` + field is present in the local keyring and has been cryptographically + verified as the true author of the bundle, so it is possible to update + this bundle. This value will usually only be returned for + locally-authored bundles that have recently been examined individually + (eg, [GET /restful/rhizome/BID.rhm](#get-restful-rhizome-bid-rhm)), if + Serval DNA has cached the result of the verification in memory. * `filesize` - the number of bytes in the bundle's payload; an integer zero or positive with a maximum value of 2^64 − 1. diff --git a/rhizome_bundle.c b/rhizome_bundle.c index 7484c612..811376cc 100644 --- a/rhizome_bundle.c +++ b/rhizome_bundle.c @@ -1587,8 +1587,10 @@ int rhizome_lookup_author(rhizome_manifest *m) { IN(); keyring_iterator it; - switch (m->authorship) { + case AUTHOR_LOCAL: + case AUTHOR_AUTHENTIC: + RETURN(1); case AUTHOR_NOT_CHECKED: DEBUGF(rhizome, "manifest[%d] lookup author=%s", m->manifest_record_number, alloca_tohex_sid_t(m->author)); keyring_iterator_start(keyring, &it); @@ -1609,13 +1611,11 @@ int rhizome_lookup_author(rhizome_manifest *m) RETURN(1); } } + // fall through case AUTHENTICATION_ERROR: case AUTHOR_UNKNOWN: case AUTHOR_IMPOSTOR: RETURN(0); - case AUTHOR_LOCAL: - case AUTHOR_AUTHENTIC: - RETURN(1); } FATALF("m->authorship = %d", m->authorship); RETURN(0); diff --git a/rhizome_cli.c b/rhizome_cli.c index 26020412..8ed52c20 100644 --- a/rhizome_cli.c +++ b/rhizome_cli.c @@ -751,17 +751,27 @@ static int app_rhizome_list(const struct cli_parsed *parsed, struct cli_context cli_put_long(context, m->version, ":"); cli_put_long(context, m->has_date ? m->date : 0, ":"); cli_put_long(context, m->inserttime, ":"); + // The 'fromhere' flag indicates if the author is a known (unlocked) identity in the local + // keyring. The values are 0 (no), 1 (yes), 2 (yes and cryptographically verified). In the + // implementation below, the 0 value (no) is redundant, because it only occurs when the + // 'author' column is null, but in future the author SID might be reported for non-local + // authors, so clients should only use 'fromhere != 0', never 'author != null', to detect + // local authorship. + int fromhere = 0; switch (m->authorship) { - case AUTHOR_LOCAL: case AUTHOR_AUTHENTIC: + fromhere = 2; + cli_put_hexvalue(context, m->author.binary, sizeof m->author.binary, ":"); + break; + case AUTHOR_LOCAL: + fromhere = 1; cli_put_hexvalue(context, m->author.binary, sizeof m->author.binary, ":"); - cli_put_long(context, 1, ":"); break; default: cli_put_string(context, NULL, ":"); - cli_put_long(context, 0, ":"); break; } + cli_put_long(context, fromhere, ":"); cli_put_long(context, m->filesize, ":"); cli_put_hexvalue(context, m->filesize ? m->filehash.binary : NULL, sizeof m->filehash.binary, ":"); cli_put_hexvalue(context, m->has_sender ? m->sender.binary : NULL, sizeof m->sender.binary, ":"); diff --git a/rhizome_restful.c b/rhizome_restful.c index d0069929..43264d07 100644 --- a/rhizome_restful.c +++ b/rhizome_restful.c @@ -299,17 +299,29 @@ static int restful_rhizome_bundlelist_json_content_chunk(struct http_request *hr strbuf_json_null(b); strbuf_putc(b, ','); strbuf_sprintf(b, "%"PRItime_ms_t",", m->inserttime); + // The 'fromhere' flag indicates if the author is a known (unlocked) identity in the local + // keyring. The values are 0 (no), 1 (yes), 2 (yes and cryptographically verified). In the + // implementation below, the 0 value (no) is redundant, because it only occurs when the + // 'author' column is null, but in future the author SID might be reported for non-local + // authors, so clients should only use 'fromhere != 0', never 'author != null', to detect + // local authorship. + int fromhere = 0; switch (m->authorship) { - case AUTHOR_LOCAL: case AUTHOR_AUTHENTIC: + fromhere = 2; + strbuf_json_hex(b, m->author.binary, sizeof m->author.binary); + break; + case AUTHOR_LOCAL: + fromhere = 1; strbuf_json_hex(b, m->author.binary, sizeof m->author.binary); - strbuf_puts(b, ",1,"); break; default: strbuf_json_null(b); - strbuf_puts(b, ",1,"); break; } + strbuf_putc(b, ','); + strbuf_sprintf(b, "%d", fromhere); + strbuf_putc(b, ','); strbuf_sprintf(b, "%"PRIu64, m->filesize); strbuf_putc(b, ','); strbuf_json_hex(b, m->filesize ? m->filehash.binary : NULL, sizeof m->filehash.binary);