1 Star 0 Fork 0

lqinggang/psiphon-tunnel-core

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
obfuscator.go 7.14 KB
一键复制 编辑 原始数据 按行查看 历史
Rod Hynes 提交于 2016-11-28 21:58 . Test refactoring
/*
* Copyright (c) 2015, Psiphon Inc.
* All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package common
import (
"bytes"
"crypto/rc4"
"crypto/sha1"
"encoding/binary"
"errors"
"io"
)
const (
OBFUSCATE_SEED_LENGTH = 16
OBFUSCATE_KEY_LENGTH = 16
OBFUSCATE_HASH_ITERATIONS = 6000
OBFUSCATE_MAX_PADDING = 8192
OBFUSCATE_MAGIC_VALUE = 0x0BF5CA7E
OBFUSCATE_CLIENT_TO_SERVER_IV = "client_to_server"
OBFUSCATE_SERVER_TO_CLIENT_IV = "server_to_client"
)
// Obfuscator implements the seed message, key derivation, and
// stream ciphers for:
// https://github.com/brl/obfuscated-openssh/blob/master/README.obfuscation
type Obfuscator struct {
seedMessage []byte
clientToServerCipher *rc4.Cipher
serverToClientCipher *rc4.Cipher
}
type ObfuscatorConfig struct {
Keyword string
MaxPadding int
}
// NewClientObfuscator creates a new Obfuscator, staging a seed message to be
// sent to the server (by the caller) and initializing stream ciphers to
// obfuscate data.
func NewClientObfuscator(
config *ObfuscatorConfig) (obfuscator *Obfuscator, err error) {
seed, err := MakeSecureRandomBytes(OBFUSCATE_SEED_LENGTH)
if err != nil {
return nil, ContextError(err)
}
clientToServerCipher, serverToClientCipher, err := initObfuscatorCiphers(seed, config)
if err != nil {
return nil, ContextError(err)
}
maxPadding := OBFUSCATE_MAX_PADDING
if config.MaxPadding > 0 {
maxPadding = config.MaxPadding
}
seedMessage, err := makeSeedMessage(maxPadding, seed, clientToServerCipher)
if err != nil {
return nil, ContextError(err)
}
return &Obfuscator{
seedMessage: seedMessage,
clientToServerCipher: clientToServerCipher,
serverToClientCipher: serverToClientCipher}, nil
}
// NewServerObfuscator creates a new Obfuscator, reading a seed message directly
// from the clientReader and initializing stream ciphers to obfuscate data.
func NewServerObfuscator(
clientReader io.Reader, config *ObfuscatorConfig) (obfuscator *Obfuscator, err error) {
clientToServerCipher, serverToClientCipher, err := readSeedMessage(
clientReader, config)
if err != nil {
return nil, ContextError(err)
}
return &Obfuscator{
clientToServerCipher: clientToServerCipher,
serverToClientCipher: serverToClientCipher}, nil
}
// SendSeedMessage returns the seed message created in NewObfuscatorClient,
// removing the reference so that it may be garbage collected.
func (obfuscator *Obfuscator) SendSeedMessage() []byte {
seedMessage := obfuscator.seedMessage
obfuscator.seedMessage = nil
return seedMessage
}
// ObfuscateClientToServer applies the client RC4 stream to the bytes in buffer.
func (obfuscator *Obfuscator) ObfuscateClientToServer(buffer []byte) {
obfuscator.clientToServerCipher.XORKeyStream(buffer, buffer)
}
// ObfuscateServerToClient applies the server RC4 stream to the bytes in buffer.
func (obfuscator *Obfuscator) ObfuscateServerToClient(buffer []byte) {
obfuscator.serverToClientCipher.XORKeyStream(buffer, buffer)
}
func initObfuscatorCiphers(
seed []byte, config *ObfuscatorConfig) (*rc4.Cipher, *rc4.Cipher, error) {
clientToServerKey, err := deriveKey(seed, []byte(config.Keyword), []byte(OBFUSCATE_CLIENT_TO_SERVER_IV))
if err != nil {
return nil, nil, ContextError(err)
}
serverToClientKey, err := deriveKey(seed, []byte(config.Keyword), []byte(OBFUSCATE_SERVER_TO_CLIENT_IV))
if err != nil {
return nil, nil, ContextError(err)
}
clientToServerCipher, err := rc4.NewCipher(clientToServerKey)
if err != nil {
return nil, nil, ContextError(err)
}
serverToClientCipher, err := rc4.NewCipher(serverToClientKey)
if err != nil {
return nil, nil, ContextError(err)
}
return clientToServerCipher, serverToClientCipher, nil
}
func deriveKey(seed, keyword, iv []byte) ([]byte, error) {
h := sha1.New()
h.Write(seed)
h.Write(keyword)
h.Write(iv)
digest := h.Sum(nil)
for i := 0; i < OBFUSCATE_HASH_ITERATIONS; i++ {
h.Reset()
h.Write(digest)
digest = h.Sum(nil)
}
if len(digest) < OBFUSCATE_KEY_LENGTH {
return nil, ContextError(errors.New("insufficient bytes for obfuscation key"))
}
return digest[0:OBFUSCATE_KEY_LENGTH], nil
}
func makeSeedMessage(maxPadding int, seed []byte, clientToServerCipher *rc4.Cipher) ([]byte, error) {
// paddingLength is integer in range [0, maxPadding]
paddingLength, err := MakeSecureRandomInt(maxPadding + 1)
if err != nil {
return nil, ContextError(err)
}
padding, err := MakeSecureRandomBytes(paddingLength)
if err != nil {
return nil, ContextError(err)
}
buffer := new(bytes.Buffer)
err = binary.Write(buffer, binary.BigEndian, seed)
if err != nil {
return nil, ContextError(err)
}
err = binary.Write(buffer, binary.BigEndian, uint32(OBFUSCATE_MAGIC_VALUE))
if err != nil {
return nil, ContextError(err)
}
err = binary.Write(buffer, binary.BigEndian, uint32(paddingLength))
if err != nil {
return nil, ContextError(err)
}
err = binary.Write(buffer, binary.BigEndian, padding)
if err != nil {
return nil, ContextError(err)
}
seedMessage := buffer.Bytes()
clientToServerCipher.XORKeyStream(seedMessage[len(seed):], seedMessage[len(seed):])
return seedMessage, nil
}
func readSeedMessage(
clientReader io.Reader, config *ObfuscatorConfig) (*rc4.Cipher, *rc4.Cipher, error) {
seed := make([]byte, OBFUSCATE_SEED_LENGTH)
_, err := io.ReadFull(clientReader, seed)
if err != nil {
return nil, nil, ContextError(err)
}
clientToServerCipher, serverToClientCipher, err := initObfuscatorCiphers(seed, config)
if err != nil {
return nil, nil, ContextError(err)
}
fixedLengthFields := make([]byte, 8) // 4 bytes each for magic value and padding length
_, err = io.ReadFull(clientReader, fixedLengthFields)
if err != nil {
return nil, nil, ContextError(err)
}
clientToServerCipher.XORKeyStream(fixedLengthFields, fixedLengthFields)
buffer := bytes.NewReader(fixedLengthFields)
var magicValue, paddingLength int32
err = binary.Read(buffer, binary.BigEndian, &magicValue)
if err != nil {
return nil, nil, ContextError(err)
}
err = binary.Read(buffer, binary.BigEndian, &paddingLength)
if err != nil {
return nil, nil, ContextError(err)
}
if magicValue != OBFUSCATE_MAGIC_VALUE {
return nil, nil, ContextError(errors.New("invalid magic value"))
}
if paddingLength < 0 || paddingLength > OBFUSCATE_MAX_PADDING {
return nil, nil, ContextError(errors.New("invalid padding length"))
}
padding := make([]byte, paddingLength)
_, err = io.ReadFull(clientReader, padding)
if err != nil {
return nil, nil, ContextError(err)
}
clientToServerCipher.XORKeyStream(padding, padding)
return clientToServerCipher, serverToClientCipher, nil
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/lqinggang/psiphon-tunnel-core.git
git@gitee.com:lqinggang/psiphon-tunnel-core.git
lqinggang
psiphon-tunnel-core
psiphon-tunnel-core
v1.0.0

搜索帮助

0d507c66 1850385 C8b1a773 1850385