|
| 1 | +use crate::post_compute::errors::{PostComputeError, ReplicateStatusCause::*}; |
| 2 | +use crate::utils::hash_utils::{concatenate_and_hash, hex_string_to_byte_array}; |
| 3 | +use alloy_signer::{Signature, SignerSync}; |
| 4 | +use alloy_signer_local::PrivateKeySigner; |
| 5 | +use std::env; |
| 6 | + |
| 7 | +const SIGN_WORKER_ADDRESS: &str = "SIGN_WORKER_ADDRESS"; |
| 8 | +const SIGN_TEE_CHALLENGE_PRIVATE_KEY: &str = "SIGN_TEE_CHALLENGE_PRIVATE_KEY"; |
| 9 | + |
| 10 | +/// Signs a message hash using the provided enclave challenge private key. |
| 11 | +/// |
| 12 | +/// This function takes a message hash in hexadecimal string format, converts it to a byte array, |
| 13 | +/// and signs it using the provided private key. The resulting signature is then converted back |
| 14 | +/// to a string representation. |
| 15 | +/// |
| 16 | +/// # Arguments |
| 17 | +/// |
| 18 | +/// * `message_hash` - A hexadecimal string representing the hash to be signed |
| 19 | +/// * `enclave_challenge_private_key` - A string containing the private key used for signing |
| 20 | +/// |
| 21 | +/// # Returns |
| 22 | +/// |
| 23 | +/// * `Ok(String)` - The signature as a hexadecimal string if successful |
| 24 | +/// * `Err(PostComputeError)` - An error if the private key is invalid or if signing fails |
| 25 | +/// |
| 26 | +/// # Errors |
| 27 | +/// |
| 28 | +/// This function will return an error in the following situations: |
| 29 | +/// * The provided private key cannot be parsed as a valid `PrivateKeySigner` (returns `PostComputeInvalidEnclaveChallengePrivateKey`) |
| 30 | +/// * The signing operation fails (returns `PostComputeInvalidTeeSignature`) |
| 31 | +/// |
| 32 | +/// # Example |
| 33 | +/// |
| 34 | +/// ``` |
| 35 | +/// let message_hash = "0x5cd0e9c5180dd35e2b8285d0db4ded193a9b4be6fbfab90cbadccecab130acad"; |
| 36 | +/// let private_key = "0xdd3b993ec21c71c1f6d63a5240850e0d4d8dd83ff70d29e49247958548c1d479"; |
| 37 | +/// |
| 38 | +/// match sign_enclave_challenge(message_hash, private_key) { |
| 39 | +/// Ok(signature) => println!("Signature: {}", signature), |
| 40 | +/// Err(e) => eprintln!("Error: {:?}", e), |
| 41 | +/// } |
| 42 | +/// ``` |
| 43 | +pub fn sign_enclave_challenge( |
| 44 | + message_hash: &str, |
| 45 | + enclave_challenge_private_key: &str, |
| 46 | +) -> Result<String, PostComputeError> { |
| 47 | + let signer: PrivateKeySigner = enclave_challenge_private_key |
| 48 | + .parse::<PrivateKeySigner>() |
| 49 | + .map_err(|_| PostComputeError::new(PostComputeInvalidEnclaveChallengePrivateKey))?; |
| 50 | + |
| 51 | + let signature: Signature = signer |
| 52 | + .sign_message_sync(&hex_string_to_byte_array(message_hash)) |
| 53 | + .map_err(|_| PostComputeError::new(PostComputeInvalidTeeSignature))?; |
| 54 | + |
| 55 | + Ok(signature.to_string()) |
| 56 | +} |
| 57 | + |
| 58 | +/// Generates a challenge signature for a given chain task ID. |
| 59 | +/// |
| 60 | +/// This function retrieves the worker address and TEE challenge private key from the environment, |
| 61 | +/// then creates a message hash by concatenating and hashing the chain task ID and worker address. |
| 62 | +/// Finally, it signs this message hash with the private key. |
| 63 | +/// |
| 64 | +/// # Arguments |
| 65 | +/// |
| 66 | +/// * `chain_task_id` - A string identifier for the chain task |
| 67 | +/// |
| 68 | +/// # Returns |
| 69 | +/// |
| 70 | +/// * `Ok(String)` - The challenge signature as a hexadecimal string if successful |
| 71 | +/// * `Err(PostComputeError)` - An error if required environment variables are missing or if signing fails |
| 72 | +/// |
| 73 | +/// # Errors |
| 74 | +/// |
| 75 | +/// This function will return an error in the following situations: |
| 76 | +/// * The worker address environment variable is missing (returns `PostComputeWorkerAddressMissing`) |
| 77 | +/// * The TEE challenge private key environment variable is missing (returns `PostComputeTeeChallengePrivateKeyMissing`) |
| 78 | +/// * The signing operation fails (returns `PostComputeInvalidTeeSignature`) |
| 79 | +/// |
| 80 | +/// # Environment Variables |
| 81 | +/// |
| 82 | +/// * `SIGN_WORKER_ADDRESS` - The worker's address used in message hash calculation |
| 83 | +/// * `SIGN_TEE_CHALLENGE_PRIVATE_KEY` - The private key used for signing the challenge |
| 84 | +/// |
| 85 | +/// # Example |
| 86 | +/// |
| 87 | +/// ``` |
| 88 | +/// // Assuming the necessary environment variables are set: |
| 89 | +/// // SIGN_WORKER_ADDRESS=0xabcdef123456789 |
| 90 | +/// // SIGN_TEE_CHALLENGE_PRIVATE_KEY=0xdd3b993ec21c71c1f6d63a5240850e0d4d8dd83ff70d29e49247958548c1d479 |
| 91 | +/// |
| 92 | +/// let chain_task_id = "0x123456789abcdef"; |
| 93 | +/// |
| 94 | +/// match challenge(chain_task_id) { |
| 95 | +/// Ok(signature) => println!("Challenge signature: {}", signature), |
| 96 | +/// Err(e) => eprintln!("Error generating challenge: {:?}", e), |
| 97 | +/// } |
| 98 | +/// ``` |
| 99 | +pub fn get_challenge(chain_task_id: &str) -> Result<String, PostComputeError> { |
| 100 | + let worker_address: String = match env::var(SIGN_WORKER_ADDRESS) { |
| 101 | + Ok(val) => val, |
| 102 | + Err(_) => Err(PostComputeError::new(PostComputeWorkerAddressMissing))?, |
| 103 | + }; |
| 104 | + let tee_challenge_private_key = match env::var(SIGN_TEE_CHALLENGE_PRIVATE_KEY) { |
| 105 | + Ok(val) => val, |
| 106 | + Err(_) => Err(PostComputeError::new( |
| 107 | + PostComputeTeeChallengePrivateKeyMissing, |
| 108 | + ))?, |
| 109 | + }; |
| 110 | + let message_hash: String = concatenate_and_hash(&[chain_task_id, &worker_address]); |
| 111 | + sign_enclave_challenge(&message_hash, &tee_challenge_private_key) |
| 112 | +} |
| 113 | + |
| 114 | +#[cfg(test)] |
| 115 | +mod tests { |
| 116 | + use super::*; |
| 117 | + use temp_env::with_vars; |
| 118 | + |
| 119 | + const CHAIN_TASK_ID: &str = "0x123456789abcdef"; |
| 120 | + const WORKER_ADDRESS: &str = "0xabcdef123456789"; |
| 121 | + const ENCLAVE_CHALLENGE_PRIVATE_KEY: &str = |
| 122 | + "0xdd3b993ec21c71c1f6d63a5240850e0d4d8dd83ff70d29e49247958548c1d479"; |
| 123 | + const MESSAGE_HASH: &str = "0x5cd0e9c5180dd35e2b8285d0db4ded193a9b4be6fbfab90cbadccecab130acad"; |
| 124 | + const EXPECTED_SIGNATURE: &str = "0xfcc6bce5eb04284c2eb1ed14405b943574343b1abda33628fbf94a374b18dd16541c6ebf63c6943d8643ff03c7aa17f1cb17b0a8d297d0fd95fc914bdd0e85f81b"; |
| 125 | + |
| 126 | + #[test] |
| 127 | + fn should_sign_enclave_challenge() { |
| 128 | + let result = sign_enclave_challenge(MESSAGE_HASH, ENCLAVE_CHALLENGE_PRIVATE_KEY); |
| 129 | + assert!(result.is_ok(), "Signing should succeed with valid inputs"); |
| 130 | + assert_eq!( |
| 131 | + result.unwrap(), |
| 132 | + EXPECTED_SIGNATURE, |
| 133 | + "The signature should match the expected value exactly" |
| 134 | + ); |
| 135 | + } |
| 136 | + |
| 137 | + #[test] |
| 138 | + fn should_not_sign_enclave_challenge_with_invalid_key() { |
| 139 | + let invalid_key = "invalid_private_key"; |
| 140 | + let result = sign_enclave_challenge(MESSAGE_HASH, invalid_key); |
| 141 | + assert!( |
| 142 | + matches!( |
| 143 | + result, |
| 144 | + Err(ref err) if err.exit_cause == PostComputeInvalidEnclaveChallengePrivateKey |
| 145 | + ), |
| 146 | + "Should return missing TEE challenge private key error" |
| 147 | + ); |
| 148 | + } |
| 149 | + |
| 150 | + #[test] |
| 151 | + fn should_get_challenge() { |
| 152 | + with_vars( |
| 153 | + vec![ |
| 154 | + (SIGN_WORKER_ADDRESS, Some(WORKER_ADDRESS)), |
| 155 | + ( |
| 156 | + SIGN_TEE_CHALLENGE_PRIVATE_KEY, |
| 157 | + Some(ENCLAVE_CHALLENGE_PRIVATE_KEY), |
| 158 | + ), |
| 159 | + ], |
| 160 | + || { |
| 161 | + let expected_message_hash = concatenate_and_hash(&[CHAIN_TASK_ID, WORKER_ADDRESS]); |
| 162 | + let expected_signature = |
| 163 | + sign_enclave_challenge(&expected_message_hash, ENCLAVE_CHALLENGE_PRIVATE_KEY) |
| 164 | + .unwrap(); |
| 165 | + |
| 166 | + let result = get_challenge(CHAIN_TASK_ID); |
| 167 | + assert!( |
| 168 | + result.is_ok(), |
| 169 | + "get_challenge should succeed with valid environment variables" |
| 170 | + ); |
| 171 | + let signature = result.unwrap(); |
| 172 | + assert_eq!( |
| 173 | + signature, expected_signature, |
| 174 | + "The challenge signature should match expected value" |
| 175 | + ); |
| 176 | + }, |
| 177 | + ); |
| 178 | + } |
| 179 | + |
| 180 | + #[test] |
| 181 | + fn should_fail_on_missing_worker_address_env_var() { |
| 182 | + with_vars( |
| 183 | + vec![ |
| 184 | + (SIGN_WORKER_ADDRESS, None), |
| 185 | + ( |
| 186 | + SIGN_TEE_CHALLENGE_PRIVATE_KEY, |
| 187 | + Some(ENCLAVE_CHALLENGE_PRIVATE_KEY), |
| 188 | + ), |
| 189 | + ], |
| 190 | + || { |
| 191 | + let result = get_challenge(CHAIN_TASK_ID); |
| 192 | + assert!( |
| 193 | + matches!( |
| 194 | + result, |
| 195 | + Err(ref err) if err.exit_cause == PostComputeWorkerAddressMissing |
| 196 | + ), |
| 197 | + "Should return missing worker address error" |
| 198 | + ); |
| 199 | + }, |
| 200 | + ); |
| 201 | + } |
| 202 | + |
| 203 | + #[test] |
| 204 | + fn should_fail_on_missing_private_key_env_var() { |
| 205 | + with_vars( |
| 206 | + vec![ |
| 207 | + (SIGN_WORKER_ADDRESS, Some(WORKER_ADDRESS)), |
| 208 | + (SIGN_TEE_CHALLENGE_PRIVATE_KEY, None), |
| 209 | + ], |
| 210 | + || { |
| 211 | + let result = get_challenge(CHAIN_TASK_ID); |
| 212 | + assert!( |
| 213 | + matches!( |
| 214 | + result, |
| 215 | + Err(ref err) if err.exit_cause == PostComputeTeeChallengePrivateKeyMissing |
| 216 | + ), |
| 217 | + "Should return missing private key error" |
| 218 | + ); |
| 219 | + }, |
| 220 | + ); |
| 221 | + } |
| 222 | +} |
0 commit comments