#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
#
import hashlib
import os
import shutil
import tempfile
from . import generic
"""Class representing the signature-type packaging object."""
"chain_cert_openers"]
self.chain_cert_openers = []
try:
except KeyError:
_("Missing algorithm attribute"))
# If there's a hash, then there's a certificate to deliver
# with this action.
return False
return True
return self.has_payload
def file_opener():
return file_opener
"""Store the information about the certs needed to validate
this signature in the signature.
The 'chain_certs' parameter is a list of paths to certificates.
"""
self.chain_cert_openers = []
# chain_hshes and chain_chshes are dictionaries which map a
# given hash or compressed hash attribute to a list of the hash
# values for each path in chain_certs.
chain_hshes = {}
chain_chshes = {}
chain_csizes = []
chain_sizes = []
chain_hshes[attr] = []
chain_chshes[attr] = []
for pth in chain_certs:
try:
except EnvironmentError as e:
# misc.get_data_digest takes care of closing the file
# that's opened below.
with file_opener() as fh:
# We need a filename to use for the uncompressed chain
# cert, so get the preferred chain hash value from the
# chain_hshes
if alg == "sha1":
attr = "chain"
else:
# Remove any unused hash attributes.
if chain_hshes:
# These attributes are stored as a single value with
# spaces in it rather than multiple values to ensure
# the ordering remains consistent.
"""Get the cryptopgraphy Hash() class based on the OpenSSL
algorithm name."""
return h
return res
res = 0
return res
# The length of 'chain' is also going to be the length
# of pkg.chain.<hash alg>, so there's no need to look for
# other hash attributes here.
if c == chain:
return int(s)
return None
if c == chain:
return int(s)
return None
"""Create a stable string representation of an action that
is deterministic in its creation. If creating a string from an
action is non-deterministic, then manifest signing cannot work.
The parameter 'a' is the signature action that's going to use
the string produced. It's needed for the signature string
action, and is here to keep the method signature the same.
"""
# Any changes to this function mean Action.sig_version must be
# incremented.
# Signature actions don't sign other signature actions. So if
# the action that's doing the signing isn't ourself, return
# nothing.
return None
# It's necessary to sign the action as the client will see it,
# post publication. To do that, it's necessary to simulate the
# publication process on a copy of the action, converting
# paths to hashes and adding size information.
# The signature action can't sign the value of the value
# attribute, but it can sign that attribute's name.
# "hash" is special since it shouldn't appear in
# the action attributes, it gets set as a member
# instead.
# The use of self.hash here is just to point to a
# filename, the type of hash used for self.hash is
# irrelevant. Note that our use of self.hash for the
# basename will need to be modified when we finally move
# off SHA-1 hashes.
csizes = []
chain_hashes = {}
chain_chashes = {}
chain_hashes[attr] = []
chain_chashes[attr] = []
if chain_hashes:
if chain_hashes[attr]:
# Now that tmp_a looks like the post-published action, transform
# it into a string using the generic sig_str method.
"""Transforms a collection of actions into a string that is
used to sign those actions."""
# If a is None, then the action was another signature action so
# discard it from the information to be signed.
(a for a in
if a is not None)))
"""Retrieve the chain certificates needed to validate this
signature."""
# We may not have any chain certs for this signature
if not chain_val:
return
"""Return a list of the chain certificates needed to validate
this signature. When retrieving the content from the
repository, we use the "least preferred" hash for backwards
compatibility, but when verifying the content, we use the
"most preferred" hash."""
if least_preferred:
else:
if not chain_val:
return []
"""Return a list of the chain certificates needed to validate
this signature."""
if least_preferred:
else:
if not chain_chash_val:
return []
return chain_chash_val.split()
"""Returns True if this action is signed using a key, instead
of simply being a hash. Since variant tagged signature
actions are not handled yet, it also returns False in that
case."""
"""Split the sig_alg attribute up in to something useful."""
for s in valid_sig_algs:
for h in valid_hash_algs:
t = "{0}-{1}".format(s, h)
if val == t:
return s, h
for h in valid_hash_algs:
if h == val:
return None, h
return None, None
required_names=None):
"""Try to verify this signature. It can return True or
None. None means we didn't know how to verify this signature.
If we do know how to verify the signature but it doesn't verify,
then an exception is raised.
The 'acts' parameter is the iterable of actions against which
to verify the signature.
The 'pub' parameter is the publisher that published the
package this action signed.
The 'trust_anchors' parameter contains the trust anchors to use
when verifying the signature.
The 'required_names' parameter is a set of strings that must
be seen as a CN in the chain of trust for the certificate."""
# If this signature is tagged with variants, if the version is
# higher than one we know about, or it uses an unrecognized
# hash algorithm, we can't handle it yet.
if self.get_variant_template() or \
return None
# Turning this into a list makes debugging vastly more
# tractable.
# If self.hash is None, then the signature is storing a hash
# of the actions, not a signed value.
computed_hash = h.digest()
# The attrs value is stored in hex so that it's easy
# to read.
_("The signature value did not match the "
return True
# Verify a signature that's not just a hash.
return None
# Get the certificate paired with the key which signed this
# action.
# Make sure that the intermediate certificates that are needed
# to validate this signature are present.
try:
# This import is placed here to break a circular
# import seen when merge.py is used.
# Verify the certificate whose key created this
# signature action.
except apx.SigningException as e:
raise
# Check that the certificate verifies against this signature.
hhash())
try:
except InvalidSignature:
_("The signature value did not match the expected "
"value."))
return True
chash_dir=None):
"""Sets the signature value for this action.
The 'acts' parameter is the iterable of actions this action
should sign.
The 'key_path' parameter is the path to the file containing the
private key which is used to sign the actions.
The 'chain_paths' parameter is an iterable of paths to
certificates which are needed to form the chain of trust from
the certificate associated with the key in 'key_path' to one of
the CAs for the publisher of the actions.
The 'chash_dir' parameter is the temporary directory to use
while calculating the compressed hashes for chain certs."""
# Turning this into a list makes debugging vastly more
# tractable.
# If key_path is None, then set value to be the hash
# of the actions.
if key_path is None:
# If no private key is set, then no certificate should
# have been given.
else:
# If a private key is used, then the certificate it's
# paired with must be provided.
try:
except ValueError:
"be a RSA key but could not be read "
"""Generates the indices needed by the search dictionary. See
generic.py for a more detailed explanation."""
res = []
# We already have an index entry for self.hash;
# we only want hash attributes other than "hash".
return res
"""Check whether another action is identical to this
signature."""
# Only signature actions can be identical to other signature
# actions.
return False
# If the code signing certs are identical, the more checking is
# needed.
# Determine if we share any hash attribute values with the other
# action.
if attr == "hash":
# we deal with the 'hash' member later
continue
# Technically 'hsh' isn't a hash attr, it's
# a hash attr value, but that's enough for us
# to consider it as potentially identical.
# If the algorithms are using different algorithms or
# have different versions, then they're not identical.
return False
# If the values are the same, then they're identical.
return True
return False
"""Performs additional validation of action attributes that
for performance or other reasons cannot or should not be done
during Action object creation. An ActionError exception (or
subclass of) will be raised if any attributes are not valid.
This is primarily intended for use during publication or during
error handling to provide additional diagonostics.
'fmri' is an optional package FMRI (object or string) indicating
what package contained this action.
"""
# 'value' can only be required at publication time since signing
# relies on the ability to construct actions without one despite
# the fact that it is the key attribute.
"chash", "value"))