-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdecrypter.go
127 lines (108 loc) · 2.53 KB
/
decrypter.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
package kekcrypt
import (
"crypto/hmac"
"fmt"
"io"
"os"
"path/filepath"
"github.com/d4c5d1e0/kekcrypt/internal/crypto"
)
type Decrypter struct {
fPath string
f *os.File
str *crypto.StreamReader
key *crypto.DerivedKey
nonce []byte
hmacPos int64
header *FileHeader
out *os.File
}
func NewDecrypter(path string, key *crypto.DerivedKey, header *FileHeader) *Decrypter {
return &Decrypter{
fPath: path,
key: key,
nonce: header.Nonce,
header: header,
}
}
func (d *Decrypter) Decrypt(out string) error {
var err error
d.f, err = os.Open(d.fPath)
if err != nil {
return fmt.Errorf("hmac: open: %w", err)
}
defer d.f.Close()
// validate authenticity of data
err = d.updateMac()
if err != nil {
return fmt.Errorf("kekcrypt: decrypt: %w", err)
}
defaultName, err := DecryptFilename(d.key.Filename, d.header.EncodedFilename)
if err != nil {
return fmt.Errorf("kekcrypt: decrypt: filename: %w", err)
}
if len(out) == 0 {
dir, _ := filepath.Split(d.fPath)
out = filepath.Join(dir, defaultName)
}
d.out, err = os.OpenFile(out, os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
return fmt.Errorf("kekcrypt: decrypt: %w", err)
}
defer d.out.Close()
d.str, err = crypto.NewDecryptStreamReader(d.key, d.nonce, d.f)
if err != nil {
return fmt.Errorf("kekcrypt: reader: %w", err)
}
err = d.process()
if err != nil {
return fmt.Errorf("kekcrypt: process: %w", err)
}
return nil
}
func (d *Decrypter) process() error {
// retarget reader
_, err := d.f.Seek(0, io.SeekStart)
if err != nil {
return err
}
// skip fileHeader
trash := make([]byte, d.header.totalSize)
_, err = d.f.Read(trash)
if err != nil {
return fmt.Errorf("read: %w", err)
}
_, err = io.CopyN(d.out, d.str, d.hmacPos-int64(d.header.totalSize))
if err != nil && err != io.EOF {
return fmt.Errorf("copy: %w", err)
}
return nil
}
func (d *Decrypter) updateMac() error {
var err error
h := hmac.New(HMacFunc, d.key.Mac)
sig := make([]byte, HMacSize)
d.hmacPos, err = d.f.Seek(-HMacSize, io.SeekEnd)
if err != nil {
return fmt.Errorf("hmac: %w", err)
}
// read the last 64 bytes
_, err = io.ReadFull(d.f, sig)
if err != nil {
return fmt.Errorf("hmac: readfull: %w", err)
}
// retarget the reader
_, err = d.f.Seek(0, io.SeekStart)
if err != nil {
return fmt.Errorf("hmac: seek: %w", err)
}
// flush all our file to our hmac
_, err = io.CopyN(h, d.f, d.hmacPos)
if err != nil {
return fmt.Errorf("hmac: copy: %w", err)
}
if !hmac.Equal(h.Sum(nil), sig) {
return ErrBadAuthentication
}
return nil
}