Initial commit: Open sourcing all of the Maple Open Technologies code.
This commit is contained in:
commit
755d54a99d
2010 changed files with 448675 additions and 0 deletions
235
cloud/maplefile-backend/pkg/maplefile/e2ee/file.go
Normal file
235
cloud/maplefile-backend/pkg/maplefile/e2ee/file.go
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
// Package e2ee provides end-to-end encryption operations for the MapleFile SDK.
|
||||
package e2ee
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// FileMetadata represents decrypted file metadata.
|
||||
type FileMetadata struct {
|
||||
Name string `json:"name"`
|
||||
MimeType string `json:"mime_type"`
|
||||
Size int64 `json:"size"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
}
|
||||
|
||||
// EncryptFile encrypts file content using the file key.
|
||||
// Returns the combined nonce + ciphertext.
|
||||
// NOTE: This uses ChaCha20-Poly1305 (12-byte nonce). For web frontend compatibility,
|
||||
// use EncryptFileSecretBox instead.
|
||||
func EncryptFile(plaintext, fileKey []byte) ([]byte, error) {
|
||||
encryptedData, err := Encrypt(plaintext, fileKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to encrypt file: %w", err)
|
||||
}
|
||||
|
||||
// Combine nonce and ciphertext for storage
|
||||
combined := CombineNonceAndCiphertext(encryptedData.Nonce, encryptedData.Ciphertext)
|
||||
return combined, nil
|
||||
}
|
||||
|
||||
// EncryptFileSecretBox encrypts file content using XSalsa20-Poly1305 (NaCl secretbox).
|
||||
// Returns the combined nonce (24 bytes) + ciphertext.
|
||||
// This is compatible with the web frontend's libsodium implementation.
|
||||
func EncryptFileSecretBox(plaintext, fileKey []byte) ([]byte, error) {
|
||||
encryptedData, err := EncryptWithSecretBox(plaintext, fileKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to encrypt file: %w", err)
|
||||
}
|
||||
|
||||
// Combine nonce and ciphertext for storage (matching web frontend format)
|
||||
combined := CombineNonceAndCiphertext(encryptedData.Nonce, encryptedData.Ciphertext)
|
||||
return combined, nil
|
||||
}
|
||||
|
||||
// DecryptFile decrypts file content using the file key.
|
||||
// The input should be combined nonce + ciphertext.
|
||||
// Auto-detects the cipher based on nonce size:
|
||||
// - 24-byte nonce: XSalsa20-Poly1305 (web frontend / SecretBox)
|
||||
// - 12-byte nonce: ChaCha20-Poly1305 (legacy native app)
|
||||
func DecryptFile(encryptedData, fileKey []byte) ([]byte, error) {
|
||||
// Split nonce and ciphertext (auto-detect nonce size)
|
||||
nonce, ciphertext, err := SplitNonceAndCiphertextAuto(encryptedData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to split encrypted data: %w", err)
|
||||
}
|
||||
|
||||
// Decrypt using appropriate algorithm based on nonce size
|
||||
plaintext, err := DecryptWithAlgorithm(ciphertext, nonce, fileKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decrypt file: %w", err)
|
||||
}
|
||||
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
// EncryptFileWithNonce encrypts file content and returns the ciphertext and nonce separately.
|
||||
func EncryptFileWithNonce(plaintext, fileKey []byte) (ciphertext []byte, nonce []byte, err error) {
|
||||
encryptedData, err := Encrypt(plaintext, fileKey)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to encrypt file: %w", err)
|
||||
}
|
||||
|
||||
return encryptedData.Ciphertext, encryptedData.Nonce, nil
|
||||
}
|
||||
|
||||
// DecryptFileWithNonce decrypts file content using separate ciphertext and nonce.
|
||||
func DecryptFileWithNonce(ciphertext, nonce, fileKey []byte) ([]byte, error) {
|
||||
plaintext, err := Decrypt(ciphertext, nonce, fileKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decrypt file: %w", err)
|
||||
}
|
||||
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
// EncryptMetadata encrypts file metadata using the file key.
|
||||
// Returns base64-encoded combined nonce + ciphertext.
|
||||
// NOTE: This uses ChaCha20-Poly1305 (12-byte nonce). For web frontend compatibility,
|
||||
// use EncryptMetadataSecretBox instead.
|
||||
func EncryptMetadata(metadata *FileMetadata, fileKey []byte) (string, error) {
|
||||
// Convert metadata to JSON
|
||||
metadataBytes, err := json.Marshal(metadata)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to marshal metadata: %w", err)
|
||||
}
|
||||
|
||||
// Encrypt metadata
|
||||
encryptedData, err := Encrypt(metadataBytes, fileKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to encrypt metadata: %w", err)
|
||||
}
|
||||
|
||||
// Combine nonce and ciphertext
|
||||
combined := CombineNonceAndCiphertext(encryptedData.Nonce, encryptedData.Ciphertext)
|
||||
|
||||
// Encode to base64
|
||||
return EncodeToBase64(combined), nil
|
||||
}
|
||||
|
||||
// EncryptMetadataSecretBox encrypts file metadata using XSalsa20-Poly1305 (NaCl secretbox).
|
||||
// Returns base64-encoded combined nonce + ciphertext.
|
||||
// This is compatible with the web frontend's libsodium implementation.
|
||||
func EncryptMetadataSecretBox(metadata *FileMetadata, fileKey []byte) (string, error) {
|
||||
// Convert metadata to JSON
|
||||
metadataBytes, err := json.Marshal(metadata)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to marshal metadata: %w", err)
|
||||
}
|
||||
|
||||
// Encrypt metadata using SecretBox
|
||||
encryptedData, err := EncryptWithSecretBox(metadataBytes, fileKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to encrypt metadata: %w", err)
|
||||
}
|
||||
|
||||
// Combine nonce and ciphertext
|
||||
combined := CombineNonceAndCiphertext(encryptedData.Nonce, encryptedData.Ciphertext)
|
||||
|
||||
// Encode to base64
|
||||
return EncodeToBase64(combined), nil
|
||||
}
|
||||
|
||||
// DecryptMetadata decrypts file metadata using the file key.
|
||||
// The input should be base64-encoded combined nonce + ciphertext.
|
||||
func DecryptMetadata(encryptedMetadata string, fileKey []byte) (*FileMetadata, error) {
|
||||
// Decode from base64
|
||||
combined, err := DecodeFromBase64(encryptedMetadata)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode encrypted metadata: %w", err)
|
||||
}
|
||||
|
||||
// Split nonce and ciphertext
|
||||
nonce, ciphertext, err := SplitNonceAndCiphertext(combined)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to split encrypted metadata: %w", err)
|
||||
}
|
||||
|
||||
// Decrypt
|
||||
decryptedBytes, err := Decrypt(ciphertext, nonce, fileKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decrypt metadata: %w", err)
|
||||
}
|
||||
|
||||
// Parse JSON
|
||||
var metadata FileMetadata
|
||||
if err := json.Unmarshal(decryptedBytes, &metadata); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse decrypted metadata: %w", err)
|
||||
}
|
||||
|
||||
return &metadata, nil
|
||||
}
|
||||
|
||||
// EncryptMetadataWithNonce encrypts file metadata and returns nonce separately.
|
||||
func EncryptMetadataWithNonce(metadata *FileMetadata, fileKey []byte) (ciphertext []byte, nonce []byte, err error) {
|
||||
// Convert metadata to JSON
|
||||
metadataBytes, err := json.Marshal(metadata)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to marshal metadata: %w", err)
|
||||
}
|
||||
|
||||
// Encrypt metadata
|
||||
encryptedData, err := Encrypt(metadataBytes, fileKey)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to encrypt metadata: %w", err)
|
||||
}
|
||||
|
||||
return encryptedData.Ciphertext, encryptedData.Nonce, nil
|
||||
}
|
||||
|
||||
// DecryptMetadataWithNonce decrypts file metadata using separate ciphertext and nonce.
|
||||
func DecryptMetadataWithNonce(ciphertext, nonce, fileKey []byte) (*FileMetadata, error) {
|
||||
// Decrypt
|
||||
decryptedBytes, err := Decrypt(ciphertext, nonce, fileKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decrypt metadata: %w", err)
|
||||
}
|
||||
|
||||
// Parse JSON
|
||||
var metadata FileMetadata
|
||||
if err := json.Unmarshal(decryptedBytes, &metadata); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse decrypted metadata: %w", err)
|
||||
}
|
||||
|
||||
return &metadata, nil
|
||||
}
|
||||
|
||||
// EncryptData encrypts arbitrary data using the provided key.
|
||||
// Returns base64-encoded combined nonce + ciphertext.
|
||||
func EncryptData(data, key []byte) (string, error) {
|
||||
encryptedData, err := Encrypt(data, key)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to encrypt data: %w", err)
|
||||
}
|
||||
|
||||
// Combine nonce and ciphertext
|
||||
combined := CombineNonceAndCiphertext(encryptedData.Nonce, encryptedData.Ciphertext)
|
||||
|
||||
// Encode to base64
|
||||
return EncodeToBase64(combined), nil
|
||||
}
|
||||
|
||||
// DecryptData decrypts arbitrary data using the provided key.
|
||||
// The input should be base64-encoded combined nonce + ciphertext.
|
||||
func DecryptData(encryptedData string, key []byte) ([]byte, error) {
|
||||
// Decode from base64
|
||||
combined, err := DecodeFromBase64(encryptedData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode encrypted data: %w", err)
|
||||
}
|
||||
|
||||
// Split nonce and ciphertext
|
||||
nonce, ciphertext, err := SplitNonceAndCiphertext(combined)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to split encrypted data: %w", err)
|
||||
}
|
||||
|
||||
// Decrypt
|
||||
plaintext, err := Decrypt(ciphertext, nonce, key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decrypt data: %w", err)
|
||||
}
|
||||
|
||||
return plaintext, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue