1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
|
# Threat Model
## Summary
`goaes` is a CLI tool for encrypting files at rest before storage using an AEAD scheme (e.g., AES-256-GCM). The intended users are the author and other CLI-savvy technical users. The key material is provided via a base64-encoded value from an environment variable.
This threat model focuses on offline compromise scenarios (stolen encrypted files) and does not attempt to defend against a compromised runtime environment.
## Goals
### Security goals
- **Confidentiality:** An attacker who obtains encrypted files should not be able to recover plaintext without the secret key.
- **Integrity:** Any modification to ciphertext should be detected at decryption time (fail closed).
- **Practical usability:** The tool should remain simple to use for technical users and not require complex operational dependencies.
### Non-goals
- **Multi-user key sharing / access control**
- **Compliance alignment** (e.g., FIPS, SOC 2, HIPAA)
- **Key rotation / crypto-erasure** as a first-class feature (not a current priority)
## System overview
### Primary workflow
1. User supplies a base64-encoded secret via environment variable (the "KEK" or master key).
2. `goaes` encrypts file content and writes an encrypted output file.
3. `goaes` decrypts files given the same environment-supplied key.
### Data stored alongside ciphertext
- File format version identifier (cleartext)
- Salt (cleartext) if used for key derivation / domain separation / format evolution
Note: cleartext metadata is permitted as it does not reveal the key or plaintext.
## Assets
- **Plaintext file contents** (primary asset)
- **Encryption key material** (base64 env var secret; decoded bytes in memory during runtime)
- **Integrity of decrypted output** (avoid silent corruption or malleability)
## Trust boundaries
- **Trusted:** Local machine *only insofar as* it is not compromised at runtime.
- **Untrusted:** Storage locations where ciphertext may reside (cloud object storage, backups, shared filesystems), and any party who can read encrypted files.
## Adversaries (in scope)
1. **Offline file thief**
- Obtains encrypted files (e.g., stolen laptop disk image, copied backup set, exfiltrated object storage bucket data).
- Has unlimited time to attempt offline attacks.
2. **Curious cloud/storage provider**
- Can read stored ciphertext and associated metadata.
3. **Malicious insider with file access**
- Has access to encrypted files via shared storage, backups, or internal systems.
- Does **not** have authorized access to the environment variable secret.
## Adversaries (explicitly out of scope)
If an attacker can obtain the environment variable secret, the primary confidentiality guarantee is already lost. Therefore the following are out of scope:
- **Compromised host / root attacker**
- **Memory scraping / debugger attachment**
- **Process inspection (e.g., reading `/proc`, environment, shell history)**
- **Malware with user-level access capable of reading env vars**
- **Keylogging / clipboard capture used to obtain the secret**
- **Any attacker who can read the secret from CI logs, shell config, or secrets management mistakes**
This is a deliberate scoping decision: `goaes` is not attempting to be a complete endpoint hardening solution.
## Assumptions
- Cryptographic primitives are implemented correctly by Go’s standard library.
- AES-256-GCM (or equivalent AEAD) is used correctly:
- Unique nonce per encryption under the same key
- Authentication failures cause decryption to fail closed
- The environment-supplied secret is high entropy:
- Prefer **32 random bytes** (256 bits) encoded as base64
- Not a human-memorable password
If the secret is not high entropy, offline guessing becomes viable and a password KDF would be required.
## Key management model
### Current model
- Single secret (provided via environment variable) used to encrypt/decrypt files.
### Design intent
- The security of `goaes` depends primarily on **keeping this secret unknown to attackers** and **ensuring it has sufficient entropy**.
### KDF position (current)
- Not required if the environment secret is truly random high-entropy key material.
- If users ever supply human-chosen secrets, the project should add an explicit "passphrase mode" using a memory-hard KDF (Argon2id).
## Threats and mitigations
### T1: Offline brute force / guessing
**Threat:** Attacker steals encrypted files and attempts to guess the key.
**Mitigation:** Use a high-entropy secret (32 random bytes).
**Notes:** A KDF is not necessary for truly random keys, but becomes necessary for human-chosen secrets.
### T2: Ciphertext tampering / corruption
**Threat:** Attacker modifies ciphertext to cause malicious or silent changes to plaintext.
**Mitigation:** Use AEAD (AES-GCM) and fail closed on authentication error.
### T3: Nonce reuse under the same key
**Threat:** Reusing a nonce with AES-GCM under the same key can catastrophically weaken confidentiality and integrity.
**Mitigation:** Ensure nonce uniqueness per encryption operation (cryptographically random nonces or a robust deterministic strategy). Treat nonce generation as a critical security requirement.
### T4: Metadata manipulation (version/salt)
**Threat:** Attacker edits metadata to influence parsing or key derivation logic.
**Mitigation:** Authenticate relevant metadata by binding it into AEAD (AAD). At minimum, authenticate the version field and any salt/parameters that affect keying.
### T5: Key disclosure via operational mistakes
**Threat:** Secret leaks through shell history, logs, CI output, process inspection, or misconfigured environment handling.
**Mitigation (operational guidance):**
- Do not pass secrets via CLI args (they show in process lists and shell history).
- Avoid logging the secret or decoded bytes.
- Encourage secret injection via a secrets manager or `.env` with correct permissions.
- Prefer per-host secrets and limit reuse where feasible.
(These mitigations reduce risk but do not change the out-of-scope boundary: if the secret is exposed, confidentiality is lost.)
## Security requirements (implementation checklist)
- Use AEAD encryption (AES-256-GCM or equivalent).
- Enforce:
- Nonce uniqueness per encryption under the same key
- Authentication check on decrypt (fail closed)
- File format includes:
- Magic bytes + version
- Nonce
- Ciphertext (including auth tag)
- Optional salt (cleartext), if used
- Bind into AAD:
- Version
- Salt (and any future KDF params)
- Zero/overwrite sensitive buffers where practical (best-effort; Go does not guarantee).
- Provide a deterministic test vector suite to prevent accidental format/crypto regressions.
## Open design decisions (explicit)
- **Passphrase mode:** Not currently supported. If added, it must be explicit and use Argon2id with published parameters.
- **Key rotation / crypto-erasure:** Not currently a priority. If implemented later, it will likely require a KEK/DEK envelope scheme with header metadata and versioned key slots.
- **Threat boundary:** The tool does not attempt to protect against runtime compromise or secret exfiltration from the host environment.
## "Secure by default" guidance for users
- Generate the secret as **32 random bytes**, then base64 encode it.
- Store it in a secure secret store or protected env file; do not paste it into shell history.
- Treat encrypted files as safe to store in untrusted locations, but treat the secret as highly sensitive.
|