Skip to content

Commit 6649a30

Browse files
committed
add fat error encryption and decryption
1 parent 78880cd commit 6649a30

7 files changed

+952
-0
lines changed

attributable_error_crypto.go

+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
package sphinx
2+
3+
import (
4+
"crypto/hmac"
5+
"crypto/sha256"
6+
"io"
7+
)
8+
9+
type payloadSource byte
10+
11+
const (
12+
// payloadIntermediateNode is a marker to signal that this attributable
13+
// error payload is originating from a node between the payer and the error
14+
// source.
15+
payloadIntermediateNode payloadSource = 0
16+
17+
// payloadErrorNode is a marker to signal that this attributable error
18+
// payload is originating from the error source.
19+
payloadErrorNode payloadSource = 1
20+
)
21+
22+
// AttributableErrorStructure contains the parameters that define the structure
23+
// of the error message that is passed back.
24+
type AttributableErrorStructure struct {
25+
// HopCount is the assumed maximum number of hops in the path.
26+
HopCount int
27+
28+
// FixedPayloadLen is the length of the payload data that each hop along the
29+
// route can add.
30+
FixedPayloadLen int
31+
}
32+
33+
func (o *AttributableErrorStructure) maxHops() int {
34+
return o.HopCount
35+
}
36+
37+
func (o *AttributableErrorStructure) payloadDataLen() int {
38+
return o.FixedPayloadLen
39+
}
40+
41+
// totalHmacs is the total number of hmacs that is present in the failure
42+
// message. Every hop adds HopCount hmacs to the message, but as the error
43+
// back-propagates, downstream hmacs can be pruned. This results in the number
44+
// of hmacs for each hop decreasing by one for each step that we move away from
45+
// the current node.
46+
func (o *AttributableErrorStructure) totalHmacs() int {
47+
return (o.HopCount * (o.HopCount + 1)) / 2
48+
}
49+
50+
// allHmacsLen is the total length in the bytes of all hmacs in the failure
51+
// message.
52+
func (o *AttributableErrorStructure) allHmacsLen() int {
53+
return o.totalHmacs() * sha256.Size
54+
}
55+
56+
// hmacsAndPayloadsLen is the total length in bytes of all hmacs and payloads
57+
// together.
58+
func (o *AttributableErrorStructure) hmacsAndPayloadsLen() int {
59+
return o.allHmacsLen() + o.allPayloadsLen()
60+
}
61+
62+
// allPayloadsLen is the total length in bytes of all payloads in the failure
63+
// message.
64+
func (o *AttributableErrorStructure) allPayloadsLen() int {
65+
return o.payloadLen() * o.HopCount
66+
}
67+
68+
// payloadLen is the size of the per-node payload. It consists of a 1-byte
69+
// payload type followed by the payload data.
70+
func (o *AttributableErrorStructure) payloadLen() int {
71+
return 1 + o.payloadDataLen()
72+
}
73+
74+
// message returns a slice containing the message in the given failure data
75+
// block. The message is positioned at the beginning of the block.
76+
func (o *AttributableErrorStructure) message(data []byte) []byte {
77+
return data[:len(data)-o.hmacsAndPayloadsLen()]
78+
}
79+
80+
// payloads returns a slice containing all payloads in the given failure
81+
// data block. The payloads follow the message in the block.
82+
func (o *AttributableErrorStructure) payloads(data []byte) []byte {
83+
return data[len(data)-o.hmacsAndPayloadsLen() : len(data)-o.allHmacsLen()]
84+
}
85+
86+
// hmacs returns a slice containing all hmacs in the given failure data block.
87+
// The hmacs are positioned at the end of the data block.
88+
func (o *AttributableErrorStructure) hmacs(data []byte) []byte {
89+
return data[len(data)-o.allHmacsLen():]
90+
}
91+
92+
// calculateHmac calculates an hmac given a shared secret and a presumed
93+
// position in the path. Position is expressed as the distance to the error
94+
// source. The error source itself is at position 0.
95+
func (o *AttributableErrorStructure) calculateHmac(sharedSecret Hash256,
96+
position int, message, payloads, hmacs []byte) []byte {
97+
98+
umKey := generateKey("um", &sharedSecret)
99+
hash := hmac.New(sha256.New, umKey[:])
100+
101+
// Include message.
102+
_, _ = hash.Write(message)
103+
104+
// Include payloads including our own.
105+
_, _ = hash.Write(payloads[:(o.maxHops()-position)*o.payloadLen()])
106+
107+
// Include downstream hmacs.
108+
writeDownstreamHmacs(position, o.maxHops(), hmacs, hash)
109+
110+
return hash.Sum(nil)
111+
}
112+
113+
// writeDownstreamHmacs writes the hmacs of downstream nodes that are relevant
114+
// for the given position to a writer instance.
115+
func writeDownstreamHmacs(position, maxHops int, hmacs []byte, w io.Writer) {
116+
// Track the index of the next hmac to write in a variable. The first
117+
// maxHops slots are reserved for the hmacs of the current hop and can
118+
// therefore be skipped. The first hmac to write is part of the block of
119+
// hmacs that was written by the first downstream node. Which hmac exactly
120+
// is determined by the assumed position of the current node.
121+
var currentHmacIdx = maxHops + position
122+
123+
// Iterate over all downstream nodes.
124+
for j := 0; j < maxHops-position-1; j++ {
125+
_, _ = w.Write(
126+
hmacs[currentHmacIdx*sha256.Size : (currentHmacIdx+1)*sha256.Size],
127+
)
128+
129+
// Calculate the total number of hmacs in the block of the current
130+
// downstream node.
131+
blockSize := maxHops - j - 1
132+
133+
// Skip to the next block. The new hmac index will point to the hmac
134+
// that corresponds to the next downstream node which is one step closer
135+
// to the assumed error source.
136+
currentHmacIdx += blockSize
137+
}
138+
}

0 commit comments

Comments
 (0)