Skip to content

Commit

Permalink
Merge pull request #28 from ipfs/key-derivation
Browse files Browse the repository at this point in the history
Feat: key derivation for libp2p identities
  • Loading branch information
hsanjuan authored Oct 18, 2023
2 parents 520be23 + 0c5c370 commit e06e453
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 12 deletions.
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ require (
github.com/libp2p/go-libp2p-record v0.2.0
github.com/libp2p/go-libp2p-routing-helpers v0.7.3
github.com/mitchellh/go-server-timing v1.0.1
github.com/mr-tron/base58 v1.2.0
github.com/multiformats/go-multiaddr v0.11.0
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58
github.com/prometheus/client_golang v1.16.0
Expand All @@ -35,6 +36,7 @@ require (
go.opentelemetry.io/otel v1.16.0
go.opentelemetry.io/otel/sdk v1.16.0
go.opentelemetry.io/otel/trace v1.16.0
golang.org/x/crypto v0.12.0
golang.org/x/sys v0.11.0
)

Expand Down Expand Up @@ -119,7 +121,6 @@ require (
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/multiformats/go-base32 v0.1.0 // indirect
github.com/multiformats/go-base36 v0.2.0 // indirect
github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect
Expand Down Expand Up @@ -172,7 +173,6 @@ require (
go.uber.org/fx v1.20.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.25.0 // indirect
golang.org/x/crypto v0.12.0 // indirect
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.14.0 // indirect
Expand Down
45 changes: 45 additions & 0 deletions keys.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package main

import (
"crypto/ed25519"
crand "crypto/rand"
"crypto/sha256"
"errors"
"io"

libp2p "github.com/libp2p/go-libp2p/core/crypto"
"github.com/mr-tron/base58"
"golang.org/x/crypto/hkdf"
)

const seedBytes = 32

// newSeed returns a b58 encoded random seed.
func newSeed() (string, error) {
bs := make([]byte, seedBytes)
_, err := io.ReadFull(crand.Reader, bs)
if err != nil {
return "", err
}
return base58.Encode(bs), nil
}

// derive derives libp2p keys from a b58-encoded seed.
func deriveKey(b58secret string, info []byte) (libp2p.PrivKey, error) {
secret, err := base58.Decode(b58secret)
if err != nil {
return nil, err
}
if len(secret) < seedBytes {
return nil, errors.New("derivation seed is too short")
}

hash := sha256.New
hkdf := hkdf.New(hash, secret, nil, info)
keySeed := make([]byte, ed25519.SeedSize)
if _, err := io.ReadFull(hkdf, keySeed); err != nil {
return nil, err
}
key := ed25519.NewKeyFromSeed(keySeed)
return libp2p.UnmarshalEd25519PrivateKey(key)
}
59 changes: 57 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import (
"time"

logging "github.com/ipfs/go-log/v2"
"github.com/libp2p/go-libp2p/core/crypto"
peer "github.com/libp2p/go-libp2p/core/peer"
"github.com/urfave/cli/v2"
"go.opentelemetry.io/contrib/propagators/autoprop"
"go.opentelemetry.io/otel"
Expand All @@ -35,6 +37,18 @@ func main() {
Value: "",
Usage: "specify the directory that cache data will be stored",
},
&cli.StringFlag{
Name: "seed",
Value: "",
EnvVars: []string{"RAINBOW_SEED"},
Usage: "Specify a seed to derive peerID from (needs --seed-index)",
},
&cli.IntFlag{
Name: "seed-index",
Value: -1,
EnvVars: []string{"RAINBOW_SEED_INDEX"},
Usage: "Specify an index to derivate the peerID from the key (needs --seed)",
},
&cli.IntFlag{
Name: "gateway-port",
Value: 8090,
Expand Down Expand Up @@ -93,6 +107,27 @@ func main() {
},
}

app.Commands = []*cli.Command{
{
Name: "gen-seed",
Usage: "Generate a seed for key derivation",
Description: `
Running this command will generate a random seed and print it. The value can
be used with the RAINBOW_SEED env-var to use key-derivation from a single seed
to create libp2p identities for the gateway.
`,
Flags: []cli.Flag{},
Action: func(c *cli.Context) error {
seed, err := newSeed()
if err != nil {
return err
}
fmt.Println(seed)
return nil
},
},
}

app.Name = "rainbow"
app.Usage = "a standalone ipfs gateway"
app.Version = version
Expand All @@ -101,6 +136,21 @@ func main() {
cdns := newCachedDNS(dnsCacheRefreshInterval)
defer cdns.Close()

var priv crypto.PrivKey
var err error
seed := cctx.String("seed")

index := cctx.Int("seed-index")
if len(seed) > 0 && index >= 0 {
priv, err = deriveKey(seed, []byte(fmt.Sprintf("rainbow-%d", index)))
} else {
keyFile := filepath.Join(ddir, "libp2p.key")
priv, err = loadOrInitPeerKey(keyFile)
}
if err != nil {
return err
}

gnd, err := Setup(cctx.Context, Config{
DataDir: ddir,
ConnMgrLow: cctx.Int("connmgr-low"),
Expand All @@ -109,7 +159,7 @@ func main() {
MaxMemory: cctx.Uint64("max-memory"),
MaxFD: cctx.Int("max-fd"),
InMemBlockCache: cctx.Int64("inmem-block-cache"),
Libp2pKeyFile: filepath.Join(ddir, "libp2p.key"),
Libp2pKey: priv,
RoutingV1: cctx.String("routing"),
KuboRPCURLs: getEnvs(EnvKuboRPC, DefaultKuboRPC),
DHTSharedHost: cctx.Bool("dht-fallback-shared-host"),
Expand All @@ -133,7 +183,12 @@ func main() {
Handler: handler,
}

fmt.Printf("Starting %s %s\n\n", name, version)
fmt.Printf("Starting %s %s\n", name, version)
pid, err := peer.IDFromPublicKey(priv.GetPublic())
if err != nil {
return err
}
fmt.Printf("PeerID: %s\n\n", pid)
registerVersionMetric(version)

tp, shutdown, err := newTracerProvider(cctx.Context)
Expand Down
13 changes: 5 additions & 8 deletions setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package main
import (
"context"
crand "crypto/rand"
"errors"
"fmt"
"io/fs"
"net/http"
"os"
"path/filepath"
Expand Down Expand Up @@ -83,7 +85,7 @@ type Config struct {
ListenAddrs []string
AnnounceAddrs []string

Libp2pKeyFile string
Libp2pKey crypto.PrivKey

ConnMgrLow int
ConnMgrHi int
Expand All @@ -102,11 +104,6 @@ type Config struct {
}

func Setup(ctx context.Context, cfg Config) (*Node, error) {
peerkey, err := loadOrInitPeerKey(cfg.Libp2pKeyFile)
if err != nil {
return nil, err
}

ds, err := setupDatastore(cfg)
if err != nil {
return nil, err
Expand All @@ -129,7 +126,7 @@ func Setup(ctx context.Context, cfg Config) (*Node, error) {
libp2p.ListenAddrStrings(cfg.ListenAddrs...),
libp2p.NATPortMap(),
libp2p.ConnectionManager(cmgr),
libp2p.Identity(peerkey),
libp2p.Identity(cfg.Libp2pKey),
libp2p.BandwidthReporter(bwc),
libp2p.DefaultTransports,
libp2p.DefaultMuxers,
Expand Down Expand Up @@ -296,7 +293,7 @@ func Setup(ctx context.Context, cfg Config) (*Node, error) {
bn.Start(bswap)

err = os.Mkdir("denylists", 0755)
if err != nil {
if err != nil && !errors.Is(err, fs.ErrExist) {
return nil, err
}

Expand Down

0 comments on commit e06e453

Please sign in to comment.