Built-in DHCP (raw Ethernet frames through tunnel), automatic reconnection, host route management, classless static routes (option 121/249), DNS config. Single static binary, Linux only. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
137 lines
3.5 KiB
Go
137 lines
3.5 KiB
Go
// SHA-0 implementation for SoftEther VPN password authentication.
|
|
//
|
|
// SoftEther uses SHA-0 (the original, withdrawn FIPS-180 hash — NOT SHA-1) for
|
|
// password hashing and the SecurePassword challenge-response. SHA-0 differs from
|
|
// SHA-1 only in that the message schedule expansion does NOT include a left-rotate
|
|
// by 1 bit (see transform() below).
|
|
//
|
|
// Auth flow:
|
|
// 1. Client hashes the plaintext password: HashedPassword = SHA0(password)
|
|
// 2. Server sends a 20-byte random challenge in the Hello pack
|
|
// 3. Client computes: SecurePassword = SHA0(HashedPassword + ServerRandom)
|
|
// 4. Client sends SecurePassword in the auth pack
|
|
//
|
|
// Reference C implementation:
|
|
// https://github.com/SoftEtherVPN/SoftEtherVPN/blob/v5.02.5187/src/Mayaqua/Encrypt.c#L1088 (Sha0)
|
|
// https://github.com/SoftEtherVPN/SoftEtherVPN/blob/v5.02.5187/src/Cedar/Sam.c#L105 (SecurePassword)
|
|
package client
|
|
|
|
const sha1Size = 20
|
|
|
|
func rol(bits int, value uint32) uint32 {
|
|
return (value << bits) | (value >> (32 - bits))
|
|
}
|
|
|
|
type sha0ctx struct {
|
|
count uint64
|
|
buf [64]byte
|
|
state [8]uint32
|
|
}
|
|
|
|
func (c *sha0ctx) init() {
|
|
c.state[0] = 0x67452301
|
|
c.state[1] = 0xEFCDAB89
|
|
c.state[2] = 0x98BADCFE
|
|
c.state[3] = 0x10325476
|
|
c.state[4] = 0xC3D2E1F0
|
|
c.count = 0
|
|
}
|
|
|
|
func (c *sha0ctx) transform() {
|
|
var W [80]uint32
|
|
|
|
p := 0
|
|
t := 0
|
|
for ; t < 16; t++ {
|
|
W[t] = uint32(c.buf[p])<<24 | uint32(c.buf[p+1])<<16 | uint32(c.buf[p+2])<<8 | uint32(c.buf[p+3])
|
|
p += 4
|
|
}
|
|
// SHA-0: W[t] = W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16] (NO rotate)
|
|
// SHA-1 would be: W[t] = rol(1, W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])
|
|
for ; t < 80; t++ {
|
|
W[t] = W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]
|
|
}
|
|
|
|
A, B, C, D, E := c.state[0], c.state[1], c.state[2], c.state[3], c.state[4]
|
|
for t = 0; t < 80; t++ {
|
|
tmp := rol(5, A) + E + W[t]
|
|
if t < 20 {
|
|
tmp += (D ^ (B & (C ^ D))) + 0x5A827999
|
|
} else if t < 40 {
|
|
tmp += (B ^ C ^ D) + 0x6ED9EBA1
|
|
} else if t < 60 {
|
|
tmp += ((B & C) | (D & (B | C))) + 0x8F1BBCDC
|
|
} else {
|
|
tmp += (B ^ C ^ D) + 0xCA62C1D6
|
|
}
|
|
E = D
|
|
D = C
|
|
C = rol(30, B)
|
|
B = A
|
|
A = tmp
|
|
}
|
|
|
|
c.state[0] += A
|
|
c.state[1] += B
|
|
c.state[2] += C
|
|
c.state[3] += D
|
|
c.state[4] += E
|
|
}
|
|
|
|
func (c *sha0ctx) update(data []byte) {
|
|
i := int(c.count & 63)
|
|
c.count += uint64(len(data))
|
|
for _, d := range data {
|
|
c.buf[i] = d
|
|
i++
|
|
if i == 64 {
|
|
c.transform()
|
|
i = 0
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *sha0ctx) final() {
|
|
cnt := c.count * 8
|
|
c.update([]byte{0x80})
|
|
for c.count&63 != 56 {
|
|
c.update([]byte{0x0})
|
|
}
|
|
for i := 0; i < 8; i++ {
|
|
c.update([]byte{byte(cnt >> ((7 - i) * 8))})
|
|
}
|
|
p := 0
|
|
for i := 0; i < 5; i++ {
|
|
c.buf[p] = byte(c.state[i] >> 24)
|
|
c.buf[p+1] = byte(c.state[i] >> 16)
|
|
c.buf[p+2] = byte(c.state[i] >> 8)
|
|
c.buf[p+3] = byte(c.state[i])
|
|
p += 4
|
|
}
|
|
}
|
|
|
|
func sha0(data []byte) [sha1Size]byte {
|
|
var c sha0ctx
|
|
c.init()
|
|
c.update(data)
|
|
c.final()
|
|
var out [sha1Size]byte
|
|
copy(out[:], c.buf[:sha1Size])
|
|
return out
|
|
}
|
|
|
|
// SecurePassword computes the challenge-response: SHA0(HashedPassword + ServerRandom).
|
|
// See: https://github.com/SoftEtherVPN/SoftEtherVPN/blob/v5.02.5187/src/Cedar/Sam.c#L105
|
|
func SecurePassword(hashedPassword, random [sha1Size]byte) [sha1Size]byte {
|
|
buf := make([]byte, sha1Size*2)
|
|
copy(buf, hashedPassword[:])
|
|
copy(buf[sha1Size:], random[:])
|
|
return sha0(buf)
|
|
}
|
|
|
|
// HashPassword computes SHA0(password) — the first step of SoftEther password auth.
|
|
// The result is what SoftEther stores server-side as "HashedPassword".
|
|
// See: https://github.com/SoftEtherVPN/SoftEtherVPN/blob/v5.02.5187/src/Cedar/Sam.c#L91 (HashPassword)
|
|
func HashPassword(password string) [sha1Size]byte {
|
|
return sha0([]byte(password))
|
|
}
|