Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

crypto: Rederive pubkey for encode/decode #6989

Merged
merged 1 commit into from
Jan 5, 2023
Merged

crypto: Rederive pubkey for encode/decode #6989

merged 1 commit into from
Jan 5, 2023

Conversation

joyqvq
Copy link
Contributor

@joyqvq joyqvq commented Dec 22, 2022

#6940

What

This changes the serialization of a SuiKeyPair from base64 encoded flag || pubkey || privkey to flag || privkey.

Why

This is to never trust the pubkey bytes inputs when but to always derive it directly from privkey when bootstrapping from read/write from files.

Notes for breaking change

If you have existing keys in sui.keystore, add new keys will throw error because the old serialization cannot be parsed anymore.

target/debug/sui keytool generate ed25519
Invalid Keypair file InvalidInput "~/.sui/sui_config/sui.keystore"

How to Fix

If you are ok with wiping the keys, rm ~/.sui/sui_config/sui.keystore and then sui client new-address should work again and new keys are formatted with only private key.

If you would like to keep the existing keys, you will need to convert them manually. Here is a manual python script:

import base64
def convert(s): 
    bytes = base64.b64decode(s)
    print(f"{len(bytes)} orginal: {s}")
    if len(bytes) == 65 and bytes[0] == 0:
        print(f"ed25519 key, converted: {base64.b64encode(bytes[:33])}")
    elif len(bytes) == 66 and bytes[0] == 1:
        print(f"secpk1 key, converted: {base64.b64encode(bytes[:33])}")
    elif len(bytes) == 66 and bytes[0] == 2:
        print(f"secpr1 key, converted: {base64.b64encode(bytes[:33])}")
    elif len(bytes) == 128:
        print(f"bls key, converted: {base64.b64encode(bytes[:32])}")
    elif len(bytes) == 64:
        print(f"no flag ed25519, converted: {base64.b64encode(bytes[:32])}")
convert("$INSERT_KEY_HERE")

To check if the conversion works, all keys should be of length 33 regardless of the the signature scheme flag.

cat ~/.sui/sui_config/sui.keystore
[
  "ATWhXWl/zlrENWnc2AIbjChwAB5FGMsBp3RBcKUCUe90",
  "AOhFYQNpqLgJyYmfg975nwQmQ8YCPXCnTw/l8ZjIBEqy",
  "ArYljv5d6NQWahm8/oDF7xa+E5f6XpyMdDxTfPuxhLzD"
]

Test

target/debug/sui client new-address ed25519
Created new keypair for address with scheme ED25519: [0x25142b365037715dae989375ac2b74b2517edd62]
Secret Recovery Phrase : [original rent brick leader middle target cheap rather choose elephant system abstract despair math pudding music garment young syrup foam core try good market]

target/debug/sui client new-address secp256k1
Created new keypair for address with scheme Secp256k1: [0x049045049bbcd071609e0caf88b0542ed791aa3e]
Secret Recovery Phrase : [enroll field short hurry unique morning glad can bridge immune sorry blood badge collect gesture math gallery rifle lens surge nasty hurt couch delay]

target/debug/sui client new-address secp256r1 
Created new keypair for address with scheme Secp256r1: [0xa76b0f7b5e19b86c768486b1ffb757b9952f8d61]
Secret Recovery Phrase : [witness stuff sorry try chuckle cliff moon share purchase person runway online usual exit lunar same limit act walk spread abandon cradle basic coin]

target/debug/sui keytool list
                Sui Address                 |              Public Key (Base64)              | Scheme
----------------------------------------------------------------------------------------------------
 0x049045049bbcd071609e0caf88b0542ed791aa3e | AQKxLzhpin3HwLzcjscgPGzrcdRvCX6vZT0xTH5X6PDKRA== | secp256k1
 0x25142b365037715dae989375ac2b74b2517edd62 | AOBT/yUKPQuCNh8eiLaGyyIoUTa3HbWpxwYxM/1dNdoV  | ed25519
 0xa76b0f7b5e19b86c768486b1ffb757b9952f8d61 | AgNy9UEXhU/K1aro4vY/QxssR6pfw9K6NmNLsUDi0VydSg== | secp256r1

@vercel
Copy link

vercel bot commented Dec 22, 2022

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated
explorer 🔄 Building (Inspect) Jan 5, 2023 at 3:43AM (UTC)
2 Ignored Deployments
Name Status Preview Comments Updated
explorer-storybook ⬜️ Ignored (Inspect) Jan 5, 2023 at 3:43AM (UTC)
wallet-adapter ⬜️ Ignored (Inspect) Jan 5, 2023 at 3:43AM (UTC)

@joyqvq joyqvq marked this pull request as ready for review December 22, 2022 07:18
@@ -136,61 +136,56 @@ impl FromStr for SuiKeyPair {
}

impl EncodeDecodeBase64 for SuiKeyPair {
/// Encode a SuiKeyPair as `flag || pubkey || privkey` in Base64. The pubkey bytesare derived directly from privkey.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bytes _ are

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, do we want for the base64 to have flag || pubkey || privkey or flag || privkey and then rederive the pub key as we do in the rest of serializers?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand base64 is mostly for external UX, so I'm happy with both decisions, but we should document it properly as we received some feedback re different serializers or behavior in the past.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO its more consistent to keep it flag || pubkey || privkey since the struct itself is called a keypair. As long as in our system, we read a keypair and ignores the pubkey part but to only read the privkey bytes to derive the keypair, i think its safe and less confusing.

+1 lemme document this more explicitly in comments

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It depends on why we need such a serializer (cause encoding is usually used for (de)serialization). Assuming each receiver of the encoded format wants to deserialize the key pair into internal structs, then we should promote secure deserialization by rederiving the pub part for better hygiene, especially sensitive object types.

Re the argument "since the struct is called keypair": there are many types across programming languages that their serialized bytes are compressed for a reason (security or bandwidth). I'd only buy the use case of compatibility with other (ie JS) libs to allow for quick conversion without rederivation, but still, IMO it's better to provide a tool to rederive Vs encoding it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revised.

Copy link
Collaborator

@kchalkias kchalkias left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this work affected by the recent changes in fastcrypto + conflicts re serialization api changes?

@joyqvq
Copy link
Contributor Author

joyqvq commented Jan 2, 2023

Is this work affected by the recent changes in fastcrypto + conflicts re serialization api changes?

rebased.

crates/sui-core/tests/staged/sui.yaml Outdated Show resolved Hide resolved
}
}
Base64::encode(&bytes[..])
}

/// Decode a SuiKeyPair from Base64 encoded `flag || privkey || pubkey` in Base64. Use the privkey bytes only to derive the pubkey and the full keypair instead of using the pubkey bytes.
fn decode_base64(value: &str) -> Result<Self, eyre::Report> {
let bytes = Base64::decode(value).map_err(|e| eyre::eyre!("{}", e.to_string()))?;
match bytes.first() {
Some(x) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldn't something like
match bytes.first() { Some(Ed25519SuiSignature::SCHEME.flag()) => ....

work here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

simplified this part a bit

worker-key-pair:
value: AB8qeQGoQuTTYjvGHOHBcX0udo4P1y34NBr1ZhW5FvA4fsz863qJR38mPjuvloaZBE4vbibFPgrwQXUa+OGTTNM=
value: AH7M/Ot6iUd/Jj47r5aGmQROL24mxT4K8EF1Gvjhk0zT
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is used for swarm test only

@joyqvq joyqvq enabled auto-merge (squash) January 5, 2023 03:50
@joyqvq joyqvq merged commit 28c4823 into main Jan 5, 2023
@joyqvq joyqvq deleted the rederivekp1 branch January 5, 2023 04:02
mwtian added a commit that referenced this pull request Jan 5, 2023
lxfind added a commit that referenced this pull request Jan 5, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants