crypto.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. package crypto
  2. import (
  3. "encoding/base64"
  4. "encoding/binary"
  5. "errors"
  6. "fmt"
  7. "github.com/minio/blake2b-simd"
  8. "golang.org/x/crypto/argon2"
  9. )
  10. const (
  11. loginSubKeyLen = 32
  12. loginSubKeyId = 1
  13. loginSubKeyContext = "loginctx"
  14. decryptionBufferSize = 4 * 1024 * 1024
  15. )
  16. const (
  17. cryptoKDFBlake2bBytesMin = 16
  18. cryptoKDFBlake2bBytesMax = 64
  19. cryptoGenerichashBlake2bSaltBytes = 16
  20. cryptoGenerichashBlake2bPersonalBytes = 16
  21. BoxSealBytes = 48 // 32 for the ephemeral public key + 16 for the MAC
  22. )
  23. var (
  24. ErrOpenBox = errors.New("failed to open box")
  25. ErrSealedOpenBox = errors.New("failed to open sealed box")
  26. )
  27. const ()
  28. // DeriveArgonKey generates a 32-bit cryptographic key using the Argon2id algorithm.
  29. // Parameters:
  30. // - password: The plaintext password to be hashed.
  31. // - salt: The salt as a base64 encoded string.
  32. // - memLimit: The memory limit in bytes.
  33. // - opsLimit: The number of iterations.
  34. //
  35. // Returns:
  36. // - A byte slice representing the derived key.
  37. // - An error object, which is nil if no error occurs.
  38. func DeriveArgonKey(password, salt string, memLimit, opsLimit int) ([]byte, error) {
  39. if memLimit < 1024 || opsLimit < 1 {
  40. return nil, fmt.Errorf("invalid memory or operation limits")
  41. }
  42. // Decode salt from base64
  43. saltBytes, err := base64.StdEncoding.DecodeString(salt)
  44. if err != nil {
  45. return nil, fmt.Errorf("invalid salt: %v", err)
  46. }
  47. // Generate key using Argon2id
  48. // Note: We're assuming a fixed key length of 32 bytes and changing the threads
  49. key := argon2.IDKey([]byte(password), saltBytes, uint32(opsLimit), uint32(memLimit/1024), 1, 32)
  50. return key, nil
  51. }
  52. // DeriveLoginKey derives a login key from the given key encryption key.
  53. // This loginKey act as user provided password during SRP authentication.
  54. // Parameters: keyEncKey: This is the keyEncryptionKey that is derived from the user's password.
  55. func DeriveLoginKey(keyEncKey []byte) []byte {
  56. subKey, _ := deriveSubKey(keyEncKey, loginSubKeyContext, loginSubKeyId, loginSubKeyLen)
  57. // return the first 16 bytes of the derived key
  58. return subKey[:16]
  59. }
  60. func deriveSubKey(masterKey []byte, context string, subKeyID uint64, subKeyLength uint32) ([]byte, error) {
  61. if subKeyLength < cryptoKDFBlake2bBytesMin || subKeyLength > cryptoKDFBlake2bBytesMax {
  62. return nil, fmt.Errorf("subKeyLength out of bounds")
  63. }
  64. // Pad the context
  65. ctxPadded := make([]byte, cryptoGenerichashBlake2bPersonalBytes)
  66. copy(ctxPadded, []byte(context))
  67. // Convert subKeyID to byte slice and pad
  68. salt := make([]byte, cryptoGenerichashBlake2bSaltBytes)
  69. binary.LittleEndian.PutUint64(salt, subKeyID)
  70. // Create a BLAKE2b configuration
  71. config := &blake2b.Config{
  72. Size: uint8(subKeyLength),
  73. Key: masterKey,
  74. Salt: salt,
  75. Person: ctxPadded,
  76. }
  77. hasher, err := blake2b.New(config)
  78. if err != nil {
  79. return nil, err
  80. }
  81. hasher.Write(nil) // No data, just using key, salt, and personalization
  82. return hasher.Sum(nil), nil
  83. }
  84. func DecryptChaChaBase64(data string, key []byte, nonce string) (string, []byte, error) {
  85. // Decode data from base64
  86. dataBytes, err := base64.StdEncoding.DecodeString(data)
  87. if err != nil {
  88. return "", nil, fmt.Errorf("invalid data: %v", err)
  89. }
  90. // Decode nonce from base64
  91. nonceBytes, err := base64.StdEncoding.DecodeString(nonce)
  92. if err != nil {
  93. return "", nil, fmt.Errorf("invalid nonce: %v", err)
  94. }
  95. // Decrypt data
  96. decryptedData, err := decryptChaCha20poly1305(dataBytes, key, nonceBytes)
  97. if err != nil {
  98. return "", nil, fmt.Errorf("failed to decrypt data: %v", err)
  99. }
  100. return base64.StdEncoding.EncodeToString(decryptedData), decryptedData, nil
  101. }