Source code for malduck.crypto.rsa

# Copyright (C) 2018 Jurriaan Bremer.
# This file is part of Roach - https://github.com/jbremer/roach.
# See the file 'docs/LICENSE.txt' for copying permission.

import io

from Cryptodome.PublicKey import RSA as RSA_
from itertools import takewhile

from .winhdr import BLOBHEADER, BaseBlob
from ..string.bin import uint32, bigint
from ..py2compat import long

__all__ = ["PublicKeyBlob", "PrivateKeyBlob", "RSA", "rsa"]


[docs]class PublicKeyBlob(BaseBlob): magic = b"RSA1" def __init__(self): BaseBlob.__init__(self) self.e = None self.n = None def parse(self, buf): header = buf.read(12) if len(header) != 12 or header[:4] != self.magic: return self.bitsize = uint32(header[4:8]) self.e = int(uint32(header[8:12])) n = buf.read(self.bitsize // 8) if len(n) != self.bitsize // 8: return self.n = bigint(n, self.bitsize) return 12 + self.bitsize // 8 def export_key(self): return RSA.export_key(self.n, self.e)
[docs]class PrivateKeyBlob(PublicKeyBlob): magic = b"RSA2" def __init__(self): PublicKeyBlob.__init__(self) self.p1 = None self.p2 = None self.exp1 = None self.exp2 = None self.coeff = None self.d = None def parse(self, buf): off = PublicKeyBlob.parse(self, buf) if not off: return self.p1 = bigint(buf.read(self.bitsize // 16), self.bitsize // 2) if self.p1 is None: return self.p2 = bigint(buf.read(self.bitsize // 16), self.bitsize // 2) if self.p2 is None: return self.exp1 = bigint(buf.read(self.bitsize // 16), self.bitsize // 2) if self.exp1 is None: return self.exp2 = bigint(buf.read(self.bitsize // 16), self.bitsize // 2) if self.exp2 is None: return self.coeff = bigint(buf.read(self.bitsize // 16), self.bitsize // 2) if self.coeff is None: return self.d = bigint(buf.read(self.bitsize // 8), self.bitsize) if self.d is None: return def export_key(self): return RSA.export_key(self.n, self.e, self.d)
BlobTypes = { 6: PublicKeyBlob, 7: PrivateKeyBlob, }
[docs]class RSA(object): algorithms = (0x0000A400,) # RSA
[docs] @staticmethod def import_key(data): r""" Extracts key from buffer containing :class:`PublicKeyBlob` or :class:`PrivateKeyBlob` data :param data: Buffer with `BLOB` structure data :type data: bytes :return: RSA key in PEM format :rtype: bytes """ try: return RSA_.import_key(data).export_key() except (ValueError, IndexError): pass if len(data) < BLOBHEADER.sizeof(): return buf = io.BytesIO(data) header = BLOBHEADER.parse(buf.read(BLOBHEADER.sizeof())) if header.bType not in BlobTypes: return if header.aiKeyAlg not in RSA.algorithms: return obj = BlobTypes[header.bType]() obj.parse(buf) return obj.export_key()
[docs] @staticmethod def export_key(n, e, d=None, p=None, q=None, crt=None): r""" Constructs key from tuple of RSA components :param n: RSA modulus n :param e: Public exponent e :param d: Private exponent d :param p: First factor of n :param q: Second factor of n :param crt: CRT coefficient q :return: RSA key in PEM format :rtype: bytes """ def wrap(x): return None if x is None else long(x) tup = wrap(n), wrap(e), wrap(d), wrap(p), wrap(q), wrap(crt) # PyCryptodome accepts only variable-length tuples tup = tuple(takewhile(lambda x: x is not None, tup)) return RSA_.construct(tup, consistency_check=False).export_key()
rsa = RSA