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

ARC-52 Contextual Wallet API #239

Open
wants to merge 53 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
f533319
Contextual Wallet API
ehanoc Aug 29, 2023
2a8eeb9
Merge branch 'main' of github.com:algorandfoundation/ARCs into dev_arc78
ehanoc Aug 29, 2023
fcdd0e4
Merge branch 'main' into pr/239
SudoWeezy Aug 30, 2023
ba9a4c1
Assign number + fix linting
SudoWeezy Aug 30, 2023
62f7bf6
Merge branch 'wallet-api-context' of github.com:ehanoc/ARCs into dev_…
ehanoc Sep 25, 2023
585d48a
Update specification keygen & sign
ehanoc Sep 25, 2023
5874b3f
Update authors
ehanoc Sep 25, 2023
2488fa5
doc: Update ARC52 API and description
ehanoc Oct 25, 2023
048e508
Fix lint + add Rational, Reference Implementation & Security Consider…
SudoWeezy Oct 25, 2023
76899c7
ECDH section and examples added
ehanoc Oct 27, 2023
3f4fc71
Add BIP44 link
ehanoc Oct 27, 2023
aa256ab
Rationale section after spec
ehanoc Oct 27, 2023
1fb8d83
Remove dependency on ARC44
ehanoc Oct 27, 2023
0107c5c
Add reference implementation
ehanoc Oct 27, 2023
326445b
BIP32-Ed25519 by the book impl and test vectors. Reference Paper incl…
ehanoc Oct 29, 2023
8feb165
Update README with reference impl link
ehanoc Oct 29, 2023
9ea1f1d
Update use cases to reject regular transaction signing
ehanoc Nov 1, 2023
ca90868
Small refactor
ehanoc Nov 1, 2023
8e29901
Test Vectors against known ed25519 javascript lib
ehanoc Nov 3, 2023
8ad26e3
Test Vectors bip39 bip32-ed25519 bip44
ehanoc Nov 3, 2023
6692a77
Authentication challenge Schema example. Added cases for when encodin…
ehanoc Nov 4, 2023
f459553
Link schema examples on spec
ehanoc Nov 8, 2023
ea9dd28
Fix: SHA256(0x01||seed), 0x01 was 0x00 array
ehanoc Nov 27, 2023
62b841e
Fix Html link to allow access from generated website
SudoWeezy Jan 11, 2024
660e82f
Updating ECDH tests so that bob has different rootKey than Alice
ehanoc Jan 24, 2024
b38ef81
Merge branch 'wallet-api-context' of github.com:ehanoc/ARCs into dev_…
ehanoc Jan 24, 2024
4efe105
Fix: slicing scalar too soon before signing
ehanoc Feb 27, 2024
f36cf8b
chore: specify types
ehanoc Feb 27, 2024
8e26a6f
fix: ECDH using common Curve25519 point and concatenation into hash f…
ehanoc Feb 27, 2024
0192f5c
chore: comment in ECDH function
ehanoc Feb 27, 2024
d44a8e9
feat: simplify signData
HashMapsData2Value Feb 27, 2024
5766c5f
chore: clarifies tests, small refactor of ecdh
HashMapsData2Value Mar 4, 2024
3ab2169
fix: remove commented out test, for now
HashMapsData2Value Mar 4, 2024
f66b436
chore: Refactor, include tweetnacl to verify correctness
ehanoc Mar 13, 2024
950020c
1-1 feature parity with Kotlin
ori-shem-tov Mar 25, 2024
87d5291
add public key verification to trnasaction signing test
ori-shem-tov Mar 26, 2024
4319a82
Merge pull request #1 from ori-shem-tov/wallet-api-context
HashMapsData2Value Mar 26, 2024
6e8337c
Revert "1-1 feature parity with Kotlin"
ehanoc Mar 26, 2024
8b4de1d
Merge pull request #2 from ehanoc/revert-1-wallet-api-context
ehanoc Mar 26, 2024
ad3642a
1-1 feature parity with Kotlin
ori-shem-tov Mar 25, 2024
a151b98
remove algosdk dep
ori-shem-tov Mar 27, 2024
718e059
Merge pull request #3 from ori-shem-tov/wallet-api-context
ehanoc Mar 27, 2024
421089f
Update assets/arc-0052/contextual.api.crypto.ts
ehanoc Mar 27, 2024
43aa9c0
Update assets/arc-0052/contextual.api.crypto.ts
ehanoc Mar 27, 2024
ced1c70
feat: support to Peikerts bip32ed25519 ammendent to derivations
ehanoc Apr 24, 2024
b84c6be
feat: support to Peikerts bip32ed25519 ammendent to derivations
ehanoc Apr 24, 2024
1b51687
doc: Update README with new test vectors and details
ehanoc Apr 25, 2024
333601d
fix: package.json, logs, run all tests
ehanoc Apr 25, 2024
e008a3d
chore: Add public derivation tests and use cases. Check for correctne…
ehanoc Apr 25, 2024
5b1a230
chore: rearranged lines to align with steps in paper
HashMapsData2Value Apr 26, 2024
c068a44
chore: clarify childChainCode naming
HashMapsData2Value Apr 26, 2024
a6ffb6e
chore: remove whitespace
HashMapsData2Value Apr 26, 2024
6a02c3f
fix: zero out the most significant bits in a little endian array. Wro…
ehanoc May 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
210 changes: 210 additions & 0 deletions ARCs/arc-0052.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
---
arc: 52
title: Wallet Contextual KeyGen and Signing
description: Algorand Wallet API for Contextual KeyGen and Signing
author: Bruno Martins (@ehanoc), Patrick Bennett (@pbennett), Kevin Wellenzohn (@k13n)
discussions-to: https://github.com/algorandfoundation/ARCs/issues/239
status: Draft
type: Standards Track
category: Interface
created: 2023-06-30
---

# Algorand Wallet Contextual KeyGen and Signing

## Abstract

This document specifies an expansion of the Algorand Wallet API to support contextual key generation and signing for **non-algorand-transaction** contexts.

## Specification

The key words "**MUST**", "**MUST NOT**", "**REQUIRED**", "**SHALL**", "**SHALL NOT**", "**SHOULD**", "**SHOULD NOT**", "**RECOMMENDED**", "**MAY**", and "**OPTIONAL**" in this document are to be interpreted as described in <a href="https://www.ietf.org/rfc/rfc2119.txt">RFC-2119</a>.

> Comments like this are non-normative.

### Overview

> This overview section is non-normative.

At a high level this specification defines a new API to generate keys and signing in other contexts than transactions.

> Interfaces are defined in TypeScript. All the objects that are defined are valid JSON objects.

#### Contexts

An enum of contexts that are supported by the wallet. This enum is extensible and can be expanded in the future to support more contexts (i.e messaging, ephemeral keys, other chain derivations, etc)

```ts
enum KeyContext {
Address = 0, // Otherwise known as Account?
Identity = 1,
...
}
```

#### BIP44 Context Derivation Paths

There is several reasons why we want to avoid key re-use in different contexts. One of the reasons is that we want to avoid the possibility of **leaking** information about the identity of the user. Another reason is that there is some cryptographic security concers when re-using keys or in case of keys being compromised the impact is **compartmentalized**.

For this reason is advisable to have different derivation paths for different contexts supporting recoveribility of the keys by using the same mnemonic as before.

| Purpose | Coin Type | Account | Change | Address Index | Context |

Choose a reason for hiding this comment

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

A few points here:

  1. Is there any particular reason why we opted for purpose 44 equal to BIP-44? IMO we should use a different purpose from Bitcoin's wallets.
  2. Coin Type: we should provide default registered coin types (like here), at least for ALGO, and common ASAs e.g. USDC, gALGO, goETH, goBTC, ...
  3. Are we enforcing the same account discovery algorithm ad BIP-44 here? Does it make sense to put a limit on account indexes that are valid only if previously active on-chain? What if I want to reserve 2/3 consecutive derivation paths for accounts that I will never use on-chain (maybe one for document encryption, another for authentication, erc..)

Copy link
Author

Choose a reason for hiding this comment

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

A few points here:

  1. Is there any particular reason why we opted for purpose 44 equal to BIP-44? IMO we should use a different purpose from Bitcoin's wallets.
  2. Coin Type: we should provide default registered coin types (like here), at least for ALGO, and common ASAs e.g. USDC, gALGO, goETH, goBTC, ...
  3. Are we enforcing the same account discovery algorithm ad BIP-44 here? Does it make sense to put a limit on account indexes that are valid only if previously active on-chain? What if I want to reserve 2/3 consecutive derivation paths for accounts that I will never use on-chain (maybe one for document encryption, another for authentication, erc..)

Hey Stefano,

So, regarding

  1. We have discussed this before. It doesn't really matter that is the same path as in bitcoin, it's 2 different elliptic curves. And this is following the spec for the W3C Universal Wallet API(https://github.com/w3c-ccg/universal-wallet-interop-spec)

  2. Yea, we could extend it in the future to other ecosystems / applications

  3. The discovery algorithm should be the same as any other chain wallet. Recursive finding until no account / address has any activity. For off-chain use, if you need to recovery, all is still recoverable, depending on the context you need to loop through them and see if you can use them, or can include them in wallet backups.

Copy link

@deanstef deanstef Feb 22, 2024

Choose a reason for hiding this comment

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

  1. Yes we use different curves. However, this ARC aims at providing "Contextual Wallet APIs" for different purposes and contexts. In that case, we might want to use another purpose as a derivation path. For example, SLIP-0013 provides authentication for HD wallets and uses purpose = 13' for authentication with deterministic hierarchy.
  2. Yes! Maybe providing a table with "reserved" values could be useful before having fragmented implementations in the ecosystem. WDYT?
  3. I am brainstorming here. It depends on the purpose and context. While I think the discovery algorithm is needed for multi-account HD wallets (BIP-44-like) it might be not necessary in other cases (e.g. authentication with non-rekeyed accounts or document encryption).

Choose a reason for hiding this comment

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

I don't understand the point about assigning the coin type to different ASAs. Given that Algo is needed to opt in and transact with an ASA regardless, how would assigning ASA IDs to coin type work? And what if you want/need to hold multiple ASAs in one address?

Copy link

@deanstef deanstef Feb 23, 2024

Choose a reason for hiding this comment

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

What I like about having different coin types for ASAs is that it forces users to split their holdings into different addresses. This would improve security in case of compromised keys.

Maybe changing the coin type is not the best approach because of ALGO though.

As you mentioned, ALGO opt-in is a protocol requirement as well as fees, so any address should be intended to hold ALGOs with the ASA. However, we could think of another derivation level dedicated to ASAs. Some related discussion here.

Choose a reason for hiding this comment

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

I feel that the biggest risk here is that the seed gets compromised, in which case all addresses are compromised regardless of the derivation path. I think it is unlikely that a single derived private key (i.e. a scalar output of the deriveKey function) could be compromised without the seed or many addresses also being compromised in the process.

If we want users want to split their funds wallets can encourage them and make it easy to use different indices or different accounts for accepting different tokens.

| :-----: | :-------: | :-----: | :----: | :-----------: | :-----------: |
| 44' | 283' | x' | x | x | Addresses |
| 44' | 0' | x' | x | x | Identity Keys |

see <a href="https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki"> `BIP-44` </a>

#### Wallet New Interface

This new interface is for the wallet to implement. It is an extension of the current wallet interface.

This extension is **OPTIONAL** and separate from the current interface that handles address generation and transaction signing.

```ts
interface Wallet {
...
keyGen(context: KeyContext, account:number, keyIndex: number): Promise<Uint8Array>;
Copy link

@deanstef deanstef Feb 22, 2024

Choose a reason for hiding this comment

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

If we want to comply with BIP-32 we should be mandatory on the number size here.

Public derivation indices range from 0 through 231-1. The hardened derivation indices range from 231 through 232-1. Therefore, numbers must be at least 4 bytes.

Copy link
Author

Choose a reason for hiding this comment

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

It's hardened within the implementation.

accounts are hardend
indexes are not

Choose a reason for hiding this comment

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

I am ok with having those well specified in the implementation. However, I would like to be mandatory in the spec as well.

IMHO those constraints should be clarified in the ARC-52 spec, or at least should be redirected to BIP-32. My issue with the current text is that an Algorand wallet might be compliant with ARC-52 but not compliant with BIP-32.

Copy link
Author

Choose a reason for hiding this comment

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

BIP-44 defines the hardening at account / index level.

I don't think we should expose that here as this is a wallet API. Anyone that would like to do BIP32 but not BIP44 compliant, should just use a low level BIP32 API to derive keys that way (i.e non hardened accounts or hardened indexes instead)

Choose a reason for hiding this comment

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

Regardless of the value that those indexes may assume, which depends on the hardened / non-hardened implementation, the type number that the API requires for account and keyIndex params must be a 4-byte integer.

My issue here is that ARC-52 assumes that the reader is confident with BIP-32 and understands the constraints of that specification. If we choose to make this assumption (which I believe is reasonable), it would be beneficial to explicitly mention it in the overview.

Copy link
Author

Choose a reason for hiding this comment

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

I understand, although i see problems with that.

We could expose more options or a more "advanced" API. problem is not every integrator understands the nuances of technical standards that are used underneath. Good API's in my opinion are strict and don't offer too many options that can be miss used.

There can be an integrator that understands BIP-32 and another that doesn't. This would lead for implementations that might not be interoperable and recovery in one wallet wouldn't work on another wallet.

signData(context: KeyContext, account:number, keyIndex: number, message: Uint8Array): Promise<Uint8Array>;
ECDH(context: KeyContext, account: number, keyIndex: number, otherPartyPub: Uint8Array): Promise<Uint8Array>
...
}
```

##### keyGen

Wallet API function method that derives a key in a given context. The key should be derived from the wallet's master key. The key derivation should be deterministic, meaning that the same key should be derived from the same context and name.

In order to derive a key, the wallet should lookup the different derivation paths for the given context.

- **method name**: `keyGen`
- **parameters**:

- `context`: The context of the key to be generated
- `account`: The account index to be used for key derivation. The value value should be hardened as per BIP44
- `keyIndex`: The key index to be used for key derivation.

Choose a reason for hiding this comment

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

Bip-44 includes the change type but we are not incorporating it?


- **returns**: Byte array cointaing the public key, which is a point on the Ed25519 elliptic curve. The public key is encoded as compressed EC point: the y-coordinate, combined with the lowest bit (the parity) of the x-coordinate, making up 32 bytes.

###### Example

```ts
const identityKey: Uint8Array = await wallet.keyGen(KeyContext.Identity, 0, 0);
```

##### sign

Signing operation that can be used to perform arbitrary signing operations in a given context. These can be for example:

- Identity signatures (Documents, DIDs, etc)
- Address signatures (i.e Signing auth challenges)

The function should check if the data, before and after decoding, isn't a regular Algorand transaction. If it is, the function should throw an error.

- **method name**: `sign`
- **parameters**:
- `data`: The data to be signed
- `context`: The context of the key to be used for signing
- `account`: The account index to be used for key derivation. The value value should be hardened as per BIP44
- `keyIndex`: The key index to be used for key derivation.
- `metadata`: Object to describe the type of data that is being signed. It should specify the encoding used (i.e msgpack, cbor, etc) and a schema so that the wallet can decode it to show the user.
- `encoding`: The encoding used for the data
- `schema`: The schema of the data in the form of a JSON schema. The schemas are an powerful tool for wallet providers to be able to enforce the type and format of the data that is being signed. You can check schema examples <a href= "https://github.com/algorandfoundation/ARCs/tree/main/assets/arc-0052/schemas">here</a>.
- **returns**: Byte array containing the signature. The signature is encoded as a 64-byte array (32 + 32 bytes). It holds a compressed point R + the integer s (confirming that the signer knows the msg and the privKey).
- Throws error if data doesn't match the schema

###### Example

```ts
const message = {
letter: "Hello World",
};

const encoded: Buffer = Buffer.from(to_base64(JSON.stringify(message)));

// Schema of what we are signing
const jsonSchema = {
type: "object",
properties: {
letter: {
type: "string",
},
},
};

const metadata: SignMetadata = {
encoding: Encoding.BASE64,
schema: jsonSchema,
};

const signature: Uint8Array = await cryptoService.signData(
KeyContext.Address,
0,
0,
encoded,
metadata
);
expect(signature).toHaveLength(64);
```

##### ECDH (Elliptic Curve Diffie-Hellman)

This operation is used to perform a secret key aggrement between two parties. This is useful for example for deriving a shared symmetric secret that is only known between the participants.

The symmetric secret can be used for encrypting and decrypting messages.

- **method name**: `ECDH`
- **parameters**:
- `context`: The context of the key to be used for signing
- `account`: The account index to be used for key derivation. The value value should be hardened as per BIP44
- `keyIndex`: The key index to be used for key derivation.
- `otherPartyPub`: The public key of the other party
- **returns**: Byte array containing the shared secret. The shared secret is a 32-byte array.

###### Example

```ts
const aliceKey: Uint8Array = await cryptoService.keyGen(
KeyContext.Address,
0,
0
);

const bobKey: Uint8Array = await cryptoService.keyGen(KeyContext.Address, 0, 1);

const aliceSharedSecret: Uint8Array = await cryptoService.ECDH(
KeyContext.Address,
0,
0,
bobKey
);

const bobSharedSecret: Uint8Array = await cryptoService.ECDH(
KeyContext.Address,
0,
1,
aliceKey
);

// Same secret can be used to encrypt by Alice and decrypt by Bob
//and vice-versa
expect(aliceSharedSecret).toEqual(bobSharedSecret);
```

## Rationale

At it's core, blockchain keys and signatures are just implementations of cryptographic primitives. These primitives can be used for a wide variety of use cases, not just for the purpose of signing transactions. And wallets being applications that contain or serve as Key Management Systems (KMS), should be able to support a multiple of other use cases that are not just related to transactions. This creates the possibility of attaching on-chain behavior with off-chain behavior, allowing for example to concepts of Identity and Authentication.

The current wallet APIs available only support key derivation for addresses and signing for transactions. In the broader context of Algorand and web3 ecosystems, there is a need for a more flexible API that allows for contextual key generation and signing. An example of this could be identity dedicated keys (i.e DIDs), or passkeys for authentication.

## Reference Implementation

Reference Implementation is included in the `assets` folder <a href= "https://github.com/algorandfoundation/ARCs/tree/main/assets/arc-0052">here</a>.

## Security Considerations

As this functionality is based on EdDSA, BIP32 HD derivations and ECDH, the security considerations of those specifications apply. Particularly, the Ed25519 derived keys must be clamped to avoid known attacks
ehanoc marked this conversation as resolved.
Show resolved Hide resolved

## Copyright

Copyright and related rights waived via <a href="https://creativecommons.org/publicdomain/zero/1.0/">CCO</a>.
1 change: 1 addition & 0 deletions assets/arc-0052/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
Binary file not shown.
107 changes: 107 additions & 0 deletions assets/arc-0052/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# ARC52 reference implementation

This implementation is not meant to be used in production. It is a reference implementation for the ARC52 specification.

It shows how the ARC52 specification can be implemented on top of a BIP39 / BIP32-ed25519 / BIP44 wallet.

note: Bip32-ed25519 is meant to be compatible with our Ledger implementation. Meaning that we only do hard-derivation at the level of the `account` as per BIP44

## Run

```shell
$ yarn
$ yarn test
```

## Output

```shell
PASS ./contextual.api.crypto.spec.ts
Contextual Derivation & Signing
✓ (OK) Root Key (2 ms)
(JS Library) Reference Implementation alignment with known BIP32-Ed25519 JS LIB
✓ (OK) BIP32-Ed25519 derive key m'/44'/283'/0'/0/0 (135 ms)
✓ (OK) BIP32-Ed25519 derive key m'/44'/283'/0'/0/1 (120 ms)
✓ (OK) BIP32-Ed25519 derive PUBLIC key m'/44'/283'/1'/0/1 (284 ms)
✓ (OK) BIP32-Ed25519 derive PUBLIC key m'/44'/0'/1'/0/2 (277 ms)
(Derivations) Context
✓ (OK) ECDH (4 ms)
✓ (OK) ECDH, Encrypt and Decrypt (5 ms)
✓ Libsodium example ECDH (8 ms)
Addresses
Soft Derivations
✓ (OK) Derive m'/44'/283'/0'/0/0 Algorand Address Key (1 ms)
✓ (OK) Derive m'/44'/283'/0'/0/1 Algorand Address Key (1 ms)
✓ (OK) Derive m'/44'/283'/0'/0/2 Algorand Address Key (2 ms)
Hard Derivations
✓ (OK) Derive m'/44'/283'/1'/0/0 Algorand Address Key (3 ms)
✓ (OK) Derive m'/44'/283'/2'/0/1 Algorand Address Key (2 ms)
✓ (OK) Derive m'/44'/283'/3'/0/0 Algorand Address Key (1 ms)
Identities
Soft Derivations
✓ (OK) Derive m'/44'/0'/0'/0/0 Identity Key (1 ms)
✓ (OK) Derive m'/44'/0'/0'/0/1 Identity Key (2 ms)
✓ (OK) Derive m'/44'/0'/0'/0/2 Identity Key (1 ms)
Hard Derivations
✓ (OK) Derive m'/44'/0'/1'/0/0 Identity Key (2 ms)
✓ (OK) Derive m'/44'/0'/2'/0/1 Identity Key (1 ms)
Signing Typed Data
✓ (OK) Sign Arbitrary Message against Schem (54 ms)
✓ (FAIL) Signing attempt fails because of invalid data against Schema (33 ms)
Reject Regular Transaction Signing. IF TAG Prexies are present signing must fail
✓ (FAIL) [TX] Tag
✓ (FAIL) [MX] Tag (1 ms)
✓ (FAIL) [Program] Tag
✓ (FAIL) [progData] Tag (1 ms)
Reject tags present in the encoded payload
✓ (FAIL) [TX] Tag (2 ms)
✓ (FAIL) [MX] Tag
✓ (FAIL) [Program] Tag (1 ms)
✓ (FAIL) [progData] Tag


```

## BIP39 / BIP32-ed25519 / BIP44 Test Vectors

All keys are in extended format: [kl][kr][chaincode]

Public key = kl * Ed25519GeneratorPoint

- `BIP39 mnemonic`: _salon zoo engage submit smile frost later decide wing sight chaos renew lizard rely canal coral scene hobby scare step bus leaf tobacco slice_

- `root key (hex)`: a8ba80028922d9fcfa055c78aede55b5c575bcd8d5a53168edf45f36d9ec8f4694592b4bc892907583e22669ecdf1b0409a9f3bd5549f2dd751b51360909cd05b4b67277d74d4ddb3688daeeb02075482ceb812db8a5757c9e792d14ec791554

### BIP44 paths

#### Child Private Derivation

- `m'/44'/283'/0'/0/0`: 70db1fe0dd722955f44b57b26eee09ac282172559eb2bc6b408f69e2e9ec8f461b8fbc75f0e2a9f454d7de35a812c97fa08b984df342389f4d1f9aec5637e67468c11d826d16f7f505d27bd52c314caea79a7b9b6e87f5aa7e20f6cf06ac51f9
- corresponding public key: 7915e7ecbaad1dc9bc22a9e496686687f1a8cb4895b7ca46f86d64dd56c6cd97 (kl * Ed25519GeneratorPoint)

- `m'/44'/283'/0'/0/1`: 58678408f4f88d7ec700be1090940bedd584c21dc7f8b00028b69e01edec8f4645779d1c90ecc5168945024e4201552a349a825518badb8d4b017bf1dcce4dac910cb8dbcd10680c7754ca101089f4c9da61a2dff79e0301d9a1fb7656fb8c73
- public key: 054a6881d8809c348a402d67ba2feedcd8e3145f40f21a6bbd0de09c30c78d0a


- `m'/44'/283'/0'/0/2`: 88ce207945dfecea41acbb2a9b4563268d3bee03ea2e199af502c500e7ec8f467b95d890e8c7e5aa79d9905f3c6794e1fc3bb9478552b7f24c751b94dd3becd76699130de29273b2ae742991b7daa0adae6fb656038b25895d4af3c85769a64d
- public key: 8cea8052cfa1fd8cec0b4fad6241a91f2edbfe9f072586f243839174e40a25ef

- `m'/44'/283'/1'/0/0`: 18ada9291ae27f7415e99f4485d81493e68952c8b1af4f335cc52962ecec8f464c106ef1f56fa64b2a13631a68a6d6c9902eec52e2c6226a3ed953ae94bc8a613303bbf403f94b5eb189eed703b3985e12c6726d6b06d4ed8aac3d5b258e3c8c
- public key: 04f3ba279aa781ab4f8f79aaf6cf91e3d7ff75429064dc757001a30e10c628df

- `m'/44'/283'/2'/0/0`: 209dc0b3458ee1f7484189bf584c02807f1a5726168aff5ea14fc117f3ec8f46b68ea14ca84c0da34aa4990416c6da01b14fbe30c5c225238515a93a0a28a33dbecbe8e64a514d44c8c0a3775844d5aa8e18ea2182321f1ff87061e49adc4a77
- public key: 400d78302258dc7b3cb56d1a09f85b018e8100865ced0b5cda474c26bbc07c30


- `m'/44'/283'/3'/0/0`: 3008165b92e6b29d6a3f28b593f15dcf6ce4c9a5a1aadc3a43ca068ce7ec8f46b8251db83f68dedaee1054ec545257f95ba00e33566d99926bef8743b77b42b8867472d1f1886c88ec36991f14a333454003236b5375d4a8bf4f01b2ff85ec9d
- public key: cf8d28a3d41bc656acbfeadb64d06054142c97bee6a987c11d934f84853df866

- `m'/44'/0'/0'/0/0`: 08b10dfcfc37a0cc998ec3305c6c32d4412de73f3f9633342248d14aeeec8f461997b1136524a5d3499d933c6f6e739d38fbf7404a183f4d835c6fe105cf44d016634694e4087b2d547c8b550a0f053d2fba990ba7e5e58186963393994d06a9
- public key: 28804f08d8c145e172c998fe75058237b8181846ca763894ae3eefea6ab88352

- `m'/44'/0'/0'/0/1`: 80d6dd1cefb42fc93187739ad102e5dfd533b54b847b0693661bfee9edec8f466cab918344a8e7e9ae196d94cce301aa1d7360f550b25f3dc7468a006e423e3de80e43cd2588a9996b8156d3dc233ca31470f7d49261edd2e13d8c4c0096163c
- public key: fc8d1c79edd406fa415cb0a76435eb83b6f8af72cd9bd673753471470205057a

- `m'/44'/0'/0'/0/2`: 7847139bee38330c809a56f3d2261bfbd7da7c1c88999c38bb12175cefec8f467617b7dba8330b644eae1c24a2d6b7211f5f36d9919d9a240777fa32d382feb3debfe21afa0de5021342f3bfafe18a91e11c441ab98ff5bcbba2dfba3190ce6f
- public key: f7e317899420454886fe79f24e25af0bbb1856c440b14829674015e5fc2ad28a