|
1 | 1 | package sphinx |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "bytes" |
4 | 5 | "crypto/hmac" |
5 | 6 | "crypto/sha256" |
6 | 7 | "errors" |
@@ -175,26 +176,65 @@ func NewOnionDeobfuscator(circuit *Circuit) *OnionDeobfuscator { |
175 | 176 | // obfuscation in reverse order. |
176 | 177 | func (o *OnionDeobfuscator) Deobfuscate(obfuscatedData []byte) (*btcec.PublicKey, []byte, error) { |
177 | 178 |
|
178 | | - for i, sharedSecret := range generateSharedSecrets(o.circuit.PaymentPath, |
179 | | - o.circuit.SessionKey) { |
| 179 | + sharedSecrets := generateSharedSecrets( |
| 180 | + o.circuit.PaymentPath, |
| 181 | + o.circuit.SessionKey, |
| 182 | + ) |
| 183 | + |
| 184 | + var ( |
| 185 | + sender *btcec.PublicKey |
| 186 | + msg []byte |
| 187 | + dummySecret [sha256.Size]byte |
| 188 | + ) |
| 189 | + copy(dummySecret[:], bytes.Repeat([]byte{1}, 32)) |
| 190 | + |
| 191 | + // We'll iterate a constant amount of hops to ensure that we don't give |
| 192 | + // away an timing information pertaining to the position in the route |
| 193 | + // that the error emanated from. |
| 194 | + for i := 0; i < NumMaxHops; i++ { |
| 195 | + var sharedSecret [sha256.Size]byte |
| 196 | + |
| 197 | + // If we've already found the sender, then we'll use our dummy |
| 198 | + // secret to continue decryption attempts to fill out the rest |
| 199 | + // of the loop. Otherwise, we'll use the next shared secret in |
| 200 | + // line. |
| 201 | + if sender != nil { |
| 202 | + sharedSecret = dummySecret |
| 203 | + } else { |
| 204 | + sharedSecret = sharedSecrets[i] |
| 205 | + } |
| 206 | + |
| 207 | + // With the shared secret, we'll now strip off a layer of |
| 208 | + // encryption from the encrypted error payload. |
180 | 209 | obfuscatedData = onionObfuscation(sharedSecret, obfuscatedData) |
181 | | - umKey := generateKey("um", sharedSecret) |
182 | 210 |
|
183 | | - // Split the data and hmac. |
| 211 | + // Next, we'll need to separate the data, from the MAC itself |
| 212 | + // so we can reconstruct and verify it. |
184 | 213 | expectedMac := obfuscatedData[:sha256.Size] |
185 | 214 | data := obfuscatedData[sha256.Size:] |
186 | 215 |
|
187 | | - // Calculate the real hmac. |
| 216 | + // With the data split, we'll now re-generate the MAC using its |
| 217 | + // specified key. |
| 218 | + umKey := generateKey("um", sharedSecret) |
188 | 219 | h := hmac.New(sha256.New, umKey[:]) |
189 | 220 | h.Write(data) |
190 | | - realMac := h.Sum(nil) |
191 | 221 |
|
192 | | - if hmac.Equal(realMac, expectedMac) { |
193 | | - return o.circuit.PaymentPath[i], data, nil |
| 222 | + // If the MAC matches up, then we've found the sender of the |
| 223 | + // error and have also obtained the fully decrypted message. |
| 224 | + realMac := h.Sum(nil) |
| 225 | + if hmac.Equal(realMac, expectedMac) && sender == nil { |
| 226 | + sender = o.circuit.PaymentPath[i] |
| 227 | + msg = data |
194 | 228 | } |
195 | 229 | } |
196 | 230 |
|
197 | | - return nil, nil, errors.New("unable to retrieve onion failure") |
| 231 | + // If the sender pointer is still nil, then we haven't found the |
| 232 | + // sender, meaning we've failed to decrypt. |
| 233 | + if sender == nil { |
| 234 | + return nil, nil, errors.New("unable to retrieve onion failure") |
| 235 | + } |
| 236 | + |
| 237 | + return sender, msg, nil |
198 | 238 | } |
199 | 239 |
|
200 | 240 | // Decode writes converted deobfucator in the byte stream. |
|
0 commit comments