Official cwtch.im peer and server implementations. https://cwtch.im
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.

90 lines
2.3KB

  1. package storage
  2. import (
  3. "crypto/rand"
  4. "fmt"
  5. "git.openprivacy.ca/openprivacy/libricochet-go/log"
  6. "golang.org/x/crypto/nacl/secretbox"
  7. "golang.org/x/crypto/pbkdf2"
  8. "golang.org/x/crypto/sha3"
  9. "io"
  10. "io/ioutil"
  11. )
  12. // fileStore stores a cwtchPeer in an encrypted file
  13. type fileStore struct {
  14. filename string
  15. password string
  16. }
  17. type FileStore interface {
  18. Save([]byte) error
  19. Load() ([]byte, error)
  20. }
  21. // NewFileStore instantiates a fileStore given a filename and a password
  22. func NewFileStore(filename string, password string) FileStore {
  23. filestore := new(fileStore)
  24. filestore.password = password
  25. filestore.filename = filename
  26. return filestore
  27. }
  28. // Save serializes a cwtchPeer to a file
  29. func (fps *fileStore) Save(data []byte) error {
  30. key, salt, _ := createKey(fps.password)
  31. encryptedbytes, err := encryptFileData(data, key)
  32. if err != nil {
  33. return err
  34. }
  35. // the salt for the derived key is appended to the front of the file
  36. encryptedbytes = append(salt[:], encryptedbytes...)
  37. err = ioutil.WriteFile(fps.filename, encryptedbytes, 0600)
  38. return err
  39. }
  40. //encryptFileData encrypts the cwtchPeer via the specified key.
  41. func encryptFileData(data []byte, key [32]byte) ([]byte, error) {
  42. var nonce [24]byte
  43. if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {
  44. log.Errorf("Cannot read from random: %v\n", err)
  45. return nil, err
  46. }
  47. encrypted := secretbox.Seal(nonce[:], data, &nonce, &key)
  48. return encrypted, nil
  49. }
  50. //decryptFile decrypts the passed ciphertext into a cwtchPeer via the specified key.
  51. func decryptFile(ciphertext []byte, key [32]byte) ([]byte, error) {
  52. var decryptNonce [24]byte
  53. copy(decryptNonce[:], ciphertext[:24])
  54. decrypted, ok := secretbox.Open(nil, ciphertext[24:], &decryptNonce, &key)
  55. if ok {
  56. return decrypted, nil
  57. }
  58. return nil, fmt.Errorf("Failed to decrypt")
  59. }
  60. // Load instantiates a cwtchPeer from the file store
  61. func (fps *fileStore) Load() ([]byte, error) {
  62. encryptedbytes, err := ioutil.ReadFile(fps.filename)
  63. if err == nil {
  64. var dkr [32]byte
  65. //Separate the salt from the encrypted bytes, then generate the derived key
  66. salt, encryptedbytes := encryptedbytes[0:128], encryptedbytes[128:]
  67. dk := pbkdf2.Key([]byte(fps.password), salt, 4096, 32, sha3.New512)
  68. copy(dkr[:], dk)
  69. data, err := decryptFile(encryptedbytes, dkr)
  70. if err == nil {
  71. return data, nil
  72. }
  73. return nil, err
  74. }
  75. return nil, err
  76. }