I use the same protocol files, but I find that they have different output in Python and C++.
My protocol file:
namespace serial.proto.api.login;
table LoginReq {
account:string; //账号
passwd:string; //密码
device:string; //设备信息
token:string;
}
table LoginRsp {
account:string; //账号
passwd:string; //密码
device:string; //设备信息
token:string;
}
table LogoutReq {
account:string;
}
table LogoutRsp {
account:string;
}
My python code:
builder = flatbuffers.Builder()
account = builder.CreateString('test')
paswd = builder.CreateString('test')
device = builder.CreateString('test')
token = builder.CreateString('test')
LoginReq.LoginReqStart(builder)
LoginReq.LoginReqAddPasswd(builder, paswd)
LoginReq.LoginReqAddToken(builder, token)
LoginReq.LoginReqAddDevice(builder, device)
LoginReq.LoginReqAddAccount(builder, account)
login = LoginReq.LoginReqEnd(builder)
builder.Finish(login)
buf = builder.Output()
print(buf)
with open("layer.bin1","wb") as f:
f.write(buf)
My C++ code:
flatbuffers::FlatBufferBuilder builder;
auto account = builder.CreateString("test");
auto device = builder.CreateString("test");
auto passwd = builder.CreateString("test");
auto token = builder.CreateString("test");
auto l = CreateLoginReq(builder, account = account, passwd = passwd, device = device, token = token);
builder.Finish(l);
auto buf = builder.GetBufferPointer();
flatbuffers::SaveFile("layer.bin", reinterpret_cast<char *>(buf), builder.GetSize(), true);
output:
md5 layer.bin
MD5 (layer.bin) = 496e5031dda0f754fb4462fadce9e975
Flatbuffers generated by different implementations (i.e. generators) don't necessarily have the same binary layout, but can still be equivalent. It depends on how the implementation decide to write out the contents. So taking the hash of the binary is not going to tell you equivalence.
Related
I need to have a python code and a swift code exchange encrypted message.
Here's what I tried:
Fernet
After a review of the options, I thought that a symetric key algorithm could work well.
In python (as usual), it is straightforward to encrypt and decrypt:
Fernet(key).encrypt(b"mdg") # encrypt
Fernet(key).decrypt(encryptedMsg) # decrypt
In swift, it seemed initially straightforward with something along the lines of:
func encrypt(key: String, msg: String) throws -> String {
let data = Data(base64URL: key)!
let symetricKey = try! SymmetricKey(data: d)
let msgUtf8 = msg.data(using: .utf8)!
let sealBox = try! AES.GCM.seal(msgUtf8, using: symetricKey, nonce: nil)
return sealBox.combined.base64EncodedString();
}
However, I have been unable to find the algorithm in swift matching python's Fernet.
ChaCha
While searching for the problem, I landed on this amazing answer from Bram. Very unfortunately it only solves one side of my problem : encrypting messages in python and decoding them in swift. I also need the reverse process.
How to solve this?
To start, we first need a way to create secure random values to generate the IV and keys. You can also generate the keys using CryptoKit's SymmetricKey and extract the data from them, but for now, I'll use this function.
extension Data {
static func secureRandom(ofSize size: Int) -> Data {
var output = [UInt8](repeating: 0, count: size)
_ = SecRandomCopyBytes(kSecRandomDefault, size, &output)
return Data(output)
}
}
We then require the possibility to compute the AES CBC ciphertext, which can be done using CommonCrypto.
func encrypt(plaintext: Data, key: Data, iv: Data) -> Data {
var encryptor: CCCryptorRef?
defer {
CCCryptorRelease(encryptor)
}
var key = Array(key)
var iv = Array(iv)
var plaintext = Array(plaintext)
CCCryptorCreate(CCOperation(kCCEncrypt), CCAlgorithm(kCCAlgorithmAES), CCOperation(kCCOptionPKCS7Padding), &key, key.count, &iv, &encryptor)
var outputBytes = [UInt8](repeating: 0, count: CCCryptorGetOutputLength(encryptor, plaintext.count, false))
CCCryptorUpdate(encryptor, &plaintext, plaintext.count, &outputBytes, outputBytes.count, nil)
var movedBytes = 0
var finalBytes = [UInt8](repeating: 0, count: CCCryptorGetOutputLength(encryptor, 0, true))
CCCryptorFinal(encryptor, &finalBytes, finalBytes.count, &movedBytes)
return Data(outputBytes + finalBytes[0 ..< movedBytes])
}
and the HMAC with the SHA-256 hash function. I recommend using CryptoKit's HMAC implementation here, but to keep things simple, I went with the CommonCrypto implementation.
func computeHMAC(_ data: Data, using key: Data) -> Data {
var data = Array(data)
var key = Array(key)
var macOut = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), &key, key.count, &data, data.count, &macOut)
return Data(macOut)
}
This brings all of this together into the following
let plaintext = Data("Hello world!".utf8)
let signingKey = Data.secureRandom(ofSize: kCCKeySizeAES128)
let cryptoKey = Data.secureRandom(ofSize: kCCKeySizeAES128)
let fernetKey = (signingKey + cryptoKey).base64EncodedString()
let version: [UInt8] = [0x80]
let timestamp: [UInt8] = {
let timestamp = Int(Date().timeIntervalSince1970).bigEndian
return withUnsafeBytes(of: timestamp, Array.init)
}()
let iv = Data.secureRandom(ofSize: kCCBlockSizeAES128)
let ciphertext = encrypt(plaintext: plaintext, key: cryptoKey, iv: iv)
let hmac = computeHMAC(version + timestamp + iv + ciphertext, using: signingKey)
let fernetToken = (version + timestamp + iv + ciphertext + hmac).base64EncodedString()
print("Fernet key: \(fernetKey)")
print("Fernet token: \(fernetToken)")
An example output can be
Fernet key: 7EwFlYNKTGfj+2fSgL3AUqtrRqRs4D1TWNK7t2XbGJQ=
Fernet token: gAAAAABivCLM0y0poDtGOohT1yK4XTDJppYPJdu4fuDTZ5tb9P9KP5ACgX8aJq4imsSdbzOCcvY3Tueo4FYbwyG+ZugozILL+Q==
We can use this in python using cryptography.io's implementation
from cryptography.fernet import Fernet
key = b'7EwFlYNKTGfj+2fSgL3AUqtrRqRs4D1TWNK7t2XbGJQ='
token = b'gAAAAABivCLM0y0poDtGOohT1yK4XTDJppYPJdu4fuDTZ5tb9P9KP5ACgX8aJq4imsSdbzOCcvY3Tueo4FYbwyG+ZugozILL+Q=='
Fernet(key).decrypt(token)
# b'Hello world!'
I'm trying to sign message with RSA in Python and then verifying data in C#, but getting fail still after hours of testing/fighting.
Python code for signing:
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import serialization
message = b"Test string"
with open("id_rsa", "rb") as key_file:
private_key = serialization.load_pem_private_key(
key_file.read(),
password=None,
)
signature = private_key.sign(
message,
padding.PKCS1v15(),
hashes.SHA256()
)
signaturefile = open('signed.dat', 'wb')
signaturefile.write(signature)
signaturefile.close()
datafile = open('message.dat', 'wb')
datafile.write(message)
datafile.close()
And C# code for verifying:
private void button_Click(object sender, EventArgs e)
{
byte[] data = File.ReadAllBytes("message.dat");
byte[] signature = File.ReadAllBytes("signed.dat");
try
{
using (var reader = File.OpenText("id_rsa.pub"))
{
var pem = new PemReader(reader);
var o = (RsaKeyParameters)pem.ReadObject();
using (var rsa = new RSACryptoServiceProvider())
{
var parameters = new RSAParameters();
parameters.Modulus = o.Modulus.ToByteArray();
parameters.Exponent = o.Exponent.ToByteArray();
rsa.ImportParameters(parameters);
bool ok = rsa.VerifyData(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
if (ok) Console.WriteLine("Verified");
}
}
}
catch (CryptographicException exc)
{
Console.WriteLine(exc.Message);
}
}
I'm getting always fail from rsa.VerifyData. Could someone point me what is the problem?
Same files can be verified well in Python.
RSAParameters#Modulus and RSAParameters#Exponent expect modulus and exponent unsigned, so Org.BouncyCastle.Math.BigInteger#ToByteArrayUnsigned() must be used instead of Org.BouncyCastle.Math.BigInteger#ToByteArray(). With this change, verification is successful.
Also, the big endian byte order is required, but this applies to both methods.
Note that System.Numerics.BigInteger#ToByteArray() returns the data signed with little endian order.
Your current approach uses BC for key import and built-in .NET methods for verification. Alternatively, BC classes can also be used for verification, making for a slightly more efficient implementation overall.
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Security;
...
ISigner signer = SignerUtilities.GetSigner("SHA256withRSA");
signer.Init(false, o);
signer.BlockUpdate(data, 0, data.Length);
bool verified = signer.VerifySignature(signature);
I'm trying to connect to a MQTT brooker to get data from my iot device.
For that, I'm basing myself on an exisitng python library that does exactly that with the exact brooker i need (user_id and key are provided before with and HTTP API authentication) :
def __init__
mqtt_pass = generate_mqtt_password(user_id=self._cloud_creds.user_id, key=self._cloud_creds.key)
self._mqtt_client = mqtt.Client(client_id=self._client_id, protocol=mqtt.MQTTv311)
....
self._mqtt_client.username_pw_set(username=self._cloud_creds.user_id, password=mqtt_pass)
self._mqtt_client.tls_set(ca_certs=self._ca_cert, certfile=None,
keyfile=None, cert_reqs=ssl.CERT_REQUIRED,
tls_version=ssl.PROTOCOL_TLS,
ciphers=None)
....
def generate_mqtt_password(user_id: str, key: str):
"""
Generates the MQTT password that the APP uses to connect to the mqtt server.
:param user_id:
:param key:
:return:
"""
md5_hash = md5()
clearpwd = f"{user_id}{key}"
md5_hash.update(clearpwd.encode("utf8"))
return md5_hash.hexdigest()
ref : https://github.com/albertogeniola/MerossIot/blob/07375049fc0cbc6adee2815b48fa639e74d0d946/meross_iot/manager.py
So I translated it to this in C#, using MQTTnet
var factory = new MqttFactory();
var mqttClient = factory.CreateMqttClient();
byte[] tmpSource = Encoding.UTF8.GetBytes(userId + key);
byte[] tmpHash = new MD5CryptoServiceProvider().ComputeHash(tmpSource);
string passMd5 = BitConverter.ToString(tmpHash).Replace("-", string.Empty).ToLower();
// Create TCP based options using the builder.
var options = new MqttClientOptionsBuilder()
.WithClientId(RandomString(32))
.WithTcpServer("iot.meross.com", 2001)
.WithCredentials(userId, passMd5)
.WithProtocolVersion(MqttProtocolVersion.V311)
.WithTls(new MqttClientOptionsBuilderTlsParameters()
{
UseTls = true,
SslProtocol = SslProtocols.Tls12
})
.Build();
var result = await mqttClient.ConnectAsyn
However with my code It raise an exceptioin "Connecting with MQTT server failed (NotAuthorized)".
I've checked and the "password" I compute is exactly the same as the one the python code does.
Is there a difference I'm missing ?
I have a problem with crypto-js vs python sha256. It would like to write a nodejs client for duplicati. So I tried to port some python code to js.
https://github.com/Pectojin/duplicati-client/blob/master/auth.py#L136
Javascript
const CryptoJS = require('crypto-js');
function sha256(to_sign) {
var hash = CryptoJS.SHA256(to_sign.toString());
var hashInBase64 = CryptoJS.enc.Base64.stringify(hash);
return hashInBase64.toString('utf-8');
}
let salt = "ZAwQqEAAwR78oZOxFu0nVH2FLy/BnulVxhuu9IOnBwg="
let salt2 = "YQ=="
let password = "abc"
let saltedpwd = sha256(Buffer.concat([Buffer.from(password),Buffer.from(salt,'base64')]));
let saltedpwd2 = sha256(Buffer.concat([Buffer.from(password),Buffer.from(salt2,'base64')]));
let new_password = saltedpwd.toString('base64');
let new_password2 = saltedpwd2.toString('base64');
console.log(new_password)
console.log(new_password2)
returns:
pw1: 0udYFffMXd2QWW9dVXbFl3qp/6lnRcnspr4M1VEtgJA=
pw2: XD9nt/qE374RGDh8rRR5OSmEWlvHwAgMTYMJ03uqaNA=
Python
import base64
import hashlib
import sys
password = "abc"
salt = "ZAwQqEAAwR78oZOxFu0nVH2FLy/BnulVxhuu9IOnBwg="
salt2 = "YQ=="
salt_password = password.encode() + base64.b64decode(salt)
saltedpwd = hashlib.sha256(salt_password).digest()
print (base64.b64encode(saltedpwd).decode('utf-8'))
salt_password2 = password.encode() + base64.b64decode(salt2)
saltedpwd2 = hashlib.sha256(salt_password2).digest()
print (base64.b64encode(saltedpwd2).decode('utf-8'))
returns:
pw1: v9bAzxPatGzA2W7ORkraUvh+nyXotWXItAKpawGSo+A=
pw2: XD9nt/qE374RGDh8rRR5OSmEWlvHwAgMTYMJ03uqaNA=
as you can see, pw2 with the very simple base64 salt is both the same.
the salt from pw1 comes from the duplicati server, so I can't control it...
I've tried some many combinations of encoding, CryptoJS options, so I'm at the point where I will soon stop my 'project'... :(
Can you please give me any advice, what I'm doing wrong? I would be glad about any information.
Regards,
Benjamin
You're 99% of the way there, I think the fix is a one liner (isn't it so often!).
We just have to change
var hash = CryptoJS.SHA256(to_sign.toString());
to
var hash = CryptoJS.SHA256(CryptoJS.lib.WordArray.create(to_sign));
I believe this is because we want to convert directly from the buffer we created by concatenating the password and salt, rather than converting to a string, which is causing the wrong hash to be computed.
In any case, we get the same output as the Python code, which is what we want, ie
v9bAzxPatGzA2W7ORkraUvh+nyXotWXItAKpawGSo+A=
XD9nt/qE374RGDh8rRR5OSmEWlvHwAgMTYMJ03uqaNA=
The new code would look like this in Node.js:
const CryptoJS = require('crypto-js');
function sha256(to_sign) {
var hash = CryptoJS.SHA256(CryptoJS.lib.WordArray.create(to_sign));
var hashInBase64 = CryptoJS.enc.Base64.stringify(hash);
return hashInBase64.toString('utf-8');
}
let salt = "ZAwQqEAAwR78oZOxFu0nVH2FLy/BnulVxhuu9IOnBwg="
let salt2 = "YQ=="
let password = "abc"
let saltedpwd = sha256(Buffer.concat([Buffer.from(password),Buffer.from(salt,'base64')]));
let saltedpwd2 = sha256(Buffer.concat([Buffer.from(password),Buffer.from(salt2,'base64')]));
let new_password = saltedpwd.toString('base64');
let new_password2 = saltedpwd2.toString('base64');
console.log(new_password)
console.log(new_password2)
I extended the win32comext MAPI with the Interface IExchangeModifyTable to edit ACLs via the MAPI. I can modify existing ACL entries, but I stuck in adding new entries. I need the users entry ID to add it, according this C example
(Example Source from MSDN)
STDMETHODIMP AddUserPermission(
LPSTR szUserAlias,
LPMAPISESSION lpSession,
LPEXCHANGEMODIFYTABLE lpExchModTbl,
ACLRIGHTS frights)
{
HRESULT hr = S_OK;
LPADRBOOK lpAdrBook;
ULONG cbEid;
LPENTRYID lpEid = NULL;
SPropValue prop[2] = {0};
ROWLIST rowList = {0};
char szExName[MAX_PATH];
// Replace with "/o=OrganizationName/ou=SiteName/cn=Recipients/cn="
char* szServerDN = "/o=org/ou=site/cn=Recipients/cn=";
strcpy(szExName, szServerDN);
strcat(szExName, szUserAlias);
// Open the address book.
hr = lpSession->OpenAddressBook(0,
0,
MAPI_ACCESS_MODIFY,
&lpAdrBook );
if ( FAILED( hr ) ) goto cleanup;
// Obtain the entry ID for the recipient.
hr = HrCreateDirEntryIdEx(lpAdrBook,
szExName,
&cbEid,
&lpEid);
if ( FAILED( hr ) ) goto cleanup;
prop[0].ulPropTag = PR_MEMBER_ENTRYID;
prop[0].Value.bin.cb = cbEid;
prop[0].Value.bin.lpb = (BYTE*)lpEid;
prop[1].ulPropTag = PR_MEMBER_RIGHTS;
prop[1].Value.l = frights;
rowList.cEntries = 1;
rowList.aEntries->ulRowFlags = ROW_ADD;
rowList.aEntries->cValues = 2;
rowList.aEntries->rgPropVals = &prop[0];
hr = lpExchModTbl->ModifyTable(0, &rowList);
if(FAILED(hr)) goto cleanup;
printf("Added user permission. \n");
cleanup:
if (lpAdrBook)
lpAdrBook->Release();
return hr;
}
I can open the Address Book, but HrCreateDirEntryIdEx is not provided in the pywin32 mapi. I found it in the exchange extension, which does not compile on my system, the missing library problem. Do you have any idea to retrieve the users entry ID?
Thank.
Patrick
I got this piece of code and it works fine
from binascii import b2a_hex, a2b_hex
import active_directory as ad
# entry_type, see http://msdn.microsoft.com/en-us/library/cc840018.aspx
# + AB_DT_CONTAINER 0x000000100
# + AB_DT_TEMPLATE 0x000000101
# + AB_DT_OOUSER 0x000000102
# + AB_DT_SEARCH 0x000000200
# ab_flags, maybe see here: https://svn.openchange.org/openchange/trunk/libmapi/mapidefs.h
def gen_exchange_entry_id(user_id, ab_flags=0, entry_type = 0):
muidEMSAB = "DCA740C8C042101AB4B908002B2FE182"
version = 1
# Find user and bail out if it's not there
ad_obj = ad.find_user(user_id)
if not ad_obj:
return None
return "%08X%s%08X%08X%s00" % (
ab_flags,
muidEMSAB,
version,
entry_type,
b2a_hex(ad_obj.legacyExchangeDN.upper()).upper(),
)
data = gen_exchange_entry_id("myusername")
print data
print len(a2b_hex(data))