summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/sphinx/rest.rst15
-rw-r--r--src/backend/anastasis-httpd_policy-meta.c16
-rw-r--r--src/include/anastasis.h67
-rw-r--r--src/include/anastasis_database_plugin.h2
-rw-r--r--src/include/anastasis_service.h5
-rw-r--r--src/lib/Makefile.am1
-rw-r--r--src/lib/anastasis_backup.c27
-rw-r--r--src/lib/anastasis_meta.c180
-rw-r--r--src/restclient/anastasis_api_policy_meta_lookup.c33
-rw-r--r--src/stasis/plugin_anastasis_postgres.c13
-rw-r--r--src/stasis/stasis-0001.sql3
11 files changed, 324 insertions, 38 deletions
diff --git a/doc/sphinx/rest.rst b/doc/sphinx/rest.rst
index 7341992..b5ab50d 100644
--- a/doc/sphinx/rest.rst
+++ b/doc/sphinx/rest.rst
@@ -167,11 +167,18 @@ In the following, UUID is always defined and used according to `RFC 4122`_.
.. ts:def:: RecoveryMetaSummary
interface RecoveryMetaSummary {
- // Version numbers as a string (!) are used as keys,
- // the value being the base32-encoded encrypted meta data
- // for that version. A value can be NULL if the document
+ // Version numbers as a string (!) are used as keys.
+ "$VERSION": MetaData;
+ }
+
+ interface MetaData {
+ // The meta value can be NULL if the document
// exists but no meta data was provided.
- "$VERSION": EncryptedMetaData;
+ meta?: String;
+
+ // Server-time indicative of when the recovery
+ // document was uploaded.
+ upload_time: Timestamp;
}
.. http:get:: /policy/$ACCOUNT_PUB[?version=$NUMBER]
diff --git a/src/backend/anastasis-httpd_policy-meta.c b/src/backend/anastasis-httpd_policy-meta.c
index a786c68..7d143ef 100644
--- a/src/backend/anastasis-httpd_policy-meta.c
+++ b/src/backend/anastasis-httpd_policy-meta.c
@@ -36,6 +36,7 @@
*
* @param cls closure with a `json_t *` to build up
* @param version the version of the recovery document
+ * @param ts timestamp when the document was created
* @param recovery_meta_data contains meta data about the encrypted recovery document
* @param recovery_meta_data_size size of @a recovery_meta_data blob
* @return #GNUNET_OK to continue to iterate, #GNUNET_NO to abort iteration
@@ -43,6 +44,7 @@
static enum GNUNET_GenericReturnValue
build_meta_result (void *cls,
uint32_t version,
+ struct GNUNET_TIME_Timestamp ts,
const void *recovery_meta_data,
size_t recovery_meta_data_size)
{
@@ -57,11 +59,15 @@ build_meta_result (void *cls,
json_object_set_new (
result,
version_s,
- (NULL == recovery_meta_data)
- ? json_null ()
- : GNUNET_JSON_from_data (
- recovery_meta_data,
- recovery_meta_data_size)));
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_data_varsize (
+ "meta",
+ recovery_meta_data,
+ recovery_meta_data_size)),
+ GNUNET_JSON_pack_timestamp (
+ "upload_time",
+ ts))));
return GNUNET_OK;
}
diff --git a/src/include/anastasis.h b/src/include/anastasis.h
index b957f18..92c0745 100644
--- a/src/include/anastasis.h
+++ b/src/include/anastasis.h
@@ -374,6 +374,67 @@ ANASTASIS_challenge_abort (struct ANASTASIS_Challenge *c);
/**
+ * Handle for an operation to get available recovery
+ * document versions.
+ */
+struct ANASTASIS_VersionCheck;
+
+
+/**
+ * Callback which passes back meta data about one of the
+ * recovery documents available at the provider.
+ *
+ * @param cls closure for the callback
+ * @param version version number of the policy document,
+ * 0 for the end of the list
+ * @param server_time time of the backup at the provider
+ * @param recdoc_id hash of the compressed recovery document, uniquely
+ * identifies the document; NULL for the end of the list
+ * @param secret_name name of the secret as chosen by the user,
+ * or NULL if the user did not provide a name
+ */
+typedef void
+(*ANASTASIS_MetaPolicyCallback)(void *cls,
+ uint32_t version,
+ struct GNUNET_TIME_Timestamp server_time,
+ const struct GNUNET_HashCode *recdoc_id,
+ const char *secret_name);
+
+
+/**
+ * Obtain an overview of available recovery policies from the
+ * specified provider.
+ *
+ * @param ctx context for making HTTP requests
+ * @param id_data contains the users identity, (user account on providers)
+ * @param version defines the version which will be downloaded, 0 for latest version
+ * @param anastasis_provider_url provider url
+ * @param provider_salt the server salt
+ * @param mpc function called with the available versions
+ * @param mpc_cls closure for @a mpc callback
+ * @return recovery operation handle
+ */
+struct ANASTASIS_VersionCheck *
+ANASTASIS_recovery_get_versions (
+ struct GNUNET_CURL_Context *ctx,
+ const json_t *id_data,
+ unsigned int max_version,
+ const char *anastasis_provider_url,
+ const struct ANASTASIS_CRYPTO_ProviderSaltP *provider_salt,
+ ANASTASIS_MetaPolicyCallback mpc,
+ void *mpc_cls);
+
+
+/**
+ * Cancel version check operation.
+ *
+ * @param vc operation to cancel
+ */
+void
+ANASTASIS_recovery_get_versions_cancel (struct ANASTASIS_VersionCheck *vc);
+
+
+/**
* Defines a Decryption Policy with multiple escrow methods
*/
struct ANASTASIS_DecryptionPolicy
@@ -528,9 +589,9 @@ struct ANASTASIS_Recovery;
*
* @param ctx context for making HTTP requests
* @param id_data contains the users identity, (user account on providers)
- * @param version defines the version which will be downloaded NULL for latest version
- * @param anastasis_provider_url NULL terminated list of possible provider urls
- * @param provider_salt the server salt
+ * @param version defines the version which will be downloaded, 0 for latest version
+ * @param anastasis_provider_url provider REST API endpoint url
+ * @param provider_salt the provider's salt
* @param pc opens the policy call back which holds the downloaded version and the policies
* @param pc_cls closure for callback
* @param csc core secret callback is opened, with this the core secert is passed to the client after the authentication
diff --git a/src/include/anastasis_database_plugin.h b/src/include/anastasis_database_plugin.h
index 20e87d1..2082bf9 100644
--- a/src/include/anastasis_database_plugin.h
+++ b/src/include/anastasis_database_plugin.h
@@ -165,6 +165,7 @@ typedef bool
*
* @param cls closure
* @param version the version of the recovery document
+ * @param ts timestamp when the document was uploaded
* @param recovery_meta_data contains meta data about the encrypted recovery document
* @param recovery_meta_data_size size of @a recovery_meta_data blob
* @return #GNUNET_OK to continue to iterate, #GNUNET_NO to abort iteration
@@ -172,6 +173,7 @@ typedef bool
typedef enum GNUNET_GenericReturnValue
(*ANASTASIS_DB_RecoveryMetaCallback)(void *cls,
uint32_t version,
+ struct GNUNET_TIME_Timestamp ts,
const void *recovery_meta_data,
size_t recovery_meta_data_size);
diff --git a/src/include/anastasis_service.h b/src/include/anastasis_service.h
index ee24b7a..3bc8f87 100644
--- a/src/include/anastasis_service.h
+++ b/src/include/anastasis_service.h
@@ -162,6 +162,11 @@ struct ANASTASIS_MetaDataEntry
{
/**
+ * Timestamp of the backup at the server.
+ */
+ struct GNUNET_TIME_Timestamp server_time;
+
+ /**
* The encrypted meta data we downloaded.
*/
const void *meta_data;
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index 07460d4..6f71418 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -14,6 +14,7 @@ libanastasis_la_LDFLAGS = \
-no-undefined
libanastasis_la_SOURCES = \
anastasis_backup.c \
+ anastasis_meta.c \
anastasis_recovery.c
libanastasis_la_LIBADD = \
$(top_builddir)/src/util/libanastasisutil.la \
diff --git a/src/lib/anastasis_backup.c b/src/lib/anastasis_backup.c
index 20c77e4..2e769ca 100644
--- a/src/lib/anastasis_backup.c
+++ b/src/lib/anastasis_backup.c
@@ -734,6 +734,8 @@ ANASTASIS_secret_share (struct GNUNET_CURL_Context *ctx,
json_t *esc_methods;
size_t recovery_document_size;
char *recovery_document_str;
+ size_t meta_size;
+ void *meta;
if (0 == pss_length)
{
@@ -889,6 +891,18 @@ ANASTASIS_secret_share (struct GNUNET_CURL_Context *ctx,
recovery_document_str = (char *) cbuf;
}
+ meta_size = sizeof (struct GNUNET_HashCode);
+ if (NULL != secret_name)
+ meta_size += strlen (secret_name) + 1;
+ meta = GNUNET_malloc (meta_size);
+ GNUNET_CRYPTO_hash (recovery_document_str,
+ recovery_document_size,
+ (struct GNUNET_HashCode *) meta);
+ if (NULL != secret_name)
+ memcpy (meta + sizeof (struct GNUNET_HashCode),
+ secret_name,
+ strlen (secret_name) + 1);
+
for (unsigned int l = 0; l < ss->pss_length; l++)
{
struct PolicyStoreState *pss = &ss->pss[l];
@@ -905,12 +919,11 @@ ANASTASIS_secret_share (struct GNUNET_CURL_Context *ctx,
ANASTASIS_CRYPTO_user_identifier_derive (id_data,
&pss->server_salt,
&pss->id);
- if (NULL != secret_name)
- ANASTASIS_CRYPTO_recovery_metadata_encrypt (&pss->id,
- secret_name,
- strlen (secret_name),
- &enc_meta,
- &enc_meta_size);
+ ANASTASIS_CRYPTO_recovery_metadata_encrypt (&pss->id,
+ meta,
+ meta_size,
+ &enc_meta,
+ &enc_meta_size);
ANASTASIS_CRYPTO_account_private_key_derive (&pss->id,
&anastasis_priv);
ANASTASIS_CRYPTO_recovery_document_encrypt (&pss->id,
@@ -943,9 +956,11 @@ ANASTASIS_secret_share (struct GNUNET_CURL_Context *ctx,
GNUNET_break (0);
ANASTASIS_secret_share_cancel (ss);
GNUNET_free (recovery_document_str);
+ GNUNET_free (meta);
return NULL;
}
}
+ GNUNET_free (meta);
GNUNET_free (recovery_document_str);
return ss;
}
diff --git a/src/lib/anastasis_meta.c b/src/lib/anastasis_meta.c
new file mode 100644
index 0000000..7812f6b
--- /dev/null
+++ b/src/lib/anastasis_meta.c
@@ -0,0 +1,180 @@
+/*
+ This file is part of Anastasis
+ Copyright (C) 2022 Anastasis SARL
+
+ Anastasis is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @brief anastasis client api to get recovery document meta data
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "anastasis.h"
+#include <taler/taler_json_lib.h>
+#include <gnunet/gnunet_util_lib.h>
+#include <taler/taler_merchant_service.h>
+
+
+/**
+ * Handle for a version check operation.
+ */
+struct ANASTASIS_VersionCheck
+{
+ /**
+ * Function to call with results.
+ */
+ ANASTASIS_MetaPolicyCallback mpc;
+
+ /**
+ * Closure for @e mpc.
+ */
+ void *mpc_cls;
+
+ /**
+ * Handle for the actual REST operation.
+ */
+ struct ANASTASIS_PolicyMetaLookupOperation *plm;
+
+ /**
+ * User identifier (needed to decrypt).
+ */
+ struct ANASTASIS_CRYPTO_UserIdentifierP id;
+};
+
+
+/**
+ * Function called with results from a GET /policy/$POL/meta request
+ *
+ * @param cls closure with the `struct ANASTASIS_VersionCheck *`
+ * @param http_status HTTP status code for this request
+ * @param dd the response details
+ */
+static void
+meta_cb (
+ void *cls,
+ unsigned int http_status,
+ const struct ANASTASIS_MetaDownloadDetails *dd)
+{
+ struct ANASTASIS_VersionCheck *vc = cls;
+
+ vc->plm = NULL;
+ if ( (MHD_HTTP_OK != http_status) ||
+ (NULL == dd) )
+ {
+ vc->mpc (vc->mpc_cls,
+ 0,
+ GNUNET_TIME_UNIT_ZERO_TS,
+ NULL,
+ NULL);
+ ANASTASIS_recovery_get_versions_cancel (vc);
+ return;
+ }
+ for (size_t i = 0; i<dd->metas_length; i++)
+ {
+ const struct ANASTASIS_MetaDataEntry *meta = &dd->metas[i];
+ const char *secret_name = NULL;
+ const struct GNUNET_HashCode *eph;
+ void *dec;
+ size_t dec_len;
+
+ if (GNUNET_OK !=
+ ANASTASIS_CRYPTO_recovery_metadata_decrypt (
+ &vc->id,
+ meta->meta_data,
+ meta->meta_data_size,
+ &dec,
+ &dec_len))
+ {
+ GNUNET_break_op (0);
+ continue;
+ }
+ if (sizeof (*eph) > dec_len)
+ {
+ GNUNET_break_op (0);
+ GNUNET_free (dec);
+ continue;
+ }
+ eph = dec;
+ if (sizeof (*eph) < dec_len)
+ {
+ secret_name = (const char *) &eph[1];
+ dec_len -= sizeof (*eph);
+ if ('\0' != secret_name[dec_len - 1])
+ {
+ GNUNET_break_op (0);
+ GNUNET_free (dec);
+ continue;
+ }
+ }
+ vc->mpc (vc->mpc_cls,
+ meta->version,
+ meta->server_time,
+ eph,
+ secret_name);
+ GNUNET_free (dec);
+ }
+ vc->mpc (vc->mpc_cls,
+ 0,
+ GNUNET_TIME_UNIT_ZERO_TS,
+ NULL,
+ NULL);
+ ANASTASIS_recovery_get_versions_cancel (vc);
+}
+
+
+struct ANASTASIS_VersionCheck *
+ANASTASIS_recovery_get_versions (
+ struct GNUNET_CURL_Context *ctx,
+ const json_t *id_data,
+ unsigned int max_version,
+ const char *anastasis_provider_url,
+ const struct ANASTASIS_CRYPTO_ProviderSaltP *provider_salt,
+ ANASTASIS_MetaPolicyCallback mpc,
+ void *mpc_cls)
+{
+ struct ANASTASIS_VersionCheck *vc;
+ struct ANASTASIS_CRYPTO_AccountPublicKeyP account_pub;
+
+ vc = GNUNET_new (struct ANASTASIS_VersionCheck);
+ vc->mpc = mpc;
+ vc->mpc_cls = mpc_cls;
+ ANASTASIS_CRYPTO_user_identifier_derive (id_data,
+ provider_salt,
+ &vc->id);
+ ANASTASIS_CRYPTO_account_public_key_derive (&vc->id,
+ &account_pub);
+ vc->plm = ANASTASIS_policy_meta_lookup (ctx,
+ anastasis_provider_url,
+ &account_pub,
+ max_version,
+ &meta_cb,
+ vc);
+ if (NULL == vc->plm)
+ {
+ GNUNET_break (0);
+ GNUNET_free (vc);
+ return NULL;
+ }
+ return vc;
+}
+
+
+void
+ANASTASIS_recovery_get_versions_cancel (struct ANASTASIS_VersionCheck *vc)
+{
+ if (NULL != vc->plm)
+ {
+ ANASTASIS_policy_meta_lookup_cancel (vc->plm);
+ vc->plm = NULL;
+ }
+ GNUNET_free (vc);
+}
diff --git a/src/restclient/anastasis_api_policy_meta_lookup.c b/src/restclient/anastasis_api_policy_meta_lookup.c
index 9be49ca..b49d1b8 100644
--- a/src/restclient/anastasis_api_policy_meta_lookup.c
+++ b/src/restclient/anastasis_api_policy_meta_lookup.c
@@ -28,6 +28,7 @@
#include <microhttpd.h> /* just for HTTP status codes */
#include "anastasis_service.h"
#include "anastasis_api_curl_defaults.h"
+#include <gnunet/gnunet_json_lib.h>
#include <taler/taler_signatures.h>
@@ -132,7 +133,15 @@ handle_policy_meta_lookup_finished (void *cls,
{
unsigned int ver;
char dummy;
- const char *vals;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_varsize ("meta",
+ &md[off],
+ &metas[off].meta_data_size)),
+ GNUNET_JSON_spec_timestamp ("upload_time",
+ &metas[off].server_time),
+ GNUNET_JSON_spec_end ()
+ };
if (1 != sscanf (label,
"%u%c",
@@ -142,27 +151,15 @@ handle_policy_meta_lookup_finished (void *cls,
GNUNET_break (0);
break;
}
- metas[off].version = (uint32_t) ver;
- if (json_is_null (val))
- {
- metas[off].meta_data = NULL;
- metas[off].meta_data_size = 0;
- off++;
- continue;
- }
- vals = json_string_value (val);
- if ( (NULL == vals) ||
- (GNUNET_OK !=
- GNUNET_STRINGS_string_to_data_alloc (vals,
- strlen (vals),
- &md[off],
- &metas[off].meta_data_size)) )
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (val,
+ spec,
+ NULL, NULL))
{
- GNUNET_break (0);
+ GNUNET_break_op (0);
break;
}
metas[off].version = (uint32_t) ver;
- metas[off].meta_data = md[off];
off++;
}
if (off < mlen)
diff --git a/src/stasis/plugin_anastasis_postgres.c b/src/stasis/plugin_anastasis_postgres.c
index ef13c6c..bfe86da 100644
--- a/src/stasis/plugin_anastasis_postgres.c
+++ b/src/stasis/plugin_anastasis_postgres.c
@@ -334,9 +334,10 @@ prepare_statements (void *cls)
",recovery_data_hash"
",recovery_data"
",recovery_meta_data"
+ ",creation_date"
") VALUES "
- "($1, $2, $3, $4, $5, $6);",
- 6),
+ "($1, $2, $3, $4, $5, $6, $7);",
+ 7),
GNUNET_PQ_make_prepare ("truth_select",
"SELECT "
" method_name"
@@ -348,6 +349,7 @@ prepare_statements (void *cls)
GNUNET_PQ_make_prepare ("recoverydocument_select_meta",
"SELECT "
" version"
+ ",creation_date"
",recovery_meta_data"
" FROM anastasis_recoverydocument"
" WHERE user_id=$1"
@@ -1019,6 +1021,8 @@ postgres_store_recovery_document (
/* finally, actually insert the recovery document */
{
+ struct GNUNET_TIME_Timestamp now
+ = GNUNET_TIME_timestamp_get ();
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (account_pub),
GNUNET_PQ_query_param_uint32 (version),
@@ -1030,6 +1034,7 @@ postgres_store_recovery_document (
? GNUNET_PQ_query_param_null ()
: GNUNET_PQ_query_param_fixed_size (recovery_meta_data,
recovery_meta_data_size),
+ GNUNET_PQ_query_param_timestamp (&now),
GNUNET_PQ_query_param_end
};
@@ -2248,10 +2253,13 @@ meta_iterator (void *cls,
uint32_t version;
void *meta_data = NULL;
size_t meta_data_size = 0;
+ struct GNUNET_TIME_Timestamp ts;
bool unused = false;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_uint32 ("version",
&version),
+ GNUNET_PQ_result_spec_timestamp ("creation_date",
+ &ts),
GNUNET_PQ_result_spec_allow_null (
GNUNET_PQ_result_spec_variable_size ("recovery_meta_data",
&meta_data,
@@ -2272,6 +2280,7 @@ meta_iterator (void *cls,
}
ret = ctx->cb (ctx->cb_cls,
version,
+ ts,
meta_data,
meta_data_size);
GNUNET_PQ_cleanup_result (rs);
diff --git a/src/stasis/stasis-0001.sql b/src/stasis/stasis-0001.sql
index e3c9c5c..d512473 100644
--- a/src/stasis/stasis-0001.sql
+++ b/src/stasis/stasis-0001.sql
@@ -141,6 +141,7 @@ CREATE TABLE IF NOT EXISTS anastasis_recoverydocument
recovery_data_hash BYTEA NOT NULL CHECK(length(recovery_data_hash)=64),
recovery_data BYTEA NOT NULL,
recovery_meta_data BYTEA DEFAULT NULL,
+ creation_date INT8 NOT NULL,
PRIMARY KEY (user_id, version));
COMMENT ON TABLE anastasis_recoverydocument
IS 'Stores a recovery document which contains the policy and the encrypted core secret';
@@ -152,6 +153,8 @@ COMMENT ON COLUMN anastasis_recoverydocument.account_sig
IS 'Signature of the recovery document';
COMMENT ON COLUMN anastasis_recoverydocument.recovery_data_hash
IS 'Hash of the recovery document to prevent unnecessary uploads';
+COMMENT ON COLUMN anastasis_recoverydocument.creation_date
+ IS 'Creation date of the recovery document (when it was uploaded)';
COMMENT ON COLUMN anastasis_recoverydocument.recovery_data
IS 'Contains the encrypted policy and core secret';
COMMENT ON COLUMN anastasis_recoverydocument.recovery_meta_data