| 1 | package libp2p |
| 2 | |
| 3 | import ( |
| 4 | "bytes" |
| 5 | "context" |
| 6 | "fmt" |
| 7 | "time" |
| 8 | |
| 9 | "github.com/ipfs/kubo/repo" |
| 10 | |
| 11 | "github.com/libp2p/go-libp2p" |
| 12 | "github.com/libp2p/go-libp2p/core/host" |
| 13 | "github.com/libp2p/go-libp2p/core/pnet" |
| 14 | "go.uber.org/fx" |
| 15 | "golang.org/x/crypto/salsa20" |
| 16 | "golang.org/x/crypto/sha3" |
| 17 | ) |
| 18 | |
| 19 | type PNetFingerprint []byte |
| 20 | |
| 21 | func PNet(repo repo.Repo) (opts Libp2pOpts, fp PNetFingerprint, err error) { |
| 22 | swarmkey, err := repo.SwarmKey() |
| 23 | if err != nil || swarmkey == nil { |
| 24 | return opts, nil, err |
| 25 | } |
| 26 | |
| 27 | psk, err := pnet.DecodeV1PSK(bytes.NewReader(swarmkey)) |
| 28 | if err != nil { |
| 29 | return opts, nil, fmt.Errorf("failed to configure private network: %s", err) |
| 30 | } |
| 31 | |
| 32 | opts.Opts = append(opts.Opts, libp2p.PrivateNetwork(psk)) |
| 33 | |
| 34 | return opts, pnetFingerprint(psk), nil |
| 35 | } |
| 36 | |
| 37 | func PNetChecker(repo repo.Repo, ph host.Host, lc fx.Lifecycle) error { |
| 38 | // TODO: better check? |
| 39 | swarmkey, err := repo.SwarmKey() |
| 40 | if err != nil || swarmkey == nil { |
| 41 | return err |
| 42 | } |
| 43 | |
| 44 | done := make(chan struct{}) |
| 45 | lc.Append(fx.Hook{ |
| 46 | OnStart: func(_ context.Context) error { |
| 47 | go func() { |
| 48 | t := time.NewTicker(30 * time.Second) |
| 49 | defer t.Stop() |
| 50 | |
| 51 | <-t.C // swallow one tick |
| 52 | for { |
| 53 | select { |
| 54 | case <-t.C: |
| 55 | if len(ph.Network().Peers()) == 0 { |
| 56 | log.Warn("We are in private network and have no peers.") |
| 57 | log.Warn("This might be configuration mistake.") |
| 58 | } |
| 59 | case <-done: |
| 60 | return |
| 61 | } |
| 62 | } |
| 63 | }() |
| 64 | return nil |
| 65 | }, |
| 66 | OnStop: func(_ context.Context) error { |
| 67 | close(done) |
| 68 | return nil |
| 69 | }, |
| 70 | }) |
| 71 | return nil |
| 72 | } |
| 73 | |
| 74 | func pnetFingerprint(psk pnet.PSK) []byte { |
| 75 | var pskArr [32]byte |
| 76 | copy(pskArr[:], psk) |
| 77 | |
| 78 | enc := make([]byte, 64) |
| 79 | zeros := make([]byte, 64) |
| 80 | out := make([]byte, 16) |
| 81 | |
| 82 | // We encrypt data first so we don't feed PSK to hash function. |
| 83 | // Salsa20 function is not reversible thus increasing our security margin. |
| 84 | salsa20.XORKeyStream(enc, zeros, []byte("finprint"), &pskArr) |
| 85 | |
| 86 | // Then do Shake-128 hash to reduce its length. |
| 87 | // This way if for some reason Shake is broken and Salsa20 preimage is possible, |
| 88 | // attacker has only half of the bytes necessary to recreate psk. |
| 89 | sha3.ShakeSum128(out, enc) |
| 90 | |
| 91 | return out |
| 92 | } |