You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

99 lines
3.7 KiB

  1. # ADR 013: Need for symmetric cryptography
  2. ## Context
  3. We require symmetric ciphers to handle how we encrypt keys in the sdk,
  4. and to potentially encrypt `priv_validator.json` in tendermint.
  5. Currently we use AEAD's to support symmetric encryption,
  6. which is great since we want data integrity in addition to privacy and authenticity.
  7. We don't currently have a scenario where we want to encrypt without data integrity,
  8. so it is fine to optimize our code to just use AEAD's.
  9. Currently there is not a way to switch out AEAD's easily, this ADR outlines a way
  10. to easily swap these out.
  11. ### How do we encrypt with AEAD's
  12. AEAD's typically require a nonce in addition to the key.
  13. For the purposes we require symmetric cryptography for,
  14. we need encryption to be stateless.
  15. Because of this we use random nonces.
  16. (Thus the AEAD must support random nonces)
  17. We currently construct a random nonce, and encrypt the data with it.
  18. The returned value is `nonce || encrypted data`.
  19. The limitation of this is that does not provide a way to identify
  20. which algorithm was used in encryption.
  21. Consequently decryption with multiple algoritms is sub-optimal.
  22. (You have to try them all)
  23. ## Decision
  24. We should create the following two methods in a new `crypto/encoding/symmetric` package:
  25. ```golang
  26. func Encrypt(aead cipher.AEAD, plaintext []byte) (ciphertext []byte, err error)
  27. func Decrypt(key []byte, ciphertext []byte) (plaintext []byte, err error)
  28. func Register(aead cipher.AEAD, algo_name string, NewAead func(key []byte) (cipher.Aead, error)) error
  29. ```
  30. This allows you to specify the algorithm in encryption, but not have to specify
  31. it in decryption.
  32. This is intended for ease of use in downstream applications, in addition to people
  33. looking at the file directly.
  34. One downside is that for the encrypt function you must have already initialized an AEAD,
  35. but I don't really see this as an issue.
  36. If there is no error in encryption, Encrypt will return `algo_name || nonce || aead_ciphertext`.
  37. `algo_name` should be length prefixed, using standard varuint encoding.
  38. This will be binary data, but thats not a problem considering the nonce and ciphertext are also binary.
  39. This solution requires a mapping from aead type to name.
  40. We can achieve this via reflection.
  41. ```golang
  42. func getType(myvar interface{}) string {
  43. if t := reflect.TypeOf(myvar); t.Kind() == reflect.Ptr {
  44. return "*" + t.Elem().Name()
  45. } else {
  46. return t.Name()
  47. }
  48. }
  49. ```
  50. Then we maintain a map from the name returned from `getType(aead)` to `algo_name`.
  51. In decryption, we read the `algo_name`, and then instantiate a new AEAD with the key.
  52. Then we call the AEAD's decrypt method on the provided nonce/ciphertext.
  53. `Register` allows a downstream user to add their own desired AEAD to the symmetric package.
  54. It will error if the AEAD name is already registered.
  55. This prevents a malicious import from modifying / nullifying an AEAD at runtime.
  56. ## Implementation strategy
  57. The golang implementation of what is proposed is rather straight forward.
  58. The concern is that we will break existing private keys if we just switch to this.
  59. If this is concerning, we can make a simple script which doesn't require decoding privkeys,
  60. for converting from the old format to the new one.
  61. ## Status
  62. Proposed.
  63. ## Consequences
  64. ### Positive
  65. - Allows us to support new AEAD's, in a way that makes decryption easier
  66. - Allows downstream users to add their own AEAD
  67. ### Negative
  68. - We will have to break all private keys stored on disk.
  69. They can be recovered using seed words, and upgrade scripts are simple.
  70. ### Neutral
  71. - Caller has to instantiate the AEAD with the private key.
  72. However it forces them to be aware of what signing algorithm they are using, which is a positive.