kuon.steam.steam_guard.SteamGuard.__init__()   B
last analyzed

Complexity

Conditions 7

Size

Total Lines 41
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
eloc 23
dl 0
loc 41
ccs 0
cts 22
cp 0
rs 7.9279
c 0
b 0
f 0
cc 7
nop 7
crap 56
1
#!/usr/bin/python
2
# -*- coding: utf-8 -*-
3
import hmac
4
import os
5
import struct
6
import time
7
from base64 import b64decode, b64encode
8
from hashlib import sha1
9
10
import dotenv
11
12
from kuon.steam.api.exceptions import *
13
14
15
class SteamGuard(object):
16
    """Class to provide functionality regarding SteamGuard
17
    big thanks to bukson/steampy who extracted most of these functions from the mobile app"""
18
19
    def __init__(self, user: str = None, password: str = None, id_64: str = None, api_key: str = None,
20
                 secret: str = None, identity: str = None) -> None:
21
        """Initializing function
22
23
        :type user: str
24
        :type password: str
25
        :type id_64: str
26
        :type api_key: str
27
        :type secret: str
28
        :type identity: str
29
        """
30
31
        dotenv_path = os.path.join(os.path.dirname(__file__), os.path.pardir, os.path.pardir, '.env')
32
        dotenv.load_dotenv(dotenv_path)
33
34
        self._user = user
35
        self._password = password
36
        self._id64 = id_64
37
        self._api_key = api_key
38
        self._secret = secret
39
        self._identity = identity
40
41
        if not self._user:
42
            self._user = os.environ.get('STEAM_LOGIN_ID')
43
44
        if not self._password:
45
            self._password = os.environ.get('STEAM_LOGIN_PASSWORD')
46
47
        if not self._id64:
48
            self._id64 = os.environ.get('STEAM_ID64')
49
50
        if not api_key:
51
            self._api_key = os.environ.get('STEAM_API_KEY')
52
53
        if not secret:
54
            self._secret = os.environ.get('STEAM_2FA_SECRET')
55
56
        if not self._identity:
57
            self._identity = os.environ.get('STEAM_IDENTITY_SECRET')
58
59
        self.check_required_variables()
60
61
    @property
62
    def generate_one_time_code(self) -> str:
63
        """Generate the login code like the mobile app does
64
65
        :return:
66
        """
67
        timestamp = int(time.time())
68
        time_buffer = struct.pack('>Q', timestamp // 30)  # pack as Big endian, uint64
69
        time_hmac = hmac.new(b64decode(self._secret), time_buffer, digestmod=sha1).digest()
70
        begin = ord(time_hmac[19:20]) & 0xf
71
        full_code = struct.unpack('>I', time_hmac[begin:begin + 4])[0] & 0x7fffffff  # unpack as Big endian uint32
72
        chars = '23456789BCDFGHJKMNPQRTVWXY'
73
        code = ''
74
75
        for i in range(5):
76
            full_code, i = divmod(full_code, len(chars))
77
            code += chars[i]
78
79
        return code
80
81
    def get_confirmation_key(self, tag: str) -> bytes:
82
        """Generate the confirmation key of the tag like the mobile app does
83
84
        :type tag: str
85
        :return:
86
        """
87
        timestamp = int(time.time())
88
        buffer = struct.pack('>Q', timestamp) + tag.encode('ascii')
89
        return b64encode(hmac.new(b64decode(self._identity), buffer, digestmod=sha1).digest())
90
91
    @property
92
    def generate_device_id(self) -> str:
93
        """Generate a device needed for confirmations (similar to the mobile device app)
94
95
        :return:
96
        """
97
        hexed_steam_id = sha1(self._id64.encode('ascii')).hexdigest()
98
        return 'android:' + '-'.join([hexed_steam_id[:8],
99
                                      hexed_steam_id[8:12],
100
                                      hexed_steam_id[12:16],
101
                                      hexed_steam_id[16:20],
102
                                      hexed_steam_id[20:32]])
103
104
    def check_required_variables(self) -> None:
105
        """check all required variables for the full usage
106
107
        :return:
108
        """
109
        if not self._user:
110
            raise NoLoginIdProvidedException('Please provide a username for the login')
111
112
        if not self._password:
113
            raise NoLoginPassProvidedException('Please provide a password for the login')
114
115
        if not self._id64:
116
            raise NoSteamID64ProvidedException('Please provide your SteamID64')
117
118
        if not self._api_key:
119
            raise NoAPIKeyProvidedException('Please provide an API key via .env or as argument')
120
121
        if not self._secret:
122
            raise No2FASecretProvidedException('Please provide a secret to generate One Time Passwords')
123
124
        if not self._identity:
125
            raise NoIdentitySecretKeyProvidedException('Please provide the identity secret via .env or as argument')
126