A Web of String: PKI Signatures and the Tin Can API

| Comments

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
{
  "actor": {
    "mbox": "mailto:russell.duhon@saltbox.com",
    "name": "Russell Duhon"
  },
  "verb": {
    "id": "http://adlnet.gov/expapi/verbs/created",
    "display": {
      "en": "created"
    }
  },
  "object": {
    "id": "http://blog.saltbox.com/blog/2012/12/31/a-web-of-string-pki-signatures-and-the-tin-can-api/",
    "definition": {
      "name": {
        "en": "A Web of String: PKI Signatures and the Tin Can API"
      }
    }
  },
  "context": {
    "contextActivities": {
      "parent": {
        "id": "http://blog.saltbox.com/"
      }
    },
    "extensions": {
      "http://saltbox.com/extensions/wot#custody": {
        "created": {
          "signature": "-----BEGIN PGP SIGNATURE-----
          Version: GnuPG v1.4.11 (GNU/Linux)

          iQEcBAABAgAGBQJQ4eyyAAoJEE7mDEdQvcuSQhIIAL0f56yyFDa/G3rpzDBeJbYy
          kgJPDrKx9RneAObaUVwvi6sVB/n59W/wbR9BtSZWj5bRwX+qehYWlHcI5ShbR+MY
          LwzCE0V4FyStgah7X3+qYWLyi2NNKdBilVBmtFFXzTyCxK7n0l5iPQNNC692TGMT
          KEieFhJXp+nhMr9gFifDSKqqrDKpXCcLfHfU4QLKdn4iJqtAZSsCZ8zoXyokurTN
          4WECm+dp0NlwqOWlUPvaqKWHL62olHU7w6eDjIPRW49CWRqKnI4irdmiGb6DoB/7
          U3FofqcpH/CDW8TUq6EWXwoVKmyIuSyhNHsNDKoWYp1R5kCeidMGl8uSqdy2g4k=
          =aAY8
          -----END PGP SIGNATURE-----",
          "accepted": {
            "signature": "-----BEGIN PGP SIGNATURE-----
            Version: GnuPG v1.4.11 (GNU/Linux)

            iQEcBAABAgAGBQJQ4fDrAAoJEE7mDEdQvcuSCw4H/1+aBfgYvo4kis9BXy3t529E
            /ufu8Y++ZnqwMHVCmUTLMheVndDC5zkp+GhLWfZ/8pg69IxJIG4gqIVhKfC8l5gW
            VluGsO+4lqiyvyQ3m94ozbjZLRduG7je9Gb6ZzY53QCBc0mOcUlcvkNAN+XyChga
            wyenJsgZkSLFUH63y4CpSi5f27Vb9b0qkGovgvL0dC1kbGwrzKRmJzxV7VpSdwDK
            r62vuyoqRk66mC9WeXbi5TRfTPhBdHK/9nnZxTWFxxKQkt2mL04HtinFL5eN6jec
            XC/mX6tkDUs2WuY0SxbFTRzOv237KAHsUDmHgvWmPi58aL8oVpWl1J+aRQm6kus=
            =YkPf
            -----END PGP SIGNATURE-----",
            "lrs": "http://lrs.example.com/XAPI/",
            "trust": "marginal"
          }
        }
      }
    }
  }
}

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
"created": {
  "signature": "-----BEGIN PGP SIGNATURE-----
  Version: GnuPG v1.4.11 (GNU/Linux)

  iQEcBAABAgAGBQJQ4eyyAAoJEE7mDEdQvcuSQhIIAL0f56yyFDa/G3rpzDBeJbYy
  kgJPDrKx9RneAObaUVwvi6sVB/n59W/wbR9BtSZWj5bRwX+qehYWlHcI5ShbR+MY
  LwzCE0V4FyStgah7X3+qYWLyi2NNKdBilVBmtFFXzTyCxK7n0l5iPQNNC692TGMT
  KEieFhJXp+nhMr9gFifDSKqqrDKpXCcLfHfU4QLKdn4iJqtAZSsCZ8zoXyokurTN
  4WECm+dp0NlwqOWlUPvaqKWHL62olHU7w6eDjIPRW49CWRqKnI4irdmiGb6DoB/7
  U3FofqcpH/CDW8TUq6EWXwoVKmyIuSyhNHsNDKoWYp1R5kCeidMGl8uSqdy2g4k=
  =aAY8
  -----END PGP SIGNATURE-----"
}

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
"created": {
  "signature": "-----BEGIN PGP SIGNATURE-----
  Version: GnuPG v1.4.11 (GNU/Linux)

  iQEcBAABAgAGBQJQ4eyyAAoJEE7mDEdQvcuSQhIIAL0f56yyFDa/G3rpzDBeJbYy
  kgJPDrKx9RneAObaUVwvi6sVB/n59W/wbR9BtSZWj5bRwX+qehYWlHcI5ShbR+MY
  LwzCE0V4FyStgah7X3+qYWLyi2NNKdBilVBmtFFXzTyCxK7n0l5iPQNNC692TGMT
  KEieFhJXp+nhMr9gFifDSKqqrDKpXCcLfHfU4QLKdn4iJqtAZSsCZ8zoXyokurTN
  4WECm+dp0NlwqOWlUPvaqKWHL62olHU7w6eDjIPRW49CWRqKnI4irdmiGb6DoB/7
  U3FofqcpH/CDW8TUq6EWXwoVKmyIuSyhNHsNDKoWYp1R5kCeidMGl8uSqdy2g4k=
  =aAY8
  -----END PGP SIGNATURE-----",
  "accepted": {
    "signature": "-----BEGIN PGP SIGNATURE-----
    Version: GnuPG v1.4.11 (GNU/Linux)

    iQEcBAABAgAGBQJQ4fDrAAoJEE7mDEdQvcuSCw4H/1+aBfgYvo4kis9BXy3t529E
    /ufu8Y++ZnqwMHVCmUTLMheVndDC5zkp+GhLWfZ/8pg69IxJIG4gqIVhKfC8l5gW
    VluGsO+4lqiyvyQ3m94ozbjZLRduG7je9Gb6ZzY53QCBc0mOcUlcvkNAN+XyChga
    wyenJsgZkSLFUH63y4CpSi5f27Vb9b0qkGovgvL0dC1kbGwrzKRmJzxV7VpSdwDK
    r62vuyoqRk66mC9WeXbi5TRfTPhBdHK/9nnZxTWFxxKQkt2mL04HtinFL5eN6jec
    XC/mX6tkDUs2WuY0SxbFTRzOv237KAHsUDmHgvWmPi58aL8oVpWl1J+aRQm6kus=
    =YkPf
    -----END PGP SIGNATURE-----",
    "lrs": "http://lrs.example.com/XAPI/",
    "trust": "marginal"
  }
}

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
{
  "actor": {
    "mbox": "mailto:russell.duhon@saltbox.com",
    "name": "Russell Duhon"
  },
  "verb": {
    "id": "http://saltbox.com/verbs/wot#certifies",
    "display": {
      "en": "certifies"
    }
  },
  "object": {
    "id": "55555555-5555-5555-5555-555555555555",
    "objectType": "StatementRef",
    "http://saltbox.com/extensions/wot#signature":
        "-----BEGIN PGP SIGNATURE-----
        Version: GnuPG v1.4.11 (GNU/Linux)

        iQEcBAABAgAGBQJQ4eyyAAoJEE7mDEdQvcuSQhIIAL0f56yyFDa/G3rpzDBeJbYy
        kgJPDrKx9RneAObaUVwvi6sVB/n59W/wbR9BtSZWj5bRwX+qehYWlHcI5ShbR+MY
        LwzCE0V4FyStgah7X3+qYWLyi2NNKdBilVBmtFFXzTyCxK7n0l5iPQNNC692TGMT
        KEieFhJXp+nhMr9gFifDSKqqrDKpXCcLfHfU4QLKdn4iJqtAZSsCZ8zoXyokurTN
        4WECm+dp0NlwqOWlUPvaqKWHL62olHU7w6eDjIPRW49CWRqKnI4irdmiGb6DoB/7
        U3FofqcpH/CDW8TUq6EWXwoVKmyIuSyhNHsNDKoWYp1R5kCeidMGl8uSqdy2g4k=
        =aAY8
        -----END PGP SIGNATURE-----"
  },
  "result": {
    "response": "I checked this statement out with the instructor",
    "extensions": {
      "http://saltbox.com/extensions/wot#signature":
        "-----BEGIN PGP SIGNATURE-----
        Version: GnuPG v1.4.11 (GNU/Linux)

        kgJPDrKx9RneAObaUVwvi6sVB/n59W/wbR9BtSZWj5bRwX+qehYWlHcI5ShbR+MY
        iQEcBAABAgAGBQJQ4eyyAAoJEE7mDEdQvcuSQhIIAL0f56yyFDa/G3rpzDBeJbYy
        LwzCE0V4FyStgah7X3+qYWLyi2NNKdBilVBmtFFXzTyCxK7n0l5iPQNNC692TGMT
        KEieFhJXp+nhMr9gFifDSKqqrDKpXCcLfHfU4QLKdn4iJqtAZSsCZ8zoXyokurTN
        4WECm+dp0NlwqOWlUPvaqKWHL62olHU7w6eDjIPRW49CWRqKnI4irdmiGb6DoB/7
        U3FofqcpH/CDW8TUq6EWXwoVKmyIuSyhNHsNDKoWYp1R5kCeidMGl8uSqdy2g4k=
        =aAY8
        -----END PGP SIGNATURE-----"
    }
  }
}

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.

Other Uses

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.

General Notes

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.

Comments