This repository was archived by the owner on Sep 2, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 33
/
Copy pathsecureData.go
159 lines (149 loc) · 4.6 KB
/
secureData.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
package models
import (
"crypto/rand"
"encoding/json"
"errors"
"fmt"
"io"
"golang.org/x/crypto/nacl/box"
)
// SecureData is used to store and send access controlled Param values
// to the locations they are needed at. SecureData uses a simple
// encryption mechanism based on the NACL Box API (as implemented by
// libsodium, golang.org/x/crypto/nacl/box, and many others).
//
// All fields in this struct will be marshalled into JSON as base64
// encoded strings.
//
// swagger:model
type SecureData struct {
// Key is the ephemeral public key created by Seal(). It must not
// be modified after Seal() has completed, and it must be 32 bytes
// long.
Key []byte
// Nonce must be 24 bytes of cryptographically random numbers. It is
// populated by Seal(), and must not be modified afterwards.
Nonce []byte
// Payload is the encrypted payload generated by Seal(). It must
// not be modified, and will be 16 bytes longer than the unencrypted
// data.
Payload []byte
}
var (
BadKey = errors.New("Key must be 32 bytes long")
BadNonce = errors.New("Nonce must be 24 bytes long")
Corrupt = errors.New("SecureData corrupted")
)
func IsSecureData(val interface{}) bool {
s := &SecureData{}
if err := Remarshal(val, s); err != nil {
return false
}
return s.Validate() == nil
}
// Validate makes sure that the lengths we expect for the Key and
// Nonce are correct.
func (s *SecureData) Validate() error {
if len(s.Key) != 32 {
return BadKey
}
if len(s.Nonce) != 24 {
return BadNonce
}
if len(s.Payload) < box.Overhead {
return Corrupt
}
return nil
}
// Seal takes curve25519 public key advertised by where the payload
// should be stored, and fills in the SecureData with the data
// required for the Open operation to succeed.
//
// Seal performs the following operations internally:
//
// * Generate ephemeral cuve25519 public and private keys from the
// system's cryptographically secure random number generator.
//
// * Generate a 24 byte nonce from the same random number generator used
// to create the keys.
//
// * Encrypt the data using the peer public key, the ephemeral private key,
// and the generated nonce.
//
// * Populate s.Key with the ephemeral public key, s.Nonce with the
// generated nonce, and s.Payload with the encrypted data.
func (s *SecureData) Seal(peerPublicKey *[32]byte, data []byte) error {
ourPublicKey, ourPrivateKey, err := box.GenerateKey(rand.Reader)
if err != nil {
return fmt.Errorf("Error generating ephemeral local keys: %v", err)
}
s.Key = ourPublicKey[:]
nonce := [24]byte{}
_, err = io.ReadFull(rand.Reader, nonce[:])
if err != nil {
return fmt.Errorf("Error generating nonce: %v", err)
}
s.Nonce = nonce[:]
s.Payload = box.Seal(nil, data, &nonce, peerPublicKey, ourPrivateKey)
return nil
}
// Marshal marshals the passed-in data into JSON and calls Seal() with
// peerPublicKey and the marshalled data.
func (s *SecureData) Marshal(peerPublicKey []byte, data interface{}) error {
if len(peerPublicKey) != 32 {
return BadKey
}
buf, err := json.Marshal(data)
if err != nil {
return err
}
ppk := [32]byte{}
copy(ppk[:], peerPublicKey)
return s.Seal(&ppk, buf)
}
// Open opens a sealed SecureData item. It takes the private key that
// matches the peerPublicKey passed to the Seal() operation. If the
// SecureData object has not been corrupted or tampered with, Open()
// will return the decrypted data , otherwise it will return an error.
//
// Open performs the following operations internally:
//
// * Validate that the lengths of all the fields of the SecureData
// struct are within expected ranges. This is not required for
// correctness, but it alows us to return nicer errors.
//
// * Extract the stored ephemeral public key and nonce from the
// SecureData object.
//
// * Decrypt Payload using the extracted nonce, the extracted public key,
// and the passed-in private key.
//
// * If any errors were returned in the decrypt process, return a
// Corrupt error, otherwise return the decrypted data.
func (s *SecureData) Open(targetPrivateKey *[32]byte) ([]byte, error) {
err := s.Validate()
if err != nil {
return nil, err
}
peerPublicKey := [32]byte{}
copy(peerPublicKey[:], s.Key[:])
nonce := [24]byte{}
copy(nonce[:], s.Nonce[:])
res, opened := box.Open(nil, s.Payload, &nonce, &peerPublicKey, targetPrivateKey)
if !opened {
return nil, Corrupt
}
return res, nil
}
func (s *SecureData) Unmarshal(targetPrivateKey []byte, res interface{}) error {
if len(targetPrivateKey) != 32 {
return BadKey
}
tpk := [32]byte{}
copy(tpk[:], targetPrivateKey)
buf, err := s.Open(&tpk)
if err != nil {
return err
}
return json.Unmarshal(buf, res)
}