mirror of https://github.com/gwuhaolin/livego.git
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.
207 lines
4.5 KiB
207 lines
4.5 KiB
package core |
|
|
|
import ( |
|
"bytes" |
|
"crypto/hmac" |
|
"crypto/rand" |
|
"crypto/sha256" |
|
"fmt" |
|
"io" |
|
|
|
"time" |
|
|
|
"github.com/gwuhaolin/livego/utils/pio" |
|
) |
|
|
|
var ( |
|
timeout = 5 * time.Second |
|
) |
|
|
|
var ( |
|
hsClientFullKey = []byte{ |
|
'G', 'e', 'n', 'u', 'i', 'n', 'e', ' ', 'A', 'd', 'o', 'b', 'e', ' ', |
|
'F', 'l', 'a', 's', 'h', ' ', 'P', 'l', 'a', 'y', 'e', 'r', ' ', |
|
'0', '0', '1', |
|
0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1, |
|
0x02, 0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, |
|
0x93, 0xB8, 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE, |
|
} |
|
hsServerFullKey = []byte{ |
|
'G', 'e', 'n', 'u', 'i', 'n', 'e', ' ', 'A', 'd', 'o', 'b', 'e', ' ', |
|
'F', 'l', 'a', 's', 'h', ' ', 'M', 'e', 'd', 'i', 'a', ' ', |
|
'S', 'e', 'r', 'v', 'e', 'r', ' ', |
|
'0', '0', '1', |
|
0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1, |
|
0x02, 0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, |
|
0x93, 0xB8, 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE, |
|
} |
|
hsClientPartialKey = hsClientFullKey[:30] |
|
hsServerPartialKey = hsServerFullKey[:36] |
|
) |
|
|
|
func hsMakeDigest(key []byte, src []byte, gap int) (dst []byte) { |
|
h := hmac.New(sha256.New, key) |
|
if gap <= 0 { |
|
h.Write(src) |
|
} else { |
|
h.Write(src[:gap]) |
|
h.Write(src[gap+32:]) |
|
} |
|
return h.Sum(nil) |
|
} |
|
|
|
func hsCalcDigestPos(p []byte, base int) (pos int) { |
|
for i := 0; i < 4; i++ { |
|
pos += int(p[base+i]) |
|
} |
|
pos = (pos % 728) + base + 4 |
|
return |
|
} |
|
|
|
func hsFindDigest(p []byte, key []byte, base int) int { |
|
gap := hsCalcDigestPos(p, base) |
|
digest := hsMakeDigest(key, p, gap) |
|
if bytes.Compare(p[gap:gap+32], digest) != 0 { |
|
return -1 |
|
} |
|
return gap |
|
} |
|
|
|
func hsParse1(p []byte, peerkey []byte, key []byte) (ok bool, digest []byte) { |
|
var pos int |
|
if pos = hsFindDigest(p, peerkey, 772); pos == -1 { |
|
if pos = hsFindDigest(p, peerkey, 8); pos == -1 { |
|
return |
|
} |
|
} |
|
ok = true |
|
digest = hsMakeDigest(key, p[pos:pos+32], -1) |
|
return |
|
} |
|
|
|
func hsCreate01(p []byte, time uint32, ver uint32, key []byte) { |
|
p[0] = 3 |
|
p1 := p[1:] |
|
rand.Read(p1[8:]) |
|
pio.PutU32BE(p1[0:4], time) |
|
pio.PutU32BE(p1[4:8], ver) |
|
gap := hsCalcDigestPos(p1, 8) |
|
digest := hsMakeDigest(key, p1, gap) |
|
copy(p1[gap:], digest) |
|
} |
|
|
|
func hsCreate2(p []byte, key []byte) { |
|
rand.Read(p) |
|
gap := len(p) - 32 |
|
digest := hsMakeDigest(key, p, gap) |
|
copy(p[gap:], digest) |
|
} |
|
|
|
func (conn *Conn) HandshakeClient() (err error) { |
|
var random [(1 + 1536*2) * 2]byte |
|
|
|
C0C1C2 := random[:1536*2+1] |
|
C0 := C0C1C2[:1] |
|
C0C1 := C0C1C2[:1536+1] |
|
C2 := C0C1C2[1536+1:] |
|
|
|
S0S1S2 := random[1536*2+1:] |
|
|
|
C0[0] = 3 |
|
// > C0C1 |
|
conn.Conn.SetDeadline(time.Now().Add(timeout)) |
|
if _, err = conn.rw.Write(C0C1); err != nil { |
|
return |
|
} |
|
conn.Conn.SetDeadline(time.Now().Add(timeout)) |
|
if err = conn.rw.Flush(); err != nil { |
|
return |
|
} |
|
|
|
// < S0S1S2 |
|
conn.Conn.SetDeadline(time.Now().Add(timeout)) |
|
if _, err = io.ReadFull(conn.rw, S0S1S2); err != nil { |
|
return |
|
} |
|
|
|
S1 := S0S1S2[1 : 1536+1] |
|
if ver := pio.U32BE(S1[4:8]); ver != 0 { |
|
C2 = S1 |
|
} else { |
|
C2 = S1 |
|
} |
|
|
|
// > C2 |
|
conn.Conn.SetDeadline(time.Now().Add(timeout)) |
|
if _, err = conn.rw.Write(C2); err != nil { |
|
return |
|
} |
|
conn.Conn.SetDeadline(time.Time{}) |
|
return |
|
} |
|
|
|
func (conn *Conn) HandshakeServer() (err error) { |
|
var random [(1 + 1536*2) * 2]byte |
|
|
|
C0C1C2 := random[:1536*2+1] |
|
C0 := C0C1C2[:1] |
|
C1 := C0C1C2[1 : 1536+1] |
|
C0C1 := C0C1C2[:1536+1] |
|
C2 := C0C1C2[1536+1:] |
|
|
|
S0S1S2 := random[1536*2+1:] |
|
S0 := S0S1S2[:1] |
|
S1 := S0S1S2[1 : 1536+1] |
|
S0S1 := S0S1S2[:1536+1] |
|
S2 := S0S1S2[1536+1:] |
|
|
|
// < C0C1 |
|
conn.Conn.SetDeadline(time.Now().Add(timeout)) |
|
if _, err = io.ReadFull(conn.rw, C0C1); err != nil { |
|
return |
|
} |
|
conn.Conn.SetDeadline(time.Now().Add(timeout)) |
|
if C0[0] != 3 { |
|
err = fmt.Errorf("rtmp: handshake version=%d invalid", C0[0]) |
|
return |
|
} |
|
|
|
S0[0] = 3 |
|
|
|
clitime := pio.U32BE(C1[0:4]) |
|
srvtime := clitime |
|
srvver := uint32(0x0d0e0a0d) |
|
cliver := pio.U32BE(C1[4:8]) |
|
|
|
if cliver != 0 { |
|
var ok bool |
|
var digest []byte |
|
if ok, digest = hsParse1(C1, hsClientPartialKey, hsServerFullKey); !ok { |
|
err = fmt.Errorf("rtmp: handshake server: C1 invalid") |
|
return |
|
} |
|
hsCreate01(S0S1, srvtime, srvver, hsServerPartialKey) |
|
hsCreate2(S2, digest) |
|
} else { |
|
copy(S1, C2) |
|
copy(S2, C1) |
|
} |
|
|
|
// > S0S1S2 |
|
conn.Conn.SetDeadline(time.Now().Add(timeout)) |
|
if _, err = conn.rw.Write(S0S1S2); err != nil { |
|
return |
|
} |
|
conn.Conn.SetDeadline(time.Now().Add(timeout)) |
|
if err = conn.rw.Flush(); err != nil { |
|
return |
|
} |
|
|
|
// < C2 |
|
conn.Conn.SetDeadline(time.Now().Add(timeout)) |
|
if _, err = io.ReadFull(conn.rw, C2); err != nil { |
|
return |
|
} |
|
conn.Conn.SetDeadline(time.Time{}) |
|
return |
|
}
|
|
|