Пример #1
0
        internal static void ApplySignature(ProtectedTask task, byte[] data, byte[] signature)
        {
            task.Signature = CryptoServices.EncodePrefixed(HashType.Signature, signature);

            using (var buffer = new MemoryStream())
            {
                buffer.Write(data, 0, data.Length);
                buffer.Write(signature, 0, signature.Length);

                task.SignedOperation = buffer.ToArray().ToHexString();
            }
        }
Пример #2
0
        // 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);
            }
Пример #3
0
        private void ValidateOperation(ProtectedTask task, byte[] operationData)
        {
            if (configuration.InTestMode)
            {
                return;
            }

            void Fail(string reason)
            {
                var exception = new ApplicationException($"Validation | {reason}");

                Trace(exception);
                throw exception;
            }

            var parsed = new ParsedOperation(operationData);

            // Only main and service item
            if (parsed.Transfers.Count != 2)
            {
                Fail("Too many transfers in operation");
            }

            // Service fee as offered to user
            var serviceTransfer = parsed.Transfers[1];

            if (serviceTransfer.Amount != task.ServiceFee)
            {
                Fail("Wrong service fee");
            }

            // Network fee as offered to user
            var mainTransfer = parsed.Transfers[0];

            if (mainTransfer.Fee != task.NetworkFee)
            {
                Fail("Wrong network fee");
            }

            // Transfer amount
            if (mainTransfer.Amount != task.TransferAmount)
            {
                Fail("Wrong amount");
            }

            // Task specific validation
            switch (task)
            {
            case TransferTask transfer:
            {
                if (mainTransfer.Kind != "Transfer")
                {
                    Fail("Wrong task kind");
                }

                if (mainTransfer.DestinationID != transfer.DestinationID)
                {
                    Fail("Wrong transfer destination");
                }
            }
            break;

            case CreateContractTask originate:
            {
                if (mainTransfer.Kind != "Origination")
                {
                    Fail("Wrong task kind");
                }
            }
            break;

            default:
                break;
            }
        }