Files
brianmcgee 9fafb3a75d use tls transport variant with libp2p
Nodes must have an identity cert which is signed by a certificate authority to connect to the p2p network.
2026-02-20 09:58:16 +00:00

199 lines
4.9 KiB
Go

package config
import (
"fmt"
"reflect"
"strings"
"git.clan.lol/clan/data-mesher/pkg/crypto"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/mitchellh/mapstructure"
)
// stringToPublicKeyHookFunc provides a decode hook for mapstructure to convert a string into a crypto.PublicKey
// instance.
// It processes input types to ensure correct conversion only when input is a string and target is crypto.PublicKey.
// Returns the parsed PublicKey instance or the original data if conditions are not met or parsing fails.
func stringToPublicKeyHookFunc() mapstructure.DecodeHookFuncType {
return func(
from reflect.Type, to reflect.Type, data any,
) (any, error) {
if from.Kind() != reflect.String {
return data, nil
}
if to != reflect.TypeFor[crypto.PublicKey]() {
return data, nil
}
str, ok := data.(string)
if !ok || str == "" {
return data, nil
}
return crypto.LoadPublicKey(str)
}
}
// stringToPrivateKeyHookFunc returns a DecodeHookFuncType for converting strings into *crypto.PrivateKey instances.
// The hook checks if the input type is a string and the target type is *crypto.PrivateKey, then attempts to load it.
// Input strings can be file paths or PEM/base64-encoded ED25519 private keys.
// Returns the parsed private key or an error if parsing fails.
func stringToPrivateKeyHookFunc() mapstructure.DecodeHookFuncType {
return func(
from reflect.Type, to reflect.Type, data any,
) (any, error) {
if from.Kind() != reflect.String {
return data, nil
}
if to != reflect.PointerTo(reflect.TypeFor[crypto.PrivateKey]()) {
return data, nil
}
str, ok := data.(string)
if !ok || str == "" {
return data, nil
}
return crypto.LoadPrivateKey(str)
}
}
// stringToCertificateHookFunc returns a DecodeHookFuncType for converting a string (file path or base64/PEM)
// into a *crypto.Certificate.
func stringToCertificateHookFunc() mapstructure.DecodeHookFuncType {
return func(
from reflect.Type, to reflect.Type, data any,
) (any, error) {
if from.Kind() != reflect.String {
return data, nil
}
if to != reflect.PointerTo(reflect.TypeFor[crypto.Certificate]()) {
return data, nil
}
str, ok := data.(string)
if !ok || str == "" {
return data, nil
}
return crypto.LoadCertificate(str)
}
}
// stringToPublicKeyListFunc returns a DecodeHookFuncType for converting a string slice or comma-separated string
// into a slice of PublicKey values or pointers.
func stringToPublicKeyListFunc() mapstructure.DecodeHookFuncType {
return func(
from reflect.Type, to reflect.Type, data any,
) (any, error) {
valueSlice := to == reflect.SliceOf(reflect.TypeFor[crypto.PublicKey]())
ptrSlice := to == reflect.SliceOf(reflect.PointerTo(reflect.TypeFor[crypto.PublicKey]()))
if !valueSlice && !ptrSlice {
return data, nil
}
keyStrings, ok := toStringSlice(from, data)
if !ok {
return data, nil
}
keys, err := loadPublicKeys(keyStrings)
if err != nil {
return nil, err
}
if ptrSlice {
return keys, nil
}
result := make([]crypto.PublicKey, 0, len(keys))
for _, key := range keys {
result = append(result, *key)
}
return result, nil
}
}
// loadPublicKeys parses a slice of key strings into public keys.
func loadPublicKeys(keyStrings []string) ([]*crypto.PublicKey, error) {
result := make([]*crypto.PublicKey, 0, len(keyStrings))
for _, keyStr := range keyStrings {
key, err := crypto.LoadPublicKey(keyStr)
if err != nil {
return nil, fmt.Errorf("failed to decode public key %q: %w", keyStr, err)
}
result = append(result, key)
}
return result, nil
}
// stringToAddrInfoSliceHookFunc returns a DecodeHookFuncType for converting a string slice or comma-separated
// string into a slice of peer.AddrInfo.
func stringToAddrInfoSliceHookFunc() mapstructure.DecodeHookFuncType {
return func(
from reflect.Type, to reflect.Type, data any,
) (any, error) {
if to != reflect.SliceOf(reflect.TypeFor[peer.AddrInfo]()) {
return data, nil
}
strs, ok := toStringSlice(from, data)
if !ok {
return data, nil
}
result := make([]peer.AddrInfo, 0, len(strs))
for _, s := range strs {
ai, err := peer.AddrInfoFromString(s)
if err != nil {
return nil, fmt.Errorf("failed to parse peer address %q: %w", s, err)
}
result = append(result, *ai)
}
return result, nil
}
}
// toStringSlice extracts a []string from either a comma-separated string or a []any slice of strings.
func toStringSlice(from reflect.Type, data any) ([]string, bool) {
switch from.Kind() { //nolint:exhaustive // only String and Slice are relevant
case reflect.String:
str, ok := data.(string)
if !ok || str == "" {
return nil, false
}
return strings.Split(str, ","), true
case reflect.Slice:
slice, ok := data.([]any)
if !ok {
return nil, false
}
result := make([]string, 0, len(slice))
for _, v := range slice {
if s, ok := v.(string); ok {
result = append(result, s)
}
}
return result, true
default:
return nil, false
}
}