@@ -2,7 +2,12 @@ package api
2
2
3
3
import (
4
4
"context"
5
+ "encoding/hex"
5
6
"encoding/json"
7
+ "fmt"
8
+ "time"
9
+
10
+ "github.com/ProtonMail/gopenpgp/v3/crypto"
6
11
)
7
12
8
13
type MetadataKeyType string
@@ -40,14 +45,15 @@ type MetadataKey struct {
40
45
41
46
// MetadataPrivateKey is a MetadataPrivateKey
42
47
type MetadataPrivateKey struct {
43
- ID string `json:"id,omitempty"`
44
- MetadataKeyID string `json:"metadata_key_id,omitempty"`
45
- UserID * string `json:"user_id,omitempty"` // TODO, is this nullable. The Docs says yes and no
46
- Data string `json:"data,omitempty"`
47
- Created Time `json:"created,omitempty"`
48
- Modified Time `json:"modified,omitempty"`
49
- CreatedBy * string `json:"created_by,omitempty"`
50
- ModifiedBy * string `json:"modified_by,omitempty"`
48
+ ID string `json:"id,omitempty"`
49
+ MetadataKeyID string `json:"metadata_key_id,omitempty"`
50
+ UserID * string `json:"user_id,omitempty"` // TODO, is this nullable. The Docs says yes and no
51
+ Data string `json:"data,omitempty"`
52
+ Created Time `json:"created,omitempty"`
53
+ Modified Time `json:"modified,omitempty"`
54
+ CreatedBy * string `json:"created_by,omitempty"`
55
+ ModifiedBy * string `json:"modified_by,omitempty"`
56
+ DataSignedByCurrentUser * Time `json:"data_signed_by_current_user,omitempty"`
51
57
}
52
58
53
59
// MetadataPrivateKeyData is a MetadataPrivateKeyData
@@ -60,6 +66,8 @@ type MetadataPrivateKeyData struct {
60
66
ArmoredKey string `json:"armored_key,omitempty"`
61
67
// Passphrase must be Empty for Server Keys
62
68
Passphrase string `json:"passphrase,omitempty"`
69
+ // When this key was Signed by our User for Trusting new keys which where trusted on other Devices
70
+ Signed Time `json:"signed,omitempty"`
63
71
}
64
72
65
73
// GetMetadataKeysOptions are all available query parameters
@@ -70,6 +78,16 @@ type GetMetadataKeysOptions struct {
70
78
ContainMetadataPrivateKeys bool `url:"contain[metadata_private_keys],omitempty"`
71
79
}
72
80
81
+ // SetTrustedMetadatakeyFingerprint
82
+ func (c * Client ) SetTrustedMetadatakeyFingerprint (fingerprint string , signTime time.Time ) {
83
+ c .trustedMetadataKeyFingerprint = & fingerprint
84
+ }
85
+
86
+ // GetTrustedMetadatakeyFingerprint
87
+ func (c * Client ) GetTrustedMetadatakeyFingerprint () * string {
88
+ return c .trustedMetadataKeyFingerprint
89
+ }
90
+
73
91
// GetMetadataKeys gets all Passbolt GetMetadataKeys
74
92
func (c * Client ) GetMetadataKeys (ctx context.Context , opts * GetMetadataKeysOptions ) ([]MetadataKey , error ) {
75
93
msg , err := c .DoCustomRequestV5 (ctx , "GET" , "/metadata/keys.json" , nil , opts )
@@ -84,3 +102,169 @@ func (c *Client) GetMetadataKeys(ctx context.Context, opts *GetMetadataKeysOptio
84
102
}
85
103
return metadataKeys , nil
86
104
}
105
+
106
+ // GetMetadataKey gets a Metadata key, Personal indicates if the function should return the personal key,
107
+ // If personal keys have been disabled on the server then we return the shared key
108
+ // Returns the Key ID, Key Type and the Key itself
109
+ func (c * Client ) GetMetadataKey (ctx context.Context , personal bool ) (string , MetadataKeyType , * crypto.Key , error ) {
110
+ // if personal is requsted and it is allowed by the server, then return that
111
+ if personal && c .MetadataKeySettings ().AllowUsageOfPersonalKeys {
112
+ key , err := c .GetUserPrivateKeyCopy ()
113
+ if err != nil {
114
+ return "" , "" , nil , fmt .Errorf ("Get User Private Key: %w" , err )
115
+ }
116
+
117
+ me , err := c .GetMe (ctx )
118
+ if err != nil {
119
+ return "" , "" , nil , fmt .Errorf ("Get User Me: %w" , err )
120
+ }
121
+
122
+ if me .GPGKey == nil {
123
+ return "" , "" , nil , fmt .Errorf ("User Me GPG Key nil" )
124
+ }
125
+
126
+ return me .GPGKey .ID , MetadataKeyTypeUserKey , key , nil
127
+ }
128
+
129
+ keys , err := c .GetMetadataKeys (ctx , & GetMetadataKeysOptions {
130
+ ContainMetadataPrivateKeys : true ,
131
+ })
132
+ if err != nil {
133
+ return "" , "" , nil , fmt .Errorf ("Get Metadata Key: %w" , err )
134
+ }
135
+
136
+ // Get The Newest Metadata Key
137
+ metadatakey := keys [len (keys )- 1 ]
138
+ var privateMetadataKey * MetadataPrivateKey = nil
139
+ for _ , _privateMetadataKey := range metadatakey .MetadataPrivateKeys {
140
+ if * _privateMetadataKey .UserID == c .userID {
141
+ privateMetadataKey = & _privateMetadataKey
142
+ c .log ("Found privateMetadataKey for our user %v" , _privateMetadataKey .ID )
143
+ break
144
+ }
145
+ }
146
+
147
+ if privateMetadataKey == nil {
148
+ return "" , "" , nil , fmt .Errorf ("No Metadata Private key for our user" )
149
+ }
150
+
151
+ decPrivateMetadatakey , err := c .DecryptMessage (privateMetadataKey .Data )
152
+ if err != nil {
153
+ return "" , "" , nil , fmt .Errorf ("Decrypt Metadata Private Key Data: %w" , err )
154
+ }
155
+
156
+ var data MetadataPrivateKeyData
157
+ err = json .Unmarshal ([]byte (decPrivateMetadatakey ), & data )
158
+ if err != nil {
159
+ return "" , "" , nil , fmt .Errorf ("Parse Metadata Private Key Data" )
160
+ }
161
+
162
+ metadataPrivateKeyObj , err := GetPrivateKeyFromArmor (data .ArmoredKey , []byte (data .Passphrase ))
163
+ if err != nil {
164
+ return "" , "" , nil , fmt .Errorf ("Get Metadata Private Key: %w" , err )
165
+ }
166
+
167
+ // Verify the key
168
+ if c .GetTrustedMetadatakeyFingerprint () == nil || metadataPrivateKeyObj .GetFingerprint () != * c .GetTrustedMetadatakeyFingerprint () {
169
+
170
+ if c .trustedMetadataKeySigntime != nil && ! data .Signed .Time .After (* c .trustedMetadataKeySigntime ) {
171
+ return "" , "" , nil , fmt .Errorf ("New Metadata Key is older than the currently trusted one: %w" , err )
172
+ }
173
+
174
+ userPrivateKey , err := c .GetUserPrivateKeyCopy ()
175
+ if err != nil {
176
+ return "" , "" , nil , fmt .Errorf ("Get User Private Key Copy: %w" , err )
177
+ }
178
+
179
+ verify , err := c .pgp .Verify ().VerificationKey (userPrivateKey ).New ()
180
+ verifyRes , err := verify .VerifyInline ([]byte (privateMetadataKey .Data ), crypto .Armor )
181
+ if err != nil {
182
+ return "" , "" , nil , fmt .Errorf ("Verify User Metadata Private Key Signature: %w" , err )
183
+ }
184
+
185
+ signedByFingerprint := hex .EncodeToString (verifyRes .SignedByFingerprint ())
186
+ c .log ("Metadata Private key Signed by %v" , signedByFingerprint )
187
+ c .log ("User key Fingerprint %v" , userPrivateKey .GetFingerprint ())
188
+
189
+ // Check if the Metadata Private Key was signed by our User Private key
190
+ trusted := false
191
+ if signedByFingerprint == userPrivateKey .GetFingerprint () {
192
+ trusted = true
193
+ c .log ("New Metadata Private Key has been signed by our Private key" )
194
+ } else {
195
+ c .log ("New Metadata Private Key has failed the signature check" )
196
+ }
197
+
198
+ // Callback not Defined
199
+ if c .MetadataKeyUpdatedCallback == nil {
200
+ // Fail if there is a key pinned but the signature check failed
201
+ if c .trustedMetadataKeyFingerprint != nil || ! trusted {
202
+ return "" , "" , nil , fmt .Errorf ("Metadata Key has changed, The Callback is nil, There is a Key Pinned but the new one is not trusted" )
203
+ }
204
+ c .log ("No Callback is defined, No Metadata key is pinned and the Signature is by our Private key, automatically trusting" )
205
+
206
+ } else {
207
+ err = c .MetadataKeyUpdatedCallback (ctx , trusted , metadataPrivateKeyObj .GetFingerprint (), data .Signed .Time )
208
+ if err != nil {
209
+ return "" , "" , nil , fmt .Errorf ("Metadata Key has changed, Callback: %w" , err )
210
+ }
211
+ }
212
+
213
+ // Callback has not Returned an error, Thus the New Key has been accepted
214
+ c .SetTrustedMetadatakeyFingerprint (metadataPrivateKeyObj .GetFingerprint (), data .Signed .Time )
215
+ }
216
+
217
+ return metadatakey .ID , MetadataKeyTypeSharedKey , metadataPrivateKeyObj , nil
218
+ }
219
+
220
+ // GetMetadataKeyById is for fetching a specific metadatakey if needed for Decryption, these are not verified
221
+ func (c * Client ) GetMetadataKeyById (ctx context.Context , id string ) (* crypto.Key , error ) {
222
+ keys , err := c .GetMetadataKeys (ctx , & GetMetadataKeysOptions {
223
+ ContainMetadataPrivateKeys : true ,
224
+ })
225
+ if err != nil {
226
+ return nil , fmt .Errorf ("Get Metadata Key: %w" , err )
227
+ }
228
+ var key * MetadataKey
229
+ for _ , k := range keys {
230
+ if k .ID == id {
231
+ key = & k
232
+ break
233
+ }
234
+ }
235
+
236
+ if key == nil {
237
+ return nil , fmt .Errorf ("Metadata key not found: %v" , id )
238
+ }
239
+
240
+ if len (key .MetadataPrivateKeys ) == 0 {
241
+ return nil , fmt .Errorf ("No Metadata Private key for our user" )
242
+ }
243
+
244
+ if len (key .MetadataPrivateKeys ) > 1 {
245
+ return nil , fmt .Errorf ("More than 1 metadata Private key for our user" )
246
+ }
247
+
248
+ var privMetdata MetadataPrivateKey = key .MetadataPrivateKeys [0 ]
249
+ if * privMetdata .UserID != c .GetUserID () {
250
+ return nil , fmt .Errorf ("MetadataPrivateKey is not for our user id: %v" , privMetdata .UserID )
251
+ }
252
+
253
+ decPrivMetadatakey , err := c .DecryptMessage (privMetdata .Data )
254
+ if err != nil {
255
+ return nil , fmt .Errorf ("Decrypt Metadata Private Key Data: %w" , err )
256
+ }
257
+
258
+ var data MetadataPrivateKeyData
259
+ err = json .Unmarshal ([]byte (decPrivMetadatakey ), & data )
260
+ if err != nil {
261
+ return nil , fmt .Errorf ("Parse Metadata Private Key Data" )
262
+ }
263
+
264
+ metadataPrivateKeyObj , err := GetPrivateKeyFromArmor (data .ArmoredKey , []byte (data .Passphrase ))
265
+ if err != nil {
266
+ return nil , fmt .Errorf ("Get Metadata Private Key: %w" , err )
267
+ }
268
+
269
+ return metadataPrivateKeyObj , nil
270
+ }
0 commit comments