-
Notifications
You must be signed in to change notification settings - Fork 35
/
seal.go
170 lines (138 loc) · 5.66 KB
/
seal.go
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
165
166
167
168
169
170
// Copyright (c) Edgeless Systems GmbH.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
// Package seal implements sealing operations for the Coordinator.
package seal
import (
"crypto/rand"
"encoding/binary"
"errors"
"fmt"
"github.com/edgelesssys/ego/ecrypto"
)
// EncryptionKeyError occurs if the encryption key cannot be unsealed.
type EncryptionKeyError struct {
Err error
}
func (e *EncryptionKeyError) Error() string {
return fmt.Sprintf("cannot unseal encryption key: %s", e.Err)
}
func (e *EncryptionKeyError) Unwrap() error {
return e.Err
}
// ErrMissingEncryptionKey occurs if the encryption key is not set.
var ErrMissingEncryptionKey = errors.New("encryption key not set")
// Sealer handles encryption and decryption of data.
type Sealer interface {
// Seal encrypts data using the encryption key of the Sealer.
Seal(unencryptedData []byte, toBeEncrypted []byte) (encryptedData []byte, err error)
// Unseal decrypts the given data and returns the plain text, as well as the unencrypted metadata.
Unseal(encryptedData []byte) (unencryptedData []byte, decryptedData []byte, err error)
// SealEncryptionKey seals an encryption key using the sealer.
SealEncryptionKey(key []byte, mode Mode) (encryptedKey []byte, err error)
// SetEncryptionKey sets the encryption key of the sealer.
SetEncryptionKey(key []byte)
// UnsealEncryptionKey decrypts an encrypted key.
UnsealEncryptionKey(encryptedKey []byte) ([]byte, error)
}
// AESGCMSealer implements the Sealer interface using AES-GCM for confidentiality and authentication.
type AESGCMSealer struct {
encryptionKey []byte
}
// NewAESGCMSealer creates and initializes a new AESGCMSealer object.
func NewAESGCMSealer() *AESGCMSealer {
return &AESGCMSealer{}
}
// Unseal decrypts sealedData and returns the decrypted data,
// as well as the prefixed unencrypted metadata of the cipher text.
func (s *AESGCMSealer) Unseal(sealedData []byte) ([]byte, []byte, error) {
unencryptedData, cipherText, err := prepareCipherText(sealedData)
if err != nil {
return unencryptedData, nil, err
}
if s.encryptionKey == nil {
return unencryptedData, nil, fmt.Errorf("decrypting sealed data: %w", ErrMissingEncryptionKey)
}
// Decrypt data with the unsealed encryption key and return it
decryptedData, err := ecrypto.Decrypt(cipherText, s.encryptionKey, nil)
if err != nil {
return unencryptedData, nil, fmt.Errorf("decrypting sealed data: %w", err)
}
return unencryptedData, decryptedData, nil
}
// Seal encrypts and stores information to the fs.
func (s *AESGCMSealer) Seal(unencryptedData []byte, toBeEncrypted []byte) ([]byte, error) {
return sealData(unencryptedData, toBeEncrypted, s.encryptionKey)
}
// SealEncryptionKey seals an encryption key with the selected enclave key.
func (s *AESGCMSealer) SealEncryptionKey(encryptionKey []byte, mode Mode) ([]byte, error) {
switch mode {
case ModeProductKey:
return ecrypto.SealWithProductKey(encryptionKey, nil)
case ModeUniqueKey:
return ecrypto.SealWithUniqueKey(encryptionKey, nil)
}
return nil, errors.New("sealing is disabled")
}
// SetEncryptionKey sets the encryption key of the Sealer.
func (s *AESGCMSealer) SetEncryptionKey(encryptionKey []byte) {
s.encryptionKey = encryptionKey
}
// UnsealEncryptionKey unseals the encryption key.
func (s *AESGCMSealer) UnsealEncryptionKey(encryptedKey []byte) ([]byte, error) {
// Decrypt stored encryption key with seal key
encryptionKey, err := ecrypto.Unseal(encryptedKey, nil)
if err != nil {
return nil, err
}
return encryptionKey, nil
}
// GenerateEncryptionKey generates a new random 16 byte encryption key.
func GenerateEncryptionKey() ([]byte, error) {
encryptionKey := make([]byte, 16)
_, err := rand.Read(encryptionKey)
if err != nil {
return nil, err
}
return encryptionKey, nil
}
// prepareCipherText validates format of the given sealed data.
// It returns the unencrypted metadata and the cipher text.
func prepareCipherText(sealedData []byte) (unencryptedData []byte, cipherText []byte, err error) {
if len(sealedData) <= 4 {
return nil, nil, errors.New("sealed state is missing data")
}
// Retrieve recovery secret hash map
encodedUnencryptedDataLength := binary.LittleEndian.Uint32(sealedData[:4])
// Check if we do not go out of bounds
if 4+encodedUnencryptedDataLength > uint32(len(sealedData)) {
return nil, nil, errors.New("sealed state is corrupted, embedded length does not fit the data")
}
if encodedUnencryptedDataLength != 0 {
unencryptedData = sealedData[4 : 4+encodedUnencryptedDataLength]
}
cipherText = sealedData[4+encodedUnencryptedDataLength:]
return unencryptedData, cipherText, nil
}
// sealData encrypts data and seals it with the given key.
// It returns the encrypted data prefixed with the unencrypted data and it's length.
//
// Format: uint32(littleEndian(len(unencryptedData))) || unencryptedData || encrypt(toBeEncrypted).
func sealData(unencryptedData, toBeEncrypted, encryptionKey []byte) ([]byte, error) {
if encryptionKey == nil {
return nil, fmt.Errorf("encrypting data: %w", ErrMissingEncryptionKey)
}
// Encrypt data to seal with generated encryption key
encryptedData, err := ecrypto.Encrypt(toBeEncrypted, encryptionKey, nil)
if err != nil {
return nil, fmt.Errorf("encrypting data: %w", err)
}
unencryptedDataLength := make([]byte, 4)
binary.LittleEndian.PutUint32(unencryptedDataLength, uint32(len(unencryptedData)))
unencryptedData = append(unencryptedDataLength, unencryptedData...)
// Append unencrypted data with encrypted data
encryptedData = append(unencryptedData, encryptedData...)
return encryptedData, nil
}