mirror of https://github.com/Dreamacro/clash.git
104 lines
3.5 KiB
Go
104 lines
3.5 KiB
Go
package vmess
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
"crypto/hmac"
|
|
"crypto/rand"
|
|
"crypto/sha256"
|
|
"encoding/binary"
|
|
"hash"
|
|
"hash/crc32"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
kdfSaltConstAuthIDEncryptionKey = "AES Auth ID Encryption"
|
|
kdfSaltConstAEADRespHeaderLenKey = "AEAD Resp Header Len Key"
|
|
kdfSaltConstAEADRespHeaderLenIV = "AEAD Resp Header Len IV"
|
|
kdfSaltConstAEADRespHeaderPayloadKey = "AEAD Resp Header Key"
|
|
kdfSaltConstAEADRespHeaderPayloadIV = "AEAD Resp Header IV"
|
|
kdfSaltConstVMessAEADKDF = "VMess AEAD KDF"
|
|
kdfSaltConstVMessHeaderPayloadAEADKey = "VMess Header AEAD Key"
|
|
kdfSaltConstVMessHeaderPayloadAEADIV = "VMess Header AEAD Nonce"
|
|
kdfSaltConstVMessHeaderPayloadLengthAEADKey = "VMess Header AEAD Key_Length"
|
|
kdfSaltConstVMessHeaderPayloadLengthAEADIV = "VMess Header AEAD Nonce_Length"
|
|
)
|
|
|
|
func kdf(key []byte, path ...string) []byte {
|
|
hmacCreator := &hMacCreator{value: []byte(kdfSaltConstVMessAEADKDF)}
|
|
for _, v := range path {
|
|
hmacCreator = &hMacCreator{value: []byte(v), parent: hmacCreator}
|
|
}
|
|
hmacf := hmacCreator.Create()
|
|
hmacf.Write(key)
|
|
return hmacf.Sum(nil)
|
|
}
|
|
|
|
type hMacCreator struct {
|
|
parent *hMacCreator
|
|
value []byte
|
|
}
|
|
|
|
func (h *hMacCreator) Create() hash.Hash {
|
|
if h.parent == nil {
|
|
return hmac.New(sha256.New, h.value)
|
|
}
|
|
return hmac.New(h.parent.Create, h.value)
|
|
}
|
|
|
|
func createAuthID(cmdKey []byte, time int64) [16]byte {
|
|
buf := &bytes.Buffer{}
|
|
binary.Write(buf, binary.BigEndian, time)
|
|
|
|
random := make([]byte, 4)
|
|
rand.Read(random)
|
|
buf.Write(random)
|
|
zero := crc32.ChecksumIEEE(buf.Bytes())
|
|
binary.Write(buf, binary.BigEndian, zero)
|
|
|
|
aesBlock, _ := aes.NewCipher(kdf(cmdKey[:], kdfSaltConstAuthIDEncryptionKey)[:16])
|
|
var result [16]byte
|
|
aesBlock.Encrypt(result[:], buf.Bytes())
|
|
return result
|
|
}
|
|
|
|
func sealVMessAEADHeader(key [16]byte, data []byte, t time.Time) []byte {
|
|
generatedAuthID := createAuthID(key[:], t.Unix())
|
|
connectionNonce := make([]byte, 8)
|
|
rand.Read(connectionNonce)
|
|
|
|
aeadPayloadLengthSerializedByte := make([]byte, 2)
|
|
binary.BigEndian.PutUint16(aeadPayloadLengthSerializedByte, uint16(len(data)))
|
|
|
|
var payloadHeaderLengthAEADEncrypted []byte
|
|
|
|
{
|
|
payloadHeaderLengthAEADKey := kdf(key[:], kdfSaltConstVMessHeaderPayloadLengthAEADKey, string(generatedAuthID[:]), string(connectionNonce))[:16]
|
|
payloadHeaderLengthAEADNonce := kdf(key[:], kdfSaltConstVMessHeaderPayloadLengthAEADIV, string(generatedAuthID[:]), string(connectionNonce))[:12]
|
|
payloadHeaderLengthAEADAESBlock, _ := aes.NewCipher(payloadHeaderLengthAEADKey)
|
|
payloadHeaderAEAD, _ := cipher.NewGCM(payloadHeaderLengthAEADAESBlock)
|
|
payloadHeaderLengthAEADEncrypted = payloadHeaderAEAD.Seal(nil, payloadHeaderLengthAEADNonce, aeadPayloadLengthSerializedByte, generatedAuthID[:])
|
|
}
|
|
|
|
var payloadHeaderAEADEncrypted []byte
|
|
|
|
{
|
|
payloadHeaderAEADKey := kdf(key[:], kdfSaltConstVMessHeaderPayloadAEADKey, string(generatedAuthID[:]), string(connectionNonce))[:16]
|
|
payloadHeaderAEADNonce := kdf(key[:], kdfSaltConstVMessHeaderPayloadAEADIV, string(generatedAuthID[:]), string(connectionNonce))[:12]
|
|
payloadHeaderAEADAESBlock, _ := aes.NewCipher(payloadHeaderAEADKey)
|
|
payloadHeaderAEAD, _ := cipher.NewGCM(payloadHeaderAEADAESBlock)
|
|
payloadHeaderAEADEncrypted = payloadHeaderAEAD.Seal(nil, payloadHeaderAEADNonce, data, generatedAuthID[:])
|
|
}
|
|
|
|
var outputBuffer = &bytes.Buffer{}
|
|
|
|
outputBuffer.Write(generatedAuthID[:])
|
|
outputBuffer.Write(payloadHeaderLengthAEADEncrypted)
|
|
outputBuffer.Write(connectionNonce)
|
|
outputBuffer.Write(payloadHeaderAEADEncrypted)
|
|
|
|
return outputBuffer.Bytes()
|
|
}
|