// The signing function _____________________________________________________________________ internal async Task <bool> Sign(ProtectedTask task, Identity signingIdentity) { Approval approval = null; // Find signing provider var provider = signProviders .FirstOrDefault(p => p.Contains(signingIdentity.AccountID)); if (provider == null) { Trace($"No provider found for {signingIdentity}"); return(false); } Trace($"Operation: {task.Operation}"); // Fetch operation data to sign later. // This prevents tampering with the operation during approval var operationData = task.Operation.HexToByteArray(); // Check validity of operation data // This protects against forgery due to a compromised node ValidateOperation(task, operationData); // Approval mechanism registered? if (ApprovalRequired != null) { approval = new Approval { Task = task, Signer = signingIdentity, Timeout = ApprovalTimeout, }; // Send approval to UI and wait for completion await ExecuteSynchronized(() => { ApprovalRequired(approval); }); WaitForUser: Trace("Wait for user decision"); var userResult = await approval.GetUserResult(); Trace($"Approval result: {userResult}"); if (userResult != SigningResult.Approved) { // Cancel or timeout approval.Close(userResult); return(false); } // Unlock identity if needed if (signingIdentity.IsLocked) { if (!signingIdentity.Unlock(approval.Passphrase)) { // Wrong credentials approval.Retry(SigningResult.InvalidCredentials); goto WaitForUser; } } } // Sign Trace("Sign CreateRequest"); var dataToHash = new byte[1 + operationData.Length]; dataToHash[0] = 3; operationData.CopyTo(dataToHash, 1); var hashedData = CryptoServices.Hash(dataToHash, 32); if (await provider.Sign(signingIdentity.AccountID, hashedData, out byte[] signature)) { // Success task.Signature = CryptoServices.EncodePrefixed(HashType.Signature, signature); using (var buffer = new MemoryStream()) { buffer.Write(operationData, 0, operationData.Length); buffer.Write(signature, 0, signature.Length); task.SignedOperation = buffer.ToArray().ToHexString(); } approval?.Close(SigningResult.Signed); return(true); }