2
0

crypto.go 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. package cert
  2. import (
  3. "crypto/aes"
  4. "crypto/cipher"
  5. "crypto/ed25519"
  6. "crypto/rand"
  7. "encoding/pem"
  8. "fmt"
  9. "io"
  10. "math"
  11. "golang.org/x/crypto/argon2"
  12. "google.golang.org/protobuf/proto"
  13. )
  14. type NebulaEncryptedData struct {
  15. EncryptionMetadata NebulaEncryptionMetadata
  16. Ciphertext []byte
  17. }
  18. type NebulaEncryptionMetadata struct {
  19. EncryptionAlgorithm string
  20. Argon2Parameters Argon2Parameters
  21. }
  22. // Argon2Parameters KDF factors
  23. type Argon2Parameters struct {
  24. version rune
  25. Memory uint32 // KiB
  26. Parallelism uint8
  27. Iterations uint32
  28. salt []byte
  29. }
  30. // NewArgon2Parameters Returns a new Argon2Parameters object with current version set
  31. func NewArgon2Parameters(memory uint32, parallelism uint8, iterations uint32) *Argon2Parameters {
  32. return &Argon2Parameters{
  33. version: argon2.Version,
  34. Memory: memory, // KiB
  35. Parallelism: parallelism,
  36. Iterations: iterations,
  37. }
  38. }
  39. // Encrypts data using AES-256-GCM and the Argon2id key derivation function
  40. func aes256Encrypt(passphrase []byte, kdfParams *Argon2Parameters, data []byte) ([]byte, error) {
  41. key, err := aes256DeriveKey(passphrase, kdfParams)
  42. if err != nil {
  43. return nil, err
  44. }
  45. // this should never happen, but since this dictates how our calls into the
  46. // aes package behave and could be catastraphic, let's sanity check this
  47. if len(key) != 32 {
  48. return nil, fmt.Errorf("invalid AES-256 key length (%d) - cowardly refusing to encrypt", len(key))
  49. }
  50. block, err := aes.NewCipher(key)
  51. if err != nil {
  52. return nil, err
  53. }
  54. gcm, err := cipher.NewGCM(block)
  55. if err != nil {
  56. return nil, err
  57. }
  58. nonce := make([]byte, gcm.NonceSize())
  59. if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
  60. return nil, err
  61. }
  62. ciphertext := gcm.Seal(nil, nonce, data, nil)
  63. blob := joinNonceCiphertext(nonce, ciphertext)
  64. return blob, nil
  65. }
  66. // Decrypts data using AES-256-GCM and the Argon2id key derivation function
  67. // Expects the data to include an Argon2id parameter string before the encrypted data
  68. func aes256Decrypt(passphrase []byte, kdfParams *Argon2Parameters, data []byte) ([]byte, error) {
  69. key, err := aes256DeriveKey(passphrase, kdfParams)
  70. if err != nil {
  71. return nil, err
  72. }
  73. block, err := aes.NewCipher(key)
  74. if err != nil {
  75. return nil, err
  76. }
  77. gcm, err := cipher.NewGCM(block)
  78. if err != nil {
  79. return nil, err
  80. }
  81. nonce, ciphertext, err := splitNonceCiphertext(data, gcm.NonceSize())
  82. if err != nil {
  83. return nil, err
  84. }
  85. plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
  86. if err != nil {
  87. return nil, fmt.Errorf("invalid passphrase or corrupt private key")
  88. }
  89. return plaintext, nil
  90. }
  91. func aes256DeriveKey(passphrase []byte, params *Argon2Parameters) ([]byte, error) {
  92. if params.salt == nil {
  93. params.salt = make([]byte, 32)
  94. if _, err := rand.Read(params.salt); err != nil {
  95. return nil, err
  96. }
  97. }
  98. // keySize of 32 bytes will result in AES-256 encryption
  99. key, err := deriveKey(passphrase, 32, params)
  100. if err != nil {
  101. return nil, err
  102. }
  103. return key, nil
  104. }
  105. // Derives a key from a passphrase using Argon2id
  106. func deriveKey(passphrase []byte, keySize uint32, params *Argon2Parameters) ([]byte, error) {
  107. if params.version != argon2.Version {
  108. return nil, fmt.Errorf("incompatible Argon2 version: %d", params.version)
  109. }
  110. if params.salt == nil {
  111. return nil, fmt.Errorf("salt must be set in argon2Parameters")
  112. } else if len(params.salt) < 16 {
  113. return nil, fmt.Errorf("salt must be at least 128 bits")
  114. }
  115. key := argon2.IDKey(passphrase, params.salt, params.Iterations, params.Memory, params.Parallelism, keySize)
  116. return key, nil
  117. }
  118. // Prepends nonce to ciphertext
  119. func joinNonceCiphertext(nonce []byte, ciphertext []byte) []byte {
  120. return append(nonce, ciphertext...)
  121. }
  122. // Splits nonce from ciphertext
  123. func splitNonceCiphertext(blob []byte, nonceSize int) ([]byte, []byte, error) {
  124. if len(blob) <= nonceSize {
  125. return nil, nil, fmt.Errorf("invalid ciphertext blob - blob shorter than nonce length")
  126. }
  127. return blob[:nonceSize], blob[nonceSize:], nil
  128. }
  129. // EncryptAndMarshalSigningPrivateKey is a simple helper to encrypt and PEM encode a private key
  130. func EncryptAndMarshalSigningPrivateKey(curve Curve, b []byte, passphrase []byte, kdfParams *Argon2Parameters) ([]byte, error) {
  131. ciphertext, err := aes256Encrypt(passphrase, kdfParams, b)
  132. if err != nil {
  133. return nil, err
  134. }
  135. b, err = proto.Marshal(&RawNebulaEncryptedData{
  136. EncryptionMetadata: &RawNebulaEncryptionMetadata{
  137. EncryptionAlgorithm: "AES-256-GCM",
  138. Argon2Parameters: &RawNebulaArgon2Parameters{
  139. Version: kdfParams.version,
  140. Memory: kdfParams.Memory,
  141. Parallelism: uint32(kdfParams.Parallelism),
  142. Iterations: kdfParams.Iterations,
  143. Salt: kdfParams.salt,
  144. },
  145. },
  146. Ciphertext: ciphertext,
  147. })
  148. if err != nil {
  149. return nil, err
  150. }
  151. switch curve {
  152. case Curve_CURVE25519:
  153. return pem.EncodeToMemory(&pem.Block{Type: EncryptedEd25519PrivateKeyBanner, Bytes: b}), nil
  154. case Curve_P256:
  155. return pem.EncodeToMemory(&pem.Block{Type: EncryptedECDSAP256PrivateKeyBanner, Bytes: b}), nil
  156. default:
  157. return nil, fmt.Errorf("invalid curve: %v", curve)
  158. }
  159. }
  160. // UnmarshalNebulaEncryptedData will unmarshal a protobuf byte representation of a nebula cert into its
  161. // protobuf-generated struct.
  162. func UnmarshalNebulaEncryptedData(b []byte) (*NebulaEncryptedData, error) {
  163. if len(b) == 0 {
  164. return nil, fmt.Errorf("nil byte array")
  165. }
  166. var rned RawNebulaEncryptedData
  167. err := proto.Unmarshal(b, &rned)
  168. if err != nil {
  169. return nil, err
  170. }
  171. if rned.EncryptionMetadata == nil {
  172. return nil, fmt.Errorf("encoded EncryptionMetadata was nil")
  173. }
  174. if rned.EncryptionMetadata.Argon2Parameters == nil {
  175. return nil, fmt.Errorf("encoded Argon2Parameters was nil")
  176. }
  177. params, err := unmarshalArgon2Parameters(rned.EncryptionMetadata.Argon2Parameters)
  178. if err != nil {
  179. return nil, err
  180. }
  181. ned := NebulaEncryptedData{
  182. EncryptionMetadata: NebulaEncryptionMetadata{
  183. EncryptionAlgorithm: rned.EncryptionMetadata.EncryptionAlgorithm,
  184. Argon2Parameters: *params,
  185. },
  186. Ciphertext: rned.Ciphertext,
  187. }
  188. return &ned, nil
  189. }
  190. func unmarshalArgon2Parameters(params *RawNebulaArgon2Parameters) (*Argon2Parameters, error) {
  191. if params.Version < math.MinInt32 || params.Version > math.MaxInt32 {
  192. return nil, fmt.Errorf("Argon2Parameters Version must be at least %d and no more than %d", math.MinInt32, math.MaxInt32)
  193. }
  194. if params.Memory <= 0 || params.Memory > math.MaxUint32 {
  195. return nil, fmt.Errorf("Argon2Parameters Memory must be be greater than 0 and no more than %d KiB", uint32(math.MaxUint32))
  196. }
  197. if params.Parallelism <= 0 || params.Parallelism > math.MaxUint8 {
  198. return nil, fmt.Errorf("Argon2Parameters Parallelism must be be greater than 0 and no more than %d", math.MaxUint8)
  199. }
  200. if params.Iterations <= 0 || params.Iterations > math.MaxUint32 {
  201. return nil, fmt.Errorf("-argon-iterations must be be greater than 0 and no more than %d", uint32(math.MaxUint32))
  202. }
  203. return &Argon2Parameters{
  204. version: params.Version,
  205. Memory: params.Memory,
  206. Parallelism: uint8(params.Parallelism),
  207. Iterations: params.Iterations,
  208. salt: params.Salt,
  209. }, nil
  210. }
  211. // DecryptAndUnmarshalSigningPrivateKey will try to pem decode and decrypt an Ed25519/ECDSA private key with
  212. // the given passphrase, returning any other bytes b or an error on failure
  213. func DecryptAndUnmarshalSigningPrivateKey(passphrase, b []byte) (Curve, []byte, []byte, error) {
  214. var curve Curve
  215. k, r := pem.Decode(b)
  216. if k == nil {
  217. return curve, nil, r, fmt.Errorf("input did not contain a valid PEM encoded block")
  218. }
  219. switch k.Type {
  220. case EncryptedEd25519PrivateKeyBanner:
  221. curve = Curve_CURVE25519
  222. case EncryptedECDSAP256PrivateKeyBanner:
  223. curve = Curve_P256
  224. default:
  225. return curve, nil, r, fmt.Errorf("bytes did not contain a proper nebula encrypted Ed25519/ECDSA private key banner")
  226. }
  227. ned, err := UnmarshalNebulaEncryptedData(k.Bytes)
  228. if err != nil {
  229. return curve, nil, r, err
  230. }
  231. var bytes []byte
  232. switch ned.EncryptionMetadata.EncryptionAlgorithm {
  233. case "AES-256-GCM":
  234. bytes, err = aes256Decrypt(passphrase, &ned.EncryptionMetadata.Argon2Parameters, ned.Ciphertext)
  235. if err != nil {
  236. return curve, nil, r, err
  237. }
  238. default:
  239. return curve, nil, r, fmt.Errorf("unsupported encryption algorithm: %s", ned.EncryptionMetadata.EncryptionAlgorithm)
  240. }
  241. switch curve {
  242. case Curve_CURVE25519:
  243. if len(bytes) != ed25519.PrivateKeySize {
  244. return curve, nil, r, fmt.Errorf("key was not %d bytes, is invalid ed25519 private key", ed25519.PrivateKeySize)
  245. }
  246. case Curve_P256:
  247. if len(bytes) != 32 {
  248. return curve, nil, r, fmt.Errorf("key was not 32 bytes, is invalid ECDSA P256 private key")
  249. }
  250. }
  251. return curve, bytes, r, nil
  252. }