Skip to content

Latest commit

 

History

History
116 lines (83 loc) · 5.23 KB

File metadata and controls

116 lines (83 loc) · 5.23 KB
  • FIP: 68
  • title: Login into Portable account with metamask
  • author: Sabyasachi Patra, @asabya
  • status: draft
  • created: 2023-03-01

Summary

Addressing web3 login workflow to portable account where users can retrieve their wallet in a decentralized manner from browser with metamask.

Context, motivation and guide level explanation

The solution worked out here allows to log in to the user account from any browser into the same account described in FIP-59 created with fdp-create-account project.

Reference-level explanation

For the login to work with metamask, user should already have a portable account created with fdp-create-account.

As we know while creating a portable account users can provide a 12 word mnemonic phrase for the portable wallet or a 12 word mnemonic will be generated for them.

This solution starts with importing the portable wallet into metamask. After that it is a two-step process

Step 1:

ConnectPortableAccountWithWallet(userName, passPhrase, addressHex, signature string) error

This function will get the portable wallet (wallet seed) and re-upload an alternate socTopic described later

Step 2:

LoginWithWallet(addressHex, signature string) (*user.Info, error) 

This function will get the encrypted portable wallet (wallet seed) and username from the alternate socTopic and decrypt it with the signature provided by the user.

Uploading portable wallet (wallet seed) to Ethereum Swarm to an alternate socTopic

In FIP-59 we have discussed, about the socTopic which is a topic in the swarm where the portable account is stored. In this solution we will be using an alternate socTopic, but with the same logic, to store the wallet seed.

socTopic = H(fdpLoginVersion + portableWalletAddress + signature)

Where portableWalletAddress and the signature will be provided by the user. As we have already imported the portable wallet into metamask, we can get the portableWalletAddress from there. The signature will be generated by signing the portableWalletAddress with metamask with the portable wallet.

Wallet seed encryption

In FIP-59 we have discussed, about the encryption of wallet seed and storing it in a chunk.

  • We will first generate FIP-59 socTopic with username and password provided by the user.
  • Then download the wallet seed chunk from FIP-59 socTopic and decrypt it with the password provided by the user.

We have the seed now. We will store the username with the seed for identifying the user while logging in with metamask.

Creating the chunk

The content of the chunk will look something lite this

SEED + USERNAME_SIZE + USERNAME + RANDOM_DATA

We store the username length in 8 bytes, then the username and then the random data.

The SEED + USERNAME_SIZE + USERNAME will be padded with random data with byte length CHUNK_SIZE - SEED_SIZE - IV_LENGTH - USERNAME_LENGTH - USERNAME_SIZE.

*** USERNAME_SIZE is 8 bytes, USERNAME_LENGTH is the length of the username provided by the user.

The resulted data will be encrypted with AES, where the encryption key is the SHA256 hash of the signature.

func (*Info) PadSeedName(seed []byte, username string, passphrase string) ([]byte, error) {
    usernameLength := len(username)
    endIndexBytes := make([]byte, nameSize)
    binary.LittleEndian.PutUint64(endIndexBytes, uint64(usernameLength))
    paddingLength := utils.MaxChunkLength - aes.BlockSize - seedSize - nameSize - usernameLength
    randomBytes, err := utils.GetRandBytes(paddingLength)
    if err != nil { 
        return nil, err
    }
    chunkData := make([]byte, 0, utils.MaxChunkLength)
    chunkData = append(chunkData, seed...)
    chunkData = append(chunkData, endIndexBytes...)
    chunkData = append(chunkData, []byte(username)...)
    chunkData = append(chunkData, randomBytes...)
    encryptedBytes, err := utils.EncryptBytes([]byte(passphrase), chunkData)
    if err != nil {
        return nil, fmt.Errorf("seed padding failed: %w", err)
    }
    return encryptedBytes, nil
}

Getting seed back from the chunk

We decrypt the chunk with the same signature and then get the seed and username from the decrypted data.

func (*Info) RemovePadFromSeedName(paddedSeed []byte, passphrase string) ([]byte, string, error) {
	decryptedBytes, err := utils.DecryptBytes([]byte(passphrase), paddedSeed)
	if err != nil {
		return nil, "", fmt.Errorf("seed decryption failed: %w", err)
	}
	usernameLength := int(binary.LittleEndian.Uint64(decryptedBytes[seedSize : seedSize+nameSize]))
	return decryptedBytes[:seedSize], string(decryptedBytes[seedSize+nameSize : seedSize+nameSize+usernameLength]), nil
}

Implementation can be found here

Prior art

FIP-59

Copyright

Copyright and related rights waived via CC0.