@@ -15,29 +15,19 @@ use std::{
1515 fs:: { self , File , OpenOptions } ,
1616 io:: { Read as _, Write as _} ,
1717 net:: { Ipv4Addr , TcpListener , TcpStream } ,
18- os:: unix:: {
19- fs:: { OpenOptionsExt as _, PermissionsExt as _} ,
20- net:: { UnixListener , UnixStream } ,
21- } ,
18+ os:: unix:: fs:: { OpenOptionsExt as _, PermissionsExt as _} ,
2219 path:: { Path , PathBuf } ,
2320 process:: { Child , Command , Output , Stdio } ,
24- sync:: {
25- atomic:: { AtomicBool , Ordering } ,
26- Arc , OnceLock ,
27- } ,
21+ sync:: OnceLock ,
2822 thread,
2923 time:: { Duration , Instant } ,
3024} ;
3125
32- use base64:: {
33- engine:: general_purpose:: { STANDARD , URL_SAFE_NO_PAD } ,
34- Engine as _,
35- } ;
26+ use base64:: { engine:: general_purpose:: URL_SAFE_NO_PAD , Engine as _} ;
3627use chrono:: Utc ;
37- use p256:: ecdsa:: {
38- signature:: { hazmat:: PrehashSigner as _, Signer as _} ,
39- Signature , SigningKey ,
40- } ;
28+ use p256:: ecdsa:: { signature:: Signer as _, Signature , SigningKey } ;
29+ use registry_platform_crypto:: document_signature:: test_support:: TestTransit ;
30+ use registry_platform_crypto:: PrivateJwk ;
4131use serde_json:: { json, Value } ;
4232
4333const TOKEN_AUDIENCE : & str = "registry-evidence-production-test" ;
@@ -756,23 +746,6 @@ impl Drop for DevStopGuard {
756746 }
757747}
758748
759- struct TransitServer {
760- stop : Arc < AtomicBool > ,
761- socket : PathBuf ,
762- worker : Option < thread:: JoinHandle < ( ) > > ,
763- }
764-
765- impl Drop for TransitServer {
766- fn drop ( & mut self ) {
767- self . stop . store ( true , Ordering :: Release ) ;
768- let _ = UnixStream :: connect ( & self . socket ) ;
769- if let Some ( worker) = self . worker . take ( ) {
770- worker. join ( ) . expect ( "synthetic Transit server" ) ;
771- }
772- let _ = fs:: remove_file ( & self . socket ) ;
773- }
774- }
775-
776749struct SourceProxy {
777750 docker : PathBuf ,
778751 container_id : String ,
@@ -984,41 +957,27 @@ impl Fixture {
984957 proxy
985958 }
986959
987- fn start_transit ( & self ) -> TransitServer {
988- let socket = self . root . join ( "transit-proxy.sock" ) ;
989- let listener = UnixListener :: bind ( & socket) . expect ( "bind synthetic Transit socket" ) ;
990- listener
991- . set_nonblocking ( true )
992- . expect ( "synthetic Transit nonblocking listener" ) ;
993- let stop = Arc :: new ( AtomicBool :: new ( false ) ) ;
994- let worker_stop = Arc :: clone ( & stop) ;
995- let root = self . root . clone ( ) ;
996- let worker = thread:: spawn ( move || {
997- while !worker_stop. load ( Ordering :: Acquire ) {
998- match listener. accept ( ) {
999- Ok ( ( mut stream, _) ) => {
1000- if worker_stop. load ( Ordering :: Acquire ) {
1001- break ;
1002- }
1003- // Darwin inherits O_NONBLOCK from the listener; wait for
1004- // each complete request within the bounded read timeout.
1005- stream
1006- . set_nonblocking ( false )
1007- . expect ( "synthetic Transit blocking accepted stream" ) ;
1008- serve_transit_request ( & root, & mut stream) ;
1009- }
1010- Err ( error) if error. kind ( ) == std:: io:: ErrorKind :: WouldBlock => {
1011- thread:: sleep ( Duration :: from_millis ( 5 ) ) ;
1012- }
1013- Err ( error) => panic ! ( "synthetic Transit accept failed: {error}" ) ,
1014- }
1015- }
1016- } ) ;
1017- TransitServer {
1018- stop,
1019- socket,
1020- worker : Some ( worker) ,
1021- }
960+ /// Serve the Evidence signing key the staged target binds, version 1 of
961+ /// `evidence-signing` on the `transit` mount, at the target's Transit
962+ /// socket. The key is the one target staging generated, so stage the
963+ /// target first.
964+ fn start_transit ( & self ) -> TestTransit {
965+ let key: PrivateJwk = serde_json:: from_slice (
966+ & fs:: read (
967+ self . root
968+ . join ( "transit-evidence-key/signing-p256-private-jwk" ) ,
969+ )
970+ . expect ( "Evidence Transit fixture private JWK" ) ,
971+ )
972+ . expect ( "Evidence Transit fixture private JWK parses" ) ;
973+ TestTransit :: start (
974+ & self . root . join ( "transit-proxy.sock" ) ,
975+ "transit" ,
976+ "evidence-signing" ,
977+ 1 ,
978+ & key,
979+ )
980+ . expect ( "start the test Transit proxy" )
1022981 }
1023982
1024983 fn stage_authoring_project ( & self ) {
@@ -1888,209 +1847,6 @@ impl Drop for Fixture {
18881847 }
18891848}
18901849
1891- fn serve_transit_request ( root : & Path , stream : & mut UnixStream ) {
1892- stream
1893- . set_read_timeout ( Some ( Duration :: from_secs ( 2 ) ) )
1894- . expect ( "synthetic Transit read timeout" ) ;
1895- let response = read_transit_request ( stream) . and_then ( |( method, path, body) | {
1896- if method == "GET" {
1897- let key_name = path
1898- . strip_prefix ( "/v1/transit/keys/" )
1899- . ok_or ( "unsupported Transit metadata path" ) ?;
1900- transit_metadata ( root, key_name)
1901- } else if method == "POST" {
1902- let key_name = path
1903- . strip_prefix ( "/v1/transit/sign/" )
1904- . and_then ( |path| path. strip_suffix ( "/sha2-256" ) )
1905- . ok_or ( "unsupported Transit signing path" ) ?;
1906- transit_signature ( root, key_name, & body)
1907- } else {
1908- Err ( "unsupported Transit method" )
1909- }
1910- } ) ;
1911- let ( status, document) = match response {
1912- Ok ( document) => ( 200 , document) ,
1913- Err ( message) => ( 400 , json ! ( { "errors" : [ message] } ) ) ,
1914- } ;
1915- let bytes = serde_json:: to_vec ( & document) . expect ( "synthetic Transit response JSON" ) ;
1916- let reason = if status == 200 { "OK" } else { "Bad Request" } ;
1917- write ! (
1918- stream,
1919- "HTTP/1.1 {status} {reason}\r \n Content-Type: application/json\r \n Content-Length: {}\r \n Connection: close\r \n \r \n " ,
1920- bytes. len( )
1921- )
1922- . expect ( "synthetic Transit response headers" ) ;
1923- stream
1924- . write_all ( & bytes)
1925- . expect ( "synthetic Transit response body" ) ;
1926- }
1927-
1928- fn read_transit_request (
1929- stream : & mut UnixStream ,
1930- ) -> Result < ( String , String , Vec < u8 > ) , & ' static str > {
1931- let mut request = Vec :: new ( ) ;
1932- let header_end = loop {
1933- let mut chunk = [ 0_u8 ; 4096 ] ;
1934- let read = stream
1935- . read ( & mut chunk)
1936- . map_err ( |_| "Transit request could not be read" ) ?;
1937- if read == 0 {
1938- return Err ( "Transit request ended before its headers" ) ;
1939- }
1940- request. extend_from_slice ( & chunk[ ..read] ) ;
1941- if request. len ( ) > 128 * 1024 {
1942- return Err ( "Transit request exceeded the fixture bound" ) ;
1943- }
1944- if let Some ( position) = request. windows ( 4 ) . position ( |bytes| bytes == b"\r \n \r \n " ) {
1945- break position + 4 ;
1946- }
1947- } ;
1948- let headers = std:: str:: from_utf8 ( & request[ ..header_end] )
1949- . map_err ( |_| "Transit request headers were not UTF-8" ) ?;
1950- let mut lines = headers. lines ( ) ;
1951- let mut request_line = lines
1952- . next ( )
1953- . ok_or ( "Transit request line was missing" ) ?
1954- . split_whitespace ( ) ;
1955- let method = request_line
1956- . next ( )
1957- . ok_or ( "Transit request method was missing" ) ?
1958- . to_owned ( ) ;
1959- let path = request_line
1960- . next ( )
1961- . ok_or ( "Transit request path was missing" ) ?
1962- . to_owned ( ) ;
1963- let content_length = lines
1964- . filter_map ( |line| line. split_once ( ':' ) )
1965- . find ( |( name, _) | name. eq_ignore_ascii_case ( "content-length" ) )
1966- . map ( |( _, value) | value. trim ( ) . parse :: < usize > ( ) )
1967- . transpose ( )
1968- . map_err ( |_| "Transit content length was invalid" ) ?
1969- . unwrap_or ( 0 ) ;
1970- if content_length > 64 * 1024 {
1971- return Err ( "Transit request body exceeded the fixture bound" ) ;
1972- }
1973- while request. len ( ) < header_end + content_length {
1974- let mut chunk = [ 0_u8 ; 4096 ] ;
1975- let read = stream
1976- . read ( & mut chunk)
1977- . map_err ( |_| "Transit request body could not be read" ) ?;
1978- if read == 0 {
1979- return Err ( "Transit request ended before its body" ) ;
1980- }
1981- request. extend_from_slice ( & chunk[ ..read] ) ;
1982- }
1983- Ok ( (
1984- method,
1985- path,
1986- request[ header_end..header_end + content_length] . to_vec ( ) ,
1987- ) )
1988- }
1989-
1990- fn transit_metadata ( root : & Path , key_name : & str ) -> Result < Value , & ' static str > {
1991- let private = transit_private_jwk ( root, key_name) ?;
1992- let public_key = transit_public_pem ( & private) ?;
1993- Ok ( json ! ( {
1994- "data" : {
1995- "type" : "ecdsa-p256" ,
1996- "derived" : false ,
1997- "exportable" : false ,
1998- "allow_plaintext_backup" : false ,
1999- "supports_signing" : true ,
2000- "latest_version" : 1 ,
2001- "min_encryption_version" : 1 ,
2002- "keys" : { "1" : { "public_key" : public_key} }
2003- }
2004- } ) )
2005- }
2006-
2007- fn transit_signature ( root : & Path , key_name : & str , body : & [ u8 ] ) -> Result < Value , & ' static str > {
2008- let private = transit_private_jwk ( root, key_name) ?;
2009- let request: Value =
2010- serde_json:: from_slice ( body) . map_err ( |_| "Transit signing JSON invalid" ) ?;
2011- if request[ "key_version" ] != "1"
2012- || request[ "marshaling_algorithm" ] != "jws"
2013- || request[ "prehashed" ] != true
2014- {
2015- return Err ( "Transit signing contract invalid" ) ;
2016- }
2017- let digest = request[ "input" ]
2018- . as_str ( )
2019- . ok_or ( "Transit signing input missing" )
2020- . and_then ( |input| {
2021- STANDARD
2022- . decode ( input)
2023- . map_err ( |_| "Transit signing input invalid" )
2024- } ) ?;
2025- if digest. len ( ) != 32 {
2026- return Err ( "Transit signing digest invalid" ) ;
2027- }
2028- let secret = private[ "d" ]
2029- . as_str ( )
2030- . ok_or ( "Transit private JWK scalar missing" )
2031- . and_then ( |value| {
2032- URL_SAFE_NO_PAD
2033- . decode ( value)
2034- . map_err ( |_| "Transit private JWK scalar invalid" )
2035- } ) ?;
2036- let key = SigningKey :: from_slice ( & secret) . map_err ( |_| "Transit private JWK invalid" ) ?;
2037- let signature: Signature = key
2038- . sign_prehash ( & digest)
2039- . map_err ( |_| "Transit signing failed" ) ?;
2040- Ok ( json ! ( {
2041- "data" : {
2042- "signature" : format!( "vault:v1:{}" , URL_SAFE_NO_PAD . encode( signature. to_bytes( ) ) )
2043- }
2044- } ) )
2045- }
2046-
2047- fn transit_private_jwk ( root : & Path , key_name : & str ) -> Result < Value , & ' static str > {
2048- let path = match key_name {
2049- "evidence-signing" => root. join ( "transit-evidence-key/signing-p256-private-jwk" ) ,
2050- _ => return Err ( "unknown Transit key" ) ,
2051- } ;
2052- let bytes = fs:: read ( path) . map_err ( |_| "Transit fixture key unavailable" ) ?;
2053- serde_json:: from_slice ( & bytes) . map_err ( |_| "Transit fixture key invalid" )
2054- }
2055-
2056- fn transit_public_pem ( private : & Value ) -> Result < String , & ' static str > {
2057- let x = private[ "x" ]
2058- . as_str ( )
2059- . ok_or ( "Transit public JWK x missing" )
2060- . and_then ( |value| {
2061- URL_SAFE_NO_PAD
2062- . decode ( value)
2063- . map_err ( |_| "Transit public JWK x invalid" )
2064- } ) ?;
2065- let y = private[ "y" ]
2066- . as_str ( )
2067- . ok_or ( "Transit public JWK y missing" )
2068- . and_then ( |value| {
2069- URL_SAFE_NO_PAD
2070- . decode ( value)
2071- . map_err ( |_| "Transit public JWK y invalid" )
2072- } ) ?;
2073- if x. len ( ) != 32 || y. len ( ) != 32 {
2074- return Err ( "Transit public JWK coordinate invalid" ) ;
2075- }
2076- let mut spki = vec ! [
2077- 0x30 , 0x59 , 0x30 , 0x13 , 0x06 , 0x07 , 0x2a , 0x86 , 0x48 , 0xce , 0x3d , 0x02 , 0x01 , 0x06 , 0x08 ,
2078- 0x2a , 0x86 , 0x48 , 0xce , 0x3d , 0x03 , 0x01 , 0x07 , 0x03 , 0x42 , 0x00 , 0x04 ,
2079- ] ;
2080- spki. extend_from_slice ( & x) ;
2081- spki. extend_from_slice ( & y) ;
2082- let encoded = STANDARD . encode ( spki) ;
2083- let body = encoded
2084- . as_bytes ( )
2085- . chunks ( 64 )
2086- . map ( |line| std:: str:: from_utf8 ( line) . expect ( "base64 is ASCII" ) )
2087- . collect :: < Vec < _ > > ( )
2088- . join ( "\n " ) ;
2089- Ok ( format ! (
2090- "-----BEGIN PUBLIC KEY-----\n {body}\n -----END PUBLIC KEY-----\n "
2091- ) )
2092- }
2093-
20941850fn read_json_document ( path : & Path ) -> Value {
20951851 serde_norway:: from_slice (
20961852 & fs:: read ( path) . unwrap_or_else ( |error| panic ! ( "read {}: {error}" , path. display( ) ) ) ,
0 commit comments