Create authorless bundle from bundle secret

Until now, the bundle secret was never used to create a new bundle,
only to update a bundle whose secret was known from its creation.
This commit is contained in:
Andrew Bettison 2014-11-13 15:54:20 +10:30
parent d8f26a76ee
commit e743e4d7f9
5 changed files with 138 additions and 88 deletions

View File

@ -344,6 +344,8 @@ int rhizome_store_cleanup(struct rhizome_cleanup_report *report);
void rhizome_vacuum_db(sqlite_retry_state *retry);
int rhizome_manifest_createid(rhizome_manifest *m);
int rhizome_get_bundle_from_seed(rhizome_manifest *m, const char *seed);
int rhizome_get_bundle_from_secret(rhizome_manifest *m, const rhizome_bk_t *bsk);
int rhizome_new_bundle_from_secret(rhizome_manifest *m, const rhizome_bk_t *bsk);
struct rhizome_manifest_summary {
rhizome_bid_t bid;

View File

@ -112,7 +112,7 @@ static int app_rhizome_add_file(const struct cli_parsed *parsed, struct cli_cont
{
if (config.debug.verbose)
DEBUG_cli_parsed(parsed);
const char *filepath, *manifestpath, *manifestid, *authorSidHex, *bskhex;
const char *filepath, *manifestpath, *manifestid, *authorSidHex, *bsktext;
int force_new = 0 == cli_arg(parsed, "--force-new", NULL, NULL, NULL);
cli_arg(parsed, "filepath", &filepath, NULL, "");
@ -120,20 +120,19 @@ static int app_rhizome_add_file(const struct cli_parsed *parsed, struct cli_cont
return -1;
cli_arg(parsed, "manifestpath", &manifestpath, NULL, "");
cli_arg(parsed, "manifestid", &manifestid, NULL, "");
if (cli_arg(parsed, "bsk", &bskhex, cli_optional_bundle_secret_key, NULL) == -1)
if (cli_arg(parsed, "bsk", &bsktext, cli_optional_bundle_secret_key, NULL) == -1)
return -1;
sid_t authorSid;
if (authorSidHex[0] && str_to_sid_t(&authorSid, authorSidHex) == -1)
return WHYF("invalid author_sid: %s", authorSidHex);
rhizome_bk_t bsk;
// treat empty string the same as null
if (bskhex && !*bskhex)
bskhex=NULL;
if (bskhex && str_to_rhizome_bk_t(&bsk, bskhex) == -1)
return WHYF("invalid bsk: \"%s\"", bskhex);
if (bsktext && !*bsktext)
bsktext = NULL;
rhizome_bk_t bsk;
if (bsktext && str_to_rhizome_bsk_t(&bsk, bsktext) == -1)
return WHYF("invalid bsk: \"%s\"", bsktext);
int journal = strcasecmp(parsed->args[1], "journal")==0;
@ -143,19 +142,16 @@ static int app_rhizome_add_file(const struct cli_parsed *parsed, struct cli_cont
if (!(keyring = keyring_open_instance_cli(parsed)))
return -1;
if (rhizome_opendb() == -1){
keyring_free(keyring);
keyring = NULL;
return -1;
}
int ret = -1;
rhizome_manifest *m = NULL;
if (rhizome_opendb() == -1)
goto finish;
/* Create a new manifest that will represent the file. If a manifest file was supplied, then read
* it, otherwise create a blank manifest. */
rhizome_manifest *m = rhizome_new_manifest();
if (!m){
keyring_free(keyring);
keyring = NULL;
return WHY("Manifest struct could not be allocated -- not added to rhizome");
if ((m = rhizome_new_manifest()) == NULL){
ret = WHY("Manifest struct could not be allocated -- not added to rhizome");
goto finish;
}
if (manifestpath && *manifestpath && access(manifestpath, R_OK) == 0) {
if (config.debug.rhizome)
@ -165,26 +161,20 @@ static int app_rhizome_add_file(const struct cli_parsed *parsed, struct cli_cont
trying to write it out. However, we do insist that whatever we load is
parsed okay and not malformed. */
if (rhizome_read_manifest_from_file(m, manifestpath) || m->malformed) {
rhizome_manifest_free(m);
keyring_free(keyring);
keyring = NULL;
return WHY("Manifest file could not be loaded -- not added to rhizome");
ret = WHY("Manifest file could not be loaded -- not added to rhizome");
goto finish;
}
} else if (manifestid && *manifestid) {
if (config.debug.rhizome)
DEBUGF("Reading manifest from database");
rhizome_bid_t bid;
if (str_to_rhizome_bid_t(&bid, manifestid) == -1) {
rhizome_manifest_free(m);
keyring_free(keyring);
keyring = NULL;
return WHYF("Invalid bundle ID: %s", alloca_str_toprint(manifestid));
ret = WHYF("Invalid bundle ID: %s", alloca_str_toprint(manifestid));
goto finish;
}
if (rhizome_retrieve_manifest(&bid, m) != RHIZOME_BUNDLE_STATUS_SAME){
rhizome_manifest_free(m);
keyring_free(keyring);
keyring = NULL;
return WHY("Existing manifest could not be loaded -- not added to rhizome");
if (rhizome_retrieve_manifest(&bid, m) != RHIZOME_BUNDLE_STATUS_SAME) {
ret = WHY("Existing manifest could not be loaded -- not added to rhizome");
goto finish;
}
} else {
if (config.debug.rhizome)
@ -196,64 +186,80 @@ static int app_rhizome_add_file(const struct cli_parsed *parsed, struct cli_cont
}
if (journal && !m->is_journal){
rhizome_manifest_free(m);
keyring_free(keyring);
keyring = NULL;
return WHY("Existing manifest is not a journal");
ret = WHY("Existing manifest is not a journal");
goto finish;
}
if (!journal && m->is_journal) {
rhizome_manifest_free(m);
keyring_free(keyring);
keyring = NULL;
return WHY("Existing manifest is a journal");
ret = WHY("Existing manifest is a journal");
goto finish;
}
if (bskhex)
rhizome_apply_bundle_secret(m, &bsk);
if (bsktext) {
if (m->has_id) {
if (!rhizome_apply_bundle_secret(m, &bsk)) {
ret = WHY("Supplied bundle secret does not match Bundle Id");
goto finish;
}
} else {
if (rhizome_new_bundle_from_secret(m, &bsk) == -1) {
ret = WHY("Failed to create bundle from given secret");
goto finish;
}
}
}
if (m->service == NULL)
rhizome_manifest_set_service(m, RHIZOME_SERVICE_FILE);
if (rhizome_fill_manifest(m, filepath, *authorSidHex ? &authorSid : NULL)) {
rhizome_manifest_free(m);
keyring_free(keyring);
keyring = NULL;
return -1;
}
if (rhizome_fill_manifest(m, filepath, *authorSidHex ? &authorSid : NULL))
goto finish;
enum rhizome_bundle_status status = RHIZOME_BUNDLE_STATUS_NEW;
enum rhizome_payload_status pstatus;
if (journal){
pstatus = rhizome_append_journal_file(m, 0, filepath);
if (config.debug.rhizome)
DEBUGF("rhizome_append_journal_file() returned %d %s", pstatus, rhizome_payload_status_message(pstatus));
} else {
pstatus = rhizome_stat_payload_file(m, filepath);
if (config.debug.rhizome)
DEBUGF("rhizome_stat_payload_file() returned %d %s", pstatus, rhizome_payload_status_message(pstatus));
assert(m->filesize != RHIZOME_SIZE_UNSET);
if (pstatus == RHIZOME_PAYLOAD_STATUS_NEW) {
assert(m->filesize > 0);
pstatus = rhizome_store_payload_file(m, filepath);
if (config.debug.rhizome)
DEBUGF("rhizome_store_payload_file() returned %d %s", pstatus, rhizome_payload_status_message(pstatus));
}
}
enum rhizome_bundle_status status = RHIZOME_BUNDLE_STATUS_ERROR;
int pstatus_valid = 0;
switch (pstatus) {
case RHIZOME_PAYLOAD_STATUS_EMPTY:
case RHIZOME_PAYLOAD_STATUS_STORED:
case RHIZOME_PAYLOAD_STATUS_NEW:
pstatus_valid = 1;
status = RHIZOME_BUNDLE_STATUS_NEW;
break;
case RHIZOME_PAYLOAD_STATUS_TOO_BIG:
case RHIZOME_PAYLOAD_STATUS_EVICTED:
pstatus_valid = 1;
status = RHIZOME_BUNDLE_STATUS_NO_ROOM;
INFO("Insufficient space to store payload");
break;
case RHIZOME_PAYLOAD_STATUS_ERROR:
pstatus_valid = 1;
status = RHIZOME_BUNDLE_STATUS_ERROR;
break;
case RHIZOME_PAYLOAD_STATUS_WRONG_SIZE:
case RHIZOME_PAYLOAD_STATUS_WRONG_HASH:
pstatus_valid = 1;
status = RHIZOME_BUNDLE_STATUS_INCONSISTENT;
break;
case RHIZOME_PAYLOAD_STATUS_CRYPTO_FAIL:
pstatus_valid = 1;
status = RHIZOME_BUNDLE_STATUS_READONLY;
break;
default:
FATALF("pstatus = %d", pstatus);
}
if (!pstatus_valid)
FATALF("pstatus = %d", pstatus);
rhizome_manifest *mout = NULL;
if (status == RHIZOME_BUNDLE_STATUS_NEW) {
if (!rhizome_manifest_validate(m) || m->malformed)
@ -301,10 +307,12 @@ static int app_rhizome_add_file(const struct cli_parsed *parsed, struct cli_cont
FATALF("status=%d", status);
if (mout && mout != m)
rhizome_manifest_free(mout);
ret = status;
finish:
rhizome_manifest_free(m);
keyring_free(keyring);
keyring = NULL;
return status;
return ret;
}
DEFINE_CMD(app_rhizome_import_bundle, 0,
@ -486,11 +494,11 @@ static int app_rhizome_extract(const struct cli_parsed *parsed, struct cli_conte
{
if (config.debug.verbose)
DEBUG_cli_parsed(parsed);
const char *manifestpath, *filepath, *manifestid, *bskhex;
const char *manifestpath, *filepath, *manifestid, *bsktext;
if ( cli_arg(parsed, "manifestid", &manifestid, cli_manifestid, "") == -1
|| cli_arg(parsed, "manifestpath", &manifestpath, NULL, "") == -1
|| cli_arg(parsed, "filepath", &filepath, NULL, "") == -1
|| cli_arg(parsed, "bsk", &bskhex, cli_optional_bundle_secret_key, NULL) == -1)
|| cli_arg(parsed, "bsk", &bsktext, cli_optional_bundle_secret_key, NULL) == -1)
return -1;
int extract = strcasecmp(parsed->args[1], "extract")==0;
@ -504,31 +512,28 @@ static int app_rhizome_extract(const struct cli_parsed *parsed, struct cli_conte
if (!(keyring = keyring_open_instance_cli(parsed)))
return -1;
rhizome_manifest *m = NULL;
int ret=0;
rhizome_bid_t bid;
if (str_to_rhizome_bid_t(&bid, manifestid) == -1){
keyring_free(keyring);
keyring = NULL;
return WHY("Invalid manifest ID");
if (str_to_rhizome_bid_t(&bid, manifestid) == -1) {
ret = WHY("Invalid manifest ID");
goto finish;
}
// treat empty string the same as null
if (bskhex && !*bskhex)
bskhex=NULL;
if (bsktext && !*bsktext)
bsktext = NULL;
rhizome_bk_t bsk;
if (bskhex && str_to_rhizome_bk_t(&bsk, bskhex) == -1){
keyring_free(keyring);
keyring = NULL;
return WHYF("invalid bsk: \"%s\"", bskhex);
if (bsktext && str_to_rhizome_bsk_t(&bsk, bsktext) == -1) {
ret = WHYF("invalid bsk: \"%s\"", bsktext);
goto finish;
}
rhizome_manifest *m = rhizome_new_manifest();
if (m==NULL){
keyring_free(keyring);
keyring = NULL;
return WHY("Out of manifests");
if ((m = rhizome_new_manifest()) == NULL) {
ret = WHY("Out of manifests");
goto finish;
}
switch(rhizome_retrieve_manifest(&bid, m)){
@ -539,7 +544,7 @@ static int app_rhizome_extract(const struct cli_parsed *parsed, struct cli_conte
if (ret==0){
assert(m->finalised);
if (bskhex)
if (bsktext)
rhizome_apply_bundle_secret(m, &bsk);
rhizome_authenticate_author(m);
assert(m->authorship != AUTHOR_LOCAL);
@ -593,8 +598,8 @@ static int app_rhizome_extract(const struct cli_parsed *parsed, struct cli_conte
default:
FATALF("pstatus = %d", pstatus);
}
if (m)
rhizome_manifest_free(m);
finish:
rhizome_manifest_free(m);
keyring_free(keyring);
keyring = NULL;
return ret;

View File

@ -42,19 +42,15 @@ int rhizome_manifest_createid(rhizome_manifest *m)
return 0;
}
struct signing_key{
struct signing_key {
unsigned char Private[crypto_sign_edwards25519sha512batch_SECRETKEYBYTES];
rhizome_bid_t Public;
};
/* generate a keypair from a given seed string */
static int generate_keypair(const char *seed, struct signing_key *key)
/* generate a keypair from a given secret key */
static int generate_keypair_from_secret(const rhizome_bk_t *bsk, struct signing_key *key)
{
unsigned char hash[crypto_hash_sha512_BYTES];
crypto_hash_sha512(hash, (unsigned char *)seed, strlen(seed));
// The first 256 bits (32 bytes) of the hash will be used as the private key of the BID.
bcopy(hash, key->Private, sizeof key->Private);
bcopy(bsk->binary, key->Private, sizeof bsk->binary); // first 32 bytes
if (crypto_sign_compute_public_key(key->Private, key->Public.binary) == -1)
return WHY("Could not generate public key");
// The last 32 bytes of the private key should be identical to the public key. This is what
@ -68,15 +64,24 @@ static int generate_keypair(const char *seed, struct signing_key *key)
* Then either fetch it from the database or initialise a new empty manifest */
int rhizome_get_bundle_from_seed(rhizome_manifest *m, const char *seed)
{
struct signing_key key;
if (generate_keypair(seed, &key))
union {
unsigned char hash[crypto_hash_sha512_BYTES];
rhizome_bk_t bsk;
} u;
crypto_hash_sha512(u.hash, (unsigned char *)seed, strlen(seed));
// The first 256 bits (32 bytes) of the hash will be used as the private key of the BID.
return rhizome_get_bundle_from_secret(m, &u.bsk);
}
/* Generate a bundle id deterministically from the given bundle secret key.
* Then either fetch it from the database or initialise a new empty manifest
*/
int rhizome_get_bundle_from_secret(rhizome_manifest *m, const rhizome_bk_t *bsk)
{
if (rhizome_new_bundle_from_secret(m, bsk) == -1)
return -1;
switch(rhizome_retrieve_manifest(&key.Public, m)){
switch (rhizome_retrieve_manifest(&m->cryptoSignPublic, m)) {
case RHIZOME_BUNDLE_STATUS_NEW:
// manifest not retrieved
rhizome_manifest_set_id(m, &key.Public); // zerofills m->cryptoSignSecret
m->haveSecret = NEW_BUNDLE_ID;
break;
case RHIZOME_BUNDLE_STATUS_SAME:
m->haveSecret = EXISTING_BUNDLE_ID;
@ -84,7 +89,19 @@ int rhizome_get_bundle_from_seed(rhizome_manifest *m, const char *seed)
default:
return -1;
}
return 0;
}
/* Generate a bundle id deterministically from the given bundle secret key.
* Then initialise a new empty manifest.
*/
int rhizome_new_bundle_from_secret(rhizome_manifest *m, const rhizome_bk_t *bsk)
{
struct signing_key key;
if (generate_keypair_from_secret(bsk, &key))
return -1;
rhizome_manifest_set_id(m, &key.Public); // zerofills m->cryptoSignSecret
m->haveSecret = NEW_BUNDLE_ID;
bcopy(key.Private, m->cryptoSignSecret, sizeof m->cryptoSignSecret);
// Disabled for performance, these asserts should nevertheless always hold.
//assert(cmp_rhizome_bid_t(&m->cryptoSignPublic, &key.Public) == 0);

View File

@ -35,7 +35,6 @@ assert_manifest_complete() {
tfw_cat -v "$manifest"
assertGrep "$manifest" "^service=$rexp_service\$"
assertGrep "$manifest" "^id=$rexp_manifestid\$"
assertGrep "$manifest" "^BK=$rexp_bundlekey\$"
assertGrep "$manifest" "^date=$rexp_date\$"
assertGrep "$manifest" "^version=$rexp_version\$"
assertGrep "$manifest" "^filesize=$rexp_filesize\$"

View File

@ -782,6 +782,33 @@ test_AddServiceUnsupported() {
tfw_cat --stdout --stderr
}
doc_AddUpdateNoAuthorWithPassphrase="Add and update authorless bundle with only secret passphrase"
setup_AddUpdateNoAuthorWithPassphrase() {
setup_servald
setup_rhizome
create_file file1 1k
create_file file1_2 2k
pass='On the Ning Nang Nong'
}
test_AddUpdateNoAuthorWithPassphrase() {
executeOk_servald rhizome add file '' file1 file1.manifest "#$pass"
tfw_cat --stdout --stderr
assert_stdout_add_file file1 !.author '!BK'
assert_manifest_complete file1.manifest
extract_manifest_id BID file1.manifest
executeOk_servald rhizome list
assert_rhizome_list --fromhere=0 file1
executeOk_servald rhizome add file '' file1_2 file1_2.manifest "#$pass"
tfw_cat --stdout --stderr
assert_stdout_add_file file1_2 !.author '!BK'
assert_manifest_complete file1_2.manifest
extract_manifest_id BID2 file1.manifest
assert [ "$BID" = "$BID2" ]
executeOk_servald rhizome extract file $BID file1x
tfw_cat --stdout --stderr
assert diff file1_2 file1x
}
doc_EncryptedPayload="Add and extract an encrypted payload"
setup_EncryptedPayload() {
setup_servald