Browse Source

实现短期动态密码生成与验证功能,优化相关代码逻辑

pull/1077/head
chuchur 2 months ago
parent
commit
e5ac080597
  1. 3
      auth.go
  2. 115
      qiutea.go
  3. 6
      socks.go
  4. 5
      ss.go
  5. 33
      totp.go
  6. 59
      totp_test.go

3
auth.go

@ -36,7 +36,8 @@ func (au *LocalAuthenticator) IFAuthenticate(ip net.IP, user, password string) b
} else {
// if !ip.IsLoopback() && !ip.IsPrivate() { // 存的时候已经判断.
secret := generateSecret(ip.String(), user)
ok, _ := verifyOTP(secret, password)
// ok, _ := verifyOTP(secret, password)
ok := VerifyShortOTP(secret, password, config.Auth.DynamicPeriod)
if !ok {
log.Logf("otp verify fail user=%s ip=%s pass=%s", user, ip, password)

115
qiutea.go

@ -0,0 +1,115 @@
package gost
const delta = 0x9E3779B9
func toBytes(v []uint32, includeLength bool) []byte {
length := uint32(len(v))
n := length << 2
if includeLength {
m := v[length-1]
n -= 4
if (m < n-3) || (m > n) {
return nil
}
n = m
}
bytes := make([]byte, n)
for i := uint32(0); i < n; i++ {
bytes[i] = byte(v[i>>2] >> ((i & 3) << 3))
}
return bytes
}
func toUint32s(bytes []byte, includeLength bool) (v []uint32) {
length := uint32(len(bytes))
n := length >> 2
if length&3 != 0 {
n++
}
if includeLength {
v = make([]uint32, n+1)
v[n] = length
} else {
v = make([]uint32, n)
}
for i := uint32(0); i < length; i++ {
v[i>>2] |= uint32(bytes[i]) << ((i & 3) << 3)
}
return v
}
func mx(sum uint32, y uint32, z uint32, p uint32, e uint32, k []uint32) uint32 {
return ((z>>5 ^ y<<2) + (y>>3 ^ z<<4)) ^ ((sum ^ y) + (k[p&3^e] ^ z))
}
func fixk(k []uint32) []uint32 {
if len(k) < 4 {
key := make([]uint32, 4)
copy(key, k)
return key
}
return k
}
func encrypt(v []uint32, k []uint32) []uint32 {
length := uint32(len(v))
n := length - 1
k = fixk(k)
var y, z, sum, e, p, q uint32
z = v[n]
sum = 0
for q = 6 + 52/length; q > 0; q-- {
sum += delta
e = sum >> 2 & 3
for p = 0; p < n; p++ {
y = v[p+1]
v[p] += mx(sum, y, z, p, e, k)
z = v[p]
}
y = v[0]
v[n] += mx(sum, y, z, p, e, k)
z = v[n]
}
return v
}
func decrypt(v []uint32, k []uint32) []uint32 {
length := uint32(len(v))
n := length - 1
k = fixk(k)
var y, z, sum, e, p, q uint32
y = v[0]
q = 6 + 52/length
for sum = q * delta; sum != 0; sum -= delta {
e = sum >> 2 & 3
for p = n; p > 0; p-- {
z = v[p-1]
v[p] -= mx(sum, y, z, p, e, k)
y = v[p]
}
z = v[n]
v[0] -= mx(sum, y, z, p, e, k)
y = v[0]
}
return v
}
// Encrypt the data with key.
// data is the bytes to be encrypted.
// key is the encrypt key. It is the same as the decrypt key.
func Encrypt(data []byte, key []byte) []byte {
if len(data) == 0 {
return data
}
return toBytes(encrypt(toUint32s(data, true), toUint32s(key, false)), false)
}
// Decrypt the data with key.
// data is the bytes to be decrypted.
// key is the decrypted key. It is the same as the encrypt key.
func Decrypt(data []byte, key []byte) []byte {
if len(data) == 0 {
return data
}
return toBytes(decrypt(toUint32s(data, false), toUint32s(key, false)), true)
}

6
socks.go

@ -1314,11 +1314,7 @@ func (h *socks5Handler) transportUDP(relay, peer net.PacketConn) (err error) {
}
}()
select {
case err = <-errc:
//log.Println("w exit", err)
}
err = <-errc
return
}

5
ss.go

@ -410,10 +410,7 @@ func (h *shadowUDPHandler) transportPacket(conn, cc net.PacketConn) (err error)
}
}()
select {
case err = <-errc:
}
err = <-errc
return
}

33
totp.go

@ -7,6 +7,7 @@ import (
"encoding/binary"
"encoding/hex"
"fmt"
"math/rand"
"time"
)
@ -65,3 +66,35 @@ func GeneratePassword(ip, user string) string {
hashedSrcHex := hex.EncodeToString(hashedSrc)
return hashedSrcHex
}
func GenerateShortOTP(key string) string {
data := make([]byte, 8)
binary.BigEndian.PutUint32(data[:4], rand.Uint32())
binary.BigEndian.PutUint32(data[4:], uint32(time.Now().Unix()))
encryptData := Encrypt(data, []byte(key))
return fmt.Sprintf("%x", encryptData)
}
func VerifyShortOTP(key string, otp string, maxAge int64) bool {
var encryptData []byte
_, err := fmt.Sscanf(otp, "%x", &encryptData)
if err != nil || len(encryptData) == 0 {
return false
}
decryptData := Decrypt(encryptData, []byte(key))
if len(decryptData) != 8 {
return false
}
timestamp := int64(binary.BigEndian.Uint32(decryptData[4:]))
now := time.Now().Unix()
if now-timestamp > maxAge || timestamp > now+5 {
return false
}
return true
}

59
totp_test.go

@ -0,0 +1,59 @@
package gost
import (
"testing"
"time"
)
func TestQiuTea(t *testing.T) {
var secret = "abcdefg"
startSec := 0
passkey := GenerateShortOTP(secret)
println("password:", passkey)
for {
println("password:", passkey)
startSec++
time.Sleep(1 * time.Second)
valid := VerifyShortOTP(secret, passkey, 40)
println("times:", startSec)
if !valid {
println(" false")
break
}
}
}
func TestQiuTea2(t *testing.T) {
secret := generateSecret("192.168.1.1", "user")
for {
passkey := GenerateShortOTP(secret)
println("secret: ", secret)
println("password:", passkey)
time.Sleep(1 * time.Second)
}
}
func TestQiuTea3(t *testing.T) {
password := "8b0775013ae1e3714914c9e6"
secret := "c00cd5a1f706b614c7e4882ff7ea6d535971e685b8d75741b446f05d28db3447"
startSec := 0
for {
println("password:", password)
startSec++
time.Sleep(1 * time.Second)
valid := VerifyShortOTP(secret, password, 20)
println("times:", startSec)
if !valid {
println(" false")
break
}
}
}
Loading…
Cancel
Save