Nodes must have an identity cert which is signed by a certificate authority to connect to the p2p network.
199 lines
4.9 KiB
Go
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
|
|
}
|
|
}
|