mirror of
https://github.com/servalproject/serval-dna.git
synced 2024-12-24 23:36:46 +00:00
Issue #17, 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:
parent
f4d0b7a16a
commit
3f1f495d68
@ -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_get(m, "id", NULL, 0) == NULL) {
|
||||||
if (rhizome_manifest_bind_id(m) == -1) {
|
if (rhizome_manifest_bind_id(m) == -1) {
|
||||||
rhizome_manifest_free(m);
|
rhizome_manifest_free(m);
|
||||||
m = NULL;
|
|
||||||
return WHY("Could not bind manifest to an ID");
|
return WHY("Could not bind manifest to an ID");
|
||||||
}
|
}
|
||||||
} else if (bskhex[0]) {
|
} else {
|
||||||
/* Modifying an existing bundle. If the caller provides the bundle secret key, then ensure that
|
// Modifying an existing bundle. If an author SID is supplied, we must ensure that it is valid,
|
||||||
it corresponds to the bundle's public key (its bundle ID), otherwise the caller cannot modify
|
// ie, that identity has permission to alter the bundle. If no author SID is supplied but a BSK
|
||||||
the bundle. */
|
// 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);
|
memcpy(m->cryptoSignSecret, bsk, RHIZOME_BUNDLE_KEY_BYTES);
|
||||||
if (rhizome_verify_bundle_privatekey(m) == -1) {
|
if (rhizome_verify_bundle_privatekey(m) == -1) {
|
||||||
rhizome_manifest_free(m);
|
rhizome_manifest_free(m);
|
||||||
m = NULL;
|
|
||||||
return WHY("Incorrect BID secret key.");
|
return WHY("Incorrect BID secret key.");
|
||||||
}
|
}
|
||||||
} else if (!authorSidHex[0]) {
|
}
|
||||||
/* In order to modify an existing bundle, the author must be known. */
|
}
|
||||||
|
// 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);
|
rhizome_manifest_free(m);
|
||||||
m = NULL;
|
switch (result) {
|
||||||
return WHY("Author SID not specified");
|
case -1:
|
||||||
} else if (rhizome_extract_privatekey(m, authorSid) == -1) {
|
return WHY("error in rhizome_find_bundle_author()");
|
||||||
/* Only the original author can modify an existing bundle. */
|
case 4:
|
||||||
rhizome_manifest_free(m);
|
return WHY("Manifest does not have BK field");
|
||||||
m = NULL;
|
case 1:
|
||||||
return WHY("Could not extract BID secret key. Does the manifest have a BK?");
|
return WHY("No author found");
|
||||||
|
default:
|
||||||
|
return WHYF("Unknown result from rhizome_find_bundle_author(): %d", result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
int encryptP = 0; // TODO Determine here whether payload is to be encrypted.
|
int encryptP = 0; // TODO Determine here whether payload is to be encrypted.
|
||||||
if (rhizome_manifest_bind_file(m, filepath, encryptP)) {
|
if (rhizome_manifest_bind_file(m, filepath, encryptP)) {
|
||||||
|
@ -50,7 +50,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||||||
get - 8 bit variable value
|
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 SAS_SIZE 32 // == crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES
|
||||||
#define DID_MINSIZE 5
|
#define DID_MINSIZE 5
|
||||||
#define DID_MAXSIZE 32
|
#define DID_MAXSIZE 32
|
||||||
|
@ -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
|
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
|
variable out of the manifest when sending it, or some other scheme to avoid sending all the
|
||||||
extra bytes. */
|
extra bytes. */
|
||||||
char id[RHIZOME_MANIFEST_ID_STRLEN + 1];
|
rhizome_manifest_set(m_in, "id", alloca_tohex_bid(m_in->cryptoSignPublic));
|
||||||
rhizome_bytes_to_hex_upper(m_in->cryptoSignPublic, id, RHIZOME_MANIFEST_ID_BYTES);
|
|
||||||
rhizome_manifest_set(m_in, "id", id);
|
|
||||||
if (!is_sid_any(m_in->author)) {
|
if (!is_sid_any(m_in->author)) {
|
||||||
/* Set the BK using the provided authorship information.
|
/* Set the BK using the provided authorship information.
|
||||||
Serval Security Framework defines BK as being:
|
Serval Security Framework defines BK as being:
|
||||||
|
@ -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);
|
void rhizome_bytes_to_hex_upper(unsigned const char *in, char *out, int byteCount);
|
||||||
int rhizome_find_privatekey(rhizome_manifest *m);
|
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)
|
__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 bkin[crypto_sign_edwards25519sha512batch_SECRETKEYBYTES],
|
||||||
unsigned char bkout[crypto_sign_edwards25519sha512batch_SECRETKEYBYTES]);
|
unsigned char bkout[crypto_sign_edwards25519sha512batch_SECRETKEYBYTES]);
|
||||||
unsigned char *rhizome_bundle_shared_secret(rhizome_manifest *m);
|
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_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,
|
int rhizome_queue_ignore_manifest(rhizome_manifest *m,
|
||||||
struct sockaddr_in *peerip,int timeout);
|
struct sockaddr_in *peerip,int timeout);
|
||||||
int rhizome_ignore_manifest_check(rhizome_manifest *m,
|
int rhizome_ignore_manifest_check(rhizome_manifest *m,
|
||||||
|
@ -570,22 +570,17 @@ int rhizome_manifest_pack_variables(rhizome_manifest *m)
|
|||||||
*/
|
*/
|
||||||
int rhizome_manifest_selfsign(rhizome_manifest *m)
|
int rhizome_manifest_selfsign(rhizome_manifest *m)
|
||||||
{
|
{
|
||||||
if (!m->haveSecret) return WHY("Need private key to sign manifest");
|
if (!m->haveSecret)
|
||||||
rhizome_signature *sig = rhizome_sign_hash(m, m->cryptoSignSecret);
|
return WHY("Need private key to sign manifest");
|
||||||
if (!sig) return WHY("rhizome_sign_hash() failed.");
|
rhizome_signature sig;
|
||||||
|
if (rhizome_sign_hash(m, &sig) == -1)
|
||||||
|
return WHY("rhizome_sign_hash() failed");
|
||||||
/* Append signature to end of manifest data */
|
/* Append signature to end of manifest data */
|
||||||
if (sig->signatureLength+m->manifest_bytes>MAX_MANIFEST_BYTES) {
|
if (sig.signatureLength + m->manifest_bytes > MAX_MANIFEST_BYTES)
|
||||||
free(sig);
|
return WHY("Manifest plus signatures is too long");
|
||||||
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;
|
||||||
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);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
265
rhizome_crypto.c
265
rhizome_crypto.c
@ -38,123 +38,187 @@ int rhizome_manifest_createid(rhizome_manifest *m)
|
|||||||
return WHY("Failed to create keypair for manifest ID.");
|
return WHY("Failed to create keypair for manifest ID.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* Given a Rhizome Secret (RS) and bundle ID (BID), XOR a bundle key 'bkin' (private or public) with
|
||||||
Return -1 if an error occurs.
|
* RS##BID.
|
||||||
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.
|
* @author Andrew Bettison <andrew@servalproject.com>
|
||||||
Return 2 if the author's identity is in the keyring but has no rhizome secret.
|
*/
|
||||||
*/
|
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
|
int rhizome_bk_xor(const unsigned char *authorSid, // binary
|
||||||
unsigned char bid[crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES],
|
unsigned char bid[crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES],
|
||||||
unsigned char bkin[crypto_sign_edwards25519sha512batch_SECRETKEYBYTES],
|
unsigned char bkin[crypto_sign_edwards25519sha512batch_SECRETKEYBYTES],
|
||||||
unsigned char bkout[crypto_sign_edwards25519sha512batch_SECRETKEYBYTES])
|
unsigned char bkout[crypto_sign_edwards25519sha512batch_SECRETKEYBYTES])
|
||||||
{
|
{
|
||||||
IN();
|
|
||||||
if (crypto_sign_edwards25519sha512batch_SECRETKEYBYTES > crypto_hash_sha512_BYTES)
|
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;
|
int cn=0,in=0,kp=0;
|
||||||
if (!keyring_find_sid(keyring,&cn,&in,&kp,authorSid)) {
|
if (!keyring_find_sid(keyring,&cn,&in,&kp,authorSid)) {
|
||||||
if (debug & DEBUG_RHIZOME) DEBUG("identity not in keyring");
|
if (debug & DEBUG_RHIZOME)
|
||||||
{ RETURN(1); }
|
DEBUGF("identity sid=%s is not in keyring", alloca_tohex_sid(authorSid));
|
||||||
|
return 2;
|
||||||
}
|
}
|
||||||
kp = keyring_identity_find_keytype(keyring, cn, in, KEYTYPE_RHIZOME);
|
kp = keyring_identity_find_keytype(keyring, cn, in, KEYTYPE_RHIZOME);
|
||||||
if (kp == -1) {
|
if (kp == -1) {
|
||||||
if (debug & DEBUG_RHIZOME) DEBUG("identity has no Rhizome Secret");
|
if (debug & DEBUG_RHIZOME)
|
||||||
RETURN(2);
|
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;
|
int rs_len = keyring->contexts[cn]->identities[in]->keypairs[kp]->private_key_len;
|
||||||
if (rs_len<16||rs_len>1024)
|
if (rs_len < 16 || rs_len > 1024)
|
||||||
{ RETURN(WHYF("invalid Rhizome Secret: length=%d", rs_len)); }
|
return WHYF("identity sid=%s has invalid Rhizome Secret: length=%d", alloca_tohex_sid(authorSid), rs_len);
|
||||||
unsigned char *rs=keyring->contexts[cn]->identities[in]->keypairs[kp]->private_key;
|
const unsigned char *rs = keyring->contexts[cn]->identities[in]->keypairs[kp]->private_key;
|
||||||
int combined_len=rs_len+crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES;
|
if (debug & DEBUG_RHIZOME)
|
||||||
unsigned char buffer[combined_len];
|
DEBUGF("using identity sid=%s", alloca_tohex_sid(authorSid));
|
||||||
bcopy(&rs[0],&buffer[0],rs_len);
|
rhizome_bk_xor_rs(rs, rs_len, bid, bkin, bkout);
|
||||||
bcopy(&bid[0],&buffer[rs_len],crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES);
|
return 0;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* See if the manifest has a BK entry, and if so, use it to obtain the
|
/* See if the manifest has a BK entry, and if so, use it to obtain the private key for the BID. The
|
||||||
private key for the BID. Decoding BK's relies on the provision of
|
* manifest's 'author' field must contain the (binary) SID of the purported author of the bundle,
|
||||||
the appropriate SID.
|
* which is used to look up the author's rhizome secret in the keyring.
|
||||||
|
*
|
||||||
Return 0 if the private key was extracted, 1 if not. Return -1 if an error occurs.
|
* 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.
|
||||||
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
|
* Returns 1 if the manifest does not have a BK field.
|
||||||
public key from a cryptosign private key. We thus have to trust that
|
*
|
||||||
the supplied SID is correct.
|
* Returns 2 if the author is not found in the keyring (not unlocked?) -- this return code from
|
||||||
|
* rhizome_bk_xor().
|
||||||
*/
|
*
|
||||||
int rhizome_extract_privatekey(rhizome_manifest *m, const unsigned char *authorSid)
|
* 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();
|
IN();
|
||||||
char *bk = rhizome_manifest_get(m, "BK", NULL, 0);
|
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];
|
unsigned char bkBytes[RHIZOME_BUNDLE_KEY_BYTES];
|
||||||
if (fromhexstr(bkBytes, bk, RHIZOME_BUNDLE_KEY_BYTES) == -1)
|
if (fromhexstr(bkBytes, bk, RHIZOME_BUNDLE_KEY_BYTES) == -1)
|
||||||
{ RETURN(WHYF("invalid BK field: %s", bk)); }
|
RETURN(WHYF("invalid BK field: %s", bk));
|
||||||
switch (rhizome_bk_xor(authorSid, m->cryptoSignPublic, bkBytes, m->cryptoSignSecret)) {
|
int result = rhizome_bk_xor(m->author, m->cryptoSignPublic, bkBytes, m->cryptoSignSecret);
|
||||||
case -1:
|
if (result == 0) {
|
||||||
RETURN(WHY("rhizome_bk_xor() failed"));
|
int verified = rhizome_verify_bundle_privatekey(m);
|
||||||
case 0:
|
if (verified == 0)
|
||||||
RETURN(rhizome_verify_bundle_privatekey(m));
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* Discover if the given manifest was created (signed) by any unlocked identity currently in the
|
||||||
Test to see if the given manifest was created (signed) by any unlocked identity currently in the
|
* keyring.
|
||||||
keyring.
|
*
|
||||||
- Returns -1 if an error occurs, eg, the manifest contains an invalid BK field.
|
* Returns 0 if an identity is found with permission to alter the bundle, after setting the manifest
|
||||||
- Return 0 if the manifest's BK field was produced by any currently unlocked SID.
|
* 'author' field to the SID of the identity and the manifest 'cryptoSignSecret' field to the bundle
|
||||||
- Returns 1 if the manifest has no BK field.
|
* secret key and the 'haveSecret' field to 1.
|
||||||
- Returns 2 otherwise.
|
*
|
||||||
Currently unused; was called from rhizome_list_manifests() to compute the now-defunct
|
* Returns 1 if no identity in the keyring is the author of this bundle.
|
||||||
".selfsigned" column, but that made the Rhizome List view too slow in the Serval Mesh app.
|
*
|
||||||
See issue servalproject/serval-dna#17.
|
* Returns 4 if the manifest has no BK field.
|
||||||
@author Andrew Bettison <andrew@servalproject.com>
|
*
|
||||||
|
* 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();
|
IN();
|
||||||
char *bk = rhizome_manifest_get(m, "BK", NULL, 0);
|
char *bk = rhizome_manifest_get(m, "BK", NULL, 0);
|
||||||
if (!bk) {
|
if (!bk) {
|
||||||
if (debug & DEBUG_RHIZOME) DEBUGF("missing BK field");
|
if (debug & DEBUG_RHIZOME)
|
||||||
RETURN(1);
|
DEBUGF("missing BK field");
|
||||||
|
RETURN(4);
|
||||||
}
|
}
|
||||||
unsigned char bkBytes[RHIZOME_BUNDLE_KEY_BYTES];
|
unsigned char bkBytes[RHIZOME_BUNDLE_KEY_BYTES];
|
||||||
if (fromhexstr(bkBytes, bk, RHIZOME_BUNDLE_KEY_BYTES) == -1)
|
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;
|
int cn = 0, in = 0, kp = 0;
|
||||||
for (; keyring_next_identity(keyring, &cn, &in, &kp); ++kp) {
|
for (; keyring_next_identity(keyring, &cn, &in, &kp); ++kp) {
|
||||||
const unsigned char *authorSid = keyring->contexts[cn]->identities[in]->keypairs[kp]->public_key;
|
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);
|
int rkp = keyring_identity_find_keytype(keyring, cn, in, KEYTYPE_RHIZOME);
|
||||||
if (rkp != -1) {
|
if (rkp != -1) {
|
||||||
switch (rhizome_bk_xor(authorSid, m->cryptoSignPublic, bkBytes, m->cryptoSignSecret)) {
|
int rs_len = keyring->contexts[cn]->identities[in]->keypairs[rkp]->private_key_len;
|
||||||
case -1:
|
if (rs_len < 16 || rs_len > 1024)
|
||||||
RETURN(WHY("rhizome_bk_xor() failed"));
|
RETURN(WHYF("invalid Rhizome Secret: length=%d", rs_len));
|
||||||
case 0:
|
const unsigned char *rs = keyring->contexts[cn]->identities[in]->keypairs[rkp]->private_key;
|
||||||
if (rhizome_verify_bundle_privatekey(m) == 0)
|
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
|
RETURN(0); // bingo
|
||||||
break;
|
}
|
||||||
|
memset(m->cryptoSignSecret, 0, sizeof m->cryptoSignSecret);
|
||||||
|
if (verified == -1)
|
||||||
|
RETURN(WHY("rhizome_verify_bundle_privatekey() failed"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if (debug & DEBUG_RHIZOME)
|
||||||
RETURN(2); // not self signed
|
DEBUG("bundle author not found");
|
||||||
|
RETURN(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Verify the validity of the manifest's sccret key.
|
/* Verify the validity of the manifest's secret key, ie, is the given manifest's 'cryptoSignSecret'
|
||||||
Return 0 if valid, 1 if not. Return -1 if an error occurs.
|
* field actually the secret key corresponding to the public key in 'cryptoSignPublic'?
|
||||||
XXX This is a pretty ugly way to do it, but NaCl offers no API to
|
* Return 0 if valid, 1 if not. Return -1 if an error occurs.
|
||||||
do this cleanly.
|
*
|
||||||
|
* 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)
|
int rhizome_verify_bundle_privatekey(rhizome_manifest *m)
|
||||||
{
|
{
|
||||||
@ -194,45 +258,32 @@ int rhizome_verify_bundle_privatekey(rhizome_manifest *m)
|
|||||||
#endif //!ge25519
|
#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();
|
IN();
|
||||||
unsigned char *hash=m->manifesthash;
|
unsigned char *hash = m->manifesthash;
|
||||||
unsigned char *publicKeyBytes=m->cryptoSignPublic;
|
unsigned char *publicKeyBytes = m->cryptoSignPublic;
|
||||||
|
if (!m->haveSecret && rhizome_extract_privatekey(m))
|
||||||
if (!m->haveSecret && rhizome_extract_privatekey(m, authorSid)) {
|
RETURN(WHY("Cannot find secret key to sign manifest data."));
|
||||||
WHY("Cannot find secret key to sign manifest data.");
|
|
||||||
RETURN(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Signature is formed by running crypto_sign_edwards25519sha512batch() on the
|
/* Signature is formed by running crypto_sign_edwards25519sha512batch() on the
|
||||||
hash of the manifest. The signature actually contains the hash, so to save
|
hash of the manifest. The signature actually contains the hash, so to save
|
||||||
space we cut the hash out of the signature. */
|
space we cut the hash out of the signature. */
|
||||||
unsigned char signatureBuffer[crypto_sign_edwards25519sha512batch_BYTES+crypto_hash_sha512_BYTES];
|
unsigned char signatureBuffer[crypto_sign_edwards25519sha512batch_BYTES + crypto_hash_sha512_BYTES];
|
||||||
unsigned long long sigLen=0;
|
unsigned long long sigLen = 0;
|
||||||
int mLen=crypto_hash_sha512_BYTES;
|
int mLen = crypto_hash_sha512_BYTES;
|
||||||
|
int r = crypto_sign_edwards25519sha512batch(signatureBuffer, &sigLen, &hash[0], mLen, m->cryptoSignSecret);
|
||||||
int r=crypto_sign_edwards25519sha512batch(signatureBuffer,&sigLen,
|
if (r)
|
||||||
&hash[0],mLen,m->cryptoSignSecret);
|
RETURN(WHY("crypto_sign_edwards25519sha512batch() failed."));
|
||||||
if (r) {
|
|
||||||
WHY("crypto_sign() failed.");
|
|
||||||
RETURN(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
rhizome_signature *out=calloc(sizeof(rhizome_signature),1);
|
|
||||||
|
|
||||||
/* Here we use knowledge of the internal structure of the signature block
|
/* Here we use knowledge of the internal structure of the signature block
|
||||||
to remove the hash, since that is implicitly transported, thus reducing the
|
to remove the hash, since that is implicitly transported, thus reducing the
|
||||||
actual signature size down to 64 bytes.
|
actual signature size down to 64 bytes.
|
||||||
We do then need to add the public key of the signatory on. */
|
We do then need to add the public key of the signatory on. */
|
||||||
bcopy(&signatureBuffer[0],&out->signature[1],32);
|
bcopy(&signatureBuffer[0], &out->signature[1], 32);
|
||||||
bcopy(&signatureBuffer[96],&out->signature[33],32);
|
bcopy(&signatureBuffer[96], &out->signature[33], 32);
|
||||||
bcopy(&publicKeyBytes[0],&out->signature[65],crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES);
|
bcopy(&publicKeyBytes[0], &out->signature[65], crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES);
|
||||||
out->signatureLength=65+crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES;
|
out->signatureLength = 65 + crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES;
|
||||||
|
out->signature[0] = out->signatureLength;
|
||||||
out->signature[0]=out->signatureLength;
|
RETURN(0);
|
||||||
|
|
||||||
RETURN(out);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct manifest_signature_block_cache {
|
typedef struct manifest_signature_block_cache {
|
||||||
|
6
serval.h
6
serval.h
@ -914,9 +914,9 @@ void dump_stack();
|
|||||||
_this_call.totals=&_aggregate_stats; \
|
_this_call.totals=&_aggregate_stats; \
|
||||||
fd_func_enter(&_this_call);
|
fd_func_enter(&_this_call);
|
||||||
|
|
||||||
#define OUT() fd_func_exit(&_this_call);
|
#define OUT() fd_func_exit(&_this_call)
|
||||||
#define RETURN(X) { OUT() return(X); }
|
#define RETURN(X) do { OUT(); return (X); } while (0);
|
||||||
#define RETURNNULL { OUT() return(NULL); }
|
#define RETURNNULL do { OUT(); return (NULL); } while (0);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -366,6 +366,19 @@ test_AddUpdateNewVersion() {
|
|||||||
assert_rhizome_list file1_2@$SIDB1 file2@$SIDB1
|
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"
|
doc_AddUpdateNoAuthor="Cannot add new payload to authorless manifest"
|
||||||
setup_AddUpdateNoAuthor() {
|
setup_AddUpdateNoAuthor() {
|
||||||
setup_AddUpdateNewVersion
|
setup_AddUpdateNewVersion
|
||||||
|
Loading…
Reference in New Issue
Block a user