Skip to content

Commit 44f1a78

Browse files
authored
Bring changes of libepic from PR #261 (#266)
* libepic * add changes to scion_spec.gobra
1 parent 75eaad1 commit 44f1a78

5 files changed

Lines changed: 123 additions & 25 deletions

File tree

pkg/experimental/epic/epic.go

Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,26 @@ func VerifyTimestamp(timestamp time.Time, epicTS uint32, now time.Time) (err err
107107
// If the same buffer is provided in subsequent calls to this function, the previously returned
108108
// EPIC MAC may get overwritten. Only the most recently returned EPIC MAC is guaranteed to be
109109
// valid.
110+
// (VerifiedSCION) the following function is marked as trusted, even though it is verified,
111+
// due to an incompletness of Gobra that keeps it from being able to prove that we have
112+
// the magic wand at the end of a successful run.
110113
// @ trusted
111-
// @ requires Uncallable()
114+
// @ requires len(auth) == 16
115+
// @ requires sl.AbsSlice_Bytes(buffer, 0, len(buffer))
116+
// @ preserves acc(s.Mem(ub), R20)
117+
// @ preserves acc(sl.AbsSlice_Bytes(ub, 0, len(ub)), R20)
118+
// @ preserves acc(sl.AbsSlice_Bytes(auth, 0, len(auth)), R30)
119+
// @ ensures reserr == nil ==> sl.AbsSlice_Bytes(res, 0, len(res))
120+
// @ ensures reserr == nil ==> (sl.AbsSlice_Bytes(res, 0, len(res)) --* sl.AbsSlice_Bytes(buffer, 0, len(buffer)))
121+
// @ ensures reserr != nil ==> reserr.ErrorMem()
122+
// @ ensures reserr != nil ==> sl.AbsSlice_Bytes(buffer, 0, len(buffer))
123+
// @ decreases
112124
func CalcMac(auth []byte, pktID epic.PktID, s *slayers.SCION,
113-
timestamp uint32, buffer []byte) ([]byte, error) {
125+
timestamp uint32, buffer []byte /*@ , ghost ub []byte @*/) (res []byte, reserr error) {
114126

115127
if len(buffer) < MACBufferSize {
116128
buffer = make([]byte, MACBufferSize)
129+
// @ fold sl.AbsSlice_Bytes(buffer, 0, len(buffer))
117130
}
118131

119132
// Initialize cryptographic MAC function
@@ -122,39 +135,58 @@ func CalcMac(auth []byte, pktID epic.PktID, s *slayers.SCION,
122135
return nil, err
123136
}
124137
// Prepare the input for the MAC function
125-
inputLength, err := prepareMacInput(pktID, s, timestamp, buffer)
138+
inputLength, err := prepareMacInput(pktID, s, timestamp, buffer /*@, ub @*/)
126139
if err != nil {
127140
return nil, err
128141
}
142+
// @ assert 16 <= inputLength
143+
// @ assert f.BlockSize() == 16
129144
// Calculate Epic MAC = first 4 bytes of the last CBC block
145+
// @ sl.SplitRange_Bytes(buffer, 0, inputLength, writePerm)
130146
input := buffer[:inputLength]
131147
f.CryptBlocks(input, input)
132-
return input[len(input)-f.BlockSize() : len(input)-f.BlockSize()+4], nil
148+
// @ ghost start := len(input)-f.BlockSize()
149+
// @ ghost end := start + 4
150+
result := input[len(input)-f.BlockSize() : len(input)-f.BlockSize()+4]
151+
// @ sl.SplitRange_Bytes(input, start, end, writePerm)
152+
// @ package (sl.AbsSlice_Bytes(result, 0, len(result)) --* sl.AbsSlice_Bytes(buffer, 0, len(buffer))) {
153+
// @ sl.CombineRange_Bytes(input, start, end, writePerm)
154+
// @ sl.CombineRange_Bytes(buffer, 0, inputLength, writePerm)
155+
// @ }
156+
// @ assert (sl.AbsSlice_Bytes(result, 0, len(result)) --* sl.AbsSlice_Bytes(buffer, 0, len(buffer)))
157+
return result, nil
133158
}
134159

135160
// VerifyHVF verifies the correctness of the HVF (PHVF or the LHVF) field in the EPIC packet by
136161
// recalculating and comparing it. If the EPIC authenticator (auth), which denotes the full 16
137162
// bytes of the SCION path type MAC, has invalid length, or if the MAC calculation gives an error,
138163
// also VerifyHVF returns an error. The verification was successful if and only if VerifyHVF
139164
// returns nil.
140-
// @ trusted
141-
// @ requires Uncallable()
165+
// @ preserves sl.AbsSlice_Bytes(buffer, 0, len(buffer))
166+
// @ preserves acc(s.Mem(ub), R20)
167+
// @ preserves acc(sl.AbsSlice_Bytes(hvf, 0, len(hvf)), R50)
168+
// @ preserves acc(sl.AbsSlice_Bytes(ub, 0, len(ub)), R20)
169+
// @ preserves acc(sl.AbsSlice_Bytes(auth, 0, len(auth)), R30)
170+
// @ ensures reserr != nil ==> reserr.ErrorMem()
171+
// @ decreases
142172
func VerifyHVF(auth []byte, pktID epic.PktID, s *slayers.SCION,
143-
timestamp uint32, hvf []byte, buffer []byte) error {
173+
timestamp uint32, hvf []byte, buffer []byte /*@ , ghost ub []byte @*/) (reserr error) {
144174

145175
if s == nil || len(auth) != AuthLen {
146176
return serrors.New("invalid input")
147177
}
148178

149-
mac, err := CalcMac(auth, pktID, s, timestamp, buffer)
179+
mac, err := CalcMac(auth, pktID, s, timestamp, buffer /*@ , ub @*/)
150180
if err != nil {
151181
return err
152182
}
153183

154184
if subtle.ConstantTimeCompare(hvf, mac) == 0 {
185+
// @ apply sl.AbsSlice_Bytes(mac, 0, len(mac)) --* sl.AbsSlice_Bytes(buffer, 0, len(buffer))
155186
return serrors.New("epic hop validation field verification failed",
156187
"hvf in packet", hvf, "calculated mac", mac, "auth", auth)
157188
}
189+
// @ apply sl.AbsSlice_Bytes(mac, 0, len(mac)) --* sl.AbsSlice_Bytes(buffer, 0, len(buffer))
158190
return nil
159191
}
160192

@@ -172,12 +204,9 @@ func CoreFromPktCounter(counter uint32) (uint8, uint32) {
172204
return coreID, coreCounter
173205
}
174206

175-
// (VerifiedSCION) The following verifies if we remove `Uncallable()“
176-
// from the precondition, but it seems to suffer from perf. problems.
177-
// @ requires Uncallable()
178207
// @ requires len(key) == 16
179-
// @ requires sl.AbsSlice_Bytes(key, 0, len(key))
180-
// @ ensures reserr == nil ==> res.Mem()
208+
// @ preserves acc(sl.AbsSlice_Bytes(key, 0, len(key)), R50)
209+
// @ ensures reserr == nil ==> res != nil && res.Mem() && res.BlockSize() == 16
181210
// @ ensures reserr != nil ==> reserr.ErrorMem()
182211
// @ decreases
183212
func initEpicMac(key []byte) (res cipher.BlockMode, reserr error) {
@@ -193,13 +222,11 @@ func initEpicMac(key []byte) (res cipher.BlockMode, reserr error) {
193222
return mode, nil
194223
}
195224

196-
// (VerifiedSCION) This function is mostly verified, but needs to be revisited before
197-
// dropping the precondition `Uncallable()`.
198-
// @ requires Uncallable()
199225
// @ requires MACBufferSize <= len(inputBuffer)
200226
// @ preserves acc(s.Mem(ub), R20)
201227
// @ preserves acc(sl.AbsSlice_Bytes(ub, 0, len(ub)), R20)
202228
// @ preserves sl.AbsSlice_Bytes(inputBuffer, 0, len(inputBuffer))
229+
// @ ensures reserr == nil ==> 16 <= res && res <= len(inputBuffer)
203230
// @ ensures reserr != nil ==> reserr.ErrorMem()
204231
// @ decreases
205232
func prepareMacInput(pktID epic.PktID, s *slayers.SCION, timestamp uint32,
@@ -230,6 +257,10 @@ func prepareMacInput(pktID epic.PktID, s *slayers.SCION, timestamp uint32,
230257

231258
// Calculate a multiple of 16 such that the input fits in
232259
nrBlocks := int(math.Ceil((float64(23) + float64(l)) / float64(16)))
260+
// (VerifiedSCION) The following assumptions cannot be currently proven due to Gobra's incomplete
261+
// support for floats.
262+
// @ assume 23 + l <= nrBlocks * 16
263+
// @ assume nrBlocks * 16 <= 23 + l + 16
233264
inputLength := 16 * nrBlocks
234265

235266
// Fill input
@@ -263,9 +294,24 @@ func prepareMacInput(pktID epic.PktID, s *slayers.SCION, timestamp uint32,
263294
// @ &inputBuffer[offset:][i] == &inputBuffer[offset+i]
264295
binary.BigEndian.PutUint16(inputBuffer[offset:], s.PayloadLen)
265296
offset += 2
297+
// @ assert offset == 23 + l
298+
// @ assert offset <= inputLength
299+
// @ assert inputLength <= len(inputBuffer)
266300
// @ assert forall i int :: { &inputBuffer[offset:inputLength][i] } 0 <= i && i < len(inputBuffer[offset:inputLength]) ==>
267301
// @ &inputBuffer[offset:inputLength][i] == &inputBuffer[offset+i]
268-
copy(inputBuffer[offset:inputLength], zeroInitVector[:] /*@ , R20 @*/)
302+
// @ assert forall i int :: { &inputBuffer[offset:inputLength][i] } 0 <= i && i < len(inputBuffer[offset:inputLength]) ==>
303+
// @ acc(&inputBuffer[offset:inputLength][i])
304+
// @ establishPostInitInvariant()
305+
// @ unfold acc(postInitInvariant(), _)
306+
// @ assert acc(sl.AbsSlice_Bytes(zeroInitVector[:], 0, 16), _)
307+
// (VerifiedSCION) From the package invariant, we learn that we have a wildcard access to zeroInitVector.
308+
// Unfortunately, it is not possible to call `copy` with a wildcard amount, even though
309+
// that would be perfectly fine. The spec of `copy` would need to be adapted to allow for that case.
310+
// @ inhale acc(sl.AbsSlice_Bytes(zeroInitVector[:], 0, len(zeroInitVector[:])), R55)
311+
// @ unfold acc(sl.AbsSlice_Bytes(zeroInitVector[:], 0, len(zeroInitVector[:])), R55)
312+
// @ assert forall i int :: { &zeroInitVector[:][i] } 0 <= i && i < len(zeroInitVector[:]) ==>
313+
// @ &zeroInitVector[:][i] == &zeroInitVector[i]
314+
copy(inputBuffer[offset:inputLength], zeroInitVector[:] /*@ , R55 @*/)
269315
// @ fold sl.AbsSlice_Bytes(inputBuffer, 0, len(inputBuffer))
270316
return inputLength, nil
271317
}

pkg/slayers/scion_spec.gobra

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,18 @@ func (s *SCION) UBPath(ub []byte) []byte {
281281
ub[CmnHdrLen+s.AddrHdrLenSpecInternal() : s.HdrLen*LineLen]
282282
}
283283

284+
ghost
285+
pure
286+
requires acc(s.Mem(ub), _)
287+
decreases
288+
func (s *SCION) UBScionPath(ub []byte) []byte {
289+
return unfolding acc(s.Mem(ub), _) in
290+
let ubPath := ub[CmnHdrLen+s.AddrHdrLenSpecInternal() : s.HdrLen*LineLen] in
291+
typeOf(s.Path) == *epic.Path ?
292+
unfolding acc(s.Path.Mem(ubPath), _) in ubPath[epic.MetadataLen:] :
293+
ubPath
294+
}
295+
284296
ghost
285297
pure
286298
requires acc(s.Mem(ub), _)
@@ -297,6 +309,26 @@ func (s *SCION) PathEndIdx(ub []byte) int {
297309
return unfolding acc(s.Mem(ub), _) in int(s.HdrLen*LineLen)
298310
}
299311

312+
ghost
313+
pure
314+
requires acc(s.Mem(ub), _)
315+
decreases
316+
func (s *SCION) PathScionStartIdx(ub []byte) int {
317+
return unfolding acc(s.Mem(ub), _) in
318+
let offset := CmnHdrLen+s.AddrHdrLenSpecInternal() in
319+
typeOf(s.Path) == *epic.Path ?
320+
offset + epic.MetadataLen :
321+
offset
322+
}
323+
324+
ghost
325+
pure
326+
requires acc(s.Mem(ub), _)
327+
decreases
328+
func (s *SCION) PathScionEndIdx(ub []byte) int {
329+
return unfolding acc(s.Mem(ub), _) in int(s.HdrLen*LineLen)
330+
}
331+
300332
ghost
301333
requires 0 < p
302334
preserves acc(s.Mem(ub), p)
@@ -317,6 +349,19 @@ func (s *SCION) GetPath(ub []byte) path.Path {
317349
return unfolding acc(s.Mem(ub), _) in s.Path
318350
}
319351

352+
ghost
353+
pure
354+
requires acc(s.Mem(ub), _)
355+
decreases
356+
func (s *SCION) GetScionPath(ub []byte) path.Path {
357+
return unfolding acc(s.Mem(ub), _) in (
358+
typeOf(s.Path) == *epic.Path ?
359+
(let ubPath := ub[CmnHdrLen+s.AddrHdrLenSpecInternal() : s.HdrLen*LineLen] in
360+
unfolding acc(s.Path.Mem(ubPath), _) in
361+
(path.Path)(s.Path.(*epic.Path).ScionPath)) :
362+
s.Path)
363+
}
364+
320365
ghost
321366
requires acc(s.Mem(ub), _)
322367
decreases

verification/dependencies/crypto/aes/cipher.gobra

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ package aes
1010

1111
import "crypto/cipher"
1212
import "github.com/scionproto/scion/verification/utils/slices"
13+
import . "github.com/scionproto/scion/verification/utils/definitions"
1314

1415
// The AES block size in bytes.
1516
const BlockSize = 16
@@ -19,10 +20,13 @@ const BlockSize = 16
1920
// either 16, 24, or 32 bytes to select
2021
// AES-128, AES-192, or AES-256.
2122
trusted
22-
requires len(key) == 16 || len(key) == 24 || len(key) == 32
23-
preserves slices.AbsSlice_Bytes(key, 0, len(key))
23+
preserves acc(slices.AbsSlice_Bytes(key, 0, len(key)), R50)
2424
ensures err == nil ==>
25-
(result != nil && result.Mem() && result.BlockSize() == len(key))
25+
len(key) == 16 || len(key) == 24 || len(key) == 32
26+
ensures err == nil ==>
27+
(result != nil &&
28+
result.Mem() &&
29+
result.BlockSize() == len(key))
2630
ensures err != nil ==> err.ErrorMem()
2731
decreases
2832
func NewCipher(key []byte) (result cipher.Block, err error) {

verification/dependencies/crypto/cipher/cbc.gobra

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@ import "github.com/scionproto/scion/verification/utils/slices"
2121
// mode, using the given Block. The length of iv must be the same as the
2222
// Block's block size.
2323
trusted
24-
requires b != nil && b.Mem()
25-
requires len(iv) == b.BlockSize()
26-
requires slices.AbsSlice_Bytes(iv, 0, len(iv))
27-
ensures result.Mem()
24+
requires b != nil && b.Mem()
25+
requires len(iv) == b.BlockSize()
26+
preserves acc(slices.AbsSlice_Bytes(iv, 0, len(iv)), _)
27+
ensures result != nil && result.Mem()
28+
ensures result.BlockSize() == old(b.BlockSize())
2829
decreases _
2930
func NewCBCEncrypter(b Block, iv []byte) (result BlockMode) {
3031
if len(iv) != b.BlockSize() {

verification/dependencies/crypto/cipher/cipher.gobra

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,10 @@ type BlockMode interface {
9494
// maintains state and does not reset at each CryptBlocks call.
9595
requires len(src) <= len(dst)
9696
preserves Mem()
97-
preserves slices.AbsSlice_Bytes(dst, 0, len(dst))
97+
preserves acc(slices.AbsSlice_Bytes(dst, 0, len(dst)), 1 - R10)
98+
preserves dst !== src ==> acc(slices.AbsSlice_Bytes(dst, 0, len(dst)), R10)
9899
preserves acc(slices.AbsSlice_Bytes(src, 0, len(src)), R10)
100+
ensures BlockSize() == old(BlockSize())
99101
decreases
100102
CryptBlocks(dst, src []byte)
101103
}

0 commit comments

Comments
 (0)