DISCLAIMER: None of the new URIs in this post should be considered final.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
I’ve been thinking about options for using public key infrastructure (PKI) and web of trust (WOT) concepts in the Tin Can API/Experience API to support a number of important scenarios. Spurred by a conversation with @ellenm1 I’ve put together this blog post.
There are numerous possible applications of PKI in the API, such as encrypted payloads, but I think the biggest likely applications relate to cryptographic signing. PKI is asymmetric: the person or system that needs to sign things has a secret key that they use to do it, and anyone with the public key can verify that the content has not been changed and was signed by exactly that secret key. This can be very useful, if they know about the secret key and trust the owner.
I’ve come up with three use cases for verifiable cryptographic signatures with Tin Can:
- verifying the original sender and form of a statement
- given a trusted exchange between the original sender and the receiving LRS, a chain of custody appears and can be maintained even as the statement is passed between loosely coupled LRSs under certain constraints
- additionally, if someone wants to certify the content of a statement, they can issue an additional statement to do so
Verifying the Original Sender
When a system creates a statement, it can sign a normalized form of the statement, and place the signature in the context extensions before sending, something like below:
1 2 3 4 5 6 7 8 9 10 11 12 13
This approach requires no cooperation on the part of the receiving LRS, since once the statement arrives at the LRS it is kept immutable, so a retriever can be sure that, if it gets a signed statement from an LRS, it received it in the signed form (to the extent it trusts the LRS at all). A badly behaved system might sign things as created that it didn’t create, but that behavior would be quickly noticed and the signing identity treated as untrusted.
In fact, a signed statement such as this is only useful to the extent the receiving system trusts the signer, and by default there’s no trust. With just a signed statement, establishing trust needs to happen out of band, perhaps by standard Web of Trust mechanisms or just direct assignment. An LRS could provide an administrator with a list of unknown signers for the administrator to investigate. There’s another option, though, the…
Chain of Custody
An LRS can initiate a chain of custody when it receives a signed statement. Layers of the chain of custody are nested, so that when normalizing to check a given layer, you cut off the lower levels and verify all the levels above as well as the rest of the statement content.
The beginning of a chain of custody looks something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
All the signers of a chain of custody (distinct from the creator’s signature) should be LRSs. As a statement is passed from LRS to LRS (that some of understand chains of custody), the chain is maintained, so that the content remains the same as when it truly originated can be reasonably trusted under some conditions, even if the verifying system is not aware of the creator’s identity.
When an LRS fetches a statement from another and finds a chain of custody signature, if the LRS has at least minimal trust (out of band) of the signer of any link of the chain of custody, it may extend the chain at the last trusted link. Yes, this does mean modifying the statement, but it’s a modification in an extension in the vein of authority changing as the statement changes hands, and so acceptable.
This means that a client receiving a statement can, if it trusts any link in the chain, be reasonably sure that the statement originated in the form given. However, that’s not a very big guarantee – a verifiably issued statement is not necessarily good and accurate, and it would be useful to have a verifiable, portable way to say that about statements, a statement to…
Certify the Content of a Statement
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
It’s possible to talk about other statements using statements. In this case, a statement has been made about another statement, using StatementRef along with a signature to verify the contents of the target statement. Then, this statement is signed, so that its contents cannot be messed with. Any client receiving this statement, who trusts the signer, has strong reason to believe the certified statement’s contents. This sort of verifiability will be important for exchanging especially important and sensitive data.
PKI is an extremely useful technology, and I’m sure signing will see other uses with statements, such as simple trusted source bulk statement exchange across untrusted intermediaries. If you’ve got ideas, I’d love to hear them in the comments.
A key part of all this is statement normalization. It must be possible for all parties, seeing just the statement, to agree on what the normalized form of the statement is. In rough outline, normalizing a statement should include: deleting the stored time and authority, serializing the JSON in a particular way, requiring the ID and timestamp to already be set, requiring a particular format of the timestamp, and so forth. It is not simple, but since it will be a very exactly specified profile, it will not be hard to meet.
Another consideration is that private keys MUST be kept private for all this to work. Anyone who exposes, however briefly, a private key has made that private key completely useless. This means signing must always take place on the server side.