Document crypto and master key JSON struct

This commit is contained in:
Alexander Neumann 2015-03-22 21:03:01 +01:00
parent 65a653693e
commit 0ed2a066a0
4 changed files with 88 additions and 18 deletions

View file

@ -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")

View file

@ -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) {

View file

@ -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
View file

@ -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>"