aes_gcm_storage.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. // Copyright 2018 The go-ethereum Authors
  2. // This file is part of go-ethereum.
  3. //
  4. // go-ethereum is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // go-ethereum is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
  16. //
  17. package storage
  18. import (
  19. "crypto/aes"
  20. "crypto/cipher"
  21. "crypto/rand"
  22. "encoding/json"
  23. "io"
  24. "io/ioutil"
  25. "os"
  26. "github.com/ethereum/go-ethereum/log"
  27. )
  28. type storedCredential struct {
  29. // The iv
  30. Iv []byte `json:"iv"`
  31. // The ciphertext
  32. CipherText []byte `json:"c"`
  33. }
  34. // AESEncryptedStorage is a storage type which is backed by a json-faile. The json-file contains
  35. // key-value mappings, where the keys are _not_ encrypted, only the values are.
  36. type AESEncryptedStorage struct {
  37. // File to read/write credentials
  38. filename string
  39. // Key stored in base64
  40. key []byte
  41. }
  42. // NewAESEncryptedStorage creates a new encrypted storage backed by the given file/key
  43. func NewAESEncryptedStorage(filename string, key []byte) *AESEncryptedStorage {
  44. return &AESEncryptedStorage{
  45. filename: filename,
  46. key: key,
  47. }
  48. }
  49. // Put stores a value by key. 0-length keys results in no-op
  50. func (s *AESEncryptedStorage) Put(key, value string) {
  51. if len(key) == 0 {
  52. return
  53. }
  54. data, err := s.readEncryptedStorage()
  55. if err != nil {
  56. log.Warn("Failed to read encrypted storage", "err", err, "file", s.filename)
  57. return
  58. }
  59. ciphertext, iv, err := encrypt(s.key, []byte(value))
  60. if err != nil {
  61. log.Warn("Failed to encrypt entry", "err", err)
  62. return
  63. }
  64. encrypted := storedCredential{Iv: iv, CipherText: ciphertext}
  65. data[key] = encrypted
  66. if err = s.writeEncryptedStorage(data); err != nil {
  67. log.Warn("Failed to write entry", "err", err)
  68. }
  69. }
  70. // Get returns the previously stored value, or the empty string if it does not exist or key is of 0-length
  71. func (s *AESEncryptedStorage) Get(key string) string {
  72. if len(key) == 0 {
  73. return ""
  74. }
  75. data, err := s.readEncryptedStorage()
  76. if err != nil {
  77. log.Warn("Failed to read encrypted storage", "err", err, "file", s.filename)
  78. return ""
  79. }
  80. encrypted, exist := data[key]
  81. if !exist {
  82. log.Warn("Key does not exist", "key", key)
  83. return ""
  84. }
  85. entry, err := decrypt(s.key, encrypted.Iv, encrypted.CipherText)
  86. if err != nil {
  87. log.Warn("Failed to decrypt key", "key", key)
  88. return ""
  89. }
  90. return string(entry)
  91. }
  92. // readEncryptedStorage reads the file with encrypted creds
  93. func (s *AESEncryptedStorage) readEncryptedStorage() (map[string]storedCredential, error) {
  94. creds := make(map[string]storedCredential)
  95. raw, err := ioutil.ReadFile(s.filename)
  96. if err != nil {
  97. if os.IsNotExist(err) {
  98. // Doesn't exist yet
  99. return creds, nil
  100. } else {
  101. log.Warn("Failed to read encrypted storage", "err", err, "file", s.filename)
  102. }
  103. }
  104. if err = json.Unmarshal(raw, &creds); err != nil {
  105. log.Warn("Failed to unmarshal encrypted storage", "err", err, "file", s.filename)
  106. return nil, err
  107. }
  108. return creds, nil
  109. }
  110. // writeEncryptedStorage write the file with encrypted creds
  111. func (s *AESEncryptedStorage) writeEncryptedStorage(creds map[string]storedCredential) error {
  112. raw, err := json.Marshal(creds)
  113. if err != nil {
  114. return err
  115. }
  116. if err = ioutil.WriteFile(s.filename, raw, 0600); err != nil {
  117. return err
  118. }
  119. return nil
  120. }
  121. func encrypt(key []byte, plaintext []byte) ([]byte, []byte, error) {
  122. block, err := aes.NewCipher(key)
  123. if err != nil {
  124. return nil, nil, err
  125. }
  126. aesgcm, err := cipher.NewGCM(block)
  127. nonce := make([]byte, aesgcm.NonceSize())
  128. if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
  129. return nil, nil, err
  130. }
  131. if err != nil {
  132. return nil, nil, err
  133. }
  134. ciphertext := aesgcm.Seal(nil, nonce, plaintext, nil)
  135. return ciphertext, nonce, nil
  136. }
  137. func decrypt(key []byte, nonce []byte, ciphertext []byte) ([]byte, error) {
  138. block, err := aes.NewCipher(key)
  139. if err != nil {
  140. return nil, err
  141. }
  142. aesgcm, err := cipher.NewGCM(block)
  143. if err != nil {
  144. return nil, err
  145. }
  146. plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil)
  147. if err != nil {
  148. return nil, err
  149. }
  150. return plaintext, nil
  151. }