const std = @import("std");
const common = @import("../common.zig");
const crypto = std.crypto;
const debug = std.debug;
const math = std.math;
const mem = std.mem;
const Field = common.Field;
const NonCanonicalError = std.crypto.errors.NonCanonicalError;
const NotSquareError = std.crypto.errors.NotSquareError;
pub const encoded_length = 48;
pub const CompressedScalar = [encoded_length]u8;
const Fe = Field(.{
.fiat = @import("p384_scalar_64.zig"),
.field_order = 39402006196394479212279040100143613805079739270465446667946905279627659399113263569398956308152294913554433653942643,
.field_bits = 384,
.saturated_bits = 384,
.encoded_length = encoded_length,
});
pub const field_order = Fe.field_order;
pub fn rejectNonCanonical(s: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!void {
return Fe.rejectNonCanonical(s, endian);
}
pub fn reduce64(s: [64]u8, endian: std.builtin.Endian) CompressedScalar {
return ScalarDouble.fromBytes64(s, endian).toBytes(endian);
}
pub fn mul(a: CompressedScalar, b: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!CompressedScalar {
return (try Scalar.fromBytes(a, endian)).mul(try Scalar.fromBytes(b, endian)).toBytes(endian);
}
pub fn mulAdd(a: CompressedScalar, b: CompressedScalar, c: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!CompressedScalar {
return (try Scalar.fromBytes(a, endian)).mul(try Scalar.fromBytes(b, endian)).add(try Scalar.fromBytes(c, endian)).toBytes(endian);
}
pub fn add(a: CompressedScalar, b: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!CompressedScalar {
return (try Scalar.fromBytes(a, endian)).add(try Scalar.fromBytes(b, endian)).toBytes(endian);
}
pub fn neg(s: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!CompressedScalar {
return (try Scalar.fromBytes(s, endian)).neg().toBytes(endian);
}
pub fn sub(a: CompressedScalar, b: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!CompressedScalar {
return (try Scalar.fromBytes(a, endian)).sub(try Scalar.fromBytes(b, endian)).toBytes(endian);
}
pub fn random(endian: std.builtin.Endian) CompressedScalar {
return Scalar.random().toBytes(endian);
}
pub const Scalar = struct {
fe: Fe,
pub const zero = Scalar{ .fe = Fe.zero };
pub const one = Scalar{ .fe = Fe.one };
pub fn fromBytes(s: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!Scalar {
return Scalar{ .fe = try Fe.fromBytes(s, endian) };
}
pub fn fromBytes64(s: [64]u8, endian: std.builtin.Endian) Scalar {
const t = ScalarDouble.fromBytes(512, s, endian);
return t.reduce(512);
}
pub fn toBytes(n: Scalar, endian: std.builtin.Endian) CompressedScalar {
return n.fe.toBytes(endian);
}
pub fn isZero(n: Scalar) bool {
return n.fe.isZero();
}
pub fn equivalent(a: Scalar, b: Scalar) bool {
return a.fe.equivalent(b.fe);
}
pub fn add(x: Scalar, y: Scalar) Scalar {
return Scalar{ .fe = x.fe.add(y.fe) };
}
pub fn sub(x: Scalar, y: Scalar) Scalar {
return Scalar{ .fe = x.fe.sub(y.fe) };
}
pub fn dbl(n: Scalar) Scalar {
return Scalar{ .fe = n.fe.dbl() };
}
pub fn mul(x: Scalar, y: Scalar) Scalar {
return Scalar{ .fe = x.fe.mul(y.fe) };
}
pub fn sq(n: Scalar) Scalar {
return Scalar{ .fe = n.fe.sq() };
}
pub fn pow(a: Scalar, comptime T: type, comptime n: T) Scalar {
return Scalar{ .fe = a.fe.pow(n) };
}
pub fn neg(n: Scalar) Scalar {
return Scalar{ .fe = n.fe.neg() };
}
pub fn invert(n: Scalar) Scalar {
return Scalar{ .fe = n.fe.invert() };
}
pub fn isSquare(n: Scalar) Scalar {
return n.fe.isSquare();
}
pub fn sqrt(n: Scalar) NotSquareError!Scalar {
return Scalar{ .fe = try n.fe.sqrt() };
}
pub fn random() Scalar {
var s: [64]u8 = undefined;
while (true) {
crypto.random.bytes(&s);
const n = Scalar.fromBytes64(s, .Little);
if (!n.isZero()) {
return n;
}
}
}
};
const ScalarDouble = struct {
x1: Fe,
x2: Fe,
fn fromBytes(comptime bits: usize, s_: [bits / 8]u8, endian: std.builtin.Endian) ScalarDouble {
debug.assert(bits > 0 and bits <= 512 and bits >= Fe.saturated_bits and bits <= Fe.saturated_bits * 2);
var s = s_;
if (endian == .Big) {
for (s_) |x, i| s[s.len - 1 - i] = x;
}
var t = ScalarDouble{ .x1 = undefined, .x2 = Fe.zero };
{
var b = [_]u8{0} ** encoded_length;
const len = math.min(s.len, 32);
mem.copy(u8, b[0..len], s[0..len]);
t.x1 = Fe.fromBytes(b, .Little) catch unreachable;
}
if (s_.len >= 32) {
var b = [_]u8{0} ** encoded_length;
const len = math.min(s.len - 32, 32);
mem.copy(u8, b[0..len], s[32..][0..len]);
t.x2 = Fe.fromBytes(b, .Little) catch unreachable;
}
return t;
}
fn reduce(expanded: ScalarDouble, comptime bits: usize) Scalar {
debug.assert(bits > 0 and bits <= Fe.saturated_bits * 3 and bits <= 512);
var fe = expanded.x1;
if (bits >= 256) {
const st1 = Fe.fromInt(1 << 256) catch unreachable;
fe = fe.add(expanded.x2.mul(st1));
}
return Scalar{ .fe = fe };
}
};