keys.go

v1.0.0
Doc Versions Source
1
package crypto
2
3
import (
4
	"crypto/rand"
5
	"crypto/rsa"
6
	"encoding/base64"
7
	"fmt"
8
	"sync"
9
)
10
11
// KeyChain holds all derived keys for an authenticated session.
12
type KeyChain struct {
13
	mu sync.RWMutex
14
15
	// Master key derived from password + email
16
	masterKey []byte
17
18
	// Stretched master key (enc + mac), used to decrypt the protected symmetric key
19
	stretchedKey *SymmetricKey
20
21
	// User symmetric key (decrypted from server's protected key)
22
	userKey *SymmetricKey
23
24
	// RSA private key (decrypted from server's encrypted private key)
25
	rsaPrivateKey *rsa.PrivateKey
26
27
	// Organization keys: orgID → symmetric key
28
	orgKeys map[string]*SymmetricKey
29
}
30
31
// NewKeyChain derives all keys from the master password and server-provided encrypted keys.
32
func NewKeyChain(email, password string, cfg DeriveKeyConfig, protectedSymKeyStr, encPrivateKeyStr string) (*KeyChain, error) {
33
	masterKey, err := DeriveMasterKey(password, email, cfg)
34
	if err != nil {
35
		return nil, fmt.Errorf("derive master key: %w", err)
36
	}
37
38
	encKey, macKey := StretchMasterKey(masterKey)
39
	stretchedKeyBytes := make([]byte, 64)
40
	copy(stretchedKeyBytes[:32], encKey)
41
	copy(stretchedKeyBytes[32:], macKey)
42
	stretchedKey, err := NewSymmetricKey(stretchedKeyBytes)
43
	if err != nil {
44
		return nil, fmt.Errorf("create stretched key: %w", err)
45
	}
46
47
	// Decrypt the protected symmetric key (decrypts to raw 64 bytes)
48
	cs, err := ParseCipherString(protectedSymKeyStr)
49
	if err != nil {
50
		return nil, fmt.Errorf("parse protected sym key: %w", err)
51
	}
52
	userKeyRaw, err := stretchedKey.Decrypt(cs)
53
	if err != nil {
54
		return nil, fmt.Errorf("decrypt protected sym key: %w", err)
55
	}
56
57
	userKey, err := NewSymmetricKey(userKeyRaw)
58
	if err != nil {
59
		return nil, fmt.Errorf("create user key from %d bytes: %w", len(userKeyRaw), err)
60
	}
61
62
	kc := &KeyChain{
63
		masterKey:    masterKey,
64
		stretchedKey: stretchedKey,
65
		userKey:      userKey,
66
		orgKeys:      make(map[string]*SymmetricKey),
67
	}
68
69
	// Decrypt RSA private key if provided
70
	if encPrivateKeyStr != "" {
71
		privCS, err := ParseCipherString(encPrivateKeyStr)
72
		if err != nil {
73
			return nil, fmt.Errorf("parse encrypted private key: %w", err)
74
		}
75
		privKeyDER, err := userKey.Decrypt(privCS)
76
		if err != nil {
77
			return nil, fmt.Errorf("decrypt private key: %w", err)
78
		}
79
		kc.rsaPrivateKey, err = ParsePrivateKey(privKeyDER)
80
		if err != nil {
81
			return nil, fmt.Errorf("parse private key DER: %w", err)
82
		}
83
	}
84
85
	return kc, nil
86
}
87
88
// UserKey returns the user's symmetric key.
89
func (kc *KeyChain) UserKey() *SymmetricKey {
90
	kc.mu.RLock()
91
	defer kc.mu.RUnlock()
92
	return kc.userKey
93
}
94
95
// RSAPrivateKey returns the user's RSA private key.
96
func (kc *KeyChain) RSAPrivateKey() *rsa.PrivateKey {
97
	kc.mu.RLock()
98
	defer kc.mu.RUnlock()
99
	return kc.rsaPrivateKey
100
}
101
102
// AddOrgKey decrypts and stores an organization's symmetric key.
103
// The orgKey is RSA-encrypted with the user's public key.
104
func (kc *KeyChain) AddOrgKey(orgID, encOrgKeyStr string) error {
105
	kc.mu.Lock()
106
	defer kc.mu.Unlock()
107
108
	if kc.rsaPrivateKey == nil {
109
		return fmt.Errorf("no RSA private key available to decrypt org key")
110
	}
111
112
	cs, err := ParseCipherString(encOrgKeyStr)
113
	if err != nil {
114
		return fmt.Errorf("parse org key: %w", err)
115
	}
116
117
	orgKeyRaw, err := DecryptRSA(cs, kc.rsaPrivateKey)
118
	if err != nil {
119
		return fmt.Errorf("decrypt org key: %w", err)
120
	}
121
122
	orgKey, err := NewSymmetricKey(orgKeyRaw)
123
	if err != nil {
124
		return fmt.Errorf("create org symmetric key: %w", err)
125
	}
126
127
	kc.orgKeys[orgID] = orgKey
128
	return nil
129
}
130
131
// OrgKey returns the symmetric key for an organization.
132
func (kc *KeyChain) OrgKey(orgID string) *SymmetricKey {
133
	kc.mu.RLock()
134
	defer kc.mu.RUnlock()
135
	return kc.orgKeys[orgID]
136
}
137
138
// KeyForCipher returns the appropriate decryption key for a cipher.
139
// If the cipher has a per-item key, it decrypts and returns that.
140
// Otherwise returns the user key (personal) or org key (organization item).
141
func (kc *KeyChain) KeyForCipher(orgID, encCipherKey string) (*SymmetricKey, error) {
142
	kc.mu.RLock()
143
	defer kc.mu.RUnlock()
144
145
	// Determine the base key (user or org)
146
	var baseKey *SymmetricKey
147
	if orgID != "" {
148
		baseKey = kc.orgKeys[orgID]
149
		if baseKey == nil {
150
			return nil, fmt.Errorf("no key available for organization %s", orgID)
151
		}
152
	} else {
153
		baseKey = kc.userKey
154
	}
155
156
	// If there's a per-cipher key, decrypt it
157
	if encCipherKey != "" {
158
		cs, err := ParseCipherString(encCipherKey)
159
		if err != nil {
160
			return nil, fmt.Errorf("parse cipher key: %w", err)
161
		}
162
		cipherKeyRaw, err := baseKey.Decrypt(cs)
163
		if err != nil {
164
			return nil, fmt.Errorf("decrypt cipher key: %w", err)
165
		}
166
		return NewSymmetricKey(cipherKeyRaw)
167
	}
168
169
	return baseKey, nil
170
}
171
172
// MasterPasswordHash returns the hash to send to the server for authentication.
173
func (kc *KeyChain) MasterPasswordHash(password string) string {
174
	hash := HashMasterPassword(kc.masterKey, password)
175
	return base64.StdEncoding.EncodeToString(hash)
176
}
177
178
// GenerateUserKeys generates a new random symmetric key and RSA key pair,
179
// encrypting them with the stretched master key. Used for registration.
180
func GenerateUserKeys(stretchedKey *SymmetricKey) (encSymKey string, pubKeyB64 string, encPrivKey string, err error) {
181
	// Generate random 64-byte symmetric key
182
	symKeyRaw := make([]byte, 64)
183
	if _, err = rand.Read(symKeyRaw); err != nil {
184
		return "", "", "", fmt.Errorf("generate sym key: %w", err)
185
	}
186
187
	// Encrypt symmetric key with stretched master key
188
	symCS, err := stretchedKey.Encrypt(symKeyRaw)
189
	if err != nil {
190
		return "", "", "", fmt.Errorf("encrypt sym key: %w", err)
191
	}
192
	encSymKey = symCS.String()
193
194
	// Generate RSA key pair
195
	rsaKey, err := GenerateRSAKeyPair()
196
	if err != nil {
197
		return "", "", "", fmt.Errorf("generate RSA key: %w", err)
198
	}
199
200
	// Marshal public key
201
	pubKeyDER, err := MarshalPublicKey(&rsaKey.PublicKey)
202
	if err != nil {
203
		return "", "", "", fmt.Errorf("marshal public key: %w", err)
204
	}
205
	pubKeyB64 = base64.StdEncoding.EncodeToString(pubKeyDER)
206
207
	// Encrypt private key with the user symmetric key
208
	privKeyDER, err := MarshalPrivateKey(rsaKey)
209
	if err != nil {
210
		return "", "", "", fmt.Errorf("marshal private key: %w", err)
211
	}
212
213
	symKey, err := NewSymmetricKey(symKeyRaw)
214
	if err != nil {
215
		return "", "", "", fmt.Errorf("create sym key: %w", err)
216
	}
217
	privCS, err := symKey.Encrypt(privKeyDER)
218
	if err != nil {
219
		return "", "", "", fmt.Errorf("encrypt private key: %w", err)
220
	}
221
	encPrivKey = privCS.String()
222
223
	return encSymKey, pubKeyB64, encPrivKey, nil
224
}
225
226
// DeriveSendKey derives the encryption key for a Send from the raw send key bytes.
227
// The send key (stored encrypted in Send.Key) is expanded via HKDF to a 64-byte
228
// symmetric key (32 enc + 32 mac) using info="bitwarden-send".
229
func DeriveSendKey(sendKeyRaw []byte) (*SymmetricKey, error) {
230
	derived := HKDFExpandSlice(sendKeyRaw, []byte("bitwarden-send"), 64)
231
	return NewSymmetricKey(derived)
232
}
233
234
// Clear zeros out all sensitive key material.
235
func (kc *KeyChain) Clear() {
236
	kc.mu.Lock()
237
	defer kc.mu.Unlock()
238
239
	clear(kc.masterKey)
240
	kc.masterKey = nil
241
242
	if kc.stretchedKey != nil {
243
		clear(kc.stretchedKey.EncKey)
244
		clear(kc.stretchedKey.MacKey)
245
		kc.stretchedKey = nil
246
	}
247
	if kc.userKey != nil {
248
		clear(kc.userKey.EncKey)
249
		clear(kc.userKey.MacKey)
250
		kc.userKey = nil
251
	}
252
	kc.rsaPrivateKey = nil
253
	for k, v := range kc.orgKeys {
254
		clear(v.EncKey)
255
		clear(v.MacKey)
256
		delete(kc.orgKeys, k)
257
	}
258
}
259

Source Files