watchtower/multi: use new blob.BreachKey for enc/dec of justice kits

This commit is contained in:
Conner Fromknecht 2019-06-13 17:27:17 -07:00
parent 59bf4857f4
commit 3b51906a78
No known key found for this signature in database
GPG Key ID: E7D737B67FA592C7
6 changed files with 27 additions and 37 deletions

@ -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
}

@ -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)
}

@ -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 {

@ -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)
}

@ -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
}

@ -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)
}