hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit b93bc49b99c75182e3281863d16e21d3c36a5f1c
parent a2447c471d352eae8fcd272bab37291e52644ff3
Author: Thomas Bracht Laumann Jespersen <t@laumann.xyz>
Date:   Thu,  4 Nov 2021 12:39:28 +0100

crypto/curve25519: New module

This implementation is based on the Martin Kleppmann paper "Implementing
Curve25519/X25519: A Tutorial on Elliptic Curve Cryptography" [0] in
which the author outlines a C implementation that is both fast and
constant-time. The C implementation itself is based on TweetNaCl [1].

Different implementations were consulted for inputs on this
implementation, in particular the Go implementation [1] and the OpenSSL
implementation [2].

[0]: https://martin.kleppmann.com/papers/curve25519.pdf
[1]: https://tweetnacl.cr.yp.to/
[2]: https://github.com/golang/crypto/blob/master/curve25519/curve25519.go
[3]: https://github.com/openssl/openssl/blob/master/crypto/ec/curve25519.c

Diffstat:
Acrypto/curve25519/+test.ha | 229+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrypto/curve25519/README | 5+++++
Acrypto/curve25519/curve25519.ha | 204+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mscripts/gen-stdlib | 12++++++++++++
Mstdlib.mk | 33+++++++++++++++++++++++++++++++++
5 files changed, 483 insertions(+), 0 deletions(-)

diff --git a/crypto/curve25519/+test.ha b/crypto/curve25519/+test.ha @@ -0,0 +1,229 @@ +use bytes; +use fmt; +use io; +use encoding::hex; +use crypto::random; + +@test fn unpack25519() void = { + let in: [SCALARSZ]u8 = [1u8...]; + + let fe = unpack25519(&in); + let expected: elem = [257...]; + + for (let i = 0z; i < FIELDSZ; i += 1) { + assert(fe[i] == expected[i]); + }; +}; + +@test fn subfe() void = { + let in: [SCALARSZ]u8 = [1u8...]; + let in2: [SCALARSZ]u8 = [3u8...]; + + let fe = unpack25519(&in); + let fe2 = unpack25519(&in2); + + let out: elem = [0...]; + + subfe(&out, &fe2, &fe); + + let e2: elem = [514...]; + for (let i = 0z; i < FIELDSZ; i += 1) { + assert(out[i] == e2[i]); + }; +}; + +@test fn swap25519() void = { + let pin: [SCALARSZ]u8 = [1u8...]; + let qin: [SCALARSZ]u8 = [3u8...]; + + let p = unpack25519(&pin); + let q = unpack25519(&qin); + + swap25519(&p, &q, 1); + + let expected = unpack25519(&qin); + for (let i = 0z; i < FIELDSZ; i += 1) { + assert(p[i] == expected[i]); + }; + + expected = unpack25519(&pin); + for (let i = 0z; i < FIELDSZ; i += 1) { + assert(q[i] == expected[i]); + }; + + swap25519(&p, &q, 0); // nop + + let expected = unpack25519(&qin); + for (let i = 0z; i < FIELDSZ; i += 1) { + assert(p[i] == expected[i]); + }; + + expected = unpack25519(&pin); + for (let i = 0z; i < FIELDSZ; i += 1) { + assert(q[i] == expected[i]); + }; +}; + +@test fn unpackpack() void = { + let in: [SCALARSZ]u8 = [1u8...]; + + let fe = unpack25519(&in); + let expected: elem = [257...]; + + for (let i = 0z; i < FIELDSZ; i += 1) { + assert(fe[i] == expected[i]); + }; + + let out: [SCALARSZ]u8 = [0u8...]; + pack25519(&out, &fe); + + for (let i = 0z; i < FIELDSZ; i += 1) { + assert(in[i] == out[i]); + }; + +}; + +@test fn x25519basepoint() void = { + let x: [SCALARSZ]u8 = [1, 0...]; + let out: [SCALARSZ]u8 = [0...]; + + for (let i = 0z; i < 200; i += 1) { + x25519(&out, &x, &BASEPOINT); + x[..] = out[..]; + }; + + let hex = hex::encodestr(out[..]); + const expectedhex = "89161fde887b2b53de549af483940106ecc114d6982daa98256de23bdf77661a"; + if (hex != expectedhex) { + fmt::errorfln("\ngot {}, want {}", hex, expectedhex)!; + abort(); + }; + +}; + +@test fn vectors() void = { + const vectors = [( + [0x66u8, 0x8f, 0xb9, 0xf7, 0x6a, 0xd9, 0x71, 0xc8, 0x1a, 0xc9, 0x0, 0x7, 0x1a, 0x15, 0x60, 0xbc, 0xe2, 0xca, 0x0, 0xca, 0xc7, 0xe6, 0x7a, 0xf9, 0x93, 0x48, 0x91, 0x37, 0x61, 0x43, 0x40, 0x14], // in + [0xdbu8, 0x5f, 0x32, 0xb7, 0xf8, 0x41, 0xe7, 0xa1, 0xa0, 0x9, 0x68, 0xef, 0xfd, 0xed, 0x12, 0x73, 0x5f, 0xc4, 0x7a, 0x3e, 0xb1, 0x3b, 0x57, 0x9a, 0xac, 0xad, 0xea, 0xe8, 0x9, 0x39, 0xa7, 0xdd], // base + [0x9u8, 0xd, 0x85, 0xe5, 0x99, 0xea, 0x8e, 0x2b, 0xee, 0xb6, 0x13, 0x4, 0xd3, 0x7b, 0xe1, 0xe, 0xc5, 0xc9, 0x5, 0xf9, 0x92, 0x7d, 0x32, 0xf4, 0x2a, 0x9a, 0xa, 0xfb, 0x3e, 0xb, 0x40, 0x74], // expect + ), + ( + [0x63, 0x66, 0x95, 0xe3, 0x4f, 0x75, 0xb9, 0xa2, 0x79, 0xc8, 0x70, 0x6f, 0xad, 0x12, 0x89, 0xf2, 0xc0, 0xb1, 0xe2, 0x2e, 0x16, 0xf8, 0xb8, 0x86, 0x17, 0x29, 0xc1, 0xa, 0x58, 0x29, 0x58, 0xaf], + [0x9, 0xd, 0x7, 0x1, 0xf8, 0xfd, 0xe2, 0x8f, 0x70, 0x4, 0x3b, 0x83, 0xf2, 0x34, 0x62, 0x25, 0x41, 0x9b, 0x18, 0xa7, 0xf2, 0x7e, 0x9e, 0x3d, 0x2b, 0xfd, 0x4, 0xe1, 0xf, 0x3d, 0x21, 0x3e], + [0xbf, 0x26, 0xec, 0x7e, 0xc4, 0x13, 0x6, 0x17, 0x33, 0xd4, 0x40, 0x70, 0xea, 0x67, 0xca, 0xb0, 0x2a, 0x85, 0xdc, 0x1b, 0xe8, 0xcf, 0xe1, 0xff, 0x73, 0xd5, 0x41, 0xcc, 0x8, 0x32, 0x55, 0x6], + ), + ( + [0x73, 0x41, 0x81, 0xcd, 0x1a, 0x94, 0x6, 0x52, 0x2a, 0x56, 0xfe, 0x25, 0xe4, 0x3e, 0xcb, 0xf0, 0x29, 0x5d, 0xb5, 0xdd, 0xd0, 0x60, 0x9b, 0x3c, 0x2b, 0x4e, 0x79, 0xc0, 0x6f, 0x8b, 0xd4, 0x6d], + [0xf8, 0xa8, 0x42, 0x1c, 0x7d, 0x21, 0xa9, 0x2d, 0xb3, 0xed, 0xe9, 0x79, 0xe1, 0xfa, 0x6a, 0xcb, 0x6, 0x2b, 0x56, 0xb1, 0x88, 0x5c, 0x71, 0xc5, 0x11, 0x53, 0xcc, 0xb8, 0x80, 0xac, 0x73, 0x15], + [0x11, 0x76, 0xd0, 0x16, 0x81, 0xf2, 0xcf, 0x92, 0x9d, 0xa2, 0xc7, 0xa3, 0xdf, 0x66, 0xb5, 0xd7, 0x72, 0x9f, 0xd4, 0x22, 0x22, 0x6f, 0xd6, 0x37, 0x42, 0x16, 0xbf, 0x7e, 0x2, 0xfd, 0xf, 0x62], + ), + ( + [0x1f, 0x70, 0x39, 0x1f, 0x6b, 0xa8, 0x58, 0x12, 0x94, 0x13, 0xbd, 0x80, 0x1b, 0x12, 0xac, 0xbf, 0x66, 0x23, 0x62, 0x82, 0x5c, 0xa2, 0x50, 0x9c, 0x81, 0x87, 0x59, 0xa, 0x2b, 0xe, 0x61, 0x72], + [0xd3, 0xea, 0xd0, 0x7a, 0x0, 0x8, 0xf4, 0x45, 0x2, 0xd5, 0x80, 0x8b, 0xff, 0xc8, 0x97, 0x9f, 0x25, 0xa8, 0x59, 0xd5, 0xad, 0xf4, 0x31, 0x2e, 0xa4, 0x87, 0x48, 0x9c, 0x30, 0xe0, 0x1b, 0x3b], + [0xf8, 0x48, 0x2f, 0x2e, 0x9e, 0x58, 0xbb, 0x6, 0x7e, 0x86, 0xb2, 0x87, 0x24, 0xb3, 0xc0, 0xa3, 0xbb, 0xb5, 0x7, 0x3e, 0x4c, 0x6a, 0xcd, 0x93, 0xdf, 0x54, 0x5e, 0xff, 0xdb, 0xba, 0x50, 0x5f], + ), + ( + [0x3a, 0x7a, 0xe6, 0xcf, 0x8b, 0x88, 0x9d, 0x2b, 0x7a, 0x60, 0xa4, 0x70, 0xad, 0x6a, 0xd9, 0x99, 0x20, 0x6b, 0xf5, 0x7d, 0x90, 0x30, 0xdd, 0xf7, 0xf8, 0x68, 0xc, 0x8b, 0x1a, 0x64, 0x5d, 0xaa], + [0x4d, 0x25, 0x4c, 0x80, 0x83, 0xd8, 0x7f, 0x1a, 0x9b, 0x3e, 0xa7, 0x31, 0xef, 0xcf, 0xf8, 0xa6, 0xf2, 0x31, 0x2d, 0x6f, 0xed, 0x68, 0xe, 0xf8, 0x29, 0x18, 0x51, 0x61, 0xc8, 0xfc, 0x50, 0x60], + [0x47, 0xb3, 0x56, 0xd5, 0x81, 0x8d, 0xe8, 0xef, 0xac, 0x77, 0x4b, 0x71, 0x4c, 0x42, 0xc4, 0x4b, 0xe6, 0x85, 0x23, 0xdd, 0x57, 0xdb, 0xd7, 0x39, 0x62, 0xd5, 0xa5, 0x26, 0x31, 0x87, 0x62, 0x37], + ), + ( + [0x20, 0x31, 0x61, 0xc3, 0x15, 0x9a, 0x87, 0x6a, 0x2b, 0xea, 0xec, 0x29, 0xd2, 0x42, 0x7f, 0xb0, 0xc7, 0xc3, 0xd, 0x38, 0x2c, 0xd0, 0x13, 0xd2, 0x7c, 0xc3, 0xd3, 0x93, 0xdb, 0xd, 0xaf, 0x6f], + [0x6a, 0xb9, 0x5d, 0x1a, 0xbe, 0x68, 0xc0, 0x9b, 0x0, 0x5c, 0x3d, 0xb9, 0x4, 0x2c, 0xc9, 0x1a, 0xc8, 0x49, 0xf7, 0xe9, 0x4a, 0x2a, 0x4a, 0x9b, 0x89, 0x36, 0x78, 0x97, 0xb, 0x7b, 0x95, 0xbf], + [0x11, 0xed, 0xae, 0xdc, 0x95, 0xff, 0x78, 0xf5, 0x63, 0xa1, 0xc8, 0xf1, 0x55, 0x91, 0xc0, 0x71, 0xde, 0xa0, 0x92, 0xb4, 0xd7, 0xec, 0xaa, 0xc8, 0xe0, 0x38, 0x7b, 0x5a, 0x16, 0xc, 0x4e, 0x5d], + ), + ( + [0x13, 0xd6, 0x54, 0x91, 0xfe, 0x75, 0xf2, 0x3, 0xa0, 0x8, 0xb4, 0x41, 0x5a, 0xbc, 0x60, 0xd5, 0x32, 0xe6, 0x95, 0xdb, 0xd2, 0xf1, 0xe8, 0x3, 0xac, 0xcb, 0x34, 0xb2, 0xb7, 0x2c, 0x3d, 0x70], + [0x2e, 0x78, 0x4e, 0x4, 0xca, 0x0, 0x73, 0x33, 0x62, 0x56, 0xa8, 0x39, 0x25, 0x5e, 0xd2, 0xf7, 0xd4, 0x79, 0x6a, 0x64, 0xcd, 0xc3, 0x7f, 0x1e, 0xb0, 0xe5, 0xc4, 0xc8, 0xd1, 0xd1, 0xe0, 0xf5], + [0x56, 0x3e, 0x8c, 0x9a, 0xda, 0xa7, 0xd7, 0x31, 0x1, 0xb0, 0xf2, 0xea, 0xd3, 0xca, 0xe1, 0xea, 0x5d, 0x8f, 0xcd, 0x5c, 0xd3, 0x60, 0x80, 0xbb, 0x8e, 0x6e, 0xc0, 0x3d, 0x61, 0x45, 0x9, 0x17], + ), + ( + [0x68, 0x6f, 0x7d, 0xa9, 0x3b, 0xf2, 0x68, 0xe5, 0x88, 0x6, 0x98, 0x31, 0xf0, 0x47, 0x16, 0x3f, 0x33, 0x58, 0x99, 0x89, 0xd0, 0x82, 0x6e, 0x98, 0x8, 0xfb, 0x67, 0x8e, 0xd5, 0x7e, 0x67, 0x49], + [0x8b, 0x54, 0x9b, 0x2d, 0xf6, 0x42, 0xd3, 0xb2, 0x5f, 0xe8, 0x38, 0xf, 0x8c, 0xc4, 0x37, 0x5f, 0x99, 0xb7, 0xbb, 0x4d, 0x27, 0x5f, 0x77, 0x9f, 0x3b, 0x7c, 0x81, 0xb8, 0xa2, 0xbb, 0xc1, 0x29], + [0x1, 0x47, 0x69, 0x65, 0x42, 0x6b, 0x61, 0x71, 0x74, 0x9a, 0x8a, 0xdd, 0x92, 0x35, 0x2, 0x5c, 0xe5, 0xf5, 0x57, 0xfe, 0x40, 0x9, 0xf7, 0x39, 0x30, 0x44, 0xeb, 0xbb, 0x8a, 0xe9, 0x52, 0x79], + ), + ( + [0x82, 0xd6, 0x1c, 0xce, 0xdc, 0x80, 0x6a, 0x60, 0x60, 0xa3, 0x34, 0x9a, 0x5e, 0x87, 0xcb, 0xc7, 0xac, 0x11, 0x5e, 0x4f, 0x87, 0x77, 0x62, 0x50, 0xae, 0x25, 0x60, 0x98, 0xa7, 0xc4, 0x49, 0x59], + [0x8b, 0x6b, 0x9d, 0x8, 0xf6, 0x1f, 0xc9, 0x1f, 0xe8, 0xb3, 0x29, 0x53, 0xc4, 0x23, 0x40, 0xf0, 0x7, 0xb5, 0x71, 0xdc, 0xb0, 0xa5, 0x6d, 0x10, 0x72, 0x4e, 0xce, 0xf9, 0x95, 0xc, 0xfb, 0x25], + [0x9c, 0x49, 0x94, 0x1f, 0x9c, 0x4f, 0x18, 0x71, 0xfa, 0x40, 0x91, 0xfe, 0xd7, 0x16, 0xd3, 0x49, 0x99, 0xc9, 0x52, 0x34, 0xed, 0xf2, 0xfd, 0xfb, 0xa6, 0xd1, 0x4a, 0x5a, 0xfe, 0x9e, 0x5, 0x58], + ), + ( + [0x7d, 0xc7, 0x64, 0x4, 0x83, 0x13, 0x97, 0xd5, 0x88, 0x4f, 0xdf, 0x6f, 0x97, 0xe1, 0x74, 0x4c, 0x9e, 0xb1, 0x18, 0xa3, 0x1a, 0x7b, 0x23, 0xf8, 0xd7, 0x9f, 0x48, 0xce, 0x9c, 0xad, 0x15, 0x4b], + [0x1a, 0xcd, 0x29, 0x27, 0x84, 0xf4, 0x79, 0x19, 0xd4, 0x55, 0xf8, 0x87, 0x44, 0x83, 0x58, 0x61, 0xb, 0xb9, 0x45, 0x96, 0x70, 0xeb, 0x99, 0xde, 0xe4, 0x60, 0x5, 0xf6, 0x89, 0xca, 0x5f, 0xb6], + [0x0, 0xf4, 0x3c, 0x2, 0x2e, 0x94, 0xea, 0x38, 0x19, 0xb0, 0x36, 0xae, 0x2b, 0x36, 0xb2, 0xa7, 0x61, 0x36, 0xaf, 0x62, 0x8a, 0x75, 0x1f, 0xe5, 0xd0, 0x1e, 0x3, 0xd, 0x44, 0x25, 0x88, 0x59], + ), + ( + [0xfb, 0xc4, 0x51, 0x1d, 0x23, 0xa6, 0x82, 0xae, 0x4e, 0xfd, 0x8, 0xc8, 0x17, 0x9c, 0x1c, 0x6, 0x7f, 0x9c, 0x8b, 0xe7, 0x9b, 0xbc, 0x4e, 0xff, 0x5c, 0xe2, 0x96, 0xc6, 0xbc, 0x1f, 0xf4, 0x45], + [0x55, 0xca, 0xff, 0x21, 0x81, 0xf2, 0x13, 0x6b, 0xe, 0xd0, 0xe1, 0xe2, 0x99, 0x44, 0x48, 0xe1, 0x6c, 0xc9, 0x70, 0x64, 0x6a, 0x98, 0x3d, 0x14, 0xd, 0xc4, 0xea, 0xb3, 0xd9, 0x4c, 0x28, 0x4e], + [0xae, 0x39, 0xd8, 0x16, 0x53, 0x23, 0x45, 0x79, 0x4d, 0x26, 0x91, 0xe0, 0x80, 0x1c, 0xaa, 0x52, 0x5f, 0xc3, 0x63, 0x4d, 0x40, 0x2c, 0xe9, 0x58, 0xb, 0x33, 0x38, 0xb4, 0x6f, 0x8b, 0xb9, 0x72], + ), + ( + [0x4e, 0x6, 0xc, 0xe1, 0xc, 0xeb, 0xf0, 0x95, 0x9, 0x87, 0x16, 0xc8, 0x66, 0x19, 0xeb, 0x9f, 0x7d, 0xf6, 0x65, 0x24, 0x69, 0x8b, 0xa7, 0x98, 0x8c, 0x3b, 0x90, 0x95, 0xd9, 0xf5, 0x1, 0x34], + [0x57, 0x73, 0x3f, 0x2d, 0x86, 0x96, 0x90, 0xd0, 0xd2, 0xed, 0xae, 0xc9, 0x52, 0x3d, 0xaa, 0x2d, 0xa9, 0x54, 0x45, 0xf4, 0x4f, 0x57, 0x83, 0xc1, 0xfa, 0xec, 0x6c, 0x3a, 0x98, 0x28, 0x18, 0xf3], + [0xa6, 0x1e, 0x74, 0x55, 0x2c, 0xce, 0x75, 0xf5, 0xe9, 0x72, 0xe4, 0x24, 0xf2, 0xcc, 0xb0, 0x9c, 0x83, 0xbc, 0x1b, 0x67, 0x1, 0x47, 0x48, 0xf0, 0x2c, 0x37, 0x1a, 0x20, 0x9e, 0xf2, 0xfb, 0x2c], + ), + ( + [0x5c, 0x49, 0x2c, 0xba, 0x2c, 0xc8, 0x92, 0x48, 0x8a, 0x9c, 0xeb, 0x91, 0x86, 0xc2, 0xaa, 0xc2, 0x2f, 0x1, 0x5b, 0xf3, 0xef, 0x8d, 0x3e, 0xcc, 0x9c, 0x41, 0x76, 0x97, 0x62, 0x61, 0xaa, 0xb1], + [0x67, 0x97, 0xc2, 0xe7, 0xdc, 0x92, 0xcc, 0xbe, 0x7c, 0x5, 0x6b, 0xec, 0x35, 0xa, 0xb6, 0xd3, 0xbd, 0x2a, 0x2c, 0x6b, 0xc5, 0xa8, 0x7, 0xbb, 0xca, 0xe1, 0xf6, 0xc2, 0xaf, 0x80, 0x36, 0x44], + [0xfc, 0xf3, 0x7, 0xdf, 0xbc, 0x19, 0x2, 0xb, 0x28, 0xa6, 0x61, 0x8c, 0x6c, 0x62, 0x2f, 0x31, 0x7e, 0x45, 0x96, 0x7d, 0xac, 0xf4, 0xae, 0x4a, 0xa, 0x69, 0x9a, 0x10, 0x76, 0x9f, 0xde, 0x14], + ), + ( + [0xea, 0x33, 0x34, 0x92, 0x96, 0x5, 0x5a, 0x4e, 0x8b, 0x19, 0x2e, 0x3c, 0x23, 0xc5, 0xf4, 0xc8, 0x44, 0x28, 0x2a, 0x3b, 0xfc, 0x19, 0xec, 0xc9, 0xdc, 0x64, 0x6a, 0x42, 0xc3, 0x8d, 0xc2, 0x48], + [0x2c, 0x75, 0xd8, 0x51, 0x42, 0xec, 0xad, 0x3e, 0x69, 0x44, 0x70, 0x4, 0x54, 0xc, 0x1c, 0x23, 0x54, 0x8f, 0xc8, 0xf4, 0x86, 0x25, 0x1b, 0x8a, 0x19, 0x46, 0x3f, 0x3d, 0xf6, 0xf8, 0xac, 0x61], + [0x5d, 0xca, 0xb6, 0x89, 0x73, 0xf9, 0x5b, 0xd3, 0xae, 0x4b, 0x34, 0xfa, 0xb9, 0x49, 0xfb, 0x7f, 0xb1, 0x5a, 0xf1, 0xd8, 0xca, 0xe2, 0x8c, 0xd6, 0x99, 0xf9, 0xc1, 0xaa, 0x33, 0x37, 0x34, 0x2f], + ), + ( + [0x4f, 0x29, 0x79, 0xb1, 0xec, 0x86, 0x19, 0xe4, 0x5c, 0xa, 0xb, 0x2b, 0x52, 0x9, 0x34, 0x54, 0x1a, 0xb9, 0x44, 0x7, 0xb6, 0x4d, 0x19, 0xa, 0x76, 0xf3, 0x23, 0x14, 0xef, 0xe1, 0x84, 0xe7], + [0xf7, 0xca, 0xe1, 0x8d, 0x8d, 0x36, 0xa7, 0xf5, 0x61, 0x17, 0xb8, 0xb7, 0xe, 0x25, 0x52, 0x27, 0x7f, 0xfc, 0x99, 0xdf, 0x87, 0x56, 0xb5, 0xe1, 0x38, 0xbf, 0x63, 0x68, 0xbc, 0x87, 0xf7, 0x4c], + [0xe4, 0xe6, 0x34, 0xeb, 0xb4, 0xfb, 0x66, 0x4f, 0xe8, 0xb2, 0xcf, 0xa1, 0x61, 0x5f, 0x0, 0xe6, 0x46, 0x6f, 0xff, 0x73, 0x2c, 0xe1, 0xf8, 0xa0, 0xc8, 0xd2, 0x72, 0x74, 0x31, 0xd1, 0x6f, 0x14], + ), + ( + [0xf5, 0xd8, 0xa9, 0x27, 0x90, 0x1d, 0x4f, 0xa4, 0x24, 0x90, 0x86, 0xb7, 0xff, 0xec, 0x24, 0xf5, 0x29, 0x7d, 0x80, 0x11, 0x8e, 0x4a, 0xc9, 0xd3, 0xfc, 0x9a, 0x82, 0x37, 0x95, 0x1e, 0x3b, 0x7f], + [0x3c, 0x23, 0x5e, 0xdc, 0x2, 0xf9, 0x11, 0x56, 0x41, 0xdb, 0xf5, 0x16, 0xd5, 0xde, 0x8a, 0x73, 0x5d, 0x6e, 0x53, 0xe2, 0x2a, 0xa2, 0xac, 0x14, 0x36, 0x56, 0x4, 0x5f, 0xf2, 0xe9, 0x52, 0x49], + [0xab, 0x95, 0x15, 0xab, 0x14, 0xaf, 0x9d, 0x27, 0xe, 0x1d, 0xae, 0xc, 0x56, 0x80, 0xcb, 0xc8, 0x88, 0xb, 0xd8, 0xa8, 0xe7, 0xeb, 0x67, 0xb4, 0xda, 0x42, 0xa6, 0x61, 0x96, 0x1e, 0xfc, 0xb], + )]; + + for (let i = 0z; i < len(vectors); i += 1) { + let got: [SCALARSZ]u8 = [0...]; + scalarmult(&got, &vectors[i].0, &vectors[i].1); + if (!bytes::equal(got[..], vectors[i].2[..])) { + fmt::errorfln("Case i={} failed", i)!; + printvector("in", &vectors[i].0); + printvector("base", &vectors[i].1); + printvector("got", &got); + printvector("expect", &vectors[i].2); + abort(); + }; + }; +}; + +@test fn highbitignored() void = { + let s: [SCALARSZ]u8 = [0...]; + let u: [POINTSZ]u8 = [0...]; + + // FIXME(laumann): check that enough was read + io::read(random::stream, s[..])!; + io::read(random::stream, u[..])!; + + let hi0: [32]u8 = [0...]; + let hi1: [32]u8 = [0...]; + + u[31] &= 0x7f; + scalarmult(&hi0, &s, &u); + + u[31] |= 0x80; + scalarmult(&hi1, &s, &u); + + if (!bytes::equal(hi0[..], hi1[..])) { + fmt::errorfln("high bit of group point should not affect result")!; + abort(); + }; +}; + +fn printvector(name: str, vec: const *[SCALARSZ]u8) void = { + fmt::errorf("{:6} = [", name)!; + for (let i = 0z; i < 31; i += 1) { + fmt::errorf("{:02x}, ", vec[i])!; + }; + fmt::errorfln("{:02x}]", vec[31])!; +}; diff --git a/crypto/curve25519/README b/crypto/curve25519/README @@ -0,0 +1,5 @@ +The curve25519 module implements the x25519 function which performs scalar +multiplication on the elliptic curve known as Curve25519. See RFC 7748. + +The implementation is based on the paper "Implementing Curve25519/X25519: A +Tutorial on Elliptic Curve Cryptography" by Martin Kleppmann. diff --git a/crypto/curve25519/curve25519.ha b/crypto/curve25519/curve25519.ha @@ -0,0 +1,204 @@ +// Implements the curve25519 elliptic curve + +// Implementations used for reference +// +// Kleppmann: https://martin.kleppmann.com/papers/curve25519.pdf +// OpenSSL: https://github.com/openssl/openssl/blob/master/crypto/ec/curve25519.c +// Go: https://github.com/golang/crypto/blob/master/curve25519/curve25519.go +// TweetNaCl: https://tweetnacl.cr.yp.to/ + +// The size of the scalar input to X25519. +export def SCALARSZ: size = 32; + +// The size of the point input to X25519. +export def POINTSZ: size = 32; + +// The canonical Curve25519 generator +export const BASEPOINT: [POINTSZ]u8 = [9, 0...]; + +// Compute the result of the scalar multiplication (scalar * point) and put the +// result in out. +export fn x25519( + out: *[SCALARSZ]u8, + scalar: const *[SCALARSZ]u8, + point: const *[POINTSZ]u8 +) void = { + scalarmult(out, scalar, point); +}; + +// Compute the result of the scalar multiplication (scalar * point) where point +// is BASEPOINT. +export fn scalarmult_base( + out: *[SCALARSZ]u8, + scalar: const *[SCALARSZ]u8 +) void = { + scalarmult(out, scalar, &BASEPOINT); +}; + +// Set out to the product (scalar * point) +export fn scalarmult( + out: *[SCALARSZ]u8, + scalar: const *[SCALARSZ]u8, + point: const *[POINTSZ]u8 +) void = { + let clamped: [32]u8 = *scalar; + clamped[0] &= 0xf8; + clamped[31] = (clamped[31] & 0x7f) | 0x40; + let x = unpack25519(point); + let b: elem = *&x; + let a: elem = [1, 0...]; + let c: elem = [0...]; + let d: elem = [1, 0...]; + let e: elem = [0...]; + let f: elem = [0...]; + + for (let i = 254i; i >= 0; i -= 1) { + let iz = i : size; + let bit = ((clamped[iz >> 3] >> (iz & 7)) & 1) : i64; + swap25519(&a, &b, bit); + swap25519(&c, &d, bit); + addfe(&e, &a, &c); + subfe(&a, &a, &c); + addfe(&c, &b, &d); + subfe(&b, &b, &d); + mulfe(&d, &e, &e); + mulfe(&f, &a, &a); + mulfe(&a, &c, &a); + mulfe(&c, &b, &e); + addfe(&e, &a, &c); + subfe(&a, &a, &c); + mulfe(&b, &a, &a); + subfe(&c, &d, &f); + mulfe(&a, &c, &_121665); + addfe(&a, &a, &d); + mulfe(&c, &c, &a); + mulfe(&a, &d, &f); + mulfe(&d, &b, &x); + mulfe(&b, &e, &e); + swap25519(&a, &b, bit); + swap25519(&c, &d, bit); + }; + + invfe(&c, &c); + mulfe(&a, &a, &c); + pack25519(out, &a); +}; + +// An internal representation used for arithmetic operations. +def FIELDSZ: size = 16; +type elem = [FIELDSZ]i64; + +// Constant used in scalar multiplication. +const _121665: elem = [0xdb41, 1, 0...]; + +fn unpack25519(in: *[SCALARSZ]u8) elem = { + let fe: elem = [0...]; + + for (let i = 0z; i < FIELDSZ; i += 1) { + fe[i] = in[2 * i] : i64 + ((in[2 * i + 1] : i64) << 8); + }; + fe[15] &= 0x7fff; + + return fe; +}; + +fn carry25519(fe: *elem) void = { + let carry = 0i64; + + for (let i = 0z; i < FIELDSZ; i += 1) { + carry = fe[i] >> 16; + fe[i] -= (carry << 16); + if (i < 15) { + fe[i + 1] += carry; + } else { + fe[0] += (38 * carry); + }; + }; +}; + +// Set out = a + b +fn addfe(out: *elem, a: const *elem, b: const *elem) void = { + for (let i = 0z; i < FIELDSZ; i += 1) { + out[i] = a[i] + b[i]; + }; +}; + +// Set out = a - b +fn subfe(out: *elem, a: const *elem, b: const *elem) void = { + for (let i = 0z; i < FIELDSZ; i += 1) { + out[i] = a[i] - b[i]; + }; +}; + +// Set out = a * b +fn mulfe(out: *elem, a: const *elem, b: const *elem) void = { + let product: [31]i64 = [0...]; + + for (let i = 0z; i < FIELDSZ; i += 1) { + for (let j = 0z; j < FIELDSZ; j += 1) { + product[i + j] += a[i] * b[j]; + }; + }; + + for (let i = 0z; i < 15; i += 1) { + product[i] += (38 * product[i + 16]); + }; + out[0..FIELDSZ] = product[0..FIELDSZ]; + + carry25519(out); + carry25519(out); +}; + +// Compute the multiplicative inverse +fn invfe(out: *elem, a: const *elem) void = { + let c: elem = *a; + for (let i = 253i; i >= 0; i -= 1) { + mulfe(&c, &c, &c); + if (i != 2 && i != 4) { + mulfe(&c, &c, a); + }; + }; + out[..] = c[..]; +}; + +// Swap inputs p and q, if bit is 1, do nothing if bit is 0. +// +// If bit is 1, this function swaps the content of parameters p and q (both in +// elem representation), and it does nothing if bit is 0. As bit may be +// part of a secret value, this function cannot use a simple if statement, +// because that would not be constant-time +fn swap25519(p: *elem, q: *elem, bit: i64) void = { + let c = ~(bit : u64 - 1) : i64; + for (let i = 0z; i < FIELDSZ; i += 1) { + let t = c & (p[i] ^ q[i]); + p[i] ^= t; + q[i] ^= t; + }; +}; + +fn pack25519(out: *[SCALARSZ]u8, a: const *elem) void = { + let m: elem = [0...]; + let t: elem = *a; + + carry25519(&t); + carry25519(&t); + carry25519(&t); + + for (let _i = 0z; _i < 2; _i += 1) { + m[0] = t[0] - 0xffed; + for (let i = 1z; i < 15; i += 1) { + m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1); + m[i - 1] &= 0xffff; + }; + m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1); + let carry = (m[15] >> 16) & 1; + m[14] &= 0xffff; + swap25519(&t, &m, 1 - carry); + }; + + for (let i = 0z; i < FIELDSZ; i += 1) { + out[2 * i] = (t[i] & 0xff) : u8; + out[2 * i + 1] = (t[i] >> 8) : u8; + }; +}; + diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib @@ -279,6 +279,17 @@ crypto_md5() { fi } +crypto_curve25519() { + if [ $testing -eq 0 ] + then + gen_srcs crypto::curve25519 curve25519.ha + gen_ssa crypto::curve25519 + else + gen_srcs crypto::curve25519 curve25519.ha +test.ha + gen_ssa crypto::curve25519 bytes fmt io encoding::hex crypto::random + fi +} + dirs() { gen_srcs dirs \ xdg.ha @@ -1040,6 +1051,7 @@ crypto::md5 crypto::sha1 crypto::sha256 crypto::sha512 +crypto::curve25519 dirs encoding::base64 encoding::hex diff --git a/stdlib.mk b/stdlib.mk @@ -192,6 +192,12 @@ stdlib_deps_any+=$(stdlib_crypto_sha512_any) stdlib_crypto_sha512_linux=$(stdlib_crypto_sha512_any) stdlib_crypto_sha512_freebsd=$(stdlib_crypto_sha512_any) +# gen_lib crypto::curve25519 (any) +stdlib_crypto_curve25519_any=$(HARECACHE)/crypto/curve25519/crypto_curve25519-any.o +stdlib_deps_any+=$(stdlib_crypto_curve25519_any) +stdlib_crypto_curve25519_linux=$(stdlib_crypto_curve25519_any) +stdlib_crypto_curve25519_freebsd=$(stdlib_crypto_curve25519_any) + # gen_lib dirs (any) stdlib_dirs_any=$(HARECACHE)/dirs/dirs-any.o stdlib_deps_any+=$(stdlib_dirs_any) @@ -709,6 +715,16 @@ $(HARECACHE)/crypto/sha512/crypto_sha512-any.ssa: $(stdlib_crypto_sha512_any_src @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Ncrypto::sha512 \ -t$(HARECACHE)/crypto/sha512/crypto_sha512.td $(stdlib_crypto_sha512_any_srcs) +# crypto::curve25519 (+any) +stdlib_crypto_curve25519_any_srcs= \ + $(STDLIB)/crypto/curve25519/curve25519.ha + +$(HARECACHE)/crypto/curve25519/crypto_curve25519-any.ssa: $(stdlib_crypto_curve25519_any_srcs) $(stdlib_rt) + @printf 'HAREC \t$@\n' + @mkdir -p $(HARECACHE)/crypto/curve25519 + @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Ncrypto::curve25519 \ + -t$(HARECACHE)/crypto/curve25519/crypto_curve25519.td $(stdlib_crypto_curve25519_any_srcs) + # dirs (+any) stdlib_dirs_any_srcs= \ $(STDLIB)/dirs/xdg.ha @@ -1821,6 +1837,12 @@ testlib_deps_any+=$(testlib_crypto_sha512_any) testlib_crypto_sha512_linux=$(testlib_crypto_sha512_any) testlib_crypto_sha512_freebsd=$(testlib_crypto_sha512_any) +# gen_lib crypto::curve25519 (any) +testlib_crypto_curve25519_any=$(TESTCACHE)/crypto/curve25519/crypto_curve25519-any.o +testlib_deps_any+=$(testlib_crypto_curve25519_any) +testlib_crypto_curve25519_linux=$(testlib_crypto_curve25519_any) +testlib_crypto_curve25519_freebsd=$(testlib_crypto_curve25519_any) + # gen_lib dirs (any) testlib_dirs_any=$(TESTCACHE)/dirs/dirs-any.o testlib_deps_any+=$(testlib_dirs_any) @@ -2345,6 +2367,17 @@ $(TESTCACHE)/crypto/sha512/crypto_sha512-any.ssa: $(testlib_crypto_sha512_any_sr @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ncrypto::sha512 \ -t$(TESTCACHE)/crypto/sha512/crypto_sha512.td $(testlib_crypto_sha512_any_srcs) +# crypto::curve25519 (+any) +testlib_crypto_curve25519_any_srcs= \ + $(STDLIB)/crypto/curve25519/curve25519.ha \ + $(STDLIB)/crypto/curve25519/+test.ha + +$(TESTCACHE)/crypto/curve25519/crypto_curve25519-any.ssa: $(testlib_crypto_curve25519_any_srcs) $(testlib_rt) $(testlib_bytes_$(PLATFORM)) $(testlib_fmt_$(PLATFORM)) $(testlib_io_$(PLATFORM)) $(testlib_encoding_hex_$(PLATFORM)) $(testlib_crypto_random_$(PLATFORM)) + @printf 'HAREC \t$@\n' + @mkdir -p $(TESTCACHE)/crypto/curve25519 + @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ncrypto::curve25519 \ + -t$(TESTCACHE)/crypto/curve25519/crypto_curve25519.td $(testlib_crypto_curve25519_any_srcs) + # dirs (+any) testlib_dirs_any_srcs= \ $(STDLIB)/dirs/xdg.ha