-
Notifications
You must be signed in to change notification settings - Fork 73
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
Changes from 29 commits
659d3cb
af5e21e
1018805
345c5c1
876092b
8fcce07
d5e5001
f23d11e
15baaca
4178bcc
1b7f276
6942efa
1c39f47
32681b5
3035b33
a2d5214
db5de9a
08ed97d
36a6dcf
1255ed8
1d0486f
96bd569
fbf8e09
803b4dd
e6ac023
df12b40
2293dab
4bb7c8c
ef9a8ef
d6f154d
359ff79
e3e5fa7
5987dc7
66e7ec0
fb50931
99b83b3
fa61b61
44c1f31
3357b5a
f167dc0
0999bc8
71a3d36
bc7e4be
c63f0ab
215fe58
33d7a60
f9167c4
fd3d8cc
d777fb2
c955e3a
0b09c02
06739e8
1db7d6e
ebc2339
8e7dfd2
9d135e2
cc5fdb6
f586b31
bb55f79
da6c1fc
36a92cc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 | ||
|
||
``` | ||
{ | ||
"recipients" : [ | ||
{ | ||
"header" : { | ||
"typ" : "x-b64nacl" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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> ` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think a There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are both the wallet and the verkey needed here for authcrypt? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
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. |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.