Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FCP HIPE: Wire Messages (formerly AMES) #43

Merged
merged 61 commits into from
Jan 25, 2019
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
659d3cb
added description of AnonCrypt and AuthCrypt
kdenhartog Jul 13, 2018
af5e21e
reversed sender and random priv_key in step 3
kdenhartog Aug 6, 2018
1018805
Merge branch 'master' of github.com:hyperledger/indy-hipe
kdenhartog Aug 6, 2018
345c5c1
Initial version of Wire Message HIPE
swcurran Aug 17, 2018
876092b
Clarifying the requirements of the unpack() when using the authcrypt
swcurran Aug 20, 2018
8fcce07
Args to pack() must include myPubKey when AuthCrypt being used
swcurran Aug 20, 2018
d5e5001
Merge branch 'master' of github.com:swcurran/indy-hipe
kdenhartog Aug 21, 2018
f23d11e
Merge branch 'wireMessages' of github.com:swcurran/indy-hipe
kdenhartog Aug 21, 2018
15baaca
fixed prior art sections to match implementation details
kdenhartog Sep 17, 2018
4178bcc
Merge branch 'master' of github.com:hyperledger/indy-hipe
kdenhartog Sep 18, 2018
1b7f276
AMES (aka JWMs) HIPE
kdenhartog Sep 18, 2018
6942efa
updated summary
kdenhartog Sep 18, 2018
1c39f47
fixed location and removed non-accepted hipes
kdenhartog Sep 18, 2018
32681b5
updated unpack API
kdenhartog Sep 18, 2018
3035b33
separated Route Table work - will go in separate HIPE
kdenhartog Sep 20, 2018
a2d5214
Removed route table docs, will move to separate hipe
kdenhartog Sep 20, 2018
db5de9a
moved hipe to text directory only
kdenhartog Sep 20, 2018
08ed97d
updated motivations; removed other mention of route tables
kdenhartog Sep 20, 2018
36a6dcf
Added link to code to track progress
kdenhartog Sep 20, 2018
1255ed8
updated HIPE to fit API format
kdenhartog Oct 1, 2018
1d0486f
updated HIPE to align with code
kdenhartog Oct 1, 2018
96bd569
Merge branch 'master' into AMES
kdenhartog Oct 1, 2018
fbf8e09
Added additonal questions section
kdenhartog Oct 2, 2018
803b4dd
merge master
kdenhartog Oct 2, 2018
e6ac023
added additional descriptions which now support encrypting headers
kdenhartog Oct 3, 2018
df12b40
reformat to find sections easier
kdenhartog Oct 3, 2018
2293dab
updated to detail new changes needed for restructed data and encrypti…
kdenhartog Oct 4, 2018
4bb7c8c
updated to reflect code
kdenhartog Oct 6, 2018
ef9a8ef
updated cek to e_cek in json
kdenhartog Oct 6, 2018
d6f154d
updated hipe to align with Wire message HIPE and Mike Lodder's format
kdenhartog Nov 12, 2018
359ff79
Merge branch 'master' into AMES
kdenhartog Nov 12, 2018
e3e5fa7
additional updates - added more authors
kdenhartog Nov 12, 2018
5987dc7
Merge branch 'AMES' of github.com:kdenhartog/indy-hipe into AMES
kdenhartog Nov 12, 2018
66e7ec0
updated HIPE for new definitions alignment
kdenhartog Nov 30, 2018
fb50931
added additional details from original wire message HIPE
kdenhartog Nov 30, 2018
99b83b3
ready for FCP
kdenhartog Dec 3, 2018
fa61b61
fixed minor details
kdenhartog Dec 4, 2018
44c1f31
Merge branch 'master' into AMES
kdenhartog Dec 4, 2018
3357b5a
updates from FCP comments
kdenhartog Dec 7, 2018
f167dc0
Merge branch 'AMES' of github.com:kdenhartog/indy-hipe into AMES
kdenhartog Dec 7, 2018
0999bc8
fix typo
kdenhartog Dec 7, 2018
71a3d36
updated feature branch
kdenhartog Dec 7, 2018
bc7e4be
updated to match indy-SDK implementation
kdenhartog Dec 11, 2018
c63f0ab
removed algorithms that won't be in the IndySDK implementation
kdenhartog Dec 11, 2018
215fe58
added future changes functionality
kdenhartog Dec 11, 2018
33d7a60
updated HIPE to match code and add algorithm for serializing
kdenhartog Jan 7, 2019
f9167c4
Merge branch 'master' into AMES
kdenhartog Jan 7, 2019
fd3d8cc
tweaked to clarify in response to telegramsam comments
kdenhartog Jan 7, 2019
d777fb2
Merge branch 'AMES' of github.com:kdenhartog/indy-hipe into AMES
kdenhartog Jan 7, 2019
c955e3a
Merge branch 'master' into AMES
kdenhartog Jan 7, 2019
0b09c02
added examples of json
kdenhartog Jan 8, 2019
06739e8
Merge branch 'AMES' of github.com:kdenhartog/indy-hipe into AMES
kdenhartog Jan 8, 2019
1db7d6e
added additional details to reflect @swcurran comments
kdenhartog Jan 11, 2019
ebc2339
aligned HIPE with final implementation
kdenhartog Jan 22, 2019
8e7dfd2
added recipient_verkey
kdenhartog Jan 23, 2019
9d135e2
updated unpack algorithm details
kdenhartog Jan 23, 2019
cc5fdb6
fix typo
kdenhartog Jan 23, 2019
f586b31
updated docs
kdenhartog Jan 23, 2019
bb55f79
added changes based on new comments
kdenhartog Jan 23, 2019
da6c1fc
Explain for non-Indy audience
dhh1128 Jan 25, 2019
36a92cc
Merge pull request #3 from dhh1128/ames
kdenhartog Jan 25, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 135 additions & 0 deletions AMES/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
- Name: AMES
- Author: Kyle Den Hartog
- Start Date: 2018-07-10 (approximate, backdated)
- HIPE PR: (leave this empty)
- Jira Issue: (leave this empty)

# AMES
[summary]: #summary

Agent Message Encryption Serialization (AMES) are intended to be a standardized format that allows for all necessary information to encrypt, decrypt, and perform routing can be found in the message while remaining asynchronous. In this HIPE we'll describe the API of the Pack and Unpack functions as well as the route tables API. This HIPE does not currently go into detail about how to use the API to prevent data exposure, but should be updated to detail this before being accepted.

# Motivation
[motivation]: #motivation

Many aspects of this hipe have been derived from [JSON Web Encryption - RFC 7516](https://tools.ietf.org/html/rfc7516). It has diverged from this spec due to assumptions around TLS and encryption schemes, as well as to focus on the DIDs usecase.

# Technicals

## Serialization Examples

### JSON Serialization
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this an example of Authcrypt, or Anoncrypt, or both? How do they differ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Authcrypt and Anoncrypt only differ 1 but significant way. You're right this should be documented somewhere. I briefly talk about them in this HIPE

Anoncrypt generates an ephemeral key pair, uses the private key with the receivers relationship public key to perform Elliptic Curve Integration Encryption Scheme (ECIES). This means the receiver will not have a cryptographic method to verify who sent it. This offers forward secrecy for the sender. This is used when the sender's public key is not known by the receiver yet but the receivers key is or can be known.

Authcrypt instead of using an ephemeral key pair, uses the senders relationship key pair with the receivers relationship key pair. No forward secrecy is offered. The receiver has a cryptographic method to verify the sender.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm splitting the API for this now. I do mention this in the API aspects below, but to make the API cleaner and to stay consistent with the way we anon/authcrypt now I'm planning to separate.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I understand AuthCrypt and AnonCrypt. I was looking for how Kyle was demonstrating which.


```
{
"recipients" : [
{
"header" : {
"typ" : "x-b64nacl"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

x-b64nacl was intended as a temporary typ value until we defined this format. We should replace this with ames or something to designate this format. Same for alg.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with changing typ. I'm not sure about changing alg. These are intended to show how the cek is being encrypted. I suspect when I do the refactors to encrypt headers (in order to protect to/from keys) then this information will need to be changed as well.

"alg" : "x-auth",
"to" : "<recipient_verkey>",
"from" : "<sender_verkey>",
},
"cek" : <encrypted symmetrical key to unlock ciphertext>
},
{
"header" : {
"typ" : "x-b64nacl",
"alg" : "x-auth",
"to" : "<recipient_verkey>",
"from" : "<sender_verkey>"
},
"cek" : <encrypted symmetrical key to unlock ciphertext>
}
],
"enc" : "xsalsa20poly1305",
"iv" : <Nonce>,
"ciphertext" : <message ciphertext>,
"tag" : <Authentication Tag from NaCl>
}
```

### Compact Serialization
` <header_json> . <content_encryption_key> . <iv> . <ciphertext> . <tag> `
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if we adjust the header format to include a list of recipients. That way, multi-recipient messages can be also presented in compact form.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consolidating to a single serialization format would make the code much cleaner. Both are implemented right now. The format outputted is dependent on the number of keys passed into pack_message at the moment.


Each of these are base64URL encoded strings which are dot (.) separated.
The base64URL decoded header json will adhere to the following format:

```
{
"typ" : "x-b64nacl",
"alg" : "x-anon",
"enc" : "xsalsa20poly1305",
"kid" : "<recipient_verkey>",
"jwk" : "<sender_verkey>"
}
```

An example looks like the following:
`
eyJ0eXAiOiJ4LWI2NG5hY2wiLCJhbGciOiJ4LWF1dGgiLCJlbmMiOiJ4c2Fsc2EyMHBvbHkxMzA1Iiwia2lkIjoiQzVxMk1EbWRHMjZuVnc3M3loVmhkeiIsImp3ayI6IkVGYkM0V3hEWG1GZkhveW43bUNCbksifQ==.ZW5jcnlwdGVkX2tleQ==.RkFLRV9JVlRPVEVTVEpXTVNFUklBTElaRQ==.dW5lbmNyeXB0ZWQgdGV4dCB3aGljaCB3b3VsZCBub3JtYWxseSBiZSBlbmNyeXB0ZWQgYWxyZWFkeQ==.RkFLRV9UQUdUT1RFU1RKV01TRVJJQUxJWkU=
`

which would decode to the following data in tuple form hence the parentheses on the outer most layer:
```
(
{
"typ":"x-b64nacl",
"alg":"x-auth",
"enc":"xsalsa20poly1305",
"kid":"C5q2MDmdG26nVw73yhVhdz",
"jwk":"EFbC4WxDXmFfHoyn7mCBnK"
},
"encrypted_key",
"FAKE_IVTOTESTJWMSERIALIZE",
"unencrypted text which would normally be encrypted already"
"FAKE_TAGTOTESTJWMSERIALIZE"
)
```

## Additional IndySDK APIs

### unpack_message(AMES, my_vk, wallet_handle) -> plaintext message :
The unpack function is used to decrypt a message on the receiver side. It will output the plaintext of the corresponding pack_message if the verkey provided is found within the header. This works for both compact serializations and for JSON serializations.

The parameters should be used in this way:

AMES: This should pass in a json string that follows one of the two serializations provided above.

my_vk: This should be the verkey that you wish to use to decrypt the message.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the message itself contain a clue as to which verkey is needed to decrypt the message? This would be useful (if not a reason against it), and we should either modify the unpack message to not need this argument, or provide a method that would inspect the message and inform about expected keys prior to calling unpack.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been under the assumption that this was to be handled by a different layer. If there is a 1 -> 1 mapping of DID -> Key owned by agent it could be removed, but currently there's no way to do this because wallet lookups are dependent on verkeys rather than DIDs.


wallet_handle: This is the wallet that contains the key.

output: A decrypted message

### pack_message(plaintext, auth, recv_keys, wallet_handle, my_vk) -> JSON String:
The purpose of this function is to take in a message, encrypt the message for the keys specified and output a JSON string with one of the two serializations identified above.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a compact boolean argument to explicitly indicate compact or full encoding is more useful, rather than always going compact for one form and full for the other. If an invalid option is requested, return an error.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think consolidating to one format only would be a better approach rather than adding another boolean on to determine format. I do agree, that the number of keys determining the serialization format is confusing, but it's what I came up with as I was coding it. Definitely worth a refactor.


The parameters should be used in this way:

plaintext: This should be the message that is intended to be encrypted. It's required and should be of type String. The most common forms of messages that will be passed in here are json strings that follow the format of a particular message family.

auth: This is a boolean type that should be passed true if authcrypt should be used or false if anoncrypt should be used.

recv_keys: This is a list of verkeys passed as a string. If only 1 key is passed in, then a Compact serialization will be outputed. If > 1 keys are passed in then JSON serialization will be outputted from this function.

wallet_handle: this is the wallet_handle that contains the key used to encrypt the message (when using authcrypt). It is required even if using anoncrypt.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are both the wallet and the verkey needed here for authcrypt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the function knows which wallet to find the private key in. It's not necessary when anoncrypting because an ephemeral key is being generated on the fly.


my_vk: This is an optional parameter that must include a verkey as a string if auth is set to true (authcrypting). Otherwise, this must be set to none if anoncrypt is being used.

output: a string in the form of either JSON serialization or Compact serialization

## Routing Table
The routing table is specific to agents which will allow an agent to keep a persistent copy on disk to be able to lookup how to route a message. Questions that still need to be answered is how does the routing table get filled?

#### add_route(did_with_key_frag, endpoint, wallet_handle):
This adds a route to the table by taking the the DID#key and the endpoint and adding a record into the table. The did_with_key_frag parameter SHOULD match the format of the "to" field of the header in the JSON serialization. This function is expected to not return any data upon completion.

### lookup_route(did_with_key_frag, wallet_handle):
This function queries the routing table for an endpoint based upon the provided parameter. The pramater SHOULD match the "to" field of the header so that an incoming AMES to a routing agent can use the "to" field to lookup where it needs to route a message to. This function is expected to return an Endpoint as a string.

### remove_route(did_with_key_frag, wallet_handle):
This function removes a route (did#key, endpoint) from the routing table for an endpoint based upon the provided parameter. It's expected that this function will not return any data upon completion.

### update_route(DID#key, endpoint, wallet_handle):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method feels very redundant with add_route. One method that serves as an upsert feels like the right answer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I preferred separating them to make the code cleaner under the hood. The wallet is handling these two actions as separate as well, so I wanted to continue this pattern. Thanks, for coming in and taking a look at this. There's some good ideas in the suggested refactors.

This function updates a route entry in the routing table by comparing the DID#key and replacing the current endpoint with the endpoint parameter. It's expected that this function will not return any data upon completion.
133 changes: 133 additions & 0 deletions text/AMES/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
- Name: AMES
TelegramSam marked this conversation as resolved.
Show resolved Hide resolved
- Author: Kyle Den Hartog
- Start Date: 2018-07-10 (approximate, backdated)
- HIPE PR: (leave this empty)
- Jira Issue: (leave this empty)

# AMES
[summary]: #summary

Agent Message Encryption Serialization (AMES) are intended to be a standardized format that allows for all necessary information to encrypt, decrypt, and perform routing can be found in the message while remaining asynchronous. In this HIPE we'll describe the API of the Pack and Unpack functions as well as the route tables API. This HIPE does not currently go into detail about how to use the API to prevent data exposure, but should be updated to detail this before being accepted.

# Motivation
[motivation]: #motivation

Many aspects of this hipe have been derived from [JSON Web Encryption - RFC 7516](https://tools.ietf.org/html/rfc7516). It has diverged from this spec due to assumptions around TLS and encryption schemes, as well as to focus on the DIDs usecase.

# Technicals

## Serialization Examples

### JSON Serialization

```
{
"recipients" : [
{
"header" : {
"typ" : "x-b64nacl"
"alg" : "x-auth",
"to" : "<recipient_verkey>",
"from" : "<sender_verkey>",
},
"cek" : <encrypted symmetrical key to unlock ciphertext>
},
{
"header" : {
"typ" : "x-b64nacl",
"alg" : "x-auth",
"to" : "<recipient_verkey>",
"from" : "<sender_verkey>"
},
"cek" : <encrypted symmetrical key to unlock ciphertext>
}
],
"enc" : "xsalsa20poly1305",
"iv" : <Nonce>,
"ciphertext" : <message ciphertext>,
"tag" : <Authentication Tag from NaCl>
}
```

### Compact Serialization
` <header_json> . <content_encryption_key> . <iv> . <ciphertext> . <tag> `

Each of these are base64URL encoded strings which are dot (.) separated.
The base64URL decoded header json will adhere to the following format:

```
{
"typ" : "x-b64nacl",
"alg" : "x-anon",
"enc" : "xsalsa20poly1305",
"kid" : "<recipient_verkey>",
"jwk" : "<sender_verkey>"
}
```

An example looks like the following:
`
eyJ0eXAiOiJ4LWI2NG5hY2wiLCJhbGciOiJ4LWF1dGgiLCJlbmMiOiJ4c2Fsc2EyMHBvbHkxMzA1Iiwia2lkIjoiQzVxMk1EbWRHMjZuVnc3M3loVmhkeiIsImp3ayI6IkVGYkM0V3hEWG1GZkhveW43bUNCbksifQ==.ZW5jcnlwdGVkX2tleQ==.RkFLRV9JVlRPVEVTVEpXTVNFUklBTElaRQ==.dW5lbmNyeXB0ZWQgdGV4dCB3aGljaCB3b3VsZCBub3JtYWxseSBiZSBlbmNyeXB0ZWQgYWxyZWFkeQ==.RkFLRV9UQUdUT1RFU1RKV01TRVJJQUxJWkU=
`

which would decode to the following data in tuple form hence the parentheses on the outer most layer:
```
(
{
"typ":"x-b64nacl",
"alg":"x-auth",
"enc":"xsalsa20poly1305",
"kid":"C5q2MDmdG26nVw73yhVhdz",
"jwk":"EFbC4WxDXmFfHoyn7mCBnK"
},
"encrypted_key",
"FAKE_IVTOTESTJWMSERIALIZE",
"unencrypted text which would normally be encrypted already"
"FAKE_TAGTOTESTJWMSERIALIZE"
)
```

## Additional IndySDK APIs

### unpack_message(JWM, my_vk) -> plaintext message :
The unpack function is used to decrypt a message on the receiver side. It will output the plaintext of the corresponding pack_message if the verkey provided is found within the header. This works for both compact serializations and for JSON serializations.

The parameters should be used in this way:

JWM: This should pass in a json string that follows one of the two serializations provided above.

my_vk: This should be the verkey that you wish to use to decrypt the message.

output: A decrypted message

### pack_message(plaintext, auth, recv_keys, wallet_handle, my_vk) -> JSON String:
The purpose of this function is to take in a message, encrypt the message for the keys specified and output a JSON string with one of the two serializations identified above.

The parameters should be used in this way:

plaintext: This should be the message that is intended to be encrypted. It's required and should be of type String. The most common forms of messages that will be passed in here are json strings that follow the format of a particular message family.

auth: This is a boolean type that should be passed true if authcrypt should be used or false if anoncrypt should be used.

recv_keys: This is a list of verkeys passed as a string. If only 1 key is passed in, then a Compact serialization will be outputed. If > 1 keys are passed in then JSON serialization will be outputted from this function.

wallet_handle: this is the wallet_handle that contains the key used to encrypt the message (when using authcrypt). It is required even if using anoncrypt.

my_vk: This is an optional parameter that must include a verkey as a string if auth is set to true (authcrypting). Otherwise, this must be set to none if anoncrypt is being used.

output: a string in the form of either JSON serialization or Compact serialization

## Routing Table
The routing table is specific to agents which will allow an agent to keep a persistent copy on disk to be able to lookup how to route a message. Questions that still need to be answered is how does the routing table get filled?

#### add_route(did_with_key_frag, endpoint, wallet_handle):
This adds a route to the table by taking the the DID#key and the endpoint and adding a record into the table. The did_with_key_frag parameter SHOULD match the format of the "to" field of the header in the JSON serialization. This function is expected to not return any data upon completion.

### lookup_route(did_with_key_frag, wallet_handle):
This function queries the routing table for an endpoint based upon the provided parameter. The pramater SHOULD match the "to" field of the header so that an incoming AMES to a routing agent can use the "to" field to lookup where it needs to route a message to. This function is expected to return an Endpoint as a string.

### remove_route(did_with_key_frag, wallet_handle):
This function removes a route (did#key, endpoint) from the routing table for an endpoint based upon the provided parameter. It's expected that this function will not return any data upon completion.

### update_route(DID#key, endpoint, wallet_handle):
This function updates a route entry in the routing table by comparing the DID#key and replacing the current endpoint with the endpoint parameter. It's expected that this function will not return any data upon completion.