-
-
Notifications
You must be signed in to change notification settings - Fork 115
/
rsa_pad.go
161 lines (138 loc) · 5.11 KB
/
rsa_pad.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
158
159
160
161
package crypto
import (
"bytes"
"crypto/aes"
"crypto/rsa"
"crypto/sha256"
"io"
"math/big"
"github.com/go-faster/errors"
"github.com/go-faster/xor"
"github.com/gotd/ige"
"github.com/gotd/td/bin"
)
func reverseBytes(s []byte) {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
}
const (
rsaPadDataLimit = 144
dataWithPaddingLength = 192
dataWithHashLength = dataWithPaddingLength + sha256.Size
tempKeySize = 32
)
// RSAPad encrypts given data with RSA, prefixing with a hash.
//
// See https://core.telegram.org/mtproto/auth_key#presenting-proof-of-work-server-authentication.
func RSAPad(data []byte, key *rsa.PublicKey, randomSource io.Reader) ([]byte, error) {
// 1) data_with_padding := data + random_padding_bytes; — where random_padding_bytes are
// chosen so that the resulting length of data_with_padding is precisely 192 bytes, and
// data is the TL-serialized data to be encrypted as before.
//
// One has to check that data is not longer than 144 bytes.
if len(data) > rsaPadDataLimit {
return nil, errors.Errorf("data length is bigger that 144 (%d)", len(data))
}
dataWithPadding := make([]byte, dataWithPaddingLength)
copy(dataWithPadding, data)
// Filling data_with_padding with random bytes.
if _, err := io.ReadFull(randomSource, dataWithPadding[len(data):]); err != nil {
return nil, errors.Wrap(err, "pad data with random")
}
// Make a copy.
dataPadReversed := make([]byte, dataWithPaddingLength)
copy(dataPadReversed, dataWithPadding)
// 2) data_pad_reversed := BYTE_REVERSE(data_with_padding);
reverseBytes(dataPadReversed)
for {
// 3) A random 32-byte temp_key is generated.
tempKey := make([]byte, tempKeySize)
if _, err := io.ReadFull(randomSource, tempKey); err != nil {
return nil, errors.Wrap(err, "generate temp_key")
}
// 4) data_with_hash := data_pad_reversed + SHA256(temp_key + data_with_padding);
// — after this assignment, data_with_hash is exactly 224 bytes long.
dataWithHash := make([]byte, 0, dataWithHashLength)
dataWithHash = append(dataWithHash, dataPadReversed...)
{
h := sha256.New()
_, _ = h.Write(tempKey)
_, _ = h.Write(dataWithPadding)
dataWithHash = h.Sum(dataWithHash)
dataWithHash = dataWithHash[:dataWithHashLength]
}
// 5) aes_encrypted := AES256_IGE(data_with_hash, temp_key, 0); — AES256-IGE encryption with zero IV.
aesEncrypted := make([]byte, len(dataWithHash))
{
aesBlock, err := aes.NewCipher(tempKey)
if err != nil {
return nil, errors.Wrap(err, "create cipher")
}
var zeroIV bin.Int256
ige.EncryptBlocks(aesBlock, zeroIV[:], aesEncrypted, dataWithHash)
}
// 6) temp_key_xor := temp_key XOR SHA256(aes_encrypted); — adjusted key, 32 bytes
tempKeyXor := make([]byte, tempKeySize)
{
aesEncryptedHash := sha256.Sum256(aesEncrypted)
xor.Bytes(tempKeyXor, tempKey, aesEncryptedHash[:])
}
// 7) key_aes_encrypted := temp_key_xor + aes_encrypted; — exactly 256 bytes (2048 bits) long.
keyAESEncrypted := make([]byte, 0, tempKeySize+dataWithHashLength)
keyAESEncrypted = append(keyAESEncrypted, tempKeyXor...)
keyAESEncrypted = append(keyAESEncrypted, aesEncrypted...)
// 8) The value of key_aes_encrypted is compared with the RSA-modulus of server_pubkey
// as a big-endian 2048-bit (256-byte) unsigned integer. If key_aes_encrypted turns out to be
// greater than or equal to the RSA modulus, the previous steps starting from the generation
// of new random temp_key are repeated.
keyAESEncryptedBig := big.NewInt(0).SetBytes(keyAESEncrypted)
if keyAESEncryptedBig.Cmp(key.N) >= 0 {
continue
}
// Otherwise the final step is performed:
// 9) encrypted_data := RSA(key_aes_encrypted, server_pubkey);
// — 256-byte big-endian integer is elevated to the requisite power from the RSA public key
// modulo the RSA modulus, and the result is stored as a big-endian integer consisting of
// exactly 256 bytes (with leading zero bytes if required).
//
// Encrypting "key_aes_encrypted" with RSA.
res := rsaEncrypt(keyAESEncrypted, key)
return res, nil
}
}
// DecodeRSAPad implements server-side decoder of RSAPad.
func DecodeRSAPad(data []byte, key *rsa.PrivateKey) ([]byte, error) {
var encryptedData [256]byte
if !rsaDecrypt(data, key, encryptedData[:]) {
return nil, errors.New("invalid encrypted_data")
}
tempKeyXor := encryptedData[:tempKeySize]
aesEncrypted := encryptedData[tempKeySize:]
tempKey := make([]byte, tempKeySize)
{
aesEncryptedHash := sha256.Sum256(aesEncrypted)
xor.Bytes(tempKey, tempKeyXor, aesEncryptedHash[:])
}
dataWithHash := make([]byte, len(aesEncrypted))
{
aesBlock, err := aes.NewCipher(tempKey)
if err != nil {
return nil, errors.Wrap(err, "create cipher")
}
var zeroIV bin.Int256
ige.DecryptBlocks(aesBlock, zeroIV[:], dataWithHash, aesEncrypted)
}
dataWithPadding := dataWithHash[:dataWithPaddingLength]
reverseBytes(dataWithPadding)
hash := dataWithHash[dataWithPaddingLength:]
{
h := sha256.New()
_, _ = h.Write(tempKey)
_, _ = h.Write(dataWithPadding)
if !bytes.Equal(hash, h.Sum(nil)) {
return nil, errors.New("hash mismatch")
}
}
return dataWithPadding, nil
}