@@ -56,7 +56,7 @@ TEST(StatelessResetToken, Basic) {
5656
5757 CHECK_EQ (token, token2);
5858
59- // Let's pretend out secret is also a token just for the sake
59+ // Let's pretend our secret is also a token just for the sake
6060 // of the test. That's ok because they're the same length.
6161 StatelessResetToken token3 (secret);
6262
@@ -85,6 +85,83 @@ TEST(StatelessResetToken, Basic) {
8585 CHECK_EQ (found->second , token);
8686}
8787
88+ TEST (StatelessResetToken, Ngtcp2StructIntegration) {
89+ uint8_t secret[] = {1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 0 , 1 , 2 , 3 , 4 , 5 , 6 };
90+ uint8_t cid_data[] = {1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 0 };
91+ ngtcp2_cid cid_;
92+ ngtcp2_cid_init (&cid_, cid_data, 10 );
93+ TokenSecret fixed_secret (secret);
94+ CID cid (cid_);
95+
96+ // Owning token — generated into the inherited ngtcp2_stateless_reset_token
97+ StatelessResetToken owning (fixed_secret, cid);
98+ CHECK (owning);
99+
100+ // The ngtcp2_stateless_reset_token* conversion operator should return
101+ // a valid pointer to the token data.
102+ const ngtcp2_stateless_reset_token* as_struct = owning;
103+ CHECK_NE (as_struct, nullptr );
104+ // The struct's data should match the uint8_t* conversion.
105+ const uint8_t * as_bytes = owning;
106+ CHECK_EQ (
107+ memcmp (
108+ as_struct->data , as_bytes, StatelessResetToken::kStatelessTokenLen ),
109+ 0 );
110+
111+ // Non-owning from const ngtcp2_stateless_reset_token* — wraps an
112+ // existing struct without copying.
113+ StatelessResetToken from_struct (as_struct);
114+ CHECK (from_struct);
115+ CHECK_EQ (from_struct, owning);
116+ // The pointer should be the same (non-owning wraps, doesn't copy).
117+ const ngtcp2_stateless_reset_token* from_struct_ptr = from_struct;
118+ CHECK_EQ (from_struct_ptr, as_struct);
119+
120+ // Owning into external ngtcp2_stateless_reset_token — generates the
121+ // token into a caller-provided struct.
122+ ngtcp2_stateless_reset_token external_struct{};
123+ StatelessResetToken into_struct (&external_struct, fixed_secret, cid);
124+ CHECK (into_struct);
125+ CHECK_EQ (into_struct, owning);
126+ // The external struct should now contain the generated token.
127+ CHECK_EQ (memcmp (external_struct.data ,
128+ as_bytes,
129+ StatelessResetToken::kStatelessTokenLen ),
130+ 0 );
131+ // The conversion operator should return a pointer to the external struct.
132+ const ngtcp2_stateless_reset_token* into_struct_ptr = into_struct;
133+ CHECK_EQ (into_struct_ptr, &external_struct);
134+
135+ // Copy of an owning token should itself be owning (independent copy).
136+ StatelessResetToken copy_of_owning = owning;
137+ CHECK_EQ (copy_of_owning, owning);
138+ const ngtcp2_stateless_reset_token* copy_ptr = copy_of_owning;
139+ // Should NOT point to the same memory as the original.
140+ CHECK_NE (copy_ptr, as_struct);
141+ // But data should match.
142+ CHECK_EQ (
143+ memcmp (copy_ptr->data , as_bytes, StatelessResetToken::kStatelessTokenLen ),
144+ 0 );
145+
146+ // Copy of a non-owning token should become owning (copies data).
147+ StatelessResetToken copy_of_non_owning = from_struct;
148+ CHECK_EQ (copy_of_non_owning, from_struct);
149+ const ngtcp2_stateless_reset_token* copy_no_ptr = copy_of_non_owning;
150+ // Should NOT point to the original non-owning source.
151+ CHECK_NE (copy_no_ptr, from_struct_ptr);
152+
153+ // kInvalid conversions.
154+ const ngtcp2_stateless_reset_token* invalid_ptr =
155+ StatelessResetToken::kInvalid ;
156+ CHECK_EQ (invalid_ptr, nullptr );
157+ const uint8_t * invalid_bytes = StatelessResetToken::kInvalid ;
158+ // When ptr_ is null, falls back to inherited data (zeroed).
159+ uint8_t zeroed[StatelessResetToken::kStatelessTokenLen ]{};
160+ CHECK_EQ (
161+ memcmp (invalid_bytes, zeroed, StatelessResetToken::kStatelessTokenLen ),
162+ 0 );
163+ }
164+
88165TEST (RetryToken, Basic) {
89166 auto & random = CID::Factory::random ();
90167 TokenSecret secret;
0 commit comments