## Problem

Encrypting a wallet through JSON RPC or Qt *appears to* allow a user to include null characters in the passphrase, but silently ignores everything including and after the first null character.

For instance (on regtest), trying to set a passphrase of "`a{null character}b`":

`curl --user __cookie__ --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "encryptwallet", "params": ["a\u0000b"]}' -H 'content-type: text/plain;' http://127.0.0.1:18443/`

This will succeed, but allow the user to unlock with the passphrase "`a`", instead of the expected full passphrase (which also works):

`curl --user __cookie__ --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "walletpassphrase", "params": ["a",10]}' -H 'content-type: text/plain;' http://127.0.0.1:18443/`

I am also able to replicate it in Qt on my macOS machine by running `printf 'a\0b' | pbcopy` and pasting the result into the passphrase dialog. 

My main concern is a user thinking that they're generating, say, 32 random bytes as a passphrase, and if they're unlucky and get a zero in the first few bytes, it unexpectedly cuts their security down to almost nothing. 

## Root Cause

The reason is due to our `SecureString` type. `SecureString` is a `std::string` specialization with a secure allocator. However, when assigned, it's treated like a C- string (no explicit length and null-terminated). See the [original PR](https://github.com/bitcoin/bitcoin/pull/666) for more details.

## Potential Solutions

I think there are two plausible approaches to take. The first (and my preference) is to allow and support null characters, and I will submit a PR that enables that (by making `SecureString` use the entire string). The second is to explicitly reject any passphrases that contain null characters.

One significant complication may be that, if anyone is *already* using a passphrase with a null, then my first solution would stop their wallet from unlocking. However, it would still be unlockable just by trimming the null and any subsequent characters.
for (size_t i = 0; i < mtx.vout.size(); ++i) {
            const auto& txout = mtx.vout[i];
            bool is_mine = 0 < (wallet.IsMine(txout) & filter);
            changes += new_utxos[COutPoint(hash, i)] = is_mine ? txout.nValue : 0;
        }
    }
    UniValue result(UniValue::VOBJ);
    result.pushKV("balance_change", ValueFromAmount(changes));
    return result;
}
    };
}
static RPCHelpMan migratewallet()
{
    return RPCHelpMan{"migratewallet",
        "EXPERIMENTAL warning: This call may not work as expected and may be changed in future releases\n"
        "\nMigrate the wallet to a descriptor wallet.\n"
        "A new wallet backup will need to be made.\n"
        "\nThe migration process will create a backup of the wallet before migrating. This backup\n"
        "file will be named <wallet name>-<timestamp>.legacy.bak and can be found in the directory\n"
        "for this wallet. In the event of an incorrect migration, the backup can be restored using restorewallet."
        "\nEncrypted wallets must have the passphrase provided as an argument to this call.",
        {
            {"wallet_name", RPCArg::Type::STR, RPCArg::DefaultHint{"the wallet name from the RPC endpoint"}, "The name of the wallet to migrate. If provided both here and in the RPC endpoint, the two must be identical."},
            {"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "The wallet passphrase"},
        },
        RPCResult{
            RPCResult::Type::OBJ, "", "",
            {
                {RPCResult::Type::STR, "wallet_name", "The name of the primary migrated wallet"},