Skip to content

Commit

Permalink
feat: add verify shasum
Browse files Browse the repository at this point in the history
  • Loading branch information
hendriknielaender committed Oct 15, 2023
1 parent e0b1bed commit 14b00c4
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 13 deletions.
21 changes: 16 additions & 5 deletions src/download.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const std = @import("std");
const builtin = @import("builtin");
const crypto = @import("std").crypto;
const architecture = @import("architecture.zig");
const progress = @import("progress.zig");
const alias = @import("alias.zig");
Expand All @@ -12,7 +13,7 @@ fn getZvmPathSegment(segment: []const u8) ![]u8 {
return std.fs.path.join(std.heap.page_allocator, &[_][]const u8{ user_home, ".zvm", segment });
}

pub fn content(allocator: std.mem.Allocator, version: []const u8, url: []const u8) !void {
pub fn content(allocator: std.mem.Allocator, version: []const u8, url: []const u8) !?[32]u8 {
const uri = std.Uri.parse(url) catch unreachable;
const version_folder_name = try std.fmt.allocPrint(allocator, "versions/{s}", .{version});
defer allocator.free(version_folder_name);
Expand All @@ -30,10 +31,10 @@ pub fn content(allocator: std.mem.Allocator, version: []const u8, url: []const u
if (confirmUserChoice()) {
try alias.setZigVersion(version);
std.debug.print("Version {s} has been set as the default.\n", .{version});
return;
return null;
} else {
std.debug.print("Aborting...\n", .{});
return;
return null;
}
}

Expand All @@ -45,9 +46,11 @@ pub fn content(allocator: std.mem.Allocator, version: []const u8, url: []const u
const version_path = try getZvmPathSegment("versions");
defer allocator.free(version_path);

try downloadAndExtract(allocator, uri, version_path, version);
const computedHash = try downloadAndExtract(allocator, uri, version_path, version);

try alias.setZigVersion(version);

return computedHash;
}

fn checkExistingVersion(version_path: []const u8) bool {
Expand All @@ -63,7 +66,7 @@ fn confirmUserChoice() bool {
return std.ascii.toLower(buffer[0]) == 'y';
}

fn downloadAndExtract(allocator: std.mem.Allocator, uri: std.Uri, version_path: []const u8, version: []const u8) !void {
fn downloadAndExtract(allocator: std.mem.Allocator, uri: std.Uri, version_path: []const u8, version: []const u8) !?[32]u8 {
var client = std.http.Client{ .allocator = allocator };
defer client.deinit();

Expand Down Expand Up @@ -91,13 +94,18 @@ fn downloadAndExtract(allocator: std.mem.Allocator, uri: std.Uri, version_path:
const file_stream = try zvm_dir.createFile(file_name, .{});
defer file_stream.close();

var sha256 = crypto.sha256.Sha256.init();

while (true) {
var buffer: [8192]u8 = undefined;
const bytes_read = try req.reader().read(buffer[0..]);
if (bytes_read == 0) break;

downloadedBytes += bytes_read;
progress.print(downloadedBytes, totalSize);

sha256.update(buffer[0..bytes_read]);

try file_stream.writeAll(buffer[0..bytes_read]);
}

Expand All @@ -124,7 +132,10 @@ fn downloadAndExtract(allocator: std.mem.Allocator, uri: std.Uri, version_path:
std.debug.print("✓ Successfully renamed {s} to {s}.\n", .{ fx, lastp });
} else |err| {
std.debug.print("✗ Error: Failed to rename {s} to {s}. Reason: {any}\n", .{ fx, lastp, err });
return null;
}

return sha256.final();
}

fn openOrCreateZvmDir() !std.fs.Dir {
Expand Down
38 changes: 37 additions & 1 deletion src/hash.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const std = @import("std");
const crypto = @import("std").crypto;
const testing = std.testing;
const mem = std.mem;

pub fn computeSHA256(buffer: []const u8) [32]u8 {
var sha256 = crypto.hash.sha2.Sha256.init(.{});
Expand All @@ -10,5 +12,39 @@ pub fn computeSHA256(buffer: []const u8) [32]u8 {
}

pub fn verifyHash(computedHash: [32]u8, expectedHash: []const u8) bool {
return std.mem.eql(u8, &computedHash, expectedHash);
return mem.eql(u8, &computedHash, expectedHash);
}

test "computeSHA256 basic test" {
const sample_data = "Hello, Zig!";
const expected_hash_str = "2fb0af70aa67adcc5dc7a41a9e2c1edd34e9de0cf61dddfb4d931477ef99e08e";

var expected_hash: [expected_hash_str.len / 2]u8 = undefined;
_ = try std.fmt.hexToBytes(expected_hash[0..], expected_hash_str);

const computed_hash = computeSHA256(sample_data);

//std.debug.print("Computed: {s}\n", .{std.fmt.fmtSliceHexLower(computed_hash[0..])});
//std.debug.print("Expected: {s}\n", .{expected_hash_str});

try testing.expect(mem.eql(u8, &computed_hash, expected_hash[0..]));
}

test "verifyHash basic test" {
const sample_hash_first: [16]u8 = [_]u8{ 0x33, 0x9a, 0x89, 0xdc, 0x08, 0x73, 0x6b, 0x84, 0xc4, 0x75, 0x2b, 0x3d, 0xed, 0xdc, 0x0f, 0x2c };
const sample_hash_second: [16]u8 = [_]u8{ 0x71, 0xb5, 0x0b, 0x66, 0xa2, 0x68, 0x5f, 0x26, 0x77, 0x9c, 0xbb, 0xac, 0x46, 0x11, 0x1b, 0x68 };
const sample_hash: [32]u8 = sample_hash_first ++ sample_hash_second;

try testing.expect(verifyHash(sample_hash, &sample_hash));
try testing.expect(!verifyHash(sample_hash, "incorrect_hash"));
}

test "computeSHA256 with empty data" {
const empty_data = "";
const expected_hash_first: [16]u8 = [_]u8{ 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24 };
const expected_hash_second: [16]u8 = [_]u8{ 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55 };
const expected_hash: [32]u8 = expected_hash_first ++ expected_hash_second;

const computed_hash = computeSHA256(empty_data);
try testing.expect(mem.eql(u8, &computed_hash, &expected_hash));
}
13 changes: 6 additions & 7 deletions src/install.zig
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,12 @@ pub fn fromVersion(version: []const u8) !void {
std.debug.print("Install {s}\n", .{data.name});

// Download and verify
_ = try download.content(allocator, data.name, data.tarball.?);
//const computedHash: [32]u8 = hash.computeSHA256(content);
//std.debug.print("Computed hash {s}\n", .{computedHash});
//if (!hash.verifyHash(computedHash, data.shasum.?)) {
// return error.HashMismatch;
//}

const content = try download.content(allocator, data.name, data.tarball.?);
const computedHash: [32]u8 = hash.computeSHA256(content);
std.debug.print("Computed hash {s}\n", .{computedHash});
if (!hash.verifyHash(computedHash, data.shasum.?)) {
return error.HashMismatch;
}
} else {
return Error.UnsupportedVersion;
}
Expand Down

0 comments on commit 14b00c4

Please sign in to comment.