zig/lib/std / crypto/pcurves/p384/scalar.zig

Number of bytes required to encode a scalar.

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;

encoded_length

A compressed scalar, in canonical form.


const Field = common.Field;

CompressedScalar

The scalar field order.


const NonCanonicalError = std.crypto.errors.NonCanonicalError;
const NotSquareError = std.crypto.errors.NotSquareError;

field_order

Reject a scalar whose encoding is not canonical.


/// Number of bytes required to encode a scalar.
pub const encoded_length = 48;

rejectNonCanonical()

Reduce a 64-bytes scalar to the field size.


/// A compressed scalar, in canonical form.
pub const CompressedScalar = [encoded_length]u8;

reduce64()

Return a*b (mod L)


const Fe = Field(.{
    .fiat = @import("p384_scalar_64.zig"),
    .field_order = 39402006196394479212279040100143613805079739270465446667946905279627659399113263569398956308152294913554433653942643,
    .field_bits = 384,
    .saturated_bits = 384,
    .encoded_length = encoded_length,
});

mul()

Return a*b+c (mod L)


/// The scalar field order.
pub const field_order = Fe.field_order;

mulAdd()

Return a+b (mod L)


/// Reject a scalar whose encoding is not canonical.
pub fn rejectNonCanonical(s: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!void {
    return Fe.rejectNonCanonical(s, endian);

zero

Return -s (mod L)

}

neg()

Return (a-b) (mod L)


/// Reduce a 64-bytes scalar to the field size.
pub fn reduce64(s: [64]u8, endian: std.builtin.Endian) CompressedScalar {
    return ScalarDouble.fromBytes64(s, endian).toBytes(endian);

zero

Return a random scalar

}

random()

A scalar in unpacked representation.


/// Return a*b (mod L)
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);

zero

Zero.

}

zero

One.


/// Return a*b+c (mod L)
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);

one

Unpack a serialized representation of a scalar.

}

fromBytes()

Reduce a 512 bit input to the field size.


/// Return a+b (mod L)
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);
}

fromBytes64()

Pack a scalar into bytes.


/// Return -s (mod L)
pub fn neg(s: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!CompressedScalar {
    return (try Scalar.fromBytes(s, endian)).neg().toBytes(endian);
}

toBytes()

Return true if the scalar is zero..


/// Return (a-b) (mod L)
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);
}

isZero()

Return true if the scalar is odd.


/// Return a random scalar
pub fn random(endian: std.builtin.Endian) CompressedScalar {
    return Scalar.random().toBytes(endian);
}

isOdd()

Return true if a and b are equivalent.


/// A scalar in unpacked representation.
pub const Scalar = struct {
    fe: Fe,

equivalent()

Compute x+y (mod L)


    /// Zero.
    pub const zero = Scalar{ .fe = Fe.zero };

add()

Compute x-y (mod L)


    /// One.
    pub const one = Scalar{ .fe = Fe.one };

sub()

Compute 2n (mod L)


    /// Unpack a serialized representation of a scalar.
    pub fn fromBytes(s: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!Scalar {
        return Scalar{ .fe = try Fe.fromBytes(s, endian) };
    }

dbl()

Compute x*y (mod L)


    /// Reduce a 512 bit input to the field size.
    pub fn fromBytes64(s: [64]u8, endian: std.builtin.Endian) Scalar {
        const t = ScalarDouble.fromBytes(512, s, endian);
        return t.reduce(512);
    }

mul()

Compute x^2 (mod L)


    /// Pack a scalar into bytes.
    pub fn toBytes(n: Scalar, endian: std.builtin.Endian) CompressedScalar {
        return n.fe.toBytes(endian);
    }

sq()

Compute x^n (mod L)


    /// Return true if the scalar is zero..
    pub fn isZero(n: Scalar) bool {
        return n.fe.isZero();
    }

pow()

Compute -x (mod L)


    /// Return true if the scalar is odd.
    pub fn isOdd(n: Scalar) bool {
        return n.fe.isOdd();
    }

neg()

Compute x^-1 (mod L)


    /// Return true if a and b are equivalent.
    pub fn equivalent(a: Scalar, b: Scalar) bool {
        return a.fe.equivalent(b.fe);
    }

invert()

Return true if n is a quadratic residue mod L.


    /// Compute x+y (mod L)
    pub fn add(x: Scalar, y: Scalar) Scalar {
        return Scalar{ .fe = x.fe.add(y.fe) };
    }

isSquare()

Return the square root of L, or NotSquare if there isn't any solutions.


    /// Compute x-y (mod L)
    pub fn sub(x: Scalar, y: Scalar) Scalar {
        return Scalar{ .fe = x.fe.sub(y.fe) };
    }

sqrt()

Return a random scalar < L.


    /// Compute 2n (mod L)
    pub fn dbl(n: Scalar) Scalar {
        return Scalar{ .fe = n.fe.dbl() };
    }

random()


    /// Compute x*y (mod L)
    pub fn mul(x: Scalar, y: Scalar) Scalar {
        return Scalar{ .fe = x.fe.mul(y.fe) };
    }

    /// Compute x^2 (mod L)
    pub fn sq(n: Scalar) Scalar {
        return Scalar{ .fe = n.fe.sq() };
    }

    /// Compute x^n (mod L)
    pub fn pow(a: Scalar, comptime T: type, comptime n: T) Scalar {
        return Scalar{ .fe = a.fe.pow(n) };
    }

    /// Compute -x (mod L)
    pub fn neg(n: Scalar) Scalar {
        return Scalar{ .fe = n.fe.neg() };
    }

    /// Compute x^-1 (mod L)
    pub fn invert(n: Scalar) Scalar {
        return Scalar{ .fe = n.fe.invert() };
    }

    /// Return true if n is a quadratic residue mod L.
    pub fn isSquare(n: Scalar) Scalar {
        return n.fe.isSquare();
    }

    /// Return the square root of L, or NotSquare if there isn't any solutions.
    pub fn sqrt(n: Scalar) NotSquareError!Scalar {
        return Scalar{ .fe = try n.fe.sqrt() };
    }

    /// Return a random scalar < L.
    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_, 0..) |x, i| s[s.len - 1 - i] = x;
        }
        var t = ScalarDouble{ .x1 = undefined, .x2 = Fe.zero };
        {
            var b = [_]u8{0} ** encoded_length;
            const len = @min(s.len, 32);
            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 = @min(s.len - 32, 32);
            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 };
    }
};