Remove deprecated "sign-the-hash" approach

This change removes the "sign-the-hash" signature
verification code in the signature_utils module and
the ImageProxy class. This code was deprecated in
Mitaka and scheduled for removal in Newton.

Change-Id: I8862f6c94538dd818c7360ba287e14c1264ff20f
Closes-Bug: #1516031
This commit is contained in:
Dane Fichter 2016-04-19 01:27:02 -04:00
parent 3c194e6cda
commit 5ab63107b6
4 changed files with 0 additions and 390 deletions

View File

@ -27,7 +27,6 @@ from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import hashes
from cryptography import x509
import debtcollector
from oslo_log import log as logging
from oslo_serialization import base64
from oslo_utils import encodeutils
@ -295,90 +294,6 @@ def get_verifier(context, image_properties):
)
@debtcollector.removals.remove(message="This will be removed in the N cycle.")
def should_verify_signature(image_properties):
"""Determine whether a signature should be verified.
Using the image properties, determine whether existing properties indicate
that signature verification should be done.
:param image_properties: the key-value properties about the image
:returns: True, if signature metadata properties exist, False otherwise
"""
return (image_properties is not None and
OLD_CERT_UUID in image_properties and
OLD_HASH_METHOD in image_properties and
OLD_SIGNATURE in image_properties and
OLD_KEY_TYPE in image_properties)
@debtcollector.removals.remove(
message="Starting with the Mitaka release, this approach to signature "
"verification using the image 'checksum' and signature metadata "
"properties that do not start with 'img' will not be supported. "
"This functionality will be removed in the N release. This "
"approach is being replaced with a signature of the data "
"directly, instead of a signature of the hash method, and the new "
"approach uses properties that start with 'img_'.")
def verify_signature(context, checksum_hash, image_properties):
"""Retrieve the image properties and use them to verify the signature.
:param context: the user context for authentication
:param checksum_hash: the 'checksum' hash of the image data
:param image_properties: the key-value properties about the image
:returns: True if verification succeeds
:raises glance.common.exception.SignatureVerificationError:
if verification fails
"""
if not should_verify_signature(image_properties):
raise exception.SignatureVerificationError(
_('Required image properties for signature verification do not'
' exist. Cannot verify signature.')
)
checksum_hash = encodeutils.to_utf8(checksum_hash)
signature = get_signature(image_properties[OLD_SIGNATURE])
hash_method = get_hash_method(image_properties[OLD_HASH_METHOD])
signature_key_type = SignatureKeyType.lookup(
image_properties[OLD_KEY_TYPE])
public_key = get_public_key(context,
image_properties[OLD_CERT_UUID],
signature_key_type)
# create the verifier based on the signature key type
try:
verifier = signature_key_type.create_verifier(signature,
hash_method,
public_key,
image_properties)
except crypto_exception.UnsupportedAlgorithm as e:
msg = (_LE("Unable to create verifier since algorithm is "
"unsupported: %(e)s")
% {'e': encodeutils.exception_to_unicode(e)})
LOG.error(msg)
raise exception.SignatureVerificationError(
_('Unable to verify signature since the algorithm is unsupported '
'on this system')
)
if verifier:
# Verify the signature
verifier.update(checksum_hash)
try:
verifier.verify()
return True
except crypto_exception.InvalidSignature:
raise exception.SignatureVerificationError(
_('Signature verification failed.')
)
else:
# Error creating the verifier
raise exception.SignatureVerificationError(
_('Error occurred while verifying the signature')
)
def get_signature(signature_data):
"""Decode the signature data and returns the signature.

View File

@ -17,7 +17,6 @@ import collections
import copy
from cryptography import exceptions as crypto_exception
import debtcollector
import glance_store as store
from oslo_config import cfg
from oslo_log import log as logging
@ -427,8 +426,6 @@ class ImageProxy(glance.domain.proxy.Image):
context=self.context,
verifier=verifier)
self._verify_signature_if_needed(checksum)
# NOTE(bpoulos): if verification fails, exception will be raised
if verifier:
try:
@ -446,19 +443,6 @@ class ImageProxy(glance.domain.proxy.Image):
self.image.checksum = checksum
self.image.status = 'active'
@debtcollector.removals.remove(
message="This will be removed in the N cycle.")
def _verify_signature_if_needed(self, checksum):
# Verify the signature (if correct properties are present)
if (signature_utils.should_verify_signature(
self.image.extra_properties)):
# NOTE(bpoulos): if verification fails, exception will be raised
result = signature_utils.verify_signature(
self.context, checksum, self.image.extra_properties)
if result:
LOG.info(_LI("Successfully verified signature for image %s"),
self.image.image_id)
def get_data(self, offset=0, chunk_size=None):
if not self.image.locations:
# NOTE(mclaren): This is the only set of arguments

View File

@ -25,7 +25,6 @@ from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import hashes
from debtcollector import removals
from glance.common import exception
from glance.common import signature_utils
@ -50,15 +49,6 @@ TEST_ECC_PRIVATE_KEY = ec.generate_private_key(ec.SECP521R1(),
signature_utils.CERT_UUID
)
# Required image property names
# TODO(bpoulos): remove when 'sign-the-hash' approach is no longer supported
(OLD_SIGNATURE, OLD_HASH_METHOD, OLD_KEY_TYPE, OLD_CERT_UUID) = (
signature_utils.OLD_SIGNATURE,
signature_utils.OLD_HASH_METHOD,
signature_utils.OLD_KEY_TYPE,
signature_utils.OLD_CERT_UUID
)
# Optional image property names for RSA-PSS
(MASK_GEN_ALG, PSS_SALT_LENGTH) = (
signature_utils.MASK_GEN_ALG,
@ -129,224 +119,6 @@ class BadPublicKey(object):
class TestSignatureUtils(test_utils.BaseTestCase):
"""Test methods of signature_utils"""
@removals.remove(message="This will be removed in the N cycle.")
def test_old_should_verify_signature(self):
image_props = {OLD_CERT_UUID: 'OLD_CERT_UUID',
OLD_HASH_METHOD: 'OLD_HASH_METHOD',
OLD_SIGNATURE: 'OLD_SIGNATURE',
OLD_KEY_TYPE: 'SIG_KEY_TYPE'}
self.assertTrue(signature_utils.should_verify_signature(image_props))
@removals.remove(message="This will be removed in the N cycle.")
def test_old_should_verify_signature_fail(self):
bad_image_properties = [{OLD_CERT_UUID: 'OLD_CERT_UUID',
OLD_HASH_METHOD: 'OLD_HASH_METHOD',
OLD_SIGNATURE: 'OLD_SIGNATURE'},
{OLD_CERT_UUID: 'OLD_CERT_UUID',
OLD_HASH_METHOD: 'OLD_HASH_METHOD',
OLD_KEY_TYPE: 'SIG_KEY_TYPE'},
{OLD_CERT_UUID: 'OLD_CERT_UUID',
OLD_SIGNATURE: 'OLD_SIGNATURE',
OLD_KEY_TYPE: 'SIG_KEY_TYPE'},
{OLD_HASH_METHOD: 'OLD_HASH_METHOD',
OLD_SIGNATURE: 'OLD_SIGNATURE',
OLD_KEY_TYPE: 'SIG_KEY_TYPE'}]
for bad_props in bad_image_properties:
result = signature_utils.should_verify_signature(bad_props)
self.assertFalse(result)
@unittest.skipIf(not default_backend().hash_supported(hashes.SHA256()),
"SHA-2 hash algorithms not supported by backend")
@removals.remove(message="This will be removed in the N cycle.")
@mock.patch('glance.common.signature_utils.get_public_key')
def test_old_verify_signature_PSS(self, mock_get_pub_key):
checksum_hash = b'224626ae19824466f2a7f39ab7b80f7f'
mock_get_pub_key.return_value = TEST_RSA_PRIVATE_KEY.public_key()
for hash_name, hash_alg in signature_utils.HASH_METHODS.items():
signer = TEST_RSA_PRIVATE_KEY.signer(
padding.PSS(
mgf=padding.MGF1(hash_alg),
salt_length=padding.PSS.MAX_LENGTH
),
hash_alg
)
signer.update(checksum_hash)
signature = base64.b64encode(signer.finalize())
image_props = {OLD_CERT_UUID:
'fea14bc2-d75f-4ba5-bccc-b5c924ad0693',
OLD_HASH_METHOD: hash_name,
OLD_KEY_TYPE: 'RSA-PSS',
MASK_GEN_ALG: 'MGF1',
OLD_SIGNATURE: signature}
self.assertTrue(signature_utils.verify_signature(None,
checksum_hash,
image_props))
@unittest.skipIf(not default_backend().hash_supported(hashes.SHA256()),
"SHA-2 hash algorithms not supported by backend")
@removals.remove(message="This will be removed in the N cycle.")
@mock.patch('glance.common.signature_utils.get_public_key')
def test_old_verify_signature_custom_PSS_salt(self, mock_get_pub_key):
checksum_hash = b'224626ae19824466f2a7f39ab7b80f7f'
mock_get_pub_key.return_value = TEST_RSA_PRIVATE_KEY.public_key()
custom_salt_length = 32
for hash_name, hash_alg in signature_utils.HASH_METHODS.items():
signer = TEST_RSA_PRIVATE_KEY.signer(
padding.PSS(
mgf=padding.MGF1(hash_alg),
salt_length=custom_salt_length
),
hash_alg
)
signer.update(checksum_hash)
signature = base64.b64encode(signer.finalize())
image_props = {OLD_CERT_UUID:
'fea14bc2-d75f-4ba5-bccc-b5c924ad0693',
OLD_HASH_METHOD: hash_name,
OLD_KEY_TYPE: 'RSA-PSS',
MASK_GEN_ALG: 'MGF1',
PSS_SALT_LENGTH: custom_salt_length,
OLD_SIGNATURE: signature}
self.assertTrue(signature_utils.verify_signature(None,
checksum_hash,
image_props))
@unittest.skipIf(not default_backend().hash_supported(hashes.SHA256()),
"SHA-2 hash algorithms not supported by backend")
@removals.remove(message="This will be removed in the N cycle.")
@mock.patch('glance.common.signature_utils.get_public_key')
def test_old_verify_signature_bad_signature(self, mock_get_pub_key):
checksum_hash = '224626ae19824466f2a7f39ab7b80f7f'
mock_get_pub_key.return_value = TEST_RSA_PRIVATE_KEY.public_key()
image_properties = {OLD_CERT_UUID:
'fea14bc2-d75f-4ba5-bccc-b5c924ad0693',
OLD_HASH_METHOD: 'SHA-256',
OLD_KEY_TYPE: 'RSA-PSS',
MASK_GEN_ALG: 'MGF1',
OLD_SIGNATURE: 'BLAH'}
self.assertRaisesRegex(exception.SignatureVerificationError,
'Signature verification failed.',
signature_utils.verify_signature,
None, checksum_hash, image_properties)
@removals.remove(message="This will be removed in the N cycle.")
@mock.patch('glance.common.signature_utils.should_verify_signature')
def test_old_verify_signature_invalid_image_props(self, mock_should):
mock_should.return_value = False
self.assertRaisesRegex(exception.SignatureVerificationError,
'Required image properties for signature'
' verification do not exist. Cannot verify'
' signature.',
signature_utils.verify_signature,
None, None, None)
@removals.remove(message="This will be removed in the N cycle.")
@mock.patch('glance.common.signature_utils.get_public_key')
def test_old_verify_signature_bad_sig_key_type(self, mock_get_pub_key):
checksum_hash = '224626ae19824466f2a7f39ab7b80f7f'
mock_get_pub_key.return_value = TEST_RSA_PRIVATE_KEY.public_key()
image_properties = {OLD_CERT_UUID:
'fea14bc2-d75f-4ba5-bccc-b5c924ad0693',
OLD_HASH_METHOD: 'SHA-256',
OLD_KEY_TYPE: 'BLAH',
MASK_GEN_ALG: 'MGF1',
OLD_SIGNATURE: 'BLAH'}
self.assertRaisesRegex(exception.SignatureVerificationError,
'Invalid signature key type: .*',
signature_utils.verify_signature,
None, checksum_hash, image_properties)
@unittest.skipIf(not default_backend().hash_supported(hashes.SHA256()),
"SHA-2 hash algorithms not supported by backend")
@removals.remove(message="This will be removed in the N cycle.")
@mock.patch('glance.common.signature_utils.get_public_key')
def test_old_verify_signature_RSA_no_mask_gen(self, mock_get_pub_key):
checksum_hash = '224626ae19824466f2a7f39ab7b80f7f'
mock_get_pub_key.return_value = TEST_RSA_PRIVATE_KEY.public_key()
image_properties = {OLD_CERT_UUID:
'fea14bc2-d75f-4ba5-bccc-b5c924ad0693',
OLD_HASH_METHOD: 'SHA-256',
OLD_KEY_TYPE: 'RSA-PSS',
OLD_SIGNATURE: 'BLAH'}
self.assertRaisesRegex(exception.SignatureVerificationError,
'Signature verification failed.',
signature_utils.verify_signature,
None, checksum_hash, image_properties)
@removals.remove(message="This will be removed in the N cycle.")
@mock.patch('glance.common.signature_utils.get_public_key')
def test_old_verify_signature_RSA_bad_mask_gen(self, mock_get_pub_key):
checksum_hash = '224626ae19824466f2a7f39ab7b80f7f'
mock_get_pub_key.return_value = TEST_RSA_PRIVATE_KEY.public_key()
image_properties = {OLD_CERT_UUID:
'fea14bc2-d75f-4ba5-bccc-b5c924ad0693',
OLD_HASH_METHOD: 'SHA-256',
OLD_KEY_TYPE: 'RSA-PSS',
MASK_GEN_ALG: 'BLAH',
OLD_SIGNATURE: 'BLAH'}
self.assertRaisesRegex(exception.SignatureVerificationError,
'Invalid mask_gen_algorithm: .*',
signature_utils.verify_signature,
None, checksum_hash, image_properties)
@removals.remove(message="This will be removed in the N cycle.")
@mock.patch('glance.common.signature_utils.get_public_key')
def test_old_verify_signature_bad_pss_salt(self, mock_get_pub_key):
checksum_hash = '224626ae19824466f2a7f39ab7b80f7f'
mock_get_pub_key.return_value = TEST_RSA_PRIVATE_KEY.public_key()
image_properties = {OLD_CERT_UUID:
'fea14bc2-d75f-4ba5-bccc-b5c924ad0693',
OLD_HASH_METHOD: 'SHA-256',
OLD_KEY_TYPE: 'RSA-PSS',
MASK_GEN_ALG: 'MGF1',
PSS_SALT_LENGTH: 'BLAH',
OLD_SIGNATURE: 'BLAH'}
self.assertRaisesRegex(exception.SignatureVerificationError,
'Invalid pss_salt_length: .*',
signature_utils.verify_signature,
None, checksum_hash, image_properties)
@removals.remove(message="This will be removed in the N cycle.")
@mock.patch('glance.common.signature_utils.get_public_key')
def test_old_verify_signature_verifier_none(self, mock_get_pub_key):
checksum_hash = '224626ae19824466f2a7f39ab7b80f7f'
mock_get_pub_key.return_value = BadPublicKey()
image_properties = {OLD_CERT_UUID:
'fea14bc2-d75f-4ba5-bccc-b5c924ad0693',
OLD_HASH_METHOD: 'SHA-256',
OLD_KEY_TYPE: 'RSA-PSS',
MASK_GEN_ALG: 'MGF1',
OLD_SIGNATURE: 'BLAH'}
self.assertRaisesRegex(exception.SignatureVerificationError,
'Error occurred while verifying'
' the signature',
signature_utils.verify_signature,
None, checksum_hash, image_properties)
@removals.remove(message="This will be removed in the N cycle.")
@mock.patch('glance.common.signature_utils.get_public_key')
def test_old_verify_signature_unsupported_algorithm(self,
mock_get_pub_key):
checksum_hash = '224626ae19824466f2a7f39ab7b80f7f'
public_key = TEST_RSA_PRIVATE_KEY.public_key()
public_key.verifier = mock.MagicMock(
side_effect=crypto_exception.UnsupportedAlgorithm(
"When OpenSSL is older than 1.0.1 then only SHA1 is "
"supported with MGF1.",
crypto_exception._Reasons.UNSUPPORTED_HASH))
mock_get_pub_key.return_value = public_key
image_properties = {OLD_CERT_UUID:
'fea14bc2-d75f-4ba5-bccc-b5c924ad0693',
OLD_HASH_METHOD: 'SHA-256',
OLD_KEY_TYPE: 'RSA-PSS',
OLD_SIGNATURE: 'BLAH'}
self.assertRaisesRegex(exception.SignatureVerificationError,
'Unable to verify signature since the '
'algorithm is unsupported on this system',
signature_utils.verify_signature,
None, checksum_hash, image_properties)
def test_should_create_verifier(self):
image_props = {CERT_UUID: 'CERT_UUID',
HASH_METHOD: 'HASH_METHOD',

View File

@ -15,8 +15,6 @@
import glance_store
import mock
from debtcollector import removals
from glance.common import exception
from glance.common import signature_utils
import glance.location
@ -190,65 +188,6 @@ class TestStoreImage(utils.BaseTestCase):
self.store_api.get_from_backend,
image.locations[0]['url'], context={})
@removals.remove(message="This will be removed in the N cycle.")
def test_old_image_set_data_valid_signature(self):
context = glance.context.RequestContext(user=USER1)
extra_properties = {
'signature_certificate_uuid': 'UUID',
'signature_hash_method': 'METHOD',
'signature_key_type': 'TYPE',
'signature': 'VALID'
}
image_stub = ImageStub(UUID2, status='queued',
extra_properties=extra_properties)
self.stubs.Set(signature_utils, 'verify_signature',
unit_test_utils.fake_old_verify_signature)
image = glance.location.ImageProxy(image_stub, context,
self.store_api, self.store_utils)
image.set_data('YYYY', 4)
self.assertEqual(UUID2, image.locations[0]['url'])
self.assertEqual('Z', image.checksum)
self.assertEqual('active', image.status)
@removals.remove(message="This will be removed in the N cycle.")
def test_old_image_set_data_invalid_signature(self):
context = glance.context.RequestContext(user=USER1)
extra_properties = {
'signature_certificate_uuid': 'UUID',
'signature_hash_method': 'METHOD',
'signature_key_type': 'TYPE',
'signature': 'INVALID'
}
image_stub = ImageStub(UUID2, status='queued',
extra_properties=extra_properties)
self.stubs.Set(signature_utils, 'verify_signature',
unit_test_utils.fake_old_verify_signature)
image = glance.location.ImageProxy(image_stub, context,
self.store_api, self.store_utils)
self.assertRaises(exception.SignatureVerificationError,
image.set_data,
'YYYY', 4)
@removals.remove(message="This will be removed in the N cycle.")
def test_old_image_set_data_invalid_signature_missing_metadata(self):
context = glance.context.RequestContext(user=USER1)
extra_properties = {
'signature_hash_method': 'METHOD',
'signature_key_type': 'TYPE',
'signature': 'INVALID'
}
image_stub = ImageStub(UUID2, status='queued',
extra_properties=extra_properties)
self.stubs.Set(signature_utils, 'verify_signature',
unit_test_utils.fake_old_verify_signature)
image = glance.location.ImageProxy(image_stub, context,
self.store_api, self.store_utils)
image.set_data('YYYY', 4)
self.assertEqual(UUID2, image.locations[0]['url'])
self.assertEqual('Z', image.checksum)
# Image is still active, since invalid signature was ignored
self.assertEqual('active', image.status)
@mock.patch('glance.location.LOG')
def test_image_set_data_valid_signature(self, mock_log):
context = glance.context.RequestContext(user=USER1)