From 3b51906a787a4117a151945620c85908e43bda00 Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Thu, 13 Jun 2019 17:27:17 -0700 Subject: [PATCH] watchtower/multi: use new blob.BreachKey for enc/dec of justice kits --- watchtower/blob/justice_kit.go | 26 +++++-------------- watchtower/blob/justice_kit_test.go | 4 +-- watchtower/lookout/lookout.go | 7 +++-- watchtower/lookout/lookout_test.go | 11 +++++--- watchtower/wtclient/backup_task.go | 13 ++++------ .../wtclient/backup_task_internal_test.go | 3 ++- 6 files changed, 27 insertions(+), 37 deletions(-) diff --git a/watchtower/blob/justice_kit.go b/watchtower/blob/justice_kit.go index 46abaa00..dd0213fb 100644 --- a/watchtower/blob/justice_kit.go +++ b/watchtower/blob/justice_kit.go @@ -75,11 +75,6 @@ var ( "ciphertext is too small for chacha20poly1305", ) - // ErrKeySize signals that the provided key is improperly sized. - ErrKeySize = fmt.Errorf( - "chacha20poly1305 key size must be %d bytes", KeySize, - ) - // ErrNoCommitToRemoteOutput is returned when trying to retrieve the // commit to-remote output from the blob, though none exists. ErrNoCommitToRemoteOutput = errors.New( @@ -223,12 +218,7 @@ 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 []byte, blobType Type) ([]byte, error) { - // Fail if the nonce is not 32-bytes. - if len(key) != KeySize { - return nil, ErrKeySize - } - +func (b *JusticeKit) Encrypt(key BreachKey, blobType Type) ([]byte, error) { // Encode the plaintext using the provided version, to obtain the // plaintext bytes. var ptxtBuf bytes.Buffer @@ -238,7 +228,7 @@ func (b *JusticeKit) Encrypt(key []byte, blobType Type) ([]byte, error) { } // Create a new chacha20poly1305 cipher, using a 32-byte key. - cipher, err := chacha20poly1305.NewX(key) + cipher, err := chacha20poly1305.NewX(key[:]) if err != nil { return nil, err } @@ -264,21 +254,17 @@ func (b *JusticeKit) Encrypt(key []byte, blobType Type) ([]byte, error) { // Decrypt unenciphers a blob of justice by decrypting the ciphertext using // chacha20poly1305 with the chosen (nonce, key) pair. The internal plaintext is // then deserialized using the given encoding version. -func Decrypt(key, ciphertext []byte, blobType Type) (*JusticeKit, error) { - switch { +func Decrypt(key BreachKey, ciphertext []byte, + blobType Type) (*JusticeKit, error) { // Fail if the blob's overall length is less than required for the nonce // and expansion factor. - case len(ciphertext) < NonceSize+CiphertextExpansion: + if len(ciphertext) < NonceSize+CiphertextExpansion { return nil, ErrCiphertextTooSmall - - // Fail if the key is not 32-bytes. - case len(key) != KeySize: - return nil, ErrKeySize } // Create a new chacha20poly1305 cipher, using a 32-byte key. - cipher, err := chacha20poly1305.NewX(key) + cipher, err := chacha20poly1305.NewX(key[:]) if err != nil { return nil, err } diff --git a/watchtower/blob/justice_kit_test.go b/watchtower/blob/justice_kit_test.go index 977289e0..13ee4117 100644 --- a/watchtower/blob/justice_kit_test.go +++ b/watchtower/blob/justice_kit_test.go @@ -165,8 +165,8 @@ func testBlobJusticeKitEncryptDecrypt(t *testing.T, test descriptorTest) { // Generate a random encryption key for the blob. The key is // sized at 32 byte, as in practice we will be using the remote // party's commitment txid as the key. - key := make([]byte, blob.KeySize) - _, err := io.ReadFull(rand.Reader, key) + var key blob.BreachKey + _, err := rand.Read(key[:]) if err != nil { t.Fatalf("unable to generate blob encryption key: %v", err) } diff --git a/watchtower/lookout/lookout.go b/watchtower/lookout/lookout.go index 6ab41b28..fc7badde 100644 --- a/watchtower/lookout/lookout.go +++ b/watchtower/lookout/lookout.go @@ -202,13 +202,16 @@ func (l *Lookout) processEpoch(epoch *chainntnfs.BlockEpoch, // The decryption key for the state update should be the full // txid of the breaching commitment transaction. - commitTxID := commitTx.TxHash() + // The decryption key for the state update should be computed as + // key = SHA256(txid). + breachTxID := commitTx.TxHash() + breachKey := blob.NewBreachKeyFromHash(&breachTxID) // Now, decrypt the blob of justice that we received in the // state update. This will contain all information required to // sweep the breached commitment outputs. justiceKit, err := blob.Decrypt( - commitTxID[:], match.EncryptedBlob, + breachKey, match.EncryptedBlob, match.SessionInfo.Policy.BlobType, ) if err != nil { diff --git a/watchtower/lookout/lookout_test.go b/watchtower/lookout/lookout_test.go index 66a485ca..ba62a015 100644 --- a/watchtower/lookout/lookout_test.go +++ b/watchtower/lookout/lookout_test.go @@ -148,14 +148,17 @@ func TestLookoutBreachMatching(t *testing.T) { CommitToLocalSig: makeArray64(2), } - // Encrypt the first justice kit under the txid of the first txn. - encBlob1, err := blob1.Encrypt(hash1[:], blob.FlagCommitOutputs.Type()) + key1 := blob.NewBreachKeyFromHash(&hash1) + key2 := blob.NewBreachKeyFromHash(&hash2) + + // Encrypt the first justice kit under breach key one. + encBlob1, err := blob1.Encrypt(key1, blob.FlagCommitOutputs.Type()) if err != nil { t.Fatalf("unable to encrypt sweep detail 1: %v", err) } - // Encrypt the second justice kit under the txid of the second txn. - encBlob2, err := blob2.Encrypt(hash2[:], blob.FlagCommitOutputs.Type()) + // Encrypt the second justice kit under breach key two. + encBlob2, err := blob2.Encrypt(key2, blob.FlagCommitOutputs.Type()) if err != nil { t.Fatalf("unable to encrypt sweep detail 2: %v", err) } diff --git a/watchtower/wtclient/backup_task.go b/watchtower/wtclient/backup_task.go index b166428a..9f284b98 100644 --- a/watchtower/wtclient/backup_task.go +++ b/watchtower/wtclient/backup_task.go @@ -276,22 +276,19 @@ func (t *backupTask) craftSessionPayload( } } - // Compute the breach hint from the breach transaction id's prefix. - breachKey := t.breachInfo.BreachTransaction.TxHash() + breachTxID := t.breachInfo.BreachTransaction.TxHash() + + // Compute the breach key as SHA256(txid). + hint, key := blob.NewBreachHintAndKeyFromHash(&breachTxID) // 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(breachKey[:], t.blobType) + encBlob, err := justiceKit.Encrypt(key, t.blobType) if err != nil { return hint, nil, err } - // Finally, compute the breach hint, taken as the first half of the - // breach transactions txid. Once the tower sees the breach transaction - // on the network, it can use the full txid to decyrpt the blob. - hint = blob.NewBreachHintFromHash(&breachKey) - return hint, encBlob, nil } diff --git a/watchtower/wtclient/backup_task_internal_test.go b/watchtower/wtclient/backup_task_internal_test.go index 807a7260..77fc3fbd 100644 --- a/watchtower/wtclient/backup_task_internal_test.go +++ b/watchtower/wtclient/backup_task_internal_test.go @@ -524,7 +524,8 @@ func testBackupTask(t *testing.T, test backupTaskTest) { // Decrypt the return blob to obtain the JusticeKit containing its // contents. - jKit, err := blob.Decrypt(breachTxID[:], encBlob, policy.BlobType) + key := blob.NewBreachKeyFromHash(&breachTxID) + jKit, err := blob.Decrypt(key, encBlob, policy.BlobType) if err != nil { t.Fatalf("unable to decrypt blob: %v", err) }