Skip to content

Commit

Permalink
Merge pull request #111 from ddddddO/tlsv12_for_ipv6
Browse files Browse the repository at this point in the history
Support TLSv1.2 for IPv6
  • Loading branch information
ddddddO authored Feb 16, 2025
2 parents 57383d9 + 73eddb4 commit b9338a8
Show file tree
Hide file tree
Showing 2 changed files with 313 additions and 12 deletions.
22 changes: 11 additions & 11 deletions internal/tui/sender.go
Original file line number Diff line number Diff line change
Expand Up @@ -403,17 +403,17 @@ func (s *sender) send(ctx context.Context, currentLayer string) (err error) {
s.packets.http.Bytes(),
)
case "IPv6":
return fmt.Errorf("not implemented")
// ctx, cancel := context.WithTimeout(context.Background(), TIMEOUT)
// defer cancel()
// return packemon.EstablishConnectionAndSendPayloadXxxForIPv6(
// ctx,
// DEFAULT_NW_INTERFACE,
// s.packets.ethernet,
// s.packets.ipv6,
// s.packets.tcp,
// s.packets.http.Bytes(),
// )
ctx, cancel := context.WithTimeout(context.Background(), TIMEOUT)
defer cancel()

return packemon.EstablishTCPTLSv1_2AndSendPayloadForIPv6(
ctx,
DEFAULT_NW_INTERFACE,
s.packets.ethernet,
s.packets.ipv6,
s.packets.tcp,
s.packets.http.Bytes(),
)
case "ARP":
return fmt.Errorf("unsupported under protocol: %s", selectedL3)
default:
Expand Down
303 changes: 302 additions & 1 deletion tcp_tlsv1_2.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,9 @@ func EstablishTCPTLSv1_2AndSendPayload(ctx context.Context, nwInterface string,
ipv4.CalculateTotalLength()
ipv4.CalculateChecksum()

ethernetFrame = NewEthernetFrame(dstMACAddr, srcMACAddr, ETHER_TYPE_IPv4, ipv4.Bytes())
// ここの EtherType は、ユーザー指定のを使う
// TODO: 他のパケットもそうした方が良い?
ethernetFrame = NewEthernetFrame(dstMACAddr, srcMACAddr, fEthrh.Typ, ipv4.Bytes())
if err := nw.Send(ethernetFrame); err != nil {
return err
}
Expand Down Expand Up @@ -317,3 +319,302 @@ func SendTLSClientHello(nw *NetworkInterface, clientHello *TLSClientHello, srcPo
ethernetFrame := NewEthernetFrame(dstMACAddr, srcMACAddr, ETHER_TYPE_IPv4, ipv4.Bytes())
return nw.Send(ethernetFrame)
}

// TCP 3way handshake と TLSv1.2 の handshake 後にリクエストする関数(IPv6用)
func EstablishTCPTLSv1_2AndSendPayloadForIPv6(ctx context.Context, nwInterface string, fEthrh *EthernetHeader, fIpv6 *IPv6, fTcp *TCP, upperLayerData []byte) error {
nw, err := NewNetworkInterface(nwInterface)
if err != nil {
return err
}

srcIPAddr := fIpv6.SrcAddr
dstIPAddr := fIpv6.DstAddr
srcMACAddr := fEthrh.Src
dstMACAddr := fEthrh.Dst

tcpConn := NewTCPConnection(fTcp.SrcPort, fTcp.DstPort)
tcp := NewTCPSyn(tcpConn.SrcPort, tcpConn.DstPort)
ipv6 := NewIPv6(IPv4_PROTO_TCP, srcIPAddr, dstIPAddr)
tcp.CalculateChecksumForIPv6(ipv6)

ipv6.Data = tcp.Bytes()
ipv6.PayloadLength = uint16(len(ipv6.Data))

ethernetFrame := NewEthernetFrame(dstMACAddr, srcMACAddr, ETHER_TYPE_IPv6, ipv6.Bytes())
if err := nw.Send(ethernetFrame); err != nil {
return err
}
tcpConn.SetState(TCP_STATE_3WAY_HANDSHAKE_SEND_SYN)

tlsConn := NewTLSv12Connection()

for {
select {
case <-ctx.Done():
return fmt.Errorf("timeout!")
default:
recieved := make([]byte, 1500)
n, _, err := unix.Recvfrom(nw.Socket, recieved, 0)
if err != nil {
if n == -1 {
continue
}
return err
}

ethernetFrame := ParsedEthernetFrame(recieved)
if ethernetFrame.Header.Typ != ETHER_TYPE_IPv6 {
continue
}

ipv6 := ParsedIPv6(ethernetFrame.Data)
if ipv6.NextHeader != IPv4_PROTO_TCP {
continue
}

tcp := ParsedTCP(ipv6.Data)
// TODO: このあたりで(10)443ポートがdstで絞った方がいいかも

if tcpConn.IsPassiveSynAckForHandshake(tcp) {
// syn/ackを受け取ったのでack送信
tcp := NewTCPAck(tcpConn.SrcPort, tcpConn.DstPort, tcp.Sequence, tcp.Acknowledgment)
ipv6 := NewIPv6(IPv4_PROTO_TCP, srcIPAddr, dstIPAddr)
tcp.CalculateChecksumForIPv6(ipv6)

ipv6.Data = tcp.Bytes()
ipv6.PayloadLength = uint16(len(ipv6.Data))

ethernetFrame := NewEthernetFrame(dstMACAddr, srcMACAddr, ETHER_TYPE_IPv6, ipv6.Bytes())
if err := nw.Send(ethernetFrame); err != nil {
return err
}
tcpConn.EstablishedConnection()

// ここで TLS Client Helloを送る
if err := SendTLSClientHelloForIPv6(nw, tlsConn.TLSClientHello, tcpConn.SrcPort, tcpConn.DstPort, srcIPAddr, dstIPAddr, dstMACAddr, tcp.Sequence, tcp.Acknowledgment); err != nil {
return err
}

continue
}

// ServerHello/Certificate/ServerHelloDone がセグメント分割されたパケットで届くことが多々あるため、このブロック内で連続して受信している
if tcpConn.IsPassiveAck(tcp) && tlsConn.IsPassiveServerHello(tcp) {
for {
recieved := make([]byte, 1500)
n, _, err := unix.Recvfrom(nw.Socket, recieved, 0)
if err != nil {
if n == -1 {
continue
}
return err
}
eth := ParsedEthernetFrame(recieved)
ip := ParsedIPv6(eth.Data)
t := ParsedTCP(ip.Data)

if tcpConn.IsPassivePshAck(t) {
// tcp data の末尾の0パディングを取り除く
tmp1 := tcp.Data
for offset := len(tcp.Data) - 2; bytes.Equal(tcp.Data[offset:offset+2], []byte{00, 00}); offset -= 2 {
tmp1 = tmp1[:len(tmp1)-2]
}
tmp2 := t.Data
for offset := len(t.Data) - 4; bytes.Equal(t.Data[offset:offset+4], []byte{00, 00, 00, 00}); offset -= 4 {
tmp2 = tmp2[:len(tmp2)-4]
}
mergedTCPData := append(tmp1, tmp2...)

tlsConn.TLSServerHello = ParsedTLSServerHello(mergedTCPData)
if err := tlsConn.TLSServerHello.Certificate.Validate(); err != nil {
return err
}

// ackを返し
tcp := NewTCPAck(tcpConn.SrcPort, tcpConn.DstPort, t.Sequence, t.Acknowledgment)
ipv6 := NewIPv6(IPv4_PROTO_TCP, srcIPAddr, dstIPAddr)
tcp.CalculateChecksumForIPv6(ipv6)

ipv6.Data = tcp.Bytes()
ipv6.PayloadLength = uint16(len(ipv6.Data))

ethernetFrame := NewEthernetFrame(dstMACAddr, srcMACAddr, ETHER_TYPE_IPv6, ipv6.Bytes())
if err := nw.Send(ethernetFrame); err != nil {
return err
}

// さらに ClientKeyExchange や Finished などを返す
tlsConn.TLSClientKeyExchange, tlsConn.KeyBlock, tlsConn.ClientSequence, tlsConn.Master, tlsConn.TLSClientFinished = NewTLSClientKeyExchangeAndChangeCipherSpecAndFinished(
tlsConn.TLSClientHello,
tlsConn.TLSServerHello,
)
tcp = NewTCPWithData(tcpConn.SrcPort, tcpConn.DstPort, tlsConn.TLSClientKeyExchange.Bytes(), tcp.Sequence, tcp.Acknowledgment)
ipv6 = NewIPv6(IPv4_PROTO_TCP, srcIPAddr, dstIPAddr)
tcp.CalculateChecksumForIPv6(ipv6)

ipv6.Data = tcp.Bytes()
ipv6.PayloadLength = uint16(len(ipv6.Data))

ethernetFrame = NewEthernetFrame(dstMACAddr, srcMACAddr, ETHER_TYPE_IPv6, ipv6.Bytes())
if err := nw.Send(ethernetFrame); err != nil {
return err
}

break
}
continue
}

continue
}

// ServerHelloを受信
// SeverHello(0x02)
if tcpConn.IsPassivePshAck(tcp) && tlsConn.IsPassiveServerHello(tcp) {
// TODO: server から、ServerHello/Certificate/ServerHelloDone でひとまとまりで返ってくればパースできるが、ServerHello と Certificate/ServerHelloDone がわかれて返ってくることがある。それで失敗してるよう?
// 分かれてるとき、ServerHello はフラグが ACK だけど、分かれてないとき PSH/ACK
// <- そうでもなかった、環境によるみたい。example.com にリクエストすると ServerHello 単体パケットで PSH/ACK
tlsConn.TLSServerHello = ParsedTLSServerHello(tcp.Data)
if err := tlsConn.TLSServerHello.Certificate.Validate(); err != nil {
return err
}

// ackを返し
tcp := NewTCPAck(tcpConn.SrcPort, tcpConn.DstPort, tcp.Sequence, tcp.Acknowledgment)
ipv6 := NewIPv6(IPv4_PROTO_TCP, srcIPAddr, dstIPAddr)
tcp.CalculateChecksumForIPv6(ipv6)

ipv6.Data = tcp.Bytes()
ipv6.PayloadLength = uint16(len(ipv6.Data))

ethernetFrame := NewEthernetFrame(dstMACAddr, srcMACAddr, ETHER_TYPE_IPv6, ipv6.Bytes())
if err := nw.Send(ethernetFrame); err != nil {
return err
}

// さらに ClientKeyExchange や Finished などを返す
tlsConn.TLSClientKeyExchange, tlsConn.KeyBlock, tlsConn.ClientSequence, tlsConn.Master, tlsConn.TLSClientFinished = NewTLSClientKeyExchangeAndChangeCipherSpecAndFinished(
tlsConn.TLSClientHello,
tlsConn.TLSServerHello,
)
tcp = NewTCPWithData(tcpConn.SrcPort, tcpConn.DstPort, tlsConn.TLSClientKeyExchange.Bytes(), tcp.Sequence, tcp.Acknowledgment)
ipv6 = NewIPv6(IPv4_PROTO_TCP, srcIPAddr, dstIPAddr)
tcp.CalculateChecksumForIPv6(ipv6)

ipv6.Data = tcp.Bytes()
ipv6.PayloadLength = uint16(len(ipv6.Data))

ethernetFrame = NewEthernetFrame(dstMACAddr, srcMACAddr, ETHER_TYPE_IPv6, ipv6.Bytes())
if err := nw.Send(ethernetFrame); err != nil {
return err
}

continue
}

// ChangeCipherSpec/Finishedを受信
if tcpConn.IsPassivePshAck(tcp) && tlsConn.IsPassiveChangeCipherSpecAndFinished(tcp) {
tlsChangeCiperSpecAndFinished := ParsedTLSChangeCipherSpecAndFinished(tcp.Data, tlsConn.KeyBlock, tlsConn.ClientSequence, tlsConn.VerifingData())
_ = tlsChangeCiperSpecAndFinished

// TODO: 上のParsed内でserverからきたFinishedの検証してるけど、この辺りに持ってきた方がいいかも

tlsConn.EstablishedConnection()

// Finishedの検証が成功したので、以降からApplicationDataをやりとり
tlsConn.ClientSequence++
tlsApplicationData := NewTLSApplicationData(upperLayerData, tlsConn.KeyBlock, tlsConn.ClientSequence)

tcp = NewTCPWithData(tcpConn.SrcPort, tcpConn.DstPort, tlsApplicationData, tcp.Acknowledgment, tcp.Sequence)
ipv6 = NewIPv6(IPv4_PROTO_TCP, srcIPAddr, dstIPAddr)
tcp.CalculateChecksumForIPv6(ipv6)

ipv6.Data = tcp.Bytes()
ipv6.PayloadLength = uint16(len(ipv6.Data))

// ここの EtherType は、ユーザー指定のを使う
// TODO: 他のパケットもそうした方が良い?
ethernetFrame = NewEthernetFrame(dstMACAddr, srcMACAddr, fEthrh.Typ, ipv6.Bytes())
if err := nw.Send(ethernetFrame); err != nil {
return err
}
tlsConn.SetState(TLSv12_STATE_SEND_APPLICATION_DATA)

continue
}

// 送信した Application Data に対するレスポンスを受けて FinAck 送信
if tcpConn.IsPassivePshAck(tcp) && tlsConn.IsSendApplicationData() {
// 受信した Application Data を復号
lengthOfEncrypted := bytesToInt(tcp.Data[3:5])
encrypted := tcp.Data[5 : 5+lengthOfEncrypted]
decrypted := DecryptApplicationData(encrypted, tlsConn.KeyBlock, tlsConn.ClientSequence)
// log.Printf("👺decrypted application data: %x, %s\n", decrypted, string(decrypted))
_ = decrypted

// TLS handshake の終了開始
tlsConn.ClientSequence++
tlsEncryptedAlert, _ := EncryptClientMessageForAlert(tlsConn.KeyBlock, tlsConn.ClientSequence, []byte{0x01, 0x00})
tcp := NewTCPWithData(tcpConn.SrcPort, tcpConn.DstPort, tlsEncryptedAlert, tcp.Acknowledgment, tcp.Sequence)
ipv6 := NewIPv6(IPv4_PROTO_TCP, srcIPAddr, dstIPAddr)
tcp.CalculateChecksumForIPv6(ipv6)

ipv6.Data = tcp.Bytes()
ipv6.PayloadLength = uint16(len(ipv6.Data))

ethernetFrame := NewEthernetFrame(dstMACAddr, srcMACAddr, ETHER_TYPE_IPv6, ipv6.Bytes())
if err := nw.Send(ethernetFrame); err != nil {
return err
}

// 続けてFinAck
tcp = NewTCPFinAck(tcpConn.SrcPort, tcpConn.DstPort, tcp.Sequence+uint32(len(tcp.Data)), tcp.Acknowledgment)
ipv6 = NewIPv6(IPv4_PROTO_TCP, srcIPAddr, dstIPAddr)
tcp.CalculateChecksumForIPv6(ipv6)

ipv6.Data = tcp.Bytes()
ipv6.PayloadLength = uint16(len(ipv6.Data))

ethernetFrame = NewEthernetFrame(dstMACAddr, srcMACAddr, ETHER_TYPE_IPv6, ipv6.Bytes())
if err := nw.Send(ethernetFrame); err != nil {
return err
}
continue
}

if tcpConn.IsPassiveFinAck(tcp) {
// それにack
tcp := NewTCPAck(tcpConn.SrcPort, tcpConn.DstPort, tcp.Sequence, tcp.Acknowledgment)
ipv6 := NewIPv6(IPv4_PROTO_TCP, srcIPAddr, dstIPAddr)
tcp.CalculateChecksumForIPv6(ipv6)

ipv6.Data = tcp.Bytes()
ipv6.PayloadLength = uint16(len(ipv6.Data))

ethernetFrame := NewEthernetFrame(dstMACAddr, srcMACAddr, ETHER_TYPE_IPv6, ipv6.Bytes())
if err := nw.Send(ethernetFrame); err != nil {
return err
}
tlsConn.Close()
tcpConn.Close()
return nil
}
}
}

return nil
}

func SendTLSClientHelloForIPv6(nw *NetworkInterface, clientHello *TLSClientHello, srcPort, dstPort uint16, srcIPAddr []uint8, dstIPAddr []uint8, firsthopMACAddr [6]byte, prevSequence uint32, prevAcknowledgment uint32) error {
tcp := NewTCPWithData(srcPort, dstPort, clientHello.Bytes(), prevSequence, prevAcknowledgment)
ipv6 := NewIPv6(IPv4_PROTO_TCP, srcIPAddr, dstIPAddr)
tcp.CalculateChecksumForIPv6(ipv6)

ipv6.Data = tcp.Bytes()
ipv6.PayloadLength = uint16(len(ipv6.Data))

dstMACAddr := HardwareAddr(firsthopMACAddr)
srcMACAddr := HardwareAddr(nw.Intf.HardwareAddr)
ethernetFrame := NewEthernetFrame(dstMACAddr, srcMACAddr, ETHER_TYPE_IPv6, ipv6.Bytes())
return nw.Send(ethernetFrame)
}

0 comments on commit b9338a8

Please sign in to comment.