forked from dunhamsteve/ios
/
keybag.go
157 lines (142 loc) · 3.29 KB
/
keybag.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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// This has been run against a keybag from Manifest.plist in an iOS backup.
// It will probably need work to handle other keybag variants.
//
// /var/db/lockdown plists appear to no longer contain keybags. (And 0x835 was needed to decrypt them anyway.)
//
package keybag
import (
"crypto/sha1"
"crypto/sha256"
"encoding/binary"
"errors"
"fmt"
"log"
"encoding/hex"
"time"
"github.com/chiefbrain/ios/crypto/aeswrap"
"golang.org/x/crypto/pbkdf2"
)
type Key struct {
UUID []byte
Class uint32
Wrap uint32
KeyType uint32
WrappedKey []byte
Key []byte
}
type Keybag struct {
Version uint32
Type uint32
UUID []byte
HMAC []byte
Wrap uint32
Salt []byte
Iter uint32
AuxSalt []byte
AuxIter uint32
Keys []*Key
}
var be = binary.BigEndian
func Read(data []byte) Keybag {
var kb Keybag
var key *Key
var state = 0
for pos := 0; pos+8 < len(data); {
fourcc := string(data[pos : pos+4])
size := int(be.Uint32(data[pos+4 : pos+8]))
pos += 8
value := data[pos : pos+size]
var ivalue uint32
pos += size
if size == 4 {
ivalue = be.Uint32(value[:4])
}
// UUID appears once in the top matter, then once per entry thereafter.
if state < 2 {
switch fourcc {
case "VERS":
kb.Version = ivalue
case "TYPE":
kb.Type = ivalue
case "WRAP":
kb.Wrap = ivalue
case "HMCK":
kb.HMAC = value
case "SALT":
kb.Salt = value
case "ITER":
kb.Iter = ivalue
case "DPWT":
// not sure what this one is
case "DPIC":
kb.AuxIter = ivalue
case "DPSL":
kb.AuxSalt = value
case "UUID":
state++
if state == 2 {
// Rewind position to let the UUID show up again
pos -= 8 + size
} else {
kb.UUID = value
}
default:
log.Fatalln("fourcc", fourcc, "not handled", len(value), hex.EncodeToString(value))
}
} else {
switch fourcc {
case "UUID":
key = new(Key)
kb.Keys = append(kb.Keys, key)
key.UUID = value
case "CLAS":
key.Class = ivalue
case "WRAP":
key.Wrap = ivalue
case "KTYP":
key.KeyType = ivalue
case "WPKY":
key.WrappedKey = value
default:
log.Fatal("fourcc ", fourcc, " not handled")
}
}
}
return kb
}
// Get a class key, or nil if not available
func (kb *Keybag) GetClassKey(class uint32) []byte {
for _, key := range kb.Keys {
if key.Class == class {
return key.Key
}
}
return nil
}
// SetPassword decrypts the keybag, recovering some of the keys.
func (kb *Keybag) SetPassword(password string, encrypted bool) error {
var passkey = []byte(password)
if encrypted { // iOS 10.2
if len(password) == 64 {
passkey, _ = hex.DecodeString(password)
} else {
start := time.Now()
if kb.AuxIter > 0 {
passkey = pbkdf2.Key(passkey, kb.AuxSalt, int(kb.AuxIter), 32, sha256.New)
}
passkey = pbkdf2.Key(passkey, kb.Salt, int(kb.Iter), 32, sha1.New)
fmt.Println("key derivation took", time.Now().Sub(start), "use the password", hex.EncodeToString(passkey), "to skip")
}
} else {
passkey = pbkdf2.Key([]byte(password), kb.Salt, int(kb.Iter), 32, sha1.New)
}
for _, key := range kb.Keys {
if key.Wrap == 2 { // 3 means we need 0x835 too, 1 means only 0x835
key.Key = aeswrap.Unwrap(passkey, key.WrappedKey)
if key.Key == nil {
return errors.New("Bad password")
}
}
}
return nil
}