package globalplatform import ( "github.com/status-im/keycard-go/apdu" "github.com/status-im/keycard-go/globalplatform/crypto" ) // Constants used in apdu commands and responses as defined by iso7816 and globalplatform. const ( ClaISO7816 = 0x00 ClaGp = 0x80 ClaMac = 0x84 InsSelect = 0xA4 InsInitializeUpdate = 0x50 InsExternalAuthenticate = 0x82 InsGetResponse = 0xC0 InsDelete = 0xE4 InsLoad = 0xE8 InsInstall = 0xE6 InsGetStatus = 0xF2 P1ExternalAuthenticateCMAC = 0x01 P1InstallForLoad = 0x02 P1InstallForInstall = 0x04 P1InstallForMakeSelectable = 0x08 P1LoadMoreBlocks = 0x00 P1LoadLastBlock = 0x80 P1GetStatusIssuerSecurityDomain = 0x80 P1GetStatusApplications = 0x40 P1GetStatusExecLoadFiles = 0x20 P1GetStatusExecLoadFilesAndModules = 0x10 P2GetStatusTLVData = 0x02 Sw1ResponseDataIncomplete = 0x61 SwOK = 0x9000 SwFileNotFound = 0x6A82 SwReferencedDataNotFound = 0x6A88 SwSecurityConditionNotSatisfied = 0x6982 SwAuthenticationMethodBlocked = 0x6983 tagDeleteAID = 0x4F tagLoadFileDataBlock = 0xC4 tagGetStatusAID = 0x4F ) // NewCommandSelect returns a Select command as defined in the globalplatform specifications. func NewCommandSelect(aid []byte) *apdu.Command { c := apdu.NewCommand( ClaISO7816, InsSelect, 0x04, 0, aid, ) // with T=0 we can both set or not the Le value // with T=1 it works only without Le // c.SetLe(0x00) return c } // NewCommandInitializeUpdate returns an Initialize Update command as defined in the globalplatform specifications. func NewCommandInitializeUpdate(challenge []byte) *apdu.Command { c := apdu.NewCommand( ClaGp, InsInitializeUpdate, 0, 0, challenge, ) // with T=0 we can both set or not the Le value // with T=1 it works only if Le is set c.SetLe(0x00) return c } // NewCommandExternalAuthenticate returns an External Authenticate command as defined in the globalplatform specifications. func NewCommandExternalAuthenticate(encKey, cardChallenge, hostChallenge []byte) (*apdu.Command, error) { hostCryptogram, err := calculateHostCryptogram(encKey, cardChallenge, hostChallenge) if err != nil { return nil, err } return apdu.NewCommand( ClaMac, InsExternalAuthenticate, P1ExternalAuthenticateCMAC, 0, hostCryptogram, ), nil } // NewCommandGetResponse returns a Get Response command as defined in the globalplatform specifications. func NewCommandGetResponse(length uint8) *apdu.Command { c := apdu.NewCommand( ClaISO7816, InsGetResponse, 0, 0, nil, ) c.SetLe(length) return c } // NewCommandDelete returns a Delete command as defined in the globalplatform specifications. func NewCommandDelete(aid []byte) *apdu.Command { data := []byte{tagDeleteAID, byte(len(aid))} data = append(data, aid...) return apdu.NewCommand( ClaGp, InsDelete, 0, 0, data, ) } // NewCommandInstallForLoad returns an Install command with the install-for-load parameter as defined in the globalplatform specifications. func NewCommandInstallForLoad(aid, sdaid []byte) *apdu.Command { data := []byte{byte(len(aid))} data = append(data, aid...) data = append(data, byte(len(sdaid))) data = append(data, sdaid...) // empty hash length and hash data = append(data, []byte{0x00, 0x00, 0x00}...) return apdu.NewCommand( ClaGp, InsInstall, P1InstallForLoad, 0, data, ) } // NewCommandInstallForInstall returns an Install command with the install-for-instalp parameter as defined in the globalplatform specifications. func NewCommandInstallForInstall(pkgAID, appletAID, instanceAID, params []byte) *apdu.Command { data := []byte{byte(len(pkgAID))} data = append(data, pkgAID...) data = append(data, byte(len(appletAID))) data = append(data, appletAID...) data = append(data, byte(len(instanceAID))) data = append(data, instanceAID...) // privileges priv := []byte{0x00} data = append(data, byte(len(priv))) data = append(data, priv...) // params fullParams := []byte{byte(0xC9), byte(len(params))} fullParams = append(fullParams, params...) data = append(data, byte(len(fullParams))) data = append(data, fullParams...) // empty perform token data = append(data, byte(0x00)) return apdu.NewCommand( ClaGp, InsInstall, P1InstallForInstall|P1InstallForMakeSelectable, 0, data, ) } // NewCommandGetStatus returns a Get Status command as defined in the globalplatform specifications. func NewCommandGetStatus(aid []byte, p1 uint8) *apdu.Command { data := []byte{tagGetStatusAID} data = append(data, byte(len(aid))) data = append(data, aid...) return apdu.NewCommand( ClaGp, InsGetStatus, p1, P2GetStatusTLVData, data, ) } func calculateHostCryptogram(encKey, cardChallenge, hostChallenge []byte) ([]byte, error) { var data []byte data = append(data, cardChallenge...) data = append(data, hostChallenge...) data = crypto.AppendDESPadding(data) return crypto.Mac3DES(encKey, data, crypto.NullBytes8) }