@@ -15,6 +15,8 @@ module A2mliser.ABI.Layout
1515import A2mliser.ABI.Types
1616import Data.Vect
1717import Data.So
18+ import Data.Nat
19+ import Decidable.Equality
1820
1921%default total
2022
@@ -28,7 +30,7 @@ paddingFor : (offset : Nat) -> (alignment : Nat) -> Nat
2830paddingFor offset alignment =
2931 if offset `mod` alignment == 0
3032 then 0
31- else alignment - (offset `mod` alignment)
33+ else minus alignment (offset `mod` alignment)
3234
3335||| Proof that alignment divides aligned size
3436public export
@@ -41,11 +43,6 @@ alignUp : (size : Nat) -> (alignment : Nat) -> Nat
4143alignUp size alignment =
4244 size + paddingFor size alignment
4345
44- ||| Proof that alignUp produces aligned result
45- public export
46- alignUpCorrect : (size : Nat ) -> (align : Nat ) -> (align > 0) -> Divides align (alignUp size align)
47- alignUpCorrect size align prf =
48- DivideBy ((size + paddingFor size align) `div` align) Refl
4946
5047-- ------------------------------------------------------------------------------
5148-- Struct Field Layout
@@ -77,7 +74,7 @@ record StructLayout where
7774
7875||| Calculate total struct size with padding
7976public export
80- calcStructSize : Vect n Field -> Nat -> Nat
77+ calcStructSize : Vect k Field -> Nat -> Nat
8178calcStructSize [] align = 0
8279calcStructSize (f :: fs) align =
8380 let lastOffset = foldl (\ acc, field => nextFieldOffset field) f. offset fs
@@ -86,23 +83,38 @@ calcStructSize (f :: fs) align =
8683
8784||| Proof that field offsets are correctly aligned
8885public export
89- data FieldsAligned : Vect n Field -> Type where
86+ data FieldsAligned : Vect k Field -> Type where
9087 NoFields : FieldsAligned []
9188 ConsField :
9289 (f : Field) ->
93- (rest : Vect n Field) ->
90+ (rest : Vect k Field) ->
9491 Divides f.alignment f.offset ->
9592 FieldsAligned rest ->
9693 FieldsAligned (f :: rest)
9794
98- ||| Verify a struct layout is valid
95+ ||| Decide whether `n` divides `m`, returning a Divides witness when it does.
96+ ||| Sound: only returns Just when m is genuinely a multiple of n.
97+ public export
98+ decDivides : (n : Nat ) -> (m : Nat ) -> Maybe (Divides n m)
99+ decDivides Z m = Nothing
100+ decDivides (S k) m =
101+ let q = div m (S k) in
102+ case decEq m (q * (S k)) of
103+ Yes prf => Just (DivideBy q prf)
104+ No _ => Nothing
105+
106+ ||| Verify a struct layout is valid.
107+ ||| Requires both the size obligation and a genuine divisibility witness.
99108public export
100- verifyLayout : (fields : Vect n Field) -> (align : Nat ) -> Either String StructLayout
109+ verifyLayout : (fields : Vect k Field) -> (align : Nat ) -> Either String StructLayout
101110verifyLayout fields align =
102- let size = calcStructSize fields align
103- in case decSo (size >= sum (map (\ f => f. size) fields)) of
104- Yes prf => Right (MkStructLayout fields size align)
105- No _ => Left " Invalid struct size"
111+ let size = calcStructSize fields align in
112+ case decSo (size >= sum (map (\ f => f. size) fields)) of
113+ No _ => Left " Invalid struct size"
114+ Yes prf =>
115+ case decDivides align size of
116+ Nothing => Left " Struct size not aligned"
117+ Just dv => Right (MkStructLayout fields size align {sizeCorrect = prf} {aligned = dv})
106118
107119-- ------------------------------------------------------------------------------
108120-- Attestation Envelope Header Layout
@@ -135,6 +147,8 @@ envelopeHeaderLayout =
135147 ]
136148 32 -- Total size: 32 bytes
137149 8 -- Alignment: 8 bytes
150+ {sizeCorrect = Oh }
151+ {aligned = DivideBy 4 Refl }
138152
139153-- ------------------------------------------------------------------------------
140154-- Digest Buffer Layout
@@ -150,6 +164,8 @@ digestBufferLayout =
150164 ]
151165 32 -- Total size: 32 bytes
152166 1 -- Alignment: 1 byte (byte array)
167+ {sizeCorrect = Oh }
168+ {aligned = DivideBy 32 Refl }
153169
154170-- ------------------------------------------------------------------------------
155171-- Signature Buffer Layout
@@ -164,6 +180,8 @@ ed25519SignatureLayout =
164180 ]
165181 64 -- Total size: 64 bytes
166182 1 -- Alignment: 1 byte
183+ {sizeCorrect = Oh }
184+ {aligned = DivideBy 64 Refl }
167185
168186||| Layout for an Ed448 signature buffer (114 bytes).
169187public export
@@ -174,6 +192,8 @@ ed448SignatureLayout =
174192 ]
175193 114 -- Total size: 114 bytes (no padding needed for byte arrays)
176194 1 -- Alignment: 1 byte
195+ {sizeCorrect = Oh }
196+ {aligned = DivideBy 114 Refl }
177197
178198-- ------------------------------------------------------------------------------
179199-- Provenance Chain Entry Layout
@@ -201,6 +221,8 @@ provenanceEntryLayout =
201221 ]
202222 56 -- Total size: 56 bytes
203223 8 -- Alignment: 8 bytes
224+ {sizeCorrect = Oh }
225+ {aligned = DivideBy 7 Refl }
204226
205227-- ------------------------------------------------------------------------------
206228-- Platform-Specific Layouts
@@ -234,21 +256,52 @@ data CABICompliant : StructLayout -> Type where
234256 FieldsAligned layout.fields ->
235257 CABICompliant layout
236258
259+ ||| Decide, soundly, whether every field's offset is aligned to its own
260+ ||| alignment, building a genuine FieldsAligned witness when so.
261+ public export
262+ decFieldsAligned : (fields : Vect k Field) -> Maybe (FieldsAligned fields)
263+ decFieldsAligned [] = Just NoFields
264+ decFieldsAligned (f :: fs) =
265+ case decDivides f. alignment f. offset of
266+ Nothing => Nothing
267+ Just dv =>
268+ case decFieldsAligned fs of
269+ Nothing => Nothing
270+ Just rest => Just (ConsField f fs dv rest)
271+
237272||| Check if layout follows C ABI
238273public export
239274checkCABI : (layout : StructLayout) -> Either String (CABICompliant layout)
240275checkCABI layout =
241- Right (CABIOk layout ? fieldsAlignedProof)
276+ case decFieldsAligned layout. fields of
277+ Just fa => Right (CABIOk layout fa)
278+ Nothing => Left " Struct fields are not C-ABI aligned"
242279
243- ||| Proof that envelope header layout is C ABI compliant
280+ ||| Proof that envelope header layout is C ABI compliant.
281+ ||| Each field offset is a multiple of its alignment, witnessed directly.
244282export
245- envelopeHeaderCABI : CABICompliant envelopeHeaderLayout
246- envelopeHeaderCABI = CABIOk envelopeHeaderLayout ? envelopeHeaderFieldsAligned
247-
248- ||| Proof that provenance entry layout is C ABI compliant
283+ envelopeHeaderCABI : CABICompliant Layout.envelopeHeaderLayout
284+ envelopeHeaderCABI =
285+ CABIOk Layout . envelopeHeaderLayout
286+ (ConsField _ _ (DivideBy 0 Refl )
287+ (ConsField _ _ (DivideBy 1 Refl )
288+ (ConsField _ _ (DivideBy 2 Refl )
289+ (ConsField _ _ (DivideBy 3 Refl )
290+ (ConsField _ _ (DivideBy 2 Refl )
291+ (ConsField _ _ (DivideBy 6 Refl )
292+ (ConsField _ _ (DivideBy 7 Refl )
293+ NoFields )))))))
294+
295+ ||| Proof that provenance entry layout is C ABI compliant.
249296export
250- provenanceEntryCABI : CABICompliant provenanceEntryLayout
251- provenanceEntryCABI = CABIOk provenanceEntryLayout ? provenanceEntryFieldsAligned
297+ provenanceEntryCABI : CABICompliant Layout.provenanceEntryLayout
298+ provenanceEntryCABI =
299+ CABIOk Layout . provenanceEntryLayout
300+ (ConsField _ _ (DivideBy 0 Refl )
301+ (ConsField _ _ (DivideBy 4 Refl )
302+ (ConsField _ _ (DivideBy 5 Refl )
303+ (ConsField _ _ (DivideBy 6 Refl )
304+ NoFields ))))
252305
253306-- ------------------------------------------------------------------------------
254307-- Offset Calculation
@@ -262,7 +315,12 @@ fieldOffset layout name =
262315 Just idx => Just (finToNat idx ** index idx layout. fields)
263316 Nothing => Nothing
264317
265- ||| Proof that field offset is within struct bounds
318+ ||| Decide whether a field lies within the struct bounds.
319+ ||| The universally-quantified form is unsound (false for fields that do not
320+ ||| belong to the layout), so this returns a Maybe witness via `choose`.
266321public export
267- offsetInBounds : (layout : StructLayout) -> (f : Field) -> So (f.offset + f.size <= layout.totalSize)
268- offsetInBounds layout f = ? offsetInBoundsProof
322+ offsetInBounds : (layout : StructLayout) -> (f : Field) -> Maybe (So (f.offset + f.size <= layout.totalSize))
323+ offsetInBounds layout f =
324+ case choose (f. offset + f. size <= layout. totalSize) of
325+ Left ok => Just ok
326+ Right _ => Nothing
0 commit comments