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

refactor: restricted CAIP-10 support for ethereum blockchain addresses to specific chains #350

Merged
merged 1 commit into from
Mar 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions tests/e2e/ssi_tests/e2e_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,8 @@ def caip10_ethereum_support_test():
"eip155:1",
"eip155:::::23",
"eip155::0x1234567"
"eip155:1000231432:0x23",
"eip155:jagrat:0x23"
]

for invalid_blockchain_id in invalid_blockchain_account_ids:
Expand All @@ -673,7 +675,7 @@ def caip10_ethereum_support_test():
}
signers.append(signPair)
create_tx_cmd = form_did_create_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME)
run_blockchain_command(create_tx_cmd, f"Registering a DID Document with an invalid blockchainAccountId: {invalid_blockchain_id}", True)
run_blockchain_command(create_tx_cmd, f"Registering a DID Document with an invalid blockchainAccountId: {invalid_blockchain_id}", True, True)

print("Registering a DID with a VM of type EcdsaSecp256k1SignatureRecovery2020 having publicKeyMultibase attribute populated")
kp = generate_key_pair(algo=kp_algo)
Expand Down Expand Up @@ -745,7 +747,7 @@ def caip10_cosmos_support_test():
signers.append(signPair)

create_tx_cmd = form_did_create_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME)
run_blockchain_command(create_tx_cmd, f"Registering a DID Document with an invalid blockchainAccountId: {invalid_blockchain_id}", True)
run_blockchain_command(create_tx_cmd, f"Registering a DID Document with an invalid blockchainAccountId: {invalid_blockchain_id}", True, True)

print("Registering a DID with a VM of type EcdsaSecp256k1VerificationKey2019 having both publicKeyMultibase and blockchainAccountId attributes populated")
kp = generate_key_pair(algo=kp_algo)
Expand Down Expand Up @@ -777,7 +779,7 @@ def caip10_cosmos_support_test():
signers.append(signPair)

create_tx_cmd = form_did_create_tx_multisig(did_doc_string, signers, DEFAULT_BLOCKCHAIN_ACCOUNT_NAME)
run_blockchain_command(create_tx_cmd, f"Registering DID with Id: {did_doc_id}", True)
run_blockchain_command(create_tx_cmd, f"Registering DID with Id: {did_doc_id}", True, True)

print("--- Test Completed ---\n")

Expand Down
41 changes: 41 additions & 0 deletions x/ssi/types/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,41 @@ var SupportedClientSpecs []string = []string{
PersonalSignClientSpec,
}

// Supported CAIP-10 Prefixes
var SupportedCAIP10Prefixes = []string{
EthereumCAIP10Prefix,
CosmosCAIP10Prefix,
}

var SupportedCAIP10EthereumChainIds = []string{
// Ethereum-Based Mainnet Chains
"1", // Ethereum Mainnet
"137", // Polygon Mainnet
"56", // Binance Smart Chain

// Ethereum-Based Testnet Chains
"3", // Ropsten (Ethereum Testnet)
"4", // Rinkeby (Ethereum Testnet)
"5", // Goerli (Ethereum Testnet)
"80001", // Polygon Mumbai Testnet
"97", // Binance Smart Chain Testnet
}

var SupportedCAIP10CosmosChainIds = []string{
"cosmoshub-4", // Cosmos Hub
"osmosis-1", // Osmosis
"akashnet-2", // Akash
"stargaze-1", // Stargaze
"core-1", // Persistence
"crypto-org-chain-mainnet-1", // Crypto.Org Chain

"theta-testnet-001", // Cosmos Hub Theta Testnet
"osmo-test-4", // Osmosis Testnet
"elgafar-1", // Stargaze Testnet
"test-core-1", // Persistence Testnet
"jagrat", // Hypersign Identity Network - Jagrat Testnet
}

// Map between supported cosmos chain-id and their respective blockhchain address prefix
var CosmosCAIP10ChainIdBech32PrefixMap = map[string]string{
// Mainnet Chains
Expand All @@ -70,3 +105,9 @@ var CosmosCAIP10ChainIdBech32PrefixMap = map[string]string{
"test-core-1": "persistence",
"jagrat": "hid",
}

// Map between support CAIP-10 prefix and list of chain-ids
var SupportedCAIP10PrefixChainIdsMap = map[string][]string{
EthereumCAIP10Prefix: SupportedCAIP10EthereumChainIds,
CosmosCAIP10Prefix: SupportedCAIP10CosmosChainIds,
}
40 changes: 40 additions & 0 deletions x/ssi/types/diddoc_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,14 @@ func verificationKeyCheck(vm *VerificationMethod) error {
return fmt.Errorf("unsupported verification method type: %v", supportedVerificationMethodTypes)
}

// validate blockchainAccountId
if vm.BlockchainAccountId != "" {
err := validateBlockchainAccountId(vm.BlockchainAccountId)
if err != nil {
return fmt.Errorf("invalid blockchainAccount Id %v: "+err.Error(), vm.BlockchainAccountId)
}
}

return nil
}

Expand Down Expand Up @@ -270,6 +278,38 @@ func validateVmRelationships(didDoc *Did) error {
return nil
}

func validateBlockchainAccountId(blockchainAccountId string) error {
blockchainId, err := NewBlockchainId(blockchainAccountId)
if err != nil {
return err
}

var validationErr error

// Check for supported CAIP-10 prefix
validationErr = blockchainId.ValidateSupportedCAIP10Prefix()
if validationErr != nil {
return validationErr
}

// Check for supported CAIP-10 chain-ids
validationErr = blockchainId.ValidateSupportChainId()
if validationErr != nil {
return validationErr
}

// Check for supported CAIP-10 bech32 prefix. Perform this validation
// only when the CAIP-10 prefix is "cosmos"
if blockchainId.CAIP10Prefix == CosmosCAIP10Prefix {
validationErr = blockchainId.ValidateSupportedBech32Prefix()
if validationErr != nil {
return validationErr
}
}

return nil
}

// ValidateDidDocument validates the DID Document
func (didDoc *Did) ValidateDidDocument() error {
// Id check
Expand Down
68 changes: 68 additions & 0 deletions x/ssi/types/ssi_types.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package types

import (
fmt "fmt"
"strings"

proto "github.com/gogo/protobuf/proto"
)

Expand Down Expand Up @@ -93,3 +96,68 @@ type SSIProofInterface interface {
GetType() string
GetVerificationMethod() string
}

// CAIP-10 Blockchain Account Id
type BlockchainId struct {
CAIP10Prefix string
ChainId string
BlockchainAddress string
}

func NewBlockchainId(blockchainAccountId string) (*BlockchainId, error) {
segments := strings.Split(blockchainAccountId, ":")

// Validate blockchainAccountId segments
if len(segments) != 3 {
return nil, fmt.Errorf(
"invalid CAIP-10 format for blockchainAccountId '%v'. Please refer: https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-10.md",
blockchainAccountId,
)
}

return &BlockchainId{
CAIP10Prefix: segments[0],
ChainId: segments[1],
BlockchainAddress: segments[2],
}, nil
}

func (bid *BlockchainId) ValidateSupportedCAIP10Prefix() error {
if !FindInSlice(SupportedCAIP10Prefixes, bid.CAIP10Prefix) {
return fmt.Errorf(
"unsupported CAIP-10 prefix: '%v', supported CAIP-10 prefixes are %v",
bid.CAIP10Prefix,
SupportedCAIP10Prefixes,
)
}
return nil
}

func (bid *BlockchainId) ValidateSupportChainId() error {
supportedChainIds := SupportedCAIP10PrefixChainIdsMap[bid.CAIP10Prefix]

if !FindInSlice(supportedChainIds, bid.ChainId) {
return fmt.Errorf(
"unsupported CAIP-10 chain-id: '%v', supported CAIP-10 chain-ids are %v",
bid.ChainId,
supportedChainIds,
)
}

return nil
}

func (bid *BlockchainId) ValidateSupportedBech32Prefix() error {
extractedBech32Prefix := strings.Split(bid.BlockchainAddress, "1")[0]

supportedBech32Prefix, supported := CosmosCAIP10ChainIdBech32PrefixMap[bid.ChainId]
if !supported {
return fmt.Errorf("chain-id %v is not supported", bid.ChainId)
}

if supportedBech32Prefix != extractedBech32Prefix {
return fmt.Errorf("invalid bech32 prefix for blockchain address: %v", bid.BlockchainAddress)
}

return nil
}
11 changes: 8 additions & 3 deletions x/ssi/verification/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,15 @@ import (
)

// publicKeyToCosmosBech32Address converts publicKey byteArray to Bech32 encoded blockchain address
func publicKeyToCosmosBech32Address(addressPrefix string, pubKeyBytes []byte) string {
func publicKeyToCosmosBech32Address(addressPrefix string, pubKeyBytes []byte) (string, error) {
// Throw error if the length of secp256k1 publicKey is not 33
if len(pubKeyBytes) != 33 {
panic(fmt.Sprintf("invalid secp256k1 public key length %v", len(pubKeyBytes)))
return "", fmt.Errorf("invalid secp256k1 public key length %v", len(pubKeyBytes))
}

// Throw an error if addressPrefix is empty
if addressPrefix == "" {
return "", fmt.Errorf("address prefix cannot be empty")
}

// Hash pubKeyBytes as: RIPEMD160(SHA256(public_key_bytes))
Expand All @@ -27,5 +32,5 @@ func publicKeyToCosmosBech32Address(addressPrefix string, pubKeyBytes []byte) st
if err != nil {
panic(err)
}
return address
return address, nil
}
51 changes: 18 additions & 33 deletions x/ssi/verification/caip10.go
Original file line number Diff line number Diff line change
@@ -1,50 +1,35 @@
package verification

import (
"fmt"
"strings"

"github.com/hypersign-protocol/hid-node/x/ssi/types"
)

// Extracts the blockchain address from blockchainAccountId
func getBlockchainAddress(blockchainAccountId string) string {
blockchainAccountIdElements := strings.Split(blockchainAccountId, ":")
blockchainAddress := blockchainAccountIdElements[len(blockchainAccountIdElements)-1]
return blockchainAddress
func getBlockchainAddress(blockchainAccountId string) (string, error) {
bid, err := types.NewBlockchainId(blockchainAccountId)
if err != nil {
return "", err
}

return bid.BlockchainAddress, nil
}

// getChainIdFromBlockchainAccountId extracts chain from blockchainAccountId
func getChainIdFromBlockchainAccountId(blockchainAccountId string) string {
chainIdIdx := 1
// getChainIdFromBlockchainAccountId extracts chain-id from blockchainAccountId
func getChainIdFromBlockchainAccountId(blockchainAccountId string) (string, error) {
bid, err := types.NewBlockchainId(blockchainAccountId)
if err != nil {
return "", err
}

blockchainAccountIdElements := strings.Split(blockchainAccountId, ":")
blockchainChainId := blockchainAccountIdElements[chainIdIdx]
return blockchainChainId
return bid.ChainId, nil
}

// Extracts the CAIP-10 prefix from blockchainAccountId and returns the chain spec
func getCAIP10Prefix(blockchainAccountId string) (string, error) {
segments := strings.Split(blockchainAccountId, ":")

// Validate blockchainAccountId
if len(segments) != 3 {
return "", fmt.Errorf(
"invalid CAIP-10 format for blockchainAccountId '%v'. Please refer: https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-10.md",
blockchainAccountId,
)
bid, err := types.NewBlockchainId(blockchainAccountId)
if err != nil {
return "", err
}

// Prefix check
if segments[0] == types.EthereumCAIP10Prefix {
return types.EthereumCAIP10Prefix, nil
} else if segments[0] == types.CosmosCAIP10Prefix {
return types.CosmosCAIP10Prefix, nil
} else {
return "", fmt.Errorf(
"unsupported CAIP-10 prefix in blockchainAccountId '%v'. Supported CAIP-10 prefixes: %v",
blockchainAccountId,
types.CAIP10PrefixForEcdsaSecp256k1RecoveryMethod2020,
)
}
return bid.CAIP10Prefix, nil
}
33 changes: 18 additions & 15 deletions x/ssi/verification/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,22 +175,22 @@ func verifyCosmosBlockchainAccountId(blockchainAccountId, publicKeyMultibase str
}

// Convert publicKeyMultibase to bech32 encoded blockchain address
chainId := getChainIdFromBlockchainAccountId(blockchainAccountId)
addressPrefix, isChainSupported := types.CosmosCAIP10ChainIdBech32PrefixMap[chainId]
if !isChainSupported {
return fmt.Errorf(
"cosmos chain with chain-id '%v' in blockchainAccountId '%v' is not supported",
chainId,
blockchainAccountId,
)
chainId, err := getChainIdFromBlockchainAccountId(blockchainAccountId)
if err != nil {
return err
}
validAddressPrefix := types.CosmosCAIP10ChainIdBech32PrefixMap[chainId]
convertedAddress, err := publicKeyToCosmosBech32Address(validAddressPrefix, publicKeyBytes)
if err != nil {
return err
}
convertedAddress := publicKeyToCosmosBech32Address(
addressPrefix,
publicKeyBytes,
)

// Compare converted blockchain address
if convertedAddress != getBlockchainAddress(blockchainAccountId) {
// Compare converted blockchain address with user provided blockchain address
inputAddress, err := getBlockchainAddress(blockchainAccountId)
if err != nil {
return err
}
if convertedAddress != inputAddress {
return fmt.Errorf(
"blockchain address provided in blockchainAccountId '%v' is unexpected",
blockchainAccountId,
Expand All @@ -205,7 +205,10 @@ func verifyCosmosBlockchainAccountId(blockchainAccountId, publicKeyMultibase str
// blockchain address, and matched with user provided blockchain address. If they do not match, error is returned.
func verifyEthereumBlockchainAccountId(extendedVm *types.ExtendedVerificationMethod, documentBytes []byte) error {
// Extract blockchain address from blockchain account id
blockchainAddress := getBlockchainAddress(extendedVm.BlockchainAccountId)
blockchainAddress, err := getBlockchainAddress(extendedVm.BlockchainAccountId)
if err != nil {
return err
}

// Convert message bytes to hash
// More info on the `personal_sign` here: https://docs.metamask.io/guide/signing-data.html#personal-sign
Expand Down