diff --git a/x/accounts/defaults/base/account_test.go b/x/accounts/defaults/base/account_test.go index 8523d47c1f9b..91af6567cca3 100644 --- a/x/accounts/defaults/base/account_test.go +++ b/x/accounts/defaults/base/account_test.go @@ -2,9 +2,18 @@ package base import ( "context" + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/sha256" + "encoding/base64" + "encoding/hex" + "encoding/json" "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" @@ -12,6 +21,7 @@ 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" @@ -22,15 +32,74 @@ import ( "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: "f482bd153df0ca2ea17d7b1e0178c14ff959628f", + 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://cosmos.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,143 @@ 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) + + require.NoError(t, err) + + 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..cf052db3f23e --- /dev/null +++ b/x/accounts/defaults/base/keys/authn/authn_pubkey.go @@ -0,0 +1,123 @@ +package authn + +import ( + "bytes" + "crypto" + ecdsa "crypto/ecdsa" + "crypto/elliptic" + "crypto/sha256" + "encoding/base64" + "encoding/hex" + "encoding/json" + "fmt" + + cometcrypto "github.com/cometbft/cometbft/crypto" + "github.com/cosmos/gogoproto/proto" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/types/address" +) + +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)(nil) +) + +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) 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..fd7eed8b65d2 --- /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, 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, 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) { + 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..b3f2e2c31270 --- /dev/null +++ b/x/accounts/proto/cosmos/accounts/defaults/base/v1/keys/authn_pubkey.proto @@ -0,0 +1,22 @@ +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) = "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; +}