zondax / ledger-stacks Goto Github PK
View Code? Open in Web Editor NEWLicense: Apache License 2.0
License: Apache License 2.0
Latest changes from #55 are not yet on npm
Currently, the app just signs the prior PreSigHash directly, but the protocol does roughly this calculation instead:
What it should be:
sign(sha512/256(concat(prior_post_sig_hash, tx_auth_flag, tx_fee, tx_nonce)))
What it does now:
sign(prior_post_sig_hash)
A hack-y version of implementing this is:
diff --git a/app/src/common/actions.h b/app/src/common/actions.h
index 1c874bd..98f6933 100644
--- a/app/src/common/actions.h
+++ b/app/src/common/actions.h
@@ -67,6 +67,7 @@ __Z_INLINE zxerr_t get_presig_hash(uint8_t* hash, uint16_t hashLen);
// Heper function to verify the previous signer post_sig_hash in a multisig transaction
__Z_INLINE zxerr_t validate_post_sig_hash(uint8_t *current_pre_sig_hash, uint16_t hash_len, uint8_t *signer_data, uint16_t signer_data_len);
+__Z_INLINE zxerr_t get_presig_hash_from_postsig(uint8_t* hash, uint16_t hashLen, uint8_t* data);
__Z_INLINE void app_sign() {
uint8_t presig_hash[CX_SHA256_SIZE];
@@ -89,7 +90,9 @@ __Z_INLINE void app_sign() {
if (data != NULL && len >= PREVIOUS_SIGNER_DATA_LEN) {
if (validate_post_sig_hash(presig_hash, CX_SHA256_SIZE, data, len) == zxerr_ok) {
// the previous signer post_sig_hash becomes our presig_hash
- memcpy(presig_hash, data, CX_SHA256_SIZE);
+ if (get_presig_hash_from_postsig(presig_hash, CX_SHA256_SIZE, data) != zxerr_ok) {
+ err = zxerr_no_data;
+ }
} else {
// An invalid post_sig_hash from a full signer data should be considered an error
err = zxerr_no_data;
@@ -208,6 +211,45 @@ __Z_INLINE zxerr_t validate_post_sig_hash(uint8_t *current_pre_sig_hash, uint16_
return zxerr_ok;
}
+__Z_INLINE zxerr_t get_presig_hash_from_postsig(uint8_t* hash, uint16_t hashLen, uint8_t* data) {
+ uint8_t hash_temp[SHA512_DIGEST_LENGTH];
+ uint8_t presig_data[PRESIG_DATA_LEN];
+ memcpy(presig_data, data, CX_SHA256_SIZE);
+ {
+ zemu_log("tx_hash: ***");
+ char buffer[65];
+ array_to_hexstr(buffer, 65, presig_data, CX_SHA256_SIZE);
+ zemu_log(buffer);
+ zemu_log("\n");
+ }
+
+ // now append the auth-flag, fee and nonce
+ uint8_t idx = CX_SHA256_SIZE;
+
+ // append the tx auth type
+ if (tx_auth_flag(&presig_data[idx++]) != zxerr_ok)
+ return zxerr_no_data;
+
+ // append the 8-byte transaction fee
+ idx += tx_fee(&presig_data[idx], 8);
+
+ // append the 8-byte transaction nonce
+ idx += tx_nonce(&presig_data[idx], 8);
+
+ if (hashLen < CX_SHA256_SIZE || idx != PRESIG_DATA_LEN)
+ return zxerr_no_data;
+
+ // Now get the presig_hash
+ sha512_256_ctx ctx;
+ SHA512_256_init(&ctx);
+ SHA512_256_starts(&ctx);
+ SHA512_256_update(&ctx, presig_data, PRESIG_DATA_LEN);
+ SHA512_256_finish(&ctx, hash_temp);
+ memcpy(hash, hash_temp, CX_SHA256_SIZE);
+
+ return zxerr_ok;
+}
+
__Z_INLINE zxerr_t get_presig_hash(uint8_t* hash, uint16_t hashLen) {
uint8_t tx_auth[INITIAL_SIGHASH_AUTH_LEN];
MEMZERO(tx_auth, INITIAL_SIGHASH_AUTH_LEN);
update to recent versions of the nanos and nanox SDKs
Hiro wants to support message signing and verification in its wallets, similar to the user flow described the MyCrypto wallet. Ledger device support is needed to offer parity between our software and hardware wallets.
The feature has been discussed in context of Hiro's wallet leather-wallet/extension#1051, and the Stacks blockchain stacks-network/stacks-core#2693
Ultimately, we'd like support for structured data signing, like Ethereum's EIP-721, though this task is scoped to prefixed bytestring message signing.
Spec
sign
, should be added to the @zondax/ledger-blockstack
packageThese are relatively high-level requirements. If more details needed, let us know. We'll follow up with input from the blockchain team.
Due to memory constraints, we have limited the number of:
Can you confirm that this limit is acceptable? or please suggest a number that would be adequate.
As requested by @nndiaye-ledger in Slack, the STX logo should have a white background like the others:
cc @jleni
On npm, the published version is 0.24.0. However, in this repo, the version in package.json (https://github.com/Zondax/ledger-blockstack/blob/main/js/package.json#L3) is 0.22.5. Why the difference in versions?
π zboto Link
A Stacks community member shared this issue where their address is displayed on their device missing a character.
It seems like this could well be related to the logic of how addresses are split to be shown on two pages, as the missing char is missing between the break. This likely only happens for addresses with particularly wide characters, such as W
.
Displayed on Ledger:
SP11KGACP5Q3DMHA3ER4QDWY6WPVT4ZGWTWRVYR1 <-- missing H
Correct address:
SP11KGACP5Q3DMHA3ER4QDWY6WPVT4ZGWHTWRVYR1
Likely solution would be to divide the chars more evenly between the two pages.
Following on from #76, we'd like users to be able to sign structured data, according to the SIP-018 specification
This feature has been implemented in the Hiro Wallet for software wallets already in leather-wallet/extension#2405
@MarvinJanssen has a reference implementation here https://github.com/MarvinJanssen/stx-signed-structured-data
cc/ @markmhx @fbwoolf @beguene
π zboto Link
In implementing message signing for the Hiro Wallet, I've noticed a discrepancy between our software implementation.
Software implementation:
// 'Stacks Message Signing:\n'.length // = 24
// 'Stacks Message Signing:\n'.length.toString(16) // = 18
const chainPrefix = '\x18Stacks Message Signing:\n';
(This uses the wrong phrasing and needs to be updated to Stacks Signed Message)
Zondax implementation:
https://github.com/Zondax/ledger-blockstack/blob/32d8f12fe93bfc9e4c4a65605a88f0a9048695ec/js/src/index.ts#L251-L254
Hiro Wallet is currently using \x18
and Zondax uses \x19
. There was some discussion about the prefix in the issue @MarvinJanssen created.
Stacks Signed Message:\n
is 24 chars, so wouldn't that make the prefix\x18
in little-endian encoding
I believe we should be using \x18
, unless I misunderstand how the prefix length works.
@neithanmo did say here that it should indeed by 19, though I don't understand why.
Edit: actually both wrong:
"Ethereum Signed Message:\n".length.toString(16) // = 19, same used in Eth prefix `\x19`
"Stacks Signed Message:\n".length.toString(16) // = 17
In previous calculations I was counting \n
manually as two chars, so believe that value should instead be:
\x17Stacks Signed Message:\n
π zboto Link
As mentioned on Slack, and discussed with @neithanmo, there are issues signing transactions created by popular Stacks app such as Arkadiko and Alex.
It seems the device is unable to parse certain principal clarity values, used as function arguments. This only happens when a principal belonging to a smart contract is used.
For example: SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.wrapped-stx-token
π zboto Link
related to #90
we should process messages that are longer than 60 characters if expert mode is enable.
As suggested by @kyranjamie with leather-wallet/desktop#291 (comment), the user will understand the micro STX amount displayed on their Ledger device more easily if the Β΅STX suffix is provided.
Otherwise, they could perhaps confuse it easily with whole STX values.
According to the documentation for Transaction payloads:
A STX token-transfer payload is encoded as follows:
We realized that there is a third field called MEMO, according to this issue. this rises the next questions:
let tt_stx = TransactionPayload::TokenTransfer(addr.clone(), 123, TokenTransferMemo([1u8; 34]));
// wire encodings of the same
let mut tt_stx_bytes = vec![];
tt_stx_bytes.push(TransactionPayloadID::TokenTransfer as u8);
addr.consensus_serialize(&mut tt_stx_bytes).unwrap();
tt_stx_bytes.append(&mut vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 123]);
tt_stx_bytes.append(&mut vec![1u8; 34]);
the tt_stx_bytes is the encoded token-transfer payload, which content is:
[0, 5, (1-byte addrs version, Hash160), 8-bytes-amount, [u8; 34(MEMO)]]
lets focus on the first 2 bytes, If we follow the documentation, those 2 bytes should be:
so that the question is, what does the 0x05 byte identifier mean??
Due to memory constraints, we have limited the number of postconditions:
Can you confirm that this limit is acceptable? or please suggest a number that would be adequate.
Re moving away from Blockstack naming
π zboto Link
It's unclear just what needs to be done to make this happen, but we'd like the Stacks app for Ledger Live to have the name "Stacks (STX)" instead of just "Stacks" so that users can search by "stx" and see it in the results for installation.
This is similar to how other tokens show the currency ticker in their listings.
@jleni has read that it's something in the configuration file for Ledger Live.
I don't believe there's an immediate rush for this feature, however inline with the expected behaviour of our addressing conventions, we do need to be able to specify network in addition to derivation path.
This could be in the form of an additional flag passed to the device.
As discussed in #90 and this previous bug on geth, the message prefix should be encoded using a variable length integer.
I'm learning as I go, but as far as I understand, this is because ascii decimal encoding presents the issue that ...\n12...
is ambiguous. Is it length 1
with value 2
, or 12
. And that using a varuint encoding fixes this.
Stacks.js implementation using this encoding
π zboto Link
This formatting should include:
The potentially dangerous sign_msg
function introduced in #82 should be removed. It is based on Ethereum's personal_sign
, the use of which is discouraged since the introduction of the much superior EIP712 standard. EIP712 addresses the many issues and shortcomings of personal_sign
. If the function is adopted in Stacks wallets, it will put the entire ecosystem on a perilous path. We should learn from the Ethereum space and skip this function completely.
personal_sign
had two main use cases:
When it comes to the first, we already have an authentication scheme. Right now it does not allow us to prove address ownership, but that can be achieved in different ways. (I will come back to this at the end.)
The second one is more important, so I will focus on that. Signed messages have been used for many different things in the Ethereum space. I will take on one such example for brevity: off-chain order signing. 0xProtocol allows users to sign an order, which can later be submitted to a smart contract to make a token trade happen. Early versions of the protocol would hash an order structure and request users to sign the order hash via personal_sign
to authorise the trade.
It first looked like this:
And then this:
Does the user have any idea what he or she is actually signing? Obviously not. The user will just have to trust that the dapp fed the right message to the wallet. The messages are also not domain specific, which means that a malicious app could trick a user into signing something meant for another platform. And since the messages that are passed to the wallet are hashes, there is no way to reconstruct the original message in the wallet. It is just all-round terrible UX and highly dangerous. In hindsight it is obvious that the function should never have existed, but back then it is all we had and such signed messages were cutting edge. They allowed for many new an exciting things. Although personal_sign
was largely left behind, it is still causing longer-lasting damage because it normalised the concept of signing byte strings. Many people are now so used to signing on-chain actions in this way, that they just hit Confirm when that wallet screen pops up. The sign_msg
function opens us up to all of it.
I cannot stress that enough. We have so many tools at our disposal to make the UX a lot better and safer. Ethereum did not have the benefit of a human-readable interpreted language like Clarity. There is no good reason to introduce a personal_sign
equivalent for Stacks.
Besides, it seems to be carelessly implemented and actually commits the same mistake as the original personal_sign
implementation.
\x19
is a (Bitcoin) varint
representing the byte length of the prefix, which is correct for Ethereum Signed Message:\n
as it's 25
, but not for Stacks Signed Message:\n
. It seems like it was copy and pasted thoughtlessly. It should have been \x18
.The language in #76 almost makes it sound like sign_msg
is a stopgap solution until we have something better. I really do not think that is the right approach. I rather have nothing, over this function, until a good message signing standard arrives. Hiro, as a shepherd of the Stacks ecosystem, should consider very carefully whether adding such a function is worth it.
There is a draft SIP that describes a structured data signing method much like EIP712. It describes a general structured message signing standard that leverages Stacks wire format, while still allowing safe human-readable text message signing. (If people insist.) I would welcome such a standard or one like it. And to go back to proving address ownership, the linked SIP would make that effortless as well.
I hope that all this is convincing enough to remove sign_msg
. For reference, we are building a hardware wallet at Ryder and will not implement an unstructured message signing function such as this one for the reasons laid out above.
The Stacks Wallet should cause the user's Ledger device to show the amount of STX they will be delegating for Stacking when confirming the transaction on their device.
See example of delegate-stx transaction with amount-ustx
Ledger is reporting that they need this enhancement in order to approve the Stacks app for removal of "developer mode".
From @tjulien-ledger at Ledger:
the actual amount staked when staking in a pool is not displayed on the device when signing the tx while users should be able to verify the amount they are staking.
we will require this feature for a public release.
From @jleni:
I think issue is that staking is a contract call and the amount is a kind of hidden argument of the call. Unless we know the destination hash is for staking.. we cannot guess the meaning of the arguments
we think it is relatively complex issue in the way Stacks work and it is not a bug or a small fix. Stacks contract calls contain this information and parsing depends on destination hashes. On a hardware wallet can be very hard to determine the semantics of a contract call parameters
From development-era the Ledger app says "DO NOT USE".
This should be removed for its production release.
The required info to be shown :
There are some memory limitations, so that, function arguments are not included as part of the visualization.
We have considered moving to GitHub-actions
This is a very punctual overflow that happens when validating transactions or showing it on the screen. It happens when the postconditions apply to whatever account that is not the origin, which involves parsing the raw account address and encoding it to the c32 stacks format.
Related #102
Stacks authentication uses a the derivation path m/888'/0'/0'/<account>
to generate "identify" keys.
The Stacks Ledger app doesn't work when passing a m/888'
leading derivation path, returning the error Data is invalid
. This is preventing us getting the public keys for this path, and may also impact our ability to use #102
Sharing a commend from the ledger team:
JWT tokens are signed on a dedicated path (888'/0').
Header for JWW tokens must be exactly {"typ":"JWT","alg":"ES256K"}. I find it a bit restrictive (some wallets may add spaces).
Displaying the hash of the data to sign is not good from a security point of view.
I suggest, if possible, displaying the domain name contained in the JWT token on the device screen. This could be added in a future version.
This could be a bit problematic as it would probably require a full JSON parser in a device whose memory is very limited.
π zboto Link
Currently, the number of PostConditions is limited to 16 due to memory restrictions. We need to format each post-conditions before presenting the relevant info through the UI. This formatting would be on-demand to reduce the memory consumption during the parsing stage.
Per @nndiaye-ledger in Slack: "I have a freezing issue as I can't navigate through the menus or quit the app"
Requested for diagnosis:
As mentioned in Slack, in order for the Stacks Ledger integration to be compatible with the Stack authentication flow, a JWT payload needs to be signed by a key on the "identities keychain", the m/888'/0'
derivation path.
This feature would need to accept a JWT payload and return a signature.
The implementation would need to function the same as our TypeScript implementation found here.
@kyranjamie could you provide updated files for the icons?
In #103 I stated that the data derivation path we need to support is m/888'/0'/0'/<account>
This is not the case. The scheme used is m/888'/0'/<account>
, with one fewer 0'
.
Would it be possible to update the Ledger app so that this new path is supported by the getIdentityPubKey
method? Here's an example of where the path is being used in the @stacks/wallet-sdk package.
π zboto Link
There are two errors in CI not related to the code but:
Device: Nano S
Stacks app: 0.19.0
Secure Element: 2.1.0
Noting one difference I've seen lately. In the LedgerError
object, we have the response number 0x6e00
/ 28160
for AppDoesNotSeemToBeOpen
.
But I've noticed when calling getVersion
when the app is not open, we get the unknown response code 0x6E01
/ 28161
Hey, I recently found that this Ledger app does not support signing the string-ascii
or string-utf8
Clarity types. This unfortunately blocks a wide range of function calls from being used with a Ledger.
The full list of Clarity types (and their prefix) can be found here: https://github.com/blockstack/stacks.js/blob/master/packages/transactions/src/clarity/clarityValue.ts#L25
We've gotten reports in Discord that users can authenticate to the Stacks Wallet v4.0.2 with Ledger Nano X but cannot subsequently send transactions successfully.
Ledger's redeployment of the Stacks app for provider 1 instead of 4 doesn't appear to have resolved this issue.
The 5757'
prefix was used in multisig wallet setups in Stacks 1.0, where the derivation path was similar to the derivation path used for bitpay multisig and electrum multisig:
m/5757'/<account-index>'/<co-signer-index>/<change>/<address-index>
Where instead of the 45' purpose, 5757' is used.
e.g.,
m/5757'/0'/0/0/0
m/5757'/0'/0/0/1
It would be helpful for those old setups to have support for those paths as well.
For mainnet vs. testnet address encodings, this should behave similar to the current interface, where the address version can be passed in by the caller library.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. πππ
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google β€οΈ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.