@@ -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
112124func 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
142172func 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
183212func 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
205232func 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}
0 commit comments