Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
Go
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123package crypto
import ( "crypto/aes" "crypto/cipher" "crypto/rand" "encoding/base64" "encoding/json" "errors" "fmt" "io")
const ( EnvelopeVersion1 = 1 NonceSize = 12)
type MasterKey [32]byte
func ParseMasterKey(encoded string) (MasterKey, error) { var key MasterKey raw, err := base64.StdEncoding.DecodeString(encoded) if err != nil { raw, err = base64.URLEncoding.DecodeString(encoded) } if err != nil { return key, fmt.Errorf("decoding master key base64: %w", err) } if len(raw) != 32 { return key, fmt.Errorf("master key must be exactly 32 bytes, got %d", len(raw)) } copy(key[:], raw) return key, nil}
type Envelope struct { Version int `json:"v"` Nonce string `json:"n"` Data string `json:"d"`}
func ComputeAAD(ownerDid, batchID, requestID string) []byte { return []byte(fmt.Sprintf("v1|%s|%s|%s", ownerDid, batchID, requestID))}
func Encrypt(key MasterKey, plaintext string, aad []byte) (string, error) { block, err := aes.NewCipher(key[:]) if err != nil { return "", fmt.Errorf("creating cipher: %w", err) }
gcm, err := cipher.NewGCM(block) if err != nil { return "", fmt.Errorf("creating gcm: %w", err) }
nonce := make([]byte, gcm.NonceSize()) if _, err := io.ReadFull(rand.Reader, nonce); err != nil { return "", fmt.Errorf("generating nonce: %w", err) }
ciphertext := gcm.Seal(nil, nonce, []byte(plaintext), aad)
env := Envelope{ Version: EnvelopeVersion1, Nonce: base64.StdEncoding.EncodeToString(nonce), Data: base64.StdEncoding.EncodeToString(ciphertext), }
bytes, err := json.Marshal(env) if err != nil { return "", fmt.Errorf("marshaling envelope: %w", err) } return string(bytes), nil}
func Decrypt(key MasterKey, envelopeJSON string, aad []byte) (string, error) { if envelopeJSON == "" { return "", errors.New("empty envelope") }
var env Envelope if err := json.Unmarshal([]byte(envelopeJSON), &env); err != nil { return "", fmt.Errorf("unmarshaling envelope: %w", err) }
if env.Version != EnvelopeVersion1 { return "", fmt.Errorf("unsupported envelope version: %d", env.Version) }
nonce, err := base64.StdEncoding.DecodeString(env.Nonce) if err != nil { return "", fmt.Errorf("decoding nonce: %w", err) }
ciphertext, err := base64.StdEncoding.DecodeString(env.Data) if err != nil { return "", fmt.Errorf("decoding ciphertext: %w", err) }
block, err := aes.NewCipher(key[:]) if err != nil { return "", fmt.Errorf("creating cipher: %w", err) }
gcm, err := cipher.NewGCM(block) if err != nil { return "", fmt.Errorf("creating gcm: %w", err) }
if len(nonce) != gcm.NonceSize() { return "", fmt.Errorf("invalid nonce size: got %d, want %d", len(nonce), gcm.NonceSize()) }
plaintext, err := gcm.Open(nil, nonce, ciphertext, aad) if err != nil { return "", fmt.Errorf("decrypting data (authentication failure or mismatched aad): %w", err) }
return string(plaintext), nil}