Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion pcapgo/ngread.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ type NgReader struct {
firstSectionFound bool
activeSection bool
bigEndian bool
decryptionSecrets []decryptionSecret
}

// NewNgReader initializes a new writer, reads the first section header, and if necessary according to the options the first interface.
Expand All @@ -64,7 +65,8 @@ func NewNgReader(r io.Reader, options NgReaderOptions) (*NgReader, error) {
currentOption: ngOption{
value: make([]byte, 1024),
},
options: options,
decryptionSecrets: make([]decryptionSecret, 0),
options: options,
}

//pcapng _must_ start with a section header
Expand Down Expand Up @@ -314,6 +316,10 @@ func (r *NgReader) firstInterface() error {
return nil
case ngBlockTypePacket, ngBlockTypeEnhancedPacket, ngBlockTypeSimplePacket, ngBlockTypeInterfaceStatistics:
return errors.New("A section must have an interface before a packet block")
case ngBlockTypeDecryptionSecrets:
if err := r.readDecryptionSecretsBlock(); err != nil {
return err
}
}
if _, err := r.r.Discard(int(r.currentBlock.length)); err != nil {
return err
Expand Down
39 changes: 39 additions & 0 deletions pcapgo/ngread_dsb.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright 2018 The GoPacket Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
// author: CFC4N <[email protected]>

package pcapgo

import "fmt"

type decryptionSecret struct {
blockInfo pcapngDecryptionSecretsBlock
payload []byte
}

// readDecryptionSecrets parses an encryption secrets section from the given
func (r *NgReader) readDecryptionSecretsBlock() error {
if err := r.readBytes(r.buf[:8]); err != nil {
return fmt.Errorf("could not read DecryptionSecret Header block length: %v", err)
}
r.currentBlock.length -= 8

var decryptionSecretsBlock = &pcapngDecryptionSecretsBlock{}
decryptionSecretsBlock.secretsType = r.getUint32(r.buf[0:4])
decryptionSecretsBlock.secretsLength = r.getUint32(r.buf[4:8])
var payload = make([]byte, decryptionSecretsBlock.secretsLength)
if err := r.readBytes(payload); err != nil {
return fmt.Errorf("could not read %d bytes from DecryptionSecret payload: %v", decryptionSecretsBlock.secretsLength, err)
}
r.currentBlock.length -= uint32(len(payload))

// save decryption secrets
var decryptSecret decryptionSecret
decryptSecret.blockInfo = *decryptionSecretsBlock
decryptSecret.payload = payload
r.decryptionSecrets = append(r.decryptionSecrets, decryptSecret)
return nil
}
72 changes: 72 additions & 0 deletions pcapgo/ngread_dsb_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package pcapgo

import (
"fmt"
"github.com/gopacket/gopacket"
"io"
"os"
"testing"
"time"
)

type BufferPacketSource struct {
index int
data [][]byte
ci []gopacket.CaptureInfo
}

// TestNgReadDSB tests the readDecryptionSecretsBlock function.
func TestNgReaderDSB(t *testing.T) {

// Test that we can read a pcapng file with DSB.
pcapngFile := "tests/le/test301.pcapng"
start := time.Now()
testf, err := os.Open(pcapngFile)
if err != nil {
t.Fatal("Couldn't open file:", err)
}
defer testf.Close()
options := DefaultNgReaderOptions
options.SkipUnknownVersion = true

var r *NgReader
r, err = NewNgReader(testf, options)
if err != nil {
t.Fatal("Couldn't read start of file:", err)
}

b := &BufferPacketSource{}
var ii int
var found bool
for {
data, ci, err := r.ReadPacketData()
if err == io.EOF {
t.Log("ReadPacketData returned EOF")
break
}
b.data = append(b.data, data)
b.ci = append(b.ci, ci)
if !found && len(r.decryptionSecrets) > 0 {
found = true
t.Log("Decryption Secrets Block found, index block:", ii)
}
ii++
}
if len(b.data) != len(b.ci) || len(b.ci) <= 0 {
t.Fatal("unexpected data or data length:", len(b.data), ", ci length", len(b.ci))
}

duration := time.Since(start)
t.Logf("bigEndian %t", r.bigEndian)
t.Logf("Reading packet data into memory: %d packets in %v, %v per packet\n", len(b.data), duration, duration/time.Duration(len(b.data)))

t.Log("decryptionSecrets:", len(r.decryptionSecrets))
i := 0
for _, secret := range r.decryptionSecrets {
t.Log(fmt.Sprintf("SecretType:%X, Length:%d, Data:%s", secret.blockInfo.secretsType, secret.blockInfo.secretsLength, secret.payload))
i++
}
if i <= 0 {
t.Fatal("Can't found decryption secrets")
}
}
110 changes: 110 additions & 0 deletions pcapgo/ngwrite_dsb.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright 2018 The GoPacket Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
// author: CFC4N <[email protected]>

package pcapgo

import (
"encoding/binary"
)

/*
Decryption Secrets Block (DSB) memory layout.
via https://github.com/pcapng/pcapng/blob/master/draft-tuexen-opsawg-pcapng.md
1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
0 | Block Type = 0x0000000A |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4 | Block Total Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
8 | Secrets Type |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
12 | Secrets Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
16 / /
/ Secrets Data /
/ (variable length, padded to 32 bits) /
/ /
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/ /
/ Options (variable) /
/ /
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/ Block Total Length /
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Block Type: The block type of the Decryption Secrets Block is 10.

Block Total Length: total size of this block, as described in {{section_block}}.

Secrets Type (32 bits): an unsigned integer identifier that describes the format of the following Secrets field. Requests for new Secrets Type codes should be made by creating a pull request to update this document as described in {{section_block_code_registry}}.

Secrets Length (32 bits): an unsigned integer that indicates the size of the following Secrets field, without any padding octets.

Secrets Data: binary data containing secrets, padded to a 32 bit boundary.

Options: optionally, a list of options (formatted according to the rules defined in {{section_opt}}) can be present. No DSB-specific options are currently defined.
*/

const (
PcapngBlockHeadersize = 8 // block type + block total length
PcapngDecryptionSecretsBlockSize = 8 // Secrets type + Secrets length
)

// pcapngBlockHeader is the header of a pcapng block.
type pcapngBlockHeader struct {
blockType uint32
blockTotalLength uint32
}

// pcapngDecryptionSecretsBlock is the header of a section.
type pcapngDecryptionSecretsBlock struct {
secretsType uint32
secretsLength uint32
}

// WriteDecryptionSecretsBlock writes a Decryption Secrets Block to the writer.
func (w *NgWriter) WriteDecryptionSecretsBlock(secretType uint32, secretPayload []byte) error {

switch secretType {
case DSB_SECRETS_TYPE_SSH, DSB_SECRETS_TYPE_ZIGBEE_NWK_KEY, DSB_SECRETS_TYPE_WIREGUARD, DSB_SECRETS_TYPE_ZIGBEE_APS_KEY, DSB_SECRETS_TYPE_TLS:
default:
// unknown secrets type
return ErrUnknownSecretsType
}

secretPayloadLen := len(secretPayload)
padding := (4 - secretPayloadLen&3) & 3

// via https://github.com/wireshark/wireshark/blob/885d6b7f731760f4a76e0f257af57d03934986ed/wiretap/pcapng.c#L5233
// langth = MIN_DSB_SIZE + secretPayloadLen + padding
// MIN_DSB_SIZE = MIN_BLOCK_SIZE + PcapngDecryptionSecretsBlockSize
// MIN_BLOCK_SIZE = PcapngBlockHeadersize + 4
//
length := uint32(PcapngBlockHeadersize + 4 + PcapngDecryptionSecretsBlockSize + secretPayloadLen + padding)

// write block header
binary.LittleEndian.PutUint32(w.buf[:4], uint32(ngBlockTypeDecryptionSecrets))
binary.LittleEndian.PutUint32(w.buf[4:8], length)

// write decryption secrets block
binary.LittleEndian.PutUint32(w.buf[8:12], secretType)
binary.LittleEndian.PutUint32(w.buf[12:16], uint32(secretPayloadLen))

if _, err := w.w.Write(w.buf[:16]); err != nil {
return err
}

// write secrets data
if _, err := w.w.Write(secretPayload); err != nil {
return err
}

binary.LittleEndian.PutUint32(w.buf[:4], 0)
_, err := w.w.Write(w.buf[4-padding : 8]) // padding + length
return err
}
122 changes: 122 additions & 0 deletions pcapgo/ngwrite_dsb_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package pcapgo

import (
"fmt"
"github.com/gopacket/gopacket/layers"
"io"
"math"
"net"
"os"
"testing"
)

// TestNgWriterDSB tests the WriteDecryptionSecretsBlock function.
func TestNgWriterDSB(t *testing.T) {

// Test that we can read a DSB file.
pcapngFile := "tests/le/test300.pcapng"
tlsKey := "CLIENT_RANDOM 65bafa1a1a37aebce6ab7af420f9a6ca10513ad1d53aececbe831a28982a5c18 8d1e0c21e653f8e0720c987c3daaca094ff6eb1ccc9e15a8384a214139dfdcf25f0ee77ac81250c7a11b8da561313528\n"
testf, err := os.Open(pcapngFile)
if err != nil {
t.Fatal("Couldn't open file:", err)
}
defer testf.Close()
options := DefaultNgReaderOptions
options.SkipUnknownVersion = true
var r *NgReader
r, err = NewNgReader(testf, options)
if err != nil {
t.Fatal("Couldn't read start of file:", err)
}

b := &BufferPacketSource{}
for {
data, ci, err := r.ReadPacketData()
if err == io.EOF {
t.Log("ReadPacketData returned EOF")
break
}
b.data = append(b.data, data)
b.ci = append(b.ci, ci)
}

t.Logf("bigEndian %t", r.bigEndian)
t.Logf("len(b.data) %d", len(b.data))
t.Logf("len(b.ci) %d", len(b.ci))

tmpPcapng := "tests/dbs_tmp.pcapng"
writer, err := createPcapng(tmpPcapng)

//write Decryption Secrets Block
err = writer.WriteDecryptionSecretsBlock(DSB_SECRETS_TYPE_TLS, []byte(tlsKey))
if err != nil {
t.Fatal("Couldn't write Decryption Secrets Block:", err)
}

for i, ci := range b.ci {
err = writer.WritePacket(ci, b.data[i])
if err != nil {
t.Fatal(err)
}
}
err = writer.Flush()
if err != nil {
t.Fatal(err)
}

t.Log("Wrote Decryption Secrets Block.")

// TODO check DSB

os.Remove(tmpPcapng)
}

func createPcapng(pcapngFilename string) (*NgWriter, error) {
pcapFile, err := os.OpenFile(pcapngFilename, os.O_TRUNC|os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
return nil, fmt.Errorf("error creating pcap file: %v", err)
}

pcapOption := NgWriterOptions{
SectionInfo: NgSectionInfo{
Hardware: "eCapture Hardware",
OS: "",
Application: "ecapture.lua",
Comment: "see https://ecapture.cc for more information.",
},
}
// write interface description
ngIface := NgInterface{
Name: "eth0",
Comment: "gopacket: https://github.com/google/gopacket",
Filter: "",
LinkType: layers.LinkTypeEthernet,
SnapLength: uint32(math.MaxUint16),
}

pcapWriter, err := NewNgWriterInterface(pcapFile, ngIface, pcapOption)
if err != nil {
return nil, err
}

netIfs, err := net.Interfaces()
if err != nil {
return nil, err
}
// insert other interfaces into pcapng file
for _, iface := range netIfs {
ngIface = NgInterface{
Name: iface.Name,
Comment: "see https://ecapture.cc for more information.",
Filter: "",
LinkType: layers.LinkTypeEthernet,
SnapLength: uint32(math.MaxUint16),
}

_, err = pcapWriter.AddInterface(ngIface)
if err != nil {
return nil, err
}
}
return pcapWriter, nil
}
Loading