Skip to content

Commit e244f62

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

7 files changed

+954
-0
lines changed

attributable_error_crypto.go

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

0 commit comments

Comments
 (0)