keepassxc

Форк
0
190 строк · 4.0 Кб
1
package main
2

3
import (
4
	"bytes"
5
	"crypto/hmac"
6
	"crypto/sha1"
7
	"fmt"
8
	"io"
9
	"io/ioutil"
10
	"log"
11
	"os"
12
	"syscall"
13

14
	"encoding/binary"
15
	"encoding/hex"
16

17
	"golang.org/x/crypto/ssh/terminal"
18
)
19

20
const fileVersionCriticalMask uint32 = 0xFFFF0000
21
const argon2Salt = "S"
22
const endOfHeader = 0
23
const endOfVariantMap = 0
24
const kdfParameters = 11
25

26
func readSecret() (string, error) {
27
	fmt.Print("Secret: ")
28
	byteSecret, err := terminal.ReadPassword(int(syscall.Stdin))
29
	fmt.Println()
30
	secret := string(byteSecret)
31
	return secret, err
32

33
}
34
func readHeaderField(reader io.Reader) (bool, byte, []byte, error) {
35
	var fieldID byte
36
	err := binary.Read(reader, binary.LittleEndian, &fieldID)
37
	if err != nil {
38
		return true, 0, nil, err
39
	}
40

41
	if fieldID == endOfHeader {
42
		return false, 0, nil, nil
43
	}
44

45
	var fieldLength uint32
46
	err = binary.Read(reader, binary.LittleEndian, &fieldLength)
47
	if err != nil {
48
		return true, fieldID, nil, err
49
	}
50

51
	fieldData := make([]byte, fieldLength)
52
	err = binary.Read(reader, binary.LittleEndian, &fieldData)
53
	if err != nil {
54
		return true, fieldID, fieldData, err
55
	}
56
	return true, fieldID, fieldData, nil
57
}
58
func readVariantMap(reader io.Reader) ([]byte, error) {
59
	var version uint16
60
	err := binary.Read(reader, binary.LittleEndian, &version)
61
	if err != nil {
62
		return nil, err
63
	}
64

65
	var fieldType byte
66
	for err = binary.Read(reader, binary.LittleEndian, &fieldType); fieldType != endOfVariantMap && err == nil; err = binary.Read(reader, binary.LittleEndian, &fieldType) {
67

68
		var nameLen uint32
69
		err = binary.Read(reader, binary.LittleEndian, &nameLen)
70
		if err != nil {
71
			return nil, err
72
		}
73

74
		nameBytes := make([]byte, nameLen)
75
		err = binary.Read(reader, binary.LittleEndian, &nameBytes)
76
		if err != nil {
77
			return nil, err
78
		}
79

80
		name := string(nameBytes)
81

82
		var valueLen uint32
83
		err = binary.Read(reader, binary.LittleEndian, &valueLen)
84
		if err != nil {
85
			return nil, err
86
		}
87

88
		value := make([]byte, valueLen)
89
		err = binary.Read(reader, binary.LittleEndian, &value)
90
		if err != nil {
91
			return nil, err
92
		}
93

94
		if name == argon2Salt {
95
			return value, nil
96
		}
97
	}
98
	return nil, nil
99
}
100
func readKeepassHeader(keepassFilename string) ([]byte, error) {
101
	dbFile, err := os.Open(keepassFilename)
102
	defer dbFile.Close()
103
	if err != nil {
104
		return nil, err
105
	}
106

107
	var sig1, sig2, version uint32
108
	err = binary.Read(dbFile, binary.LittleEndian, &sig1)
109
	if err != nil {
110
		return nil, err
111
	}
112

113
	err = binary.Read(dbFile, binary.LittleEndian, &sig2)
114
	if err != nil {
115
		return nil, err
116
	}
117

118
	err = binary.Read(dbFile, binary.LittleEndian, &version)
119
	if err != nil {
120
		return nil, err
121
	}
122

123
	version &= fileVersionCriticalMask
124

125
	var fieldData []byte
126
	var fieldID byte
127
	var moreFields bool
128

129
	for moreFields, fieldID, fieldData, err = readHeaderField(dbFile); moreFields && err == nil && fieldID != kdfParameters; moreFields, fieldID, fieldData, err = readHeaderField(dbFile) {
130
	}
131
	if err != nil {
132
		return nil, err
133
	}
134

135
	fieldReader := bytes.NewReader(fieldData)
136
	seed, err := readVariantMap(fieldReader)
137
	if err != nil {
138
		return nil, err
139
	}
140
	return seed, nil
141

142
}
143
func main() {
144
	log.SetFlags(0)
145
	args := os.Args
146

147
	if len(args) != 3 {
148
		log.Fatalf("usage: %s keepassxc-database keyfile", args[0])
149
	}
150

151
	dbFilename := args[1]
152
	keyFilename := args[2]
153

154
	if _, err := os.Stat(keyFilename); err == nil {
155
		log.Fatalf("keyfile already exists, exiting")
156
	}
157
	secretHex, err := readSecret()
158
	if err != nil {
159
		log.Fatalf("couldn't read secret from stdin: %s", err)
160
	}
161
	secret, err := hex.DecodeString(secretHex)
162

163
	if err != nil {
164
		log.Fatalf("couldn't decode secret: %s", err)
165
	}
166

167
	challenge, err := readKeepassHeader(dbFilename)
168
	if err != nil {
169
		log.Fatalf("couldn't read challenge: %s", err)
170
	}
171

172
	if len(challenge) < 64 {
173
		padd := make([]byte, 64-len(challenge))
174
		for i, _ := range padd {
175
			padd[i] = byte(64-len(challenge))
176
		}
177
		challenge = append(challenge[:], padd[:]...)
178
	}
179

180
	mac := hmac.New(sha1.New, secret)
181
	mac.Write(challenge)
182

183
	hash := mac.Sum(nil)
184

185
	err = ioutil.WriteFile(keyFilename, hash, 0644)
186
	if err != nil {
187
		log.Fatalf("couldn't write keyfile: %s", err)
188
	}
189

190
}
191

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.