Type Function Uint [src]
An unsigned big integer with a fixed maximum size (max_bits), suitable for cryptographic operations.
Unless side-channels mitigations are explicitly disabled, operations are designed to be constant-time.
Prototype
pub fn Uint(comptime max_bits: comptime_int) type
Parameters
max_bits: comptime_int
Source
pub fn Uint(comptime max_bits: comptime_int) type {
comptime assert(@bitSizeOf(Limb) % 8 == 0); // Limb size must be a multiple of 8
return struct {
const Self = @This();
const max_limbs_count = math.divCeil(usize, max_bits, t_bits) catch unreachable;
limbs_buffer: [max_limbs_count]Limb,
/// The number of active limbs.
limbs_len: usize,
/// Number of bytes required to serialize an integer.
pub const encoded_bytes = math.divCeil(usize, max_bits, 8) catch unreachable;
/// Constant slice of active limbs.
fn limbsConst(self: *const Self) []const Limb {
return self.limbs_buffer[0..self.limbs_len];
}
/// Mutable slice of active limbs.
fn limbs(self: *Self) []Limb {
return self.limbs_buffer[0..self.limbs_len];
}
// Removes limbs whose value is zero from the active limbs.
fn normalize(self: Self) Self {
var res = self;
if (self.limbs_len < 2) {
return res;
}
var i = self.limbs_len - 1;
while (i > 0 and res.limbsConst()[i] == 0) : (i -= 1) {}
res.limbs_len = i + 1;
assert(res.limbs_len <= res.limbs_buffer.len);
return res;
}
/// The zero integer.
pub const zero: Self = .{
.limbs_buffer = [1]Limb{0} ** max_limbs_count,
.limbs_len = max_limbs_count,
};
/// Creates a new big integer from a primitive type.
/// This function may not run in constant time.
pub fn fromPrimitive(comptime T: type, init_value: T) OverflowError!Self {
var x = init_value;
var out: Self = .{
.limbs_buffer = undefined,
.limbs_len = max_limbs_count,
};
for (&out.limbs_buffer) |*limb| {
limb.* = if (@bitSizeOf(T) > t_bits) @as(TLimb, @truncate(x)) else x;
x = math.shr(T, x, t_bits);
}
if (x != 0) {
return error.Overflow;
}
return out;
}
/// Converts a big integer to a primitive type.
/// This function may not run in constant time.
pub fn toPrimitive(self: Self, comptime T: type) OverflowError!T {
var x: T = 0;
var i = self.limbs_len - 1;
while (true) : (i -= 1) {
if (@bitSizeOf(T) >= t_bits and math.shr(T, x, @bitSizeOf(T) - t_bits) != 0) {
return error.Overflow;
}
x = math.shl(T, x, t_bits);
const v = math.cast(T, self.limbsConst()[i]) orelse return error.Overflow;
x |= v;
if (i == 0) break;
}
return x;
}
/// Encodes a big integer into a byte array.
pub fn toBytes(self: Self, bytes: []u8, comptime endian: Endian) OverflowError!void {
if (bytes.len == 0) {
if (self.isZero()) return;
return error.Overflow;
}
@memset(bytes, 0);
var shift: usize = 0;
var out_i: usize = switch (endian) {
.big => bytes.len - 1,
.little => 0,
};
for (0..self.limbs_len) |i| {
var remaining_bits = t_bits;
var limb = self.limbsConst()[i];
while (remaining_bits >= 8) {
bytes[out_i] |= math.shl(u8, @as(u8, @truncate(limb)), shift);
const consumed = 8 - shift;
limb >>= @as(u4, @truncate(consumed));
remaining_bits -= consumed;
shift = 0;
switch (endian) {
.big => {
if (out_i == 0) {
if (i != self.limbs_len - 1 or limb != 0) {
return error.Overflow;
}
return;
}
out_i -= 1;
},
.little => {
out_i += 1;
if (out_i == bytes.len) {
if (i != self.limbs_len - 1 or limb != 0) {
return error.Overflow;
}
return;
}
},
}
}
bytes[out_i] |= @as(u8, @truncate(limb));
shift = remaining_bits;
}
}
/// Creates a new big integer from a byte array.
pub fn fromBytes(bytes: []const u8, comptime endian: Endian) OverflowError!Self {
if (bytes.len == 0) return Self.zero;
var shift: usize = 0;
var out = Self.zero;
var out_i: usize = 0;
var i: usize = switch (endian) {
.big => bytes.len - 1,
.little => 0,
};
while (true) {
const bi = bytes[i];
out.limbs()[out_i] |= math.shl(Limb, bi, shift);
shift += 8;
if (shift >= t_bits) {
shift -= t_bits;
out.limbs()[out_i] = @as(TLimb, @truncate(out.limbs()[out_i]));
const overflow = math.shr(Limb, bi, 8 - shift);
out_i += 1;
if (out_i >= out.limbs_len) {
if (overflow != 0 or i != 0) {
return error.Overflow;
}
break;
}
out.limbs()[out_i] = overflow;
}
switch (endian) {
.big => {
if (i == 0) break;
i -= 1;
},
.little => {
i += 1;
if (i == bytes.len) break;
},
}
}
return out;
}
/// Returns `true` if both integers are equal.
pub fn eql(x: Self, y: Self) bool {
return crypto.timing_safe.eql([max_limbs_count]Limb, x.limbs_buffer, y.limbs_buffer);
}
/// Compares two integers.
pub fn compare(x: Self, y: Self) math.Order {
return crypto.timing_safe.compare(
Limb,
x.limbsConst(),
y.limbsConst(),
.little,
);
}
/// Returns `true` if the integer is zero.
pub fn isZero(x: Self) bool {
var t: Limb = 0;
for (x.limbsConst()) |elem| {
t |= elem;
}
return ct.eql(t, 0);
}
/// Returns `true` if the integer is odd.
pub fn isOdd(x: Self) bool {
return @as(u1, @truncate(x.limbsConst()[0])) != 0;
}
/// Adds `y` to `x`, and returns `true` if the operation overflowed.
pub fn addWithOverflow(x: *Self, y: Self) u1 {
return x.conditionalAddWithOverflow(true, y);
}
/// Subtracts `y` from `x`, and returns `true` if the operation overflowed.
pub fn subWithOverflow(x: *Self, y: Self) u1 {
return x.conditionalSubWithOverflow(true, y);
}
// Replaces the limbs of `x` with the limbs of `y` if `on` is `true`.
fn cmov(x: *Self, on: bool, y: Self) void {
for (x.limbs(), y.limbsConst()) |*x_limb, y_limb| {
x_limb.* = ct.select(on, y_limb, x_limb.*);
}
}
// Adds `y` to `x` if `on` is `true`, and returns `true` if the
// operation overflowed.
fn conditionalAddWithOverflow(x: *Self, on: bool, y: Self) u1 {
var carry: u1 = 0;
for (x.limbs(), y.limbsConst()) |*x_limb, y_limb| {
const res = x_limb.* + y_limb + carry;
x_limb.* = ct.select(on, @as(TLimb, @truncate(res)), x_limb.*);
carry = @truncate(res >> t_bits);
}
return carry;
}
// Subtracts `y` from `x` if `on` is `true`, and returns `true` if the
// operation overflowed.
fn conditionalSubWithOverflow(x: *Self, on: bool, y: Self) u1 {
var borrow: u1 = 0;
for (x.limbs(), y.limbsConst()) |*x_limb, y_limb| {
const res = x_limb.* -% y_limb -% borrow;
x_limb.* = ct.select(on, @as(TLimb, @truncate(res)), x_limb.*);
borrow = @truncate(res >> t_bits);
}
return borrow;
}
};
}