Major Refactoring, Client can now be used as a library
Some checks failed
Build (artifact) / build (push) Failing after 1m3s
Some checks failed
Build (artifact) / build (push) Failing after 1m3s
This commit is contained in:
98
common/crypto.go
Normal file
98
common/crypto.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
)
|
||||
|
||||
// GenerateKey returns a base64-encoded 32-byte random key suitable to use as the
|
||||
// symmetric passphrase for age scrypt mode. Store this securely (never in Git).
|
||||
func GenerateKey() (string, error) {
|
||||
k := make([]byte, 32)
|
||||
if _, err := rand.Read(k); err != nil {
|
||||
return "", err
|
||||
}
|
||||
out := make([]byte, base64.StdEncoding.EncodedLen(len(k)))
|
||||
base64.StdEncoding.Encode(out, k)
|
||||
return string(out), nil
|
||||
}
|
||||
|
||||
func decodeKey(b64 string) ([]byte, error) {
|
||||
key, err := base64.StdEncoding.DecodeString(b64) // standard padded
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(key) != chacha20poly1305.KeySize {
|
||||
return nil, fmt.Errorf("bad key length: got %d, want %d", len(key), chacha20poly1305.KeySize)
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
func EncryptFileXChaCha(keyB64, inPath, outPath string, aad []byte) error {
|
||||
key, err := decodeKey(keyB64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
aead, err := chacha20poly1305.NewX(key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("new aead: %w", err)
|
||||
}
|
||||
|
||||
plaintext, err := os.ReadFile(inPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read input: %w", err)
|
||||
}
|
||||
|
||||
nonce := make([]byte, chacha20poly1305.NonceSizeX) // 24 bytes
|
||||
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return fmt.Errorf("nonce: %w", err)
|
||||
}
|
||||
|
||||
ciphertext := aead.Seal(nil, nonce, plaintext, aad)
|
||||
|
||||
// Write: nonce || ciphertext
|
||||
out := make([]byte, 0, len(nonce)+len(ciphertext))
|
||||
out = append(out, nonce...)
|
||||
out = append(out, ciphertext...)
|
||||
|
||||
if err := os.WriteFile(outPath, out, 0600); err != nil {
|
||||
return fmt.Errorf("write output: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DecryptFileFromBytes(keyB64 string, inBytes []byte, outPath string, aad []byte) error {
|
||||
key, err := decodeKey(keyB64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
aead, err := chacha20poly1305.NewX(key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("new aead: %w", err)
|
||||
}
|
||||
|
||||
if len(inBytes) < chacha20poly1305.NonceSizeX {
|
||||
return errors.New("ciphertext too short")
|
||||
}
|
||||
|
||||
nonce := inBytes[:chacha20poly1305.NonceSizeX]
|
||||
ciphertext := inBytes[chacha20poly1305.NonceSizeX:]
|
||||
|
||||
plaintext, err := aead.Open(nil, nonce, ciphertext, aad)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decrypt/auth failed: %w", err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(outPath, plaintext, 0640); err != nil {
|
||||
return fmt.Errorf("write output: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user