|
| 1 | +# RFC: Identity Proofs |
| 2 | + |
| 3 | +* Author: @kim |
| 4 | +* Date: 2021-02-23 |
| 5 | +* Status: draft |
| 6 | +* Community discussion: n/a |
| 7 | + |
| 8 | +## Motivation |
| 9 | + |
| 10 | +A `radicle-link` [identity][ids] is a hash-linked sequence of signed statements |
| 11 | +about public-key delegations: if an entry in this sequence is found to conform |
| 12 | +to a quorum rule of cryptographic signatures, the set of keys it delegates to |
| 13 | +can be considered trustworthy _iff_ the previous set was. Yet, how can we trust |
| 14 | +the initial set of keys in this chain? |
| 15 | + |
| 16 | +## Overview |
| 17 | + |
| 18 | +We consider it impractical for most participants in the `radicle-link` network |
| 19 | +to exchange public keys out-of-band, given a pattern of casual interaction with |
| 20 | +others. While the protocol mandates connections between participants, similar to |
| 21 | +"following" relationships found in social media, we thus consider it |
| 22 | +insufficient to infer a [web of trust][wot] from those relationships. |
| 23 | + |
| 24 | +To lift the requirement for physical authenticity checks, but still increase |
| 25 | +confidence of a given public key being associated with a particular person, |
| 26 | +[keybase] have popularised a scheme dubbed "Social Proofs": a statement claiming |
| 27 | +ownership of a particular account on a social media site is written to the |
| 28 | +keybase "sigchain" (which has properties similar to a `radicle-link` identity). |
| 29 | +This claim (implying also the history of the sigchain) is signed using a key |
| 30 | +currently valid according to the chain, and the signature (along with the public |
| 31 | +key) is stored at the social media site. To verify the claim, the signature is |
| 32 | +retrieved from the social media site in such a way that it could _plausibly_ |
| 33 | +only be created by the owner of the claimed account. If both the sigchain |
| 34 | +integrity and the claim signature can be verified, the association is proven. |
| 35 | + |
| 36 | +This mechanism can be considered a practical application of the [Turing |
| 37 | +test][tt]: even though it can not be proven beyond doubt that the account is |
| 38 | +indeed associated with a real person, the evidence of others accepting it as |
| 39 | +such, as well as conversational behaviour, can increase the confidence in the |
| 40 | +authenticity of the online persona. Because key and account ownership at a given |
| 41 | +point in time can be cryptographically verified, this confidence can be extended |
| 42 | +to the proving side. |
| 43 | + |
| 44 | +We conclude that this mechanism would be a good fit for `radicle-link` (due to |
| 45 | +the similarities), and a desirable feature of applications built on top of it. |
| 46 | + |
| 47 | +The following sections describe how such claims shall be stored in the identity |
| 48 | +payload of a `radicle-link` identity, how to obtain a publishable proof, and how |
| 49 | +to verify it. |
| 50 | + |
| 51 | +## Claims |
| 52 | + |
| 53 | +Claims can only be made by identities of kind `Person`, and claim a single |
| 54 | +external account identifier. They are introduced by defining a new payload type, |
| 55 | +identified by the URL: |
| 56 | + |
| 57 | + https://radicle.xyz/link/claim/v1 |
| 58 | + |
| 59 | +The shape of the JSON structure is: |
| 60 | + |
| 61 | +```json |
| 62 | +{ |
| 63 | + "SERVICE": { |
| 64 | + "account": "STRING" |
| 65 | + "expiration": { |
| 66 | + "created": INTEGER, |
| 67 | + "expires": INTEGER |
| 68 | + }, |
| 69 | + "proof": "URL" |
| 70 | + } |
| 71 | +} |
| 72 | +``` |
| 73 | + |
| 74 | +Where the fields denote: |
| 75 | + |
| 76 | +* `SERVICE` |
| 77 | + |
| 78 | + A conventional identifier of the external service, e.g. "github", "twitter", |
| 79 | + "radicle-ethereum" |
| 80 | + |
| 81 | +* `account` |
| 82 | + |
| 83 | + The unique account identifier within the service, using the service-specific |
| 84 | + canonical string representation e.g. "kim", "0x32be343b94f860124dc4fee278fdcbd38c102d88". |
| 85 | + |
| 86 | +* `expiration` (optional) |
| 87 | + * `created` |
| 88 | + |
| 89 | + Creation timestamp of the claim, in seconds since 1970-01-01T00:00:00Z. |
| 90 | + |
| 91 | + * `expires` |
| 92 | + |
| 93 | + Seconds relative to `created`, after which the claim should no longer be |
| 94 | + considered. |
| 95 | + |
| 96 | +* `proof` (optional) |
| 97 | + |
| 98 | + A URL to assist verification tooling in retrieving the proof from the external |
| 99 | + system. This is mainly a convenience, and obviously requires creation of a new |
| 100 | + revision after the fact. |
| 101 | + |
| 102 | +## Proof Generation |
| 103 | + |
| 104 | +The above claim payload is committed to the identity history as a new revision. |
| 105 | +Technically, this revision needs to be approved by all key delegations for |
| 106 | +verification to pass later on, but since we assume that eligible keys are held |
| 107 | +by the same person, it may be acceptable to publish the proof right away for |
| 108 | +user experience reasons. |
| 109 | + |
| 110 | +The actual proof consists of the following tuple: |
| 111 | + |
| 112 | + (root, revision, public-key, signature) |
| 113 | + |
| 114 | +Note that the "git" protocol specifier of `radicle-link` URNs is implied, that |
| 115 | +is, future version MUST treat the absence of a disambiguating value as denoting |
| 116 | +"git". |
| 117 | + |
| 118 | +The values `root`, `revision`, and `public-key` are specified in |
| 119 | +[identities][ids], and it is RECOMMENDED to follow the serialisation formats |
| 120 | +devised there. `signature` is the Ed25519 signature over `revision`, in much the |
| 121 | +same way as the actual revision is signed. All values can thus be obtained by |
| 122 | +inspecting the identity storage. |
| 123 | + |
| 124 | +It is beyond the scope of this document to devise the exact external format to |
| 125 | +serialise the tuple into, as this is expected to vary from service to service. |
| 126 | + |
| 127 | +## Revocation |
| 128 | + |
| 129 | +A claim can be revoked by creating a new identity revision which simply does not |
| 130 | +contain the claim payload. Likewise, a later claim describing the same `SERVICE` |
| 131 | +invalidates an earlier one. |
| 132 | + |
| 133 | +## Verification |
| 134 | + |
| 135 | +Inputs: the 4-tuple as specified [above](#proof-generation), and the `SERVICE` |
| 136 | +as inferred from the source it was retrieved from. |
| 137 | + |
| 138 | +1. Given the 4-tuple specified, it is first verified that the signature is valid |
| 139 | + for the given `revision` and `public-key`. |
| 140 | + |
| 141 | +2. If it is, the identity history needs to be resolved from local storage, or |
| 142 | + the network. |
| 143 | + |
| 144 | + Using `git` storage, the history tip should be located at |
| 145 | + |
| 146 | + refs/namespaces/<root>/refs/remotes/<public-key>/rad/id |
| 147 | + |
| 148 | + substituting `<root>` and `<public-key>` with their respective encodings as |
| 149 | + defined in [`Identities`][ids]. |
| 150 | + |
| 151 | +3. If the history tip could be resolved, the identity MUST be verified as per |
| 152 | + [`Identities`][ids]. If this fails, the proof is rejected. |
| 153 | + |
| 154 | +4. If the identity could be verified, the identity document is read from its |
| 155 | + latest valid tip (recall that this is not necessarily the same as what the |
| 156 | + ref points to). The proof is rejected if one of the following is true: |
| 157 | + |
| 158 | + 4.1 `root` does not match |
| 159 | + 4.2 the document does not contain a claim for `SERVICE` |
| 160 | + 4.3 the document contains a claim for `SERVICE`, has an `expiration`, and |
| 161 | + `expiration.expires` is smaller than `time() - expiration.created` |
| 162 | + |
| 163 | +5. Lastly, the identity history is walked backwards until `revision` is found |
| 164 | + (or else, the proof is rejected). The proof is accepted _iff_ all of the |
| 165 | + following are true: |
| 166 | + |
| 167 | + 5.1 the document's `delegations` at `revision` contain `public-key` |
| 168 | + 5.2 the document at `revision` contains a claim for `SERVICE`, and the claim |
| 169 | + is not expired as described in 4.3 |
| 170 | + |
| 171 | +Note that steps 3.-5. can be optimised by persisting verification results, or by |
| 172 | +adding an additional accumulator to the verification fold which yields the |
| 173 | +targeted `revision`. |
| 174 | + |
| 175 | +## Discussion |
| 176 | + |
| 177 | +The inclusion of the `revision` in the proof allows to assert that `root` is |
| 178 | +indeed an ancestor, which opens up another way to detect "forks" of the identity |
| 179 | +history: due to the peer-to-peer nature of the `radicle-link` network, it is |
| 180 | +vulnerable to attacks which involve withholding data from other participants, in |
| 181 | +which case a fork may go unnoticed. |
| 182 | + |
| 183 | +It should be noted, however, that refreshing the proof from time to time in |
| 184 | +order to ensure freshness of the data retrieved through `radicle-link` is not |
| 185 | +always practicable. |
| 186 | + |
| 187 | +In order to prove that the(ir own) server is not lying by omission, Keybase |
| 188 | +[anchors a merkle root][keybase-stellar] on a blockchain, which includes all |
| 189 | +sigchains registered in the Keybase directory. Because `radicle-link` does not |
| 190 | +have such a central directory, this approach could only be applied to a partial |
| 191 | +view of the network. |
| 192 | + |
| 193 | +While conceivable that, given the right incentives, such a directory service |
| 194 | +could be operated independently (similar to what the [ceramic] network devises), |
| 195 | +it is unclear what value blockchain anchors of individual identities have, given |
| 196 | +that transaction costs discourage frequent updates. |
| 197 | + |
| 198 | +It has also been proposed to employ a mutual attestation scheme with a |
| 199 | +blockchain (namely the [radicle-contracts]), but we haven't been able to |
| 200 | +convince ourselves that this is practical (or even beneficial) because: |
| 201 | + |
| 202 | +1. the verification obligation becomes more complex |
| 203 | +2. the creation procedure becomes more complex |
| 204 | +3. it is unclear what to do in case of conflicting statements (in the presence |
| 205 | + of revocations) |
| 206 | +4. the lying by omission problem is not solved, due to the disincentive to |
| 207 | + refresh proofs |
| 208 | + |
| 209 | +We thus RECOMMEND to either: |
| 210 | + |
| 211 | +* incentivise bulk anchoring |
| 212 | + |
| 213 | +* integrate with an existing bulk anchoring service |
| 214 | + |
| 215 | + Note that in this case the external service becomes authoritative, and no |
| 216 | + extension to the `radicle-link` protocol as described here is necessary. |
| 217 | + |
| 218 | +* treat "web3"- like any other web-service |
| 219 | + |
| 220 | + This may entail finding ways to reduce costs, for example by storing the proof |
| 221 | + payload in transaction logs (which can be verified separately). |
| 222 | + |
| 223 | +--- |
| 224 | + |
| 225 | +[ids]: ../spec/identities.md |
| 226 | +[wot]: https://en.wikipedia.org/wiki/Web_of_trust |
| 227 | +[keybase]: https://keybase.io |
| 228 | +[tt]: https://en.wikipedia.org/wiki/Turing_test |
| 229 | +[keybase-stellar]: https://book.keybase.io/docs/server/stellar |
| 230 | +[ceramic]: https://github.com/ceramicnetwork/ceramic/blob/master/SPECIFICATION.md#blockchain-anchoring |
| 231 | +[radicle-contracts]: https://github.com/radicle-dev/radicle-contracts |
0 commit comments