Skip to content

Commit

Permalink
WIP request key
Browse files Browse the repository at this point in the history
  • Loading branch information
cryptix committed May 24, 2024
1 parent bf031cf commit 75afede
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 38 deletions.
24 changes: 24 additions & 0 deletions golang/sig0/answers.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,27 @@ func ParseBase64Answer(answer string) (*dns.Msg, error) {
}
return resp, nil
}

func ExpectSOA(answer *dns.Msg) (string, error) {
if len(answer.Answer) < 1 {
return "", fmt.Errorf("expected at least one authority section.")
}
firstNS := answer.Answer[0]
soa, ok := firstNS.(*dns.SOA)
if !ok {
return "", fmt.Errorf("expected SOA but got type of RR: %T: %+v", firstNS, firstNS)
}
return soa.Ns, nil
}

func ExpectAdditonalSOA(answer *dns.Msg) (string, error) {
if len(answer.Ns) < 1 {
return "", fmt.Errorf("expected at least one authority section.")
}
firstNS := answer.Ns[0]
soa, ok := firstNS.(*dns.SOA)
if !ok {
return "", fmt.Errorf("expected SOA but got type of RR: %T: %+v", firstNS, firstNS)
}
return soa.Ns, nil
}
33 changes: 5 additions & 28 deletions golang/sig0/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import (
)

// QueryA returns a base64 encoded string of a DNS Question for an A record of the passed domain name
func QuerySOA(zone string) (*dns.Msg, error) {
return QueryWithType(zone, dns.TypeSOA)
}

func QueryA(name string) (*dns.Msg, error) {
return QueryWithType(name, dns.TypeA)
}
Expand All @@ -30,38 +34,11 @@ func QueryWithType(name string, qtype uint16) (*dns.Msg, error) {
MsgHdr: dns.MsgHdr{Id: dns.Id(), Opcode: dns.OpcodeQuery, RecursionDesired: true},
Question: []dns.Question{q},
}

if os.Getenv("DEBUG") != "" {
fmt.Println("DNS Query:")
spew.Dump(m)
}

return m, nil
}

func QuerySOA(zone string) (*dns.Msg, error) {
return QueryWithType(zone, dns.TypeSOA)
}

func ExpectSOA(answer *dns.Msg) (string, error) {
if len(answer.Answer) < 1 {
return "", fmt.Errorf("expected at least one authority section.")
}
firstNS := answer.Answer[0]
soa, ok := firstNS.(*dns.SOA)
if !ok {
return "", fmt.Errorf("expected SOA but got type of RR: %T: %+v", firstNS, firstNS)
}
return soa.Ns, nil
}

func ExpectAdditonalSOA(answer *dns.Msg) (string, error) {
if len(answer.Ns) < 1 {
return "", fmt.Errorf("expected at least one authority section.")
}
firstNS := answer.Ns[0]
soa, ok := firstNS.(*dns.SOA)
if !ok {
return "", fmt.Errorf("expected SOA but got type of RR: %T: %+v", firstNS, firstNS)
}
return soa.Ns, nil
}
118 changes: 118 additions & 0 deletions golang/sig0/request_key.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package sig0

import (
"errors"
"fmt"
"strings"

"github.com/miekg/dns"
)

var (
SignalSubzonePrefix = "_signal"
DefaultTTL = 300
DefaultDOHResolver = "dns.quad9.net"
)

func CreateRequestKeyMsg(subZone, zone string) (*dns.Msg, error) {

// Determine the zone master using the provided sub zone and base zone
signalZone := fmt.Sprintf("%s.%s", SignalSubzonePrefix, zone)
querySOAForSignal, err := QuerySOA(signalZone)
if err != nil {
return nil, fmt.Errorf("Error: ZONE %s SOA record does not resolve: %w", signalZone, err)
}

soaAnswer, err := SendDOHQuery(DefaultDOHResolver, querySOAForSignal)
if err != nil {
return nil, fmt.Errorf("Error: DOH query failed for %s: %w", DefaultDOHResolver, err)
}

zoneSoa, err := ExpectSOA(soaAnswer)
if err != nil {
return nil, fmt.Errorf("Error: SOA record not found in response for %s: %w", signalZone, err)
}

if zoneSoa != "ns1.free2air.org." {
return nil, fmt.Errorf("Unexpected SOA: %s - TODO: Query SVCB to get the zone master's DOH endpoint", zoneSoa)
}
var dohUpdateURL = "doh.zenr.io"

// Check if zone already exists
newSubZone := fmt.Sprintf("%s.%s", subZone, zone)
err = checkZoneDoesntExist(dohUpdateURL, newSubZone)
if err != nil {
return nil, err
}

zoneRequest := fmt.Sprintf("%s.%s.%s", subZone, SignalSubzonePrefix, zone)
err = checkZoneDoesntExist(dohUpdateURL, zoneRequest)
if err != nil {
return nil, err
}

// craft RRs and create signed update
subZoneSigner, err := LoadOrGenerateKey(newSubZone)
if err != nil {
return nil, err
}

err = subZoneSigner.StartUpdate(zone)
if err != nil {
return nil, err
}

// Here we split the key details
keyDetails := strings.TrimSpace(subZoneSigner.Key.String())
keyFields := strings.Fields(keyDetails)
if len(keyFields) < 6 {
return nil, errors.New("Invalid key data")
}
keyData := strings.Join(keyFields[3:], " ")

nsupdateItemSig0Key := fmt.Sprintf("%s %d %s", zoneRequest, DefaultTTL, keyData)
err = subZoneSigner.UpdateParsedRR(nsupdateItemSig0Key)
if err != nil {
return nil, err
}

nsupdateItemPtr := fmt.Sprintf("%s %d IN PTR %s.", newSubZone, DefaultTTL, zoneRequest)
err = subZoneSigner.UpdateParsedRR(nsupdateItemPtr)
if err != nil {
return nil, err
}

signedUpdateMsg, err := subZoneSigner.SignUpdate()
if err != nil {
return nil, err
}

// updateAnswer, err := SendDOHQuery(dohUpdateURL, signedUpdateMsg)
// if err != nil {
// return nil, err
// }

// if updateAnswer.Rcode != dns.RcodeSuccess {
// return nil, fmt.Errorf("Error: Update failed: %v", updateAnswer)
// }

return signedUpdateMsg, nil
}

func checkZoneDoesntExist(dohServer, zone string) error {
doesExistQuery, err := QueryAny(zone)
if err != nil {
return err
}

doesExistAnswer, err := SendDOHQuery(dohServer, doesExistQuery)
if err != nil {
return err
}

if doesExistAnswer.Rcode != dns.RcodeNameError {
return fmt.Errorf("new zone %s already exists: %v", zone, doesExistAnswer)
}

return nil
}
24 changes: 24 additions & 0 deletions golang/sig0/request_key_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package sig0

import (
"crypto/rand"
"fmt"
"testing"

"github.com/stretchr/testify/require"
)

func TestRequestKey(t *testing.T) {
r := require.New(t)

buf := make([]byte, 5)
rand.Read(buf)
testSubZone := fmt.Sprintf("sig0namectl-test-%x", buf)

zoneRequestMsg, err := CreateRequestKeyMsg(testSubZone, "zenr.io")
r.NoError(err)
t.Log(zoneRequestMsg)
t.FailNow()

// TODO: cleanup test keys
}
23 changes: 13 additions & 10 deletions golang/sig0/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,24 +56,27 @@ func (signer *Signer) SignUpdate() (*dns.Msg, error) {
return m, nil
}

func (signer *Signer) UpdateRR(rr string) error {
if signer.update == nil {
return fmt.Errorf("no update in progress")
}

log.Println("-- Attach RR to dns.Msg --")
func (signer *Signer) UpdateParsedRR(rr string) error {
rrInsert, err := dns.NewRR(rr)
if err != nil {
return err
}

signer.update.Insert([]dns.RR{rrInsert})
return signer.UpdateRR(rrInsert)
}

func (signer *Signer) UpdateRR(rr dns.RR) error {
if signer.update == nil {
return fmt.Errorf("no update in progress")
}

signer.update.Insert([]dns.RR{rr})
return nil
}

// UpdateA is a convenience function to update an A record.
// Need to call StartUpdate first, then UpdateA for each record to update, then SignUpdate.
func (signer *Signer) UpdateA(host, zone, addr string) error {
func (signer *Signer) UpdateA(subZone, zone, addr string) error {
if signer.update == nil {
return fmt.Errorf("no update in progress")
}
Expand All @@ -83,6 +86,6 @@ func (signer *Signer) UpdateA(host, zone, addr string) error {
return fmt.Errorf("invalid IPv4 address: %s", addr)
}

myRR := fmt.Sprintf("%s.%s 600 IN A %s", host, zone, addr)
return signer.UpdateRR(myRR)
myRR := fmt.Sprintf("%s.%s %d IN A %s", subZone, zone, DefaultTTL, addr)
return signer.UpdateParsedRR(myRR)
}

0 comments on commit 75afede

Please sign in to comment.