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.

164 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
}