add credential generation
This commit is contained in:
		
							parent
							
								
									d4911b3bc1
								
							
						
					
					
						commit
						4b0284a778
					
				
					 3 changed files with 94 additions and 3 deletions
				
			
		
							
								
								
									
										87
									
								
								build.zig
									
										
									
									
									
								
							
							
						
						
									
										87
									
								
								build.zig
									
										
									
									
									
								
							|  | @ -89,4 +89,91 @@ pub fn build(b: *std.Build) !void { | ||||||
|         cs.addIncludePath(.{ .path = "c" }); |         cs.addIncludePath(.{ .path = "c" }); | ||||||
|         cs.linkLibrary(sqlite_dep.artifact("sqlite")); |         cs.linkLibrary(sqlite_dep.artifact("sqlite")); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     var creds_step = b.step("generate_credentials", "Generate credentials for access_keys.csv"); | ||||||
|  |     creds_step.makeFn = generateCredentials; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn generateCredentials(s: *std.build.Step, prog_node: *std.Progress.Node) error{ MakeFailed, MakeSkipped }!void { | ||||||
|  |     // Format: | ||||||
|  |     // Access Key,Account Id,Existing encoded encryption key, New encoded encryption | ||||||
|  |     _ = prog_node; | ||||||
|  |     const encryption = @import("src/encryption.zig"); | ||||||
|  |     var key: [encryption.encoded_key_length]u8 = undefined; | ||||||
|  |     encryption.randomEncodedKey(&key); | ||||||
|  | 
 | ||||||
|  |     const seed = @as(u64, @truncate(@as(u128, @bitCast(std.time.nanoTimestamp())))); | ||||||
|  |     var prng = std.rand.DefaultPrng.init(seed); | ||||||
|  |     var rand = prng.random(); | ||||||
|  |     const account_number = rand.intRangeAtMost(u64, 100000000000, 999999999999); | ||||||
|  |     const access_key_suffix: u128 = blk: { // workaround for u64 max on rand.intRangeAtMost | ||||||
|  |         const min = 0xECFF3BCC40CA2000000000; | ||||||
|  |         // const max = 0x2153E468B91C6E0000000000; | ||||||
|  |         // const diff = max - min; // 0x2066e52cecdba40000000000 (is 12 bytes/96 bits) | ||||||
|  |         // So we can use a full 64 bit range and just add to the min | ||||||
|  |         break :blk @as(u128, rand.int(u64)) + min; | ||||||
|  |     }; | ||||||
|  |     const access_key_suffix_encoded = encode( | ||||||
|  |         u128, | ||||||
|  |         s.owner.allocator, | ||||||
|  |         access_key_suffix, | ||||||
|  |     ) catch return error.MakeFailed; | ||||||
|  |     var secret_key: [30]u8 = undefined; | ||||||
|  |     rand.bytes(&secret_key); // The rest don't need to be cryptographically secure...does this? | ||||||
|  |     var encoded_secret: [40]u8 = undefined; | ||||||
|  |     _ = std.base64.standard.Encoder.encode(&encoded_secret, secret_key[0..]); | ||||||
|  | 
 | ||||||
|  |     std.debug.print( | ||||||
|  |         "access_key: EL{s}, secret_key: {s}, account_number: {d}, db_encryption_key: {s}", | ||||||
|  |         .{ | ||||||
|  |             access_key_suffix_encoded, | ||||||
|  |             encoded_secret, | ||||||
|  |             account_number, | ||||||
|  |             key, | ||||||
|  |         }, | ||||||
|  |     ); | ||||||
|  |     // Documentation describes account id as a 12 digit number: | ||||||
|  |     // https://docs.aws.amazon.com/accounts/latest/reference/manage-acct-identifiers.html | ||||||
|  |     // Random u64 | ||||||
|  |     // Max: 0x3b9ac9ff (0d999999999) | ||||||
|  |     // Min: 0x05f5e100 (0d100000000) | ||||||
|  |     // | ||||||
|  |     // Access key and secret key are probably more loose. Here is one: | ||||||
|  |     // | ||||||
|  |     //     "AccessKey": { | ||||||
|  |     //     "AccessKeyId": "AKIAYAM4POHXNMQUDBNG", | ||||||
|  |     //     "SecretAccessKey": "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", | ||||||
|  |     // } | ||||||
|  |     // Access key appears 20 characters A-Z, 0-9. Starts with AK or AS, so | ||||||
|  |     // 18 characters of random, and it looks like base36 | ||||||
|  |     // https://ziglang.org/documentation/0.11.0/std/src/std/base64.zig.html | ||||||
|  |     // https://en.wikipedia.org/wiki/Base36 | ||||||
|  |     // For 18 characters, the lower end would be: | ||||||
|  |     // NN100000000000000000 (hex: ECFF3BCC40CA2000000000) | ||||||
|  |     // Upper: | ||||||
|  |     // NNZZZZZZZZZZZZZZZZZZ (hex: 2153E468B91C6E0000000000) | ||||||
|  |     // Which can be stored in u24 | ||||||
|  |     // Secret key here is 40 characters and roughly looks like base64 encoded | ||||||
|  |     // random binary data, which it probably is. 40 characters of base64 is 32 bytes of data | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// encodes an unsigned integer into base36 | ||||||
|  | pub fn encode(comptime T: type, allocator: std.mem.Allocator, data: T) ![]const u8 { | ||||||
|  |     const alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | ||||||
|  |     const ti = @typeInfo(T); | ||||||
|  |     if (ti != .Int or ti.Int.signedness != .unsigned) | ||||||
|  |         @compileError("encode only works with unsigned integers"); | ||||||
|  |     const bits = ti.Int.bits; | ||||||
|  |     // We cannot have more than 6 bits (2^6 = 64) represented per byte in our final output | ||||||
|  |     var al = try std.ArrayList(u8).initCapacity(allocator, bits / 6); | ||||||
|  |     defer al.deinit(); | ||||||
|  | 
 | ||||||
|  |     var remaining = data; | ||||||
|  |     while (remaining > 0) : (remaining /= 36) { | ||||||
|  |         al.appendAssumeCapacity(alphabet[@as(usize, @intCast(remaining % 36))]); | ||||||
|  |     } | ||||||
|  |     // This is not exact, but 6 bits | ||||||
|  |     var rc = try al.toOwnedSlice(); | ||||||
|  |     std.mem.reverse(u8, rc); | ||||||
|  |     return rc; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ const sqlite = @import("sqlite"); // TODO: If we use this across all services, A | ||||||
| 
 | 
 | ||||||
| const test_account_key = "09aGW6z6QofVsPlWP9FGqVnshxHWAWrKZwLkwkgWs7w="; | const test_account_key = "09aGW6z6QofVsPlWP9FGqVnshxHWAWrKZwLkwkgWs7w="; | ||||||
| 
 | 
 | ||||||
|  | const log = std.log.scoped(.Account); | ||||||
| const Self = @This(); | const Self = @This(); | ||||||
| 
 | 
 | ||||||
| allocator: std.mem.Allocator, | allocator: std.mem.Allocator, | ||||||
|  | @ -13,7 +14,10 @@ pub fn accountForId(allocator: std.mem.Allocator, account_id: []const u8) !Self | ||||||
|     // TODO: Allow environment variables to house encoded keys. If not in the |     // TODO: Allow environment variables to house encoded keys. If not in the | ||||||
|     //       environment, check with LocalDB table to get it. We're |     //       environment, check with LocalDB table to get it. We're | ||||||
|     //       building LocalDB, though, so we need that working first... |     //       building LocalDB, though, so we need that working first... | ||||||
|     if (!std.mem.eql(u8, account_id, "1234")) return error.NotImplemented; |     if (!std.mem.eql(u8, account_id, "1234")) { | ||||||
|  |         log.err("Got account id '{s}', but only '1234' is valid right now", .{account_id}); | ||||||
|  |         return error.NotImplemented; | ||||||
|  |     } | ||||||
|     var key = try allocator.alloc(u8, encryption.key_length); |     var key = try allocator.alloc(u8, encryption.key_length); | ||||||
|     errdefer allocator.free(key); |     errdefer allocator.free(key); | ||||||
|     try encryption.decodeKey(key[0..encryption.key_length], test_account_key.*); |     try encryption.decodeKey(key[0..encryption.key_length], test_account_key.*); | ||||||
|  |  | ||||||
|  | @ -127,8 +127,8 @@ fn getCreds(access: []const u8) ?signing.Credentials { | ||||||
| 
 | 
 | ||||||
| fn accountForAccessKey(allocator: std.mem.Allocator, access_key: []const u8) ![]const u8 { | fn accountForAccessKey(allocator: std.mem.Allocator, access_key: []const u8) ![]const u8 { | ||||||
|     _ = allocator; |     _ = allocator; | ||||||
|     _ = access_key; |     log.debug("Finding account for access key: '{s}'", .{access_key}); | ||||||
|     return "1234, Get your woman, on the floor"; |     return "1234"; | ||||||
| } | } | ||||||
| /// Function assumes an authenticated request, so signing.verify must be called | /// Function assumes an authenticated request, so signing.verify must be called | ||||||
| /// and returned true before calling this function. If authentication header | /// and returned true before calling this function. If authentication header | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue