Example #1
0
def ParsePkcs8(pkcs8):
  seq = ParseASN1Sequence(decoder.decode(Base64WSDecode(pkcs8))[0])
  if len(seq) != 3:  # need three fields in PrivateKeyInfo
    raise kzr_errors.KeyczarError("Illegal PKCS8 String.")
  version = int(seq[0])
  if version != 0:
    raise kzr_errors.KeyczarError("Unrecognized PKCS8 Version")
  [oid, alg_params] = ParseASN1Sequence(seq[1])
  key = decoder.decode(seq[2])[0]
  # Component 2 is an OCTET STRING which is further decoded
  params = {}
  if oid == RSA_OID:
    key = ParseASN1Sequence(key)
    version = int(key[0])
    if version != 0:
      raise kzr_errors.KeyczarError("Unrecognized RSA Private Key Version")
    for i in range(len(RSA_PARAMS)):
      params[RSA_PARAMS[i]] = long(key[i+1])
  elif oid == DSA_OID:
    alg_params = ParseASN1Sequence(alg_params)
    for i in range(len(DSA_PARAMS)):
      params[DSA_PARAMS[i]] = long(alg_params[i])
    params['x'] = long(key)
  else:
    raise kzr_errors.KeyczarError("Unrecognized AlgorithmIdentifier: not RSA/DSA")
  return params
Example #2
0
def GenKey(key_type, size=None):
  """
  Generates a key of the given key_type and length.

  @param key_type: the key_type of key to generate
  @key_type key_type: L{keyinfo.KeyType}

  @param size: the length in bits of the key to be generated
  @key_type size: integer

  @return: the generated key of the given key_type and size

  @raise KeyczarError: if key_type is a public key or unsupported or if key size
                       is unsupported.
  """
  if size is None:
    size = key_type.default_size

  if not key_type.IsValidSize(size):
    raise errors.KeyczarError("Unsupported key size %d bits." % size)

  try:
    return {keyinfo.AES: AesKey.Generate,
            keyinfo.HMAC_SHA1: HmacKey.Generate,
            keyinfo.DSA_PRIV: DsaPrivateKey.Generate,
            keyinfo.RSA_PRIV: RsaPrivateKey.Generate}[key_type](size)
  except KeyError:
    if key_type == keyinfo.DSA_PUB or key_type == keyinfo.RSA_PUB:
      msg = "Public keys of key_type %s must be exported from private keys."
    else:
      msg = "Unsupported key key_type: %s"
    raise errors.KeyczarError(msg % key_type)
Example #3
0
    def __init__(self, reader):
        self.metadata = keydata.KeyMetadata.Read(reader.GetMetadata())
        self._keys = {}  # maps both KeyVersions and hash ids to keys
        self.primary_version = None  # default if no primary key
        self.default_size = self.metadata.type.default_size

        if not self.IsAcceptablePurpose(self.metadata.purpose):
            raise errors.KeyczarError("Unacceptable purpose: %s" %
                                      self.metadata.purpose)

        if self.metadata.encrypted and not isinstance(reader,
                                                      readers.EncryptedReader):
            raise errors.KeyczarError("Need encrypted reader.")

        for version in self.metadata.versions:
            if version.status == keyinfo.PRIMARY:
                if self.primary_version is not None:
                    raise errors.KeyczarError(
                        "Key sets may only have a single primary version")
                self.primary_version = version
            key = keys.ReadKey(self.metadata.type,
                               reader.GetKey(version.version_number))
            self._keys[version] = key
            self._AddHashedKey(key, key.hash_id)
            self._AddFallbackHashedKey(key)
Example #4
0
def Create(loc, name, purpose, asymmetric=None):
  if mock is None and loc is None:  # not testing
    raise errors.KeyczarError("Location missing")
  
  kmd = None
  if purpose == keyinfo.SIGN_AND_VERIFY:
    if asymmetric is None:
      kmd = keydata.KeyMetadata(name, purpose, keyinfo.HMAC_SHA1)
    elif asymmetric.lower() == "rsa":
      kmd = keydata.KeyMetadata(name, purpose, keyinfo.RSA_PRIV)
    else:  # default to DSA
      kmd = keydata.KeyMetadata(name, purpose, keyinfo.DSA_PRIV)
  elif purpose == keyinfo.DECRYPT_AND_ENCRYPT:
    if asymmetric is None:
      kmd = keydata.KeyMetadata(name, purpose, keyinfo.AES)
    else:  # default to RSA
      kmd = keydata.KeyMetadata(name, purpose, keyinfo.RSA_PRIV)
  else:
    raise errors.KeyczarError("Missing or unsupported purpose")
  
  if mock is not None:  # just testing, update mock object
    mock.kmd = kmd
  else:
    writer = writers.CreateWriter(loc)
    try:
      writer.WriteMetadata(kmd, overwrite=False)
    finally:
      writer.Close()
Example #5
0
def ReadKey(key_type, key):
    """
  Reads a key of the given key_type from a JSON string representation.

  @param key_type: the key_type of key to read
  @key_type key_type: L{keyinfo.KeyType}

  @param key: the JSON string representation of the key
  @key_type key: string

  @return: the key object read from the JSON string

  @raise KeyczarError: if key_type is unsupported
  """
    try:
        return {
            keyinfo.AES: AesKey.Read,
            keyinfo.HMAC_SHA1: HmacKey.Read,
            keyinfo.DSA_PRIV: DsaPrivateKey.Read,
            keyinfo.RSA_PRIV: RsaPrivateKey.Read,
            keyinfo.DSA_PUB: DsaPublicKey.Read,
            keyinfo.RSA_PUB: RsaPublicKey.Read
        }[key_type](key)
    except KeyError:
        raise errors.KeyczarError("Unsupported key key_type: %s" % key_type)
Example #6
0
 def PublicKeyExport(self, dest, mock=None):
     """Export the public keys corresponding to our key set to destination."""
     kmd = self.metadata
     pubkmd = None
     if kmd.type == keyinfo.DSA_PRIV and kmd.purpose == keyinfo.SIGN_AND_VERIFY:
         pubkmd = keydata.KeyMetadata(kmd.name, keyinfo.VERIFY,
                                      keyinfo.DSA_PUB)
     elif kmd.type == keyinfo.RSA_PRIV:
         if kmd.purpose == keyinfo.DECRYPT_AND_ENCRYPT:
             pubkmd = keydata.KeyMetadata(kmd.name, keyinfo.ENCRYPT,
                                          keyinfo.RSA_PUB)
         elif kmd.purpose == keyinfo.SIGN_AND_VERIFY:
             pubkmd = keydata.KeyMetadata(kmd.name, keyinfo.VERIFY,
                                          keyinfo.RSA_PUB)
     if pubkmd is None:
         raise errors.KeyczarError("Cannot export public key")
     for v in self.versions:
         pubkmd.AddVersion(v)
         pubkey = self.GetKey(v).public_key
         if mock:  # only for testing
             mock.SetPubKey(v.version_number, pubkey)
         else:
             util.WriteFile(str(pubkey),
                            os.path.join(dest, str(v.version_number)))
     if mock:  # only for testing
         mock.pubkmd = pubkmd
     else:
         util.WriteFile(str(pubkmd), os.path.join(dest, "meta"))
Example #7
0
  def __CreateKey(self):
    """
    Helper to create the actual key from the Header
    NOTE: The key determines what the optimal read buffer size will be. It is a
    size that does not require any padding to allow allow encrypting without
    using a stream anddecrypting with a stream 
    i.e. Encrypt() => DecryptingStreamReader()
    """
    is_data_avail = True
    if not self.__key:
      read_bytes, is_data_avail = self.__ReadBytes(constants.HEADER_SIZE -
                                                   len(self.__encrypted_buffer))
      if read_bytes:
        self.__encrypted_buffer += read_bytes

      if len(self.__encrypted_buffer) >= constants.HEADER_SIZE:
        hdr_bytes = self.__encrypted_buffer[:constants.HEADER_SIZE]
        self.__encrypted_buffer = (
          self.__encrypted_buffer[constants.HEADER_SIZE:])
        keys = self.__key_set._ParseHeader(hdr_bytes)
        if(len(keys) > 1):
          raise errors.KeyczarError(
            "Streaming decrypt cannot handle key collisions")
        self.__key = keys[0]
        self.__hmac_stream = self.__key.hmac_key.CreateStreamable()
        self.__hmac_stream.Update(hdr_bytes)
        if self.__buffer_size >= 0:
          self.__buffer_size = self.__key._NoPadBufferSize(self.__buffer_size)

    return is_data_avail
Example #8
0
def PubKey(loc, dest):
    if mock is None and dest is None:  # not required when testing
        raise errors.KeyczarError("Must define destination")
    czar = CreateGenericKeyczar(loc)
    if mock is None:
        util.MakeDirRecursive(dest)
    czar.PublicKeyExport(dest, mock)  # supply mock for testing if enabled
Example #9
0
    def __Decode(self, encoded_message, label=""):
        # See PKCS#1 v2.1: ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf
        if len(label) >= 2**61:
            # 2^61 = the input limit for SHA-1
            raise errors.KeyczarError(
                "OAEP Decoding Error - label is too large %d" % len(label))
        if len(encoded_message) < 2 * util.HLEN + 2:
            raise errors.KeyczarError(
                "OAEP Decoding Error - encoded_message is too small: %d" %
                len(encoded_message))

        # Step 3b  EM = Y || maskedSeed || maskedDB
        k = int(math.floor(math.log(self.key.n, 256)) + 1)  # num bytes in n
        diff_len = k - len(encoded_message)
        # PyCrypto strips out leading zero bytes.
        # In OAEP, the first byte is expected to be a zero, so we can ignore it
        if diff_len > 1:
            # If more bytes were chopped by PyCrypto, add zero bytes back on
            encoded_message = '\x00' * (diff_len - 1) + encoded_message

        masked_seed = encoded_message[:util.HLEN]
        masked_datablock = encoded_message[util.HLEN:]

        # Step 3c,d
        seed_mask = util.MGF(masked_datablock, util.HLEN)
        seed = util.Xor(masked_seed, seed_mask)

        # Step 3e
        datablock_mask = util.MGF(
            seed,
            len(masked_datablock))  # encoded_message already stripped of 0

        # Step 3f
        datablock = util.Xor(masked_datablock, datablock_mask)

        label_hash = datablock[:util.HLEN]
        expected_label_hash = util.Hash(label)  # Debugging
        if label_hash != expected_label_hash:
            raise errors.KeyczarError(
                "OAEP Decoding Error - hash_id is invalid")

        delimited_message = datablock[util.HLEN:].lstrip('\x00')
        if delimited_message[0] != '\x01':
            raise errors.KeyczarError(
                "OAEP Decoding Error - expected a 1 value")
        return delimited_message[1:]  # The message
Example #10
0
 def WriteMetadata(self, metadata, overwrite=True):
     """
 Write the metadata for the key.
 """
     fname = os.path.join(self.location, "meta")
     if not overwrite and os.path.exists(fname):
         raise errors.KeyczarError("File:%s already exists" % fname)
     util.WriteFile(str(metadata), fname)
     return
Example #11
0
    def WriteMetadata(self, metadata, overwrite=True):
        """Write the metadata for the key.

        """
        fname = 'meta'
        if not overwrite and self.location.exists(fname):
            raise errors.KeyczarError(u'File:%s already exists' % fname)
        self._write(fname, unicode(metadata))
        return
Example #12
0
def ParseX509(x509):
  seq = ParseASN1Sequence(decoder.decode(Base64WSDecode(x509))[0])
  if len(seq) != 2:  # need two fields in SubjectPublicKeyInfo
    raise kzr_errors.KeyczarError("Illegal X.509 String.")
  [oid, alg_params] = ParseASN1Sequence(seq[0])
  pubkey = decoder.decode(univ.OctetString(BinToBytes(seq[1].
                                                      prettyPrint()[2:-3])))[0]
  # Component 1 should be a BIT STRING, get raw bits by discarding extra chars,
  # then convert to OCTET STRING which can be ASN.1 decoded
  params = {}
  if oid == RSA_OID:
    [params['n'], params['e']] = [long(x) for x in ParseASN1Sequence(pubkey)]
  elif oid == DSA_OID:
    vals = [long(x) for x in ParseASN1Sequence(alg_params)]
    for i in range(len(DSA_PARAMS)):
      params[DSA_PARAMS[i]] = vals[i]
    params['y'] = long(pubkey)
  else:
    raise kzr_errors.KeyczarError("Unrecognized AlgorithmIdentifier: not RSA/DSA")
  return params
Example #13
0
    def Revoke(self, version_number):
        """
    Revokes the key with given version number if scheduled to be revoked.

    @param version_number: integer version number to revoke
    @type version_number: integer

    @raise KeyczarError: if invalid version number or key is not inactive.
    """
        version = self.metadata.GetVersion(version_number)
        if version.status == keyinfo.INACTIVE:
            self.metadata.RemoveVersion(version_number)
        else:
            raise errors.KeyczarError("Can't revoke key if not inactive.")
Example #14
0
def Revoke(loc, num):
  czar = CreateGenericKeyczar(loc)
  if num < 0:
    raise errors.KeyczarError("Missing version")
  czar.Revoke(num)
  UpdateGenericKeyczar(czar, loc)
  if mock is not None:  # testing, update mock
    mock.RemoveKey(num)
  else:
    writer = writers.CreateWriter(loc)
    try:
      writer.Remove(num)
    finally:
      writer.Close()
Example #15
0
def CreateWriter(location):
  """Factory function for Writers
  
    @param location: where (file, uri, etc) the writer should write to
    @type location: string
  """
  # make sure all writers are available
  util.ImportBackends()
  for sc in Writer.__subclasses__():
    writer = sc.CreateWriter(location)
    if writer:
      return writer
  raise errors.KeyczarError(
    "Unable to create a writer for %s. Does the location exist?" % location)
Example #16
0
  def __Encode(self, msg, label=b""):
    if len(label) >= 2**61:  # the input limit for SHA-1
      raise errors.KeyczarError("OAEP parameter string too long.")
    k = int(math.floor(math.log(self.key.n, 256)) + 1) # num bytes in n
    if len(msg) > k - 2 * util.HLEN - 2:
      raise errors.KeyczarError("Message too long to OAEP encode.")
    label_hash = util.Hash(label)
    pad_octets = (k - len(msg) - 2 * util.HLEN - 2)  # Number of zeros to pad
    if pad_octets < 0:
      raise errors.KeyczarError("Message is too long: %d" % len(msg))
    datablock = label_hash + util.RepeatByte(0x00, pad_octets) + b'\x01' + msg
    seed = util.RandBytes(util.HLEN)

    # Steps 2e, f
    datablock_mask = util.MGF(seed, k - util.HLEN - 1)
    masked_datablock = util.Xor(datablock, datablock_mask)

    # Steps 2g, h
    seed_mask = util.MGF(masked_datablock, util.HLEN)
    masked_seed = util.Xor(seed, seed_mask)

    # Step 2i: Construct the encoded message
    return b'\x00' + masked_seed + masked_datablock
Example #17
0
    def AddVersion(self, status, size=None):
        """
    Adds a new key version with given status to key set.

    Generates a new key of same type (repeated until hash identifier is unique)
    for this version. Uses supplied key size (if provided) in lieu of the
    default key size. If this is an unacceptable key size, raises an error. Uses
    next available version number.

    @param status: the status of the new key to be added
    @type status: L{keyinfo.KeyStatus}

    @param size: size of key in bits, uses default size if not provided.
    @type size: integer

    @raise KeyczarError: if either key type or key size is unsupported.
    """
        if size is None:
            size = self.default_size

        if not self.metadata.type.IsValidSize(size):
            raise errors.KeyczarError("Unsupported key size %d bits." % size)

        max_version_number = 0
        for version in self.versions:
            if max_version_number < version.version_number:
                max_version_number = version.version_number

        # Make the new version number the max of the existing versions plus one
        version = keydata.KeyVersion(max_version_number + 1, status, False)

        if status == keyinfo.PRIMARY:
            if self.primary_version is not None:
                self.primary_version.status = keyinfo.ACTIVE
            self.primary_version = version

        if size < self.default_size:
            print(
                "WARNING: %d-bit key size is less than recommended default key"
                "size of %d bits for %s keys." %
                (size, self.default_size, str(self.metadata.type)))

        # Make sure no keys collide on their identifiers
        while True:
            key = keys.GenKey(self.metadata.type, size)
            if self._keys.get(key.hash_id) is None:
                break

        self._AddKey(version, key)
Example #18
0
def CreateGenericKeyczar(loc, crypter=None):
  if mock is not None:
    return keyczar.GenericKeyczar(mock)
  if loc is None:
    raise errors.KeyczarError("Need location")
  else:
    generic = None
    reader = readers.CreateReader(loc)
    try:
      if crypter:
        reader = readers.EncryptedReader(reader, crypter)
      generic = keyczar.GenericKeyczar(reader)
    finally:
      reader.Close()
    return generic
Example #19
0
def CreateReader(location):
    """Factory function for Reader's
  
    @param location: where (file, uri, etc) the reader should read from
    @type location: string
  """
    # make sure all readers are available
    util.ImportBackends()
    # return the first that accepts the location
    for sc in Reader.__subclasses__():
        reader = sc.CreateReader(location)
        if reader:
            return reader
    raise errors.KeyczarError(
        "Unable to create a reader for %s. Does the location exist?" %
        location)
Example #20
0
def ReadFile(loc):
  """
  Read data from file at given location.

  @param loc: name of file to read from
  @type loc: string

  @return: contents of the file
  @rtype: string

  @raise KeyczarError: if unable to read from file because of IOError
  """
  try:
    return open(loc).read()
  except IOError:
    raise errors.KeyczarError("Unable to read file %s." % loc)
Example #21
0
    def GetVersion(self, version_number):
        """
    Return the version corresponding to the given version number.

    @param version_number: integer version number of desired version
    @type version_number: integer

    @return: the corresponding version if it exists
    @rtype: L{KeyVersion}

    @raise KeyczarError: if the version number is non-existent.
    """
        try:
            return self.__versions[version_number]
        except KeyError:
            raise errors.KeyczarError("No such version number: %d" %
                                      version_number)
Example #22
0
    def RemoveVersion(self, version_number):
        """
    Removes version with given version number and returns it if it exists.

    @param version_number: version number to remove
    @type version_number: integer

    @return: the removed version if it exists
    @rtype: L{KeyVersion}

    @raise KeyczarError: if the version number is non-existent
    """
        try:
            self.__versions.pop(version_number)
        except KeyError:
            raise errors.KeyczarError("No such version number: %d" %
                                      version_number)
Example #23
0
def ParseDsaSig(sig):
  """
  Given a raw byte string, return tuple of DSA signature parameters.

  @param sig: byte string of ASN.1 representation
  @type sig: string

  @return: parameters r, s as a tuple
  @rtype: tuple

  @raise KeyczarErrror: if the DSA signature format is invalid
  """
  seq = decoder.decode(sig)[0]
  if len(seq) != 2:
    raise kzr_errors.KeyczarError("Illegal DSA signature.")
  r = long(seq.getComponentByPosition(0))
  s = long(seq.getComponentByPosition(1))
  return (r, s)
Example #24
0
def WriteFile(data, loc):
  """
  Writes data to file at given location.

  @param data: contents to be written to file
  @type data: string

  @param loc: name of file to write to
  @type loc: string

  @raise KeyczarError: if unable to write to file because of IOError
  """
  try:
    f = open(loc, "w")
    f.write(data)
    f.close()
  except IOError:
    raise kzr_errors.KeyczarError("Unable to write to file %s." % loc)
Example #25
0
    def Demote(self, version_number):
        """
    Demotes the status of key with given version number.

    Demoting PRIMARY key results in a key set with no primary version.

    @param version_number: the version number to demote
    @type version_number: integer

    @raise KeyczarError: if invalid version number or trying to demote an
    inactive key, use L{Revoke} instead.
    """
        version = self.metadata.GetVersion(version_number)
        if version.status == keyinfo.PRIMARY:
            version.status = keyinfo.ACTIVE
            self.primary_version = None  # no more primary keys in the set
        elif version.status == keyinfo.ACTIVE:
            version.status = keyinfo.INACTIVE
        elif version.status == keyinfo.INACTIVE:
            raise errors.KeyczarError(
                "Can't demote an inactive key, only revoke.")
Example #26
0
def MGF(seed, mlen):
  """
  Mask Generation Function (MGF1) with SHA-1 as hash.

  @param seed: used to generate mask, a byte string
  @type seed: string

  @param mlen: desired length of mask
  @type mlen: integer

  @return: mask, byte string of length mlen
  @rtype: string

  @raise KeyczarError: if mask length too long, > 2^32 * hash_length
  """
  if mlen > 2**32 * HLEN:
    raise kzr_errors.KeyczarError("MGF1 mask length too long.")
  output = b""
  for i in range(int(math.ceil(mlen / float(HLEN)))):
    output += Hash(seed, IntToBytes(i))
  return output[:mlen]
Example #27
0
    def Promote(self, version_number):
        """
    Promotes the status of key with given version number.

    Promoting ACTIVE key automatically demotes current PRIMARY key to ACTIVE.

    @param version_number: the version number to promote
    @type version_number: integer

    @raise KeyczarError: if invalid version number or trying to promote
      a primary key
    """
        version = self.metadata.GetVersion(version_number)
        if version.status == keyinfo.PRIMARY:
            raise errors.KeyczarError("Can't promote a primary key.")
        elif version.status == keyinfo.ACTIVE:
            version.status = keyinfo.PRIMARY
            if self.primary_version is not None:
                self.primary_version.status = keyinfo.ACTIVE  # only one primary key
            self.primary_version = version
        elif version.status == keyinfo.INACTIVE:
            version.status = keyinfo.ACTIVE
Example #28
0
 def GetKey(self, version_number):
     try:
         return str(self.keys[version_number])
     except KeyError:
         raise errors.KeyczarError("Unrecognized Version Number")
Example #29
0
def GetFlag(flag):
  try:
    return flags[flag]
  except KeyError:
    raise errors.KeyczarError("Unknown flag")
Example #30
0
def GetCommand(cmd):
  try:
    return commands[cmd]
  except KeyError:
    raise errors.KeyczarError("Illegal command")