Open sschiessl-bcp opened 5 years ago
Please change BSIP type to "Informational (Client Protocol)".
Why is the payload
field in JSON an array with alternating key/value strings instead of a proper object? (Ah, perhaps because fields in a JSON object do not have a well-defined order?)
IMO it's a bad idea to use a PGP-like plain string representation, precisely because PGP has had a lot of difficulties with this. At the very least, you must define precisely how whitespace and end-of-line characters are to be handled.
The signature
field format isn't specified either, nor the signature algorithms.
Please change BSIP type to "Informational (Client Protocol)".
Why is the
payload
field in JSON an array with alternating key/value strings instead of a proper object? (Ah, perhaps because fields in a JSON object do not have a well-defined order?)IMO it's a bad idea to use a PGP-like plain string representation, precisely because PGP has had a lot of difficulties with this. At the very least, you must define precisely how whitespace and end-of-line characters are to be handled.
The
signature
field format isn't specified either, nor the signature algorithms.
yep, JS arrays are deterministic order. objects (which would allow name/value pairs) are not
IMO it's a bad idea to use a PGP-like plain string representation, precisely because PGP has had a lot of difficulties with this. At the very least, you must define precisely how whitespace and end-of-line characters are to be handled.
Haha ... very good point, we ran into those problems too.
All fields should be stripped
to not have whitespaces at either end of the string.
ECDSA is used on the secp256k1 curve. signature is presented in hexadecimal format and is prepended with signature recovery parameter. So, the signature is generated by hashing the stringified content, and signing the hash the very same way bitshares signs transactions.
What about embedded whitespace / EOL chars? What about encoding? JSON is utf-8 but plaintext is platform-dependent. I strongly advise against this.
How is the string hashed? Transaction hashing prepends the chain ID, is that also required here?
I will check and fill in details for that (the plain text one is already existing in the UI that is why I want a post-published definition for it). Ideally the UI also switches away from it.
How is the string hashed? Transaction hashing prepends the chain ID, is that also required here?
Yes, it does - and IMHO should because accounts on different chains could be owned by different people.
The encoding, I suppose should be properly defined ... wouldn't it be easiest to allow UTF-8 everywhere and encode it as such?
JSON strings can contain backslash escapes, which means there is no canonical representation either.
Why timestamp is a double
?
IMO this BSIP should mention the existing message signing method used by python and JS libraries. Implementations should also accept messages signed using that old format, but produce signatures in the new format.
IMO this BSIP should mention the existing message signing method used by python and JS libraries. Implementations should also accept messages signed using that old format, but produce signatures in the new format.
Raw String Version 1 is the current one.
IMHO string is more read-able than a UNIX timestamp integer.
Raw String Version 1 is the current one.
Oh, right. Sorry. In that case perhaps scrap the JSON version? Only adds confusion, doesn't really solve canonicalization issues etc.
This should be a draft in the repository but not only an issue.
IMHO the metadata should be dropped from the spec, as it effectively makes the metadata part of the signature. In other words, to verify the signature of a message, the verifier would need to be in possession of both the signature and the metadata. This means the following script between Valarie Validator and Bob Signer wouldn't work:
Valarie: If you are indeed Bob, please provide me a signature of the message,
"I am Bob, presenting myself on this day to Valarie."
Bob: OK, [signs message], here it is: "20ff2fea55cb4a...."
The problem is, Valerie cannot validate this signature, unless Bob also provides the metadata, which at best is cumbersome, and at worst may not be possible, if they are using a communication protocol that does not allow for this extra data.
A validator should be able to validate a known message with the signature bytes alone.
@christophersanborn I know this issue has been open a long time, but I worked up an example for my own learning and thought I would share. Of course, if you implemented a BitShares app using this modified protocol you couldn't send messages from your app to existing BitShares users who are using the web UI, for example. So, I wouldn't recommend it. But, I think it's good just to see how to verify a signature independant from the messaging protocol.
It seems like the meta data is redundant to me, because all that data can be derived from the blockchain itself. But, I am curious to learn from others what the thought process is behind including the meta data. Maybe I'm missing something.
import json, binascii, unittest
from graphenebase import BrainKey, memo
from graphenebase.ecdsa import sign_message, verify_message
class message_signing_Tests(unittest.TestCase):
receiver_brainkey = BrainKey(
brainkey='DANLI ARTHEL SOREDIA BASKER IDYL SAMMY RUFTER IDEATE TITULUS CUTUP BORGH PERIOST ARETE BANDIE MIDE BIPEDAL',
prefix='TEST',
sequence=0,
)
sender_brainkey = BrainKey(
brainkey='BELTER QUEASY TAGRAG PAXILLA COUXIA PAPISM HELM WISTE STILTY INDITER STARNEL CURIATE MAFFIA LOTS RHABDUS BEDWAY',
prefix='TEST',
sequence=0,
)
salt = datetime.utcnow()
def setUp(self) -> None:
super().setUp()
self.text = 'What is the Ultimate Question of Life, the Universe, and Everything?'
self.payload = [
'from',
'user-one',
'key',
str(self.sender_brainkey.get_public_key()),
'time',
str(self.salt),
'text',
self.text,
]
self.payload_encoded = json.dumps(self.payload, separators=(",", ":"))
def test_sign_verify_and_encrypt_payload(self):
# sign the JSON encoded payload
self.signature = binascii.hexlify(
sign_message(
self.payload_encoded,
str(self.sender_brainkey.get_private_key()),
)
).decode('ascii')
# verify the signature
self.sender_pubkey = binascii.hexlify(
verify_message(
self.payload_encoded,
binascii.unhexlify(self.signature),
)
).decode('ascii')
self.assertEqual(
repr(self.sender_brainkey.get_public_key()),
self.sender_pubkey,
)
# receiver encrypts the message into a BitShares memo.
self.memo_encrypted = memo.encode_memo(
self.sender_brainkey.get_private_key(),
self.receiver_brainkey.get_public_key(),
self.salt,
self.payload_encoded,
)
# print message
msg = '-----BEGIN BITSHARES SIGNED MESSAGE-----\n'
msg += f'{self.text}\n'
msg += '-----BEGIN SIGNATURE-----\n'
msg += f'{self.signature}\n'
msg += '-----END BITSHARES SIGNED MESSAGE-----'
print(msg)
Which, would yield this output:
-----BEGIN BITSHARES SIGNED MESSAGE-----
What is the Ultimate Question of Life, the Universe, and Everything?
-----BEGIN SIGNATURE-----
1f4ed8d6e7205245469ba54cdb7899523ccc6ee7274b256d3d337b6501d0c8d670072c2a30ae20e16f234c919b3872c35388590daaf25246240f246e9671714d88
-----END BITSHARES SIGNED MESSAGE-----
I think the meta data is to avoid replay attacks.
I think the meta data is to avoid replay attacks.
Ahh that does make sense.
Abstract
This is an informal BSIP to define the format of a signed message to establish a common format.
Specification
Raw String Version 1
Plain String version with header and footer, similar to PGP message. Everything is a string, thus no quotation marks are present.
user_message
account_name
public_key
head_block_no
datetime_string
Date.toString()
)signature
The string payload for signing is
represented as a string with visible characters
Example
JSON Version 1
Represented as a dictionary, a signed message in JSON Version 1 looks like
user_message
account_name
public_key
timestamp
signature
The string payload for signing is
which corresponds to the stringified version of the JSON payload defined previously.
Example