def save_pem(contents, pem_marker): '''Saves a PEM file. @param contents: the contents to encode in PEM format @param pem_marker: the marker of the PEM content, such as 'RSA PRIVATE KEY' when your file has '-----BEGIN RSA PRIVATE KEY-----' and '-----END RSA PRIVATE KEY-----' markers. @return the base64-encoded content between the start and end markers. ''' (pem_start, pem_end) = _markers(pem_marker) b64 = base64.encodestring(contents).replace(b('\n'), b('')) pem_lines = [pem_start] for block_start in range(0, len(b64), 64): block = b64[block_start:block_start + 64] pem_lines.append(block) pem_lines.append(pem_end) pem_lines.append(b('')) return b('\n').join(pem_lines)
def virtual_memory(): total, free, buffers, shared, _, _ = cext.linux_sysinfo() cached = active = inactive = None f = open('/proc/meminfo', 'rb') CACHED, ACTIVE, INACTIVE = b("Cached:"), b("Active:"), b("Inactive:") try: for line in f: if line.startswith(CACHED): cached = int(line.split()[1]) * 1024 elif line.startswith(ACTIVE): active = int(line.split()[1]) * 1024 elif line.startswith(INACTIVE): inactive = int(line.split()[1]) * 1024 if (cached is not None and active is not None and inactive is not None): break else: # we might get here when dealing with exotic Linux flavors, see: # https://github.com/giampaolo/psutil/issues/313 msg = "'cached', 'active' and 'inactive' memory stats couldn't " \ "be determined and were set to 0" warnings.warn(msg, RuntimeWarning) cached = active = inactive = 0 finally: f.close() avail = free + buffers + cached used = total - free percent = usage_percent((total - avail), total, _round=1) return svmem(total, avail, percent, used, free, active, inactive, buffers, cached)
def load_pem(contents, pem_marker): '''Loads a PEM file. @param contents: the contents of the file to interpret @param pem_marker: the marker of the PEM content, such as 'RSA PRIVATE KEY' when your file has '-----BEGIN RSA PRIVATE KEY-----' and '-----END RSA PRIVATE KEY-----' markers. @return the base64-decoded content between the start and end markers. @raise ValueError: when the content is invalid, for example when the start marker cannot be found. ''' (pem_start, pem_end) = _markers(pem_marker) pem_lines = [] in_pem_part = False for line in contents.splitlines(): line = line.strip() # Skip empty lines if not line: continue # Handle start marker if line == pem_start: if in_pem_part: raise ValueError('Seen start marker "%s" twice' % pem_start) in_pem_part = True continue # Skip stuff before first marker if not in_pem_part: continue # Handle end marker if in_pem_part and line == pem_end: in_pem_part = False break # Load fields if b(':') in line: continue pem_lines.append(line) # Do some sanity checks if not pem_lines: raise ValueError('No PEM start marker "%s" found' % pem_start) if in_pem_part: raise ValueError('No PEM end marker "%s" found' % pem_end) # Base64-decode the contents pem = b('').join(pem_lines) return base64.decodestring(pem)
def threads(self): thread_ids = os.listdir("/proc/%s/task" % self.pid) thread_ids.sort() retlist = [] hit_enoent = False for thread_id in thread_ids: try: f = open("/proc/%s/task/%s/stat" % (self.pid, thread_id), 'rb') except EnvironmentError: err = sys.exc_info()[1] if err.errno == errno.ENOENT: # no such file or directory; it means thread # disappeared on us hit_enoent = True continue raise try: st = f.read().strip() finally: f.close() # ignore the first two values ("pid (exe)") st = st[st.find(b(')')) + 2:] values = st.split(b(' ')) utime = float(values[11]) / CLOCK_TICKS stime = float(values[12]) / CLOCK_TICKS ntuple = _common.pthread(int(thread_id), utime, stime) retlist.append(ntuple) if hit_enoent: # raise NSP if the process disappeared on us os.stat('/proc/%s' % self.pid) return retlist
def swap_memory(): _, _, _, _, total, free = cext.linux_sysinfo() used = total - free percent = usage_percent(used, total, _round=1) # get pgin/pgouts f = open("/proc/vmstat", "rb") SIN, SOUT = b('pswpin'), b('pswpout') sin = sout = None try: for line in f: # values are expressed in 4 kilo bytes, we want bytes instead if line.startswith(SIN): sin = int(line.split(b(' '))[1]) * 4 * 1024 elif line.startswith(SOUT): sout = int(line.split(b(' '))[1]) * 4 * 1024 if sin is not None and sout is not None: break else: # we might get here when dealing with exotic Linux flavors, see: # https://github.com/giampaolo/psutil/issues/313 msg = "'sin' and 'sout' swap memory stats couldn't " \ "be determined and were set to 0" warnings.warn(msg, RuntimeWarning) sin = sout = 0 finally: f.close() return _common.sswap(total, used, free, percent, sin, sout)
def _pad_for_signing(message, target_length): r'''Pads the message for signing, returning the padded message. The padding is always a repetition of FF bytes. :return: 00 01 PADDING 00 MESSAGE >>> block = _pad_for_signing('hello', 16) >>> len(block) 16 >>> block[0:2] '\x00\x01' >>> block[-6:] '\x00hello' >>> block[2:-6] '\xff\xff\xff\xff\xff\xff\xff\xff' ''' max_msglength = target_length - 11 msglength = len(message) if msglength > max_msglength: raise OverflowError('%i bytes needed for message, but there is only' ' space for %i' % (msglength, max_msglength)) padding_length = target_length - msglength - 3 return b('').join([b('\x00\x01'), padding_length * b('\xff'), b('\x00'), message])
def save_pem(contents, pem_marker): """Saves a PEM file. @param contents: the contents to encode in PEM format @param pem_marker: the marker of the PEM content, such as 'RSA PRIVATE KEY' when your file has '-----BEGIN RSA PRIVATE KEY-----' and '-----END RSA PRIVATE KEY-----' markers. @return the base64-encoded content between the start and end markers. """ (pem_start, pem_end) = _markers(pem_marker) b64 = base64.encodestring(contents).replace(b("\n"), b("")) pem_lines = [pem_start] for block_start in range(0, len(b64), 64): block = b64[block_start : block_start + 64] pem_lines.append(block) pem_lines.append(pem_end) pem_lines.append(b("")) return b("\n").join(pem_lines)
def load_pem(contents, pem_marker): """Loads a PEM file. @param contents: the contents of the file to interpret @param pem_marker: the marker of the PEM content, such as 'RSA PRIVATE KEY' when your file has '-----BEGIN RSA PRIVATE KEY-----' and '-----END RSA PRIVATE KEY-----' markers. @return the base64-decoded content between the start and end markers. @raise ValueError: when the content is invalid, for example when the start marker cannot be found. """ (pem_start, pem_end) = _markers(pem_marker) pem_lines = [] in_pem_part = False for line in contents.splitlines(): line = line.strip() # Skip empty lines if not line: continue # Handle start marker if line == pem_start: if in_pem_part: raise ValueError('Seen start marker "%s" twice' % pem_start) in_pem_part = True continue # Skip stuff before first marker if not in_pem_part: continue # Handle end marker if in_pem_part and line == pem_end: in_pem_part = False break # Load fields if b(":") in line: continue pem_lines.append(line) # Do some sanity checks if not pem_lines: raise ValueError('No PEM start marker "%s" found' % pem_start) if in_pem_part: raise ValueError('No PEM end marker "%s" found' % pem_end) # Base64-decode the contents pem = b("").join(pem_lines) return base64.decodestring(pem)
def pack_command(self, *args): "Pack a series of arguments into the Redis protocol" output = [] # the client might have included 1 or more literal arguments in # the command name, e.g., 'CONFIG GET'. The Redis server expects these # arguments to be sent separately, so split the first argument # manually. All of these arguements get wrapped in the Token class # to prevent them from being encoded. command = args[0] if ' ' in command: args = tuple([Token.get_token(s) for s in command.split()]) + args[1:] else: args = (Token.get_token(command), ) + args[1:] buff = SYM_EMPTY.join((SYM_STAR, b(str(len(args))), SYM_CRLF)) for arg in imap(self.encoder.encode, args): # to avoid large string mallocs, chunk the command into the # output list if we're sending large values if len(buff) > 6000 or len(arg) > 6000: buff = SYM_EMPTY.join( (buff, SYM_DOLLAR, b(str(len(arg))), SYM_CRLF)) output.append(buff) output.append(arg) buff = SYM_CRLF else: buff = SYM_EMPTY.join((buff, SYM_DOLLAR, b(str(len(arg))), SYM_CRLF, arg, SYM_CRLF)) output.append(buff) return output
def pack_command(self, *args): "Pack a series of arguments into a value Redis command" args_output = SYM_EMPTY.join([ SYM_EMPTY.join((SYM_DOLLAR, b(str(len(k))), SYM_CRLF, k, SYM_CRLF)) for k in imap(self.encode, args) ]) output = SYM_EMPTY.join( (SYM_STAR, b(str(len(args))), SYM_CRLF, args_output)) return output
def _markers(pem_marker): """ Returns the start and end PEM markers """ if is_bytes(pem_marker): pem_marker = pem_marker.decode("utf-8") return (b("-----BEGIN %s-----" % pem_marker), b("-----END %s-----" % pem_marker))
def recv(self,buffer_size): try: data=self.socket.recv(buffer_size) except socket.error: why=sys.exc_info()[1] if why.args[0] in _DISCONNECTED: self.handle_close(); return b('') else: raise else: if not data:self.handle_close(); return b('') # a closed connection is indicated by signaling # a read condition, and having recv() return 0. else: return data
def _markers(pem_marker): ''' Returns the start and end PEM markers ''' if is_bytes(pem_marker): pem_marker = pem_marker.decode('utf-8') return (b('-----BEGIN %s-----' % pem_marker), b('-----END %s-----' % pem_marker))
def cpu_times(self): f = open("/proc/%s/stat" % self.pid, 'rb') try: st = f.read().strip() finally: f.close() # ignore the first two values ("pid (exe)") st = st[st.find(b(')')) + 2:] values = st.split(b(' ')) utime = float(values[11]) / CLOCK_TICKS stime = float(values[12]) / CLOCK_TICKS return _common.pcputimes(utime, stime)
def acquire(self, blocking=None, blocking_timeout=None): """ Use Redis to hold a shared, distributed lock named ``name``. Returns True once the lock is acquired. If ``blocking`` is False, always return immediately. If the lock was acquired, return True, otherwise return False. ``blocking_timeout`` specifies the maximum number of seconds to wait trying to acquire the lock. """ sleep = self.sleep token = b(uuid.uuid1().hex) if blocking is None: blocking = self.blocking if blocking_timeout is None: blocking_timeout = self.blocking_timeout stop_trying_at = None if blocking_timeout is not None: stop_trying_at = mod_time.time() + blocking_timeout while 1: if self.do_acquire(token): self.local.token = token return True if not blocking: return False if stop_trying_at is not None and mod_time.time() > stop_trying_at: return False mod_time.sleep(sleep)
def create_time(self): f = open("/proc/%s/stat" % self.pid, 'rb') try: st = f.read().strip() finally: f.close() # ignore the first two values ("pid (exe)") st = st[st.rfind(b(')')) + 2:] values = st.split(b(' ')) # According to documentation, starttime is in field 21 and the # unit is jiffies (clock ticks). # We first divide it for clock ticks and then add uptime returning # seconds since the epoch, in UTC. # Also use cached value if available. bt = BOOT_TIME or boot_time() return (float(values[19]) / CLOCK_TICKS) + bt
def encode(self, value): "Return a bytestring representation of the value" if isinstance(value, Token): return value.encoded_value elif isinstance(value, bytes): return value elif isinstance(value, (int, long)): value = b(str(value)) elif isinstance(value, float): value = b(repr(value)) elif not isinstance(value, basestring): # an object we don't know how to deal with. default to unicode() value = unicode(value) if isinstance(value, unicode): value = value.encode(self.encoding, self.encoding_errors) return value
def _save_pkcs1_pem(self): '''Saves a PKCS#1 PEM-encoded private key file. @return: contents of a PEM-encoded file that contains the private key. ''' der = self._save_pkcs1_der() return pem.save_pem(der, b('RSA PRIVATE KEY'))
def verify(message, signature, pub_key): '''Verifies that the signature matches the message. The hash method is detected automatically from the signature. :param message: the signed message. Can be an 8-bit string or a file-like object. If ``message`` has a ``read()`` method, it is assumed to be a file-like object. :param signature: the signature block, as created with :py:func:`rsa.sign`. :param pub_key: the :py:class:`rsa.PublicKey` of the person signing the message. :raise VerificationError: when the signature doesn't match the message. .. warning:: Never display the stack trace of a :py:class:`rsa.pkcs1.VerificationError` exception. It shows where in the code the exception occurred, and thus leaks information about the key. It's only a tiny bit of information, but every bit makes cracking the keys easier. ''' blocksize = common.byte_size(pub_key.n) encrypted = transform.bytes2int(signature) decrypted = core.decrypt_int(encrypted, pub_key.e, pub_key.n) clearsig = transform.int2bytes(decrypted, blocksize) # If we can't find the signature marker, verification failed. if clearsig[0:2] != b('\x00\x01'): raise VerificationError('Verification failed') # Find the 00 separator between the padding and the payload try: sep_idx = clearsig.index(b('\x00'), 2) except ValueError: raise VerificationError('Verification failed') # Get the hash and the hash method (method_name, signature_hash) = _find_method_hash(clearsig[sep_idx+1:]) message_hash = _hash(message, method_name) # Compare the real hash to the hash in the signature if message_hash != signature_hash: raise VerificationError('Verification failed') return True
def num_ctx_switches(self): vol = unvol = None f = open("/proc/%s/status" % self.pid, "rb") VOLUNTARY = b("voluntary_ctxt_switches") NON_VOLUNTARY = b("nonvoluntary_ctxt_switches") try: for line in f: if line.startswith(VOLUNTARY): vol = int(line.split()[1]) elif line.startswith(NON_VOLUNTARY): unvol = int(line.split()[1]) if vol is not None and unvol is not None: return _common.pctxsw(vol, unvol) raise NotImplementedError( "'voluntary_ctxt_switches' and 'nonvoluntary_ctxt_switches'" "fields were not found in /proc/%s/status; the kernel is " "probably older than 2.6.23" % self.pid) finally: f.close()
def num_threads(self): f = open("/proc/%s/status" % self.pid, "rb") try: THREADS = b("Threads:") for line in f: if line.startswith(THREADS): return int(line.split()[1]) raise NotImplementedError("line not found") finally: f.close()
def ppid(self): f = open("/proc/%s/status" % self.pid, 'rb') try: PPID = b("PPid:") for line in f: if line.startswith(PPID): # PPid: nnnn return int(line.split()[1]) raise NotImplementedError("line not found") finally: f.close()
def gids(self): f = open("/proc/%s/status" % self.pid, 'rb') try: GID = b('Gid:') for line in f: if line.startswith(GID): _, real, effective, saved, fs = line.split() return _common.pgids(int(real), int(effective), int(saved)) raise NotImplementedError("line not found") finally: f.close()
def terminal(self): tmap = _psposix._get_terminal_map() f = open("/proc/%s/stat" % self.pid, 'rb') try: tty_nr = int(f.read().split(b(' '))[6]) finally: f.close() try: return tmap[tty_nr] except KeyError: return None
def _load_pkcs1_pem(cls, keyfile): '''Loads a PKCS#1 PEM-encoded private key file. The contents of the file before the "-----BEGIN RSA PRIVATE KEY-----" and after the "-----END RSA PRIVATE KEY-----" lines is ignored. @param keyfile: contents of a PEM-encoded file that contains the private key. @return: a PrivateKey object ''' der = pem.load_pem(keyfile, b('RSA PRIVATE KEY')) return cls._load_pkcs1_der(der)
def boot_time(): """Return the system boot time expressed in seconds since the epoch.""" global BOOT_TIME f = open('/proc/stat', 'rb') try: BTIME = b('btime') for line in f: if line.startswith(BTIME): ret = float(line.strip().split()[1]) BOOT_TIME = ret return ret raise RuntimeError("line 'btime' not found") finally: f.close()
def io_counters(self): fname = "/proc/%s/io" % self.pid f = open(fname, 'rb') SYSCR, SYSCW = b("syscr"), b("syscw") READ_BYTES, WRITE_BYTES = b("read_bytes"), b("write_bytes") try: rcount = wcount = rbytes = wbytes = None for line in f: if rcount is None and line.startswith(SYSCR): rcount = int(line.split()[1]) elif wcount is None and line.startswith(SYSCW): wcount = int(line.split()[1]) elif rbytes is None and line.startswith(READ_BYTES): rbytes = int(line.split()[1]) elif wbytes is None and line.startswith(WRITE_BYTES): wbytes = int(line.split()[1]) for x in (rcount, wcount, rbytes, wbytes): if x is None: raise NotImplementedError( "couldn't read all necessary info from %r" % fname) return _common.pio(rcount, wcount, rbytes, wbytes) finally: f.close()
def status(self): f = open("/proc/%s/status" % self.pid, 'rb') try: STATE = b("State:") for line in f: if line.startswith(STATE): letter = line.split()[1] if PY3: letter = letter.decode() # XXX is '?' legit? (we're not supposed to return # it anyway) return PROC_STATUSES.get(letter, '?') finally: f.close()
def cpu_count_physical(): """Return the number of physical CPUs in the system.""" f = open('/proc/cpuinfo', 'rb') try: lines = f.readlines() finally: f.close() found = set() PHYSICAL_ID = b('physical id') for line in lines: if line.lower().startswith(PHYSICAL_ID): found.add(line.strip()) if found: return len(found) else: return None # mimic os.cpu_count()
def per_cpu_times(): """Return a list of namedtuple representing the CPU times for every CPU available on the system. """ cpus = [] f = open('/proc/stat', 'rb') try: # get rid of the first line which refers to system wide CPU stats f.readline() CPU = b('cpu') for line in f: if line.startswith(CPU): values = line.split() fields = values[1:len(scputimes._fields) + 1] fields = [float(x) / CLOCK_TICKS for x in fields] entry = scputimes(*fields) cpus.append(entry) return cpus finally: f.close()
def _pad_for_encryption(message, target_length): r'''Pads the message for encryption, returning the padded message. :return: 00 02 RANDOM_DATA 00 MESSAGE >>> block = _pad_for_encryption('hello', 16) >>> len(block) 16 >>> block[0:2] '\x00\x02' >>> block[-6:] '\x00hello' ''' max_msglength = target_length - 11 msglength = len(message) if msglength > max_msglength: raise OverflowError('%i bytes needed for message, but there is only' ' space for %i' % (msglength, max_msglength)) # Get random padding padding = b('') padding_length = target_length - msglength - 3 # We remove 0-bytes, so we'll end up with less padding than we've asked for, # so keep adding data until we're at the correct length. while len(padding) < padding_length: needed_bytes = padding_length - len(padding) # Always read at least 8 bytes more than we need, and trim off the rest # after removing the 0-bytes. This increases the chance of getting # enough bytes, especially when needed_bytes is small new_padding = os.urandom(needed_bytes + 5) new_padding = new_padding.replace(b('\x00'), b('')) padding = padding + new_padding[:needed_bytes] assert len(padding) == padding_length return b('').join([b('\x00\x02'), padding, b('\x00'), message])
def cpu_count_logical(): """Return the number of logical CPUs in the system.""" try: return os.sysconf("SC_NPROCESSORS_ONLN") except ValueError: # as a second fallback we try to parse /proc/cpuinfo num = 0 f = open('/proc/cpuinfo', 'rb') try: lines = f.readlines() finally: f.close() PROCESSOR = b('processor') for line in lines: if line.lower().startswith(PROCESSOR): num += 1 # unknown format (e.g. amrel/sparc architectures), see: # https://github.com/giampaolo/psutil/issues/200 # try to parse /proc/stat as a last resort if num == 0: f = open('/proc/stat', 'rt') try: lines = f.readlines() finally: f.close() search = re.compile('cpu\d') for line in lines: line = line.split(' ')[0] if search.match(line): num += 1 if num == 0: # mimic os.cpu_count() return None return num
def decrypt(crypto, priv_key): r'''Decrypts the given message using PKCS#1 v1.5 The decryption is considered 'failed' when the resulting cleartext doesn't start with the bytes 00 02, or when the 00 byte between the padding and the message cannot be found. :param crypto: the crypto text as returned by :py:func:`rsa.encrypt` :param priv_key: the :py:class:`rsa.PrivateKey` to decrypt with. :raise DecryptionError: when the decryption fails. No details are given as to why the code thinks the decryption fails, as this would leak information about the private key. >>> import rsa >>> (pub_key, priv_key) = rsa.newkeys(256) It works with strings: >>> crypto = encrypt('hello', pub_key) >>> decrypt(crypto, priv_key) 'hello' And with binary data: >>> crypto = encrypt('\x00\x00\x00\x00\x01', pub_key) >>> decrypt(crypto, priv_key) '\x00\x00\x00\x00\x01' Altering the encrypted information will *likely* cause a :py:class:`rsa.pkcs1.DecryptionError`. If you want to be *sure*, use :py:func:`rsa.sign`. .. warning:: Never display the stack trace of a :py:class:`rsa.pkcs1.DecryptionError` exception. It shows where in the code the exception occurred, and thus leaks information about the key. It's only a tiny bit of information, but every bit makes cracking the keys easier. >>> crypto = encrypt('hello', pub_key) >>> crypto = crypto[0:5] + 'X' + crypto[6:] # change a byte >>> decrypt(crypto, priv_key) Traceback (most recent call last): ... DecryptionError: Decryption failed ''' blocksize = common.byte_size(priv_key.n) encrypted = transform.bytes2int(crypto) decrypted = core.decrypt_int(encrypted, priv_key.d, priv_key.n) cleartext = transform.int2bytes(decrypted, blocksize) # If we can't find the cleartext marker, decryption failed. if cleartext[0:2] != b('\x00\x02'): raise DecryptionError('Decryption failed') # Find the 00 separator between the padding and the message try: sep_idx = cleartext.index(b('\x00'), 2) except ValueError: raise DecryptionError('Decryption failed') return cleartext[sep_idx+1:]
LENGTH := varint-encoded length of the subsequent data. Varint comes from Google Protobuf, and encodes an integer into a variable number of bytes. Each byte uses the 7 lowest bits to encode the value. The highest bit set to 1 indicates the next byte is also part of the varint. The last byte will have this bit set to 0. This file format is called the VARBLOCK format, in line with the varint format used to denote the block sizes. ''' from _compat import byte, b ZERO_BYTE = b('\x00') VARBLOCK_VERSION = 1 def read_varint(infile): '''Reads a varint from the file. When the first byte to be read indicates EOF, (0, 0) is returned. When an EOF occurs when at least one byte has been read, an EOFError exception is raised. @param infile: the file-like object to read from. It should have a read() method. @returns (varint, length), the read varint and the number of read bytes. ''' varint = 0
WARNING: this module leaks information when decryption or verification fails. The exceptions that are raised contain the Python traceback information, which can be used to deduce where in the process the failure occurred. DO NOT PASS SUCH INFORMATION to your users. ''' import hashlib import os from _compat import b import common, transform, core, varblock # ASN.1 codes that describe the hash algorithm used. HASH_ASN1 = { 'MD5': b('\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10'), 'SHA-1': b('\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14'), 'SHA-256': b('\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20'), 'SHA-384': b('\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30'), 'SHA-512': b('\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40'), } HASH_METHODS = { 'MD5': hashlib.md5, 'SHA-1': hashlib.sha1, 'SHA-256': hashlib.sha256, 'SHA-384': hashlib.sha384, 'SHA-512': hashlib.sha512, } class CryptoError(Exception):
def pids(): """Returns a list of PIDs currently running on the system.""" return [int(x) for x in os.listdir(b('/proc')) if x.isdigit()]