How to offline sign a Tron transaction in Ruby? - ruby

I'm trying to offline sign a Tron transaction in Ruby.
TronGrid has an endpoint to sign transactions, but they require to send them the account private key in order to do it, which feels a potential risk, so I'd like to sign the transaction locally in order to avoid having the private key value leaving the server.
In the specific I'm trying to convert this Javascript method in Ruby: https://github.com/tronprotocol/tronweb/blob/master/src/utils/crypto.js#L218
I've been trying using both OpenSSL and a gem to do this without much success.
This is what I've got so far:
bn = OpenSSL::BN.new(hex_private_key, 16)
ec = OpenSSL::PKey::EC.new('secp256k1')
ec.private_key = bn
ec.dsa_sign_asn1(transaction_id).unpack1('H*')
and
bn = OpenSSL::BN.new(hex_private_key, 16)
private_key = EC::PrivateKey.new(bn.to_i)
signature = private_key.sign(transaction_id)
The latter gives me the r and s that is then used in the javascript function (even though they wouldn't match what I'd get in JS), and I'm not sure where I could get that recoveryParam.
And the former doesn't return me the signature I was expecting.
I'm kinda lost on how to find out a way to sign those transactions.

Did you find out how to do it?
In the example takes raw_data_hex:
private static byte[] signTransaction2Byte(byte[] transaction, byte[] privateKey)
throws InvalidProtocolBufferException {
ECKey ecKey = ECKey.fromPrivate(privateKey);
Transaction transaction1 = Transaction.parseFrom(transaction);
byte[] rawdata = transaction1.getRawData().toByteArray();
byte[] hash = Sha256Sm3Hash.hash(rawdata);
byte[] sign = ecKey.sign(hash).toByteArray();
return transaction1.toBuilder().addSignature(ByteString.copyFrom(sign)).build().toByteArray();
}`
`Sha256Sm3Hash.hash` returns `sha256` or `sm3` depends on private key.

Related

Get elliptic curve public key from private key using Ruby OpenSSL

I'm playing around with Elliptic Curves using the Ruby 2.5.x OpenSSL library. I can easily generate a private and public key pair using
curve = OpenSSL::PKey::EC.new('secp256k1')
curve.generate_key
But given a private key I want to regenerate the public key.
I know OpenSSL can do it because the command line allows you to do it, and also the Ruby Bitcoin project does it. But the Ruby Bitcoin project has its own interface to OpenSSL using FFI rather than the one provided by Ruby.
Does Ruby 2.5.x openssl library not expose enough of the OpenSSL interfaces to be able to generate an elliptic curve public key from a private key, or that it can but it's not documented?
In case someone interested to get public key in pem format too :)
example_key = OpenSSL::PKey::EC.new('secp256k1').generate_key
puts example_key.to_pem
pkey = OpenSSL::PKey::EC.new(example_key.public_key.group)
pkey.public_key = example_key.public_key
puts pkey.to_pem
The Ruby OpenSSL bindings don’t allow you to directly get the public key from a PKey::EC object as far as I can tell, but they do expose enough to do the calculation yourself, which is straightforward.
Given a private key as an OpenSSL:BN object, which for the example we can generate like this:
example_key = OpenSSL::PKey::EC.new('secp256k1').generate_key
private_key = example_key.private_key
We can calculate the public key by multiplying the group base point (i.e. the generator) by the private key:
group = OpenSSL::PKey::EC::Group.new('secp256k1')
public_key = group.generator.mul(private_key)
The public key is an OpenSSL::PKey::EC::Point. You can compare with the original to see that is the same:
puts example_key.public_key == public_key # => true

Why is an OpenSSL::PKey::RSA key both private and public?

I'm learning about the OpenSSL ruby module.
Shown below is a pry session where I generate a key using the RSA asymmetric public key algorithm. I also call the #private? and #public? instance methods:
[1] pry(main)> require 'openssl'
=> true
[2] pry(main)> alices_key = OpenSSL::PKey::RSA.new 2048
=> #<OpenSSL::PKey::RSA:0x007fc0751cb028>
[3] pry(main)> alices_key.public?
=> true
[4] pry(main)> alices_key.private?
=> true
Why is the #<OpenSSL::PKey::RSA:0x007fc0751cb028> object both public and private?
Usually the data structure of the private key also contains the public exponent. They are generated in the same key pair generation in the first place.
It is easy to store them together as the public key is the modulus + the public exponent (usually the value 0x10001, the fourth prime of Fermat). The modulus of course is also part of the private key, so that doesn't need to be duplicated.
The public key may also be used to protect against some side channel attacks although that's not such a big issue in software.
It depends on the software if the private key can also be used as a public key and if the public exponent is stored with the private key. But it is quite common, e.g. a private key object in PKCS#11 (used for software, smart cards and HSM's) also contains the public exponent. On the other hand Java has separate PrivateKey and PublicKey classes where the PrivateKey doesn't contain the public exponent (or it doesn't expose it through the public API anyway).
In the end we cannot answer the question without consulting the original OpenSSL guys (Mr. Young and Mr Hudson, I suppose) but there are good reasons for storing the public exponent as well, and as the public key is public it doesn't hurt either.

Serializing an object to a JSON input stream using GSON?

I'm not sure if I'm asking for a right thing, but is it possible to make the GSON Gson.toJson(...) methods family work in "streaming mode" while serializing to JSON? Let's say, sometimes there are cases when using Appendable is not possible:
final String json = gson.toJson(value);
final byte[] bytes = json.getBytes(charset);
try ( final InputStream inputStream = new ByteArrayInputStream(bytes) ) {
inputStreamConsumer.accept(inputStream);
}
The example above is not perfect in this scenario, because:
It generates a string json as a temporary buffer.
The json string produces a new byte array just to wrap it up into a ByteArrayInputStream instance.
I think it's not a big problem to write a CharSequence to InputStream adapter and get rid of creating the byte array clone, but I still couldn't get rid of generating the string temporary buffer to use the inputStreamConsumer efficiently. So, I'd expect something like:
try ( final InputStream inputStream = gson.toJsonInputStream(value) ) {
inputStreamConsumer.accept(inputStream);
}
Is it possible using just GSON somehow?
According to this comment, this cannot be done using GSON.

Is CryptMsgGetParam enough to ensure file integrity?

I have a bunch of code currently to check if the PE is signed by my company but it only checks the signature (not written by me)
// hMsg was obtained earlier by using CryptQueryObject
DWORD dwSignerInfo;
bool ret = CryptMsgGetParam(hMsg,
CMSG_SIGNER_INFO_PARAM,
0,
NULL,
&dwSignerInfo);
PCMSG_SIGNER_INFO pSignerInfo = NULL;
pSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, dwSignerInfo);
ret = CryptMsgGetParam(hMsg,
CMSG_SIGNER_INFO_PARAM,
0,
(PVOID)pSignerInfo,
&dwSignerInfo);
std::vector<BYTE> fileSerial;
fileSerial.assign(pSignerInfo->SerialNumber.pbData, pSignerInfo->SerialNumber.pbData + pSignerInfo->SerialNumber.cbData);
const std::array<BYTE, 16> k_serial = {0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01}
const std::vector<BYTE> k_SerialKey(k_serial.cbegin(), k_serial.cend());
if (fileSerial == k_SerialKey)
{
// Do stuff since signature is made by us
}
The above code looks like it works well enough but it doesn't seem tp really check the integrity of the file itself. I want to be able to verify that the executable did not get corrupted by a virus or something but kept the signature intact.
I was thinking that I can keep using CryptMsgGetParam and use the params
CMSG_COMPUTED_HASH_PARAM
CMSG_HASH_DATA_PARAM
These params return 2 different hashes, if I compare them and they match does it mean that my file matches the hash in the signature?
N.B. I looked at WinVerifyTrust but I feel like its wayyy overkill for what I want, all I want is verify the file still matches the file the signature was made for.

What cryptographically secure options are there for creating random numbers in WinRT?

Ordinarily I'd do something like this:
byte[] randomBytes = new byte[bytes];
string randomString = Convert.ToBase64String(new RNGCryptoServiceProvider().GetBytes(randomBytes));
However there's no RNGCryptoServiceProvider available.
Are there any secure random alternatives available?
Thanks,
I managed to find an equivalent.
using Windows.Security.Cryptography;
IBuffer randomBuffer = CryptographicBuffer.GenerateRandom(PASSWORD_SALT_LENGTH);
string randomString = CryptographicBuffer.EncodeToBase64String(randomBuffer)
I hope this is of use to someone else.

Resources