Initial commit: crypto.go and protocol.go implementation + unit tests

This commit is contained in:
robert
2026-01-04 20:18:59 +00:00
commit e98625ef58
7 changed files with 506 additions and 0 deletions

209
crypto.go Normal file
View File

@@ -0,0 +1,209 @@
package ownwire_sdk
import (
"crypto/aes"
"crypto/cipher"
"crypto/ecdh"
"crypto/hkdf"
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"fmt"
)
const (
hkdf_info_prefix = "ownwire/v1:"
nonce_label = "ownwire/v1:gcm-nonce"
)
type Keypair struct {
ClientPriv *ecdh.PrivateKey
ClientPub []byte // 65 bytes, uncompressed
}
func GenClientKey() (Keypair, error) {
curve := ecdh.P256()
client_priv, err := curve.GenerateKey(rand.Reader)
if err != nil {
return Keypair{}, err
}
client_pub := client_priv.PublicKey().Bytes()
if len(client_pub) != 65 {
return Keypair{}, fmt.Errorf("unexpected P-256 pubkey length: %d", len(client_pub))
}
return Keypair{
ClientPriv: client_priv,
ClientPub: client_pub,
}, nil
}
// DeriveSharedKey does:
// ECDH -> 32 bytes shared secret
// HKDF-SHA256(salt, info="ownwire/v1:<session_id>") -> 32 bytes
func DeriveSharedKey(session_id string, client_priv *ecdh.PrivateKey, server_pub_raw []byte, salt []byte) ([32]byte, error) {
var out [32]byte
if client_priv == nil {
return out, fmt.Errorf("client_priv is nil")
}
curve := ecdh.P256()
server_pub, err := curve.NewPublicKey(server_pub_raw)
if err != nil {
return out, fmt.Errorf("invalid server pubkey: %w", err)
}
shared_secret, err := client_priv.ECDH(server_pub)
if err != nil {
return out, fmt.Errorf("ecdh failed: %w", err)
}
info_str := hkdf_info_prefix + session_id
prk, err := hkdf.Extract(sha256.New, shared_secret, salt)
if err != nil {
return out, fmt.Errorf("hkdf extract failed: %w", err)
}
okm, err := hkdf.Expand(sha256.New, prk, info_str, 32)
if err != nil {
return out, fmt.Errorf("hkdf expand failed: %w", err)
}
copy(out[:], okm)
zero_bytes(shared_secret)
return out, nil
}
// DeriveNonce computes:
// HMAC-SHA256(key=shared_key, data=label + uuid_bytes + salt16 + seq_be8 + flag)
// IV = first 12 bytes
func DeriveNonce(shared_key [32]byte, session_uuid_bytes [16]byte, salt16 [16]byte, seq_num uint64, is_response bool) [12]byte {
var iv [12]byte
seq_be8 := [8]byte{}
for i := 7; i >= 0; i-- {
seq_be8[i] = byte(seq_num & 0xff)
seq_num >>= 8
}
flag := byte(0)
if is_response {
flag = 1
}
mac := hmac.New(sha256.New, shared_key[:])
mac.Write([]byte(nonce_label))
mac.Write(session_uuid_bytes[:])
mac.Write(salt16[:])
mac.Write(seq_be8[:])
mac.Write([]byte{flag})
sum := mac.Sum(nil) // 32 bytes
copy(iv[:], sum[:12])
return iv
}
type EncryptedPayload struct {
ContentB64 string
SaltHex string
}
func EncryptAESGCM(shared_key [32]byte, session_uuid_bytes [16]byte, plain_text []byte, seq_num uint64, is_response bool) (EncryptedPayload, error) {
var salt16 [16]byte
if _, err := rand.Read(salt16[:]); err != nil {
return EncryptedPayload{}, err
}
iv := DeriveNonce(shared_key, session_uuid_bytes, salt16, seq_num, is_response)
block, err := aes.NewCipher(shared_key[:])
if err != nil {
return EncryptedPayload{}, err
}
aead, err := cipher.NewGCM(block)
if err != nil {
return EncryptedPayload{}, err
}
ct := aead.Seal(nil, iv[:], plain_text, nil)
return EncryptedPayload{
ContentB64: base64.StdEncoding.EncodeToString(ct),
SaltHex: hex.EncodeToString(salt16[:]),
}, nil
}
func DecryptAESGCM(shared_key [32]byte, session_uuid_bytes [16]byte, content_b64 string, salt_hex string, seq_num uint64, is_response bool) ([]byte, error) {
ct, err := base64.StdEncoding.DecodeString(content_b64)
if err != nil {
return nil, fmt.Errorf("invalid content base64: %w", err)
}
salt_raw, err := hex.DecodeString(salt_hex)
if err != nil {
return nil, fmt.Errorf("invalid salt hex: %w", err)
}
if len(salt_raw) != 16 {
return nil, fmt.Errorf("invalid salt length: %d", len(salt_raw))
}
var salt16 [16]byte
copy(salt16[:], salt_raw)
iv := DeriveNonce(shared_key, session_uuid_bytes, salt16, seq_num, is_response)
block, err := aes.NewCipher(shared_key[:])
if err != nil {
return nil, err
}
aead, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
pt, err := aead.Open(nil, iv[:], ct, nil)
if err != nil {
return nil, err
}
return pt, nil
}
// ParseUUIDBytes parses "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" into 16 bytes.
func ParseUUIDBytes(uuid_str string) ([16]byte, error) {
var out [16]byte
clean := make([]byte, 0, 32)
for i := 0; i < len(uuid_str); i++ {
b := uuid_str[i]
if b == '-' {
continue
}
clean = append(clean, b)
}
if len(clean) != 32 {
return out, fmt.Errorf("invalid uuid: %q", uuid_str)
}
decoded, err := hex.DecodeString(string(clean))
if err != nil {
return out, fmt.Errorf("invalid uuid hex: %w", err)
}
copy(out[:], decoded)
return out, nil
}
func zero_bytes(b []byte) {
for i := range b {
b[i] = 0
}
}

108
crypto_test.go Normal file
View File

@@ -0,0 +1,108 @@
package ownwire_sdk_test
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
sdk "ownwire.net/ownwire-sdk"
)
func derive_nonce_reference(shared_key [32]byte, session_uuid_bytes [16]byte, salt16 [16]byte, seq_num uint64, is_response bool) [12]byte {
seq_be8 := [8]byte{}
for i := 7; i >= 0; i-- {
seq_be8[i] = byte(seq_num & 0xff)
seq_num >>= 8
}
flag := byte(0)
if is_response {
flag = 1
}
mac := hmac.New(sha256.New, shared_key[:])
mac.Write([]byte("ownwire/v1:gcm-nonce"))
mac.Write(session_uuid_bytes[:])
mac.Write(salt16[:])
mac.Write(seq_be8[:])
mac.Write([]byte{flag})
sum := mac.Sum(nil)
var iv [12]byte
copy(iv[:], sum[:12])
return iv
}
var _ = Describe("Crypto", func() {
It("parses UUID bytes", func() {
uuid_str := "cb653f53-6f7d-4aeb-ba0d-d2b17c290d8a"
uuid_bytes, err := sdk.ParseUUIDBytes(uuid_str)
Expect(err).To(BeNil())
Expect(hex.EncodeToString(uuid_bytes[:])).To(Equal("cb653f536f7d4aebba0dd2b17c290d8a"))
})
It("derives nonce exactly as reference implementation", func() {
var shared_key [32]byte
for i := 0; i < 32; i++ {
shared_key[i] = byte(i)
}
uuid_bytes, err := sdk.ParseUUIDBytes("cb653f53-6f7d-4aeb-ba0d-d2b17c290d8a")
Expect(err).To(BeNil())
var salt16 [16]byte
for i := 0; i < 16; i++ {
salt16[i] = byte(0xa0 + i)
}
seq_num := uint64(0x0102030405060708)
got := sdk.DeriveNonce(shared_key, uuid_bytes, salt16, seq_num, false)
want := derive_nonce_reference(shared_key, uuid_bytes, salt16, seq_num, false)
Expect(got).To(Equal(want))
})
It("encrypts and decrypts roundtrip", func() {
var shared_key [32]byte
for i := 0; i < 32; i++ {
shared_key[i] = byte(0x55 ^ i)
}
uuid_bytes, err := sdk.ParseUUIDBytes("cb653f53-6f7d-4aeb-ba0d-d2b17c290d8a")
Expect(err).To(BeNil())
plain_text := []byte("hello ownwire")
seq_num := uint64(123)
enc, err := sdk.EncryptAESGCM(shared_key, uuid_bytes, plain_text, seq_num, false)
Expect(err).To(BeNil())
Expect(enc.ContentB64).ToNot(BeEmpty())
Expect(enc.SaltHex).To(HaveLen(32)) // 16 bytes hex
out, err := sdk.DecryptAESGCM(shared_key, uuid_bytes, enc.ContentB64, enc.SaltHex, seq_num, false)
Expect(err).To(BeNil())
Expect(string(out)).To(Equal(string(plain_text)))
})
It("fails decrypt if seq changes", func() {
var shared_key [32]byte
for i := 0; i < 32; i++ {
shared_key[i] = byte(0x11 + i)
}
uuid_bytes, err := sdk.ParseUUIDBytes("cb653f53-6f7d-4aeb-ba0d-d2b17c290d8a")
Expect(err).To(BeNil())
enc, err := sdk.EncryptAESGCM(shared_key, uuid_bytes, []byte("hello"), 1, false)
Expect(err).To(BeNil())
_, err = sdk.DecryptAESGCM(shared_key, uuid_bytes, enc.ContentB64, enc.SaltHex, 2, false)
Expect(err).ToNot(BeNil())
})
})

23
go.mod Normal file
View File

@@ -0,0 +1,23 @@
module ownwire.net/ownwire-sdk
go 1.25.4
require (
github.com/onsi/ginkgo/v2 v2.27.3
github.com/onsi/gomega v1.38.3
)
require (
github.com/Masterminds/semver/v3 v3.4.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/mod v0.27.0 // indirect
golang.org/x/net v0.43.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/sys v0.35.0 // indirect
golang.org/x/text v0.28.0 // indirect
golang.org/x/tools v0.36.0 // indirect
)

69
go.sum Normal file
View File

@@ -0,0 +1,69 @@
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs=
github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk=
github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01KS3zGE=
github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE=
github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo=
github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg=
github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE=
github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A=
github.com/onsi/ginkgo/v2 v2.27.3 h1:ICsZJ8JoYafeXFFlFAG75a7CxMsJHwgKwtO+82SE9L8=
github.com/onsi/ginkgo/v2 v2.27.3/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
github.com/onsi/gomega v1.38.3 h1:eTX+W6dobAYfFeGC2PV6RwXRu/MyT+cQguijutvkpSM=
github.com/onsi/gomega v1.38.3/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A=
google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

14
ownwire_sdk_suite_test.go Normal file
View File

@@ -0,0 +1,14 @@
package ownwire_sdk_test
import (
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestOwnwireSdk(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Ownwire SDK Suite")
}

47
protocol.go Normal file
View File

@@ -0,0 +1,47 @@
package ownwire_sdk
import (
"fmt"
"strconv"
"strings"
)
type SessionInit struct {
SessionId string
ServerPubKeyB64 string
SaltB64 string
SeqOut uint64
SeqInMax uint64
}
// ParseSessionInit parses:
// "/session:<id>:<server_pubkey_b64>:<salt_b64>:<seq_out>:<seq_in_max>"
func ParseSessionInit(line string) (SessionInit, error) {
if !strings.HasPrefix(line, "/session:") {
return SessionInit{}, fmt.Errorf("invalid session line prefix: %q", line)
}
parts := strings.Split(line, ":")
if len(parts) < 6 || parts[0] != "/session" {
return SessionInit{}, fmt.Errorf("invalid session line: %q", line)
}
seq_out, err := strconv.ParseUint(parts[4], 10, 64)
if err != nil {
return SessionInit{}, fmt.Errorf("invalid seq_out: %w", err)
}
seq_in_max, err := strconv.ParseUint(parts[5], 10, 64)
if err != nil {
return SessionInit{}, fmt.Errorf("invalid seq_in_max: %w", err)
}
out := SessionInit{
SessionId: parts[1],
ServerPubKeyB64: parts[2],
SaltB64: parts[3],
SeqOut: seq_out,
SeqInMax: seq_in_max,
}
return out, nil
}

36
protocol_test.go Normal file
View File

@@ -0,0 +1,36 @@
package ownwire_sdk_test
import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
sdk "ownwire.net/ownwire-sdk"
)
var _ = Describe("ParseSessionInit", func() {
It("parses a valid /session line", func() {
line := "/session:cb653f53-6f7d-4aeb-ba0d-d2b17c290d8a:SERVERPUBB64:SALTB64:12:34"
parsed, err := sdk.ParseSessionInit(line)
Expect(err).To(BeNil())
Expect(parsed.SessionId).To(Equal("cb653f53-6f7d-4aeb-ba0d-d2b17c290d8a"))
Expect(parsed.ServerPubKeyB64).To(Equal("SERVERPUBB64"))
Expect(parsed.SaltB64).To(Equal("SALTB64"))
Expect(parsed.SeqOut).To(Equal(uint64(12)))
Expect(parsed.SeqInMax).To(Equal(uint64(34)))
})
It("rejects non-session lines", func() {
_, err := sdk.ParseSessionInit("/wat:1:2:3:4:5")
Expect(err).ToNot(BeNil())
})
It("rejects missing fields", func() {
_, err := sdk.ParseSessionInit("/session:1:2:3")
Expect(err).ToNot(BeNil())
})
It("rejects bad sequence numbers", func() {
_, err := sdk.ParseSessionInit("/session:1:2:3:nope:5")
Expect(err).ToNot(BeNil())
})
})