PK!MVVphpipam_client/__init__.pyfrom phpipam_client.client import ( PhpIpamClient, GET, POST, PATCH, DELETE, )PK!gP  phpipam_client/client.py# -*- coding: utf-8 -*- from __future__ import absolute_import import json import base64 import requests from requests.auth import HTTPBasicAuth from . import rijndael GET = requests.get POST = requests.post PATCH = requests.patch DELETE = requests.delete class PhpIpamException(Exception): def __init__(self, *args, **kwargs): Exception.__init__(self, *args, **kwargs) class PhpIpamClient(object): def __init__(self, url, app_id, username=None, password=None, token=None, encryption=False, timeout=None, ssl_verify=True, user_agent=None): self._api_url = url self._api_appid = app_id self._api_username = username self._api_password = password self._api_token = token self._api_encryption = encryption self._api_timeout = timeout self._api_ssl_verify = ssl_verify self._api_headers = { 'content-type': 'application/json', } if user_agent: self._api_headers['user-agent'] = user_agent if not self._api_encryption: self.login() def query(self, path, method=GET, data=None, auth=None): _params = {} _data = None if self._api_token: self._api_headers['token'] = self._api_token if self._api_encryption: _url = '{}/api/'.format(self._api_url) _enc_data = {} for index, value in enumerate(list(filter(None, path.split('/')))): if index == 0: _enc_data['controller'] = value elif index == 1: _enc_data['id'] = value else: _enc_data['id{}'.format(index)] = value if data is not None: _enc_data.update(data) _params = { 'app_id': self._api_appid, 'enc_request': base64.b64encode(rijndael.encrypt(self._api_token, json.dumps(_enc_data))) } else: _url = '{}/api/{}{}'.format(self._api_url, self._api_appid, path) if data is not None: _data = json.dumps(data) resp = method( _url, params=_params, data=_data, headers=self._api_headers, auth=auth, verify=self._api_ssl_verify, timeout=self._api_timeout, ) result = resp.json() if resp.status_code not in (200, 201): raise PhpIpamException(resp.text) if not result['success']: raise PhpIpamException(resp.text) if 'data' in result: return result['data'] def login(self): resp = self.query('/user/', method=POST, auth=HTTPBasicAuth(self._api_username, self._api_password), ) self._api_token = resp['token'] return resp def token_check(self): try: return self.query('/user/') except: return self.login() def token_extend(self): return self.query('/user/') PK!;,,phpipam_client/rijndael.py# -*- coding: utf-8 -*- """ A pure python (slow) implementation of rijndael with a decent interface To include - from rijndael import rijndael To do a key setup - r = rijndael(key, block_size = 16) key must be a string of length 16, 24, or 32 blocksize must be 16, 24, or 32. Default is 16 To use - ciphertext = r.encrypt(plaintext) plaintext = r.decrypt(ciphertext) If any strings are of the wrong length a ValueError is thrown """ # ported from the Java reference code by Bram Cohen, April 2001 # this code is public domain, unless someone makes # an intellectual property claim against the reference # code, in which case it can be made public domain by # deleting all the comments and renaming all the variables import copy import string shifts = [[[0, 0], [1, 3], [2, 2], [3, 1]], [[0, 0], [1, 5], [2, 4], [3, 3]], [[0, 0], [1, 7], [3, 5], [4, 4]]] # [keysize][block_size] num_rounds = { 16: {16: 10, 24: 12, 32: 14}, 24: {16: 12, 24: 12, 32: 14}, 32: {16: 14, 24: 14, 32: 14}} A = [[1, 1, 1, 1, 1, 0, 0, 0], [0, 1, 1, 1, 1, 1, 0, 0], [0, 0, 1, 1, 1, 1, 1, 0], [0, 0, 0, 1, 1, 1, 1, 1], [1, 0, 0, 0, 1, 1, 1, 1], [1, 1, 0, 0, 0, 1, 1, 1], [1, 1, 1, 0, 0, 0, 1, 1], [1, 1, 1, 1, 0, 0, 0, 1]] # produce log and alog tables, needed for multiplying in the # field GF(2^m) (generator = 3) alog = [1] for i in range(255): j = (alog[-1] << 1) ^ alog[-1] if j & 0x100 != 0: j ^= 0x11B alog.append(j) log = [0] * 256 for i in range(1, 255): log[alog[i]] = i # multiply two elements of GF(2^m) def mul(a, b): if a == 0 or b == 0: return 0 return alog[(log[a & 0xFF] + log[b & 0xFF]) % 255] # substitution box based on F^{-1}(x) box = [[0] * 8 for i in range(256)] box[1][7] = 1 for i in range(2, 256): j = alog[255 - log[i]] for t in range(8): box[i][t] = (j >> (7 - t)) & 0x01 B = [0, 1, 1, 0, 0, 0, 1, 1] # affine transform: box[i] <- B + A*box[i] cox = [[0] * 8 for i in range(256)] for i in range(256): for t in range(8): cox[i][t] = B[t] for j in range(8): cox[i][t] ^= A[t][j] * box[i][j] # S-boxes and inverse S-boxes S = [0] * 256 Si = [0] * 256 for i in range(256): S[i] = cox[i][0] << 7 for t in range(1, 8): S[i] ^= cox[i][t] << (7-t) Si[S[i] & 0xFF] = i # T-boxes G = [[2, 1, 1, 3], [3, 2, 1, 1], [1, 3, 2, 1], [1, 1, 3, 2]] AA = [[0] * 8 for i in range(4)] for i in range(4): for j in range(4): AA[i][j] = G[i][j] AA[i][i+4] = 1 for i in range(4): pivot = AA[i][i] if pivot == 0: t = i + 1 while AA[t][i] == 0 and t < 4: t += 1 assert t != 4, 'G matrix must be invertible' for j in range(8): AA[i][j], AA[t][j] = AA[t][j], AA[i][j] pivot = AA[i][i] for j in range(8): if AA[i][j] != 0: AA[i][j] = alog[(255 + log[AA[i][j] & 0xFF] - log[pivot & 0xFF]) % 255] for t in range(4): if i != t: for j in range(i+1, 8): AA[t][j] ^= mul(AA[i][j], AA[t][i]) AA[t][i] = 0 iG = [[0] * 4 for i in range(4)] for i in range(4): for j in range(4): iG[i][j] = AA[i][j + 4] def mul4(a, bs): if a == 0: return 0 r = 0 for b in bs: r <<= 8 if b != 0: r = r | mul(a, b) return r T1 = [] T2 = [] T3 = [] T4 = [] T5 = [] T6 = [] T7 = [] T8 = [] U1 = [] U2 = [] U3 = [] U4 = [] for t in range(256): s = S[t] T1.append(mul4(s, G[0])) T2.append(mul4(s, G[1])) T3.append(mul4(s, G[2])) T4.append(mul4(s, G[3])) s = Si[t] T5.append(mul4(s, iG[0])) T6.append(mul4(s, iG[1])) T7.append(mul4(s, iG[2])) T8.append(mul4(s, iG[3])) U1.append(mul4(t, iG[0])) U2.append(mul4(t, iG[1])) U3.append(mul4(t, iG[2])) U4.append(mul4(t, iG[3])) # round constants rcon = [1] r = 1 for t in range(1, 30): r = mul(2, r) rcon.append(r) del A del AA del pivot del B del G del box del log del alog del i del j del r del s del t del mul del mul4 del cox del iG class rijndael: def __init__(self, key, block_size = 16): if block_size != 16 and block_size != 24 and block_size != 32: raise ValueError('Invalid block size: ' + str(block_size)) if len(key) != 16 and len(key) != 24 and len(key) != 32: raise ValueError('Invalid key size: ' + str(len(key))) self.block_size = block_size ROUNDS = num_rounds[len(key)][block_size] BC = block_size // 4 # encryption round keys Ke = [[0] * BC for i in range(ROUNDS + 1)] # decryption round keys Kd = [[0] * BC for i in range(ROUNDS + 1)] ROUND_KEY_COUNT = (ROUNDS + 1) * BC KC = len(key) // 4 # copy user material bytes into temporary ints tk = [] for i in range(0, KC): tk.append((ord(key[i * 4]) << 24) | (ord(key[i * 4 + 1]) << 16) | (ord(key[i * 4 + 2]) << 8) | ord(key[i * 4 + 3])) # copy values into round key arrays t = 0 j = 0 while j < KC and t < ROUND_KEY_COUNT: Ke[t // BC][t % BC] = tk[j] Kd[ROUNDS - (t // BC)][t % BC] = tk[j] j += 1 t += 1 tt = 0 rconpointer = 0 while t < ROUND_KEY_COUNT: # extrapolate using phi (the round key evolution function) tt = tk[KC - 1] tk[0] ^= (S[(tt >> 16) & 0xFF] & 0xFF) << 24 ^ \ (S[(tt >> 8) & 0xFF] & 0xFF) << 16 ^ \ (S[ tt & 0xFF] & 0xFF) << 8 ^ \ (S[(tt >> 24) & 0xFF] & 0xFF) ^ \ (rcon[rconpointer] & 0xFF) << 24 rconpointer += 1 if KC != 8: for i in range(1, KC): tk[i] ^= tk[i-1] else: for i in range(1, KC // 2): tk[i] ^= tk[i-1] tt = tk[KC // 2 - 1] tk[KC // 2] ^= (S[ tt & 0xFF] & 0xFF) ^ \ (S[(tt >> 8) & 0xFF] & 0xFF) << 8 ^ \ (S[(tt >> 16) & 0xFF] & 0xFF) << 16 ^ \ (S[(tt >> 24) & 0xFF] & 0xFF) << 24 for i in range(KC // 2 + 1, KC): tk[i] ^= tk[i-1] # copy values into round key arrays j = 0 while j < KC and t < ROUND_KEY_COUNT: Ke[t // BC][t % BC] = tk[j] Kd[ROUNDS - (t // BC)][t % BC] = tk[j] j += 1 t += 1 # inverse MixColumn where needed for r in range(1, ROUNDS): for j in range(BC): tt = Kd[r][j] Kd[r][j] = U1[(tt >> 24) & 0xFF] ^ \ U2[(tt >> 16) & 0xFF] ^ \ U3[(tt >> 8) & 0xFF] ^ \ U4[ tt & 0xFF] self.Ke = Ke self.Kd = Kd def encrypt(self, plaintext): if len(plaintext) != self.block_size: raise ValueError('wrong block length, expected ' + str(self.block_size) + ' got ' + str(len(plaintext))) Ke = self.Ke BC = self.block_size // 4 ROUNDS = len(Ke) - 1 if BC == 4: SC = 0 elif BC == 6: SC = 1 else: SC = 2 s1 = shifts[SC][1][0] s2 = shifts[SC][2][0] s3 = shifts[SC][3][0] a = [0] * BC # temporary work array t = [] # plaintext to ints + key for i in range(BC): t.append((ord(plaintext[i * 4 ]) << 24 | ord(plaintext[i * 4 + 1]) << 16 | ord(plaintext[i * 4 + 2]) << 8 | ord(plaintext[i * 4 + 3]) ) ^ Ke[0][i]) # apply round transforms for r in range(1, ROUNDS): for i in range(BC): a[i] = (T1[(t[ i ] >> 24) & 0xFF] ^ T2[(t[(i + s1) % BC] >> 16) & 0xFF] ^ T3[(t[(i + s2) % BC] >> 8) & 0xFF] ^ T4[ t[(i + s3) % BC] & 0xFF] ) ^ Ke[r][i] t = copy.copy(a) # last round is special result = [] for i in range(BC): tt = Ke[ROUNDS][i] result.append((S[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) result.append((S[(t[(i + s1) % BC] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) result.append((S[(t[(i + s2) % BC] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) result.append((S[ t[(i + s3) % BC] & 0xFF] ^ tt ) & 0xFF) return bytes(bytearray(result)) def decrypt(self, ciphertext): if len(ciphertext) != self.block_size: raise ValueError('wrong block length, expected ' + str(self.block_size) + ' got ' + str(len(ciphertext))) ciphertext = bytearray(ciphertext) Kd = self.Kd BC = self.block_size // 4 ROUNDS = len(Kd) - 1 if BC == 4: SC = 0 elif BC == 6: SC = 1 else: SC = 2 s1 = shifts[SC][1][1] s2 = shifts[SC][2][1] s3 = shifts[SC][3][1] a = [0] * BC # temporary work array t = [0] * BC # ciphertext to ints + key for i in range(BC): t[i] = (ciphertext[i * 4 ] << 24 | ciphertext[i * 4 + 1] << 16 | ciphertext[i * 4 + 2] << 8 | ciphertext[i * 4 + 3] ) ^ Kd[0][i] # apply round transforms for r in range(1, ROUNDS): for i in range(BC): a[i] = (T5[(t[ i ] >> 24) & 0xFF] ^ T6[(t[(i + s1) % BC] >> 16) & 0xFF] ^ T7[(t[(i + s2) % BC] >> 8) & 0xFF] ^ T8[ t[(i + s3) % BC] & 0xFF] ) ^ Kd[r][i] t = copy.copy(a) # last round is special result = [] for i in range(BC): tt = Kd[ROUNDS][i] result.append((Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) result.append((Si[(t[(i + s1) % BC] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) result.append((Si[(t[(i + s2) % BC] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) result.append((Si[ t[(i + s3) % BC] & 0xFF] ^ tt ) & 0xFF) return bytes(bytearray(result)) def encrypt(key, plaintext, key_size=16, block_size=32): padded_key = key.ljust(key_size, '\0') padded_text = plaintext + (block_size - len(plaintext) % block_size) * '\0' r = rijndael(padded_key, block_size) ciphertext = b'' for start in range(0, len(padded_text), block_size): ciphertext += r.encrypt(padded_text[start:start+block_size]) return ciphertext def decrypt(key, ciphertext, key_size=16, block_size=32): padded_key = key.ljust(key_size, '\0') r = rijndael(padded_key, block_size) padded_text = b'' for start in range(0, len(ciphertext), block_size): padded_text += r.decrypt(ciphertext[start:start+block_size]) plaintext = padded_text.decode().split('\x00', 1)[0] return plaintext PK!55&phpipam_client-0.2.0.dist-info/LICENSEMIT License Copyright (c) 2019 Alexandr Dzhurinskij Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK!H|n-WY$phpipam_client-0.2.0.dist-info/WHEEL A н#Z;/" bFF]xzwK;<*mTֻ0*Ri.4Vm0[H, JPK!H{'phpipam_client-0.2.0.dist-info/METADATATQO0~8ԇqhن:D5(M`&g;@7&Ti}ww}˘c7nP2!)x B*LK#:;䬮*f1$$Cpp,q26PU<,ǂӯӓSr$R.-Ǔטfb6Bڹ].beOh*rp b8ҽ>lvn*]MKf,UAZu3:WRrêJk0cwS>}qƻ3oば.: ]?#ܦFhV tp&\g^[ɢp昫n᜶qiPZ(V׉l!xQ{`bf؝@T*D닻Z٦!& Zl̒9~o(Ӂ/%')_?ruu rcTe=Dgx"/v 6(x<4.gZ_lT XmxTK\SiثI%-%ZA:9<%l(PpqCz~cP׷p\8%phpipam_client-0.2.0.dist-info/RECORDλr@@>߲aa@d4V;s}eǖlz2e~c Bgfyc)UUqgլIUO_+@yo V(^. , CӫtZ"/_A5(ݖnNto[s1GGR? a/ :Ne8BHa* 7xMĹ>]&9hX MI,98u-i^ZC|GV&㡧V;$6KzD( -Ҿ[{wX1]RȢ7iT |HCOPK!MVVphpipam_client/__init__.pyPK!gP  phpipam_client/client.pyPK!;,, phpipam_client/rijndael.pyPK!55&9phpipam_client-0.2.0.dist-info/LICENSEPK!H|n-WY$>phpipam_client-0.2.0.dist-info/WHEELPK!H{'>phpipam_client-0.2.0.dist-info/METADATAPK!HR>p\8%Aphpipam_client-0.2.0.dist-info/RECORDPK$wC