diff --git a/sphinx.go b/sphinx.go
index 9b7a97a..c136261 100644
--- a/sphinx.go
+++ b/sphinx.go
@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"crypto/hmac"
 	"crypto/sha256"
+	"errors"
 	"fmt"
 	"io"
 	"math/big"
@@ -784,3 +785,42 @@ func (t *Tx) Commit() ([]ProcessedPacket, *ReplaySet, error) {
 
 	return t.packets, rs, err
 }
+
+// ProcessOnionPacketWithExternalSigner uses an external sharedSecretGenerator
+func (r *Router) ProcessOnionPacketWithExternalSigner(
+	onionPkt *OnionPacket,
+	assocData []byte,
+	incomingCltv uint32,
+	sharedSecretGenerator func(dhKey *btcec.PublicKey) (Hash256, error),
+) (*ProcessedPacket, error) {
+
+	if sharedSecretGenerator == nil {
+		return nil, errors.New("no sharedSecretGenerator")
+	}
+
+	// Compute the shared secret for this onion packet.
+	sharedSecret, err := sharedSecretGenerator(onionPkt.EphemeralKey)
+	if err != nil {
+		return nil, err
+	}
+
+	// Additionally, compute the hash prefix of the shared secret, which
+	// will serve as an identifier for detecting replayed packets.
+	hashPrefix := hashSharedSecret(&sharedSecret)
+
+	// Continue to optimistically process this packet, deferring replay
+	// protection until the end to reduce the penalty of multiple IO
+	// operations.
+	packet, err := processOnionPacket(onionPkt, &sharedSecret, assocData, r)
+	if err != nil {
+		return nil, err
+	}
+
+	// Atomically compare this hash prefix with the contents of the on-disk
+	// log, persisting it only if this entry was not detected as a replay.
+	if err := r.log.Put(hashPrefix, incomingCltv); err != nil {
+		return nil, err
+	}
+
+	return packet, nil
+}