forked from TrueCloudLab/restic
Document crypto and master key JSON struct
This commit is contained in:
parent
65a653693e
commit
0ed2a066a0
4 changed files with 88 additions and 18 deletions
|
@ -23,11 +23,11 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cmd CmdCat) Usage() string {
|
func (cmd CmdCat) Usage() string {
|
||||||
return "[data|tree|snapshot|key|lock] ID"
|
return "[data|tree|snapshot|key|masterkey|lock] ID"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cmd CmdCat) Execute(args []string) error {
|
func (cmd CmdCat) Execute(args []string) error {
|
||||||
if len(args) != 2 {
|
if len(args) < 1 || (args[0] != "masterkey" && len(args) != 2) {
|
||||||
return fmt.Errorf("type or ID not specified, Usage: %s", cmd.Usage())
|
return fmt.Errorf("type or ID not specified, Usage: %s", cmd.Usage())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,18 +38,21 @@ func (cmd CmdCat) Execute(args []string) error {
|
||||||
|
|
||||||
tpe := args[0]
|
tpe := args[0]
|
||||||
|
|
||||||
id, err := backend.ParseID(args[1])
|
var id backend.ID
|
||||||
if err != nil {
|
if tpe != "masterkey" {
|
||||||
id = nil
|
id, err = backend.ParseID(args[1])
|
||||||
|
|
||||||
if tpe != "snapshot" {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// find snapshot id with prefix
|
|
||||||
id, err = s.FindSnapshot(args[1])
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
id = nil
|
||||||
|
|
||||||
|
if tpe != "snapshot" {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// find snapshot id with prefix
|
||||||
|
id, err = s.FindSnapshot(args[1])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +117,14 @@ func (cmd CmdCat) Execute(args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(string(buf))
|
fmt.Println(string(buf))
|
||||||
|
return nil
|
||||||
|
case "masterkey":
|
||||||
|
buf, err := json.MarshalIndent(s.Key().Master(), "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(string(buf))
|
||||||
return nil
|
return nil
|
||||||
case "lock":
|
case "lock":
|
||||||
return errors.New("not yet implemented")
|
return errors.New("not yet implemented")
|
||||||
|
|
37
crypto.go
37
crypto.go
|
@ -5,6 +5,7 @@ import (
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -147,6 +148,42 @@ func generateRandomIV() (iv IV) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type jsonMACKey struct {
|
||||||
|
K []byte `json:"k"`
|
||||||
|
R []byte `json:"r"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MACKey) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(jsonMACKey{K: m.K[:], R: m.R[:]})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MACKey) UnmarshalJSON(data []byte) error {
|
||||||
|
j := jsonMACKey{}
|
||||||
|
err := json.Unmarshal(data, &j)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
copy(m.K[:], j.K)
|
||||||
|
copy(m.R[:], j.R)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *AESKey) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(k[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *AESKey) UnmarshalJSON(data []byte) error {
|
||||||
|
d := make([]byte, AESKeySize)
|
||||||
|
err := json.Unmarshal(data, &d)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
copy(k[:], d)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Encrypt encrypts and signs data. Stored in ciphertext is IV || Ciphertext ||
|
// Encrypt encrypts and signs data. Stored in ciphertext is IV || Ciphertext ||
|
||||||
// MAC. Encrypt returns the ciphertext's length.
|
// MAC. Encrypt returns the ciphertext's length.
|
||||||
func Encrypt(ks *MasterKeys, ciphertext, plaintext []byte) (int, error) {
|
func Encrypt(ks *MasterKeys, ciphertext, plaintext []byte) (int, error) {
|
||||||
|
|
|
@ -107,9 +107,21 @@ last 32 byte). If the password is incorrect or the key file has been tampered
|
||||||
with, the computed MAC will not match the last 16 bytes of the data, and
|
with, the computed MAC will not match the last 16 bytes of the data, and
|
||||||
restic exits with an error. Otherwise, the data is decrypted with the
|
restic exits with an error. Otherwise, the data is decrypted with the
|
||||||
encryption key derived from `scrypt`. This yields a JSON document which
|
encryption key derived from `scrypt`. This yields a JSON document which
|
||||||
contains the master signing and encryption keys for this repository. All data
|
contains the master signing and encryption keys for this repository, encoded in
|
||||||
in the repository is encrypted and signed with these master keys with AES-256
|
Base64. The command `restic cat masterkey` can be used as follows to decrypt
|
||||||
in Counter mode and signed with Poly1305-AES as described above.
|
and pretty-print the master key:
|
||||||
|
|
||||||
|
$ restic -r /tmp/restic-repo cat masterkey
|
||||||
|
{
|
||||||
|
"sign": {
|
||||||
|
"k": "evFWd9wWlndL9jc501268g==",
|
||||||
|
"r": "E9eEDnSJZgqwTOkDtOp+Dw=="
|
||||||
|
},
|
||||||
|
"encrypt": "UQCqa0lKZ94PygPxMRqkePTZnHRYh1k1pX2k2lM2v3Q="
|
||||||
|
}
|
||||||
|
|
||||||
|
All data in the repository is encrypted and signed with these master keys with
|
||||||
|
AES-256 in Counter mode and signed with Poly1305-AES as described above.
|
||||||
|
|
||||||
A repository can have several different passwords, with a key file for each.
|
A repository can have several different passwords, with a key file for each.
|
||||||
This way, the password can be changed without having to re-encrypt all data.
|
This way, the password can be changed without having to re-encrypt all data.
|
||||||
|
|
15
key.go
15
key.go
|
@ -64,8 +64,8 @@ type Key struct {
|
||||||
// encrypted and signed as a JSON data structure in the Data field of the Key
|
// encrypted and signed as a JSON data structure in the Data field of the Key
|
||||||
// structure.
|
// structure.
|
||||||
type MasterKeys struct {
|
type MasterKeys struct {
|
||||||
Sign MACKey
|
Sign MACKey `json:"sign"`
|
||||||
Encrypt AESKey
|
Encrypt AESKey `json:"encrypt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateKey initializes a master key in the given backend and encrypts it with
|
// CreateKey initializes a master key in the given backend and encrypts it with
|
||||||
|
@ -304,6 +304,17 @@ func (k *Key) DecryptUserFrom(rd io.Reader) (io.ReadCloser, error) {
|
||||||
return DecryptFrom(k.user, rd)
|
return DecryptFrom(k.user, rd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Master() returns the master keys for this repository. Only included for
|
||||||
|
// debug purposes.
|
||||||
|
func (k *Key) Master() *MasterKeys {
|
||||||
|
return k.master
|
||||||
|
}
|
||||||
|
|
||||||
|
// User() returns the user keys for this key. Only included for debug purposes.
|
||||||
|
func (k *Key) User() *MasterKeys {
|
||||||
|
return k.user
|
||||||
|
}
|
||||||
|
|
||||||
func (k *Key) String() string {
|
func (k *Key) String() string {
|
||||||
if k == nil {
|
if k == nil {
|
||||||
return "<Key nil>"
|
return "<Key nil>"
|
||||||
|
|
Loading…
Reference in a new issue