-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Switch to pyca/cryptography as crypto backend #41
Changes from 1 commit
b5d88c8
e0c8852
6d6910f
7439477
04589cc
e4e8d83
a354558
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -116,7 +116,7 @@ def _get_by_id(t, id_v): | |
|
||
def _alg(elt): | ||
""" | ||
Return the hashlib name of an Algorithm. Hopefully. | ||
Return the xmldsig name of an Algorithm. Hopefully. | ||
:returns: None or string | ||
""" | ||
uri = elt.get('Algorithm', None) | ||
|
@@ -327,7 +327,10 @@ def _verify(t, keyspec, sig_path=".//{%s}Signature" % NS['ds'], drop_signature=F | |
si = sig.find(".//{%s}SignedInfo" % NS['ds']) | ||
logging.debug("Found signedinfo {!s}".format(etree.tostring(si))) | ||
cm_alg = _cm_alg(si) | ||
sig_digest_alg = _sig_digest(si) | ||
try: | ||
sig_digest_alg = _sig_alg(si) | ||
except AttributeError: | ||
raise XMLSigException("Failed to validate {!s} because of unsupported hash format".format(etree.tostring(sig))) | ||
|
||
refmap = _process_references(t, sig, verify_mode=True, sig_path=sig_path, drop_signature=drop_signature) | ||
for ref,obj in refmap.items(): | ||
|
@@ -336,15 +339,15 @@ def _verify(t, keyspec, sig_path=".//{%s}Signature" % NS['ds'], drop_signature=F | |
sic = _transform(cm_alg, si) | ||
logging.debug("SignedInfo C14N: %s" % sic) | ||
if this_cert.do_digest: | ||
digest = _digest(sic, sig_digest_alg) | ||
digest = _digest(sic, sig_digest_alg.name) | ||
logging.debug("SignedInfo digest: %s" % digest) | ||
b_digest = b64d(digest) | ||
actual = _signed_value(b_digest, this_cert.keysize, True, sig_digest_alg) | ||
actual = _signed_value(b_digest, this_cert.keysize, True, sig_digest_alg.name) | ||
else: | ||
actual = sic | ||
|
||
if not this_cert.verify(b64d(sv), actual, sig_digest_alg): | ||
raise XMLSigException("Failed to validate {!s} using sig digest {!s} and cm {!s}".format(etree.tostring(sig),sig_digest_alg,cm_alg)) | ||
raise XMLSigException("Failed to validate {!s} using sig digest {!s} and cm {!s}".format(etree.tostring(sig), sig_digest_alg.xmldsig_name, cm_alg)) | ||
validated.append(obj) | ||
except XMLSigException, ex: | ||
logging.error(ex) | ||
|
@@ -466,7 +469,7 @@ def sign(t, key_spec, cert_spec=None, reference_uri='', insert_index=0, sig_path | |
si = sig.find(".//{%s}SignedInfo" % NS['ds']) | ||
assert si is not None | ||
cm_alg = _cm_alg(si) | ||
digest_alg = _sig_digest(si) | ||
sig_alg = _sig_alg(si) | ||
|
||
_process_references(t, sig, verify_mode=False, sig_path=sig_path) | ||
# XXX create signature reference duplicates/overlaps process references unless a c14 is part of transforms | ||
|
@@ -476,14 +479,14 @@ def sign(t, key_spec, cert_spec=None, reference_uri='', insert_index=0, sig_path | |
|
||
# sign hash digest and insert it into the XML | ||
if private.do_digest: | ||
digest = _digest(sic, digest_alg) | ||
digest = _digest(sic, sig_alg.name) | ||
logging.debug("SignedInfo digest: %s" % digest) | ||
b_digest = b64d(digest) | ||
tbs = _signed_value(b_digest, private.keysize, private.do_padding, digest_alg) | ||
tbs = _signed_value(b_digest, private.keysize, private.do_padding, sig_alg.name) | ||
else: | ||
tbs = sic | ||
|
||
signed = private.sign(tbs, digest_alg) | ||
signed = private.sign(tbs, sig_alg) | ||
signature = b64e(signed) | ||
logging.debug("SignatureValue: %s" % signature) | ||
sv = sig.find(".//{%s}SignatureValue" % NS['ds']) | ||
|
@@ -515,8 +518,5 @@ def _sig_alg(si): | |
sig_alg = _alg(sm) | ||
if sm is None or sig_alg is None: | ||
raise XMLSigException("No SignatureMethod") | ||
return (sig_alg.split("#"))[1] | ||
|
||
|
||
def _sig_digest(si): | ||
return (_sig_alg(si).split("-"))[1] | ||
return getattr(constants.Hashes,sig_alg.lower()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I actually kept this -- don't know -- it's internal stuff anyway. Could be implemented in |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,16 +15,59 @@ | |
TRANSFORM_C14N_EXCLUSIVE = 'http://www.w3.org/2001/10/xml-exc-c14n#' | ||
TRANSFORM_C14N_INCLUSIVE = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315' | ||
|
||
ALGORITHM_DIGEST_SHA1 = "http://www.w3.org/2000/09/xmldsig#sha1" | ||
ALGORITHM_SIGNATURE_RSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#rsa-sha1" | ||
|
||
ALGORITHM_DIGEST_SHA256 = "http://www.w3.org/2001/04/xmlenc#sha256" | ||
ALGORITHM_SIGNATURE_RSA_SHA256 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" | ||
|
||
ALGORITHM_DIGEST_SHA384 = "http://www.w3.org/2001/04/xmlenc#sha384" | ||
ALGORITHM_SIGNATURE_RSA_SHA384 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384" | ||
# All suported crypto primitives and some factories normalization of names as | ||
# pyca/cryptography uses UPPERCASE object-names for hashes. | ||
|
||
ALGORITHM_DIGEST_SHA1 = "http://www.w3.org/2000/09/xmldsig#sha1" | ||
ALGORITHM_DIGEST_SHA256 = "http://www.w3.org/2001/04/xmlenc#sha256" | ||
ALGORITHM_DIGEST_SHA384 = "http://www.w3.org/2001/04/xmlenc#sha384" | ||
ALGORITHM_DIGEST_SHA512 = "http://www.w3.org/2001/04/xmlenc#sha512" | ||
|
||
ALGORITHM_SIGNATURE_RSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#rsa-sha1" | ||
ALGORITHM_SIGNATURE_RSA_SHA256 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" | ||
ALGORITHM_SIGNATURE_RSA_SHA384 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384" | ||
ALGORITHM_SIGNATURE_RSA_SHA512 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512" | ||
|
||
|
||
|
||
class _HashType(object): | ||
def __init__(self, hashlib_name, xmldsig_name, pyca_name=None): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we need to be holding on three different names. There should be just one internal name. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looking into this, I see we need one name for use by xmldsig, one for hashlib and one for pyca. The correct way to implement this is to make it explicit by denoting the respective conversions. We need
The internal name is used everywhere, while conversion takes place on the edges. sign_alg_internal_to_xmldsig = {
'sha1': ALGORITHM_SIGNATURE_RSA_SHA1,
'sha256': ALGORITHM_SIGNATURE_RSA_SHA256,
'sha384': ALGORITHM_SIGNATURE_RSA_SHA384,
'sha512': ALGORITHM_SIGNATURE_RSA_SHA512,
}
sign_alg_xmldsig_to_internal = {
ALGORITHM_SIGNATURE_RSA_SHA1: 'sha1',
ALGORITHM_SIGNATURE_RSA_SHA256: 'sha256',
ALGORITHM_SIGNATURE_RSA_SHA384: 'sha384',
ALGORITHM_SIGNATURE_RSA_SHA512: 'sha512',
}
sign_alg_internal_to_hashlib = {
'sha1': 'sha1',
'sha256': 'sha256',
'sha384': 'sha384',
'sha512': 'sha512',
}
sign_alg_internal_to_pyca_cryptography = {
'sha1': 'SHA1',
'sha256': 'SHA256',
'sha384': 'SHA384',
'sha512': 'SHA512',
}
# and the other way around like xmldsig .. Then, consumers convert to the internal representation and producers to the desired representation. def _sig_alg(si):
sm = si.find(".//{%s}SignatureMethod" % NS['ds'])
sig_alg = _alg(sm)
if sm is None or sig_alg is None:
raise XMLSigException("No SignatureMethod")
return constants.sign_alg_xmldsig_to_internal[sig_alg]
def sign(self, data, hash_alg, pad_alg="PKCS1v15"):
# [...]
hash_alg = constants.sign_alg_internal_to_pyca_cryptography[hash_alg]
hasher = getattr(pyca_hashes, hash_alg)
padder = getattr(pyca_padding, pad_alg)
return self.key.sign(data, padder(), hasher()) In the above, the factory is the mapping itself. def sign_alg_xmldsig_to_internal(xmldsig_sign_alg):
try:
constants.sign_alg_xmldsig_to_internal[xmldsig_sign_alg]
except KeyError as e:
raise UnsupportedSignAlg(xmldsig_sign_alg) from e (which can be generalized to a factory-of-factories by making PS: the same holds for padding This now raises another question: Do we need to be managing hashlib names when we are using a full-featured cryptography library? I do not think so. We can get rid of hashlib and use the primitives from |
||
self.name = hashlib_name | ||
self.xmldsig_name = xmldsig_name | ||
if pyca_name is None: | ||
self.pyca_name = self.name.upper() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do not think this ( |
||
else: | ||
self.pyca_name = pyca_name | ||
|
||
class _HashTypes(object): | ||
def __init__(self): | ||
self._HASHES = { | ||
ALGORITHM_SIGNATURE_RSA_SHA1: 'sha1', | ||
ALGORITHM_SIGNATURE_RSA_SHA256: 'sha256', | ||
ALGORITHM_SIGNATURE_RSA_SHA384: 'sha384', | ||
ALGORITHM_SIGNATURE_RSA_SHA512: 'sha512'} | ||
|
||
def __getattr__(self, item): | ||
if item in self._HASHES: | ||
return _HashType(self._HASHES[item], item) | ||
else: | ||
raise AttributeError("Hash type '%s' not supported." % item) | ||
|
||
class _PaddingType(object): | ||
def __init__(self, name): | ||
self.name = name | ||
|
||
|
||
class _PaddingTypes(object): | ||
def __init__(self): | ||
self._PADDINGS = ['PKCS1v15'] | ||
def __getattr__(self, item): | ||
if item in self._PADDINGS: | ||
return _PaddingType(item) | ||
else: | ||
raise AttributeError("Padding type '%s' not supported." % item) | ||
|
||
Hashes = _HashTypes() | ||
Paddings = _PaddingTypes() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What follows this line is a bit funny. This should just
return uri
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice one! But I want to emphasize that this piece of code was from @leifj 😛 ... if I read the github blame correctly.