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.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 log = std.log.scoped(.Account); | ||||
| const Self = @This(); | ||||
| 
 | ||||
| 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 | ||||
|     //       environment, check with LocalDB table to get it. We're | ||||
|     //       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); | ||||
|     errdefer allocator.free(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 { | ||||
|     _ = allocator; | ||||
|     _ = access_key; | ||||
|     return "1234, Get your woman, on the floor"; | ||||
|     log.debug("Finding account for access key: '{s}'", .{access_key}); | ||||
|     return "1234"; | ||||
| } | ||||
| /// Function assumes an authenticated request, so signing.verify must be called | ||||
| /// and returned true before calling this function. If authentication header | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue