Skip to content

Commit

Permalink
sec: trust config
Browse files Browse the repository at this point in the history
  • Loading branch information
pulsejet committed Jan 22, 2025
1 parent 399a0b4 commit 14bdeaa
Show file tree
Hide file tree
Showing 7 changed files with 484 additions and 52 deletions.
7 changes: 4 additions & 3 deletions dv/dv/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/named-data/ndnd/std/ndn"
mgmt "github.com/named-data/ndnd/std/ndn/mgmt_2022"
"github.com/named-data/ndnd/std/object"
sec "github.com/named-data/ndnd/std/security"
"github.com/named-data/ndnd/std/security/keychain"
"github.com/named-data/ndnd/std/security/trust_schema"
ndn_sync "github.com/named-data/ndnd/std/sync"
Expand All @@ -24,7 +25,7 @@ type Router struct {
// config for this router
config *config.Config
// trust configuration
trust *ndn.TrustConfig
trust *sec.TrustConfig
// object client
client *object.Client
// nfd management thread
Expand Down Expand Up @@ -66,7 +67,7 @@ func NewRouter(config *config.Config, engine ndn.Engine) (*Router, error) {
store := object.NewMemoryStore()

// Create security configuration
var trust *ndn.TrustConfig = nil
var trust *sec.TrustConfig = nil
if config.KeyChainUri == "insecure" {
log.Warn(nil, "Security is disabled - insecure mode")
} else {
Expand All @@ -78,7 +79,7 @@ func NewRouter(config *config.Config, engine ndn.Engine) (*Router, error) {
if err != nil {
return nil, err
}
trust = &ndn.TrustConfig{
trust = &sec.TrustConfig{
KeyChain: kc,
Schema: schema,
}
Expand Down
20 changes: 20 additions & 0 deletions std/ndn/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package ndn

import (
enc "github.com/named-data/ndnd/std/encoding"
)

// ExpressRArgs are the arguments for the express retry API
type ExpressRArgs struct {
Name enc.Name
Config *InterestConfig
AppParam enc.Wire
Signer Signer
Retries int
Callback ExpressCallbackFunc
}

type Client interface {
// Express a single interest with reliability
ExpressR(args ExpressRArgs, callback ExpressCallbackFunc)
}
18 changes: 0 additions & 18 deletions std/ndn/security_config.go

This file was deleted.

5 changes: 3 additions & 2 deletions std/object/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

enc "github.com/named-data/ndnd/std/encoding"
"github.com/named-data/ndnd/std/ndn"
sec "github.com/named-data/ndnd/std/security"
)

type Client struct {
Expand All @@ -13,7 +14,7 @@ type Client struct {
// data storage
store ndn.Store
// trust configuration
trust *ndn.TrustConfig
trust *sec.TrustConfig
// segment fetcher
fetcher rrSegFetcher

Expand All @@ -30,7 +31,7 @@ type Client struct {
}

// Create a new client with given engine and store
func NewClient(engine ndn.Engine, store ndn.Store, trust *ndn.TrustConfig) *Client {
func NewClient(engine ndn.Engine, store ndn.Store, trust *sec.TrustConfig) *Client {
client := new(Client)
client.engine = engine
client.store = store
Expand Down
163 changes: 163 additions & 0 deletions std/security/trust_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package security

import (
"fmt"
"sync"

enc "github.com/named-data/ndnd/std/encoding"
"github.com/named-data/ndnd/std/log"
"github.com/named-data/ndnd/std/ndn"
spec "github.com/named-data/ndnd/std/ndn/spec_2022"
)

// TrustConfig is the configuration of the trust module.
type TrustConfig struct {
// KeyChain is the keychain.
KeyChain ndn.KeyChain
// Schema is the trust schema.
Schema ndn.TrustSchema
// Roots are the full names of the trust anchors.
Roots []enc.Name

// mutex is the lock for keychain.
mutex sync.RWMutex
}

// Suggest suggests a signer for a given name.
func (tc *TrustConfig) Suggest(name enc.Name) ndn.Signer {
tc.mutex.RLock()
defer tc.mutex.RUnlock()

return tc.Schema.Suggest(name, tc.KeyChain)
}

// ValidateArgs are the arguments for the Validate function.
type ValidateArgs struct {
Data ndn.Data
Fetch func(enc.Name, *ndn.InterestConfig, func(ndn.Data, []byte, error))
Callback func(bool, error)

cert ndn.Data
depth int
}

// Validate validates a Data packet using a fetch API.
func (tc *TrustConfig) Validate(args ValidateArgs) {
if args.Data == nil {
args.Callback(false, fmt.Errorf("data is nil"))
return
}

if args.depth == 0 {
args.depth = 32
} else if args.depth <= 1 {
args.Callback(false, fmt.Errorf("max depth reached"))
return
} else {
args.depth--
}

if args.cert != nil {
// Check if the data claims to be a root certificate.
// This breaks the recursion for validation.
if args.Data.Name().Equal(args.cert.Name()) {
for _, root := range tc.Roots {
if args.Data.Name().Equal(root) {
args.Callback(true, nil)
return
}
}
args.Callback(false, fmt.Errorf("data claims to be a trust anchor: %s", args.Data.Name()))
return
}

// Check schema if the key is allowed
if !tc.Schema.Check(args.Data.Name(), args.cert.Name()) {
args.Callback(false, fmt.Errorf("key is not allowed: %s signed by %s", args.Data.Name(), args.cert.Name()))
return
}

// TODO: validate signature

// Recursively validate the certificate
tc.Validate(ValidateArgs{
Data: args.cert,
cert: nil,
Fetch: args.Fetch,
depth: args.depth,
Callback: args.Callback,
})
return
}

// Get the certificate using the key locator
signature := args.Data.Signature()
if signature == nil {
args.Callback(false, fmt.Errorf("signature is nil"))
return
}

// Get the key locator
keyLocator := signature.KeyName()
if keyLocator == nil {
args.Callback(false, fmt.Errorf("key locator is nil"))
return
}

// Detect if this is a self-signed certificate, and automatically pick the cert
// as itself to verify in this case.
if args.Data.ContentType() != nil && *args.Data.ContentType() == ndn.ContentTypeKey && keyLocator.IsPrefix(args.Data.Name()) {
args.cert = args.Data
tc.Validate(args)
return
}

// Attempt to get cert from store
certBytes, err := tc.KeyChain.Store().Get(keyLocator, true)
if err != nil {
log.Error(nil, "Failed to get certificate from store", "error", err)
args.Callback(false, err)
return // store is likely broken
}
if len(certBytes) > 0 {
// Attempt to parse the certificate
args.cert, _, err = spec.Spec{}.ReadData(enc.NewBufferReader(certBytes))
if err != nil {
log.Error(nil, "Failed to parse certificate in store", "error", err)
args.cert = nil
}
}

// If not found, attempt to fetch cert from network
if args.cert == nil {
args.Fetch(keyLocator, &ndn.InterestConfig{
CanBePrefix: true,
MustBeFresh: true,
}, func(cert ndn.Data, wire []byte, err error) {
if err != nil {
args.Callback(false, err)
return // failed to fetch cert
}
args.cert = cert

// Monkey patch the callback to store the cert in keychain
// if the validation passes.
origCallback := args.Callback
args.Callback = func(valid bool, err error) {
if valid && err == nil {
tc.mutex.Lock()
err := tc.KeyChain.InsertCert(wire)
tc.mutex.Unlock()
if err != nil {
log.Error(tc.KeyChain, "Failed to insert certificate", "error", err)
}
}
origCallback(valid, err) // continue validation
}

tc.Validate(args)
})
} else {
tc.Validate(args)
}
}
Loading

0 comments on commit 14bdeaa

Please sign in to comment.