diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/crypto/Mac.scala b/eclair-core/src/main/scala/fr/acinq/eclair/crypto/Mac.scala index cc95240791..ac96a916c0 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/crypto/Mac.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/crypto/Mac.scala @@ -41,7 +41,7 @@ case class Hmac256(key: ByteVector) extends Mac32 { override def mac(message: ByteVector): ByteVector32 = Mac32.hmac256(key, message) - override def verify(mac: ByteVector32, message: ByteVector): Boolean = this.mac(message) === mac + override def verify(mac: ByteVector32, message: ByteVector): Boolean = this.mac(message) == mac } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/crypto/Sphinx.scala b/eclair-core/src/main/scala/fr/acinq/eclair/crypto/Sphinx.scala index e4afbaf99c..bb50bdd555 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/crypto/Sphinx.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/crypto/Sphinx.scala @@ -18,7 +18,6 @@ package fr.acinq.eclair.crypto import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey} import fr.acinq.bitcoin.scalacompat.{ByteVector32, Crypto} -import fr.acinq.eclair.crypto.Monitoring.{Metrics, Tags} import fr.acinq.eclair.wire.protocol._ import grizzled.slf4j.Logging import scodec.Attempt @@ -272,9 +271,6 @@ object Sphinx extends Logging { object FailurePacket { - val MaxPayloadLength = 256 - val PacketLength = MacLength + MaxPayloadLength + 2 + 2 - /** * Create a failure packet that will be returned to the sender. * Each intermediate hop will add a layer of encryption and forward to the previous hop. @@ -301,16 +297,11 @@ object Sphinx extends Logging { * @return an encrypted failure packet that can be sent to the destination node. */ def wrap(packet: ByteVector, sharedSecret: ByteVector32): ByteVector = { - if (packet.length != PacketLength) { - logger.warn(s"invalid error packet length ${packet.length}, must be $PacketLength (malicious or buggy downstream node)") - } val key = generateKey("ammag", sharedSecret) - val stream = generateStream(key, PacketLength) + val stream = generateStream(key, packet.length.toInt) logger.debug(s"ammag key: $key") logger.debug(s"error stream: $stream") - // If we received a packet with an invalid length, we trim and pad to forward a packet with a normal length upstream. - // This is a poor man's attempt at increasing the likelihood of the sender receiving the error. - packet.take(PacketLength).padLeft(PacketLength) xor stream + packet xor stream } /** @@ -324,8 +315,6 @@ object Sphinx extends Logging { * decrypted, Failure otherwise. */ def decrypt(packet: ByteVector, sharedSecrets: Seq[(ByteVector32, PublicKey)]): Try[DecryptedFailurePacket] = Try { - require(packet.length == PacketLength, s"invalid error packet length ${packet.length}, must be $PacketLength") - @tailrec def loop(packet: ByteVector, secrets: Seq[(ByteVector32, PublicKey)]): DecryptedFailurePacket = secrets match { case Nil => throw new RuntimeException(s"couldn't parse error packet=$packet with sharedSecrets=$sharedSecrets") @@ -381,7 +370,7 @@ object Sphinx extends Logging { } /** - * @param route blinded route. + * @param route blinded route. * @param lastBlinding blinding point for the last node, which can be used to derive the blinded private key. */ case class BlindedRouteDetails(route: BlindedRoute, lastBlinding: PublicKey) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/FailureMessage.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/FailureMessage.scala index e9bbb5cff2..12aea7e013 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/FailureMessage.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/FailureMessage.scala @@ -22,6 +22,7 @@ import fr.acinq.eclair.wire.protocol.CommonCodecs._ import fr.acinq.eclair.wire.protocol.FailureMessageCodecs.failureMessageCodec import fr.acinq.eclair.wire.protocol.LightningMessageCodecs.{channelFlagsCodec, channelUpdateCodec, meteredLightningMessageCodec} import fr.acinq.eclair.{BlockHeight, CltvExpiry, MilliSatoshi, MilliSatoshiLong, UInt64} +import scodec.bits.ByteVector import scodec.codecs._ import scodec.{Attempt, Codec} @@ -139,19 +140,27 @@ object FailureMessageCodecs { }, (_: FailureMessage).code) ) + private def failureOnionPayload(payloadAndPadLength: Int): Codec[FailureMessage] = Codec( + encoder = f => variableSizeBytes(uint16, failureMessageCodec).encode(f).flatMap(bits => { + val payloadLength = bits.bytes.length - 2 + val padLen = payloadAndPadLength - payloadLength + variableSizeBytes(uint16, bytes).encode(ByteVector.fill(padLen)(0)).map(pad => bits ++ pad) + }), + // We accept payloads of any size in case more space is needed for future failure messages. + decoder = bits => ( + ("failure" | variableSizeBytes(uint16, failureMessageCodec)) + :: ("padding" | variableSizeBytes(uint16, bytes)) + ).map(_.head).decode(bits) + ) + /** * An onion-encrypted failure from an intermediate node: - * {{{ * +----------------+----------------------------------+-----------------+----------------------+-----+ * | HMAC(32 bytes) | failure message length (2 bytes) | failure message | pad length (2 bytes) | pad | * +----------------+----------------------------------+-----------------+----------------------+-----+ - * }}} - * with failure message length + pad length = 256 + * Bolt 4: SHOULD set pad such that the failure_len plus pad_len is equal to 256: by always using the same size we + * ensure error messages are indistinguishable. */ - def failureOnionCodec(mac: Mac32): Codec[FailureMessage] = CommonCodecs.prependmac( - paddedFixedSizeBytesDependent( - 260, - "failureMessage" | variableSizeBytes(uint16, FailureMessageCodecs.failureMessageCodec), - nBits => "padding" | variableSizeBytes(uint16, ignore(nBits - 2 * 8)) // two bytes are used to encode the padding length - ).as[FailureMessage], mac) + def failureOnionCodec(mac: Mac32, payloadAndPadLength: Int = 256): Codec[FailureMessage] = CommonCodecs.prependmac(failureOnionPayload(payloadAndPadLength).complete, mac) + } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalStateSpec.scala index 9b3af76469..8131ba92b4 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalStateSpec.scala @@ -1925,16 +1925,16 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with alice2blockchain.expectMsgType[WatchTxConfirmed] } - test("recv UpdateFailHtlc (invalid onion error length)") { f => + test("recv UpdateFailHtlc (onion error bigger than recommended value)") { f => import f._ val (_, htlc) = addHtlc(50000000 msat, alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) // Bob receives a failure with a completely invalid onion error (missing mac) - bob ! CMD_FAIL_HTLC(htlc.id, Left(ByteVector.fill(260)(42))) + bob ! CMD_FAIL_HTLC(htlc.id, Left(ByteVector.fill(561)(42))) val fail = bob2alice.expectMsgType[UpdateFailHtlc] assert(fail.id == htlc.id) - // We should rectify the packet length before forwarding upstream. - assert(fail.reason.length == Sphinx.FailurePacket.PacketLength) + // We propagate failure upstream (hopefully the sender knows how to unwrap them). + assert(fail.reason.length == 561) } private def testCmdUpdateFee(f: FixtureParam): Unit = { diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/crypto/SphinxSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/crypto/SphinxSpec.scala index 1af09e8c68..cffd37fd7d 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/crypto/SphinxSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/crypto/SphinxSpec.scala @@ -227,19 +227,19 @@ class SphinxSpec extends AnyFunSuite { val expected = DecryptedFailurePacket(publicKeys.head, InvalidOnionKey(ByteVector32.One)) val packet1 = FailurePacket.create(sharedSecrets.head, expected.failureMessage) - assert(packet1.length == FailurePacket.PacketLength) + assert(packet1.length == 292) val Success(decrypted1) = FailurePacket.decrypt(packet1, Seq(0).map(i => (sharedSecrets(i), publicKeys(i)))) assert(decrypted1 == expected) val packet2 = FailurePacket.wrap(packet1, sharedSecrets(1)) - assert(packet2.length == FailurePacket.PacketLength) + assert(packet2.length == 292) val Success(decrypted2) = FailurePacket.decrypt(packet2, Seq(1, 0).map(i => (sharedSecrets(i), publicKeys(i)))) assert(decrypted2 == expected) val packet3 = FailurePacket.wrap(packet2, sharedSecrets(2)) - assert(packet3.length == FailurePacket.PacketLength) + assert(packet3.length == 292) val Success(decrypted3) = FailurePacket.decrypt(packet3, Seq(2, 1, 0).map(i => (sharedSecrets(i), publicKeys(i)))) assert(decrypted3 == expected) @@ -307,18 +307,31 @@ class SphinxSpec extends AnyFunSuite { } } - test("intermediate node replies with an invalid onion payload length") { - // The error will not be recoverable by the sender, but we must still forward it. - val sharedSecret = ByteVector32(hex"4242424242424242424242424242424242424242424242424242424242424242") - val errors = Seq( - ByteVector.fill(FailurePacket.PacketLength - MacLength)(13), - ByteVector.fill(FailurePacket.PacketLength + MacLength)(13) - ) - - for (error <- errors) { - val wrapped = FailurePacket.wrap(error, sharedSecret) - assert(wrapped.length == FailurePacket.PacketLength) - } + test("last node replies with a failure message (arbitrary length)") { + val Success(PacketAndSecrets(packet, sharedSecrets)) = create(sessionKey, 1300, publicKeys, referencePaymentPayloads, associatedData) + val Right(DecryptedPacket(_, packet1, sharedSecret0)) = peel(privKeys(0), associatedData, packet) + val Right(DecryptedPacket(_, packet2, sharedSecret1)) = peel(privKeys(1), associatedData, packet1) + val Right(DecryptedPacket(_, packet3, sharedSecret2)) = peel(privKeys(2), associatedData, packet2) + val Right(DecryptedPacket(_, packet4, sharedSecret3)) = peel(privKeys(3), associatedData, packet3) + val Right(lastPacket@DecryptedPacket(_, _, sharedSecret4)) = peel(privKeys(4), associatedData, packet4) + assert(lastPacket.isLastPacket) + + // node #4 want to reply with an error message using a custom length + val error = createCustomLengthFailurePacket(TemporaryNodeFailure, sharedSecret4, 1024) + assert(error == hex"4ca0784803691f89f7558ff4560ba55aa6b94486e5c5cf1d0922750ad01e185ba8cb9168b60f2fd45edd73c1b0c8b33002df376801ff58aaa94000bf8a86f92620f343baef38a580102395ae3abf9128d1047a0736ff9b83d456740ebbb4aeb3aa9737f18fb4afb4aa074fb26c4d702f42968888550a3bded8c05247e045b866baef0499f079fdaeef6538f31d44deafffdfd3afa2fb4ca9082b8f1c465371a9894dd8c243fb4847e004f5256b3e90e2edde4c9fb3082ddfe4d1e734cacd96ef0706bf63c9984e22dc98851bcccd1c3494351feb458c9c6af41c0044bea3c47552b1d992ae542b17a2d0bba1a096c78d169034ecb55b6e3a7263c26017f033031228833c1daefc0dedb8cf7c3e37c9c37ebfe42f3225c326e8bcfd338804c145b16e34e4f5984bc119af09d471a61f39e9e389c4120cadabc5d9b7b1355a8ccef050ca8ad72f642fc26919927b347808bade4b1c321b08bc363f20745ba2f97f0ced2996a232f55ba28fe7dfa70a9ab0433a085388f25cce8d53de6a2fbd7546377d6ede9027ad173ba1f95767461a3689ef405ab608a21086165c64b02c1782b04a6dba2361a7784603069124e12f2f6dcb1ec7612a4fbf94c0e14631a2bef6190c3d5f35e0c4b32aa85201f449d830fd8f782ec758b0910428e3ec3ca1dba3b6c7d89f69e1ee1b9df3dfbbf6d361e1463886b38d52e8f43b73a3bd48c6f36f5897f514b93364a31d49d1d506340b1315883d425cb36f4ea553430d538fd6f3596d4afc518db2f317dd051abc0d4bfb0a7870c3db70f19fe78d6604bbf088fcb4613f54e67b038277fedcd9680eb97bdffc3be1ab2cbcbafd625b8a7ac34d8c190f98d3064ecd3b95b8895157c6a37f31ef4de094b2cb9dbf8ff1f419ba0ecacb1bb13df0253b826bec2ccca1e745dd3b3e7cc6277ce284d649e7b8285727735ff4ef6cca6c18e2714f4e2a1ac67b25213d3bb49763b3b94e7ebf72507b71fb2fe0329666477ee7cb7ebd6b88ad5add8b217188b1ca0fa13de1ec09cc674346875105be6e0e0d6c8928eb0df23c39a639e04e4aedf535c4e093f08b2c905a14f25c0c0fe47a5a1535ab9eae0d9d67bdd79de13a08d59ee05385c7ea4af1ad3248e61dd22f8990e9e99897d653dd7b1b1433a6d464ea9f74e377f2d8ce99ba7dbc753297644234d25ecb5bd528e2e2082824681299ac30c05354baaa9c3967d86d7c07736f87fc0f63e5036d47235d7ae12178ced3ae36ee5919c093a02579e4fc9edad2c446c656c790704bfc8e2c491a42500aa1d75c8d4921ce29b753f883e17c79b09ea324f1f32ddf1f3284cd70e847b09d90f6718c42e5c94484cc9cbb0df659d255630a3f5a27e7d5dd14fa6b974d1719aa98f01a20fb4b7b1c77b42d57fab3c724339d459ee4a1c6b5d3bd4e08624c786a257872acc9ad3ff62222f2265a658d9f2a007229a5293b67ec91c84c4b4407c228434bad8a815ca9b256c776bd2c9f") + val error1 = FailurePacket.wrap(error, sharedSecret3) + assert(error1 == hex"2ddcd9ac6122dc79b8b96c5e0c20c40bb64d656a8785420bcdacd3cb67dd27bca081bab1626a0011585323930fa5b9fae0c85770a2279ff59ec427ad1bbff9001c0cd1497004bd2a0f68b50704cf6d6a4bf3c8b6a0833399a24b3456961ba00736785112594f65b6b2d44d9f5ea4e49b5e1ec2af978cbe31c67114440ac51a62081df0ed46d4a3df295da0b0fe25c0115019f03f15ec86fabb4c852f83449e812f141a9395b3f70b766ebbd4ec2fae2b6955bd8f32684c15abfe8fd3a6261e52650e8807a92158d9f1463261a925e4bfba44bd20b166d532f0017185c3a6ac7957adefe45559e3072c8dc35abeba835a8cb01a71a15c736911126f27d46a36168ca5ef7dccd4e2886212602b181463e0dd30185c96348f9743a02aca8ec27c0b90dca2700c1b46d3f10242ceb286acec56576cf0e22042426c5a61d80c0298dc5ce158f46e11eaf8f32cd44d5f1213d4738768f081978420697b454700ade1c093c02a6ca0e78a7e2f3d9e5c7e49e20c3a56b624bfea51196ec9e88e4e56be38ff56031369f45f1e03be826d44a182f270c153ee0d9f8cf9f1f4132f33974e37c7887d5b857365c873cb218cbf20d4be3abdb2a2011b14add0a5672e01e5845421cf6dd6faca1f2f443757aae575c53ab797c2227ecdab03882bbbf4599318cefafa72fa0c9a0f5a51d13c9d0e5d25bfcfb0154ed25895260a9df8743ac188714a3f16960e6e2ff663c08bffda41743d50960ea2f28cda0bc3bd4a180e297b5b41c700b674cb31d99c7f2a1445e121e772984abff2bbe3f42d757ceeda3d03fb1ffe710aecabda21d738b1f4620e757e57b123dbc3c4aa5d9617dfa72f4a12d788ca596af14bea583f502f16fdc13a5e739afb0715424af2767049f6b9aa107f69c5da0e85f6d8c5e46507e14616d5d0b797c3dea8b74a1b12d4e47ba7f57f09d515f6c7314543f78b5e85329d50c5f96ee2f55bbe0df742b4003b24ccbd4598a64413ee4807dc7f2a9c0b92424e4ae1b418a3cdf02ea4da5c3b12139348aa7022cc8272a3a1714ee3e4ae111cffd1bdfd62c503c80bdf27b2feaea0d5ab8fe00f9cec66e570b00fd24b4a2ed9a5f6384f148a4d6325110a41ca5659ebc5b98721d298a52819b6fb150f273383f1c5754d320be428941922da790e17f482989c365c078f7f3ae100965e1b38c052041165295157e1a7c5b7a57671b842d4d85a7d971323ad1f45e17a16c4656d889fc75c12fc3d8033f598306196e29571e414281c5da19c12605f48347ad5b4648e371757cbe1c40adb93052af1d6110cfbf611af5c8fc682b7e2ade3bfca8b5c7717d19fc9f97964ba6025aebbc91a6671e259949dcf40984342118de1f6b514a7786bd4f6598ffbe1604cef476b2a4cb1343db608aca09d1d38fc23e98ee9c65e7f6023a8d1e61fd4f34f753454bd8e858c8ad6be6403edc599c220e03ca917db765980ac781e758179cd93983e9c1e769e4241d47c") + val error2 = FailurePacket.wrap(error1, sharedSecret2) + assert(error2 == hex"4c952d273b5c5344d7e4eb5576494a2bfabb21382d310a443c7235ba99bedaf7ff9ac25f422ddfb7466e74f81b3e545563cdd8f5524dae873de61d7bdfccd496af2584930d2b566b4f8d3881f8c043df92224f38cf094cfc09d92655989531524593ec6d6caec1863bdfaa79229b5020acc034cd6deeea1021c50586947b9b8e6faa83b81fbfa6133c0af5d6b07c017f7158fa94f0d206baf12dda6b68f785b773b360fd0497e16cc402d779c8d48d0fa6315536ef0660f3f4e1865f5b38ea49c7da4fd959de4e83ff3ab686f059a45c65ba2af4a6a79166aa0f496bf04d06987b6d2ea205bdb0d347718b9aeff5b61dfff344993a275b79717cd815b6ad4c0beb568c4ac9c36ff1c315ec1119a1993c4b61e6eaa0375e0aaf738ac691abd3263bf937e310be4b517177c9d27b9d0e30158cd0cd739f6782e71ca334e378aa129aac1395802b8866064f7bad07a50da5cf31f8c3151c4c52e525fb22ecf48f8fa39bb5adf932b50c12c10be90174b37d454a3f8b284c849e86578a6182c4a7b2e47dd57d44730a1be9fec4ad07287a397e28dce4fda57e9cdfdb2eb5afdf0d38ef19d982341d18d07a556bb16c1416f480a396f278373b8fd9897023a4ac506e65cf4c306377730f9c8ca63cf47565240b59c4861e52f1dab84d938e96fb31820064d534aca05fd3d2600834fe4caea98f2a748eb8f200af77bd9fbf46141952b9ddda66ef0ebea17ea1e7bb5bce65b6e71554c56dd0d4e14f4cf74c77a150776bf31e7419756c71e7421dc22efe9cf01de9e19fc8808d5b525431b944400db121a77994518d6025711cb25a18774068bba7faaa16d8f65c91bec8768848333156dcb4a08dfbbd9fef392da3e4de13d4d74e83a7d6e46cfe530ee7a6f711e2caf8ad5461ba8177b2ef0a518baf9058ff9156e6aa7b08d938bd8d1485a787809d7b4c8aed97be880708470cd2b2cdf8e2f13428cc4b04ef1f2acbc9562f3693b948d0aa94b0e6113cafa684f8e4a67dc431dfb835726874bef1de36f273f52ee694ec46b0700f77f8538067642a552968e866a72a3f2031ad116663ac17b172b446c5bc705b84777363a9a3fdc6443c07b2f4ef58858122168d4ebbaee920cefc312e1cea870ed6e15eec046ab2073bbf08b0a3366f55cfc6ad4681a12ab0946534e7b6f90ea8992d530ec3daa6b523b3cf03101c60cadd914f30dec932c1ef4341b5a8efac3c921e203574cfe0f1f83433fddb8ccfd273f7c3cab7bc27efe3bb61fdccd5146f1185364b9b621e7fb2b74b51f5ee6be72ab6ff46a6359dc2c855e61469724c1dbeb273df9d2e1c1fb74891239c0019dc12d5c7535f7238f963b761d7102b585372cf021b64c4fc85bfb3161e59d2e298bba44cfd34d6859d9dba9dc6271e5047d525468c814f2ae438474b0a977273036da1a2292f88fcfb89574a6bdca1185b40f8aa54026d5926725f99ef028da1be892e3586361efe15f4a148ff1bc9") + val error3 = FailurePacket.wrap(error2, sharedSecret1) + assert(error3 == hex"4385e5483ecade9dc66c52cd980c96f60143bed184abac736030d8efb91c8d17b7dfadbb55ec8dae8857add94e6702fb4c3a4de22e2e669e1ed926b04447fc73034bb730f4932acd62727b75348a648a1128744657ca6a4e713b9b646c3ca66cac02cdab44dd3439890ef3aaf61708714f7375349b8da541b2548d452d84de7084bb95b3ac2345201d624d31f4d52078aa0fa05a88b4e20202bd2b86ac5b52919ea305a8949de95e935eed0319cf3cf19ebea61d76ba92532497fcdc9411d06bcd4275094d0a4a3c5d3a945e43305a5a9256e333e1f64dbca5fcd4e03a39b9012d197506e06f29339dfee3331995b21615337ae060233d39befea925cc262873e0530408e6990f1cbd233a150ef7b004ff6166c70c68d9f8c853c1abca640b8660db29218466c8766a7103a2ebdfe36daee877fffeb8f19bb9b7e6267a37129b836b28abddfc370eb45c1699c856969e2d574fdd155945ed727fdf2aec4f056a4d49fdefc3abafe41c365a5bd14fd486d6b5e2f24199319e7813e02e798877ffe31a70ae2398d9e31b9e3727e6c1a3c0d995c67d37bb6e72e9660aaaa9232670f382add2edd468927e3303b6142672546997fe105583e7c5a3c4c2b599731308b5416e6c9a3f3ba55b181ad0439d3535356108b059f2cb8742eed7a58d4eba9fe79eaa77c34b12aff1abdaea93197aabd0e74cb271269ca464b3b06aef1d6573df5e1224179616036b368677f26479376681b772d3760e871d99efd34cca5cd6beca95190d967da820b21e5bec60082ea46d776b0517488c84f26d12873912d1f68fafd67bcf4c298e43cfa754959780682a2db0f75f95f0598c0d04fd014c50e4beb86a9e37d95f2bba7e5065ae052dc306555bca203d104c44a538b438c9762de299e1c4ad30d5b4a6460a76484661fc907682af202cd69b9a4473813b2fdc1142f1403a49b7e69a650b7cde9ff133997dcc6d43f049ecac5fce097a21e2bce49c810346426585e3a5a18569b4cddd5ff6bdec66d0b69fcbc5ab3b137b34cc8aefb8b850a764df0e685c81c326611d901c392a519866e132bbb73234f6a358ba284fbafb21aa3605cacbaf9d0c901390a98b7a7dac9d4f0b405f7291c88b2ff45874241c90ac6c5fc895a440453c344d3a365cb929f9c91b9e39cb98b142444aae03a6ae8284c77eb04b0a163813d4c21883df3c0f398f47bf127b5525f222107a2d8fe55289f0cfd3f4bbad6c5387b0594ef8a966afc9e804ccaf75fe39f35c6446f7ee076d433f2f8a44dba1515acc78e589fa8c71b0a006fe14feebd51d0e0aa4e51110d16759eee86192eee90b34432130f387e0ccd2ee71023f1f641cddb571c690107e08f592039fe36d81336a421e89378f351e633932a2f5f697d25b620ffb8e84bb6478e9bd229bf3b164b48d754ae97bd23f319e3c56b3bcdaaeb3bd7fc02ec02066b324cb72a09b6b43dec1097f49d69d3c138ce6f1a6402898baf7568c") + val error4 = FailurePacket.wrap(error3, sharedSecret0) + assert(error4 == hex"751c187d145e5498306824f193c6bf9ed4a974fa85b3cc5d32d549ce494c1e7b3a06a19f8a9145610741c83ad40b7712aefaddec8c6baf7325d92ea4ca4d1df8bce517f7e54554608bf2bd8071a4f52a7a2f7ffbb1413edad81eeea5785aa9d990f2865dc23b4bc3c301a94eec4eabebca66be5cf638f693ec256aec514620cc28ee4a94bd9565bc4d4962b9d3641d4278fb319ed2b84de5b665f307a2db0f7fbb757366067d88c50f7e829138fde4f78d39b5b5802f1b92a8a820865af5cc79f9f30bc3f461c66af95d13e5e1f0381c184572a91dee1c849048a647a1158cf884064deddbf1b0b88dfe2f791428d0ba0f6fb2f04e14081f69165ae66d9297c118f0907705c9c4954a199bae0bb96fad763d690e7daa6cfda59ba7f2c8d11448b604d12dc942b5cf1db059d3e73d63967e464b5d5cfd4052de195387de93535e88a2e618e15a7c521d67ce2cc836c49118f205c99f18570504504221e337a29e2716fb28671b2bb91e38ef5e18aaf32c6c02f2fb690358872a1ed28166172631a82c2568d23238017188ebbd48944a147f6cdb3690d5f88e51371cb70adf1fa02afe4ed8b581afc8bcc5104922843a55d52acde09bc9d2b71a663e178788280f3c3eae127d21b0b95777976b3eb17be40a702c244d0e5f833ff49dae6403ff44b131e66df8b88e33ab0a58e379f2c34bf5113c66b9ea8241fc7aa2b1fa53cf4ed3cdd91d407730c66fb039ef3a36d4050dde37d34e80bcfe02a48a6b14ae28227b1627b5ad07608a7763a531f2ffc96dff850e8c583461831b19feffc783bc1beab6301f647e9617d14c92c4b1d63f5147ccda56a35df8ca4806b8884c4aa3c3cc6a174fdc2232404822569c01aba686c1df5eecc059ba97e9688c8b16b70f0d24eacfdba15db1c71f72af1b2af85bd168f0b0800483f115eeccd9b02adf03bdd4a88eab03e43ce342877af2b61f9d3d85497cd1c6b96674f3d4f07f635bb26add1e36835e321d70263b1c04234e222124dad30ffb9f2a138e3ef453442df1af7e566890aedee568093aa922dd62db188aa8361c55503f8e2c2e6ba93de744b55c15260f15ec8e69bb01048ca1fa7bbbd26975bde80930a5b95054688a0ea73af0353cc84b997626a987cc06a517e18f91e02908829d4f4efc011b9867bd9bfe04c5f94e4b9261d30cc39982eb7b250f12aee2a4cce0484ff34eebba89bc6e35bd48d3968e4ca2d77527212017e202141900152f2fd8af0ac3aa456aae13276a13b9b9492a9a636e18244654b3245f07b20eb76b8e1cea8c55e5427f08a63a16b0a633af67c8e48ef8e53519041c9138176eb14b8782c6c2ee76146b8490b97978ee73cd0104e12f483be5a4af414404618e9f6633c55dda6f22252cb793d3d16fae4f0e1431434e7acc8fa2c009d4f6e345ade172313d558a4e61b4377e31b8ed4e28f7cd13a7fe3f72a409bc3bdabfe0ba47a6d861e21f64d2fac706dab18b3e546df4") + + // origin parses error packet and can see that it comes from node #4 + val Success(DecryptedFailurePacket(pubkey, failure)) = FailurePacket.decrypt(error4, sharedSecrets) + assert(pubkey == publicKeys(4)) + assert(failure == TemporaryNodeFailure) } test("intermediate node replies with a failure message (reference test vector)") { @@ -353,6 +366,26 @@ class SphinxSpec extends AnyFunSuite { } } + test("intermediate node replies with a failure message (arbitrary length)") { + val Success(PacketAndSecrets(packet, sharedSecrets)) = create(sessionKey, 1300, publicKeys, referencePaymentPayloads, associatedData) + val Right(DecryptedPacket(_, packet1, sharedSecret0)) = peel(privKeys(0), associatedData, packet) + val Right(DecryptedPacket(_, packet2, sharedSecret1)) = peel(privKeys(1), associatedData, packet1) + val Right(DecryptedPacket(_, _, sharedSecret2)) = peel(privKeys(2), associatedData, packet2) + + // node #2 want to reply with an error message + val error = createCustomLengthFailurePacket(InvalidRealm, sharedSecret2, 1024) + assert(error == hex"f1ca7d3b281a71af53d4a0f83f22b618aae9f9c11b1f3302b13615c66d9aefcc5f1938ef23b9dfa61e3d576b149bedaf83058f85f06a3172a3223ad6c4732d96b32955da7d2feb4140e58d86fc0f2eb5d9d1878e6f8a7f65ab9212030e8e915573ebbd7f35e1a430890be7e67c3fb4bbf2def662fa625421e7b411c29ebe81ec67b77355596b05cc155755664e59c16e21410aabe53e80404a615f44ebb31b365ca77a6e91241667b26c6cad24fb2324cf64e8b9dd6e2ce65f1f098cfd1ef41ba2d4c7def0ff165a0e7c84e7597c40e3dffe97d417c144545a0e38ee33ebaae12cc0c14650e453d46bfc48c0514f354773435ee89b7b2810606eb73262c77a1d67f3633705178d79a1078c3a01b5fadc9651feb63603d19decd3a00c1f69af2dab2595931ca50d8280758b1cc91ba2dc43dbbc3d91bf25c08b46c2ecef7a32cec64d4b61ee3a629ef563afe058b71e71bcb69033948bc8728c5ebe65ec596e4f305b9fc159d53f723dfc95b57f3d51717f1c89af97a6d587e89e62efcc92198a1b2bd66e2d875505ea4046c04389f8cb0ee98f0af03af2652e2f3d9a9c48430f2891a4d9b16e7d18099e4a3dd334c24aba1e2450792c2f22092c170da549d43a440021e699bd6c20d8bbf1961100a01ebcce06a4609f5ad93066287acf68294cfa9ea7cea03a508983b134a9f0118b16409a61c06aaa95897d2067cb7cd59123f3e2ccf0e16091571d616c44818f118bb7835a679f5c0eea8cf1bd5479882b2c2a341ec26dbe5da87b3d37d66b1fbd176f71ab203a3b6eaf7f214d579e7d0e4a3e59089ebd26ba04a62403ae7a793516ec16d971d51c5c0107a917d1a70221e6de16edca7cb057c7d06902b5191f298aa4d478a0c3a6260c257eae504ebbf2b591688e6f3f77af770b6f566ae9868d2f26c12574d3bf9323af59f0fe0072ff94ae597c2aa6fbcbf0831989e02f9d3d1b9fd6dd97f509185d9ecbf272e38bd621ee94b97af8e1cd43853a8f6aa6e8372585c71bf88246d064ade524e1e0bd8496b620c4c2d3ae06b6b064c97536aaf8d515046229f72bee8aa398cd0cc21afd5449595016bef4c77cb1e2e9d31fe1ca3ffde06515e6a4331ccc84edf702e5777b10fc844faf17601a4be3235931f6feca4582a8d247c1d6e4773f8fb6de320cf902bbb1767192782dc550d8e266e727a2aa2a414b816d1826ea46af71701537193c22bbcc0123d7ff5a23b0aa8d7967f36fef27b14fe1866ff3ab215eb29e07af49e19174887d71da7e7fe1b7aa1b3c805c063e0fafedf125fa6c57e38cce33a3f7bb35fd8a9f0950de3c22e49743c05f40bc55f960b8a8b5e2fde4bb229f125538438de418cb318d13968532499118cb7dcaaf8b6d635ac4001273bdafd12c8ea0702fb2f0dac81dbaaf68c1c32266382b293fa3951cb952ed5c1bdc41750cdbc0bd62c51bb685616874e251f031a929c06faef5bfcb0857f815ae20620b823f0abecfb5") + val error1 = FailurePacket.wrap(error, sharedSecret1) + assert(error1 == hex"fedab5542d8cfc76425c1960d1676ac551116628b2859535ed74f8934d38b82c175c570b34788dbfd0048e4a41c2bb01acf21a928c09f96b801d011d5ff805731f476679849797e76d1ace72304509e05adbbcf0f74959d7d370af32fa27066b9a7a9cb91d92518f3bdabe35a8b3ecea116db79b0c011b70742599012741c4128ca6655eeaf7e6ff343fed810af0e069fa1650659d5864f8b9f1aea92f1fcc10b1b71f3b012e1e55e53056d7f5e092daf7eb1b9244d2de468f69730f3237ce39a84cfd0ee42b12e5ac7ca63fea15bee528125e135090988e55fda565f99f15787ab49ae2b536ca34b1732069a72f314c99836091c17f4e50afecc602184c1e656cf6eb752a4ded94df315a3e16e3d3e422517e9b9a5c566f8bf3eb6144a6778df0078b51887d8ea59b73416c59594f81f8bf1b0f1c98b3d9d5ed87fe76358a47df8a705fb3edddf64770c2d49744854a5ed0272d94cec1cd1b049a6dece2e4aade89d783634c259a330bc407af06368aece354d6fe73608716da08a037dec9c71c4c73bd4a6c86fd1820b54aa2602132a95495933a24e28b189219859ab46847340ad08968a70d5a0df8223aab06a6ea532a4cb25498f3687361a59b9896975c948e03ba60f5248a1f2f4d7aa6e8f00f82f6ca92273f6084cac56c51d4dda2511d64d88dcfd11df5a07ae6779d445f141f5759fca37e09826e2e481ed5dced02956104b219f839f508f60d8828250d0a3617b9d021fad48cde24a5cb42e3278dff0d95af795d4c71bccd344fa98129c9d6f53dd4f7acab78a98711fc5d04112ae971dedc97649608597b7e53369be2fe3f9b0e6b349b3fadcf9bd2a3d24b5e876c74e1006f7c330714ea5146986f3f73b09cae5cdf6277e23a34ecce0d92d909442743ef415be81050c341eb305e93b14b07b55c079766cd894ea00826ce50d5c45707870b0cf411113b8e4e43cf34caf79f3936fdfdbeae185ff52db69ca72442d892ed0e45b9fac939aa172bfa873cebee1e2196fe124597feb92880339ebca8233acaf3061591ed8cf290dfd9b0a06d7efc299993b9c680451992e15d2cb8b5b4a3dc1e511a39d781818144a9662bdfbd01371e898c454a8a092b7a0d32a8d58aec8134891a974ac7b297c3b4f94100083db891bde0ebc1e737dc6c33dad87cf20429d1b865c7e8ee5032d66a17c5a731d288dda8fc38e2c963c317f12a786ded3eac484dcc11b5c530dec0e4cc40ec4bb2c529555a51d8655a4de08fdde774781b5672150d1c771bf0916fc5df6ddb2f2e683e86aa23a52c0fc2efe72eeb1fa5f86ad7926685f40d57ab19b29e1ce5dce8c98ae35aacf740cacb257915fe8421ec09d0883d4ae41fe2695679264b0196e8d0b874d47e2fd675c9dfba26e666d407572e19a65c84ca54ac7235ef1bd4aedd9b0f6406cc7eeb08020e325f22396bc1a42d2de5ff71042b4e098cb0358741a50757a31c45de1f7ecf3a5e5e06f8b682f0") + val error2 = FailurePacket.wrap(error1, sharedSecret0) + assert(error2 == hex"c843486107187673b4586f5cdaad43ad84fbac03b39df51bbf9169b2bd682b409a855b2feb0545705f12eba9dbaecee84e328a9c2e4c3086bb1d0909d1f2e4f8a0e9c6be9541e94a849a0887756b984031dcb74d11c20d437a55daf3ee4109dea68ad74f9b742e7571d5e4d1b2ea4f7094787cf361b448a22a547ea85b833aae20f3ba79fb41c6636414c2092d41dd5328e2c1a1c754cb1f0d297628219f91fe946169f593ce7fce79103945d4d24adce46c083ab24757870356af55fcd3d22b9cfd83c45d409eb3081b218448d5dca3a201cf89ac88c9b66049d7c262b32081d3aba2098ea853bfa173ec23aa9253e083dfa881ef487b76780435c1b9f8a1d794557f0ac91d261d280bfb8513ad0c4dab0d7152eb9ee36ae63b8d384613684326d8735dc559f31cecb21b1d55bbcf7a281127adbedd0210b243325fd291cb82d443beec8f4b96aaee4b1a619724d7456b756d391e8fd3256d2b0766e39a435eb4d6d144c7fca1c73105710266e31120565444dfd6e9099e44d73a0f28419809577a267bbbc6671f723669d00c35c8e60fad88d89d4a7477a0c30f9839485197ed76338330f2ca00cf0e31c59da4eeebef977f429ad2c61acac35939866dac5b1df1c3c487ebaf961340c0c1dbc4bedebde7ee0633c3f480b7df265a3d90e78a4bcb9497f4228169fadb647e77afe6f43aa129286bb21767f6e75ac5c092473f99f2cf8b4e191f300c70b210e077a0385d483971bc0c66f5c119c0731a8753793ad12703d9cc5153eb1c8f25b71ee88a8d1d4433aa8f8277366c82111dbebfe0f548411588d54c3606742330d3d84a2f107df98d60995297de11672f6300b11444a04e252d69d8187772798afc6a9cd8b245a5ebd51bf0659f18c57daf1d1f724d2f15d524ab6902fb17a8fa6cee8e01df67735eac34bb0efc183dcb8d2a7cb401bd786c32a17f14c9d9ffc02b4f58c4ebab898a78b4913647d4cb5bafe6f7f27b5a256d1635c10f0ca71796610068c090c270c20bb18ec9d205e640d7655bdf5c9aeae20d7f9426eade0733c19d0aa577caf31f9d5be0a99ed0c509e84ccb555389ca69f09c3e66694a4ea2785f8d839d7dfff08b2c21aff89a023161cb1ebdd1e7a46d6380c0ddbc88eb3526e624fadcd222ecaa09566c2678158f933f03623299fec134a880d39a9d82ba2b29211e7787b3f32d478df856389a02cb68b66fc0dfc0b52353e7360f31e5457a6a9dd34512e912afeb5a92f3cbd3883b62c37e3ba5e4e8b688033150103c810740d130a5597c8a4a16311f50cfb3a919aac1e0a1096f20a14a536c55068ad38f40e62fc6f178b2fee67ca2cbd8afa29ef6c89b217aee02419ca26d59b604521a55e37c0a5a693fbc3ebcba23cd62479ddf62e5521847a2b4ac5e7686ef662c29cf8a8983660530942ee9a6c53b55e08af0b43467989693cefe6267fd524435152c01c9b93aebdec6146366a94162f99ac4c7157c15b988") + + // origin parses error packet and can see that it comes from node #2 + val Success(DecryptedFailurePacket(pubkey, failure)) = FailurePacket.decrypt(error2, sharedSecrets) + assert(pubkey == publicKeys(2)) + assert(failure == InvalidRealm) + } + test("create blinded route (reference test vector)") { val alice = PrivateKey(hex"4141414141414141414141414141414141414141414141414141414141414141") val bob = PrivateKey(hex"4242424242424242424242424242424242424242424242424242424242424242") @@ -551,6 +584,12 @@ object SphinxSpec { def serializeTrampolineOnion(onion: OnionRoutingPacket): ByteVector = PaymentOnionCodecs.trampolineOnionPacketCodec.encode(onion).require.toByteVector + def createCustomLengthFailurePacket(failure: FailureMessage, sharedSecret: ByteVector32, length: Int): ByteVector = { + val um = Sphinx.generateKey("um", sharedSecret) + val packet = FailureMessageCodecs.failureOnionCodec(Hmac256(um), length).encode(failure).require.toByteVector + Sphinx.FailurePacket.wrap(packet, sharedSecret) + } + val privKeys = Seq( PrivateKey(hex"4141414141414141414141414141414141414141414141414141414141414141"), PrivateKey(hex"4242424242424242424242424242424242424242424242424242424242424242"), diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/FailureMessageCodecsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/FailureMessageCodecsSpec.scala index 10a7d7eda7..9763bc5995 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/FailureMessageCodecsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/FailureMessageCodecsSpec.scala @@ -19,7 +19,7 @@ package fr.acinq.eclair.wire.protocol import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, ByteVector64} import fr.acinq.eclair.crypto.Hmac256 import fr.acinq.eclair.wire.protocol.FailureMessageCodecs._ -import fr.acinq.eclair.{BlockHeight, CltvExpiry, CltvExpiryDelta, MilliSatoshi, MilliSatoshiLong, ShortChannelId, TimestampSecond, TimestampSecondLong, UInt64, randomBytes32, randomBytes64} +import fr.acinq.eclair.{BlockHeight, CltvExpiry, CltvExpiryDelta, MilliSatoshiLong, ShortChannelId, TimestampSecond, TimestampSecondLong, UInt64, randomBytes32, randomBytes64} import org.scalatest.funsuite.AnyFunSuite import scodec.bits._ @@ -111,11 +111,25 @@ class FailureMessageCodecsSpec extends AnyFunSuite { val codec = failureOnionCodec(Hmac256(ByteVector32.Zeroes)) val testCases = Map( // Without any data. - IncorrectOrUnknownPaymentDetails(MilliSatoshi(0), BlockHeight(0)) -> hex"0d83b55dd5a6086e4033c3659125ed1ff436964ce0e67ed5a03bddb16a9a1041 0002 400f 00fe 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + IncorrectOrUnknownPaymentDetails(0 msat, BlockHeight(0)) -> hex"0d83b55dd5a6086e4033c3659125ed1ff436964ce0e67ed5a03bddb16a9a1041 0002 400f 00fe 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", // With an amount but no height. - IncorrectOrUnknownPaymentDetails(MilliSatoshi(42), BlockHeight(0)) -> hex"ba6e122b2941619e2106e8437bf525356ffc8439ac3b2245f68546e298a08cc6 000a 400f 000000000000002a 00f6 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + IncorrectOrUnknownPaymentDetails(42 msat, BlockHeight(0)) -> hex"ba6e122b2941619e2106e8437bf525356ffc8439ac3b2245f68546e298a08cc6 000a 400f 000000000000002a 00f6 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", // With amount and height. - IncorrectOrUnknownPaymentDetails(MilliSatoshi(42), BlockHeight(1105)) -> hex"5eb766da1b2f45b4182e064dacd8da9eca2c9a33f0dce363ff308e9bdb3ee4e3 000e 400f 000000000000002a 00000451 00f2 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + IncorrectOrUnknownPaymentDetails(42 msat, BlockHeight(1105)) -> hex"5eb766da1b2f45b4182e064dacd8da9eca2c9a33f0dce363ff308e9bdb3ee4e3 000e 400f 000000000000002a 00000451 00f2 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ) + + for ((expected, bin) <- testCases) { + assert(codec.decode(bin.bits).require.value == expected) + } + } + + test("decode failure onion packet with arbitrary length") { + val codec = failureOnionCodec(Hmac256(ByteVector32.Zeroes)) + val testCases = Seq( + InvalidRealm -> hex"7bfb2aa46218240684f623322ae48af431d06986c82e210bb0cee83c7ddb2ba8 0002 4001 0002 0000", + IncorrectOrUnknownPaymentDetails(1105 msat, BlockHeight(1729)) -> hex"c508151d550a6a7fb121542b7c383fd7f18381832499c419de436e131c1f3a76 000e 400f 0000000000000451 000006c1 0004 deadbeef", + InvalidRealm -> hex"6f9e2c0e44b3692dac37523c6ff054cc9b26ecab1a78ed6906a46848bffc2bd5 0002 4001 00ff 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + IncorrectOrUnknownPaymentDetails(1105 msat, BlockHeight(1729)) -> hex"bb2873dad5447927774cb7de99f43c0b5f54f6e298b5be4d7ca88677b8f0817d 000e 400f 0000000000000451 000006c1 00ff 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", ) for ((expected, bin) <- testCases) { @@ -130,12 +144,8 @@ class FailureMessageCodecsSpec extends AnyFunSuite { hex"fd2f3eb163dacfa7fe2ec1a7dc73c33438e7ca97c561475cf0dc96dc15a75039 0020 c005 2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a 00e0 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", // Invalid mac. hex"0000000000000000000000000000000000000000000000000000000000000000 0022 c006 2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a 00de 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - // Padding too small. - hex"7bfb2aa46218240684f623322ae48af431d06986c82e210bb0cee83c7ddb2ba8 0002 4001 0002 0000", // Padding length doesn't match actual padding. hex"8c92256e45bbe765130d952e6c043cf594ab25224701f5477fce0e50ee88fa21 0002 4001 0002 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - // Padding too big. - hex"6f9e2c0e44b3692dac37523c6ff054cc9b26ecab1a78ed6906a46848bffc2bd5 0002 4001 00ff 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", // Padding length doesn't match actual padding. hex"3898307b7c01781628ff6f854a4a78524541e4afde9b44046bdb84093f082d9d 0002 4001 00ff 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" )