You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
163 lines
5.1 KiB
163 lines
5.1 KiB
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 |
|
}
|
|
|