@@ -20,6 +20,10 @@ contract Asn1Harness {
2020 return der.uintAt (der.root ());
2121 }
2222
23+ function uint384AtRoot (bytes memory der ) external pure returns (uint128 hi , uint256 lo ) {
24+ return der.uint384At (der.root ());
25+ }
26+
2327 function timestampAtRoot (bytes memory der ) external pure returns (uint256 ) {
2428 return der.timestampAt (der.root ());
2529 }
@@ -68,6 +72,20 @@ contract Asn1DecodeTest is Test {
6872 assertEq (h.uintAtRoot (hex "0203012345 " ), 0x012345 ); // INTEGER 0x012345
6973 }
7074
75+ function test_uintAt_requiredLeadingZero () public view {
76+ assertEq (h.uintAtRoot (hex "02020080 " ), 0x80 );
77+ }
78+
79+ function test_uintAt_unnecessaryLeadingZero_reverts () public {
80+ vm.expectRevert ("non-canonical INTEGER " );
81+ h.uintAtRoot (hex "0202007f " );
82+ }
83+
84+ function test_uintAt_empty_reverts () public {
85+ vm.expectRevert ("invalid INTEGER length " );
86+ h.uintAtRoot (hex "0200 " );
87+ }
88+
7189 function test_uintAt_notInteger_reverts () public {
7290 vm.expectRevert ("Not type INTEGER " );
7391 h.uintAtRoot (hex "0401ff " ); // OCTET STRING, not INTEGER
@@ -84,6 +102,22 @@ contract Asn1DecodeTest is Test {
84102 h.uintAtRoot (hex "02050000 " ); // claims 5 content bytes, only 2 present
85103 }
86104
105+ function test_uint384At_requiredLeadingZero () public view {
106+ (uint128 hi , uint256 lo ) = h.uint384AtRoot (hex "02020080 " );
107+ assertEq (hi, 0 );
108+ assertEq (lo, 0x80 );
109+ }
110+
111+ function test_uint384At_unnecessaryLeadingZero_reverts () public {
112+ vm.expectRevert ("non-canonical INTEGER " );
113+ h.uint384AtRoot (hex "0202007f " );
114+ }
115+
116+ function test_uint384At_empty_reverts () public {
117+ vm.expectRevert ("invalid INTEGER length " );
118+ h.uint384AtRoot (hex "0200 " );
119+ }
120+
87121 // --- timestampAt ---
88122
89123 function test_timestamp_utcEpoch () public view {
@@ -187,11 +221,36 @@ contract Asn1DecodeTest is Test {
187221 }
188222
189223 function testFuzz_uintAt_positive (uint64 v ) public view {
190- // INTEGER with an explicit 0x00 sign byte so the value is always positive
191- bytes memory der = abi.encodePacked (bytes1 (0x02 ), bytes1 (0x09 ), bytes1 (0x00 ), bytes8 (v));
224+ bytes memory der = _derEncodeUint64 (v);
192225 assertEq (h.uintAtRoot (der), v);
193226 }
194227
228+ function _derEncodeUint64 (uint64 v ) internal pure returns (bytes memory ) {
229+ if (v == 0 ) {
230+ return hex "020100 " ;
231+ }
232+
233+ bytes8 raw = bytes8 (v);
234+ uint256 offset;
235+ while (offset < 8 && raw[offset] == 0 ) {
236+ offset++ ;
237+ }
238+
239+ uint256 len = 8 - offset;
240+ bool needsPad = uint8 (raw[offset]) >= 0x80 ;
241+ bytes memory der = new bytes (2 + len + (needsPad ? 1 : 0 ));
242+ der[0 ] = 0x02 ;
243+ der[1 ] = bytes1 (uint8 (der.length - 2 ));
244+ uint256 dst = 2 ;
245+ if (needsPad) {
246+ der[dst++ ] = 0x00 ;
247+ }
248+ for (uint256 i = offset; i < 8 ; ++ i) {
249+ der[dst++ ] = raw[i];
250+ }
251+ return der;
252+ }
253+
195254 function _utcTime (string memory s ) internal pure returns (bytes memory ) {
196255 bytes memory b = bytes (s);
197256 require (b.length == 13 , "test: UTCTime must be 13 chars " );
0 commit comments