Skip to content

Commit 74fcef9

Browse files
committed
add attributable error encryption and decryption
1 parent 78880cd commit 74fcef9

7 files changed

+1021
-0
lines changed

attributable_error_crypto.go

+170
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
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
14+
// error 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+
// AttrErrorStructure contains the parameters that define the structure
23+
// of the error message that is passed back.
24+
type AttrErrorStructure 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
29+
// the route can add.
30+
fixedPayloadLen int
31+
32+
// hmacSize is the number of bytes that is reserved for each hmac.
33+
hmacSize int
34+
35+
zeroHmac []byte
36+
}
37+
38+
func NewAttrErrorStructure(hopCount int, fixedPayloadLen int,
39+
hmacSize int) *AttrErrorStructure {
40+
41+
return &AttrErrorStructure{
42+
hopCount: hopCount,
43+
fixedPayloadLen: fixedPayloadLen,
44+
hmacSize: hmacSize,
45+
46+
zeroHmac: make([]byte, hmacSize),
47+
}
48+
}
49+
50+
// HopCount returns the assumed maximum number of hops in the path.
51+
func (o *AttrErrorStructure) HopCount() int {
52+
return o.hopCount
53+
}
54+
55+
// FixedPayloadLen returns the length of the payload data that each hop along
56+
// the route can add.
57+
func (o *AttrErrorStructure) FixedPayloadLen() int {
58+
return o.fixedPayloadLen
59+
}
60+
61+
// HmacSize returns the number of bytes that is reserved for each hmac.
62+
func (o *AttrErrorStructure) HmacSize() int {
63+
return o.hmacSize
64+
}
65+
66+
// totalHmacs is the total number of hmacs that is present in the failure
67+
// message. Every hop adds HopCount hmacs to the message, but as the error
68+
// back-propagates, downstream hmacs can be pruned. This results in the number
69+
// of hmacs for each hop decreasing by one for each step that we move away from
70+
// the current node.
71+
func (o *AttrErrorStructure) totalHmacs() int {
72+
return (o.hopCount * (o.hopCount + 1)) / 2
73+
}
74+
75+
// allHmacsLen is the total length in the bytes of all hmacs in the failure
76+
// message.
77+
func (o *AttrErrorStructure) allHmacsLen() int {
78+
return o.totalHmacs() * o.hmacSize
79+
}
80+
81+
// hmacsAndPayloadsLen is the total length in bytes of all hmacs and payloads
82+
// together.
83+
func (o *AttrErrorStructure) hmacsAndPayloadsLen() int {
84+
return o.allHmacsLen() + o.allPayloadsLen()
85+
}
86+
87+
// allPayloadsLen is the total length in bytes of all payloads in the failure
88+
// message.
89+
func (o *AttrErrorStructure) allPayloadsLen() int {
90+
return o.payloadLen() * o.hopCount
91+
}
92+
93+
// payloadLen is the size of the per-node payload. It consists of a 1-byte
94+
// payload type followed by the payload data.
95+
func (o *AttrErrorStructure) payloadLen() int {
96+
return 1 + o.fixedPayloadLen
97+
}
98+
99+
// message returns a slice containing the message in the given failure data
100+
// block. The message is positioned at the beginning of the block.
101+
func (o *AttrErrorStructure) message(data []byte) []byte {
102+
return data[:len(data)-o.hmacsAndPayloadsLen()]
103+
}
104+
105+
// payloads returns a slice containing all payloads in the given failure
106+
// data block. The payloads follow the message in the block.
107+
func (o *AttrErrorStructure) payloads(data []byte) []byte {
108+
dataLen := len(data)
109+
110+
return data[dataLen-o.hmacsAndPayloadsLen() : dataLen-o.allHmacsLen()]
111+
}
112+
113+
// hmacs returns a slice containing all hmacs in the given failure data block.
114+
// The hmacs are positioned at the end of the data block.
115+
func (o *AttrErrorStructure) hmacs(data []byte) []byte {
116+
return data[len(data)-o.allHmacsLen():]
117+
}
118+
119+
// calculateHmac calculates an hmac given a shared secret and a presumed
120+
// position in the path. Position is expressed as the distance to the error
121+
// source. The error source itself is at position 0.
122+
func (o *AttrErrorStructure) calculateHmac(sharedSecret Hash256,
123+
position int, message, payloads, hmacs []byte) []byte {
124+
125+
umKey := generateKey("um", &sharedSecret)
126+
hash := hmac.New(sha256.New, umKey[:])
127+
128+
// Include message.
129+
_, _ = hash.Write(message)
130+
131+
// Include payloads including our own.
132+
_, _ = hash.Write(payloads[:(position+1)*o.payloadLen()])
133+
134+
// Include downstream hmacs.
135+
writeDownstreamHmacs(position, o.hopCount, hmacs, o.hmacSize, hash)
136+
137+
hmac := hash.Sum(nil)
138+
139+
return hmac[:o.hmacSize]
140+
}
141+
142+
// writeDownstreamHmacs writes the hmacs of downstream nodes that are relevant
143+
// for the given position to a writer instance. Position is expressed as the
144+
// distance to the error source. The error source itself is at position 0.
145+
func writeDownstreamHmacs(position, maxHops int, hmacs []byte, hmacBytes int,
146+
w io.Writer) {
147+
148+
// Track the index of the next hmac to write in a variable. The first
149+
// maxHops slots are reserved for the hmacs of the current hop and can
150+
// therefore be skipped. The first hmac to write is part of the block of
151+
// hmacs that was written by the first downstream node. Which hmac
152+
// exactly is determined by the assumed position of the current node.
153+
var hmacIdx = maxHops + (maxHops - position - 1)
154+
155+
// Iterate over all downstream nodes.
156+
for j := 0; j < position; j++ {
157+
_, _ = w.Write(
158+
hmacs[hmacIdx*hmacBytes : (hmacIdx+1)*hmacBytes],
159+
)
160+
161+
// Calculate the total number of hmacs in the block of the
162+
// current downstream node.
163+
blockSize := maxHops - j - 1
164+
165+
// Skip to the next block. The new hmac index will point to the
166+
// hmac that corresponds to the next downstream node which is
167+
// one step closer to the assumed error source.
168+
hmacIdx += blockSize
169+
}
170+
}

0 commit comments

Comments
 (0)