From 4583ec8b8c666ead79eeef20beef36676951393b Mon Sep 17 00:00:00 2001
From: baryon2 <opthami@gmail.com>
Date: Wed, 11 Sep 2024 15:39:03 +0530
Subject: [PATCH 1/2] add authn pubkey support to base account

---
 x/accounts/defaults/base/account_test.go      | 216 +++++++++-
 .../defaults/base/keys/authn/authn_pubkey.go  | 153 +++++++
 .../base/keys/authn/authn_pubkey.pb.go        | 378 ++++++++++++++++++
 .../defaults/base/v1/keys/authn_pubkey.proto  |  18 +
 4 files changed, 761 insertions(+), 4 deletions(-)
 create mode 100644 x/accounts/defaults/base/keys/authn/authn_pubkey.go
 create mode 100644 x/accounts/defaults/base/keys/authn/authn_pubkey.pb.go
 create mode 100644 x/accounts/proto/cosmos/accounts/defaults/base/v1/keys/authn_pubkey.proto

diff --git a/x/accounts/defaults/base/account_test.go b/x/accounts/defaults/base/account_test.go
index 8523d47c1f9b..c94183e1060f 100644
--- a/x/accounts/defaults/base/account_test.go
+++ b/x/accounts/defaults/base/account_test.go
@@ -2,6 +2,14 @@ package base
 
 import (
 	"context"
+	"crypto"
+	"crypto/ecdsa"
+	"crypto/elliptic"
+	"crypto/rand"
+	"crypto/sha256"
+	"encoding/base64"
+	"encoding/hex"
+	"encoding/json"
 	"errors"
 	"testing"
 
@@ -16,21 +24,82 @@ import (
 	aa_interface_v1 "cosmossdk.io/x/accounts/interfaces/account_abstraction/v1"
 	"cosmossdk.io/x/tx/signing"
 
+	authn "cosmossdk.io/x/accounts/defaults/base/keys/authn"
+	cometcrypto "github.com/cometbft/cometbft/crypto"
 	codectypes "github.com/cosmos/cosmos-sdk/codec/types"
 	"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
 	"github.com/cosmos/cosmos-sdk/types/address"
 	"github.com/cosmos/cosmos-sdk/types/tx"
 )
 
+type CeremonyType string
+
+type TokenBindingStatus string
+
+type TokenBinding struct {
+	Status TokenBindingStatus `json:"status"`
+	Id     string             `json:"id"`
+}
+
+type CollectedClientData struct {
+	// Type the string "webauthn.create" when creating new credentials,
+	// and "webauthn.get" when getting an assertion from an existing credential. The
+	// purpose of this member is to prevent certain types of signature confusion attacks
+	//(where an attacker substitutes one legitimate signature for another).
+	Type         CeremonyType  `json:"type"`
+	Challenge    string        `json:"challenge"`
+	Origin       string        `json:"origin"`
+	TokenBinding *TokenBinding `json:"tokenBinding,omitempty"`
+	// Chromium (Chrome) returns a hint sometimes about how to handle clientDataJSON in a safe manner
+	Hint string `json:"new_keys_may_be_added_here,omitempty"`
+}
+
+func GenerateAuthnKey(t *testing.T) (*ecdsa.PrivateKey, authn.AuthnPubKey) {
+	t.Helper()
+	curve := elliptic.P256()
+	privateKey, err := ecdsa.GenerateKey(curve, rand.Reader)
+	require.NoError(t, err)
+	pkBytes := elliptic.MarshalCompressed(curve, privateKey.PublicKey.X, privateKey.PublicKey.Y)
+	pk := authn.AuthnPubKey{
+		KeyId: "a099eda0fb05e5783379f73a06acca726673b8e07e436edcd0d71645982af65c",
+		Key:   pkBytes,
+	}
+
+	return privateKey, pk
+}
+
+func GenerateClientData(t *testing.T, msg []byte) []byte {
+	t.Helper()
+	clientData := CollectedClientData{
+		// purpose of this member is to prevent certain types of signature confusion attacks
+		//(where an attacker substitutes one legitimate signature for another).
+		Type:         "webauthn.create",
+		Challenge:    base64.RawURLEncoding.EncodeToString(msg),
+		Origin:       "https://blue.kujira.network",
+		TokenBinding: nil,
+		Hint:         "",
+	}
+	clientDataJSON, err := json.Marshal(clientData)
+	require.NoError(t, err)
+	return clientDataJSON
+}
+
 func setupBaseAccount(t *testing.T, ss store.KVStoreService) Account {
 	t.Helper()
 	deps := makeMockDependencies(ss)
 	handler := directHandler{}
 
-	createAccFn := NewAccount("base", signing.NewHandlerMap(handler), WithPubKeyWithValidationFunc(func(pt *secp256k1.PubKey) error {
-		_, err := dcrd_secp256k1.ParsePubKey(pt.Key)
-		return err
-	}))
+	createAccFn := NewAccount(
+		"base",
+		signing.NewHandlerMap(handler),
+		WithPubKeyWithValidationFunc(func(pt *secp256k1.PubKey) error {
+			_, err := dcrd_secp256k1.ParsePubKey(pt.Key)
+			return err
+		}),
+		WithPubKeyWithValidationFunc(func(pt *authn.AuthnPubKey) error {
+			return nil
+		}),
+	)
 	_, acc, err := createAccFn(deps)
 	baseAcc := acc.(Account)
 	require.NoError(t, err)
@@ -58,6 +127,7 @@ func TestInit(t *testing.T) {
 			},
 			false,
 		},
+
 		{
 			"invalid pubkey",
 			&v1.MsgInit{
@@ -267,3 +337,141 @@ func toAnyPb(t *testing.T, pm gogoproto.Message) *codectypes.Any {
 	require.NoError(t, err)
 	return pb
 }
+
+func TestAuthenticateAuthn(t *testing.T) {
+	ctx, ss := newMockContext(t)
+	baseAcc := setupBaseAccount(t, ss)
+	privKey, pk := GenerateAuthnKey(t)
+	pkAny, err := codectypes.NewAnyWithValue(&pk)
+	require.NoError(t, err)
+	_, err = baseAcc.Init(ctx, &v1.MsgInit{
+		PubKey: toAnyPb(t, &pk),
+	})
+	require.NoError(t, err)
+
+	ctx = accountstd.SetSender(ctx, address.Module("accounts"))
+	require.NoError(t, err)
+
+	transaction := tx.Tx{
+		Body: &tx.TxBody{},
+		AuthInfo: &tx.AuthInfo{
+			SignerInfos: []*tx.SignerInfo{
+				{
+					PublicKey: pkAny,
+					ModeInfo: &tx.ModeInfo{
+						Sum: &tx.ModeInfo_Single_{
+							Single: &tx.ModeInfo_Single{
+								Mode: 1,
+							},
+						},
+					},
+					Sequence: 0,
+				},
+			},
+		},
+		Signatures: [][]byte{},
+	}
+
+	bodyByte, err := transaction.Body.Marshal()
+	require.NoError(t, err)
+	authByte, err := transaction.AuthInfo.Marshal()
+	require.NoError(t, err)
+
+	txDoc := tx.SignDoc{
+		BodyBytes:     bodyByte,
+		AuthInfoBytes: authByte,
+		ChainId:       "test",
+		AccountNumber: 1,
+	}
+	signBytes, err := txDoc.Marshal()
+	require.NoError(t, err)
+
+	clientDataJson := GenerateClientData(t, signBytes)
+	clientDataHash := sha256.Sum256(clientDataJson)
+	authenticatorData := cometcrypto.CRandBytes(37)
+	payload := append(authenticatorData, clientDataHash[:]...)
+
+	h := crypto.SHA256.New()
+	h.Write(payload)
+	digest := h.Sum(nil)
+
+	sig, err := ecdsa.SignASN1(rand.Reader, privKey, digest)
+
+	require.NoError(t, err)
+
+	cborSig := authn.Signature{
+		AuthenticatorData: hex.EncodeToString(authenticatorData),
+		ClientDataJSON:    hex.EncodeToString(clientDataJson),
+		Signature:         hex.EncodeToString(sig),
+	}
+
+	sigBytes, err := json.Marshal(cborSig)
+
+	transaction.Signatures = append(transaction.Signatures, sigBytes)
+
+	rawTx := tx.TxRaw{
+		BodyBytes:     bodyByte,
+		AuthInfoBytes: authByte,
+		Signatures:    transaction.Signatures,
+	}
+
+	_, err = baseAcc.Authenticate(ctx, &aa_interface_v1.MsgAuthenticate{
+		RawTx:       &rawTx,
+		Tx:          &transaction,
+		SignerIndex: 0,
+	})
+	require.NoError(t, err)
+
+	// testing with invalid signature
+
+	// update sequence number
+	transaction = tx.Tx{
+		Body: &tx.TxBody{},
+		AuthInfo: &tx.AuthInfo{
+			SignerInfos: []*tx.SignerInfo{
+				{
+					PublicKey: pkAny,
+					ModeInfo: &tx.ModeInfo{
+						Sum: &tx.ModeInfo_Single_{
+							Single: &tx.ModeInfo_Single{
+								Mode: 1,
+							},
+						},
+					},
+					Sequence: 1,
+				},
+			},
+		},
+		Signatures: [][]byte{},
+	}
+	authByte, err = transaction.AuthInfo.Marshal()
+	require.NoError(t, err)
+
+	txDoc.BodyBytes = []byte("invalid_msg")
+	txDoc.AuthInfoBytes = authByte
+	signBytes, err = txDoc.Marshal()
+	require.NoError(t, err)
+
+	invalidClientDataJson := GenerateClientData(t, signBytes)
+	invalidClientDataHash := sha256.Sum256(invalidClientDataJson)
+	invalidAuthenticatorData := cometcrypto.CRandBytes(37)
+	invalidPayload := append(invalidAuthenticatorData, invalidClientDataHash[:]...)
+
+	ivh := crypto.SHA256.New()
+	ivh.Write(invalidPayload)
+	ivdigest := ivh.Sum(nil)
+
+	invalidSig, err := ecdsa.SignASN1(rand.Reader, privKey, ivdigest)
+	require.NoError(t, err)
+
+	transaction.Signatures = append([][]byte{}, invalidSig)
+
+	rawTx.Signatures = transaction.Signatures
+
+	_, err = baseAcc.Authenticate(ctx, &aa_interface_v1.MsgAuthenticate{
+		RawTx:       &rawTx,
+		Tx:          &transaction,
+		SignerIndex: 0,
+	})
+	require.Equal(t, errors.New("signature verification failed"), err)
+}
diff --git a/x/accounts/defaults/base/keys/authn/authn_pubkey.go b/x/accounts/defaults/base/keys/authn/authn_pubkey.go
new file mode 100644
index 000000000000..01a770b3c501
--- /dev/null
+++ b/x/accounts/defaults/base/keys/authn/authn_pubkey.go
@@ -0,0 +1,153 @@
+package authn
+
+import (
+	"bytes"
+	"crypto"
+	"crypto/elliptic"
+	"crypto/sha256"
+	"encoding/base64"
+	"encoding/hex"
+	"encoding/json"
+	"fmt"
+
+	cometcrypto "github.com/cometbft/cometbft/crypto"
+
+	ecdsa "crypto/ecdsa"
+
+	errorsmod "cosmossdk.io/errors"
+	"github.com/cosmos/cosmos-sdk/codec"
+	cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
+	"github.com/cosmos/cosmos-sdk/types/address"
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	"github.com/cosmos/gogoproto/proto"
+)
+
+type Signature struct {
+	AuthenticatorData string `json:"authenticatorData"`
+	ClientDataJSON    string `json:"clientDataJSON"`
+	Signature         string `json:"signature"`
+}
+
+const (
+	keyType    = "authn"
+	PubKeyName = "tendermint/PubKeyAuthn"
+)
+
+var (
+	_ cryptotypes.PubKey   = &AuthnPubKey{}
+	_ codec.AminoMarshaler = &AuthnPubKey{}
+)
+
+const AuthnPubKeySize = 33
+
+func (pubKey *AuthnPubKey) Address() cometcrypto.Address {
+	if len(pubKey.Key) != AuthnPubKeySize {
+		panic("length of pubkey is incorrect")
+	}
+
+	return address.Hash(proto.MessageName(pubKey), pubKey.Key)
+}
+
+func (pubKey *AuthnPubKey) Bytes() []byte {
+	return pubKey.Key
+}
+
+func (pubKey *AuthnPubKey) String() string {
+	return fmt.Sprintf("PubKeyAuthn{%X}", pubKey.Key)
+}
+
+func (pubKey *AuthnPubKey) Type() string {
+	return keyType
+}
+
+func (pubKey *AuthnPubKey) Equals(other cryptotypes.PubKey) bool {
+	return pubKey.Type() == other.Type() && bytes.Equal(pubKey.Bytes(), other.Bytes())
+}
+
+func (pubKey AuthnPubKey) MarshalAmino() ([]byte, error) {
+	return pubKey.Key, nil
+}
+
+func (pubKey *AuthnPubKey) UnmarshalAmino(bz []byte) error {
+	if len(bz) != AuthnPubKeySize {
+		return errorsmod.Wrap(sdkerrors.ErrInvalidPubKey, "invalid pubkey size")
+	}
+	pubKey.Key = bz
+
+	return nil
+}
+
+// MarshalAminoJSON overrides Amino JSON marshaling.
+func (pubKey AuthnPubKey) MarshalAminoJSON() ([]byte, error) {
+	// When we marshal to Amino JSON, we don't marshal the "key" field itself,
+	// just its contents (i.e. the key bytes).
+	return pubKey.MarshalAmino()
+}
+
+// UnmarshalAminoJSON overrides Amino JSON marshaling.
+func (pubKey *AuthnPubKey) UnmarshalAminoJSON(bz []byte) error {
+	return pubKey.UnmarshalAmino(bz)
+}
+
+func (pubKey *AuthnPubKey) VerifySignature(msg, sigStr []byte) bool {
+	sig := Signature{}
+	err := json.Unmarshal(sigStr, &sig)
+	if err != nil {
+		return false
+	}
+
+	clientDataJSON, err := hex.DecodeString(sig.ClientDataJSON)
+	if err != nil {
+		return false
+	}
+
+	clientData := make(map[string]interface{})
+	err = json.Unmarshal(clientDataJSON, &clientData)
+	if err != nil {
+		return false
+	}
+
+	challengeBase64, ok := clientData["challenge"].(string)
+	if !ok {
+		return false
+	}
+	challenge, err := base64.RawURLEncoding.DecodeString(challengeBase64)
+	if err != nil {
+		return false
+	}
+
+	// Check challenge == msg
+	if !bytes.Equal(challenge, msg) {
+		return false
+	}
+
+	publicKey := &ecdsa.PublicKey{Curve: elliptic.P256()}
+
+	publicKey.X, publicKey.Y = elliptic.UnmarshalCompressed(elliptic.P256(), pubKey.Key)
+	if publicKey.X == nil || publicKey.Y == nil {
+		return false
+	}
+
+	signatureBytes, err := hex.DecodeString(sig.Signature)
+	if err != nil {
+		return false
+	}
+
+	authenticatorData, err := hex.DecodeString(sig.AuthenticatorData)
+	if err != nil {
+		return false
+	}
+
+	// check authenticatorData length
+	if len(authenticatorData) < 37 {
+		return false
+	}
+
+	clientDataHash := sha256.Sum256(clientDataJSON)
+	payload := append(authenticatorData, clientDataHash[:]...)
+
+	h := crypto.SHA256.New()
+	h.Write(payload)
+
+	return ecdsa.VerifyASN1(publicKey, h.Sum(nil), signatureBytes)
+}
diff --git a/x/accounts/defaults/base/keys/authn/authn_pubkey.pb.go b/x/accounts/defaults/base/keys/authn/authn_pubkey.pb.go
new file mode 100644
index 000000000000..4a49fe5f8fd4
--- /dev/null
+++ b/x/accounts/defaults/base/keys/authn/authn_pubkey.pb.go
@@ -0,0 +1,378 @@
+// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// source: cosmos/accounts/defaults/base/v1/keys/authn_pubkey.proto
+
+package authn
+
+import (
+	fmt "fmt"
+	_ "github.com/cosmos/cosmos-sdk/types/tx/amino"
+	_ "github.com/cosmos/gogoproto/gogoproto"
+	proto "github.com/cosmos/gogoproto/proto"
+	io "io"
+	math "math"
+	math_bits "math/bits"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
+
+// PubKey defines a authn public key
+type AuthnPubKey struct {
+	KeyId string `protobuf:"bytes,1,opt,name=key_id,json=keyId,proto3" json:"key_id,omitempty"`
+	Key   []byte `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
+}
+
+func (m *AuthnPubKey) Reset()      { *m = AuthnPubKey{} }
+func (*AuthnPubKey) ProtoMessage() {}
+func (*AuthnPubKey) Descriptor() ([]byte, []int) {
+	return fileDescriptor_0d353fd28c6ed153, []int{0}
+}
+func (m *AuthnPubKey) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *AuthnPubKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_AuthnPubKey.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *AuthnPubKey) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_AuthnPubKey.Merge(m, src)
+}
+func (m *AuthnPubKey) XXX_Size() int {
+	return m.Size()
+}
+func (m *AuthnPubKey) XXX_DiscardUnknown() {
+	xxx_messageInfo_AuthnPubKey.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_AuthnPubKey proto.InternalMessageInfo
+
+func (m *AuthnPubKey) GetKeyId() string {
+	if m != nil {
+		return m.KeyId
+	}
+	return ""
+}
+
+func (m *AuthnPubKey) GetKey() []byte {
+	if m != nil {
+		return m.Key
+	}
+	return nil
+}
+
+func init() {
+	proto.RegisterType((*AuthnPubKey)(nil), "cosmos.accounts.defaults.base.v1.AuthnPubKey")
+}
+
+func init() {
+	proto.RegisterFile("cosmos/accounts/defaults/base/v1/keys/authn_pubkey.proto", fileDescriptor_0d353fd28c6ed153)
+}
+
+var fileDescriptor_0d353fd28c6ed153 = []byte{
+	// 263 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xb2, 0x48, 0xce, 0x2f, 0xce,
+	0xcd, 0x2f, 0xd6, 0x4f, 0x4c, 0x4e, 0xce, 0x2f, 0xcd, 0x2b, 0x29, 0xd6, 0x4f, 0x49, 0x4d, 0x4b,
+	0x2c, 0xcd, 0x29, 0x29, 0xd6, 0x4f, 0x4a, 0x2c, 0x4e, 0xd5, 0x2f, 0x33, 0xd4, 0xcf, 0x4e, 0xad,
+	0x2c, 0xd6, 0x4f, 0x2c, 0x2d, 0xc9, 0xc8, 0x8b, 0x2f, 0x28, 0x4d, 0xca, 0x4e, 0xad, 0xd4, 0x2b,
+	0x28, 0xca, 0x2f, 0xc9, 0x17, 0x52, 0x80, 0xe8, 0xd4, 0x83, 0xe9, 0xd4, 0x83, 0xe9, 0xd4, 0x03,
+	0xe9, 0xd4, 0x2b, 0x33, 0x94, 0x12, 0x4c, 0xcc, 0xcd, 0xcc, 0xcb, 0xd7, 0x07, 0x93, 0x10, 0x4d,
+	0x52, 0x22, 0xe9, 0xf9, 0xe9, 0xf9, 0x60, 0xa6, 0x3e, 0x88, 0x05, 0x11, 0x55, 0x4a, 0xe5, 0xe2,
+	0x76, 0x04, 0x59, 0x10, 0x50, 0x9a, 0xe4, 0x9d, 0x5a, 0x29, 0x24, 0xca, 0xc5, 0x96, 0x9d, 0x5a,
+	0x19, 0x9f, 0x99, 0x22, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0x19, 0xc4, 0x9a, 0x9d, 0x5a, 0xe9, 0x99,
+	0x22, 0x24, 0xc0, 0xc5, 0x9c, 0x9d, 0x5a, 0x29, 0xc1, 0xa4, 0xc0, 0xa8, 0xc1, 0x13, 0x04, 0x62,
+	0x5a, 0xe9, 0xce, 0x58, 0x20, 0xcf, 0xd0, 0xf5, 0x7c, 0x83, 0x96, 0x58, 0x49, 0x6a, 0x5e, 0x4a,
+	0x6a, 0x51, 0x6e, 0x66, 0x5e, 0x89, 0x3e, 0xc4, 0x10, 0xb0, 0x79, 0x93, 0x9e, 0x6f, 0xd0, 0xe2,
+	0x04, 0x19, 0x95, 0x96, 0x99, 0x9a, 0x93, 0xe2, 0xe4, 0x75, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47,
+	0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d,
+	0xc7, 0x72, 0x0c, 0x51, 0x06, 0x10, 0xbf, 0x14, 0xa7, 0x64, 0xeb, 0x65, 0xe6, 0xeb, 0x57, 0xe0,
+	0x0a, 0x0d, 0x44, 0x50, 0x24, 0xb1, 0x81, 0x5d, 0x6e, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0xc8,
+	0x03, 0x55, 0x76, 0x40, 0x01, 0x00, 0x00,
+}
+
+func (m *AuthnPubKey) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *AuthnPubKey) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *AuthnPubKey) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.Key) > 0 {
+		i -= len(m.Key)
+		copy(dAtA[i:], m.Key)
+		i = encodeVarintAuthnPubkey(dAtA, i, uint64(len(m.Key)))
+		i--
+		dAtA[i] = 0x12
+	}
+	if len(m.KeyId) > 0 {
+		i -= len(m.KeyId)
+		copy(dAtA[i:], m.KeyId)
+		i = encodeVarintAuthnPubkey(dAtA, i, uint64(len(m.KeyId)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func encodeVarintAuthnPubkey(dAtA []byte, offset int, v uint64) int {
+	offset -= sovAuthnPubkey(v)
+	base := offset
+	for v >= 1<<7 {
+		dAtA[offset] = uint8(v&0x7f | 0x80)
+		v >>= 7
+		offset++
+	}
+	dAtA[offset] = uint8(v)
+	return base
+}
+func (m *AuthnPubKey) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.KeyId)
+	if l > 0 {
+		n += 1 + l + sovAuthnPubkey(uint64(l))
+	}
+	l = len(m.Key)
+	if l > 0 {
+		n += 1 + l + sovAuthnPubkey(uint64(l))
+	}
+	return n
+}
+
+func sovAuthnPubkey(x uint64) (n int) {
+	return (math_bits.Len64(x|1) + 6) / 7
+}
+func sozAuthnPubkey(x uint64) (n int) {
+	return sovAuthnPubkey(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *AuthnPubKey) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowAuthnPubkey
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: AuthnPubKey: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: AuthnPubKey: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field KeyId", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowAuthnPubkey
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthAuthnPubkey
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthAuthnPubkey
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.KeyId = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
+			}
+			var byteLen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowAuthnPubkey
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				byteLen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if byteLen < 0 {
+				return ErrInvalidLengthAuthnPubkey
+			}
+			postIndex := iNdEx + byteLen
+			if postIndex < 0 {
+				return ErrInvalidLengthAuthnPubkey
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...)
+			if m.Key == nil {
+				m.Key = []byte{}
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipAuthnPubkey(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthAuthnPubkey
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func skipAuthnPubkey(dAtA []byte) (n int, err error) {
+	l := len(dAtA)
+	iNdEx := 0
+	depth := 0
+	for iNdEx < l {
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return 0, ErrIntOverflowAuthnPubkey
+			}
+			if iNdEx >= l {
+				return 0, io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		wireType := int(wire & 0x7)
+		switch wireType {
+		case 0:
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowAuthnPubkey
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				iNdEx++
+				if dAtA[iNdEx-1] < 0x80 {
+					break
+				}
+			}
+		case 1:
+			iNdEx += 8
+		case 2:
+			var length int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowAuthnPubkey
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				length |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if length < 0 {
+				return 0, ErrInvalidLengthAuthnPubkey
+			}
+			iNdEx += length
+		case 3:
+			depth++
+		case 4:
+			if depth == 0 {
+				return 0, ErrUnexpectedEndOfGroupAuthnPubkey
+			}
+			depth--
+		case 5:
+			iNdEx += 4
+		default:
+			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+		}
+		if iNdEx < 0 {
+			return 0, ErrInvalidLengthAuthnPubkey
+		}
+		if depth == 0 {
+			return iNdEx, nil
+		}
+	}
+	return 0, io.ErrUnexpectedEOF
+}
+
+var (
+	ErrInvalidLengthAuthnPubkey        = fmt.Errorf("proto: negative length found during unmarshaling")
+	ErrIntOverflowAuthnPubkey          = fmt.Errorf("proto: integer overflow")
+	ErrUnexpectedEndOfGroupAuthnPubkey = fmt.Errorf("proto: unexpected end of group")
+)
diff --git a/x/accounts/proto/cosmos/accounts/defaults/base/v1/keys/authn_pubkey.proto b/x/accounts/proto/cosmos/accounts/defaults/base/v1/keys/authn_pubkey.proto
new file mode 100644
index 000000000000..324c59d21fab
--- /dev/null
+++ b/x/accounts/proto/cosmos/accounts/defaults/base/v1/keys/authn_pubkey.proto
@@ -0,0 +1,18 @@
+syntax = "proto3";
+package cosmos.accounts.defaults.base.v1;
+
+import "amino/amino.proto";
+import "gogoproto/gogo.proto";
+
+option go_package = "cosmossdk.io/x/accounts/defaults/base/keys/authn";
+
+// PubKey defines a authn public key
+message AuthnPubKey {
+  option (amino.name) = "tendermint/PubKeyAuthn";
+
+  option (amino.message_encoding)     = "key_field";
+  option (gogoproto.goproto_stringer) = false;
+
+  string key_id = 1;
+  bytes  key    = 2;
+}

From 7960cd5eef53d7bed11df01fc1510b91b9a80667 Mon Sep 17 00:00:00 2001
From: baryon2 <opthami@gmail.com>
Date: Mon, 16 Sep 2024 20:13:47 +0530
Subject: [PATCH 2/2] Fixes

1. Update amino name
2. Add comments in proto file
3. Fix lint issues.
---
 x/accounts/defaults/base/account_test.go      | 16 +++++----
 .../defaults/base/keys/authn/authn_pubkey.go  | 36 ++-----------------
 .../base/keys/authn/authn_pubkey.pb.go        | 16 ++++-----
 .../defaults/base/v1/keys/authn_pubkey.proto  |  8 +++--
 4 files changed, 26 insertions(+), 50 deletions(-)

diff --git a/x/accounts/defaults/base/account_test.go b/x/accounts/defaults/base/account_test.go
index c94183e1060f..91af6567cca3 100644
--- a/x/accounts/defaults/base/account_test.go
+++ b/x/accounts/defaults/base/account_test.go
@@ -13,6 +13,7 @@ import (
 	"errors"
 	"testing"
 
+	cometcrypto "github.com/cometbft/cometbft/crypto"
 	gogoproto "github.com/cosmos/gogoproto/proto"
 	types "github.com/cosmos/gogoproto/types/any"
 	dcrd_secp256k1 "github.com/decred/dcrd/dcrec/secp256k1/v4"
@@ -20,12 +21,11 @@ import (
 
 	"cosmossdk.io/core/store"
 	"cosmossdk.io/x/accounts/accountstd"
+	authn "cosmossdk.io/x/accounts/defaults/base/keys/authn"
 	v1 "cosmossdk.io/x/accounts/defaults/base/v1"
 	aa_interface_v1 "cosmossdk.io/x/accounts/interfaces/account_abstraction/v1"
 	"cosmossdk.io/x/tx/signing"
 
-	authn "cosmossdk.io/x/accounts/defaults/base/keys/authn"
-	cometcrypto "github.com/cometbft/cometbft/crypto"
 	codectypes "github.com/cosmos/cosmos-sdk/codec/types"
 	"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
 	"github.com/cosmos/cosmos-sdk/types/address"
@@ -45,7 +45,7 @@ type CollectedClientData struct {
 	// Type the string "webauthn.create" when creating new credentials,
 	// and "webauthn.get" when getting an assertion from an existing credential. The
 	// purpose of this member is to prevent certain types of signature confusion attacks
-	//(where an attacker substitutes one legitimate signature for another).
+	// (where an attacker substitutes one legitimate signature for another).
 	Type         CeremonyType  `json:"type"`
 	Challenge    string        `json:"challenge"`
 	Origin       string        `json:"origin"`
@@ -61,7 +61,7 @@ func GenerateAuthnKey(t *testing.T) (*ecdsa.PrivateKey, authn.AuthnPubKey) {
 	require.NoError(t, err)
 	pkBytes := elliptic.MarshalCompressed(curve, privateKey.PublicKey.X, privateKey.PublicKey.Y)
 	pk := authn.AuthnPubKey{
-		KeyId: "a099eda0fb05e5783379f73a06acca726673b8e07e436edcd0d71645982af65c",
+		KeyId: "f482bd153df0ca2ea17d7b1e0178c14ff959628f",
 		Key:   pkBytes,
 	}
 
@@ -71,11 +71,11 @@ func GenerateAuthnKey(t *testing.T) (*ecdsa.PrivateKey, authn.AuthnPubKey) {
 func GenerateClientData(t *testing.T, msg []byte) []byte {
 	t.Helper()
 	clientData := CollectedClientData{
-		// purpose of this member is to prevent certain types of signature confusion attacks
-		//(where an attacker substitutes one legitimate signature for another).
+		// Purpose of this member is to prevent certain types of signature confusion attacks
+		// (Where an attacker substitutes one legitimate signature for another).
 		Type:         "webauthn.create",
 		Challenge:    base64.RawURLEncoding.EncodeToString(msg),
-		Origin:       "https://blue.kujira.network",
+		Origin:       "https://cosmos.network",
 		TokenBinding: nil,
 		Hint:         "",
 	}
@@ -407,6 +407,8 @@ func TestAuthenticateAuthn(t *testing.T) {
 
 	sigBytes, err := json.Marshal(cborSig)
 
+	require.NoError(t, err)
+
 	transaction.Signatures = append(transaction.Signatures, sigBytes)
 
 	rawTx := tx.TxRaw{
diff --git a/x/accounts/defaults/base/keys/authn/authn_pubkey.go b/x/accounts/defaults/base/keys/authn/authn_pubkey.go
index 01a770b3c501..cf052db3f23e 100644
--- a/x/accounts/defaults/base/keys/authn/authn_pubkey.go
+++ b/x/accounts/defaults/base/keys/authn/authn_pubkey.go
@@ -3,6 +3,7 @@ package authn
 import (
 	"bytes"
 	"crypto"
+	ecdsa "crypto/ecdsa"
 	"crypto/elliptic"
 	"crypto/sha256"
 	"encoding/base64"
@@ -11,15 +12,10 @@ import (
 	"fmt"
 
 	cometcrypto "github.com/cometbft/cometbft/crypto"
+	"github.com/cosmos/gogoproto/proto"
 
-	ecdsa "crypto/ecdsa"
-
-	errorsmod "cosmossdk.io/errors"
-	"github.com/cosmos/cosmos-sdk/codec"
 	cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
 	"github.com/cosmos/cosmos-sdk/types/address"
-	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
-	"github.com/cosmos/gogoproto/proto"
 )
 
 type Signature struct {
@@ -34,8 +30,7 @@ const (
 )
 
 var (
-	_ cryptotypes.PubKey   = &AuthnPubKey{}
-	_ codec.AminoMarshaler = &AuthnPubKey{}
+	_ cryptotypes.PubKey = (*AuthnPubKey)(nil)
 )
 
 const AuthnPubKeySize = 33
@@ -64,31 +59,6 @@ func (pubKey *AuthnPubKey) Equals(other cryptotypes.PubKey) bool {
 	return pubKey.Type() == other.Type() && bytes.Equal(pubKey.Bytes(), other.Bytes())
 }
 
-func (pubKey AuthnPubKey) MarshalAmino() ([]byte, error) {
-	return pubKey.Key, nil
-}
-
-func (pubKey *AuthnPubKey) UnmarshalAmino(bz []byte) error {
-	if len(bz) != AuthnPubKeySize {
-		return errorsmod.Wrap(sdkerrors.ErrInvalidPubKey, "invalid pubkey size")
-	}
-	pubKey.Key = bz
-
-	return nil
-}
-
-// MarshalAminoJSON overrides Amino JSON marshaling.
-func (pubKey AuthnPubKey) MarshalAminoJSON() ([]byte, error) {
-	// When we marshal to Amino JSON, we don't marshal the "key" field itself,
-	// just its contents (i.e. the key bytes).
-	return pubKey.MarshalAmino()
-}
-
-// UnmarshalAminoJSON overrides Amino JSON marshaling.
-func (pubKey *AuthnPubKey) UnmarshalAminoJSON(bz []byte) error {
-	return pubKey.UnmarshalAmino(bz)
-}
-
 func (pubKey *AuthnPubKey) VerifySignature(msg, sigStr []byte) bool {
 	sig := Signature{}
 	err := json.Unmarshal(sigStr, &sig)
diff --git a/x/accounts/defaults/base/keys/authn/authn_pubkey.pb.go b/x/accounts/defaults/base/keys/authn/authn_pubkey.pb.go
index 4a49fe5f8fd4..fd7eed8b65d2 100644
--- a/x/accounts/defaults/base/keys/authn/authn_pubkey.pb.go
+++ b/x/accounts/defaults/base/keys/authn/authn_pubkey.pb.go
@@ -92,17 +92,17 @@ var fileDescriptor_0d353fd28c6ed153 = []byte{
 	0x2c, 0xd6, 0x4f, 0x2c, 0x2d, 0xc9, 0xc8, 0x8b, 0x2f, 0x28, 0x4d, 0xca, 0x4e, 0xad, 0xd4, 0x2b,
 	0x28, 0xca, 0x2f, 0xc9, 0x17, 0x52, 0x80, 0xe8, 0xd4, 0x83, 0xe9, 0xd4, 0x83, 0xe9, 0xd4, 0x03,
 	0xe9, 0xd4, 0x2b, 0x33, 0x94, 0x12, 0x4c, 0xcc, 0xcd, 0xcc, 0xcb, 0xd7, 0x07, 0x93, 0x10, 0x4d,
-	0x52, 0x22, 0xe9, 0xf9, 0xe9, 0xf9, 0x60, 0xa6, 0x3e, 0x88, 0x05, 0x11, 0x55, 0x4a, 0xe5, 0xe2,
+	0x52, 0x22, 0xe9, 0xf9, 0xe9, 0xf9, 0x60, 0xa6, 0x3e, 0x88, 0x05, 0x11, 0x55, 0xca, 0xe3, 0xe2,
 	0x76, 0x04, 0x59, 0x10, 0x50, 0x9a, 0xe4, 0x9d, 0x5a, 0x29, 0x24, 0xca, 0xc5, 0x96, 0x9d, 0x5a,
 	0x19, 0x9f, 0x99, 0x22, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0x19, 0xc4, 0x9a, 0x9d, 0x5a, 0xe9, 0x99,
 	0x22, 0x24, 0xc0, 0xc5, 0x9c, 0x9d, 0x5a, 0x29, 0xc1, 0xa4, 0xc0, 0xa8, 0xc1, 0x13, 0x04, 0x62,
-	0x5a, 0xe9, 0xce, 0x58, 0x20, 0xcf, 0xd0, 0xf5, 0x7c, 0x83, 0x96, 0x58, 0x49, 0x6a, 0x5e, 0x4a,
-	0x6a, 0x51, 0x6e, 0x66, 0x5e, 0x89, 0x3e, 0xc4, 0x10, 0xb0, 0x79, 0x93, 0x9e, 0x6f, 0xd0, 0xe2,
-	0x04, 0x19, 0x95, 0x96, 0x99, 0x9a, 0x93, 0xe2, 0xe4, 0x75, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47,
-	0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d,
-	0xc7, 0x72, 0x0c, 0x51, 0x06, 0x10, 0xbf, 0x14, 0xa7, 0x64, 0xeb, 0x65, 0xe6, 0xeb, 0x57, 0xe0,
-	0x0a, 0x0d, 0x44, 0x50, 0x24, 0xb1, 0x81, 0x5d, 0x6e, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0xc8,
-	0x03, 0x55, 0x76, 0x40, 0x01, 0x00, 0x00,
+	0x5a, 0x99, 0xcd, 0x58, 0x20, 0xcf, 0xd0, 0xf5, 0x7c, 0x83, 0x96, 0x3c, 0xc4, 0x2d, 0xba, 0xc5,
+	0x29, 0xd9, 0xfa, 0x8e, 0x30, 0x9f, 0x40, 0x4c, 0x03, 0x1b, 0x3c, 0xe9, 0xf9, 0x06, 0x2d, 0x4e,
+	0x90, 0x99, 0x69, 0x99, 0xa9, 0x39, 0x29, 0x4e, 0x5e, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24,
+	0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x17, 0x1e, 0xcb, 0x31, 0xdc, 0x78,
+	0x2c, 0xc7, 0x10, 0x65, 0x00, 0x31, 0xa8, 0x38, 0x25, 0x5b, 0x2f, 0x33, 0x5f, 0xbf, 0x02, 0x57,
+	0xb0, 0x20, 0xc2, 0x24, 0x89, 0x0d, 0xec, 0x05, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x2b,
+	0xf6, 0x88, 0xde, 0x49, 0x01, 0x00, 0x00,
 }
 
 func (m *AuthnPubKey) Marshal() (dAtA []byte, err error) {
diff --git a/x/accounts/proto/cosmos/accounts/defaults/base/v1/keys/authn_pubkey.proto b/x/accounts/proto/cosmos/accounts/defaults/base/v1/keys/authn_pubkey.proto
index 324c59d21fab..b3f2e2c31270 100644
--- a/x/accounts/proto/cosmos/accounts/defaults/base/v1/keys/authn_pubkey.proto
+++ b/x/accounts/proto/cosmos/accounts/defaults/base/v1/keys/authn_pubkey.proto
@@ -7,12 +7,16 @@ import "gogoproto/gogo.proto";
 option go_package = "cosmossdk.io/x/accounts/defaults/base/keys/authn";
 
 // PubKey defines a authn public key
-message AuthnPubKey {
-  option (amino.name) = "tendermint/PubKeyAuthn";
+message authnpubkey {
+  option (amino.name) = "cosmos-sdk/accounts/pubkeyauthn";
 
   option (amino.message_encoding)     = "key_field";
   option (gogoproto.goproto_stringer) = false;
 
+  // The key_id (or credential ID) is a unique identifier for a passkey.
+  // This ID is provided by the authenticator when the passkey is created.
+  // As it is not possible to retrieve the public key from the authenticator after the passkey is created,
+  // if the user loses the public key - id association, the key_id can be used to retrieve the public key.
   string key_id = 1;
   bytes  key    = 2;
 }