blob_crypto.go 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. // Package blobcrypto performs whole-blob crypto operations.
  2. package blobcrypto
  3. import (
  4. "crypto/aes"
  5. "encoding/hex"
  6. "strings"
  7. "github.com/pkg/errors"
  8. "github.com/kopia/kopia/internal/gather"
  9. "github.com/kopia/kopia/repo/blob"
  10. "github.com/kopia/kopia/repo/encryption"
  11. "github.com/kopia/kopia/repo/hashing"
  12. )
  13. // Crypter encapsulates hashing and encryption.
  14. type Crypter interface {
  15. HashFunc() hashing.HashFunc
  16. Encryptor() encryption.Encryptor
  17. }
  18. // getIndexBlobIV gets the initialization vector from the provided blob ID by taking
  19. // 32 characters immediately preceding the first dash ('-') and decoding them using base16.
  20. func getIndexBlobIV(s blob.ID) ([]byte, error) {
  21. if p := strings.Index(string(s), "-"); p >= 0 { //nolint:gocritic
  22. s = s[0:p]
  23. }
  24. if len(s) < 2*aes.BlockSize {
  25. return nil, errors.Errorf("blob id too short: %v", s)
  26. }
  27. v, err := hex.DecodeString(string(s[len(s)-(aes.BlockSize*2):])) //nolint:mnd
  28. if err != nil {
  29. return nil, errors.Errorf("invalid blob ID: %v", s)
  30. }
  31. return v, nil
  32. }
  33. // Encrypt encrypts the given data using crypter-defined key and returns a name that should
  34. // be used to save the blob in the repository.
  35. func Encrypt(c Crypter, payload gather.Bytes, prefix, suffix blob.ID, output *gather.WriteBuffer) (blob.ID, error) {
  36. var hashOutput [hashing.MaxHashSize]byte
  37. hash := c.HashFunc()(hashOutput[:0], payload)
  38. blobID := prefix + blob.ID(hex.EncodeToString(hash))
  39. if suffix != "" {
  40. blobID += "-" + suffix
  41. }
  42. iv, err := getIndexBlobIV(blobID)
  43. if err != nil {
  44. return "", err
  45. }
  46. output.Reset()
  47. if err := c.Encryptor().Encrypt(payload, iv, output); err != nil {
  48. return "", errors.Wrapf(err, "error encrypting BLOB %v", blobID)
  49. }
  50. return blobID, nil
  51. }
  52. // Decrypt decrypts the provided data using provided blobID to derive initialization vector.
  53. func Decrypt(c Crypter, payload gather.Bytes, blobID blob.ID, output *gather.WriteBuffer) error {
  54. iv, err := getIndexBlobIV(blobID)
  55. if err != nil {
  56. return errors.Wrap(err, "unable to get index blob IV")
  57. }
  58. output.Reset()
  59. // Decrypt will verify the payload.
  60. if err := c.Encryptor().Decrypt(payload, iv, output); err != nil {
  61. return errors.Wrapf(err, "error decrypting BLOB %v", blobID)
  62. }
  63. return nil
  64. }