diff --git a/globalplatform/commands.go b/globalplatform/commands.go index 38754b8..a0e04c2 100644 --- a/globalplatform/commands.go +++ b/globalplatform/commands.go @@ -15,12 +15,15 @@ const ( InsExternalAuthenticate = uint8(0x82) InsGetResponse = uint8(0xC0) InsDelete = uint8(0xE4) + InsLoad = uint8(0xE8) InsInstall = uint8(0xE6) P1ExternalAuthenticateCMAC = uint8(0x01) P1InstallForLoad = uint8(0x02) P1InstallForInstall = uint8(0x04) P1InstallForMakeSelectable = uint8(0x08) + P1LoadMoreBlocks = uint8(0x00) + P1LoadLastBlock = uint8(0x80) Sw1ResponseDataIncomplete = uint8(0x61) @@ -29,7 +32,8 @@ const ( SwSecurityConditionNotSatisfied = uint16(0x6982) SwAuthenticationMethodBlocked = uint16(0x6983) - tagDeleteAID = byte(0x4F) + tagDeleteAID = byte(0x4F) + tagLoadFileDataBlock = byte(0xC4) ) func NewCommandSelect(aid []byte) *apdu.Command { @@ -65,7 +69,7 @@ func NewCommandExternalAuthenticate(encKey, cardChallenge, hostChallenge []byte) return apdu.NewCommand( ClaMac, InsExternalAuthenticate, - P1ExternalAuthenticateCMAC, // C-MAC + P1ExternalAuthenticateCMAC, uint8(0x00), hostCryptogram, ), nil diff --git a/globalplatform/load.go b/globalplatform/load.go new file mode 100644 index 0000000..412e072 --- /dev/null +++ b/globalplatform/load.go @@ -0,0 +1,150 @@ +package globalplatform + +import ( + "archive/zip" + "bytes" + "io/ioutil" + "os" + "strings" + + "github.com/status-im/status-go/smartcard/apdu" +) + +var internalFiles = []string{ + "Header", "Directory", "Import", "Applet", "Class", + "Method", "StaticField", "Export", "ConstantPool", "RefLocation", +} + +type LoadCommandStream struct { + data *bytes.Reader + currentIndex uint8 + currentData []byte + p1 uint8 +} + +func NewLoadCommandStream(file *os.File) (*LoadCommandStream, error) { + files, err := loadFiles(file) + if err != nil { + return nil, err + } + + data, err := encodeFilesData(files) + if err != nil { + return nil, err + } + + return &LoadCommandStream{ + data: bytes.NewReader(data), + p1: P1LoadMoreBlocks, + }, nil +} + +// TODO:@pilu update blockSize when using encrypted data +func (lcs *LoadCommandStream) Next() bool { + if lcs.data.Len() == 0 { + return false + } + + blockSize := 247 // 255 - 8 bytes for MAC + buf := make([]byte, blockSize) + n, err := lcs.data.Read(buf) + if err != nil { + return false + } + + lcs.currentData = buf[:n] + lcs.currentIndex++ + + if lcs.data.Len() == 0 { + lcs.p1 = P1LoadLastBlock + } + + return true +} + +func (lcs *LoadCommandStream) Index() uint8 { + return lcs.currentIndex - 1 +} + +func (lcs *LoadCommandStream) GetCommand() *apdu.Command { + return apdu.NewCommand(ClaGp, InsLoad, lcs.p1, lcs.Index(), lcs.currentData) +} + +func loadFiles(f *os.File) (map[string][]byte, error) { + fi, err := f.Stat() + if err != nil { + return nil, err + } + + z, err := zip.NewReader(f, fi.Size()) + if err != nil { + return nil, err + } + + files := make(map[string][]byte) + + for _, item := range z.File { + name := strings.Split(item.FileInfo().Name(), ".")[0] + f, err := item.Open() + if err != nil { + return nil, err + } + + data, err := ioutil.ReadAll(f) + if err != nil { + return nil, err + } + + files[name] = data + } + + return files, nil +} + +func encodeFilesData(files map[string][]byte) ([]byte, error) { + var buf bytes.Buffer + + for _, name := range internalFiles { + if data, ok := files[name]; ok { + buf.Write(data) + } + } + + filesData := buf.Bytes() + length := encodeLength(len(filesData)) + + data := make([]byte, 0) + data = append(data, tagLoadFileDataBlock) + data = append(data, length...) + data = append(data, filesData...) + + return data, nil +} + +func encodeLength(length int) []byte { + if length < 0x80 { + return []byte{byte(length)} + } + + if length < 0xFF { + return []byte{ + byte(0x81), + byte(length), + } + } + + if length < 0xFFFF { + return []byte{ + byte(0x82), + byte((length & 0xFF00) >> 8), + byte(length & 0xFF), + } + } + + return []byte{ + byte(0x83), + byte((length & 0xFF0000) >> 16), + byte((length & 0xFF00) >> 8), + byte(length & 0xFF), + } +}