Пример #1
0
            def __iter__(self):
                re = r
                sha256 = hashlib.sha256()
                counter = 0
                last_counter = 0
                dgst = None

                while dgst != digest:
                    for chunk in re.iter_content(chunk_size):
                        sha256.update(chunk)
                        counter += 1
                        yield chunk
                    dgst = 'sha256:' + sha256.hexdigest()
                    if dgst != digest:
                        if counter == last_counter:
                            raise exceptions.DXFDigestMismatchError(
                                dgst, digest)

                        last_counter = counter
                        re = dxf_instance._request(
                            'get',
                            'blobs/' + digest,
                            stream=True,
                            headers={
                                "Range":
                                "bytes={}-".format((counter + 1) * chunk_size)
                            })
Пример #2
0
    def get_alias(self, alias=None, manifest=None, verify=True, sizes=False):
        """
        Get the blob hashes assigned to an alias.

        :param alias: Alias name. You almost definitely will only need to pass this argument.
        :type alias: str

        :param manifest: If you previously obtained a manifest, specify it here instead of ``alias``. You almost definitely won't need to do this.
        :type manifest: str

        :param verify: (v1 schema only) Whether to verify the integrity of the alias definition in the registry itself. You almost definitely won't need to change this from the default (``True``).
        :type verify: bool

        :param sizes: Whether to return sizes of the blobs along with their hashes
        :type sizes: bool

        :rtype: list
        :returns: If ``sizes`` is falsey, a list of blob hashes (strings) which are assigned to the alias. If ``sizes`` is truthy, a list of (hash,size) tuples for each blob.
        """
        if alias:
            r = self._request('get',
                              'manifests/' + alias,
                              headers={
                                  'Accept':
                                  _schema2_mimetype + ', ' + _schema1_mimetype
                              })
            manifest = r.content.decode('utf-8')

            dcd = r.headers['docker-content-digest']
        else:
            dcd = None

        parsed_manifest = json.loads(manifest)
        if parsed_manifest['schemaVersion'] == 1:
            dgsts = _verify_manifest(manifest, parsed_manifest, dcd, verify)
            if not sizes:
                return dgsts
            return [(dgst, self.blob_size(dgst)) for dgst in dgsts]
        else:
            if dcd:
                method, expected_dgst = dcd.split(':')
                if method != 'sha256':
                    raise exceptions.DXFUnexpectedDigestMethodError(
                        method, 'sha256')
                hasher = hashlib.new(method)
                hasher.update(r.content)
                dgst = hasher.hexdigest()
                if dgst != expected_dgst:
                    raise exceptions.DXFDigestMismatchError(
                        dgst, expected_dgst)

            r = []
            for layer in parsed_manifest['layers']:
                method, dgst = layer['digest'].split(':')
                if method != 'sha256':
                    raise exceptions.DXFUnexpectedDigestMethodError(
                        method, 'sha256')
                r.append((dgst, layer['size']) if sizes else dgst)
            return r
Пример #3
0
 def __iter__(self):
     sha256 = hashlib.sha256()
     for chunk in r.iter_content(chunk_size):
         sha256.update(chunk)
         yield chunk
     dgst = 'sha256:' + sha256.hexdigest()
     if dgst != digest:
         raise exceptions.DXFDigestMismatchError(dgst, digest)
Пример #4
0
    def _get_alias(self, alias, manifest, verify, sizes, dcd, get_digest):
        # pylint: disable=too-many-arguments
        if alias:
            manifest, r = self.get_manifest_and_response(alias)
            dcd = r.headers['docker-content-digest']

        parsed_manifest = json.loads(manifest)

        if parsed_manifest['schemaVersion'] == 1:
            # https://github.com/docker/distribution/issues/1662#issuecomment-213101772
            # "A schema1 manifest should always produce the same image id but
            # defining the steps to produce directly from the manifest is not
            # straight forward."
            if get_digest:
                raise exceptions.DXFDigestNotAvailableForSchema1()

            r = _verify_manifest(manifest,
                                 parsed_manifest,
                                 dcd,
                                 verify)

            return [(dgst, self.blob_size(dgst)) for dgst in r] if sizes else r

        if dcd:
            method, expected_dgst = split_digest(dcd)
            hasher = hashlib.new(method)
            hasher.update(r.content)
            dgst = hasher.hexdigest()
            if dgst != expected_dgst:
                raise exceptions.DXFDigestMismatchError(
                    method + ':' + dgst,
                    method + ':' + expected_dgst)

        if get_digest:
            dgst = parsed_manifest['config']['digest']
            split_digest(dgst)
            return dgst

        r = []
        for layer in parsed_manifest['layers']:
            dgst = layer['digest']
            split_digest(dgst)
            r.append((dgst, layer['size']) if sizes else dgst)
        return r
Пример #5
0
def _verify_manifest(content, manifest, content_digest=None, verify=True):
    # pylint: disable=too-many-locals,too-many-branches

    # Adapted from https://github.com/joyent/node-docker-registry-client

    if verify or ('signatures' in manifest):
        signatures = []
        for sig in manifest['signatures']:
            protected64 = sig['protected']
            protected = _urlsafe_b64decode(protected64).decode('utf-8')
            protected_header = json.loads(protected)

            format_length = protected_header['formatLength']
            format_tail64 = protected_header['formatTail']
            format_tail = _urlsafe_b64decode(format_tail64).decode('utf-8')

            alg = sig['header']['alg']
            if alg.lower() == 'none':
                raise exceptions.DXFDisallowedSignatureAlgorithmError('none')
            if sig['header'].get('chain'):
                raise exceptions.DXFSignatureChainNotImplementedError()

            signatures.append({
                'alg': alg,
                'signature': sig['signature'],
                'protected64': protected64,
                'key': _import_key(sig['header']['jwk']),
                'format_length': format_length,
                'format_tail': format_tail
            })

        payload = content[:signatures[0]['format_length']] + \
                  signatures[0]['format_tail']
        payload64 = _urlsafe_b64encode(payload)
    else:
        payload = content

    if content_digest:
        method, expected_dgst = split_digest(content_digest)
        hasher = hashlib.new(method)
        hasher.update(payload.encode('utf-8'))
        dgst = hasher.hexdigest()
        if dgst != expected_dgst:
            raise exceptions.DXFDigestMismatchError(
                method + ':' + dgst, method + ':' + expected_dgst)

    if verify:
        for sig in signatures:
            jwstoken = jws.JWS()
            jwstoken.deserialize(
                json.dumps({
                    'payload': payload64,
                    'protected': sig['protected64'],
                    'signature': sig['signature']
                }), sig['key'], sig['alg'])

    dgsts = []
    for layer in manifest['fsLayers']:
        dgst = layer['blobSum']
        split_digest(dgst)
        dgsts.append(dgst)
    return dgsts
Пример #6
0
def _verify_manifest(content, manifest, content_digest=None, verify=True):
    # pylint: disable=too-many-locals,too-many-branches

    # Adapted from https://github.com/joyent/node-docker-registry-client

    if verify or ('signatures' in manifest):
        signatures = []
        for sig in manifest['signatures']:
            protected64 = sig['protected']
            protected = _urlsafe_b64decode(protected64).decode('utf-8')
            protected_header = json.loads(protected)

            format_length = protected_header['formatLength']
            format_tail64 = protected_header['formatTail']
            format_tail = _urlsafe_b64decode(format_tail64).decode('utf-8')

            alg = sig['header']['alg']
            if alg.lower() == 'none':
                raise exceptions.DXFDisallowedSignatureAlgorithmError('none')
            if sig['header'].get('chain'):
                raise exceptions.DXFSignatureChainNotImplementedError()

            signatures.append({
                'alg': alg,
                'signature': sig['signature'],
                'protected64': protected64,
                'key': _jwk_to_key(sig['header']['jwk']),
                'format_length': format_length,
                'format_tail': format_tail
            })

        payload = content[:signatures[0]['format_length']] + \
                  signatures[0]['format_tail']
        payload64 = _urlsafe_b64encode(payload)
    else:
        payload = content

    if content_digest:
        method, expected_dgst = content_digest.split(':')
        if method != 'sha256':
            raise exceptions.DXFUnexpectedDigestMethodError(method, 'sha256')
        hasher = hashlib.new(method)
        hasher.update(payload.encode('utf-8'))
        dgst = hasher.hexdigest()
        if dgst != expected_dgst:
            raise exceptions.DXFDigestMismatchError(dgst, expected_dgst)

    if verify:
        for sig in signatures:
            data = {'key': sig['key'], 'header': {'alg': sig['alg']}}
            jws.header.process(data, 'verify')
            sig64 = sig['signature']
            data['verifier']("%s.%s" % (sig['protected64'], payload64),
                             _urlsafe_b64decode(sig64), sig['key'])

    dgsts = []
    for layer in manifest['fsLayers']:
        method, dgst = layer['blobSum'].split(':')
        if method != 'sha256':
            raise exceptions.DXFUnexpectedDigestMethodError(method, 'sha256')
        dgsts.append(dgst)
    return dgsts