|
|
@@ -11,11 +11,43 @@ const secretFakeTLSFirstByte byte = 0xee
|
|
11
|
11
|
|
|
12
|
12
|
var secretEmptyKey [SecretKeyLength]byte
|
|
13
|
13
|
|
|
|
14
|
+// Secret is a data structure that presents a secret.
|
|
|
15
|
+//
|
|
|
16
|
+// Telegram secret is not a simple string like
|
|
|
17
|
+// "ee367a189aee18fa31c190054efd4a8e9573746f726167652e676f6f676c65617069732e636f6d".
|
|
|
18
|
+// Actually, this is a serialized datastructure of 2 parts: key and host.
|
|
|
19
|
+//
|
|
|
20
|
+// ee367a189aee18fa31c190054efd4a8e9573746f726167652e676f6f676c65617069732e636f6d
|
|
|
21
|
+// |-|-------------------------------|-------------------------------------------
|
|
|
22
|
+// p key hostname
|
|
|
23
|
+//
|
|
|
24
|
+// Serialized secret starts with 'ee'. Actually, in the past we also had
|
|
|
25
|
+// 'dd' secrets and prefixless ones. But this is history. Currently,
|
|
|
26
|
+// we do have only 'ee' secrets which mean faketls + protection from
|
|
|
27
|
+// statistical attacks on a length. 'ee' is a byte 238 (0xee).
|
|
|
28
|
+//
|
|
|
29
|
+// After that, we have 16 bytes of the key. This is a random generated
|
|
|
30
|
+// secret data of the proxy and this data is used to derive
|
|
|
31
|
+// authentication schemas. These secrets are mixed into hmacs and sha256
|
|
|
32
|
+// checksums which are used to build AEAD ciphers for obfuscated2
|
|
|
33
|
+// protocol and ensure faketls handshake.
|
|
|
34
|
+//
|
|
|
35
|
+// Host is a domain fronting hostname in latin1 (ASCII) encoding. This
|
|
|
36
|
+// hostname should be used for SNI in faketls and MTG verifies it. Also,
|
|
|
37
|
+// this is when mtg gets about a domain fronting hostname.
|
|
|
38
|
+//
|
|
|
39
|
+// Secrets can be serialized into 2 forms: hex and base64. If
|
|
|
40
|
+// you decode both forms into bytes, you'll get the same byte array.
|
|
|
41
|
+// Telegram clients nowadays accept all forms.
|
|
14
|
42
|
type Secret struct {
|
|
15
|
|
- Key [SecretKeyLength]byte
|
|
|
43
|
+ // Key is a set of bytes used for traffic authentication.
|
|
|
44
|
+ Key [SecretKeyLength]byte
|
|
|
45
|
+
|
|
|
46
|
+ // Host is a domain fronting hostname.
|
|
16
|
47
|
Host string
|
|
17
|
48
|
}
|
|
18
|
49
|
|
|
|
50
|
+// MarshalText is to support text.Marshaller interface.
|
|
19
|
51
|
func (s Secret) MarshalText() ([]byte, error) {
|
|
20
|
52
|
if s.Valid() {
|
|
21
|
53
|
return []byte(s.String()), nil
|
|
|
@@ -24,6 +56,7 @@ func (s Secret) MarshalText() ([]byte, error) {
|
|
24
|
56
|
return nil, nil
|
|
25
|
57
|
}
|
|
26
|
58
|
|
|
|
59
|
+// MarshalText is to support text.Unmarshaller interface.
|
|
27
|
60
|
func (s *Secret) UnmarshalText(data []byte) error {
|
|
28
|
61
|
text := string(data)
|
|
29
|
62
|
if text == "" {
|
|
|
@@ -62,18 +95,22 @@ func (s *Secret) UnmarshalText(data []byte) error {
|
|
62
|
95
|
return nil
|
|
63
|
96
|
}
|
|
64
|
97
|
|
|
|
98
|
+// Valid checks if this secret is valid and can be used in proxy.
|
|
65
|
99
|
func (s Secret) Valid() bool {
|
|
66
|
100
|
return s.Key != secretEmptyKey && s.Host != ""
|
|
67
|
101
|
}
|
|
68
|
102
|
|
|
|
103
|
+// String is to support fmt.Stringer interface.
|
|
69
|
104
|
func (s Secret) String() string {
|
|
70
|
105
|
return s.Base64()
|
|
71
|
106
|
}
|
|
72
|
107
|
|
|
|
108
|
+// Base64 returns a base64-encoded form of this secret.
|
|
73
|
109
|
func (s Secret) Base64() string {
|
|
74
|
110
|
return base64.RawURLEncoding.EncodeToString(s.makeBytes())
|
|
75
|
111
|
}
|
|
76
|
112
|
|
|
|
113
|
+// Hex returns a hex-encoded form of this secret (ee-secret).
|
|
77
|
114
|
func (s Secret) Hex() string {
|
|
78
|
115
|
return hex.EncodeToString(s.makeBytes())
|
|
79
|
116
|
}
|
|
|
@@ -85,6 +122,7 @@ func (s *Secret) makeBytes() []byte {
|
|
85
|
122
|
return data
|
|
86
|
123
|
}
|
|
87
|
124
|
|
|
|
125
|
+// GenerateSecret makes a new secret with a given hostname.
|
|
88
|
126
|
func GenerateSecret(hostname string) Secret {
|
|
89
|
127
|
s := Secret{
|
|
90
|
128
|
Host: hostname,
|
|
|
@@ -97,6 +135,7 @@ func GenerateSecret(hostname string) Secret {
|
|
97
|
135
|
return s
|
|
98
|
136
|
}
|
|
99
|
137
|
|
|
|
138
|
+// ParseSecret parses a secret (both hex and base64 forms).
|
|
100
|
139
|
func ParseSecret(secret string) (Secret, error) {
|
|
101
|
140
|
s := Secret{}
|
|
102
|
141
|
|