Как в Node.JS правильно использовать callback?
Все дело в асинхронности. Когда коллбэк выполнится (следующий тик ивент-лупа), цикл уже закончится и i и j достигнут последнего значения.
Тебе нужны промисы.
Если вы хотите оставить эту прелестную вложенность в коде, то можно такvar images = {};
        for (var i in news.items) {
            if(typeof news.items[i].attachments !== undefined) {
                for (var j in news.items[i].attachments)
                    if (news.items[i].attachments[j].type == 'photo') {
                        download(news.items[i].attachments[j].photo.photo_604, 'tmp/' + url.parse(news.items[i].attachments[j].photo.photo_604).pathname.split('/').pop(), function(i, j) {
                            fs.readFile('tmp/' + url.parse(news.items[i].attachments[j].photo.photo_604).pathname.split('/').pop(), function(err, data) {
                                console.log(checksum(data, 'sha1'));
                            });
                        }.bind(i, j));
                    }
            }
        }
Или так:var images = {};
Object.keys(news.items).forEach(function(key) {
    var item = news.items[key];
    if (!Array.isArray(item.attachments)) return;

    item.attachments.forEach(function(attachment) {
        it (attachment.type !== 'photo') return;

        var loc_path = 'tmp/' + url.parse(attachment.photo.photo_604).pathname.split('/').pop();
        download(attachment.photo.photo_604, loc_path, function() {
            fs.readFile(loc_path, function(data) {
                console.log(checksum(data, 'sha1'));
            });
        });
    });
});
Это минимум, который нужен чтобы код стал читаемым.
Дальше я не могу понять зачем вы сначала скачиваете файл в файловую систему, и потом грузите его в память, почему бы сразу не грузить в память. 
Так же рекомендую использовать промисы вместо каллбеков - это сделает код еще более читабельным.