@@ -1477,4 +1477,107 @@ ii+31DW+YulmysZKQKDvuk96TARuWMO/vDbhk777a2QF3bgNoIj8UPMwnw==
14771477 let result = req. set_message_signature_sync ( & signature_params, & secret_key, None ) ;
14781478 assert ! ( result. is_err( ) , "expected Err when using `@status` on request, got Ok" ) ;
14791479 }
1480+
1481+ // ---- RFC 9421 §2.2: Derived component extraction values ----
1482+
1483+ #[ test]
1484+ fn test_extract_derived_components_values ( ) {
1485+ let req = build_query_request ( ) ;
1486+ // URI: https://example.com/path?foo=bar&id=123&x=y
1487+ let req_or_res = RequestOrResponse :: Request ( & req) ;
1488+
1489+ // @method (§2.2.1)
1490+ let id = HttpMessageComponentId :: try_from ( "@method" ) . unwrap ( ) ;
1491+ let c = extract_derived_component ( & req_or_res, & id) . unwrap ( ) ;
1492+ assert_eq ! ( c. to_string( ) , "\" @method\" : GET" ) ;
1493+
1494+ // @target-uri (§2.2.2)
1495+ let id = HttpMessageComponentId :: try_from ( "@target-uri" ) . unwrap ( ) ;
1496+ let c = extract_derived_component ( & req_or_res, & id) . unwrap ( ) ;
1497+ assert_eq ! ( c. to_string( ) , "\" @target-uri\" : https://example.com/path?foo=bar&id=123&x=y" ) ;
1498+
1499+ // @authority (§2.2.3)
1500+ let id = HttpMessageComponentId :: try_from ( "@authority" ) . unwrap ( ) ;
1501+ let c = extract_derived_component ( & req_or_res, & id) . unwrap ( ) ;
1502+ assert_eq ! ( c. to_string( ) , "\" @authority\" : example.com" ) ;
1503+
1504+ // @scheme (§2.2.4)
1505+ let id = HttpMessageComponentId :: try_from ( "@scheme" ) . unwrap ( ) ;
1506+ let c = extract_derived_component ( & req_or_res, & id) . unwrap ( ) ;
1507+ assert_eq ! ( c. to_string( ) , "\" @scheme\" : https" ) ;
1508+
1509+ // @path (§2.2.6)
1510+ let id = HttpMessageComponentId :: try_from ( "@path" ) . unwrap ( ) ;
1511+ let c = extract_derived_component ( & req_or_res, & id) . unwrap ( ) ;
1512+ assert_eq ! ( c. to_string( ) , "\" @path\" : /path" ) ;
1513+
1514+ // @query (§2.2.7)
1515+ let id = HttpMessageComponentId :: try_from ( "@query" ) . unwrap ( ) ;
1516+ let c = extract_derived_component ( & req_or_res, & id) . unwrap ( ) ;
1517+ assert_eq ! ( c. to_string( ) , "\" @query\" : ?foo=bar&id=123&x=y" ) ;
1518+
1519+ // @query-param;name="id" (§2.2.8)
1520+ let id = HttpMessageComponentId :: try_from ( "\" @query-param\" ;name=\" id\" " ) . unwrap ( ) ;
1521+ let c = extract_derived_component ( & req_or_res, & id) . unwrap ( ) ;
1522+ assert_eq ! ( c. to_string( ) , "\" @query-param\" ;name=\" id\" : 123" ) ;
1523+ }
1524+
1525+ // ---- RFC 9421 §2.4: @query-param;name="...";req on response ----
1526+
1527+ #[ tokio:: test]
1528+ async fn test_response_with_query_param_req_sign_verify ( ) {
1529+ // Build a request with query params
1530+ let req = build_query_request ( ) ;
1531+ // Build a response
1532+ let body = Full :: new ( bytes:: Bytes :: new ( ) ) . map_err ( |never| match never { } ) . boxed ( ) ;
1533+ let mut res: Response < BoxBody > = Response :: builder ( ) . status ( 200 ) . body ( body) . unwrap ( ) ;
1534+
1535+ // Response signature covering @status + @query-param;name="id";req
1536+ let covered = [ "@status" , "\" @query-param\" ;name=\" id\" ;req" ] ;
1537+ let covered_components = covered
1538+ . iter ( )
1539+ . map ( |v| HttpMessageComponentId :: try_from ( * v) )
1540+ . collect :: < Result < Vec < _ > , _ > > ( )
1541+ . unwrap ( ) ;
1542+
1543+ let secret_key = SecretKey :: from_pem ( & AlgorithmName :: Ed25519 , EDDSA_SECRET_KEY ) . unwrap ( ) ;
1544+ let mut signature_params = HttpSignatureParams :: try_new ( & covered_components) . unwrap ( ) ;
1545+ signature_params. set_key_info ( & secret_key) ;
1546+
1547+ res
1548+ . set_message_signature ( & signature_params, & secret_key, None , Some ( & req) )
1549+ . await
1550+ . unwrap ( ) ;
1551+
1552+ assert ! ( req. headers( ) . get( "signature-input" ) . is_none( ) , "request should not be modified" ) ;
1553+ assert ! ( res. headers( ) . get( "signature-input" ) . is_some( ) , "signature-input header is missing on response" ) ;
1554+ assert ! ( res. headers( ) . get( "signature" ) . is_some( ) , "signature header is missing on response" ) ;
1555+
1556+ let public_key = PublicKey :: from_pem ( & AlgorithmName :: Ed25519 , EDDSA_PUBLIC_KEY ) . unwrap ( ) ;
1557+ let verification_res = res. verify_message_signature ( & public_key, None , Some ( & req) ) . await ;
1558+ assert ! ( verification_res. is_ok( ) , "signature verification failed: {:?}" , verification_res. err( ) ) ;
1559+ }
1560+
1561+ // ---- RFC 9421: Response must reject request-derived components without `req` ----
1562+
1563+ #[ tokio:: test]
1564+ async fn test_response_rejects_derived_component_without_req ( ) {
1565+ let body = Full :: new ( bytes:: Bytes :: new ( ) ) . map_err ( |never| match never { } ) . boxed ( ) ;
1566+ let mut res: Response < BoxBody > = Response :: builder ( ) . status ( 200 ) . body ( body) . unwrap ( ) ;
1567+
1568+ // `@method` without `req` on a response — must fail
1569+ let covered = vec ! [
1570+ HttpMessageComponentId :: try_from( "@status" ) . unwrap( ) ,
1571+ HttpMessageComponentId :: try_from( "@method" ) . unwrap( ) ,
1572+ ] ;
1573+
1574+ let secret_key = SecretKey :: from_pem ( & AlgorithmName :: Ed25519 , EDDSA_SECRET_KEY ) . unwrap ( ) ;
1575+ let mut signature_params = HttpSignatureParams :: try_new ( & covered) . unwrap ( ) ;
1576+ signature_params. set_key_info ( & secret_key) ;
1577+
1578+ let result = res
1579+ . set_message_signature ( & signature_params, & secret_key, None , None as Option < & Request < ( ) > > )
1580+ . await ;
1581+ assert ! ( result. is_err( ) , "expected Err when using `@method` without `req` on response" ) ;
1582+ }
14801583}
0 commit comments