From 78f70d6bdc1853537d7cc865f69368be541f746d Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Thu, 2 Sep 2021 15:53:14 -0400 Subject: [PATCH 01/14] write some words about lease renewal secrets --- docs/proposed/http-storage-node-protocol.rst | 5 +++ docs/specifications/index.rst | 1 + docs/specifications/lease.rst | 41 ++++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 docs/specifications/lease.rst diff --git a/docs/proposed/http-storage-node-protocol.rst b/docs/proposed/http-storage-node-protocol.rst index d7a41e827..09c193c65 100644 --- a/docs/proposed/http-storage-node-protocol.rst +++ b/docs/proposed/http-storage-node-protocol.rst @@ -380,6 +380,11 @@ then the expiration time of that lease will be changed to 31 days after the time If it does not match an existing lease then a new lease will be created with this ``renew-secret`` which expires 31 days after the time of this operation. +The renew and cancel secrets must be 32 bytes long +(or in the case of JSON encoding they must UTF-8 encode to 32 bytes). +The server treats them as opaque values. +See `leases`_ for details about how the Tahoe-LAFS storage client constructs these values. + In these cases the response is ``NO CONTENT`` with an empty body. It is possible that the storage server will have no shares for the given ``storage_index`` because: diff --git a/docs/specifications/index.rst b/docs/specifications/index.rst index 2029c9e5a..e813acf07 100644 --- a/docs/specifications/index.rst +++ b/docs/specifications/index.rst @@ -14,5 +14,6 @@ the data formats used by Tahoe. URI-extension mutable dirnodes + lease servers-of-happiness backends/raic diff --git a/docs/specifications/lease.rst b/docs/specifications/lease.rst new file mode 100644 index 000000000..fd5535701 --- /dev/null +++ b/docs/specifications/lease.rst @@ -0,0 +1,41 @@ +.. -*- coding: utf-8 -*- + +Share Leases +============ + +A lease is a marker attached to a share indicating that some client has asked for that share to be retained for some amount of time. +The intent is to allow clients and servers to collaborate to determine which data should still be retained and which can be discarded to reclaim storage space. +Zero or more leases may be attached to any particular share. + +Renewal Secrets +--------------- + +Each lease is uniquely identified by its **renewal secret**. +This is a 32 byte string which can be used to extend the validity period of that lease. + +To a storage server a renewal secret is an opaque value which is only ever compared to other renewal secrets to determine equality. + +Storage clients will typically want to follow a scheme to deterministically derive the renewal secret for a particular share from information the client already holds about that share. +This allows a client to maintain and renew single long-lived lease without maintaining additional local state. + +The scheme in use in Tahoe-LAFS as of 1.16.0 is as follows. + +* The **netstring encoding** of a byte string is the concatenation of: + * the base 10 representation of the length of the string + * ":" + * the string + * "," +* The **sha256d digest** is the **sha256 digest** of the **sha256 digest** of a string. +* The **sha256d tagged digest** is the **sha256d digest** of the concatenation of the **netstring encoding** of one string with one other unmodified string. +* The **sha256d tagged pair digest** the **sha256d digest** of the concatenation of the **netstring encodings** of each of three strings. +* The **bucket renewal tag** is ``allmydata_bucket_renewal_secret_v1``. +* The **file renewal tag** is ``allmydata_file_renewal_secret_v1``. +* The **client renewal tag** is ``allmydata_client_renewal_secret_v1``. +* The **lease secret** is a 32 byte string, typically randomly generated once and then persisted for all future uses. +* The **client renewal secret** is the **sha256d tagged digest** of (**lease secret**, **client renewal tag**). +* The **storage index** is constructed using a capability-type-specific scheme. + See ``storage_index_hash`` and ``ssk_storage_index_hash`` calls in ``src/allmydata/uri.py``. +* The **file renewal secret** is the **sha256d tagged pair digest** of (**file renewal tag**, **client renewal secret**, **storage index**). +* The **base32 encoding** is ``base64.b32encode`` lowercased and with trailing ``=`` stripped. +* The **peer id** is the **base32 encoding** of the SHA1 digest of the server's x509 certificate. +* The **renewal secret** is the **sha256d tagged pair digest** of (**bucket renewal tag**, **file renewal secret**, **peer id**). From b6173eea839a21cdefedb6117cb5edf9b0fbee02 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Thu, 2 Sep 2021 16:42:27 -0400 Subject: [PATCH 02/14] news fragment --- docs/3774.documentation | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/3774.documentation diff --git a/docs/3774.documentation b/docs/3774.documentation new file mode 100644 index 000000000..d58105966 --- /dev/null +++ b/docs/3774.documentation @@ -0,0 +1 @@ +There is now a specification for the scheme which Tahoe-LAFS storage clients use to derive their lease renewal secrets. From 11a0b8d20979b1d0340819bd94b6b614496abde0 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Thu, 2 Sep 2021 16:44:42 -0400 Subject: [PATCH 03/14] attempt to appease rst --- docs/specifications/lease.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/specifications/lease.rst b/docs/specifications/lease.rst index fd5535701..bf8e8f181 100644 --- a/docs/specifications/lease.rst +++ b/docs/specifications/lease.rst @@ -21,10 +21,12 @@ This allows a client to maintain and renew single long-lived lease without maint The scheme in use in Tahoe-LAFS as of 1.16.0 is as follows. * The **netstring encoding** of a byte string is the concatenation of: + * the base 10 representation of the length of the string * ":" * the string * "," + * The **sha256d digest** is the **sha256 digest** of the **sha256 digest** of a string. * The **sha256d tagged digest** is the **sha256d digest** of the concatenation of the **netstring encoding** of one string with one other unmodified string. * The **sha256d tagged pair digest** the **sha256d digest** of the concatenation of the **netstring encodings** of each of three strings. From bb63331720f3e3d32fc6e2775e057064a1cc28b7 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Fri, 3 Sep 2021 09:04:08 -0400 Subject: [PATCH 04/14] put the newsfragment in the right place --- {docs => newsfragments}/3774.documentation | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {docs => newsfragments}/3774.documentation (100%) diff --git a/docs/3774.documentation b/newsfragments/3774.documentation similarity index 100% rename from docs/3774.documentation rename to newsfragments/3774.documentation From 3ba379ce7e90736adbc2b325f9965117bec09690 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Fri, 3 Sep 2021 09:06:27 -0400 Subject: [PATCH 05/14] some formatting improvements --- docs/specifications/lease.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/specifications/lease.rst b/docs/specifications/lease.rst index bf8e8f181..a8bee427e 100644 --- a/docs/specifications/lease.rst +++ b/docs/specifications/lease.rst @@ -22,17 +22,17 @@ The scheme in use in Tahoe-LAFS as of 1.16.0 is as follows. * The **netstring encoding** of a byte string is the concatenation of: - * the base 10 representation of the length of the string - * ":" - * the string - * "," + * the ascii encoding of the base 10 representation of the length of the string + * ``":"`` + * the string itself + * ``","`` * The **sha256d digest** is the **sha256 digest** of the **sha256 digest** of a string. * The **sha256d tagged digest** is the **sha256d digest** of the concatenation of the **netstring encoding** of one string with one other unmodified string. * The **sha256d tagged pair digest** the **sha256d digest** of the concatenation of the **netstring encodings** of each of three strings. -* The **bucket renewal tag** is ``allmydata_bucket_renewal_secret_v1``. -* The **file renewal tag** is ``allmydata_file_renewal_secret_v1``. -* The **client renewal tag** is ``allmydata_client_renewal_secret_v1``. +* The **bucket renewal tag** is ``"allmydata_bucket_renewal_secret_v1"``. +* The **file renewal tag** is ``"allmydata_file_renewal_secret_v1"``. +* The **client renewal tag** is ``"allmydata_client_renewal_secret_v1"``. * The **lease secret** is a 32 byte string, typically randomly generated once and then persisted for all future uses. * The **client renewal secret** is the **sha256d tagged digest** of (**lease secret**, **client renewal tag**). * The **storage index** is constructed using a capability-type-specific scheme. From 8fe9532faf64c9d65f46f0ecf92d38af2634568e Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Fri, 3 Sep 2021 09:17:34 -0400 Subject: [PATCH 06/14] get the cross-reference right --- docs/proposed/http-storage-node-protocol.rst | 2 +- docs/specifications/lease.rst | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/proposed/http-storage-node-protocol.rst b/docs/proposed/http-storage-node-protocol.rst index 09c193c65..47f94db49 100644 --- a/docs/proposed/http-storage-node-protocol.rst +++ b/docs/proposed/http-storage-node-protocol.rst @@ -383,7 +383,7 @@ then a new lease will be created with this ``renew-secret`` which expires 31 day The renew and cancel secrets must be 32 bytes long (or in the case of JSON encoding they must UTF-8 encode to 32 bytes). The server treats them as opaque values. -See `leases`_ for details about how the Tahoe-LAFS storage client constructs these values. +:ref:`Share Leases` gives details about how the Tahoe-LAFS storage client constructs these values. In these cases the response is ``NO CONTENT`` with an empty body. diff --git a/docs/specifications/lease.rst b/docs/specifications/lease.rst index a8bee427e..54ee5cd28 100644 --- a/docs/specifications/lease.rst +++ b/docs/specifications/lease.rst @@ -1,5 +1,7 @@ .. -*- coding: utf-8 -*- +.. _share leases: + Share Leases ============ From 78a1d65b784b50486c8c59f8cdc5bad706405797 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Fri, 3 Sep 2021 12:33:07 -0400 Subject: [PATCH 07/14] RFC 7049, section 4.1 describes correct JSON encoding for byte strings --- docs/proposed/http-storage-node-protocol.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/proposed/http-storage-node-protocol.rst b/docs/proposed/http-storage-node-protocol.rst index 47f94db49..bc44468a6 100644 --- a/docs/proposed/http-storage-node-protocol.rst +++ b/docs/proposed/http-storage-node-protocol.rst @@ -380,8 +380,7 @@ then the expiration time of that lease will be changed to 31 days after the time If it does not match an existing lease then a new lease will be created with this ``renew-secret`` which expires 31 days after the time of this operation. -The renew and cancel secrets must be 32 bytes long -(or in the case of JSON encoding they must UTF-8 encode to 32 bytes). +The renew and cancel secrets must be 32 bytes long. The server treats them as opaque values. :ref:`Share Leases` gives details about how the Tahoe-LAFS storage client constructs these values. From a864bd51323f4e1378ced63a50516bad645e559f Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Fri, 3 Sep 2021 12:44:23 -0400 Subject: [PATCH 08/14] more precision --- docs/proposed/http-storage-node-protocol.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/proposed/http-storage-node-protocol.rst b/docs/proposed/http-storage-node-protocol.rst index bc44468a6..ade0bc167 100644 --- a/docs/proposed/http-storage-node-protocol.rst +++ b/docs/proposed/http-storage-node-protocol.rst @@ -380,7 +380,7 @@ then the expiration time of that lease will be changed to 31 days after the time If it does not match an existing lease then a new lease will be created with this ``renew-secret`` which expires 31 days after the time of this operation. -The renew and cancel secrets must be 32 bytes long. +``renew-secret`` and ``cancel-secret`` values must be 32 bytes long. The server treats them as opaque values. :ref:`Share Leases` gives details about how the Tahoe-LAFS storage client constructs these values. From bb57fcfb50d4e01bbc4de2e23dbbf7a60c004031 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Fri, 3 Sep 2021 12:45:45 -0400 Subject: [PATCH 09/14] words about the cancel secret --- docs/specifications/lease.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/specifications/lease.rst b/docs/specifications/lease.rst index 54ee5cd28..8fc7c42eb 100644 --- a/docs/specifications/lease.rst +++ b/docs/specifications/lease.rst @@ -43,3 +43,21 @@ The scheme in use in Tahoe-LAFS as of 1.16.0 is as follows. * The **base32 encoding** is ``base64.b32encode`` lowercased and with trailing ``=`` stripped. * The **peer id** is the **base32 encoding** of the SHA1 digest of the server's x509 certificate. * The **renewal secret** is the **sha256d tagged pair digest** of (**bucket renewal tag**, **file renewal secret**, **peer id**). + +Cancel Secrets +-------------- + +Lease cancellation is unimplemented. +Nevertheless, +a cancel secret is sent by storage clients to storage servers and stored in lease records. + +The scheme for deriving **cancel secret** in use in Tahoe-LAFS as of 1.16.0 is similar to that used to derive the **renewal secret**. + +The differences are: + +* Use of **client renewal tag** is replaced by use of **client cancel tag**. +* Use of **file renewal secret** is replaced by use of **file cancel tag**. +* Use of **bucket renewal tag** is replaced by use of **bucket cancel tag**. +* **client cancel tag** is ``"allmydata_client_cancel_secret_v1"``. +* **file cancel tag** is ``"allmydata_file_cancel_secret_v1"``. +* **bucket cancel tag** is ``"allmydata_bucket_cancel_secret_v1"``. From 8d15a0d5ebcbf16979a984484807a0c54f6b4a24 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Fri, 3 Sep 2021 16:27:57 -0400 Subject: [PATCH 10/14] words about authorization --- docs/proposed/http-storage-node-protocol.rst | 26 ++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/docs/proposed/http-storage-node-protocol.rst b/docs/proposed/http-storage-node-protocol.rst index d7a41e827..0daa48cab 100644 --- a/docs/proposed/http-storage-node-protocol.rst +++ b/docs/proposed/http-storage-node-protocol.rst @@ -24,11 +24,21 @@ Glossary storage server a Tahoe-LAFS process configured to offer storage and reachable over the network for store and retrieve operations + storage service + a Python object held in memory in the storage server which provides the implementation of the storage protocol + introducer a Tahoe-LAFS process at a known location configured to re-publish announcements about the location of storage servers fURL a self-authenticating URL-like string which can be used to locate a remote object using the Foolscap protocol + (the storage service is an example of such an object) + + NURL + a self-authenticating URL-like string almost exactly like a fURL but without being tied to Foolscap + + swissnum + a short random string which is part of a fURL and which acts as a shared secret to authorize clients to use a storage service lease state associated with a share informing a storage server of the duration of storage desired by a client @@ -128,6 +138,8 @@ The Foolscap-based protocol offers: * A careful configuration of the TLS connection parameters *may* also offer **forward secrecy**. However, Tahoe-LAFS' use of Foolscap takes no steps to ensure this is the case. +* **Storage authorization** by way of a capability contained in the fURL addressing a storage service. + Discussion !!!!!!!!!! @@ -158,6 +170,10 @@ there is no way to write data which appears legitimate to a legitimate client). Therefore, **message confidentiality** is necessary when exchanging these secrets. **Forward secrecy** is preferred so that an attacker recording an exchange today cannot launch this attack at some future point after compromising the necessary keys. +A storage service offers service only to some clients. +A client proves their authorization to use the storage service by presenting a shared secret taken from the fURL. +In this way **storage authorization** is performed to prevent disallowed parties from consuming all available storage resources. + Functionality ------------- @@ -214,6 +230,10 @@ Additionally, by continuing to interact using TLS, Bob's client and Alice's storage node are assured of both **message authentication** and **message confidentiality**. +Bob's client further inspects the fURL for the *swissnum*. +When Bob's client issues HTTP requests to Alice's storage node it includes the *swissnum* in its requests. +**Storage authorization** has been achieved. + .. note:: Foolscap TubIDs are 20 bytes (SHA1 digest of the certificate). @@ -343,6 +363,12 @@ one branch contains all of the share data; another branch contains all of the lease data; etc. +Authorization is required for all endpoints. +The standard HTTP authorization protocol is used. +The authentication *type* used is ``Tahoe-LAFS``. +The swissnum from the NURL used to locate the storage service is used as the *credentials*. +If credentials are not presented or the swissnum is not associated with a storage service then no storage processing is performed and the request receives an ``UNAUTHORIZED`` response. + General ~~~~~~~ From a4334b35a0bb6f669429306187cc0fa93cef2518 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Fri, 3 Sep 2021 16:28:27 -0400 Subject: [PATCH 11/14] news fragment --- newsfragments/3785.documentation | 1 + 1 file changed, 1 insertion(+) create mode 100644 newsfragments/3785.documentation diff --git a/newsfragments/3785.documentation b/newsfragments/3785.documentation new file mode 100644 index 000000000..4eb268f79 --- /dev/null +++ b/newsfragments/3785.documentation @@ -0,0 +1 @@ +The Great Black Swamp specification now describes the required authorization scheme. From e2b483e093b11cd31a513b2069755df7a0bf13ed Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Tue, 7 Sep 2021 08:13:03 -0400 Subject: [PATCH 12/14] an even stronger prevention is provided --- docs/proposed/http-storage-node-protocol.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/proposed/http-storage-node-protocol.rst b/docs/proposed/http-storage-node-protocol.rst index 0daa48cab..0c6186bc1 100644 --- a/docs/proposed/http-storage-node-protocol.rst +++ b/docs/proposed/http-storage-node-protocol.rst @@ -172,7 +172,7 @@ Therefore, **message confidentiality** is necessary when exchanging these secret A storage service offers service only to some clients. A client proves their authorization to use the storage service by presenting a shared secret taken from the fURL. -In this way **storage authorization** is performed to prevent disallowed parties from consuming all available storage resources. +In this way **storage authorization** is performed to prevent disallowed parties from consuming any storage resources. Functionality ------------- From 7219291343299263050ddaf7e39ac1f6af2c62b7 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Tue, 7 Sep 2021 13:30:21 -0400 Subject: [PATCH 13/14] add a reference implementation for lease renewal secret derivation --- docs/proposed/http-storage-node-protocol.rst | 2 +- docs/specifications/derive_renewal_secret.py | 87 ++++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 docs/specifications/derive_renewal_secret.py diff --git a/docs/proposed/http-storage-node-protocol.rst b/docs/proposed/http-storage-node-protocol.rst index ade0bc167..3a09ccae0 100644 --- a/docs/proposed/http-storage-node-protocol.rst +++ b/docs/proposed/http-storage-node-protocol.rst @@ -45,7 +45,7 @@ Glossary (sometimes "slot" is considered a synonym for "storage index of a slot") storage index - a short string which can address a slot or a bucket + a 16 byte string which can address a slot or a bucket (in practice, derived by hashing the encryption key associated with contents of that slot or bucket) write enabler diff --git a/docs/specifications/derive_renewal_secret.py b/docs/specifications/derive_renewal_secret.py new file mode 100644 index 000000000..75009eda4 --- /dev/null +++ b/docs/specifications/derive_renewal_secret.py @@ -0,0 +1,87 @@ + +""" +This is a reference implementation of the lease renewal secret derivation +protocol in use by Tahoe-LAFS clients as of 1.16.0. +""" + +from allmydata.util.base32 import ( + a2b as b32decode, + b2a as b32encode, +) +from allmydata.util.hashutil import ( + tagged_hash, + tagged_pair_hash, +) + + +def derive_renewal_secret(lease_secret: bytes, storage_index: bytes, tubid: bytes) -> bytes: + assert len(lease_secret) == 32 + assert len(storage_index) == 16 + assert len(tubid) == 20 + + bucket_renewal_tag = b"allmydata_bucket_renewal_secret_v1" + file_renewal_tag = b"allmydata_file_renewal_secret_v1" + client_renewal_tag = b"allmydata_client_renewal_secret_v1" + + client_renewal_secret = tagged_hash(lease_secret, client_renewal_tag) + file_renewal_secret = tagged_pair_hash( + file_renewal_tag, + client_renewal_secret, + storage_index, + ) + peer_id = tubid + + return tagged_pair_hash(bucket_renewal_tag, file_renewal_secret, peer_id) + +def demo(): + secret = b32encode(derive_renewal_secret( + b"lease secretxxxxxxxxxxxxxxxxxxxx", + b"storage indexxxx", + b"tub idxxxxxxxxxxxxxx", + )).decode("ascii") + print("An example renewal secret: {}".format(secret)) + +def test(): + # These test vectors created by intrumenting Tahoe-LAFS + # bb57fcfb50d4e01bbc4de2e23dbbf7a60c004031 to emit `self.renew_secret` in + # allmydata.immutable.upload.ServerTracker.query and then uploading a + # couple files to a couple different storage servers. + test_vector = [ + dict(lease_secret=b"boity2cdh7jvl3ltaeebuiobbspjmbuopnwbde2yeh4k6x7jioga", + storage_index=b"vrttmwlicrzbt7gh5qsooogr7u", + tubid=b"v67jiisoty6ooyxlql5fuucitqiok2ic", + expected=b"osd6wmc5vz4g3ukg64sitmzlfiaaordutrez7oxdp5kkze7zp5zq", + ), + dict(lease_secret=b"boity2cdh7jvl3ltaeebuiobbspjmbuopnwbde2yeh4k6x7jioga", + storage_index=b"75gmmfts772ww4beiewc234o5e", + tubid=b"v67jiisoty6ooyxlql5fuucitqiok2ic", + expected=b"35itmusj7qm2pfimh62snbyxp3imreofhx4djr7i2fweta75szda", + ), + dict(lease_secret=b"boity2cdh7jvl3ltaeebuiobbspjmbuopnwbde2yeh4k6x7jioga", + storage_index=b"75gmmfts772ww4beiewc234o5e", + tubid=b"lh5fhobkjrmkqjmkxhy3yaonoociggpz", + expected=b"srrlruge47ws3lm53vgdxprgqb6bz7cdblnuovdgtfkqrygrjm4q", + ), + dict(lease_secret=b"vacviff4xfqxsbp64tdr3frg3xnkcsuwt5jpyat2qxcm44bwu75a", + storage_index=b"75gmmfts772ww4beiewc234o5e", + tubid=b"lh5fhobkjrmkqjmkxhy3yaonoociggpz", + expected=b"b4jledjiqjqekbm2erekzqumqzblegxi23i5ojva7g7xmqqnl5pq", + ), + ] + + for n, item in enumerate(test_vector): + derived = b32encode(derive_renewal_secret( + b32decode(item["lease_secret"]), + b32decode(item["storage_index"]), + b32decode(item["tubid"]), + )) + assert derived == item["expected"] , \ + "Test vector {} failed: {} (expected) != {} (derived)".format( + n, + item["expected"], + derived, + ) + print("{} test vectors validated".format(len(test_vector))) + +test() +demo() From ee224305b7a64f071103d387a7eb646f197710b7 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Tue, 7 Sep 2021 13:37:12 -0400 Subject: [PATCH 14/14] link to the reference implementation --- docs/specifications/lease.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/specifications/lease.rst b/docs/specifications/lease.rst index 8fc7c42eb..16adef0a7 100644 --- a/docs/specifications/lease.rst +++ b/docs/specifications/lease.rst @@ -44,6 +44,12 @@ The scheme in use in Tahoe-LAFS as of 1.16.0 is as follows. * The **peer id** is the **base32 encoding** of the SHA1 digest of the server's x509 certificate. * The **renewal secret** is the **sha256d tagged pair digest** of (**bucket renewal tag**, **file renewal secret**, **peer id**). +A reference implementation is available. + +.. literalinclude:: derive_renewal_secret.py + :language: python + :linenos: + Cancel Secrets --------------