This question relates to the application of basic elliptic curve crypto for the needs of a Bitcoin project.
I need to generate a receive address (contract_public_key) that is directly associated with another (issuer_public_key) and some metadata, M, to form a Bitcoin contract.
I will try to put in more general terms...
So we have the following:
G is the elliptic curve base point.
issuer_private_key = <some random 256bit scalar>
issuer_public_key = issuer_private_key * G
M = 'Terms of contract bla bla and also includes issuer_public_key for safety'
I want a function, GenPub, where:
GenPub(issuer_public_key, M) = contract_public_key
I want a function, GenPriv, where:
GenPub(issuer_public_key, issuer_private_key, M) = contract_private_key
such that,
contract_public_key = contract_private_key * G
Here is my first attempt in pseudo-python:
def GenPub(issuer_public_key, M):
# generate a hash of the message
e = SHA256(M)
# create an EC point that is known to both parties
contract_point = (e * issuer_public_key)
# generate a public key for this contract
return contract_point + issuer_public_key
def GenPriv(issuer_public_key, issuer_private_key, M):
# generate a hash of the message
e = SHA256(M)
# create an EC point that is known to both parties
contract_point = (e * issuer_public_key)
# generate a private key for this contract
return contract_point + issuer_private_key
# the public key for the contract
contract_private_key = GenPub(issuer_public_key, M)
# the private key for contract
contract_private_key = GenPriv(issuer_public_key, issuer_private_key, M)
Feedback much appreciated
contract_point + issuer_private_key cannot be computed. contract_point is a point on elliptic curve but issuer_private_key is just a scalar.
Suppose you want is:
def GenPriv(issuer_public_key, issuer_private_key, M):
# generate a hash of the message
e = SHA256(M)
# generate a private key for this contract
return e + issuer_private_key
I am not sure the security of this system. It needs some cryptanalysis. Maybe you can ask help from crypto.stackexchange.com.
In my opinion, I will use a key exchange scheme to negotiate a secret key of the contract.
Related
I've got some code which uses StringSession to talk to the Telegram API using telethon.
In my unit tests, I'm trying to instantiate a mocked TelegramClient, passing it a StringSession(myvalue) object as the first parameter. The real code works fine, but I need a fake session string for 'myvalue', to use in my unit tests (where I have a mocked telegram client).
How can I create a dummy value for 'myvalue' which will successfully execute StringSession(myvalue)?
Currently, my tests are dying here:
self = <telethon.sessions.string.StringSession object at 0x7f0777492ad0>
string = 'dummyxxx'
def __init__(self, string: str = None):
super().__init__()
if string:
if string[0] != CURRENT_VERSION:
raise ValueError('Not a valid string')
string = string[1:]
ip_len = 4 if len(string) == 352 else 16
> self._dc_id, ip, self._port, key = struct.unpack(
_STRUCT_PREFORMAT.format(ip_len), StringSession.decode(string))
E struct.error: unpack requires a buffer of 275 bytes
If you don't need a valid session to start with, you can also use MemorySession instead:
from telethon.sessions import MemorySession
session = MemorySession()
# use session variable when creating the client
Someone posted an answer which helped point me in the right direction, but they later deleted it for some reason.
In case it helps anyone else, here is the code that worked for me:
import struct
import base64
from telethon.sessions import StringSession
_STRUCT_PREFORMAT = '>B{}sH256s'
CURRENT_VERSION = '1'
dc_id = 1
ip = b'\x7f\x00\x00\x01' # 127.0.0.1
port = 80
key = b'\x00' * 256
string = StringSession.encode(struct.pack(
_STRUCT_PREFORMAT.format(len(ip)),
dc_id,
ip,
port,
key
))
myvalue = CURRENT_VERSION + string
# Create the StringSession object using the dummy value to confirm it works
session = StringSession(myvalue)
print(myvalue)
I want to get sign by ECDSA secp224k1. I cannot get the same signature as in the CoinFLEX Authentication Process example from the manual. I am using C# BouncyCastle.
Why can't I get manual page's signature by my code?
// manual page's step 7
byte[] fortyByteMessage = fromHexStringToByteArr("0x00000000 00000001").Concat(fromHexStringToByteArr("0x6b347302 2e6b9b5a f2fe5d1d ae7cf5bf")).Concat(fromHexStringToByteArr("0xf08c98ca f1fd82e8 cea9825d bff04fd0")).ToArray();
// manual page's step 9
byte[] privateKey = fromHexStringToByteArr("0xb89ea7fc d22cc059 c2673dc2 4ff40b97 83074646 86560d0a d7561b83");
// manual page's step 10
X9ECParameters spec = ECNamedCurveTable.GetByName("secp224k1");
ECDomainParameters domain = new ECDomainParameters(spec.Curve, spec.G, spec.N);
ECDsaSigner signer = new ECDsaSigner(new HMacDsaKCalculator(new Sha224Digest()));
signer.Init(true, new ECPrivateKeyParameters(new BigInteger(privateKey), domain));
BigInteger[] signature = signer.GenerateSignature(fortyByteMessage);
byte[] r = signature[0].ToByteArray().SkipWhile(b => b == 0x00).Reverse().ToArray(); // (r) r should be 0x3fb77a9d 7b5b2a68 209e76f6 872078c5 791340d5 989854ad a3ab735e, but not.
Console.WriteLine(BitConverter.ToString(r).Replace("-", string.Empty).ToLower());
Expected byteArr ( step 10 r value ):
r = 0x3fb77a9d 7b5b2a68 209e76f6 872078c5 791340d5 989854ad a3ab735e<br>
My byteArr (this is wrong value, because it is different from step 10 r value )
r = 0x1e3b3f4f 7401ff9d 827b7222 47823919 452d3adb effa7aa4 52a0879e<br>
Another function:
static byte[] fromHexStringToByteArr(string paramHexString)
{
string hexString = paramHexString.Substring(2).Replace(" ", "");
byte[] result = new byte[hexString.Length / 2];
int cur = 0;
for (int i = 0; i < hexString.Length; i = i + 2)
{
string w = hexString.Substring(i, 2);
result[cur] = Convert.ToByte(w, 16);
cur++;
}
return result;
}
According to step 10 of the instructions, not the 40-byte-message should be signed, but the SHA224-hash of this message: The client signs the 28-byte SHA-224 digest of the 40-byte message.... Note, that the data are not automatically hashed by the GenerateSignature-method, i.e. it must be hashed explicitly, see also here and these examples.
The Org.BouncyCastle.Math.BigInteger.ToByteArray-method (which is used in the C#-code) outputs the byte-array in big-endian-format (unlike .Net, whose System.Numerics.BigInteger.ToByteArray-method uses the little-endian-format). Therefore, it is not necessary to reverse the byte-order (using the Reverse-method).
With these modifications, the signature is:
r = 0x1781ff4997b48d389f518df75001c4b6564082956228d74dd0321656
s = 0x0aadc68cf78dc75d44fb300f200465e72a70826ec2d5577d49b62e59
which, however, still differs from the signature shown in the instructions.
In the C#-code, the ECDsaSigner-instance is created with a HMacDsaKCalculator-instance, generating a deterministic signature based on RFC6979. When creating a signature with ECDSA, the k-parameter is chosen randomly for the non-deterministic ECDSA, whereas in the deterministic variant it is created from the message and the private key according to a specific algorithm (described in RFC6979), see here. Thus, the deterministic variant generates the same signature for the same message and private key, while the non-deterministic variant generates different signatures.
Probably the difference between the signatures is caused by the use of the non-deterministic variant by CoinFLEX. Unfortunately, the instructions do not go into detail about the ECDSA-procedure used.
Update:
Both variants, deterministic and non-deterministic, provide valid ECDSA-signatures! Before the deterministic variant (RFC6979 is from August 2013) there was only the non-deterministic variant, see here.
I installed and tested the sign_secp224k1-tool on a Linux (Debian) machine. As suspected, the tool generates different signatures for the same private key and the same message, obviously using the non-deterministic variant. This can also be easily verified from the source-code: The signature is calculated using the ecp_sign-method, which randomly determines the k-value using /dev/urandom.
Thus it is clear that the signature generated by the C#-code using the deterministic variant generally cannot match a signature generated by the sign_secp224k1-tool using the non-deterministic variant.
I'm using "node-forge" to generate a publicKey to use with my AES symmetric key but I don't know how to use the data provided by my backend to create this publicKey. Currently, I receive from an authentication api the following:
e: "10001"
n:"c7c5dd235568711a943ebbdacac890ca2cf12c1ab539f77726e8874d2ab4220cf06369358b5eff0425fb17d4f696f741cf04c5ea874415e7f67d118a2e763e641e8675b8f42e9277b3f70f14e4de23fe16f51abdc427490f47e4b28ae3e5eb3563ba797fe90f9b70ba878646b1b297c52ba735827682b67309d38b423e31b50b"
maxdigits: "131"
Where "e" is my exponent, "n" is my module and "maxdigits" is the length my BigIntegers are supposed to have.
But when I try something like this:
const keys = forge.pki.rsa.generateKeyPair({ e: res.e, n: res.n });
My backend returns an error. What am I doing wrong?
forge.pki.rsa.generateKeyPair is the wrong method in this context. forge.pki.rsa.generateKeyPair creates a new key pair with random modulus. The first parameter specifies the modulus/key size in bits, the second the exponent ([1] and [2]):
// var forge = require('node-forge'); // in nodejs-context
var pki = forge.pki;
var rsa = forge.pki.rsa;
var keypair = rsa.generateKeyPair({bits: 2048, e: 0x10001});
var pubKeyPEM = pki.publicKeyToPem(keypair.publicKey);
var privKeyPEM = pki.privateKeyToPem(keypair.privateKey);
console.log(pubKeyPEM);
console.log(privKeyPEM);
The forge.pki.rsa.setPublicKey-method is used to generate a public key via modulus and exponent, where the modulus is the first parameter and the exponent is the second parameter ([2]), both of type forge.jsbn.BigInteger ([3]):
var BigInteger = forge.jsbn.BigInteger;
var n = new BigInteger('c7c5dd235568711a943ebbdacac890ca2cf12c1ab539f77726e8874d2ab4220cf06369358b5eff0425fb17d4f696f741cf04c5ea874415e7f67d118a2e763e641e8675b8f42e9277b3f70f14e4de23fe16f51abdc427490f47e4b28ae3e5eb3563ba797fe90f9b70ba878646b1b297c52ba735827682b67309d38b423e31b50b', 16);
var e = new BigInteger('10001', 16);
var pubKey = rsa.setPublicKey(n, e);
var pubKeyPEM = pki.publicKeyToPem(pubKey)
console.log(pubKeyPEM); // Check with e.g. https://lapo.it/asn1js/
I am using this library, node-jwks-rsa, to fetch JWT keys from my auth0 jwks.json file in order to verify that the id_token my application retrieves after authentication is actually coming from my auth provider.
Under the hood it uses this method to build a public key PEM
export function certToPEM(cert) {
cert = cert.match(/.{1,64}/g).join('\n');
cert = `-----BEGIN CERTIFICATE-----\n${cert}\n-----END CERTIFICATE-----\n`;
return cert;
}
(Using the x50c as argument from the .jwks file).
which I then use in combination with jsonwebtoken to verify that the JWT(id_token) is valid.
How is this method of verification different from generating a private key(RSA) from the modulus and exponent of the jwks.json file and using it for verification instead? (as example see this library)
Additionally here is function as demonstration that generates a PEM from a mod and exponent (taken from http://stackoverflow.com/questions/18835132/xml-to-pem-in-node-js)
export function rsaPublicKeyToPEM(modulusB64, exponentB64) {
const modulus = new Buffer(modulusB64, 'base64');
const exponent = new Buffer(exponentB64, 'base64');
const modulusHex = prepadSigned(modulus.toString('hex'));
const exponentHex = prepadSigned(exponent.toString('hex'));
const modlen = modulusHex.length / 2;
const explen = exponentHex.length / 2;
const encodedModlen = encodeLengthHex(modlen);
const encodedExplen = encodeLengthHex(explen);
const encodedPubkey = '30' +
encodeLengthHex(modlen + explen + encodedModlen.length / 2 + encodedExplen.length / 2 + 2) +
'02' + encodedModlen + modulusHex +
'02' + encodedExplen + exponentHex;
const der = new Buffer(encodedPubkey, 'hex')
.toString('base64');
let pem = `-----BEGIN RSA PUBLIC KEY-----\n`;
pem += `${der.match(/.{1,64}/g).join('\n')}`;
pem += `\n-----END RSA PUBLIC KEY-----\n`;
return pem;
};
The aforementioned jsonwebtoken library can verify a JWT using either -- but why? If both of these verification methods can validate a JWT signature why do they both exist? What are the tradeoffs between them? Is one more secure than the other? Which should I use to verify most fully?
Using a RSA assymetric key pair, the JWT is signed with the private key and verified with the public. You can not verify a digital signature with the private key
Modulus and exponent are the components of the public key and you can use it to build the public key in PEM format, which is a base64 representation of the public key (modulus and exponent) encoded in DER binary format. You can use PEM, DER or modulus and exponent because the contain the same information
But anybody can't build the private key with modulus and exponent. He would need the private RSA elements, which must be kept secret so that no one can sign for you.
So I'm trying to create a private/public key from 64 characters that I already know using bitcoinjs with the code below:
key = Bitcoin.ECKey.makeRandom();
// Print your private key (in WIF format)
document.write(key.toWIF());
// => Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct
// Print your public key (toString defaults to a Bitcoin address)
document.write(key.pub.getAddress().toString());
// => 14bZ7YWde4KdRb5YN7GYkToz3EHVCvRxkF
If I try to set "key" to my 64 characters instead of "Bitcoin.ECKey.makeRandom();" it fails. Is there a method or library that I overlooked that would allow me to use the known 64 characters in order to generate the private key in wif format and the public address?
Thanks in advance to anyone that may be able to offer some help.
You should use fromWIF method to pass your own data.
from source code of eckey.js
// Static constructors
ECKey.fromWIF = function(string) {
var payload = base58check.decode(string)
var compressed = false
// Ignore the version byte
payload = payload.slice(1)
if (payload.length === 33) {
assert.strictEqual(payload[32], 0x01, 'Invalid compression flag')
// Truncate the compression flag
payload = payload.slice(0, -1)
compressed = true
}
To create WIF from your key please follow https://en.bitcoin.it/wiki/Wallet_import_format
Here is interactive tool http://gobittest.appspot.com/PrivateKey
The solution to generate private and public key:
//public-key
var address = eckey.getBitcoinAddress().toString();
var privateKeyBytesCompressed = privateKeyBytes.slice(0);
privateKeyBytesCompressed.push(0x01);
var privateKeyWIFCompressed = new Bitcoin.Address(privateKeyBytesCompressed);
privateKeyWIFCompressed.version = 0x80;
//private-key
privateKeyWIFCompressed = privateKeyWIFCompressed.toString();
Take a look at moneyart.info for beautifully designed paperwallets.