@@ -15,6 +15,7 @@ use dstack_guest_agent_rpc::{
1515 SignRequest , SignResponse , TdxQuoteArgs , TdxQuoteResponse , WorkerVersion ,
1616} ;
1717use dstack_types:: { AppKeys , SysConfig } ;
18+ use ed25519_dalek:: ed25519:: signature:: hazmat:: PrehashSigner ;
1819use ed25519_dalek:: { Signer as Ed25519Signer , SigningKey as Ed25519SigningKey } ;
1920use fs_err as fs;
2021use k256:: ecdsa:: SigningKey ;
@@ -313,6 +314,17 @@ impl DstackGuestRpc for InternalRpcHandler {
313314 self . state . inner . secp256k1_key . sign ( & request. data ) ;
314315 signature. to_bytes ( ) . to_vec ( )
315316 }
317+ "secp256k1_prehashed" => {
318+ if request. data . len ( ) != 32 {
319+ return Err ( anyhow:: anyhow!(
320+ "Pre-hashed signing requires a 32-byte digest, but received {} bytes" ,
321+ request. data. len( )
322+ ) ) ;
323+ }
324+ let signature: k256:: ecdsa:: Signature =
325+ self . state . inner . secp256k1_key . sign_prehash ( & request. data ) ?;
326+ signature. to_bytes ( ) . to_vec ( )
327+ }
316328 _ => return Err ( anyhow:: anyhow!( "Unsupported algorithm" ) ) ,
317329 } ;
318330 Ok ( SignResponse { signature } )
@@ -517,8 +529,10 @@ mod tests {
517529 use crate :: config:: { AppComposeWrapper , Config , Simulator } ;
518530 use dstack_guest_agent_rpc:: { GetAttestationForAppKeyRequest , SignRequest } ;
519531 use dstack_types:: { AppCompose , AppKeys , DockerConfig , KeyProvider } ;
532+ use ed25519_dalek:: ed25519:: signature:: hazmat:: PrehashVerifier ;
520533 use ed25519_dalek:: { Signature as Ed25519Signature , Verifier } ;
521534 use k256:: ecdsa:: { Signature as K256Signature , VerifyingKey } ;
535+ use sha2:: Sha256 ;
522536 use std:: collections:: HashSet ;
523537 use std:: convert:: TryFrom ;
524538
@@ -713,6 +727,53 @@ pNs85uhOZE8z2jr8Pg==
713727 assert ! ( public_key. verify( data_to_sign, & signature) . is_ok( ) ) ;
714728 }
715729
730+ #[ tokio:: test]
731+ async fn test_sign_secp256k1_prehashed_success ( ) {
732+ let state = setup_test_state ( ) . await ;
733+ let handler = InternalRpcHandler {
734+ state : state. clone ( ) ,
735+ } ;
736+ let data_to_sign = b"test message for secp256k1 prehashed" ;
737+
738+ let digest = Sha256 :: digest ( data_to_sign) ;
739+
740+ let request = SignRequest {
741+ algorithm : "secp256k1_prehashed" . to_string ( ) ,
742+ data : digest. to_vec ( ) ,
743+ } ;
744+
745+ let response = handler. sign ( request) . await . unwrap ( ) ;
746+
747+ let public_key = VerifyingKey :: from ( & state. inner . secp256k1_key ) ;
748+ let signature = K256Signature :: try_from ( response. signature . as_slice ( ) ) . unwrap ( ) ;
749+ assert ! ( public_key
750+ . verify_prehash( digest. as_slice( ) , & signature)
751+ . is_ok( ) ) ;
752+ }
753+
754+ #[ tokio:: test]
755+ async fn test_sign_secp256k1_prehashed_invalid_length_fails ( ) {
756+ let state = setup_test_state ( ) . await ;
757+ let handler = InternalRpcHandler {
758+ state : state. clone ( ) ,
759+ } ;
760+
761+ // digest with an invalid length
762+ let invalid_digest = vec ! [ 0 ; 31 ] ;
763+
764+ let request = SignRequest {
765+ algorithm : "secp256k1_prehashed" . to_string ( ) ,
766+ data : invalid_digest,
767+ } ;
768+
769+ let response = handler. sign ( request) . await ;
770+ assert ! ( response. is_err( ) ) ;
771+ assert ! ( response
772+ . unwrap_err( )
773+ . to_string( )
774+ . contains( "requires a 32-byte digest" ) ) ;
775+ }
776+
716777 #[ tokio:: test]
717778 async fn test_sign_unsupported_algorithm_fails ( ) {
718779 let state = setup_test_state ( ) . await ;
0 commit comments