Issue , overhaul manifest author crypto logic

Add test case for new feature of the "rhizome add" command: if the author SID
is not specified (empty arg) then it searches the keyring for the author.

Removed "authorSid" argument from several functions that also take a struct
rhizome_manifest * arg, since the author, if known, is now supplied in the
struct.

Improve return value handling and refactored some rhizome crypto code.
This commit is contained in:
Andrew Bettison 2012-10-11 17:58:24 +10:30
parent f4d0b7a16a
commit 3f1f495d68
8 changed files with 255 additions and 152 deletions

View File

@ -1042,29 +1042,75 @@ int app_rhizome_add_file(int argc, const char *const *argv, struct command_line_
if (rhizome_manifest_get(m, "id", NULL, 0) == NULL) {
if (rhizome_manifest_bind_id(m) == -1) {
rhizome_manifest_free(m);
m = NULL;
return WHY("Could not bind manifest to an ID");
}
} else if (bskhex[0]) {
/* Modifying an existing bundle. If the caller provides the bundle secret key, then ensure that
it corresponds to the bundle's public key (its bundle ID), otherwise the caller cannot modify
the bundle. */
memcpy(m->cryptoSignSecret, bsk, RHIZOME_BUNDLE_KEY_BYTES);
if (rhizome_verify_bundle_privatekey(m) == -1) {
rhizome_manifest_free(m);
m = NULL;
return WHY("Incorrect BID secret key.");
} else {
// Modifying an existing bundle. If an author SID is supplied, we must ensure that it is valid,
// ie, that identity has permission to alter the bundle. If no author SID is supplied but a BSK
// is supplied, then use that to alter the bundle. Otherwise, search the keyring for an
// identity with permission to alter the bundle.
if (!is_sid_any(m->author)) {
// Check that the given author has permission to alter the bundle, and extract the secret
// bundle key if so.
int result = rhizome_extract_privatekey(m);
switch (result) {
case -1:
rhizome_manifest_free(m);
return WHY("error in rhizome_extract_privatekey()");
case 0:
break;
case 1:
if (bskhex[0])
break;
rhizome_manifest_free(m);
return WHY("Manifest does not have BK field");
case 2:
rhizome_manifest_free(m);
return WHY("Author unknown");
case 3:
rhizome_manifest_free(m);
return WHY("Author does not have a Rhizome Secret");
case 4:
rhizome_manifest_free(m);
return WHY("Author does not have permission to modify manifest");
default:
rhizome_manifest_free(m);
return WHYF("Unknown result from rhizome_extract_privatekey(): %d", result);
}
}
if (bskhex[0]) {
if (m->haveSecret) {
// If a bundle secret key was supplied that does not match the secret key derived from the
// author, then warn but carry on using the author's.
if (memcmp(bsk, m->cryptoSignSecret, RHIZOME_BUNDLE_KEY_BYTES) != 0)
WARNF("Supplied bundle secret key is invalid -- ignoring");
} else {
// The caller provided the bundle secret key, so ensure that it corresponds to the bundle's
// public key (its bundle ID), otherwise it won't work.
memcpy(m->cryptoSignSecret, bsk, RHIZOME_BUNDLE_KEY_BYTES);
if (rhizome_verify_bundle_privatekey(m) == -1) {
rhizome_manifest_free(m);
return WHY("Incorrect BID secret key.");
}
}
}
// If we still don't know the bundle secret or the author, then search for an author.
if (!m->haveSecret && is_sid_any(m->author)) {
int result = rhizome_find_bundle_author(m);
if (result != 0) {
rhizome_manifest_free(m);
switch (result) {
case -1:
return WHY("error in rhizome_find_bundle_author()");
case 4:
return WHY("Manifest does not have BK field");
case 1:
return WHY("No author found");
default:
return WHYF("Unknown result from rhizome_find_bundle_author(): %d", result);
}
}
}
} else if (!authorSidHex[0]) {
/* In order to modify an existing bundle, the author must be known. */
rhizome_manifest_free(m);
m = NULL;
return WHY("Author SID not specified");
} else if (rhizome_extract_privatekey(m, authorSid) == -1) {
/* Only the original author can modify an existing bundle. */
rhizome_manifest_free(m);
m = NULL;
return WHY("Could not extract BID secret key. Does the manifest have a BK?");
}
int encryptP = 0; // TODO Determine here whether payload is to be encrypted.
if (rhizome_manifest_bind_file(m, filepath, encryptP)) {

View File

@ -50,7 +50,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
get - 8 bit variable value
*/
#define SID_SIZE 32
#define SID_SIZE 32 // == crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES
#define SAS_SIZE 32 // == crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES
#define DID_MINSIZE 5
#define DID_MAXSIZE 32

View File

@ -157,9 +157,7 @@ int rhizome_manifest_bind_id(rhizome_manifest *m_in)
manifests on receiver nodes works easily. We might implement something that strips the id
variable out of the manifest when sending it, or some other scheme to avoid sending all the
extra bytes. */
char id[RHIZOME_MANIFEST_ID_STRLEN + 1];
rhizome_bytes_to_hex_upper(m_in->cryptoSignPublic, id, RHIZOME_MANIFEST_ID_BYTES);
rhizome_manifest_set(m_in, "id", id);
rhizome_manifest_set(m_in, "id", alloca_tohex_bid(m_in->cryptoSignPublic));
if (!is_sid_any(m_in->author)) {
/* Set the BK using the provided authorship information.
Serval Security Framework defines BK as being:

View File

@ -234,7 +234,7 @@ int rhizome_add_manifest(rhizome_manifest *m_in,int ttl);
void rhizome_bytes_to_hex_upper(unsigned const char *in, char *out, int byteCount);
int rhizome_find_privatekey(rhizome_manifest *m);
rhizome_signature *rhizome_sign_hash(rhizome_manifest *m, const unsigned char *authorSid);
int rhizome_sign_hash(rhizome_manifest *m, rhizome_signature *out);
__RHIZOME_INLINE int sqlite_code_ok(int code)
{
@ -299,9 +299,9 @@ int rhizome_bk_xor(const unsigned char *authorSid, // binary
unsigned char bkin[crypto_sign_edwards25519sha512batch_SECRETKEYBYTES],
unsigned char bkout[crypto_sign_edwards25519sha512batch_SECRETKEYBYTES]);
unsigned char *rhizome_bundle_shared_secret(rhizome_manifest *m);
int rhizome_extract_privatekey(rhizome_manifest *m, const unsigned char *authorSid);
int rhizome_extract_privatekey(rhizome_manifest *m);
int rhizome_verify_bundle_privatekey(rhizome_manifest *m);
int rhizome_is_self_signed(rhizome_manifest *m);
int rhizome_find_bundle_author(rhizome_manifest *m);
int rhizome_queue_ignore_manifest(rhizome_manifest *m,
struct sockaddr_in *peerip,int timeout);
int rhizome_ignore_manifest_check(rhizome_manifest *m,

View File

@ -570,22 +570,17 @@ int rhizome_manifest_pack_variables(rhizome_manifest *m)
*/
int rhizome_manifest_selfsign(rhizome_manifest *m)
{
if (!m->haveSecret) return WHY("Need private key to sign manifest");
rhizome_signature *sig = rhizome_sign_hash(m, m->cryptoSignSecret);
if (!sig) return WHY("rhizome_sign_hash() failed.");
if (!m->haveSecret)
return WHY("Need private key to sign manifest");
rhizome_signature sig;
if (rhizome_sign_hash(m, &sig) == -1)
return WHY("rhizome_sign_hash() failed");
/* Append signature to end of manifest data */
if (sig->signatureLength+m->manifest_bytes>MAX_MANIFEST_BYTES) {
free(sig);
return WHY("Manifest plus signatures is too long.");
}
bcopy(&sig->signature[0],&m->manifestdata[m->manifest_bytes],sig->signatureLength);
m->manifest_bytes+=sig->signatureLength;
m->manifest_all_bytes=m->manifest_bytes;
free(sig);
if (sig.signatureLength + m->manifest_bytes > MAX_MANIFEST_BYTES)
return WHY("Manifest plus signatures is too long");
bcopy(&sig.signature[0], &m->manifestdata[m->manifest_bytes], sig.signatureLength);
m->manifest_bytes += sig.signatureLength;
m->manifest_all_bytes = m->manifest_bytes;
return 0;
}

View File

@ -38,123 +38,187 @@ int rhizome_manifest_createid(rhizome_manifest *m)
return WHY("Failed to create keypair for manifest ID.");
}
/*
Return -1 if an error occurs.
Return 0 if the author's private key is located and the XOR is performed successfully.
Return 1 if the author's identity is not in the keyring.
Return 2 if the author's identity is in the keyring but has no rhizome secret.
*/
/* Given a Rhizome Secret (RS) and bundle ID (BID), XOR a bundle key 'bkin' (private or public) with
* RS##BID.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
static void rhizome_bk_xor_rs(
const unsigned char *rs,
size_t rs_len,
unsigned char bid[crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES],
unsigned char bkin[crypto_sign_edwards25519sha512batch_SECRETKEYBYTES],
unsigned char bkout[crypto_sign_edwards25519sha512batch_SECRETKEYBYTES]
)
{
IN();
int combined_len = rs_len + crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES;
unsigned char buffer[combined_len];
bcopy(&rs[0], &buffer[0], rs_len);
bcopy(&bid[0], &buffer[rs_len], crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES);
unsigned char hash[crypto_hash_sha512_BYTES];
crypto_hash_sha512(hash,buffer,combined_len);
int i;
for(i = 0; i != crypto_sign_edwards25519sha512batch_SECRETKEYBYTES; ++i)
bkout[i] = bkin[i] ^ hash[i];
bzero(buffer, combined_len);
bzero(hash, sizeof hash);
OUT();
}
/* Given the SID of a bundle's author and the bundle ID, XOR a bundle key (private or public) with
* RS##BID where RS is the rhizome secret of the bundle's author, and BID is the bundle's public key
* (aka the Bundle ID).
*
* This will convert a manifest BK field into the bundle's private key, or vice versa.
*
* Returns -1 if an error occurs.
* Returns 0 if the author's private key is located and the XOR is performed successfully.
* Returns 2 if the author's identity is not in the keyring.
* Returns 3 if the author's identity is in the keyring but has no rhizome secret.
*
* Looks up the SID in the keyring, and if it is present and has a valid-looking RS, calls
* rhizome_bk_xor_rs() to perform the XOR.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int rhizome_bk_xor(const unsigned char *authorSid, // binary
unsigned char bid[crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES],
unsigned char bkin[crypto_sign_edwards25519sha512batch_SECRETKEYBYTES],
unsigned char bkout[crypto_sign_edwards25519sha512batch_SECRETKEYBYTES])
{
IN();
if (crypto_sign_edwards25519sha512batch_SECRETKEYBYTES > crypto_hash_sha512_BYTES)
{ RETURN(WHY("BK needs to be longer than it can be")); }
return WHY("BK needs to be longer than it can be");
int cn=0,in=0,kp=0;
if (!keyring_find_sid(keyring,&cn,&in,&kp,authorSid)) {
if (debug & DEBUG_RHIZOME) DEBUG("identity not in keyring");
{ RETURN(1); }
if (debug & DEBUG_RHIZOME)
DEBUGF("identity sid=%s is not in keyring", alloca_tohex_sid(authorSid));
return 2;
}
kp = keyring_identity_find_keytype(keyring, cn, in, KEYTYPE_RHIZOME);
if (kp == -1) {
if (debug & DEBUG_RHIZOME) DEBUG("identity has no Rhizome Secret");
RETURN(2);
if (debug & DEBUG_RHIZOME)
DEBUGF("identity sid=%s has no Rhizome Secret", alloca_tohex_sid(authorSid));
return 3;
}
int rs_len=keyring->contexts[cn]->identities[in]->keypairs[kp]->private_key_len;
if (rs_len<16||rs_len>1024)
{ RETURN(WHYF("invalid Rhizome Secret: length=%d", rs_len)); }
unsigned char *rs=keyring->contexts[cn]->identities[in]->keypairs[kp]->private_key;
int combined_len=rs_len+crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES;
unsigned char buffer[combined_len];
bcopy(&rs[0],&buffer[0],rs_len);
bcopy(&bid[0],&buffer[rs_len],crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES);
unsigned char hash[crypto_hash_sha512_BYTES];
crypto_hash_sha512(hash,buffer,combined_len);
int i;
for(i = 0; i != crypto_sign_edwards25519sha512batch_SECRETKEYBYTES; ++i)
bkout[i]=bkin[i]^hash[i];
bzero(&buffer[0],combined_len);
bzero(&hash[0],crypto_hash_sha512_BYTES);
RETURN(0);
int rs_len = keyring->contexts[cn]->identities[in]->keypairs[kp]->private_key_len;
if (rs_len < 16 || rs_len > 1024)
return WHYF("identity sid=%s has invalid Rhizome Secret: length=%d", alloca_tohex_sid(authorSid), rs_len);
const unsigned char *rs = keyring->contexts[cn]->identities[in]->keypairs[kp]->private_key;
if (debug & DEBUG_RHIZOME)
DEBUGF("using identity sid=%s", alloca_tohex_sid(authorSid));
rhizome_bk_xor_rs(rs, rs_len, bid, bkin, bkout);
return 0;
}
/* See if the manifest has a BK entry, and if so, use it to obtain the
private key for the BID. Decoding BK's relies on the provision of
the appropriate SID.
Return 0 if the private key was extracted, 1 if not. Return -1 if an error occurs.
XXX Note that this function is not able to verify that the private key
is correct, as there is no exposed API in NaCl for calculating the
public key from a cryptosign private key. We thus have to trust that
the supplied SID is correct.
*/
int rhizome_extract_privatekey(rhizome_manifest *m, const unsigned char *authorSid)
/* See if the manifest has a BK entry, and if so, use it to obtain the private key for the BID. The
* manifest's 'author' field must contain the (binary) SID of the purported author of the bundle,
* which is used to look up the author's rhizome secret in the keyring.
*
* Returns 0 if a valid private key was extracted, with the private key in the manifest
* 'cryptoSignSecret' field and the 'haveSecret' field set to 1.
*
* Returns 1 if the manifest does not have a BK field.
*
* Returns 2 if the author is not found in the keyring (not unlocked?) -- this return code from
* rhizome_bk_xor().
*
* Returns 3 if the author is found in the keyring but has no rhizome secret -- this return code
* from rhizome_bk_xor().
*
* Returns 4 if the author is found in the keyring and has a rhizome secret but the private bundle
* key formed using it does not verify.
*
* Returns -1 on error.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int rhizome_extract_privatekey(rhizome_manifest *m)
{
IN();
char *bk = rhizome_manifest_get(m, "BK", NULL, 0);
if (!bk) { RETURN(WHY("missing BK field")); }
if (!bk)
RETURN(1);
unsigned char bkBytes[RHIZOME_BUNDLE_KEY_BYTES];
if (fromhexstr(bkBytes, bk, RHIZOME_BUNDLE_KEY_BYTES) == -1)
{ RETURN(WHYF("invalid BK field: %s", bk)); }
switch (rhizome_bk_xor(authorSid, m->cryptoSignPublic, bkBytes, m->cryptoSignSecret)) {
case -1:
RETURN(WHY("rhizome_bk_xor() failed"));
case 0:
RETURN(rhizome_verify_bundle_privatekey(m));
RETURN(WHYF("invalid BK field: %s", bk));
int result = rhizome_bk_xor(m->author, m->cryptoSignPublic, bkBytes, m->cryptoSignSecret);
if (result == 0) {
int verified = rhizome_verify_bundle_privatekey(m);
if (verified == 0)
RETURN(0); // bingo
if (verified == -1)
result = WHY("rhizome_bk_xor() failed");
else {
if (debug & DEBUG_RHIZOME)
DEBUGF("identity sid=%s is not the author of bundle with BK=%s", alloca_tohex_sid(m->author), bk);
result = 3;
}
}
RETURN(WHYF("Rhizome secret for %s not found. (Have you unlocked the identity?)", alloca_tohex_sid(authorSid)));
memset(m->cryptoSignSecret, 0, sizeof m->cryptoSignSecret);
RETURN(result);
}
/*
Test to see if the given manifest was created (signed) by any unlocked identity currently in the
keyring.
- Returns -1 if an error occurs, eg, the manifest contains an invalid BK field.
- Return 0 if the manifest's BK field was produced by any currently unlocked SID.
- Returns 1 if the manifest has no BK field.
- Returns 2 otherwise.
Currently unused; was called from rhizome_list_manifests() to compute the now-defunct
".selfsigned" column, but that made the Rhizome List view too slow in the Serval Mesh app.
See issue servalproject/serval-dna#17.
@author Andrew Bettison <andrew@servalproject.com>
/* Discover if the given manifest was created (signed) by any unlocked identity currently in the
* keyring.
*
* Returns 0 if an identity is found with permission to alter the bundle, after setting the manifest
* 'author' field to the SID of the identity and the manifest 'cryptoSignSecret' field to the bundle
* secret key and the 'haveSecret' field to 1.
*
* Returns 1 if no identity in the keyring is the author of this bundle.
*
* Returns 4 if the manifest has no BK field.
*
* Returns -1 if an error occurs, eg, the manifest contains an invalid BK field.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int rhizome_is_self_signed(rhizome_manifest *m)
int rhizome_find_bundle_author(rhizome_manifest *m)
{
IN();
char *bk = rhizome_manifest_get(m, "BK", NULL, 0);
if (!bk) {
if (debug & DEBUG_RHIZOME) DEBUGF("missing BK field");
RETURN(1);
if (debug & DEBUG_RHIZOME)
DEBUGF("missing BK field");
RETURN(4);
}
unsigned char bkBytes[RHIZOME_BUNDLE_KEY_BYTES];
if (fromhexstr(bkBytes, bk, RHIZOME_BUNDLE_KEY_BYTES) == -1)
{ RETURN(WHYF("invalid BK field: %s", bk)); }
RETURN(WHYF("invalid BK field: %s", bk));
int cn = 0, in = 0, kp = 0;
for (; keyring_next_identity(keyring, &cn, &in, &kp); ++kp) {
const unsigned char *authorSid = keyring->contexts[cn]->identities[in]->keypairs[kp]->public_key;
//if (debug & DEBUG_RHIZOME) DEBUGF("identity %s", alloca_tohex(authorSid, SID_SIZE));
//if (debug & DEBUG_RHIZOME) DEBUGF("try author identity sid=%s", alloca_tohex(authorSid, SID_SIZE));
int rkp = keyring_identity_find_keytype(keyring, cn, in, KEYTYPE_RHIZOME);
if (rkp != -1) {
switch (rhizome_bk_xor(authorSid, m->cryptoSignPublic, bkBytes, m->cryptoSignSecret)) {
case -1:
RETURN(WHY("rhizome_bk_xor() failed"));
case 0:
if (rhizome_verify_bundle_privatekey(m) == 0)
RETURN(0); // bingo
break;
int rs_len = keyring->contexts[cn]->identities[in]->keypairs[rkp]->private_key_len;
if (rs_len < 16 || rs_len > 1024)
RETURN(WHYF("invalid Rhizome Secret: length=%d", rs_len));
const unsigned char *rs = keyring->contexts[cn]->identities[in]->keypairs[rkp]->private_key;
rhizome_bk_xor_rs(rs, rs_len, m->cryptoSignPublic, bkBytes, m->cryptoSignSecret);
int verified = rhizome_verify_bundle_privatekey(m);
if (verified == 0) {
memcpy(m->author, authorSid, sizeof m->author);
if (debug & DEBUG_RHIZOME)
DEBUGF("found bundle author sid=%s", alloca_tohex_sid(m->author));
RETURN(0); // bingo
}
memset(m->cryptoSignSecret, 0, sizeof m->cryptoSignSecret);
if (verified == -1)
RETURN(WHY("rhizome_verify_bundle_privatekey() failed"));
}
}
RETURN(2); // not self signed
if (debug & DEBUG_RHIZOME)
DEBUG("bundle author not found");
RETURN(1);
}
/* Verify the validity of the manifest's sccret key.
Return 0 if valid, 1 if not. Return -1 if an error occurs.
XXX This is a pretty ugly way to do it, but NaCl offers no API to
do this cleanly.
/* Verify the validity of the manifest's secret key, ie, is the given manifest's 'cryptoSignSecret'
* field actually the secret key corresponding to the public key in 'cryptoSignPublic'?
* Return 0 if valid, 1 if not. Return -1 if an error occurs.
*
* XXX This is a pretty ugly way to do it, but NaCl offers no API to do this cleanly.
*/
int rhizome_verify_bundle_privatekey(rhizome_manifest *m)
{
@ -194,45 +258,32 @@ int rhizome_verify_bundle_privatekey(rhizome_manifest *m)
#endif //!ge25519
}
rhizome_signature *rhizome_sign_hash(rhizome_manifest *m, const unsigned char *authorSid)
int rhizome_sign_hash(rhizome_manifest *m, rhizome_signature *out)
{
IN();
unsigned char *hash=m->manifesthash;
unsigned char *publicKeyBytes=m->cryptoSignPublic;
if (!m->haveSecret && rhizome_extract_privatekey(m, authorSid)) {
WHY("Cannot find secret key to sign manifest data.");
RETURN(NULL);
}
unsigned char *hash = m->manifesthash;
unsigned char *publicKeyBytes = m->cryptoSignPublic;
if (!m->haveSecret && rhizome_extract_privatekey(m))
RETURN(WHY("Cannot find secret key to sign manifest data."));
/* Signature is formed by running crypto_sign_edwards25519sha512batch() on the
hash of the manifest. The signature actually contains the hash, so to save
space we cut the hash out of the signature. */
unsigned char signatureBuffer[crypto_sign_edwards25519sha512batch_BYTES+crypto_hash_sha512_BYTES];
unsigned long long sigLen=0;
int mLen=crypto_hash_sha512_BYTES;
int r=crypto_sign_edwards25519sha512batch(signatureBuffer,&sigLen,
&hash[0],mLen,m->cryptoSignSecret);
if (r) {
WHY("crypto_sign() failed.");
RETURN(NULL);
}
rhizome_signature *out=calloc(sizeof(rhizome_signature),1);
unsigned char signatureBuffer[crypto_sign_edwards25519sha512batch_BYTES + crypto_hash_sha512_BYTES];
unsigned long long sigLen = 0;
int mLen = crypto_hash_sha512_BYTES;
int r = crypto_sign_edwards25519sha512batch(signatureBuffer, &sigLen, &hash[0], mLen, m->cryptoSignSecret);
if (r)
RETURN(WHY("crypto_sign_edwards25519sha512batch() failed."));
/* Here we use knowledge of the internal structure of the signature block
to remove the hash, since that is implicitly transported, thus reducing the
actual signature size down to 64 bytes.
We do then need to add the public key of the signatory on. */
bcopy(&signatureBuffer[0],&out->signature[1],32);
bcopy(&signatureBuffer[96],&out->signature[33],32);
bcopy(&publicKeyBytes[0],&out->signature[65],crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES);
out->signatureLength=65+crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES;
out->signature[0]=out->signatureLength;
RETURN(out);
bcopy(&signatureBuffer[0], &out->signature[1], 32);
bcopy(&signatureBuffer[96], &out->signature[33], 32);
bcopy(&publicKeyBytes[0], &out->signature[65], crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES);
out->signatureLength = 65 + crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES;
out->signature[0] = out->signatureLength;
RETURN(0);
}
typedef struct manifest_signature_block_cache {

View File

@ -914,9 +914,9 @@ void dump_stack();
_this_call.totals=&_aggregate_stats; \
fd_func_enter(&_this_call);
#define OUT() fd_func_exit(&_this_call);
#define RETURN(X) { OUT() return(X); }
#define RETURNNULL { OUT() return(NULL); }
#define OUT() fd_func_exit(&_this_call)
#define RETURN(X) do { OUT(); return (X); } while (0);
#define RETURNNULL do { OUT(); return (NULL); } while (0);

View File

@ -366,6 +366,19 @@ test_AddUpdateNewVersion() {
assert_rhizome_list file1_2@$SIDB1 file2@$SIDB1
}
doc_AddUpdateDiscoverAuthor="Add new payload to manifest with author discovery"
setup_AddUpdateDiscoverAuthor() {
setup_AddUpdateNewVersion
}
test_AddUpdateDiscoverAuthor() {
tfw_cat -v file1_2.manifest
executeOk_servald rhizome add file '' '' file1_2 file1_2.manifest "$file1_secret"
tfw_cat --stderr
# Rhizome store contents have new payload.
executeOk_servald rhizome list ''
assert_rhizome_list file1_2@$SIDB1 file2@$SIDB1
}
doc_AddUpdateNoAuthor="Cannot add new payload to authorless manifest"
setup_AddUpdateNoAuthor() {
setup_AddUpdateNewVersion