ente/cli/internal/crypto/crypto.go
Neeraj Gupta bc45db51a9
[cli] Improve logging for decryption error (#1242)
## Description
Related to #1237
## Tests
2024-03-29 11:08:45 +05:30

116 lines
3.6 KiB
Go

package crypto
import (
"encoding/base64"
"encoding/binary"
"errors"
"fmt"
"github.com/minio/blake2b-simd"
"golang.org/x/crypto/argon2"
)
const (
loginSubKeyLen = 32
loginSubKeyId = 1
loginSubKeyContext = "loginctx"
decryptionBufferSize = 4 * 1024 * 1024
)
const (
cryptoKDFBlake2bBytesMin = 16
cryptoKDFBlake2bBytesMax = 64
cryptoGenerichashBlake2bSaltBytes = 16
cryptoGenerichashBlake2bPersonalBytes = 16
BoxSealBytes = 48 // 32 for the ephemeral public key + 16 for the MAC
)
var (
ErrOpenBox = errors.New("failed to open box")
ErrSealedOpenBox = errors.New("failed to open sealed box")
)
const ()
// DeriveArgonKey generates a 32-bit cryptographic key using the Argon2id algorithm.
// Parameters:
// - password: The plaintext password to be hashed.
// - salt: The salt as a base64 encoded string.
// - memLimit: The memory limit in bytes.
// - opsLimit: The number of iterations.
//
// Returns:
// - A byte slice representing the derived key.
// - An error object, which is nil if no error occurs.
func DeriveArgonKey(password, salt string, memLimit, opsLimit int) ([]byte, error) {
if memLimit < 1024 || opsLimit < 1 {
return nil, fmt.Errorf("invalid memory or operation limits")
}
// Decode salt from base64
saltBytes, err := base64.StdEncoding.DecodeString(salt)
if err != nil {
return nil, fmt.Errorf("invalid salt: %v", err)
}
// Generate key using Argon2id
// Note: We're assuming a fixed key length of 32 bytes and changing the threads
key := argon2.IDKey([]byte(password), saltBytes, uint32(opsLimit), uint32(memLimit/1024), 1, 32)
return key, nil
}
// DeriveLoginKey derives a login key from the given key encryption key.
// This loginKey act as user provided password during SRP authentication.
// Parameters: keyEncKey: This is the keyEncryptionKey that is derived from the user's password.
func DeriveLoginKey(keyEncKey []byte) []byte {
subKey, _ := deriveSubKey(keyEncKey, loginSubKeyContext, loginSubKeyId, loginSubKeyLen)
// return the first 16 bytes of the derived key
return subKey[:16]
}
func deriveSubKey(masterKey []byte, context string, subKeyID uint64, subKeyLength uint32) ([]byte, error) {
if subKeyLength < cryptoKDFBlake2bBytesMin || subKeyLength > cryptoKDFBlake2bBytesMax {
return nil, fmt.Errorf("subKeyLength out of bounds")
}
// Pad the context
ctxPadded := make([]byte, cryptoGenerichashBlake2bPersonalBytes)
copy(ctxPadded, []byte(context))
// Convert subKeyID to byte slice and pad
salt := make([]byte, cryptoGenerichashBlake2bSaltBytes)
binary.LittleEndian.PutUint64(salt, subKeyID)
// Create a BLAKE2b configuration
config := &blake2b.Config{
Size: uint8(subKeyLength),
Key: masterKey,
Salt: salt,
Person: ctxPadded,
}
hasher, err := blake2b.New(config)
if err != nil {
return nil, err
}
hasher.Write(nil) // No data, just using key, salt, and personalization
return hasher.Sum(nil), nil
}
func DecryptChaChaBase64(data string, key []byte, nonce string) (string, []byte, error) {
// Decode data from base64
dataBytes, err := base64.StdEncoding.DecodeString(data)
if err != nil {
// safe to log the encrypted data
return "", nil, fmt.Errorf("invalid base64 data %s: %v", data, err)
}
// Decode nonce from base64
nonceBytes, err := base64.StdEncoding.DecodeString(nonce)
if err != nil {
return "", nil, fmt.Errorf("invalid nonce: %v", err)
}
// Decrypt data
decryptedData, err := decryptChaCha20poly1305(dataBytes, key, nonceBytes)
if err != nil {
return "", nil, fmt.Errorf("failed to decrypt data: %v", err)
}
return base64.StdEncoding.EncodeToString(decryptedData), decryptedData, nil
}