mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 05:44:20 +00:00
We already have a LICENSE file that covers the Zig Standard Library. We no longer need to remind everyone that the license is MIT in every single file. Previously this was introduced to clarify the situation for a fork of Zig that made Zig's LICENSE file harder to find, and replaced it with their own license that required annual payments to their company. However that fork now appears to be dead. So there is no need to reinforce the copyright notice in every single file.
162 lines
7.1 KiB
Zig
162 lines
7.1 KiB
Zig
const std = @import("std");
|
|
const crypto = std.crypto;
|
|
|
|
const IdentityElementError = crypto.errors.IdentityElementError;
|
|
const NonCanonicalError = crypto.errors.NonCanonicalError;
|
|
const WeakPublicKeyError = crypto.errors.WeakPublicKeyError;
|
|
|
|
/// Group operations over Curve25519.
|
|
pub const Curve25519 = struct {
|
|
/// The underlying prime field.
|
|
pub const Fe = @import("field.zig").Fe;
|
|
/// Field arithmetic mod the order of the main subgroup.
|
|
pub const scalar = @import("scalar.zig");
|
|
|
|
x: Fe,
|
|
|
|
/// Decode a Curve25519 point from its compressed (X) coordinates.
|
|
pub inline fn fromBytes(s: [32]u8) Curve25519 {
|
|
return .{ .x = Fe.fromBytes(s) };
|
|
}
|
|
|
|
/// Encode a Curve25519 point.
|
|
pub inline fn toBytes(p: Curve25519) [32]u8 {
|
|
return p.x.toBytes();
|
|
}
|
|
|
|
/// The Curve25519 base point.
|
|
pub const basePoint = Curve25519{ .x = Fe.curve25519BasePoint };
|
|
|
|
/// Check that the encoding of a Curve25519 point is canonical.
|
|
pub fn rejectNonCanonical(s: [32]u8) NonCanonicalError!void {
|
|
return Fe.rejectNonCanonical(s, false);
|
|
}
|
|
|
|
/// Reject the neutral element.
|
|
pub fn rejectIdentity(p: Curve25519) IdentityElementError!void {
|
|
if (p.x.isZero()) {
|
|
return error.IdentityElement;
|
|
}
|
|
}
|
|
|
|
/// Multiply a point by the cofactor
|
|
pub fn clearCofactor(p: Edwards25519) Edwards25519 {
|
|
return p.dbl().dbl().dbl();
|
|
}
|
|
|
|
fn ladder(p: Curve25519, s: [32]u8, comptime bits: usize) IdentityElementError!Curve25519 {
|
|
var x1 = p.x;
|
|
var x2 = Fe.one;
|
|
var z2 = Fe.zero;
|
|
var x3 = x1;
|
|
var z3 = Fe.one;
|
|
var swap: u8 = 0;
|
|
var pos: usize = bits - 1;
|
|
while (true) : (pos -= 1) {
|
|
const bit = (s[pos >> 3] >> @truncate(u3, pos)) & 1;
|
|
swap ^= bit;
|
|
Fe.cSwap2(&x2, &x3, &z2, &z3, swap);
|
|
swap = bit;
|
|
const a = x2.add(z2);
|
|
const b = x2.sub(z2);
|
|
const aa = a.sq();
|
|
const bb = b.sq();
|
|
x2 = aa.mul(bb);
|
|
const e = aa.sub(bb);
|
|
const da = x3.sub(z3).mul(a);
|
|
const cb = x3.add(z3).mul(b);
|
|
x3 = da.add(cb).sq();
|
|
z3 = x1.mul(da.sub(cb).sq());
|
|
z2 = e.mul(bb.add(e.mul32(121666)));
|
|
if (pos == 0) break;
|
|
}
|
|
Fe.cSwap2(&x2, &x3, &z2, &z3, swap);
|
|
z2 = z2.invert();
|
|
x2 = x2.mul(z2);
|
|
if (x2.isZero()) {
|
|
return error.IdentityElement;
|
|
}
|
|
return Curve25519{ .x = x2 };
|
|
}
|
|
|
|
/// Multiply a Curve25519 point by a scalar after "clamping" it.
|
|
/// Clamping forces the scalar to be a multiple of the cofactor in
|
|
/// order to prevent small subgroups attacks. This is the standard
|
|
/// way to use Curve25519 for a DH operation.
|
|
/// Return error.IdentityElement if the resulting point is
|
|
/// the identity element.
|
|
pub fn clampedMul(p: Curve25519, s: [32]u8) IdentityElementError!Curve25519 {
|
|
var t: [32]u8 = s;
|
|
scalar.clamp(&t);
|
|
return try ladder(p, t, 255);
|
|
}
|
|
|
|
/// Multiply a Curve25519 point by a scalar without clamping it.
|
|
/// Return error.IdentityElement if the resulting point is
|
|
/// the identity element or error.WeakPublicKey if the public
|
|
/// key is a low-order point.
|
|
pub fn mul(p: Curve25519, s: [32]u8) (IdentityElementError || WeakPublicKeyError)!Curve25519 {
|
|
const cofactor = [_]u8{8} ++ [_]u8{0} ** 31;
|
|
_ = ladder(p, cofactor, 4) catch return error.WeakPublicKey;
|
|
return try ladder(p, s, 256);
|
|
}
|
|
|
|
/// Compute the Curve25519 equivalent to an Edwards25519 point.
|
|
pub fn fromEdwards25519(p: crypto.ecc.Edwards25519) IdentityElementError!Curve25519 {
|
|
try p.clearCofactor().rejectIdentity();
|
|
const one = crypto.ecc.Edwards25519.Fe.one;
|
|
const x = one.add(p.y).mul(one.sub(p.y).invert()); // xMont=(1+yEd)/(1-yEd)
|
|
return Curve25519{ .x = x };
|
|
}
|
|
};
|
|
|
|
test "curve25519" {
|
|
var s = [32]u8{ 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 };
|
|
const p = try Curve25519.basePoint.clampedMul(s);
|
|
try p.rejectIdentity();
|
|
var buf: [128]u8 = undefined;
|
|
try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&p.toBytes())}), "E6F2A4D1C28EE5C7AD0329268255A468AD407D2672824C0C0EB30EA6EF450145");
|
|
const q = try p.clampedMul(s);
|
|
try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&q.toBytes())}), "3614E119FFE55EC55B87D6B19971A9F4CBC78EFE80BEC55B96392BABCC712537");
|
|
|
|
try Curve25519.rejectNonCanonical(s);
|
|
s[31] |= 0x80;
|
|
try std.testing.expectError(error.NonCanonical, Curve25519.rejectNonCanonical(s));
|
|
}
|
|
|
|
test "curve25519 small order check" {
|
|
var s: [32]u8 = [_]u8{1} ++ [_]u8{0} ** 31;
|
|
const small_order_ss: [7][32]u8 = .{
|
|
.{
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0 (order 4)
|
|
},
|
|
.{
|
|
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1 (order 1)
|
|
},
|
|
.{
|
|
0xe0, 0xeb, 0x7a, 0x7c, 0x3b, 0x41, 0xb8, 0xae, 0x16, 0x56, 0xe3, 0xfa, 0xf1, 0x9f, 0xc4, 0x6a, 0xda, 0x09, 0x8d, 0xeb, 0x9c, 0x32, 0xb1, 0xfd, 0x86, 0x62, 0x05, 0x16, 0x5f, 0x49, 0xb8, 0x00, // 325606250916557431795983626356110631294008115727848805560023387167927233504 (order 8) */
|
|
},
|
|
.{
|
|
0x5f, 0x9c, 0x95, 0xbc, 0xa3, 0x50, 0x8c, 0x24, 0xb1, 0xd0, 0xb1, 0x55, 0x9c, 0x83, 0xef, 0x5b, 0x04, 0x44, 0x5c, 0xc4, 0x58, 0x1c, 0x8e, 0x86, 0xd8, 0x22, 0x4e, 0xdd, 0xd0, 0x9f, 0x11, 0x57, // 39382357235489614581723060781553021112529911719440698176882885853963445705823 (order 8)
|
|
},
|
|
.{
|
|
0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, // p-1 (order 2)
|
|
},
|
|
.{
|
|
0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, // p (=0, order 4)
|
|
},
|
|
.{
|
|
0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, // p+1 (=1, order 1)
|
|
},
|
|
};
|
|
for (small_order_ss) |small_order_s| {
|
|
try std.testing.expectError(error.WeakPublicKey, Curve25519.fromBytes(small_order_s).mul(s));
|
|
var extra = small_order_s;
|
|
extra[31] ^= 0x80;
|
|
try std.testing.expectError(error.WeakPublicKey, Curve25519.fromBytes(extra).mul(s));
|
|
var valid = small_order_s;
|
|
valid[31] = 0x40;
|
|
s[0] = 0;
|
|
try std.testing.expectError(error.IdentityElement, Curve25519.fromBytes(valid).mul(s));
|
|
}
|
|
}
|