github.com/decred/dcrlnd@v0.7.6/htlcswitch/hop/error_encryptor.go (about) 1 package hop 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 8 "github.com/decred/dcrd/dcrec/secp256k1/v4" 9 "github.com/decred/dcrlnd/lnwire" 10 sphinx "github.com/decred/lightning-onion/v4" 11 ) 12 13 // EncrypterType establishes an enum used in serialization to indicate how to 14 // decode a concrete instance of the ErrorEncrypter interface. 15 type EncrypterType byte 16 17 const ( 18 // EncrypterTypeNone signals that no error encyrpter is present, this 19 // can happen if the htlc is originates in the switch. 20 EncrypterTypeNone EncrypterType = 0 21 22 // EncrypterTypeSphinx is used to identify a sphinx onion error 23 // encrypter instance. 24 EncrypterTypeSphinx = 1 25 26 // EncrypterTypeMock is used to identify a mock obfuscator instance. 27 EncrypterTypeMock = 2 28 ) 29 30 // ErrorEncrypterExtracter defines a function signature that extracts an 31 // ErrorEncrypter from an sphinx OnionPacket. 32 type ErrorEncrypterExtracter func(*secp256k1.PublicKey) (ErrorEncrypter, 33 lnwire.FailCode) 34 35 // ErrorEncrypter is an interface that is used to encrypt HTLC related errors 36 // at the source of the error, and also at each intermediate hop all the way 37 // back to the source of the payment. 38 type ErrorEncrypter interface { 39 // EncryptFirstHop transforms a concrete failure message into an 40 // encrypted opaque failure reason. This method will be used at the 41 // source that the error occurs. It differs from IntermediateEncrypt 42 // slightly, in that it computes a proper MAC over the error. 43 EncryptFirstHop(lnwire.FailureMessage) (lnwire.OpaqueReason, error) 44 45 // EncryptMalformedError is similar to EncryptFirstHop (it adds the 46 // MAC), but it accepts an opaque failure reason rather than a failure 47 // message. This method is used when we receive an 48 // UpdateFailMalformedHTLC from the remote peer and then need to 49 // convert that into a proper error from only the raw bytes. 50 EncryptMalformedError(lnwire.OpaqueReason) lnwire.OpaqueReason 51 52 // IntermediateEncrypt wraps an already encrypted opaque reason error 53 // in an additional layer of onion encryption. This process repeats 54 // until the error arrives at the source of the payment. 55 IntermediateEncrypt(lnwire.OpaqueReason) lnwire.OpaqueReason 56 57 // Type returns an enum indicating the underlying concrete instance 58 // backing this interface. 59 Type() EncrypterType 60 61 // Encode serializes the encrypter's ephemeral public key to the given 62 // io.Writer. 63 Encode(io.Writer) error 64 65 // Decode deserializes the encrypter' ephemeral public key from the 66 // given io.Reader. 67 Decode(io.Reader) error 68 69 // Reextract rederives the encrypter using the extracter, performing an 70 // ECDH with the sphinx router's key and the ephemeral public key. 71 // 72 // NOTE: This should be called shortly after Decode to properly 73 // reinitialize the error encrypter. 74 Reextract(ErrorEncrypterExtracter) error 75 } 76 77 // SphinxErrorEncrypter is a concrete implementation of both the ErrorEncrypter 78 // interface backed by an implementation of the Sphinx packet format. As a 79 // result, all errors handled are themselves wrapped in layers of onion 80 // encryption and must be treated as such accordingly. 81 type SphinxErrorEncrypter struct { 82 *sphinx.OnionErrorEncrypter 83 84 EphemeralKey *secp256k1.PublicKey 85 } 86 87 // NewSphinxErrorEncrypter initializes a blank sphinx error encrypter, that 88 // should be used to deserialize an encoded SphinxErrorEncrypter. Since the 89 // actual encrypter is not stored in plaintext while at rest, reconstructing the 90 // error encrypter requires: 91 // 1. Decode: to deserialize the ephemeral public key. 92 // 2. Reextract: to "unlock" the actual error encrypter using an active 93 // OnionProcessor. 94 func NewSphinxErrorEncrypter() *SphinxErrorEncrypter { 95 return &SphinxErrorEncrypter{ 96 OnionErrorEncrypter: nil, 97 EphemeralKey: &secp256k1.PublicKey{}, 98 } 99 } 100 101 // EncryptFirstHop transforms a concrete failure message into an encrypted 102 // opaque failure reason. This method will be used at the source that the error 103 // occurs. It differs from BackwardObfuscate slightly, in that it computes a 104 // proper MAC over the error. 105 // 106 // NOTE: Part of the ErrorEncrypter interface. 107 func (s *SphinxErrorEncrypter) EncryptFirstHop( 108 failure lnwire.FailureMessage) (lnwire.OpaqueReason, error) { 109 110 var b bytes.Buffer 111 if err := lnwire.EncodeFailure(&b, failure, 0); err != nil { 112 return nil, err 113 } 114 115 // We pass a true as the first parameter to indicate that a MAC should 116 // be added. 117 return s.EncryptError(true, b.Bytes()), nil 118 } 119 120 // EncryptMalformedError is similar to EncryptFirstHop (it adds the MAC), but 121 // it accepts an opaque failure reason rather than a failure message. This 122 // method is used when we receive an UpdateFailMalformedHTLC from the remote 123 // peer and then need to convert that into an proper error from only the raw 124 // bytes. 125 // 126 // NOTE: Part of the ErrorEncrypter interface. 127 func (s *SphinxErrorEncrypter) EncryptMalformedError( 128 reason lnwire.OpaqueReason) lnwire.OpaqueReason { 129 130 return s.EncryptError(true, reason) 131 } 132 133 // IntermediateEncrypt wraps an already encrypted opaque reason error in an 134 // additional layer of onion encryption. This process repeats until the error 135 // arrives at the source of the payment. We re-encrypt the message on the 136 // backwards path to ensure that the error is indistinguishable from any other 137 // error seen. 138 // 139 // NOTE: Part of the ErrorEncrypter interface. 140 func (s *SphinxErrorEncrypter) IntermediateEncrypt( 141 reason lnwire.OpaqueReason) lnwire.OpaqueReason { 142 143 return s.EncryptError(false, reason) 144 } 145 146 // Type returns the identifier for a sphinx error encrypter. 147 func (s *SphinxErrorEncrypter) Type() EncrypterType { 148 return EncrypterTypeSphinx 149 } 150 151 // Encode serializes the error encrypter' ephemeral public key to the provided 152 // io.Writer. 153 func (s *SphinxErrorEncrypter) Encode(w io.Writer) error { 154 ephemeral := s.EphemeralKey.SerializeCompressed() 155 _, err := w.Write(ephemeral) 156 return err 157 } 158 159 // Decode reconstructs the error encrypter's ephemeral public key from the 160 // provided io.Reader. 161 func (s *SphinxErrorEncrypter) Decode(r io.Reader) error { 162 var ephemeral [33]byte 163 if _, err := io.ReadFull(r, ephemeral[:]); err != nil { 164 return err 165 } 166 167 var err error 168 s.EphemeralKey, err = secp256k1.ParsePubKey(ephemeral[:]) 169 if err != nil { 170 return err 171 } 172 173 return nil 174 } 175 176 // Reextract rederives the error encrypter from the currently held EphemeralKey. 177 // This intended to be used shortly after Decode, to fully initialize a 178 // SphinxErrorEncrypter. 179 func (s *SphinxErrorEncrypter) Reextract( 180 extract ErrorEncrypterExtracter) error { 181 182 obfuscator, failcode := extract(s.EphemeralKey) 183 if failcode != lnwire.CodeNone { 184 // This should never happen, since we already validated that 185 // this obfuscator can be extracted when it was received in the 186 // link. 187 return fmt.Errorf("unable to reconstruct onion "+ 188 "obfuscator, got failcode: %d", failcode) 189 } 190 191 sphinxEncrypter, ok := obfuscator.(*SphinxErrorEncrypter) 192 if !ok { 193 return fmt.Errorf("incorrect onion error extracter") 194 } 195 196 // Copy the freshly extracted encrypter. 197 s.OnionErrorEncrypter = sphinxEncrypter.OnionErrorEncrypter 198 199 return nil 200 201 } 202 203 // A compile time check to ensure SphinxErrorEncrypter implements the 204 // ErrorEncrypter interface. 205 var _ ErrorEncrypter = (*SphinxErrorEncrypter)(nil)