Skip to content

Commit

Permalink
feat: NSP utils - Check Ticket (#10)
Browse files Browse the repository at this point in the history
* Add new config entry

* Placeholder for verifying nsp

* New Source interface

* Add new mock for Source

* Add header for bash file

* Split sources for clarity

* Rename files since better package

* Add more comments

* Cleaner way to verify nsp

* Fix lint issue

* Starting point for NSP

* Add reading file entry

* Read filename

* Add information about ticket

* Reading ticket information

* working title key comparison

* Basic retructuration

* Fix lint issues

* Add more credti

* Clean implementation for local files

* Update config

* Cleaner handle error for directory

* Beginning to add check for nfs

* Finish implementation for nfs

* Cleaner message

* Fix: issue with watcher and no directory

* Handling no ticket in nsp/nsz

* Add new config for debuging ticket

* Add new tests

* doc: Update readme

* Fix lint cyclo issue

* Change settings to reduce complexity lint

* doc: Give more explanation

Co-authored-by: DblK <admin@dblk.org>
  • Loading branch information
DblK and DblK authored Dec 28, 2021
1 parent 887e4fe commit 261b6c0
Show file tree
Hide file tree
Showing 25 changed files with 1,017 additions and 410 deletions.
7 changes: 6 additions & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
linters-settings:
lll:
line-length: 200
gocyclo:
min-complexity: 20

linters:
enable:
Expand Down Expand Up @@ -39,6 +41,7 @@ linters:
- whitespace
# - wsl
- exportloopref
- golint
disable:
- noctx
- scopelint
Expand All @@ -57,4 +60,6 @@ issues:
- path: _test\.go
linters:
- gochecknoglobals
- dupl
- dupl
include:
- EXC0002
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
"dupl",
"errcheck",
"Errorf",
"errorlint",
"ETICKET",
"exportloopref",
"Fatalf",
"fsnotify",
Expand Down Expand Up @@ -39,6 +41,7 @@
"nestif",
"noctx",
"nolintlint",
"nxdumptool",
"prealloc",
"renameio",
"robustio",
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ Here is the list of all main features so far:
- [X] You can specify custom titledb to be merged with official one
- [X] Auto-watch for mounted directories
- [X] Add filters path for shop
- [X] Simple ticket check in NSP/NSZ

## Filtering

Expand Down Expand Up @@ -118,9 +119,17 @@ For other type of protection, you can whitelist your own switch and this will do
First, download and replace the latest [`titles.US.en.json`](https://github.com/AdamK2003/titledb/releases/download/latest/titles.US.en.json) available (or delete it, it will be automatically downloaded at startup).
If this does not solve your issue, then you should use custom titledb entry to describe those which are missing.

## Why I still get `NCA signature verification failed` error in `tinfoil` and nothing in `tinshop`?

The current implementation to verify the NSP/NSZ are basic and based on the Ticket information.
So you might still get some error about signature failed even with `checkVerified` enabled.

Maybe later, this feature will be enhanced to add additional checks on game files (PR Welcome!).

# Credits

I would like to give back thanks to the people who helped me with or without knowing!
- [Bogdan Rosu Creative](https://www.iconfinder.com/icons/353439/basket_purse_shopping_cart_ecommerce_shop_buy_online_icon) for his shop icon.
- [Dono](https://github.com/Donorhan) for his support and tests.
- [AdamK2003](https://github.com/AdamK2003/titledb) for his up-to-date [`titles.US.en.json`](https://github.com/AdamK2003/titledb/releases/download/latest/titles.US.en.json) and his answers on discord.
- [nxdumptool](https://github.com/DarkMatterCore/nxdumptool) for the information taken of NSP format
9 changes: 8 additions & 1 deletion config.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,14 @@ debug:
nfs: false
# Remove middleware security for retrieving index
# DO NOT use in production (only for dev purpose)
noSecurity: true
noSecurity: false
# Display more information about ticket's verification
ticket: false

# All actions related to NSP file will be stored here
nsp:
# Tells if tinshop should verify the ticket inside NSP to ensure no issue with install
checkVerified: true

# All sources where we should look for games
# If this section is commented out, then the directory "games" will be looked at
Expand Down
16 changes: 16 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
type debug struct {
Nfs bool
NoSecurity bool
Ticket bool
}

type security struct {
Expand All @@ -29,6 +30,10 @@ type security struct {
BannedTheme []string `mapstructure:"bannedTheme"`
}

type nsp struct {
CheckVerified bool `mapstructure:"checkVerified"`
}

// File holds all config information
type File struct {
rootShop string
Expand All @@ -40,6 +45,7 @@ type File struct {
Name string `mapstructure:"name"`
Security security `mapstructure:"security"`
CustomTitleDB map[string]repository.TitleDBEntry `mapstructure:"customTitledb"`
NSP nsp `mapsstructure:"nsp"`
shopTemplateData repository.ShopTemplate
}

Expand Down Expand Up @@ -181,6 +187,11 @@ func (cfg *File) Port() int {
return cfg.ShopPort
}

// DebugTicket tells if we should display additional log for ticket verification
func (cfg *File) DebugTicket() bool {
return cfg.Debug.Ticket
}

// DebugNfs tells if we should display additional log for nfs
func (cfg *File) DebugNfs() bool {
return cfg.Debug.Nfs
Expand Down Expand Up @@ -226,6 +237,11 @@ func (cfg *File) ShopTitle() string {
return cfg.Name
}

// VerifyNSP tells if we need to verify NSP
func (cfg *File) VerifyNSP() bool {
return cfg.NSP.CheckVerified
}

// IsBlacklisted tells if the uid is blacklisted or not
func (cfg *File) IsBlacklisted(uid string) bool {
if len(cfg.Security.Whitelist) != 0 {
Expand Down
20 changes: 20 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,16 @@ var _ = Describe("Config", func() {
Expect(myConfig.DebugNfs()).To(BeTrue())
})
})
Describe("VerifyNSP", func() {
var myConfig = config.File{}
It("Test with empty object", func() {
Expect(myConfig.VerifyNSP()).To(BeFalse())
})
It("Test with a value", func() {
myConfig.NSP.CheckVerified = true
Expect(myConfig.VerifyNSP()).To(BeTrue())
})
})
Describe("DebugNoSecurity", func() {
var myConfig = config.File{}
It("Test with empty object", func() {
Expand All @@ -280,6 +290,16 @@ var _ = Describe("Config", func() {
Expect(myConfig.DebugNoSecurity()).To(BeTrue())
})
})
Describe("DebugTicket", func() {
var myConfig = config.File{}
It("Test with empty object", func() {
Expect(myConfig.DebugTicket()).To(BeFalse())
})
It("Test with a value", func() {
myConfig.Debug.Ticket = true
Expect(myConfig.DebugTicket()).To(BeTrue())
})
})
Describe("BannedTheme", func() {
var myConfig = config.File{}
It("Test with empty object", func() {
Expand Down
10 changes: 10 additions & 0 deletions gamescollection/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package gamescollection

import (
"encoding/json"
"errors"
"io"
"log"
"os"
Expand Down Expand Up @@ -223,3 +224,12 @@ func AddNewGames(newGames []repository.FileDesc) {
games.Files = append(games.Files, gameList...)
log.Printf("Added %d games in your library\n", len(gameList))
}

// GetKey return the key from the titledb
func GetKey(gameID string) (string, error) {
var key = Library()[gameID].Key
if key == "" {
return "", errors.New("TitleDBKey for game " + gameID + " is not found")
}
return key, nil
}
47 changes: 47 additions & 0 deletions gamescollection/collection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,4 +168,51 @@ var _ = Describe("Collection", func() {
Expect(len(filteredGames.Files)).To(Equal(1))
})
})
FDescribe("GetKey", func() {
var (
myMockConfig *mock_repository.MockConfig
ctrl *gomock.Controller
)
BeforeEach(func() {
ctrl = gomock.NewController(GinkgoT())
})
JustBeforeEach(func() {
myMockConfig = mock_repository.NewMockConfig(ctrl)
customDB := make(map[string]repository.TitleDBEntry)
custom1 := repository.TitleDBEntry{
ID: "0000000000000001",
Key: "My Key",
}
customDB["0000000000000001"] = custom1
custom2 := repository.TitleDBEntry{
ID: "0000000000000002",
Key: "",
}
customDB["0000000000000001"] = custom1
customDB["0000000000000002"] = custom2

myMockConfig.EXPECT().
CustomDB().
Return(customDB).
AnyTimes()
myMockConfig.EXPECT().
BannedTheme().
Return(nil).
AnyTimes()

collection.OnConfigUpdate(myMockConfig)
})
It("Retrieving existing Key", func() {
key, err := collection.GetKey("0000000000000001")
Expect(err).To(BeNil())
Expect(key).To(Not(BeEmpty()))
Expect(key).To(Equal("My Key"))
})
It("Retrieving not existing Key", func() {
key, err := collection.GetKey("0000000000000002")
Expect(err).To(Not(BeNil()))
Expect(err.Error()).To(Equal("TitleDBKey for game 0000000000000002 is not found"))
Expect(key).To(BeEmpty())
})
})
})
28 changes: 28 additions & 0 deletions mock_repository/mock_config.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

98 changes: 98 additions & 0 deletions mock_repository/mock_source.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions nsp/constant.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package nsp

const (
eTicketTIKFileSize uint64 = 704
eTicketTitleKeyCommon uint8 = 0
genericIssuer string = "Root-CA00000003-XS00000020"
)
Loading

0 comments on commit 261b6c0

Please sign in to comment.