Skip to content

Commit

Permalink
restricted CAIP-10 support for ethereum blockchain addresses to speci…
Browse files Browse the repository at this point in the history
…fic chains
  • Loading branch information
arnabghose997 committed Mar 22, 2023
1 parent 27c0a3b commit c9615a2
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 54 deletions.
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

0 comments on commit c9615a2

Please sign in to comment.