200 lines
5.1 KiB
Go
200 lines
5.1 KiB
Go
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
|
|
P2DeleteObject = 0x00
|
|
P2DeleteObjectAndRelatedObject = 0x80
|
|
|
|
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,
|
|
)
|
|
|
|
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, p2 uint8) *apdu.Command {
|
|
data := []byte{tagDeleteAID, byte(len(aid))}
|
|
data = append(data, aid...)
|
|
|
|
return apdu.NewCommand(
|
|
ClaGp,
|
|
InsDelete,
|
|
0,
|
|
p2,
|
|
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)
|
|
}
|