summaryrefslogtreecommitdiffstats
path: root/src/core/crypto/key_manager.h
blob: 978eec8dc5a74dd0aa52b36563c5f580cfc0a056 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.

#pragma once

#include <array>
#include <string>
#include <boost/container/flat_map.hpp>
#include <boost/optional.hpp>
#include <fmt/format.h>
#include "common/common_types.h"

namespace Loader {
enum class ResultStatus : u16;
}

namespace Core::Crypto {

constexpr u64 TICKET_FILE_TITLEKEY_OFFSET = 0x180;

using Key128 = std::array<u8, 0x10>;
using Key256 = std::array<u8, 0x20>;
using SHA256Hash = std::array<u8, 0x20>;

static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big.");
static_assert(sizeof(Key256) == 32, "Key128 must be 128 bytes big.");

enum class S256KeyType : u64 {
    Header,      //
    SDKeySource, // f1=SDKeyType
};

enum class S128KeyType : u64 {
    Master,        // f1=crypto revision
    Package1,      // f1=crypto revision
    Package2,      // f1=crypto revision
    Titlekek,      // f1=crypto revision
    ETicketRSAKek, //
    KeyArea,       // f1=crypto revision f2=type {app, ocean, system}
    SDSeed,        //
    Titlekey,      // f1=rights id LSB f2=rights id MSB
    Source,        // f1=source type, f2= sub id
};

enum class KeyAreaKeyType : u8 {
    Application,
    Ocean,
    System,
};

enum class SourceKeyType : u8 {
    SDKEK,
    AESKEKGeneration,
    AESKeyGeneration,
};

enum class SDKeyType : u8 {
    Save,
    NCA,
};

template <typename KeyType>
struct KeyIndex {
    KeyType type;
    u64 field1;
    u64 field2;

    std::string DebugInfo() const {
        u8 key_size = 16;
        if constexpr (std::is_same_v<KeyType, S256KeyType>)
            key_size = 32;
        return fmt::format("key_size={:02X}, key={:02X}, field1={:016X}, field2={:016X}", key_size,
                           static_cast<u8>(type), field1, field2);
    }
};

// boost flat_map requires operator< for O(log(n)) lookups.
template <typename KeyType>
bool operator<(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) {
    return std::tie(lhs.type, lhs.field1, lhs.field2) < std::tie(rhs.type, rhs.field1, rhs.field2);
}

class KeyManager {
public:
    KeyManager();

    bool HasKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const;
    bool HasKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const;

    Key128 GetKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const;
    Key256 GetKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const;

    void SetKey(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
    void SetKey(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);

    static bool KeyFileExists(bool title);

    // Call before using the sd seed to attempt to derive it if it dosen't exist. Needs system save
    // 8*43 and the private file to exist.
    void DeriveSDSeedLazy();

private:
    boost::container::flat_map<KeyIndex<S128KeyType>, Key128> s128_keys;
    boost::container::flat_map<KeyIndex<S256KeyType>, Key256> s256_keys;

    bool dev_mode;
    void LoadFromFile(const std::string& filename, bool is_title_keys);
    void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
                            const std::string& filename, bool title);
    template <std::size_t Size>
    void WriteKeyToFile(bool title_key, std::string_view keyname, const std::array<u8, Size>& key);

    static const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> s128_file_id;
    static const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> s256_file_id;
};

Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed);
boost::optional<Key128> DeriveSDSeed();
Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, const KeyManager& keys);

} // namespace Core::Crypto