watchtower/blob/justice_kit: add BlobType to JusticeKit struct

This is preparation for later commits where the values returned by
member methods will need to be conditioned on the blob type used during
decryption.
This commit is contained in:
Conner Fromknecht 2020-09-15 12:43:37 -04:00
parent b82695dbcc
commit ffe15e2820
No known key found for this signature in database
GPG Key ID: E7D737B67FA592C7
4 changed files with 42 additions and 10 deletions

View File

@ -100,6 +100,15 @@ type PubKey [33]byte
// and for a watchtower to later decrypt if action must be taken. The encoding
// format is versioned to allow future extensions.
type JusticeKit struct {
// BlobType encodes a bitfield that inform the tower of various features
// requested by the client when resolving a breach. Examples include
// whether the justice transaction contains a reward for the tower, or
// whether the channel is a legacy or anchor channel.
//
// NOTE: This value is not serialized in the encrypted payload. It is
// stored separately and added to the JusticeKit after decryption.
BlobType Type
// SweepAddress is the witness program of the output where the client's
// fund will be deposited. This value is included in the blobs, as
// opposed to the session info, such that the sweep addresses can't be
@ -187,17 +196,33 @@ func (b *JusticeKit) HasCommitToRemoteOutput() bool {
}
// CommitToRemoteWitnessScript returns the witness script for the commitment
// to-remote p2wkh output, which is the pubkey itself.
// to-remote output given the blob type. The script returned will either be for
// a p2wpkh to-remote output or an p2wsh anchor to-remote output which includes
// a CSV delay.
func (b *JusticeKit) CommitToRemoteWitnessScript() ([]byte, error) {
if !btcec.IsCompressedPubKey(b.CommitToRemotePubKey[:]) {
return nil, ErrNoCommitToRemoteOutput
}
// If this is a blob for an anchor channel, we'll return the p2wsh
// output containing a CSV delay of 1.
if b.BlobType.IsAnchorChannel() {
pk, err := btcec.ParsePubKey(
b.CommitToRemotePubKey[:], btcec.S256(),
)
if err != nil {
return nil, err
}
return input.CommitScriptToRemoteConfirmed(pk)
}
return b.CommitToRemotePubKey[:], nil
}
// CommitToRemoteWitnessStack returns a witness stack spending the commitment
// to-remote output, which is a regular p2wkh.
// to-remote output, which consists of a single signature satisfying either the
// legacy or anchor witness scripts.
// <to-remote-sig>
func (b *JusticeKit) CommitToRemoteWitnessStack() ([][]byte, error) {
toRemoteSig, err := b.CommitToRemoteSig.ToSignature()
@ -218,11 +243,11 @@ func (b *JusticeKit) CommitToRemoteWitnessStack() ([][]byte, error) {
//
// NOTE: It is the caller's responsibility to ensure that this method is only
// called once for a given (nonce, key) pair.
func (b *JusticeKit) Encrypt(key BreachKey, blobType Type) ([]byte, error) {
func (b *JusticeKit) Encrypt(key BreachKey) ([]byte, error) {
// Encode the plaintext using the provided version, to obtain the
// plaintext bytes.
var ptxtBuf bytes.Buffer
err := b.encode(&ptxtBuf, blobType)
err := b.encode(&ptxtBuf, b.BlobType)
if err != nil {
return nil, err
}
@ -236,7 +261,7 @@ func (b *JusticeKit) Encrypt(key BreachKey, blobType Type) ([]byte, error) {
// Allocate the ciphertext, which will contain the nonce, encrypted
// plaintext and MAC.
plaintext := ptxtBuf.Bytes()
ciphertext := make([]byte, Size(blobType))
ciphertext := make([]byte, Size(b.BlobType))
// Generate a random 24-byte nonce in the ciphertext's prefix.
nonce := ciphertext[:NonceSize]
@ -284,7 +309,9 @@ func Decrypt(key BreachKey, ciphertext []byte,
// If decryption succeeded, we will then decode the plaintext bytes
// using the specified blob version.
boj := &JusticeKit{}
boj := &JusticeKit{
BlobType: blobType,
}
err = boj.decode(bytes.NewReader(plaintext), blobType)
if err != nil {
return nil, err

View File

@ -150,6 +150,7 @@ func TestBlobJusticeKitEncryptDecrypt(t *testing.T) {
func testBlobJusticeKitEncryptDecrypt(t *testing.T, test descriptorTest) {
boj := &blob.JusticeKit{
BlobType: test.encVersion,
SweepAddress: test.sweepAddr,
RevocationPubKey: test.revPubKey,
LocalDelayPubKey: test.delayPubKey,
@ -170,7 +171,7 @@ func testBlobJusticeKitEncryptDecrypt(t *testing.T, test descriptorTest) {
// Encrypt the blob plaintext using the generated key and
// target version for this test.
ctxt, err := boj.Encrypt(key, test.encVersion)
ctxt, err := boj.Encrypt(key)
if err != test.encErr {
t.Fatalf("unable to encrypt blob: %v", err)
} else if test.encErr != nil {

View File

@ -137,7 +137,9 @@ func TestLookoutBreachMatching(t *testing.T) {
}
// Construct a justice kit for each possible breach transaction.
blobType := blob.FlagCommitOutputs.Type()
blob1 := &blob.JusticeKit{
BlobType: blobType,
SweepAddress: makeAddrSlice(22),
RevocationPubKey: makePubKey(1),
LocalDelayPubKey: makePubKey(1),
@ -145,6 +147,7 @@ func TestLookoutBreachMatching(t *testing.T) {
CommitToLocalSig: makeArray64(1),
}
blob2 := &blob.JusticeKit{
BlobType: blobType,
SweepAddress: makeAddrSlice(22),
RevocationPubKey: makePubKey(2),
LocalDelayPubKey: makePubKey(2),
@ -156,13 +159,13 @@ func TestLookoutBreachMatching(t *testing.T) {
key2 := blob.NewBreachKeyFromHash(&hash2)
// Encrypt the first justice kit under breach key one.
encBlob1, err := blob1.Encrypt(key1, blob.FlagCommitOutputs.Type())
encBlob1, err := blob1.Encrypt(key1)
if err != nil {
t.Fatalf("unable to encrypt sweep detail 1: %v", err)
}
// Encrypt the second justice kit under breach key two.
encBlob2, err := blob2.Encrypt(key2, blob.FlagCommitOutputs.Type())
encBlob2, err := blob2.Encrypt(key2)
if err != nil {
t.Fatalf("unable to encrypt sweep detail 2: %v", err)
}

View File

@ -194,6 +194,7 @@ func (t *backupTask) craftSessionPayload(
// to-local script, and the remote CSV delay.
keyRing := t.breachInfo.KeyRing
justiceKit := &blob.JusticeKit{
BlobType: t.blobType,
SweepAddress: t.sweepPkScript,
RevocationPubKey: toBlobPubKey(keyRing.RevocationKey),
LocalDelayPubKey: toBlobPubKey(keyRing.ToLocalKey),
@ -299,7 +300,7 @@ func (t *backupTask) craftSessionPayload(
// Then, we'll encrypt the computed justice kit using the full breach
// transaction id, which will allow the tower to recover the contents
// after the transaction is seen in the chain or mempool.
encBlob, err := justiceKit.Encrypt(key, t.blobType)
encBlob, err := justiceKit.Encrypt(key)
if err != nil {
return hint, nil, err
}