How to overcome 'salt must be a byte string error'? - python-3.x

Good morning,
I'm trying to recover my Multibit HD seed words using decrypt_bitcoinj_seed.py I don't know if anyone has used decrypt_bitcoinj_seed.py or not but I'm getting an error after it runs another python script in its arsenal called common.py with an error:
raise TypeError('salt must be a byte string')
There are many opportunities for errors to be thrown is this python script, so if this one is overcome there could be others to follow. (Please see code at the bottom.) The code below shows the salt error.
C:\Python38\decrypt_bitcoinj_seed-master>py decrypt_bitcoinj_seed.py
Traceback (most recent call last):
File "decrypt_bitcoinj_seed.py", line 319, in <module>
wallet = load_wallet(wallet_file, get_password)
File "decrypt_bitcoinj_seed.py", line 132, in load_wallet
key = pylibscrypt.scrypt(password.encode('utf_16_be'), salt, olen=32)
File "C:\Python38\lib\site-packages\pylibscrypt\hashlibscrypt.py", line 49, in scrypt
check_args(password, salt, N, r, p, olen)
File "C:\Python38\lib\site-packages\pylibscrypt\common.py", line 49, in check_args
raise TypeError('salt must be a byte string')
TypeError: salt must be a byte string
Does anyone know how the salt would be edited so it becomes a byte string or is there a better alternative? Also, not that it matters since I have very little programming knowledge, why does the script need all the following to crack the seed words? - password, salt, N, r, p, olen
The good news is the "check args" password script part passed. Here's the code I mentioned earlier about all the possibilities for errors to be thrown:
if not isinstance(password, bytes):
raise TypeError('password must be a byte string')
if not isinstance(salt, bytes):
raise TypeError('salt must be a byte string')
if not isinstance(N, numbers.Integral):
raise TypeError('N must be an integer')
if not isinstance(r, numbers.Integral):
raise TypeError('r must be an integer')
if not isinstance(p, numbers.Integral):
raise TypeError('p must be an integer')
if not isinstance(olen, numbers.Integral):
raise TypeError('length must be an integer')
if N > 2**63:
raise ValueError('N cannot be larger than 2**63')
if (N & (N - 1)) or N < 2:
raise ValueError('N must be a power of two larger than 1')
if r <= 0:
raise ValueError('r must be positive')
if p <= 0:
raise ValueError('p must be positive')
if r * p >= 2**30:
raise ValueError('r * p must be less than 2 ** 30')
if olen <= 0:
raise ValueError('length must be positive')
If other errors arise, I will post them here; but I'm hoping it will just work and return my seed words!

Prelude
I guess I've come up with the solution not to help myself only but to help someone one day. Had the same issue long time ago. As I understood from the question:
the main goal is to recover a seed (12-words phrase) which you will always need in order to restore your wallet and to get a full access to it, in particular sending funds (don't confuse with watch-only wallet mode like if you were using public key / wallet address to recover the wallet);
a solution does not have to be related with use of Python.
I dedicate this answer to all my brothers in misfortune who have wallets in MultibitHD which is no longer supported (Multibit is Deprecated - Do Not Use) and was abandonded with its unresolved issues like:
unconfirmed transactions;
frequent wallet repairing;
synchronization issues;
errors when entering the correct password;
backup failures etc.
Guide
We'll use the special utility which is stored as mbexport in npm (Node package manager) registry. Install mbexport package globally via npm which is installable with Node.js (npm install -g mbexport).
Next, you need to determine the path to the MultibitHD wallet file in your file system.
Generally, for Windows it is:
C:/Users/username/AppData/Roaming/MultiBitHD/wallet-id/mbhd.wallet.aes
For MacOS it is:
~/Library/Application Support/MultiBitHD/wallet-id/mbhd.wallet.aes
Where username is your Windows user, wallet-id is unique wallet identifier (starts with mbhd-)
A little hint: you can find out the path in MultibitHD application, if you can enter the wallet and know the password that you might have set. It's quite convenient especially when you have several wallets. Just navigate to Manage wallet -> Wallet dashboard.
Then you need to open a command prompt and enter the command below. path-to-wallet-file is the previously found path to the wallet.
Please note: I personally did not get the error Error opening wallet file only when I dragged the wallet file from the explorer right into the command prompt so that it became a full valid path. Also I recommend you turning off the Internet while getting the seed. Although I haven't found any issues in the source code of mbexport that dealt with sending sensitive data accross the Internet, it's a simple security matter.
mbexport path-to-wallet-file
Congratulations, you have finally got the seed! Don't share it with anyone and make sure you keep it in safe and wrote it on a physical paper as it is your key to your funds!
Restoring your wallet from the seed
Although it is not part of the question, you will probably want to know how you can restore your wallet with the seed, for example, in Electrum. Follow this guide Restoring your MultibitHD Wallet in Electrum and keep in mind that the seed you got is in obsolete BIP39 format, the derivation path in MultibitHD is m/0' and the type of address is p2pkh.

Related

Opening .hoc files in neuron simulator + "not a mechanism" problem

I am trying to run a NEURON simulation via python. I got all the libraries in order and am able to run some simple simulations, but am experiencing some troubles with a more complicated code. If you have any idea how to help I will appreciate it very much
Problem number 1:
Neuron doesn't open part of a .hoc file even though it is compiled. I get the error:
NEURON: Can't open import3d/import3d_sec.hoc
in import3d.hoc near line 1
{xopen("import3d/import3d_sec.hoc")}
^
xopen("import3d/i...")
xopen("import3d.hoc")
execute1("{xopen("im...")
load_file("C:/Users/U...")
Problem number 2:
The simulator doesn't recognize a mechanism I am trying to use. here I am a bit lost and don't know to describe further, but this is the error message:
NEURON: Im is not a MECHANISM
in L5PCbiophys5b.hoc near line 26
insert Im
^
xopen("L5PCbiophy...")
execute1("{xopen("L5...")
load_file("C:/Users/U...")
Problem number 3:
Not recognizing as a template:
NEURON: Import3d_Neurolucida3 is not a template
in L5PCtemplate_2.hoc near line 26
nl = new Import3d_Neurolucida3()
^
xopen("L5PCtempla...")
execute1("{xopen("L5...")
load_file("C:/Users/U...")
You can try to use an absolute path
The name for insert should match with the SUFFIX statement in the file; also make sure that file was compiled in and that the dll is loaded (should be a message when you start nrniv)
Perhaps a result of the file xopen problem? If it is a template-containing file you should use load_file() instead of xopen()

How Do I resolve "Illuminate\Queue\InvalidPayloadException: Unable to JSON encode payload. Error code: 5"

Trying out the queue system for a better user upload experience with Laravel-Excel.
.env was been changed from 'sync' to 'database' and migrations run. All the necessary use statements are in place yet the error above persists.
The exact error happens here:
Illuminate\Queue\Queue.php:97
$payload = json_encode($this->createPayloadArray($job, $queue, $data));
if (JSON_ERROR_NONE !== json_last_error()) {
throw new InvalidPayloadException(
If I drop ShouldQueue, the file imports perfectly in-session (large file so long wait period for user.)
I've read many stackoverflow, github etc comments on this but I don't have the technical skills to deep-dive to fix my particular situation (most of them speak of UTF-8 but I don't if that's an issue here; I changed the excel save format to UTF-8 but it didn't fix it.)
Ps. Whilst running the migration, I got the error:
SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes (SQL: alter table `jobs` add index `jobs_queue_index`(`queue`))
I bypassed by dropping the 'add index'; so my jobs table is not indexed on queue but I don't feel this is the cause.
One thing you can do when looking into json_encode() errors is use the json_last_error_msg() function, which will give you a bit more of a readable error message.
In your case you're getting a '5' back, which is the JSON_ERROR_UTF8 error code. The error message back for this is a slightly more informative one:
'Malformed UTF-8 characters, possibly incorrectly encoded'
So we know it's encountering non-UTF-8 characters, even though you're saving the file specifically with UTF-8 encoding. At first glance you might think you need to convert the encoding yourself in code (like this answer), but in this case, I don't think that'll help. For Laravel-Excel, this seems to be a limitation of trying to queue-read .xls files - from the Laravel-Excel docs:
You currently cannot queue xls imports. PhpSpreadsheet's Xls reader contains some non-utf8 characters, which makes it impossible to queue.
In this case you might be stuck with a slow, non-queueable option, or need to convert your spreadsheet into a queueable format e.g. .csv.
The key length error on running the migration is unrelated. It has been around for a while and is a side-effect of using an older version of MySQL/MariaDB. Check out this answer and the Laravel documentation around index lengths - you need to add this to your AppServiceProvider::boot() method:
Schema::defaultStringLength(191);

What is the correct method to determine if a system user exists locally on windows?

I am working on an authentication system for a local server jupyterhub that relies on OAuth protocol. Additionally, it creates a local system user on windows, in case it does not exist.
What is the correct way to check whether a user exists on windows platforms using python?
This would include cases in which the system uses LDAP authentication and the user logged in the machine at least once.
I am looking for the correct windows alternative to the unix-like:
import pwd
try:
pwd.getpwnam(user.name)
except Exception as e:
print(repr(e))
My current solution is to check for the existence of the f"os.environ["SystemDrive"]\Users\{username}" folder. Side question, is there any drawback with the current method?
Here's a solution to checking if a local Windows user exists using python:
import subprocess
def local_user_exists_windows(username):
r = subprocess.run("net user",stdout=subprocess.PIPE)
#look for username in the output. Return carriage followed by line break followed by name, then space
return f"\\r\\n{username.lower()} " in str(r.stdout).lower()
Alternative is to use a regular expression to find username match (^ is regex for beginning of line if used in conjunction with multiline, \b for word boundary):
import re
re.findall(rf"^{username}\b", out,flags=re.MULTILINE | re.IGNORECASE)
Note that the \b could be replaced with \s+ meaning a space character one or more times and yield similar results. The function above will return True if given user name is an exact match with local username on Windows.
Again, my reason for this solution is there might be drawback to checking whether the path f"os.environ["SystemDrive"]\Users\{username}" exists. For example, I have a case where a Local User (e.g,local_username) exists via the net user command or via looking at "Local Users and Groups" control panel, but there is no C:\Users\local_user_name folder. One reason for this I can think of off the top of my head is perhaps the user switched from logging in as a Local User to using a Domain Account, and their User folder was deleted to save space, so the User exists, but the folder does not, etc.)
The call to net user gets local users - and the output looks something like this:
User accounts for \\SOME-WINDOWS-COMPUTER
-------------------------------------------------------------------------------
SomeUser Administrator DefaultAccount
Guest local_admin WDAGUtilityAccount
Notice how the SomeUser in this example is preceded by a \r\n followed by multiple spaces, hence looking for a username string inside this string could yield a false positive if the string you are searching is contained inside another string.
The solution above works for me, but has been tested all of ten minutes, and there might be some other simpler or more pythonic way of doing this.

IMAP fetch() returns command error: BAD [b' Command Argument Error. 12']

I'm having trouble finding examples/troubleshooting tips online, and am not quite sure that I'm interpreting the documentation correctly. Any assistance would be greatly appreciated.
I'm connecting to an e-mail server, and want to read the e-mail subjects, and bodies. I first make my connection like so:
import imaplib
c = imaplib.IMAP4_SSL(hostname, port)
c.login(username, password)
foldername = 'INBOX/SSR'
c.select(str.encode(foldername), readonly = True)
today = datetime.date.today().strftime('%d-%b-%Y')
searchcriteria = '(SENTON '{}')'.format(today)
typ, msg_ids = c.search(None, searchcriteria)
msg_ids = [s.decode('ascii') for s in msg_ids]
for idnumber in msg_ids:
print(c.fetch(idnumber, "(BODY.PEEK[HEADER])"))
The code and works and output looks as expected, up until the last line, at which point, I get
imaplib.error: FETCH command error: BAD [b' Command Argument Error. 12']
My line of thought, and subsequent testing examined the following possible issues:
bytes vs. string. I converted input back to bytes, but the error remained constant
improper syntax: I tried other commands, such as BODY, SUBJECT, and ENVELOPE but still got the same message.
I'm not sure how to interpret the error, and don't really know where to start. Referencing https://www.rfc-editor.org/rfc/rfc3501.html from pp. 102+, I noticed that the values are labeled differently, but don't understand what the issue is with my implementation. How should I interpret the error? What is wrong with my syntax?
P.S. Correct me if I'm wrong, but the c.search shouldn't change my directory, yes? As in, by selecting foldername, I "navigate" to the selected folder, but just searching only returns values and shouldn't change my location?
I encountered the same problem while I tried to list or select a new mailbox - BAD [b' Command Argument Error. 12'], in my case, it didn't work with “Sent Box”, but it worked well with “Outbox”, so the space symbol is the point.
So it worked with c.select('"{}"'.format("Sent Box")...
Hope this information could help you.
Your last line is not correct msg_ids = [s.decode('ascii') for s in msg_ids]
msg_ids is a list with bytes string, not with elements of a list - example: [b'123 124 125']
Change the last line into msg_ids = msg_ids[0].split(b' ') and it will work as expected.

Protect user credentials when connecting R with databases using JDBC/ODBC drivers

Usually I connect to a database with R using JDBC/ODBC driver. A typical code would look like
library(RJDBC)
vDriver = JDBC(driverClass="com.vertica.jdbc.Driver", classPath="/home/Drivers/vertica-jdbc-7.0.1-0.jar")
vertica = dbConnect(vDriver, "jdbc:vertica://servername:5433/db", "username", "password")
I would like others to access the db using my credentials but I want to protect my username and password. So I plan save the above script as a "Connections.r" file and ask users to source this file.
source("/opt/mount1/Connections.r")
If I give execute only permission to Connections.r others cannot source the file
chmod 710 Connections.r
Only if I give read and execute permission R lets users to source it. If I give the read permission my credentials will be exposed. Is there anyways we could solve this by protecting user credentials?
Unless you were to deeply obfuscate your credentials by making an Rcpp function or package that does the initial JDBC connection (which won't be trivial) one of your only lighter obfuscation mechanisms is to store your credentials in a file and have your sourced R script read them from the file, use them in the call and them rm them from the environment right after that call. That will still expose them, but not directly.
One other way, since the users have their own logins to RStudio Server, is to use Hadley's new secure package (a few of us sec folks are running it through it's paces), add the user keys and have your credentials stored encrypted but have your sourced R script auto-decrypt them. You'll still need to do the rm of any variables you use since they'll be part of environment if you don't.
A final way, since you're giving them access to the data anyway, is to use a separate set of credentials (the way you phrased the question it seems you're using your credentials for this) that only work in read-only mode to the databases & tables required for these analyses. That way, it doesn't matter if the creds leak since there's nothing "bad" that can be done with them.
Ultimately, I'm as confused as to why you can't just setup the users with read only permissions on the database side? That's what role-based access controls are for. It's administrative work, but it's absolutely the right thing to do.
Do you want to give someone access, but not have them be able to see your credentials? That's not possible in this case. If my code can read a file, I can see everything in the file.
Make more accounts on the SQL server. Or make one guest account. But you're trying to solve the problem that account management solves.
Have the credentials sent as command arguments? Here's an example of how one would do that:
suppressPackageStartupMessages(library("argparse"))
# create parser object
parser <- ArgumentParser()
# specify our desired options
# by default ArgumentParser will add an help option
parser$add_argument("-v", "--verbose", action="store_true", default=TRUE,
help="Print extra output [default]")
parser$add_argument("-q", "--quietly", action="store_false",
dest="verbose", help="Print little output")
parser$add_argument("-c", "--count", type="integer", default=5,
help="Number of random normals to generate [default %(default)s]",
metavar="number")
parser$add_argument("--generator", default="rnorm",
help = "Function to generate random deviates [default \"%(default)s\"]")
parser$add_argument("--mean", default=0, type="double", help="Mean if generator == \"rnorm\" [default %(default)s]")
parser$add_argument("--sd", default=1, type="double",
metavar="standard deviation",
help="Standard deviation if generator == \"rnorm\" [default %(default)s]")
# get command line options, if help option encountered print help and exit,
# otherwise if options not found on command line then set defaults,
args <- parser$parse_args()
# print some progress messages to stderr if "quietly" wasn't requested
if ( args$verbose ) {
write("writing some verbose output to standard error...\n", stderr())
}
# do some operations based on user input
if( args$generator == "rnorm") {
cat(paste(rnorm(args$count, mean=args$mean, sd=args$sd), collapse="\n"))
} else {
cat(paste(do.call(args$generator, list(args$count)), collapse="\n"))
}
cat("\n")
Sample run (no parameters):
usage: example.R [-h] [-v] [-q] [-c number] [--generator GENERATOR] [--mean MEAN] [--sd standard deviation]
optional arguments:
-h, --help show this help message and exit
-v, --verbose Print extra output [default]
-q, --quietly Print little output
-c number, --count number
Number of random normals to generate [default 5]
--generator GENERATOR
Function to generate random deviates [default "rnorm"]
--mean MEAN Mean if generator == "rnorm" [default 0]
--sd standard deviation
Standard deviation if generator == "rnorm" [default 1]
The package was apparently inspired by the python package of the same name, so looking there may also be useful.
Looking at your code, I'd probably rewrite it as follows:
library(RJDBC)
library(argparse)
args <- ArgumentParser()
args$add_argument('--driver', dest='driver', default="com.vertica.jdbc.Driver")
args$add_argument('--classPath', dest='classPath', default="/home/Drivers/vertica-jdbc-7.0.1-0.jar")
args$add_argument('--url', dest='url', default="jdbc:vertica://servername:5433/db")
args$add_argument('--user', dest='user', default='username')
args$add_argument('--password', dest='password', default='password')
parser <- args$parse_args
vDriver <- JDBC(driverClass=parser$driver, parser$classPath)
vertica <- dbConnect(vDriver, parser$url, parser$user , parser$password)
# continue here
Jana, it seems odd that you are willing to let the users connect via R but not in any other way. How is that obscuring anything from them?
I don't understand why you would not be satisfied with a guest account that has specific SELECT-only access to certain tables (or even views)?

Resources