hare

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

commit 91b36691accecacfbcf5ad11c44cbeb590114c13
parent 444720781794b0c23d350bbf9ebc4099d0f4f188
Author: Armin Preiml <apreiml@strohwolke.at>
Date:   Thu,  7 Mar 2024 14:50:06 +0100

crypto::ec: port the prime implementation from BearSSL

along with the curves P384 and P521.

Signed-off-by: Armin Preiml <apreiml@strohwolke.at>

Diffstat:
Mcrypto/ec/curves+test.ha | 622+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcrypto/ec/genkeys+test.ha | 2++
Acrypto/ec/prime.ha | 769+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 1393 insertions(+), 0 deletions(-)

diff --git a/crypto/ec/curves+test.ha b/crypto/ec/curves+test.ha @@ -370,6 +370,280 @@ fn tmuladd(c: *curve, tcs: []multc) void = { assert(bytes::equal(r, expected)); }; +@test fn p384_muladd() void = { + let tcs = [ + multc { + a = [ + 0x04, 0xe4, 0x4f, 0x26, 0xd9, 0xce, 0xa7, 0xf0, + 0x69, 0xed, 0xa6, 0x1f, 0x01, 0xcb, 0xef, 0x53, + 0xe8, 0x2b, 0xf8, 0xdc, 0x16, 0x56, 0x05, 0x64, + 0x8a, 0x46, 0xf8, 0x54, 0x25, 0x8e, 0x3e, 0x6c, + 0xe8, 0xc3, 0x88, 0x0b, 0x30, 0xba, 0x9c, 0x8b, + 0xf7, 0xf8, 0x17, 0xbb, 0x34, 0x08, 0x4b, 0xc5, + 0xa7, 0xa3, 0x1d, 0x46, 0x64, 0x4d, 0xd1, 0xe5, + 0x68, 0x23, 0x49, 0x11, 0xbd, 0x4f, 0x21, 0x6f, + 0xcd, 0x88, 0x4d, 0xeb, 0x22, 0xad, 0x36, 0x57, + 0xf3, 0x94, 0x5b, 0x13, 0x3c, 0xf3, 0xf6, 0xc6, + 0xc6, 0x3c, 0xa1, 0x8e, 0xb6, 0x8c, 0x39, 0x85, + 0x01, 0x85, 0x22, 0x16, 0xe7, 0xba, 0x9a, 0xa1, + 0xd1, + ], + b = [], + x = [ + 0xda, 0x9c, 0x84, 0xcf, 0x2d, 0x77, 0x4c, 0xfa, + 0xe5, 0xdc, 0x52, 0xc3, 0xb3, 0xda, 0x1c, 0xa0, + 0x26, 0xb0, 0x53, 0x16, 0x66, 0x46, 0x5f, 0x60, + 0xa6, 0xbe, 0x6e, 0xd1, 0x0a, 0x5f, 0x01, 0x50, + 0x63, 0xe1, 0x47, 0x62, 0x68, 0xcf, 0x40, 0x3a, + 0x2b, 0xf0, 0x96, 0x6c, 0x1e, 0x71, 0x24, 0x79, + ], + y = [ + 0xd9, 0xa7, 0x84, 0xd0, 0x77, 0x9e, 0x07, 0x4d, + 0x78, 0x09, 0x84, 0xef, 0x5b, 0x7d, 0x94, 0xd7, + 0xd8, 0x1f, 0x94, 0x1e, 0xfc, 0x3d, 0x76, 0x6c, + 0x0d, 0x4c, 0x87, 0x76, 0xdf, 0x52, 0xfc, 0xe0, + 0x02, 0x17, 0x2c, 0x95, 0x5e, 0x4b, 0xb7, 0x1f, + 0x84, 0xce, 0x2e, 0x54, 0xf2, 0x08, 0xf7, 0x6a, + ], + result = 1, + expected = [ + 0x04, 0x69, 0xf9, 0xbb, 0x7a, 0x32, 0x63, 0xef, + 0xf2, 0xce, 0x3f, 0x4f, 0xf2, 0x48, 0x86, 0xd8, + 0xe4, 0xa1, 0x68, 0x1f, 0x7b, 0x6c, 0x24, 0xcf, + 0xa2, 0x60, 0x03, 0x0c, 0x8b, 0x10, 0x24, 0x4c, + 0xea, 0x6f, 0x47, 0xc3, 0x75, 0xb0, 0x04, 0x07, + 0xe0, 0xd1, 0x32, 0x8d, 0x02, 0x42, 0x75, 0x8c, + 0x67, 0x90, 0x19, 0x12, 0x26, 0xd3, 0xaf, 0x57, + 0x67, 0xc9, 0x20, 0x17, 0xd5, 0x2e, 0x57, 0xbb, + 0x78, 0x98, 0xe4, 0xb5, 0xd5, 0x65, 0x53, 0x78, + 0x20, 0x99, 0x2b, 0x43, 0x17, 0x1d, 0x2f, 0xdb, + 0x2c, 0xd5, 0xe5, 0x61, 0x22, 0x1f, 0xae, 0x22, + 0xae, 0x71, 0x03, 0x25, 0x9a, 0x47, 0x28, 0x4a, + 0x0f, + ], + }, + multc { + a = [ + 0x04, 0xe4, 0x4f, 0x26, 0xd9, 0xce, 0xa7, 0xf0, + 0x69, 0xed, 0xa6, 0x1f, 0x01, 0xcb, 0xef, 0x53, + 0xe8, 0x2b, 0xf8, 0xdc, 0x16, 0x56, 0x05, 0x64, + 0x8a, 0x46, 0xf8, 0x54, 0x25, 0x8e, 0x3e, 0x6c, + 0xe8, 0xc3, 0x88, 0x0b, 0x30, 0xba, 0x9c, 0x8b, + 0xf7, 0xf8, 0x17, 0xbb, 0x34, 0x08, 0x4b, 0xc5, + 0xa7, 0xa3, 0x1d, 0x46, 0x64, 0x4d, 0xd1, 0xe5, + 0x68, 0x23, 0x49, 0x11, 0xbd, 0x4f, 0x21, 0x6f, + 0xcd, 0x88, 0x4d, 0xeb, 0x22, 0xad, 0x36, 0x57, + 0xf3, 0x94, 0x5b, 0x13, 0x3c, 0xf3, 0xf6, 0xc6, + 0xc6, 0x3c, 0xa1, 0x8e, 0xb6, 0x8c, 0x39, 0x85, + 0x01, 0x85, 0x22, 0x16, 0xe7, 0xba, 0x9a, 0xa1, + 0xd1, + ], + b = [ + 0x04, 0xe0, 0xb0, 0x1d, 0x37, 0xe0, 0x12, 0xba, + 0x21, 0xcd, 0xc5, 0xc4, 0x18, 0xfd, 0x85, 0x0a, + 0x21, 0x11, 0x32, 0x69, 0x73, 0xc0, 0xd4, 0x55, + 0xf3, 0x2e, 0x9d, 0x25, 0x0c, 0x4a, 0xc8, 0x89, + 0x4d, 0x7c, 0xbf, 0xca, 0x2d, 0x28, 0x6d, 0x20, + 0x5b, 0xbf, 0x0d, 0x1b, 0x6e, 0x92, 0x97, 0xd6, + 0xbb, 0xe2, 0x1b, 0x17, 0xac, 0xce, 0xd9, 0x7c, + 0x7e, 0x54, 0xca, 0xfb, 0xaf, 0x53, 0xa8, 0xde, + 0xba, 0x4c, 0x0a, 0x0f, 0x67, 0x03, 0xe7, 0x23, + 0xcf, 0x19, 0x07, 0x31, 0x71, 0x9a, 0x49, 0x51, + 0x7f, 0xbd, 0x5b, 0xa0, 0x34, 0x5f, 0x79, 0xba, + 0x48, 0xf1, 0x41, 0x9d, 0xcc, 0x2d, 0xef, 0x80, + 0xbb, + ], + x = [ + 0xda, 0x9c, 0x84, 0xcf, 0x2d, 0x77, 0x4c, 0xfa, + 0xe5, 0xdc, 0x52, 0xc3, 0xb3, 0xda, 0x1c, 0xa0, + 0x26, 0xb0, 0x53, 0x16, 0x66, 0x46, 0x5f, 0x60, + 0xa6, 0xbe, 0x6e, 0xd1, 0x0a, 0x5f, 0x01, 0x50, + 0x63, 0xe1, 0x47, 0x62, 0x68, 0xcf, 0x40, 0x3a, + 0x2b, 0xf0, 0x96, 0x6c, 0x1e, 0x71, 0x24, 0x79, + ], + y = [ + 0xd9, 0xa7, 0x84, 0xd0, 0x77, 0x9e, 0x07, 0x4d, + 0x78, 0x09, 0x84, 0xef, 0x5b, 0x7d, 0x94, 0xd7, + 0xd8, 0x1f, 0x94, 0x1e, 0xfc, 0x3d, 0x76, 0x6c, + 0x0d, 0x4c, 0x87, 0x76, 0xdf, 0x52, 0xfc, 0xe0, + 0x02, 0x17, 0x2c, 0x95, 0x5e, 0x4b, 0xb7, 0x1f, + 0x84, 0xce, 0x2e, 0x54, 0xf2, 0x08, 0xf7, 0x6a, + ], + result = 1, + expected = [ + 0x04, 0xdc, 0x38, 0x1d, 0x38, 0x27, 0x1a, 0x83, + 0x03, 0x46, 0x18, 0xa6, 0xa7, 0xa7, 0x3e, 0xeb, + 0xa3, 0x3b, 0x9b, 0x05, 0x00, 0xe8, 0x09, 0xac, + 0x1a, 0x77, 0x5f, 0x21, 0xec, 0x5d, 0xe4, 0x70, + 0x3d, 0x18, 0x1f, 0x38, 0x5b, 0x5d, 0xaf, 0xed, + 0xc3, 0xff, 0xe4, 0x8a, 0xdb, 0x4b, 0x35, 0xfd, + 0x34, 0x2b, 0xfa, 0x29, 0x04, 0xe8, 0x55, 0x73, + 0xcf, 0xce, 0x1e, 0x2c, 0x34, 0x24, 0x08, 0xca, + 0x21, 0x71, 0xe1, 0xb4, 0x90, 0xd6, 0xe0, 0x60, + 0xff, 0x3b, 0x40, 0x70, 0xcd, 0x47, 0x26, 0xd1, + 0x5b, 0xc6, 0xbf, 0x30, 0x94, 0x40, 0x6f, 0x88, + 0x09, 0x12, 0xe7, 0x3e, 0x22, 0x88, 0x7e, 0x6e, + 0xc1, + ], + }, + // invalid a + multc { + a = [ + 0x04, 0xe4, 0x4f, 0x26, 0xd9, 0xce, 0xa7, 0xf0, + 0x69, 0xed, 0xa6, 0x1f, 0x01, 0xcb, 0xef, 0x53, + 0xe8, 0x2b, 0x08, 0xdc, 0x16, 0x56, 0x05, 0x64, + 0x8a, 0x46, 0x08, 0x54, 0x25, 0x8e, 0x3e, 0x6c, + 0xe8, 0xc3, 0xf8, 0x0b, 0x30, 0xba, 0x9c, 0x8b, + 0xf7, 0xf8, 0xf7, 0xbb, 0x34, 0x08, 0x4b, 0xc5, + 0xa7, 0xa3, 0xfd, 0x46, 0x64, 0x4d, 0xd1, 0xe5, + 0x68, 0x23, 0x49, 0x11, 0xbd, 0x4f, 0x21, 0x6f, + 0xcd, 0x88, 0x4d, 0xeb, 0x22, 0xad, 0x36, 0x57, + 0xf3, 0x94, 0x00, 0x13, 0x3c, 0xf3, 0xf6, 0xc6, + 0xc6, 0x3c, 0x00, 0x8e, 0xb6, 0x8c, 0x39, 0x85, + 0x01, 0x85, 0x00, 0x16, 0xe7, 0xba, 0x9a, 0xa1, + 0xd1, + ], + b = [ + 0x04, 0xe0, 0xb0, 0x1d, 0x37, 0xe0, 0x12, 0xba, + 0x21, 0xcd, 0xc5, 0xc4, 0x18, 0xfd, 0x85, 0x0a, + 0x21, 0x11, 0x32, 0x69, 0x73, 0xc0, 0xd4, 0x55, + 0xf3, 0x2e, 0x9d, 0x25, 0x0c, 0x4a, 0xc8, 0x89, + 0x4d, 0x7c, 0xbf, 0xca, 0x2d, 0x28, 0x6d, 0x20, + 0x5b, 0xbf, 0x0d, 0x1b, 0x6e, 0x92, 0x97, 0xd6, + 0xbb, 0xe2, 0x1b, 0x17, 0xac, 0xce, 0xd9, 0x7c, + 0x7e, 0x54, 0xca, 0xfb, 0xaf, 0x53, 0xa8, 0xde, + 0xba, 0x4c, 0x0a, 0x0f, 0x67, 0x03, 0xe7, 0x23, + 0xcf, 0x19, 0x07, 0x31, 0x71, 0x9a, 0x49, 0x51, + 0x7f, 0xbd, 0x5b, 0xa0, 0x34, 0x5f, 0x79, 0xba, + 0x48, 0xf1, 0x41, 0x9d, 0xcc, 0x2d, 0xef, 0x80, + 0xbb, + ], + x = [ + 0xda, 0x9c, 0x84, 0xcf, 0x2d, 0x77, 0x4c, 0xfa, + 0xe5, 0xdc, 0x52, 0xc3, 0xb3, 0xda, 0x1c, 0xa0, + 0x26, 0xb0, 0x53, 0x16, 0x66, 0x46, 0x5f, 0x60, + 0xa6, 0xbe, 0x6e, 0xd1, 0x0a, 0x5f, 0x01, 0x50, + 0x63, 0xe1, 0x47, 0x62, 0x68, 0xcf, 0x40, 0x3a, + 0x2b, 0xf0, 0x96, 0x6c, 0x1e, 0x71, 0x24, 0x79, + ], + y = [ + 0xd9, 0xa7, 0x84, 0xd0, 0x77, 0x9e, 0x07, 0x4d, + 0x78, 0x09, 0x84, 0xef, 0x5b, 0x7d, 0x94, 0xd7, + 0xd8, 0x1f, 0x94, 0x1e, 0xfc, 0x3d, 0x76, 0x6c, + 0x0d, 0x4c, 0x87, 0x76, 0xdf, 0x52, 0xfc, 0xe0, + 0x02, 0x17, 0x2c, 0x95, 0x5e, 0x4b, 0xb7, 0x1f, + 0x84, 0xce, 0x2e, 0x54, 0xf2, 0x08, 0xf7, 0x6a, + ], + result = 0, + expected = [], + }, + // invalid b + multc { + a = [ + 0x04, 0xe4, 0x4f, 0x26, 0xd9, 0xce, 0xa7, 0xf0, + 0x69, 0xed, 0xa6, 0x1f, 0x01, 0xcb, 0xef, 0x53, + 0xe8, 0x2b, 0xf8, 0xdc, 0x16, 0x56, 0x05, 0x64, + 0x8a, 0x46, 0xf8, 0x54, 0x25, 0x8e, 0x3e, 0x6c, + 0xe8, 0xc3, 0x88, 0x0b, 0x30, 0xba, 0x9c, 0x8b, + 0xf7, 0xf8, 0x17, 0xbb, 0x34, 0x08, 0x4b, 0xc5, + 0xa7, 0xa3, 0x1d, 0x46, 0x64, 0x4d, 0xd1, 0xe5, + 0x68, 0x23, 0x49, 0x11, 0xbd, 0x4f, 0x21, 0x6f, + 0xcd, 0x88, 0x4d, 0xeb, 0x22, 0xad, 0x36, 0x57, + 0xf3, 0x94, 0x5b, 0x13, 0x3c, 0xf3, 0xf6, 0xc6, + 0xc6, 0x3c, 0xa1, 0x8e, 0xb6, 0x8c, 0x39, 0x85, + 0x01, 0x85, 0x22, 0x16, 0xe7, 0xba, 0x9a, 0xa1, + 0xd1, + ], + b = [ + 0x04, 0xe0, 0xb0, 0x1d, 0x37, 0xe0, 0x12, 0xba, + 0x21, 0xcd, 0xc5, 0xc4, 0x18, 0xfd, 0x85, 0x0a, + 0x21, 0x11, 0x32, 0x69, 0x73, 0xc0, 0xd4, 0x55, + 0xf3, 0x2e, 0x9d, 0x25, 0x0c, 0x4a, 0xc8, 0x89, + 0x4d, 0x7c, 0xbf, 0xca, 0x2d, 0x28, 0x6d, 0x20, + 0x5b, 0x0f, 0x0d, 0x1b, 0x6e, 0x92, 0x97, 0xd6, + 0xbb, 0x0f, 0x1b, 0x17, 0xac, 0xce, 0xd9, 0x7c, + 0x7e, 0x0f, 0xca, 0xfb, 0xaf, 0x53, 0xa8, 0xde, + 0xba, 0x0f, 0x0a, 0x0f, 0x67, 0x03, 0xe7, 0x23, + 0xcf, 0x0f, 0x07, 0x31, 0x71, 0x9a, 0x49, 0x51, + 0x7f, 0x0f, 0x5b, 0xa0, 0x34, 0x5f, 0x79, 0xba, + 0x48, 0xff, 0x41, 0x9d, 0xcc, 0x2d, 0xef, 0x80, + 0xbb, + ], + x = [ + 0xda, 0x9c, 0x84, 0xcf, 0x2d, 0x77, 0x4c, 0xfa, + 0xe5, 0xdc, 0x52, 0xc3, 0xb3, 0xda, 0x1c, 0xa0, + 0x26, 0xb0, 0x53, 0x16, 0x66, 0x46, 0x5f, 0x60, + 0xa6, 0xbe, 0x6e, 0xd1, 0x0a, 0x5f, 0x01, 0x50, + 0x63, 0xe1, 0x47, 0x62, 0x68, 0xcf, 0x40, 0x3a, + 0x2b, 0xf0, 0x96, 0x6c, 0x1e, 0x71, 0x24, 0x79, + ], + y = [ + 0xd9, 0xa7, 0x84, 0xd0, 0x77, 0x9e, 0x07, 0x4d, + 0x78, 0x09, 0x84, 0xef, 0x5b, 0x7d, 0x94, 0xd7, + 0xd8, 0x1f, 0x94, 0x1e, 0xfc, 0x3d, 0x76, 0x6c, + 0x0d, 0x4c, 0x87, 0x76, 0xdf, 0x52, 0xfc, 0xe0, + 0x02, 0x17, 0x2c, 0x95, 0x5e, 0x4b, 0xb7, 0x1f, + 0x84, 0xce, 0x2e, 0x54, 0xf2, 0x08, 0xf7, 0x6a, + ], + result = 0, + expected = [], + }, + // invalid a and b + multc { + a = [ + 0x04, 0xe4, 0x4f, 0x26, 0xd9, 0xce, 0xa7, 0xf0, + 0x69, 0xed, 0xf6, 0x1f, 0x01, 0xcb, 0xef, 0x53, + 0xe8, 0x2b, 0xf8, 0xdc, 0x16, 0x56, 0x05, 0x64, + 0x8a, 0x46, 0xf8, 0x54, 0x25, 0x8e, 0x3e, 0x6c, + 0xe8, 0xc3, 0xf8, 0x0b, 0x30, 0xba, 0x9c, 0x8b, + 0xf7, 0xf8, 0xf7, 0xbb, 0x34, 0x08, 0x4b, 0xc5, + 0xa7, 0xa3, 0xfd, 0x46, 0x64, 0x4d, 0xd1, 0xe5, + 0x68, 0x23, 0xf9, 0x11, 0xbd, 0x4f, 0x21, 0x6f, + 0xcd, 0x88, 0xfd, 0xeb, 0x22, 0xad, 0x36, 0x57, + 0xf3, 0x94, 0xfb, 0x13, 0x3c, 0xf3, 0xf6, 0xc6, + 0xc6, 0x3c, 0xf1, 0x8e, 0xb6, 0x8c, 0x39, 0x85, + 0x01, 0x85, 0xf2, 0x16, 0xe7, 0xba, 0x9a, 0xa1, + 0xd1, + ], + b = [ + 0x04, 0xe0, 0xb0, 0x1d, 0x37, 0xe0, 0x12, 0xba, + 0x21, 0xcd, 0xc5, 0xc4, 0x18, 0xfd, 0x85, 0x0a, + 0x21, 0x11, 0x32, 0x60, 0x73, 0xc0, 0xd4, 0x55, + 0xf3, 0x2e, 0x9d, 0x20, 0x0c, 0x4a, 0xc8, 0x89, + 0x4d, 0x7c, 0xbf, 0xc0, 0x2d, 0x28, 0x6d, 0x20, + 0x5b, 0xbf, 0x0d, 0x10, 0x6e, 0x92, 0x97, 0xd6, + 0xbb, 0xe2, 0x1b, 0x10, 0xac, 0xce, 0xd9, 0x7c, + 0x7e, 0x54, 0xca, 0xf0, 0xaf, 0x53, 0xa8, 0xde, + 0xba, 0x4c, 0x0a, 0x00, 0x67, 0x03, 0xe7, 0x23, + 0xcf, 0x19, 0x07, 0x30, 0x71, 0x9a, 0x49, 0x51, + 0x7f, 0xbd, 0x5b, 0xa0, 0x34, 0x5f, 0x79, 0xba, + 0x48, 0xf1, 0x41, 0x90, 0xcc, 0x2d, 0xef, 0x80, + 0xbb, + ], + x = [ + 0xda, 0x9c, 0x84, 0xcf, 0x2d, 0x77, 0x4c, 0xfa, + 0xe5, 0xdc, 0x52, 0xc3, 0xb3, 0xda, 0x1c, 0xa0, + 0x26, 0xb0, 0x53, 0x16, 0x66, 0x46, 0x5f, 0x60, + 0xa6, 0xbe, 0x6e, 0xd1, 0x0a, 0x5f, 0x01, 0x50, + 0x63, 0xe1, 0x47, 0x62, 0x68, 0xcf, 0x40, 0x3a, + 0x2b, 0xf0, 0x96, 0x6c, 0x1e, 0x71, 0x24, 0x79, + ], + y = [ + 0xd9, 0xa7, 0x84, 0xd0, 0x77, 0x9e, 0x07, 0x4d, + 0x78, 0x09, 0x84, 0xef, 0x5b, 0x7d, 0x94, 0xd7, + 0xd8, 0x1f, 0x94, 0x1e, 0xfc, 0x3d, 0x76, 0x6c, + 0x0d, 0x4c, 0x87, 0x76, 0xdf, 0x52, 0xfc, 0xe0, + 0x02, 0x17, 0x2c, 0x95, 0x5e, 0x4b, 0xb7, 0x1f, + 0x84, 0xce, 0x2e, 0x54, 0xf2, 0x08, 0xf7, 0x6a, + ], + result = 0, + expected = [], + }, + ]; + + tmuladd(p384, tcs); +}; + @test fn p521_mulgen() void = { let c = p521; @@ -451,3 +725,351 @@ fn tmuladd(c: *curve, tcs: []multc) void = { assert(c.mulgen(r, priv) == 133); assert(bytes::equal(r, expected)); }; + +@test fn p521_muladd() void = { + let tcs = [ + multc { + a = [ + 0x04, 0x01, 0xb1, 0x0a, 0x39, 0x7f, 0x94, 0xe9, + 0x0b, 0x4f, 0x8f, 0xf1, 0xe8, 0x31, 0xca, 0x0a, + 0xda, 0x8f, 0x1c, 0x80, 0x1e, 0x1a, 0x95, 0x65, + 0xdb, 0x0f, 0x52, 0x7f, 0xaa, 0x14, 0x65, 0x6d, + 0xe9, 0xe6, 0x5c, 0xa2, 0x34, 0xc8, 0xea, 0x11, + 0x67, 0x4a, 0xc3, 0x5f, 0xce, 0x8b, 0xa8, 0xe8, + 0xe4, 0x8b, 0x6c, 0x9e, 0x5c, 0x0d, 0x37, 0xf0, + 0x4e, 0x33, 0xb8, 0xd1, 0x5f, 0xce, 0x90, 0x92, + 0xa7, 0x14, 0x07, 0x01, 0x1e, 0xde, 0x56, 0x92, + 0xcc, 0x39, 0xb6, 0xe9, 0xcf, 0xbe, 0xe0, 0xeb, + 0x35, 0x30, 0xfc, 0xf2, 0x2b, 0xc6, 0xe4, 0xfa, + 0x71, 0x2e, 0x2f, 0x87, 0x54, 0x83, 0xb1, 0x9c, + 0x96, 0xe2, 0xbb, 0x72, 0xca, 0x51, 0xf2, 0x58, + 0x25, 0x80, 0xf4, 0x47, 0xa3, 0xb6, 0x29, 0x45, + 0x3c, 0x28, 0xa7, 0x65, 0x85, 0x40, 0xb7, 0x2b, + 0x75, 0x38, 0x4a, 0x3e, 0x25, 0x0a, 0xb6, 0x58, + 0xe3, 0x9a, 0x7c, 0xde, 0xd5, + ], + b = [], + x = [ + 0x01, 0x74, 0xe2, 0x0e, 0x0c, 0xa6, 0xd0, 0x12, + 0xb0, 0xc3, 0x86, 0xbc, 0xfc, 0x9a, 0xcb, 0x09, + 0x7a, 0xf9, 0xca, 0xb7, 0xc8, 0x79, 0x39, 0x3e, + 0xb6, 0x8e, 0x3e, 0x2f, 0x02, 0x6a, 0xfd, 0x07, + 0x65, 0xe9, 0x97, 0xe1, 0xf5, 0xf0, 0x16, 0x9a, + 0xa2, 0xe6, 0x03, 0x75, 0x1f, 0xa2, 0xf4, 0xe5, + 0xcd, 0x54, 0x94, 0x60, 0xb2, 0xfd, 0xe7, 0x97, + 0xea, 0x72, 0x02, 0xb9, 0x96, 0x48, 0xd3, 0x45, + 0xc2, 0x26, + ], + y = [ + 0x01, 0xae, 0xa0, 0x8f, 0x5b, 0x13, 0xe1, 0x85, + 0x4f, 0xfe, 0xcf, 0x73, 0x6a, 0x18, 0xc3, 0xfa, + 0xb7, 0xe6, 0xfc, 0xe5, 0xa9, 0x09, 0x8a, 0x68, + 0x4f, 0x49, 0x93, 0x59, 0xeb, 0xfd, 0x91, 0xf9, + 0x45, 0x1d, 0xcf, 0x51, 0x61, 0x39, 0x5c, 0x87, + 0x6c, 0x70, 0x9d, 0xfa, 0x7a, 0x86, 0x30, 0x64, + 0x3a, 0x4f, 0x48, 0x78, 0x3a, 0x2f, 0x9f, 0x84, + 0x07, 0xc1, 0x94, 0x5a, 0xc7, 0x1a, 0xe2, 0x5d, + 0x73, 0xb3, + ], + result = 1, + expected = [ + 0x04, 0x00, 0x27, 0xd3, 0x90, 0xf4, 0xf7, 0xdc, + 0x2a, 0x67, 0xa0, 0x2b, 0x8c, 0x31, 0x3b, 0xe3, + 0x37, 0xb9, 0xf9, 0x08, 0x49, 0x46, 0x56, 0xa6, + 0xa4, 0x3d, 0x7c, 0x0a, 0x74, 0x98, 0x72, 0x20, + 0xbe, 0xa7, 0xf8, 0x67, 0x95, 0x7d, 0x1f, 0x6a, + 0x38, 0x03, 0xc9, 0xf3, 0xac, 0x55, 0xb9, 0x5b, + 0x5d, 0xeb, 0x01, 0xe0, 0xaf, 0xf1, 0x66, 0xcf, + 0x90, 0xe3, 0x43, 0x5c, 0x25, 0xfb, 0xcd, 0x48, + 0xd4, 0xf5, 0xbd, 0x01, 0xb5, 0xa5, 0xd4, 0xa1, + 0xe4, 0x4f, 0xab, 0x94, 0x96, 0xdd, 0x32, 0x7f, + 0x9e, 0x40, 0x1c, 0x25, 0x7d, 0xcb, 0xed, 0xa5, + 0x53, 0xac, 0x4f, 0xa3, 0x72, 0x75, 0x56, 0xd8, + 0x32, 0x2c, 0x76, 0xed, 0x0d, 0xe7, 0x5c, 0xbd, + 0xbd, 0xe8, 0x09, 0x35, 0x0e, 0x57, 0xd4, 0x20, + 0xe1, 0x46, 0x6c, 0x49, 0xe1, 0x49, 0xc6, 0x04, + 0x6e, 0xf9, 0xc9, 0x87, 0x76, 0x0a, 0x9a, 0x3b, + 0x2b, 0xa6, 0x43, 0xc2, 0x24, + ], + }, + multc { + a = [ + 0x04, 0x01, 0xb1, 0x0a, 0x39, 0x7f, 0x94, 0xe9, + 0x0b, 0x4f, 0x8f, 0xf1, 0xe8, 0x31, 0xca, 0x0a, + 0xda, 0x8f, 0x1c, 0x80, 0x1e, 0x1a, 0x95, 0x65, + 0xdb, 0x0f, 0x52, 0x7f, 0xaa, 0x14, 0x65, 0x6d, + 0xe9, 0xe6, 0x5c, 0xa2, 0x34, 0xc8, 0xea, 0x11, + 0x67, 0x4a, 0xc3, 0x5f, 0xce, 0x8b, 0xa8, 0xe8, + 0xe4, 0x8b, 0x6c, 0x9e, 0x5c, 0x0d, 0x37, 0xf0, + 0x4e, 0x33, 0xb8, 0xd1, 0x5f, 0xce, 0x90, 0x92, + 0xa7, 0x14, 0x07, 0x01, 0x1e, 0xde, 0x56, 0x92, + 0xcc, 0x39, 0xb6, 0xe9, 0xcf, 0xbe, 0xe0, 0xeb, + 0x35, 0x30, 0xfc, 0xf2, 0x2b, 0xc6, 0xe4, 0xfa, + 0x71, 0x2e, 0x2f, 0x87, 0x54, 0x83, 0xb1, 0x9c, + 0x96, 0xe2, 0xbb, 0x72, 0xca, 0x51, 0xf2, 0x58, + 0x25, 0x80, 0xf4, 0x47, 0xa3, 0xb6, 0x29, 0x45, + 0x3c, 0x28, 0xa7, 0x65, 0x85, 0x40, 0xb7, 0x2b, + 0x75, 0x38, 0x4a, 0x3e, 0x25, 0x0a, 0xb6, 0x58, + 0xe3, 0x9a, 0x7c, 0xde, 0xd5, + ], + b = [ + 0x04, 0x01, 0x01, 0xbf, 0xd2, 0xa7, 0xed, 0xe7, + 0x68, 0x5a, 0x86, 0x4b, 0xc8, 0x40, 0x42, 0x9d, + 0xea, 0x5c, 0xe5, 0x3e, 0x3d, 0x3d, 0x48, 0xc6, + 0x38, 0xb0, 0x7f, 0xd4, 0x35, 0x67, 0x67, 0xb2, + 0x12, 0xa7, 0xba, 0xd4, 0xc3, 0x22, 0x3f, 0x7d, + 0xff, 0xe2, 0x23, 0x8e, 0x72, 0x71, 0x2c, 0x24, + 0xd5, 0x91, 0xdc, 0x9c, 0xb2, 0xc7, 0x2d, 0x5c, + 0xe1, 0xf7, 0x17, 0x49, 0x09, 0xeb, 0xe4, 0x26, + 0xab, 0xe6, 0x6d, 0x01, 0x26, 0xb1, 0x8e, 0x19, + 0xb3, 0xe3, 0x72, 0xe9, 0xf4, 0x15, 0xe3, 0x52, + 0x2a, 0xb3, 0xcb, 0xac, 0xf8, 0xe7, 0xc0, 0x14, + 0x60, 0x97, 0x71, 0xa0, 0x54, 0x2b, 0x94, 0x4c, + 0x13, 0x6e, 0xfb, 0x98, 0x11, 0x72, 0x60, 0x6a, + 0x0e, 0xd5, 0xb4, 0xe8, 0x17, 0x6c, 0x09, 0x2a, + 0xff, 0x89, 0xac, 0x88, 0xcd, 0x56, 0xe4, 0xcc, + 0x66, 0x79, 0x8c, 0xb7, 0xd2, 0x44, 0x44, 0x60, + 0xd8, 0x04, 0xa2, 0x50, 0x0f, + ], + x = [ + 0x01, 0x74, 0xe2, 0x0e, 0x0c, 0xa6, 0xd0, 0x12, + 0xb0, 0xc3, 0x86, 0xbc, 0xfc, 0x9a, 0xcb, 0x09, + 0x7a, 0xf9, 0xca, 0xb7, 0xc8, 0x79, 0x39, 0x3e, + 0xb6, 0x8e, 0x3e, 0x2f, 0x02, 0x6a, 0xfd, 0x07, + 0x65, 0xe9, 0x97, 0xe1, 0xf5, 0xf0, 0x16, 0x9a, + 0xa2, 0xe6, 0x03, 0x75, 0x1f, 0xa2, 0xf4, 0xe5, + 0xcd, 0x54, 0x94, 0x60, 0xb2, 0xfd, 0xe7, 0x97, + 0xea, 0x72, 0x02, 0xb9, 0x96, 0x48, 0xd3, 0x45, + 0xc2, 0x26, + ], + y = [ + 0x01, 0xae, 0xa0, 0x8f, 0x5b, 0x13, 0xe1, 0x85, + 0x4f, 0xfe, 0xcf, 0x73, 0x6a, 0x18, 0xc3, 0xfa, + 0xb7, 0xe6, 0xfc, 0xe5, 0xa9, 0x09, 0x8a, 0x68, + 0x4f, 0x49, 0x93, 0x59, 0xeb, 0xfd, 0x91, 0xf9, + 0x45, 0x1d, 0xcf, 0x51, 0x61, 0x39, 0x5c, 0x87, + 0x6c, 0x70, 0x9d, 0xfa, 0x7a, 0x86, 0x30, 0x64, + 0x3a, 0x4f, 0x48, 0x78, 0x3a, 0x2f, 0x9f, 0x84, + 0x07, 0xc1, 0x94, 0x5a, 0xc7, 0x1a, 0xe2, 0x5d, + 0x73, 0xb3, + ], + result = 1, + expected = [ + 0x04, 0x01, 0x4b, 0x18, 0x4d, 0x28, 0xd0, 0x8f, + 0x23, 0xfc, 0x27, 0x13, 0x7e, 0xc9, 0x9d, 0xb2, + 0x39, 0xb5, 0x5b, 0x7a, 0x30, 0xcb, 0x35, 0x47, + 0x1b, 0x04, 0x63, 0x8a, 0x50, 0x15, 0xb2, 0x79, + 0x9d, 0x74, 0xf5, 0xbc, 0x21, 0x14, 0x5e, 0x9f, + 0x3f, 0x88, 0xfe, 0x46, 0x9d, 0x7c, 0xb8, 0x1a, + 0x1c, 0x5c, 0x86, 0x60, 0xf2, 0xbb, 0x04, 0xdc, + 0x81, 0x9d, 0xf3, 0x35, 0x75, 0x5e, 0xa9, 0x58, + 0x36, 0x17, 0x33, 0x01, 0x86, 0xd9, 0x16, 0xdb, + 0x10, 0xae, 0x45, 0x0a, 0xb6, 0x75, 0x6d, 0x90, + 0x85, 0x92, 0xde, 0x4c, 0x96, 0x3b, 0xfd, 0x31, + 0xe6, 0x99, 0x7f, 0xe2, 0xb6, 0xfd, 0xbb, 0x76, + 0xe4, 0x62, 0x8f, 0xb8, 0xba, 0x8c, 0x9d, 0xc9, + 0xc4, 0x2f, 0x3e, 0x67, 0xd5, 0xaf, 0xbc, 0xf0, + 0x60, 0x8c, 0xca, 0xec, 0xa9, 0x21, 0xd1, 0x8e, + 0x29, 0xc9, 0x81, 0x76, 0xdb, 0x17, 0x17, 0xd8, + 0x6b, 0x97, 0x58, 0x88, 0x78, + ], + }, + // invalid a + multc { + a = [ + 0x04, 0x01, 0xb1, 0x0a, 0x39, 0x7f, 0x94, 0xe9, + 0x0b, 0x4f, 0x8f, 0xf1, 0xe8, 0x31, 0xca, 0x0a, + 0xda, 0x8f, 0x1c, 0x80, 0x1e, 0x1a, 0x95, 0x65, + 0xdb, 0x0f, 0x52, 0x7f, 0xaa, 0x14, 0x65, 0x6d, + 0xe9, 0xef, 0x5c, 0xa2, 0x34, 0xc8, 0xea, 0x11, + 0x67, 0x4f, 0xc3, 0x5f, 0xce, 0x8b, 0xa8, 0xe8, + 0xe4, 0x8f, 0x6c, 0x9e, 0x5c, 0x0d, 0x37, 0xf0, + 0x4e, 0x3f, 0xb8, 0xd1, 0x5f, 0xce, 0x90, 0x92, + 0xa7, 0x1f, 0x07, 0x01, 0x1e, 0xde, 0x56, 0x92, + 0xcc, 0x3f, 0xb6, 0xe9, 0xcf, 0xbe, 0xe0, 0xeb, + 0x35, 0x3f, 0xfc, 0xf2, 0x2b, 0xc6, 0xe4, 0xfa, + 0x71, 0x2f, 0x2f, 0x87, 0x54, 0x83, 0xb1, 0x9c, + 0x96, 0xef, 0xbb, 0x72, 0xca, 0x51, 0xf2, 0x58, + 0x25, 0x8f, 0xf4, 0x47, 0xa3, 0xb6, 0x29, 0x45, + 0x3c, 0x2f, 0xa7, 0x65, 0x85, 0x40, 0xb7, 0x2b, + 0x75, 0x38, 0x4a, 0x3e, 0x25, 0x0a, 0xb6, 0x58, + 0xe3, 0x9a, 0x7c, 0xde, 0xd5, + ], + b = [ + 0x04, 0x01, 0x01, 0xbf, 0xd2, 0xa7, 0xed, 0xe7, + 0x68, 0x5a, 0x86, 0x4b, 0xc8, 0x40, 0x42, 0x9d, + 0xea, 0x5c, 0xe5, 0x3e, 0x3d, 0x3d, 0x48, 0xc6, + 0x38, 0xb0, 0x7f, 0xd4, 0x35, 0x67, 0x67, 0xb2, + 0x12, 0xa7, 0xba, 0xd4, 0xc3, 0x22, 0x3f, 0x7d, + 0xff, 0xe2, 0x23, 0x8e, 0x72, 0x71, 0x2c, 0x24, + 0xd5, 0x91, 0xdc, 0x9c, 0xb2, 0xc7, 0x2d, 0x5c, + 0xe1, 0xf7, 0x17, 0x49, 0x09, 0xeb, 0xe4, 0x26, + 0xab, 0xe6, 0x6d, 0x01, 0x26, 0xb1, 0x8e, 0x19, + 0xb3, 0xe3, 0x72, 0xe9, 0xf4, 0x15, 0xe3, 0x52, + 0x2a, 0xb3, 0xcb, 0xac, 0xf8, 0xe7, 0xc0, 0x14, + 0x60, 0x97, 0x71, 0xa0, 0x54, 0x2b, 0x94, 0x4c, + 0x13, 0x6e, 0xfb, 0x98, 0x11, 0x72, 0x60, 0x6a, + 0x0e, 0xd5, 0xb4, 0xe8, 0x17, 0x6c, 0x09, 0x2a, + 0xff, 0x89, 0xac, 0x88, 0xcd, 0x56, 0xe4, 0xcc, + 0x66, 0x79, 0x8c, 0xb7, 0xd2, 0x44, 0x44, 0x60, + 0xd8, 0x04, 0xa2, 0x50, 0x0f, + ], + x = [ + 0x01, 0x74, 0xe2, 0x0e, 0x0c, 0xa6, 0xd0, 0x12, + 0xb0, 0xc3, 0x86, 0xbc, 0xfc, 0x9a, 0xcb, 0x09, + 0x7a, 0xf9, 0xca, 0xb7, 0xc8, 0x79, 0x39, 0x3e, + 0xb6, 0x8e, 0x3e, 0x2f, 0x02, 0x6a, 0xfd, 0x07, + 0x65, 0xe9, 0x97, 0xe1, 0xf5, 0xf0, 0x16, 0x9a, + 0xa2, 0xe6, 0x03, 0x75, 0x1f, 0xa2, 0xf4, 0xe5, + 0xcd, 0x54, 0x94, 0x60, 0xb2, 0xfd, 0xe7, 0x97, + 0xea, 0x72, 0x02, 0xb9, 0x96, 0x48, 0xd3, 0x45, + 0xc2, 0x26, + ], + y = [ + 0x01, 0xae, 0xa0, 0x8f, 0x5b, 0x13, 0xe1, 0x85, + 0x4f, 0xfe, 0xcf, 0x73, 0x6a, 0x18, 0xc3, 0xfa, + 0xb7, 0xe6, 0xfc, 0xe5, 0xa9, 0x09, 0x8a, 0x68, + 0x4f, 0x49, 0x93, 0x59, 0xeb, 0xfd, 0x91, 0xf9, + 0x45, 0x1d, 0xcf, 0x51, 0x61, 0x39, 0x5c, 0x87, + 0x6c, 0x70, 0x9d, 0xfa, 0x7a, 0x86, 0x30, 0x64, + 0x3a, 0x4f, 0x48, 0x78, 0x3a, 0x2f, 0x9f, 0x84, + 0x07, 0xc1, 0x94, 0x5a, 0xc7, 0x1a, 0xe2, 0x5d, + 0x73, 0xb3, + ], + result = 0, + expected = [], + }, + // invalid b + multc { + a = [ + 0x04, 0x01, 0xb1, 0x0a, 0x39, 0x7f, 0x94, 0xe9, + 0x0b, 0x4f, 0x8f, 0xf1, 0xe8, 0x31, 0xca, 0x0a, + 0xda, 0x8f, 0x1c, 0x80, 0x1e, 0x1a, 0x95, 0x65, + 0xdb, 0x0f, 0x52, 0x7f, 0xaa, 0x14, 0x65, 0x6d, + 0xe9, 0xe6, 0x5c, 0xa2, 0x34, 0xc8, 0xea, 0x11, + 0x67, 0x4a, 0xc3, 0x5f, 0xce, 0x8b, 0xa8, 0xe8, + 0xe4, 0x8b, 0x6c, 0x9e, 0x5c, 0x0d, 0x37, 0xf0, + 0x4e, 0x33, 0xb8, 0xd1, 0x5f, 0xce, 0x90, 0x92, + 0xa7, 0x14, 0x07, 0x01, 0x1e, 0xde, 0x56, 0x92, + 0xcc, 0x39, 0xb6, 0xe9, 0xcf, 0xbe, 0xe0, 0xeb, + 0x35, 0x30, 0xfc, 0xf2, 0x2b, 0xc6, 0xe4, 0xfa, + 0x71, 0x2e, 0x2f, 0x87, 0x54, 0x83, 0xb1, 0x9c, + 0x96, 0xe2, 0xbb, 0x72, 0xca, 0x51, 0xf2, 0x58, + 0x25, 0x80, 0xf4, 0x47, 0xa3, 0xb6, 0x29, 0x45, + 0x3c, 0x28, 0xa7, 0x65, 0x85, 0x40, 0xb7, 0x2b, + 0x75, 0x38, 0x4a, 0x3e, 0x25, 0x0a, 0xb6, 0x58, + 0xe3, 0x9a, 0x7c, 0xde, 0xd5, + ], + b = [ + 0x04, 0x01, 0x01, 0xbf, 0xd2, 0xa7, 0xed, 0xe7, + 0x68, 0x5a, 0x86, 0x4b, 0xc8, 0x40, 0x42, 0x9d, + 0xea, 0x5c, 0xe5, 0x5e, 0x3d, 0x3d, 0x48, 0xc6, + 0x38, 0xb0, 0x7f, 0x54, 0x35, 0x67, 0x67, 0xb2, + 0x12, 0xa7, 0xba, 0x54, 0xc3, 0x22, 0x3f, 0x7d, + 0xff, 0xe2, 0x23, 0x5e, 0x72, 0x71, 0x2c, 0x24, + 0xd5, 0x91, 0xdc, 0x5c, 0xb2, 0xc7, 0x2d, 0x5c, + 0xe1, 0xf7, 0x17, 0x59, 0x09, 0xeb, 0xe4, 0x26, + 0xab, 0xe6, 0x6d, 0x51, 0x26, 0xb1, 0x8e, 0x19, + 0xb3, 0xe3, 0x72, 0x59, 0xf4, 0x15, 0xe3, 0x52, + 0x2a, 0xb3, 0xcb, 0x5c, 0xf8, 0xe7, 0xc0, 0x14, + 0x60, 0x97, 0x71, 0x50, 0x54, 0x2b, 0x94, 0x4c, + 0x13, 0x6e, 0xfb, 0x58, 0x11, 0x72, 0x60, 0x6a, + 0x0e, 0xd5, 0xb4, 0x58, 0x17, 0x6c, 0x09, 0x2a, + 0xff, 0x89, 0xac, 0x58, 0xcd, 0x56, 0xe4, 0xcc, + 0x66, 0x79, 0x8c, 0xb7, 0xd2, 0x44, 0x44, 0x60, + 0xd8, 0x04, 0xa2, 0x50, 0x0f, + ], + x = [ + 0x01, 0x74, 0xe2, 0x0e, 0x0c, 0xa6, 0xd0, 0x12, + 0xb0, 0xc3, 0x86, 0xbc, 0xfc, 0x9a, 0xcb, 0x09, + 0x7a, 0xf9, 0xca, 0xb7, 0xc8, 0x79, 0x39, 0x3e, + 0xb6, 0x8e, 0x3e, 0x2f, 0x02, 0x6a, 0xfd, 0x07, + 0x65, 0xe9, 0x97, 0xe1, 0xf5, 0xf0, 0x16, 0x9a, + 0xa2, 0xe6, 0x03, 0x75, 0x1f, 0xa2, 0xf4, 0xe5, + 0xcd, 0x54, 0x94, 0x60, 0xb2, 0xfd, 0xe7, 0x97, + 0xea, 0x72, 0x02, 0xb9, 0x96, 0x48, 0xd3, 0x45, + 0xc2, 0x26, + ], + y = [ + 0x01, 0xae, 0xa0, 0x8f, 0x5b, 0x13, 0xe1, 0x85, + 0x4f, 0xfe, 0xcf, 0x73, 0x6a, 0x18, 0xc3, 0xfa, + 0xb7, 0xe6, 0xfc, 0xe5, 0xa9, 0x09, 0x8a, 0x68, + 0x4f, 0x49, 0x93, 0x59, 0xeb, 0xfd, 0x91, 0xf9, + 0x45, 0x1d, 0xcf, 0x51, 0x61, 0x39, 0x5c, 0x87, + 0x6c, 0x70, 0x9d, 0xfa, 0x7a, 0x86, 0x30, 0x64, + 0x3a, 0x4f, 0x48, 0x78, 0x3a, 0x2f, 0x9f, 0x84, + 0x07, 0xc1, 0x94, 0x5a, 0xc7, 0x1a, 0xe2, 0x5d, + 0x73, 0xb3, + ], + result = 0, + expected = [], + }, + // invalid a and b + multc { + a = [ + 0x04, 0x01, 0xb1, 0x0a, 0x39, 0x7f, 0x94, 0xe9, + 0x0b, 0x4f, 0x8f, 0xf1, 0xe8, 0x31, 0xca, 0x0a, + 0xda, 0x8f, 0xfc, 0x80, 0x1e, 0x1a, 0x95, 0x65, + 0xdb, 0x0f, 0xf2, 0x7f, 0xaa, 0x14, 0x65, 0x6d, + 0xe9, 0xe6, 0xfc, 0xa2, 0x34, 0xc8, 0xea, 0x11, + 0x67, 0x4a, 0xf3, 0x5f, 0xce, 0x8b, 0xa8, 0xe8, + 0xe4, 0x8b, 0xfc, 0x9e, 0x5c, 0x0d, 0x37, 0xf0, + 0x4e, 0x33, 0xf8, 0xd1, 0x5f, 0xce, 0x90, 0x92, + 0xa7, 0x14, 0xf7, 0x01, 0x1e, 0xde, 0x56, 0x92, + 0xcc, 0x39, 0xf6, 0xe9, 0xcf, 0xbe, 0xe0, 0xeb, + 0x35, 0x30, 0xfc, 0xf2, 0x2b, 0xc6, 0xe4, 0xfa, + 0x71, 0x2e, 0xff, 0x87, 0x54, 0x83, 0xb1, 0x9c, + 0x96, 0xe2, 0xfb, 0x72, 0xca, 0x51, 0xf2, 0x58, + 0x25, 0x80, 0xf4, 0x47, 0xa3, 0xb6, 0x29, 0x45, + 0x3c, 0x28, 0xf7, 0x65, 0x85, 0x40, 0xb7, 0x2b, + 0x75, 0x38, 0x4a, 0x3e, 0x25, 0x0a, 0xb6, 0x58, + 0xe3, 0x9a, 0x7c, 0xde, 0xd5, + ], + b = [ + 0x04, 0x01, 0x01, 0xbf, 0xd2, 0xa7, 0xed, 0xe7, + 0x68, 0x5a, 0x06, 0x4b, 0xc8, 0x40, 0x42, 0x9d, + 0xea, 0x5c, 0x05, 0x3e, 0x3d, 0x3d, 0x48, 0xc6, + 0x38, 0xb0, 0x0f, 0xd4, 0x35, 0x67, 0x67, 0xb2, + 0x12, 0xa7, 0x0a, 0xd4, 0xc3, 0x22, 0x3f, 0x7d, + 0xff, 0xe2, 0x03, 0x8e, 0x72, 0x71, 0x2c, 0x24, + 0xd5, 0x91, 0x0c, 0x9c, 0xb2, 0xc7, 0x2d, 0x5c, + 0xe1, 0xf7, 0x07, 0x49, 0x09, 0xeb, 0xe4, 0x26, + 0xab, 0xe6, 0x0d, 0x01, 0x26, 0xb1, 0x8e, 0x19, + 0xb3, 0xe3, 0x72, 0xe9, 0xf4, 0x15, 0xe3, 0x52, + 0x2a, 0xb3, 0xcb, 0xac, 0xf8, 0xe7, 0xc0, 0x14, + 0x60, 0x97, 0x71, 0xa0, 0x54, 0x2b, 0x94, 0x4c, + 0x13, 0x6e, 0xfb, 0x98, 0x11, 0x72, 0x60, 0x6a, + 0x0e, 0xd5, 0xb4, 0xe8, 0x17, 0x6c, 0x09, 0x2a, + 0xff, 0x89, 0xac, 0x88, 0xcd, 0x56, 0xe4, 0xcc, + 0x66, 0x79, 0x8c, 0xb7, 0xd2, 0x44, 0x44, 0x60, + 0xd8, 0x04, 0xa2, 0x50, 0x0f, + ], + x = [ + 0x01, 0x74, 0xe2, 0x0e, 0x0c, 0xa6, 0xd0, 0x12, + 0xb0, 0xc3, 0x86, 0xbc, 0xfc, 0x9a, 0xcb, 0x09, + 0x7a, 0xf9, 0xca, 0xb7, 0xc8, 0x79, 0x39, 0x3e, + 0xb6, 0x8e, 0x3e, 0x2f, 0x02, 0x6a, 0xfd, 0x07, + 0x65, 0xe9, 0x97, 0xe1, 0xf5, 0xf0, 0x16, 0x9a, + 0xa2, 0xe6, 0x03, 0x75, 0x1f, 0xa2, 0xf4, 0xe5, + 0xcd, 0x54, 0x94, 0x60, 0xb2, 0xfd, 0xe7, 0x97, + 0xea, 0x72, 0x02, 0xb9, 0x96, 0x48, 0xd3, 0x45, + 0xc2, 0x26, + ], + y = [ + 0x01, 0xae, 0xa0, 0x8f, 0x5b, 0x13, 0xe1, 0x85, + 0x4f, 0xfe, 0xcf, 0x73, 0x6a, 0x18, 0xc3, 0xfa, + 0xb7, 0xe6, 0xfc, 0xe5, 0xa9, 0x09, 0x8a, 0x68, + 0x4f, 0x49, 0x93, 0x59, 0xeb, 0xfd, 0x91, 0xf9, + 0x45, 0x1d, 0xcf, 0x51, 0x61, 0x39, 0x5c, 0x87, + 0x6c, 0x70, 0x9d, 0xfa, 0x7a, 0x86, 0x30, 0x64, + 0x3a, 0x4f, 0x48, 0x78, 0x3a, 0x2f, 0x9f, 0x84, + 0x07, 0xc1, 0x94, 0x5a, 0xc7, 0x1a, 0xe2, 0x5d, + 0x73, 0xb3, + ], + result = 0, + expected = [], + }, + ]; + + tmuladd(p521, tcs); +}; diff --git a/crypto/ec/genkeys+test.ha b/crypto/ec/genkeys+test.ha @@ -7,6 +7,8 @@ use memio; @test fn keygen_p256() void = edgecases(p256); +@test fn keygen_p384() void = edgecases(p384); +@test fn keygen_p521() void = edgecases(p521); fn edgecases(c: *curve) void = { const scalarsz = len(c.order()); diff --git a/crypto/ec/prime.ha b/crypto/ec/prime.ha @@ -0,0 +1,769 @@ +// SPDX-License-Identifier: MPL-2.0 +// (c) Hare authors <https://harelang.org> + +// Ported from BearSSL +// +// Copyright (c) 2017 Thomas Pornin <pornin@bolet.org> +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +use bytes; +use crypto::math::*; +use crypto::bigint::*; +use io; + + +type curveparams = struct { + p: []word, + b: []word, + r2: []word, + p0i: word, + pointlen: size, + g: []u8, +}; + +// Parameters for supported curves (field modulus, and 'b' equation +// parameter; both values use the 'i31' format, and 'b' is in Montgomery +// representation). +const p384params = curveparams { + p = [ + 0x0000018C, 0x7FFFFFFF, 0x00000001, 0x00000000, 0x7FFFFFF8, + 0x7FFFFFEF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, + 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x00000FFF + ], + b = [ + 0x0000018C, 0x6E666840, 0x070D0392, 0x5D810231, 0x7651D50C, + 0x17E218D6, 0x1B192002, 0x44EFE441, 0x3A524E2B, 0x2719BA5F, + 0x41F02209, 0x36C5643E, 0x5813EFFE, 0x000008A5 + ], + r2 = [ + 0x0000018C, 0x00000000, 0x00000080, 0x7FFFFE00, 0x000001FF, + 0x00000800, 0x00000000, 0x7FFFE000, 0x00001FFF, 0x00008000, + 0x00008000, 0x00000000, 0x00000000, 0x00000000 + ], + p0i = 0x00000001, + pointlen = 97, + g = [ // XXX: use P384_G, when possible + 0x04, 0xaa, 0x87, 0xca, 0x22, 0xbe, 0x8b, 0x05, 0x37, 0x8e, + 0xb1, 0xc7, 0x1e, 0xf3, 0x20, 0xad, 0x74, 0x6e, 0x1d, 0x3b, + 0x62, 0x8b, 0xa7, 0x9b, 0x98, 0x59, 0xf7, 0x41, 0xe0, 0x82, + 0x54, 0x2a, 0x38, 0x55, 0x02, 0xf2, 0x5d, 0xbf, 0x55, 0x29, + 0x6c, 0x3a, 0x54, 0x5e, 0x38, 0x72, 0x76, 0x0a, 0xb7, 0x36, + 0x17, 0xde, 0x4a, 0x96, 0x26, 0x2c, 0x6f, 0x5d, 0x9e, 0x98, + 0xbf, 0x92, 0x92, 0xdc, 0x29, 0xf8, 0xf4, 0x1d, 0xbd, 0x28, + 0x9a, 0x14, 0x7c, 0xe9, 0xda, 0x31, 0x13, 0xb5, 0xf0, 0xb8, + 0xc0, 0x0a, 0x60, 0xb1, 0xce, 0x1d, 0x7e, 0x81, 0x9d, 0x7a, + 0x43, 0x1d, 0x7c, 0x90, 0xea, 0x0e, 0x5f, + ], +}; + +const p521params = curveparams { + p = [ + 0x00000219, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, + 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, + 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, + 0x7FFFFFFF, 0x7FFFFFFF, 0x01FFFFFF + ], + b = [ + 0x00000219, 0x540FC00A, 0x228FEA35, 0x2C34F1EF, 0x67BF107A, + 0x46FC1CD5, 0x1605E9DD, 0x6937B165, 0x272A3D8F, 0x42785586, + 0x44C8C778, 0x15F3B8B4, 0x64B73366, 0x03BA8B69, 0x0D05B42A, + 0x21F929A2, 0x2C31C393, 0x00654FAE + ], + r2 = [ + 0x00000219, 0x00001000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000 + ], + p0i = 0x00000001, + pointlen = 133, + g = [ // XXX: use P384_G, when possible + 0x04, 0x00, 0xC6, 0x85, 0x8E, 0x06, 0xB7, 0x04, 0x04, 0xE9, + 0xCD, 0x9E, 0x3E, 0xCB, 0x66, 0x23, 0x95, 0xB4, 0x42, 0x9C, + 0x64, 0x81, 0x39, 0x05, 0x3F, 0xB5, 0x21, 0xF8, 0x28, 0xAF, + 0x60, 0x6B, 0x4D, 0x3D, 0xBA, 0xA1, 0x4B, 0x5E, 0x77, 0xEF, + 0xE7, 0x59, 0x28, 0xFE, 0x1D, 0xC1, 0x27, 0xA2, 0xFF, 0xA8, + 0xDE, 0x33, 0x48, 0xB3, 0xC1, 0x85, 0x6A, 0x42, 0x9B, 0xF9, + 0x7E, 0x7E, 0x31, 0xC2, 0xE5, 0xBD, 0x66, 0x01, 0x18, 0x39, + 0x29, 0x6A, 0x78, 0x9A, 0x3B, 0xC0, 0x04, 0x5C, 0x8A, 0x5F, + 0xB4, 0x2C, 0x7D, 0x1B, 0xD9, 0x98, 0xF5, 0x44, 0x49, 0x57, + 0x9B, 0x44, 0x68, 0x17, 0xAF, 0xBD, 0x17, 0x27, 0x3E, 0x66, + 0x2C, 0x97, 0xEE, 0x72, 0x99, 0x5E, 0xF4, 0x26, 0x40, 0xC5, + 0x50, 0xB9, 0x01, 0x3F, 0xAD, 0x07, 0x61, 0x35, 0x3C, 0x70, + 0x86, 0xA2, 0x72, 0xC2, 0x40, 0x88, 0xBE, 0x94, 0x76, 0x9F, + 0xD1, 0x66, 0x50, + ], +}; + +@test fn bigint_support() void = { + // This is a port of the i31 variant from BearSSL. Word must be an u32 + // in order for this code to work. + static assert(size(word) == 4); +}; + +def MAX_COORDSZ = (MAX_COORDBITSZ + 61) / 31; + +type prime_jacobian = struct { + // TODO things would be a lot easier if this is a flat array + c: [3][MAX_COORDSZ]word, +}; +type reg = []word; + +// XXX: BearSSL is using memcpy. Can I do this here also? +fn jcpy(d: *prime_jacobian, a: *prime_jacobian) void = { + for (let i = 0z; i < len(d.c); i += 1) { + d.c[i][..] = a.c[i][..]; // XXX: is this copy ct? + }; +}; + +fn jccpy(ctl: u32, d: *prime_jacobian, a: *prime_jacobian) void = { + for (let i = 0z; i < len(d.c); i += 1) { + ccopyu32(ctl, d.c[i]: []u32, a.c[i][..]: []u32); + }; +}; + +fn mset(d: reg, a: reg) void = { + for (let i = 0z; i < len(d); i += 1) { + d[i] = a[i]; + }; +}; + +fn madd(d: reg, a: reg, cc: *curveparams) void = { + let ctl = add(d, a, 1); + ctl |= notu32(sub(d, cc.p, 0)); + sub(d, cc.p, ctl); +}; + +fn msub(d: reg, a: reg, cc: *curveparams) void = { + add(d, cc.p, sub(d, a, 1)); +}; + +fn mmul(d: reg, a: reg, b: reg, cc: *curveparams) void = { + montymul(d, a, b, cc.p, cc.p0i); +}; + +fn minv(d: reg, a: reg, b: reg, cc: *curveparams) void = { + let tp: [MAX_POINTSZ / 2]u8 = [0...]; + let plen = (cc.p[0] - (cc.p[0] >> 5) + 7) >> 3; + decode(tp[..plen], cc.p); + tp[plen - 1] -= 2; + + // XXX: change modpow to use two bigints as buf, like it was intended + let buf: [2 * MAX_COORDSZ]word = [0...]; + modpow(d, tp[..plen], cc.p, cc.p0i, buf); +}; + +fn prime_zero(p: *prime_jacobian, cc: *curveparams) void = { + for (let i = 0z; i < len(p.c); i += 1) { + for (let j = 0z; j < len(p.c[i]); j += 1) { + p.c[i][j] = 0; + }; + }; + + p.c[0][0] = cc.p[0]; + p.c[1][0] = cc.p[0]; + p.c[2][0] = cc.p[0]; +}; + +// Doubling formulas are: +// +// s = 4*x*y^2 +// m = 3*(x + z^2)*(x - z^2) +// x' = m^2 - 2*s +// y' = m*(s - x') - 8*y^4 +// z' = 2*y*z +// +// If y = 0 (P has order 2) then this yields infinity (z' = 0), as it +// should. This case should not happen anyway, because our curves have +// prime order, and thus do not contain any point of order 2. +// +// If P is infinity (z = 0), then again the formulas yield infinity, +// which is correct. Thus, this code works for all points. +// +// Cost: 8 multiplications +fn point_double(p: *prime_jacobian, cc: *curveparams) void = { + let px: [MAX_COORDSZ]word = p.c[0]; + let py: [MAX_COORDSZ]word = p.c[1]; + let pz: [MAX_COORDSZ]word = p.c[2]; + let t1: [MAX_COORDSZ]word = [0...]; + let t2: [MAX_COORDSZ]word = [0...]; + let t3: [MAX_COORDSZ]word = [0...]; + let t4: [MAX_COORDSZ]word = [0...]; + + // Compute z^2 (in t1). + mmul(t1, pz, pz, cc); + + // Compute x-z^2 (in t2) and then x+z^2 (in t1). + mset(t2, px); + msub(t2, t1, cc); + madd(t1, px, cc); + + // Compute m = 3*(x+z^2)*(x-z^2) (in t1). + mmul(t3, t1, t2, cc); + mset(t1, t3); + madd(t1, t3, cc); + madd(t1, t3, cc); + + // Compute s = 4*x*y^2 (in t2) and 2*y^2 (in t3). + mmul(t3, py, py, cc); + madd(t3, t3, cc); + mmul(t2, px, t3, cc); + madd(t2, t2, cc); + + // Compute x' = m^2 - 2*s. + mmul(px, t1, t1, cc); + msub(px, t2, cc); + msub(px, t2, cc); + + // Compute z' = 2*y*z. + mmul(t4, py, pz, cc); + mset(pz, t4); + madd(pz, t4, cc); + + // Compute y' = m*(s - x') - 8*y^4. Note that we already have + // 2*y^2 in t3. + msub(t2, px, cc); + mmul(py, t1, t2, cc); + mmul(t4, t3, t3, cc); + msub(py, t4, cc); + msub(py, t4, cc); + + // copy back result + p.c[0][..] = px[..]; + p.c[1][..] = py[..]; + p.c[2][..] = pz[..]; +}; + +// Addtions formulas are: +// +// u1 = x1 * z2^2 +// u2 = x2 * z1^2 +// s1 = y1 * z2^3 +// s2 = y2 * z1^3 +// h = u2 - u1 +// r = s2 - s1 +// x3 = r^2 - h^3 - 2 * u1 * h^2 +// y3 = r * (u1 * h^2 - x3) - s1 * h^3 +// z3 = h * z1 * z2 +// +// If both P1 and P2 are infinity, then z1 == 0 and z2 == 0, implying that +// z3 == 0, so the result is correct. +// If either of P1 or P2 is infinity, but not both, then z3 == 0, which is +// not correct. +// h == 0 only if u1 == u2; this happens in two cases: +// -- if s1 == s2 then P1 and/or P2 is infinity, or P1 == P2 +// -- if s1 != s2 then P1 + P2 == infinity (but neither P1 or P2 is infinity) +// +// Thus, the following situations are not handled correctly: +// -- P1 = 0 and P2 != 0 +// -- P1 != 0 and P2 = 0 +// -- P1 = P2 +// All other cases are properly computed. However, even in "incorrect" +// situations, the three coordinates still are properly formed field +// elements. +// +// The returned flag is cleared if r == 0. This happens in the following +// cases: +// -- Both points are on the same horizontal line (same Y coordinate). +// -- Both points are infinity. +// -- One point is infinity and the other is on line Y = 0. +// The third case cannot happen with our curves (there is no valid point +// on line Y = 0 since that would be a point of order 2). If the two +// source points are non-infinity, then remains only the case where the +// two points are on the same horizontal line. +// +// This allows us to detect the "P1 == P2" case, assuming that P1 != 0 and +// P2 != 0: +// -- If the returned value is not the point at infinity, then it was properly +// computed. +// -- Otherwise, if the returned flag is 1, then P1+P2 = 0, and the result +// is indeed the point at infinity. +// -- Otherwise (result is infinity, flag is 0), then P1 = P2 and we should +// use the 'double' code. +// +// Cost: 16 multiplications +fn point_add(p1: *prime_jacobian, p2: *prime_jacobian, cc: *curveparams) u32 = { + let p1x: [MAX_COORDSZ]word = p1.c[0]; + let p1y: [MAX_COORDSZ]word = p1.c[1]; + let p1z: [MAX_COORDSZ]word = p1.c[2]; + let p2x: [MAX_COORDSZ]word = p2.c[0]; + let p2y: [MAX_COORDSZ]word = p2.c[1]; + let p2z: [MAX_COORDSZ]word = p2.c[2]; + let t1: [MAX_COORDSZ]word = [0...]; + let t2: [MAX_COORDSZ]word = [0...]; + let t3: [MAX_COORDSZ]word = [0...]; + let t4: [MAX_COORDSZ]word = [0...]; + let t5: [MAX_COORDSZ]word = [0...]; + let t6: [MAX_COORDSZ]word = [0...]; + let t7: [MAX_COORDSZ]word = [0...]; + + let r: u32 = 1; + + // Compute u1 = x1*z2^2 (in t1) and s1 = y1*z2^3 (in t3). + mmul(t3, p2z, p2z, cc); + mmul(t1, p1x, t3, cc); + mmul(t4, p2z, t3, cc); + mmul(t3, p1y, t4, cc); + + // Compute u2 = x2*z1^2 (in t2) and s2 = y2*z1^3 (in t4). + mmul(t4, p1z, p1z, cc); + mmul(t2, p2x, t4, cc); + mmul(t5, p1z, t4, cc); + mmul(t4, p2y, t5, cc); + + // Compute h = u2 - u1 (in t2) and r = s2 - s1 (in t4). + msub(t2, t1, cc); + msub(t4, t3, cc); + + // Report cases where r = 0 through the returned flag. + r &= ~iszero(t4); + + // Compute u1*h^2 (in t6) and h^3 (in t5). + mmul(t7, t2, t2, cc); + mmul(t6, t1, t7, cc); + mmul(t5, t7, t2, cc); + + // Compute x3 = r^2 - h^3 - 2*u1*h^2. + // t1 and t7 can be used as scratch registers. + mmul(p1x, t4, t4, cc); + msub(p1x, t5, cc); + msub(p1x, t6, cc); + msub(p1x, t6, cc); + + // Compute y3 = r*(u1*h^2 - x3) - s1*h^3. + msub(t6, p1x, cc); + mmul(p1y, t4, t6, cc); + mmul(t1, t5, t3, cc); + msub(p1y, t1, cc); + + // Compute z3 = h*z1*z2. + mmul(t1, p1z, p2z, cc); + mmul(p1z, t1, t2, cc); + + // copy back result + p1.c[0][..] = p1x[..]; + p1.c[1][..] = p1y[..]; + p1.c[2][..] = p1z[..]; + + return r; +}; + +// Check that the point is on the curve. This code snippet assumes the +// following conventions: +// -- Coordinates x and y have been freshly decoded in P1 (but not +// converted to Montgomery coordinates yet). +// -- P2x, P2y and P2z are set to, respectively, R^2, b*R and 1. +fn prime_check(p1: *prime_jacobian, p2: *prime_jacobian, cc: *curveparams) u32 = { + let p1x: [MAX_COORDSZ]word = p1.c[0]; + let p1y: [MAX_COORDSZ]word = p1.c[1]; + let p1z: [MAX_COORDSZ]word = p1.c[2]; + let p2x: [MAX_COORDSZ]word = p2.c[0]; + let p2y: [MAX_COORDSZ]word = p2.c[1]; + let p2z: [MAX_COORDSZ]word = p2.c[2]; + let t1: [MAX_COORDSZ]word = [0...]; + let t2: [MAX_COORDSZ]word = [0...]; + + let r: u32 = 1; + + // Convert x and y to Montgomery representation. + mmul(t1, p1x, p2x, cc); + mmul(t2, p1y, p2x, cc); + mset(p1x, t1); + mset(p1y, t2); + + // Compute x^3 in t1. */ + mmul(t2, p1x, p1x, cc); + mmul(t1, p1x, t2, cc); + + // Subtract 3*x from t1. */ + msub(t1, p1x, cc); + msub(t1, p1x, cc); + msub(t1, p1x, cc); + + // Add b. */ + madd(t1, p2y, cc); + + // Compute y^2 in t2. */ + mmul(t2, p1y, p1y, cc); + + // Compare y^2 with x^3 - 3*x + b; they must match. */ + msub(t1, t2, cc); + r &= ~iszero(t1); + + // Set z to 1 (in Montgomery representation). */ + mmul(p1z, p2x, p2z, cc); + + // copy back result + p1.c[0][..] = p1x[..]; + p1.c[1][..] = p1y[..]; + p1.c[2][..] = p1z[..]; + + return r; +}; + +// Conversion back to affine coordinates. This code snippet assumes that +// the z coordinate of P2 is set to 1 (not in Montgomery representation). +fn prime_affine(p1: *prime_jacobian, p2: *prime_jacobian, cc: *curveparams) void = { + let p1x: [MAX_COORDSZ]word = p1.c[0]; + let p1y: [MAX_COORDSZ]word = p1.c[1]; + let p1z: [MAX_COORDSZ]word = p1.c[2]; + let p2x: [MAX_COORDSZ]word = p2.c[0]; + let p2y: [MAX_COORDSZ]word = p2.c[1]; + let p2z: [MAX_COORDSZ]word = p2.c[2]; + let t1: [MAX_COORDSZ]word = [0...]; + let t2: [MAX_COORDSZ]word = [0...]; + let t3: [MAX_COORDSZ]word = [0...]; + let t4: [MAX_COORDSZ]word = [0...]; + let t5: [MAX_COORDSZ]word = [0...]; + let t6: [MAX_COORDSZ]word = [0...]; + let t7: [MAX_COORDSZ]word = [0...]; + + let r: u32 = 1; + + // Save z*R in t1. */ + mset(t1, p1z); + + // Compute z^3 in t2. */ + mmul(t2, p1z, p1z, cc); + mmul(t3, p1z, t2, cc); + mmul(t2, t3, p2z, cc); + + // Invert to (1/z^3) in t2. */ + minv(t2, t3, t4, cc); + + // Compute y. */ + mset(t3, p1y); + mmul(p1y, t2, t3, cc); + + // Compute (1/z^2) in t3. */ + mmul(t3, t2, t1, cc); + + // Compute x. */ + mset(t2, p1x); + mmul(p1x, t2, t3, cc); + + // copy back result + p1.c[0][..] = p1x[..]; + p1.c[1][..] = p1y[..]; + p1.c[2][..] = p1z[..]; +}; + +fn prime_setone(x: []word, p: []word) void = { + for (let i = 0z; i < len(x); i += 1) { + x[i] = 0; + }; + x[0] = p[0]; + x[1] = 1; +}; + +fn prime_pointzero(p: *prime_jacobian) void = { + // XXX: bytes::zero? + for (let i = 0z; i < len(p.c); i += 1) { + for (let j = 0z; j < len(p.c[i]); j += 1) { + p.c[i][j] = 0; + }; + }; +}; + +fn point_mul(p: *prime_jacobian, x: []u8, cc: *curveparams) void = { + // We do a simple double-and-add ladder with a 2-bit window + // to make only one add every two doublings. We thus first + // precompute 2P and 3P in some local buffers. + // + // We always perform two doublings and one addition; the + // addition is with P, 2P and 3P and is done in a temporary + // array. + // + // The addition code cannot handle cases where one of the + // operands is infinity, which is the case at the start of the + // ladder. We therefore need to maintain a flag that controls + // this situation. + let p2 = prime_jacobian { ... }; + let p3 = prime_jacobian { ... }; + let q = prime_jacobian { ... }; + let t = prime_jacobian { ... }; + let u = prime_jacobian { ... }; + + jcpy(&p2, p); + point_double(&p2, cc); + + jcpy(&p3, p); + point_add(&p3, &p2, cc); + + prime_zero(&q, cc); + let qz: u32 = 1; + for (let i = 0z; i < len(x); i += 1) { + for (let k: i32 = 6; k >= 0; k -= 2) { + point_double(&q, cc); + point_double(&q, cc); + jcpy(&t, p); + jcpy(&u, &q); + let bits: u32 = (x[i]: u32 >> k: u32) & 3; + let bnz: u32 = nequ32(bits, 0); + jccpy(equ32(bits, 2), &t, &p2); + jccpy(equ32(bits, 3), &t, &p3); + point_add(&u, &t, cc); + jccpy(bnz & qz, &q, &t); + jccpy(bnz & ~qz, &q, &u); + qz &= ~bnz; + }; + }; + jcpy(p, &q); +}; + +// Decode point into Jacobian coordinates. This function does not support +// the point at infinity. If the point is invalid then this returns 0, but +// the coordinates are still set to properly formed field elements. +fn point_decode(p: *prime_jacobian, src: []u8, cc: *curveparams) u32 = { + // Points must use uncompressed format: + // -- first byte is 0x04; + // -- coordinates X and Y use unsigned big-endian, with the same + // length as the field modulus. + // + // We don't support hybrid format (uncompressed, but first byte + // has value 0x06 or 0x07, depending on the least significant bit + // of Y) because it is rather useless, and explicitly forbidden + // by PKIX (RFC 5480, section 2.2). + // + // We don't support compressed format either, because it is not + // much used in practice (there are or were patent-related + // concerns about point compression, which explains the lack of + // generalised support). Also, point compression support would + // need a bit more code. + let q = prime_jacobian { ... }; + + let buf = src; + prime_pointzero(p); + let plen: size = (cc.p[0] - (cc.p[0] >> 5) + 7) >> 3; + if (len(src) != 1 + (plen << 1)) { + return 0; + }; + let r: u32 = encodemod(p.c[0], buf[1..1 + plen], cc.p); + r &= encodemod(p.c[1], buf[1 + plen..1 + 2*plen], cc.p); + + // Check first byte. + r &= equ32(buf[0], 0x04); + + // Convert coordinates and check that the point is valid. + let zlen: size = ((cc.p[0] + 63) >> 5); + + q.c[0][..zlen] = cc.r2[..zlen]; + q.c[1][..zlen] = cc.b[..zlen]; + prime_setone(q.c[2], cc.p); + + r &= ~prime_check(p, &q, cc); + return r; +}; + +// Encode a point. This method assumes that the point is correct and is +// not the point at infinity. Encoded size is always 1+2*plen, where +// plen is the field modulus length, in bytes. +fn point_encode(dest: []u8, p: *prime_jacobian, cc: *curveparams) void = { + let q = prime_jacobian { ... }; + let t = prime_jacobian { ... }; + + let xbl: u32 = cc.p[0]; + xbl -= (xbl >> 5); + let plen: size = (xbl + 7) >> 3; + dest[0] = 0x04; + jcpy(&q, p); + prime_setone(t.c[2], cc.p); + + prime_affine(&q, &t, cc); + decode(dest[1..1+plen], q.c[0]); + decode(dest[1+plen..1+2*plen], q.c[1]); +}; + +fn prime_mul(g: []u8, x: []u8, cc: *curveparams) u32 = { + if (len(g) != cc.pointlen) { + return 0; + }; + let p = prime_jacobian { ... }; + let r = point_decode(&p, g, cc); + point_mul(&p, x, cc); + point_encode(g, &p, cc); + + return r; +}; + +const P384_G: [_]u8 = [ + 0x04, 0xaa, 0x87, 0xca, 0x22, 0xbe, 0x8b, 0x05, 0x37, 0x8e, 0xb1, 0xc7, + 0x1e, 0xf3, 0x20, 0xad, 0x74, 0x6e, 0x1d, 0x3b, 0x62, 0x8b, 0xa7, 0x9b, + 0x98, 0x59, 0xf7, 0x41, 0xe0, 0x82, 0x54, 0x2a, 0x38, 0x55, 0x02, 0xf2, + 0x5d, 0xbf, 0x55, 0x29, 0x6c, 0x3a, 0x54, 0x5e, 0x38, 0x72, 0x76, 0x0a, + 0xb7, 0x36, 0x17, 0xde, 0x4a, 0x96, 0x26, 0x2c, 0x6f, 0x5d, 0x9e, 0x98, + 0xbf, 0x92, 0x92, 0xdc, 0x29, 0xf8, 0xf4, 0x1d, 0xbd, 0x28, 0x9a, 0x14, + 0x7c, 0xe9, 0xda, 0x31, 0x13, 0xb5, 0xf0, 0xb8, 0xc0, 0x0a, 0x60, 0xb1, + 0xce, 0x1d, 0x7e, 0x81, 0x9d, 0x7a, 0x43, 0x1d, 0x7c, 0x90, 0xea, 0x0e, + 0x5f, +]; + +fn p384_generator() const []u8 = P384_G; + +const P384_N: [_]u8 = [ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc7, 0x63, 0x4d, 0x81, 0xf4, 0x37, 0x2d, 0xdf, 0x58, 0x1a, 0x0d, 0xb2, + 0x48, 0xb0, 0xa7, 0x7a, 0xec, 0xec, 0x19, 0x6a, 0xcc, 0xc5, 0x29, 0x73, +]; + +fn p384_order() const []u8 = P384_N; + +fn p384_mul(p: []u8, x: []u8) u32 = { + return prime_mul(p, x, &p384params); +}; + + +fn p384_mulgen(r: []u8, x: []u8) size = { + const g = P384_G[..]; + r[..len(g)] = P384_G[..]; + p384_mul(r[..len(g)], x); + return len(g); +}; + +fn prime_muladd(cc: *curveparams, a: []u8, b: []u8, x: []u8, y: []u8) u32 = { + let p = prime_jacobian { ... }; + let q = prime_jacobian { ... }; + + // TODO: see about merging the two ladders. Right now, we do + // two independent point multiplications, which is a bit + // wasteful of CPU resources (but yields short code). + + if (len(a) != cc.pointlen) { + return 0; + }; + let r = point_decode(&p, a, cc); + if (len(b) == 0) { + b = cc.g[..]; + }; + r &= point_decode(&q, b, cc); + point_mul(&p, x, cc); + point_mul(&q, y, cc); + + // We want to compute P+Q. Since the base points A and B are distinct + // from infinity, and the multipliers are non-zero and lower than the + // curve order, then we know that P and Q are non-infinity. This + // leaves two special situations to test for: + // -- If P = Q then we must use point_double(). + // -- If P+Q = 0 then we must report an error. + let t = point_add(&p, &q, cc); + point_double(&q, cc); + let z = iszero(p.c[2]); + + // If z is 1 then either P+Q = 0 (t = 1) or P = Q (t = 0). So we + // have the following: + // + // z = 0, t = 0 return P (normal addition) + // z = 0, t = 1 return P (normal addition) + // z = 1, t = 0 return Q (a 'double' case) + // z = 1, t = 1 report an error (P+Q = 0) + jccpy(z & ~t, &p, &q); + point_encode(a, &p, cc); + r &= ~(z & t); + + return r; +}; + +fn p384_muladd(a: []u8, b: []u8, x: []u8, y: []u8) u32 = + prime_muladd(&p384params, a, b, x, y); + +const _p384: curve = curve { + pointsz = P384_POINTSZ, + order = &p384_order, + generator = &p384_generator, + mul = &p384_mul, + mulgen = &p384_mulgen, + muladd = &p384_muladd, + keygen = &mask_keygen, +}; + +// Size of a [[p384]] point in bytes. +export def P384_POINTSZ = len(P384_G); + +// Size of a [[p384]] scalar in bytes. +export def P384_SCALARSZ = len(P384_N); + +// A [[curve]] implementation of P-384, also known as secp384r1; +export const p384: *curve = &_p384; + +const P521_N: [_]u8 = [ + 0x01, 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, 0xFF, 0xFF, 0xFA, 0x51, 0x86, + 0x87, 0x83, 0xBF, 0x2F, 0x96, 0x6B, 0x7F, 0xCC, 0x01, 0x48, 0xF7, 0x09, + 0xA5, 0xD0, 0x3B, 0xB5, 0xC9, 0xB8, 0x89, 0x9C, 0x47, 0xAE, 0xBB, 0x6F, + 0xB7, 0x1E, 0x91, 0x38, 0x64, 0x09, +]; + +fn p521_order() const []u8 = P521_N; + +const P521_G: [_]u8 = [ + 0x04, 0x00, 0xC6, 0x85, 0x8E, 0x06, 0xB7, 0x04, 0x04, 0xE9, 0xCD, 0x9E, + 0x3E, 0xCB, 0x66, 0x23, 0x95, 0xB4, 0x42, 0x9C, 0x64, 0x81, 0x39, 0x05, + 0x3F, 0xB5, 0x21, 0xF8, 0x28, 0xAF, 0x60, 0x6B, 0x4D, 0x3D, 0xBA, 0xA1, + 0x4B, 0x5E, 0x77, 0xEF, 0xE7, 0x59, 0x28, 0xFE, 0x1D, 0xC1, 0x27, 0xA2, + 0xFF, 0xA8, 0xDE, 0x33, 0x48, 0xB3, 0xC1, 0x85, 0x6A, 0x42, 0x9B, 0xF9, + 0x7E, 0x7E, 0x31, 0xC2, 0xE5, 0xBD, 0x66, 0x01, 0x18, 0x39, 0x29, 0x6A, + 0x78, 0x9A, 0x3B, 0xC0, 0x04, 0x5C, 0x8A, 0x5F, 0xB4, 0x2C, 0x7D, 0x1B, + 0xD9, 0x98, 0xF5, 0x44, 0x49, 0x57, 0x9B, 0x44, 0x68, 0x17, 0xAF, 0xBD, + 0x17, 0x27, 0x3E, 0x66, 0x2C, 0x97, 0xEE, 0x72, 0x99, 0x5E, 0xF4, 0x26, + 0x40, 0xC5, 0x50, 0xB9, 0x01, 0x3F, 0xAD, 0x07, 0x61, 0x35, 0x3C, 0x70, + 0x86, 0xA2, 0x72, 0xC2, 0x40, 0x88, 0xBE, 0x94, 0x76, 0x9F, 0xD1, 0x66, + 0x50 +]; + +fn p521_generator() const []u8 = P521_G; + + +fn p521_mul(p: []u8, x: []u8) u32 = { + return prime_mul(p, x, &p521params); +}; + +fn p521_mulgen(r: []u8, x: []u8) size = { + const g = P521_G[..]; + r[..len(g)] = P521_G[..]; + p521_mul(r[..len(g)], x); + return len(g); +}; + +fn p521_muladd(a: []u8, b: []u8, x: []u8, y: []u8) u32 = + prime_muladd(&p521params, a, b, x, y); + +const _p521: curve = curve { + pointsz = P521_POINTSZ, + order = &p521_order, + generator = &p521_generator, + mul = &p521_mul, + mulgen = &p521_mulgen, + muladd = &p521_muladd, + keygen = &mask_keygen, +}; + +// Size of a [[p521]] point in bytes. +export def P521_POINTSZ = len(P521_G); + +// Size of a [[p521]] scalar in bytes. +export def P521_SCALARSZ = len(P521_N); + +// A [[curve]] implementation of P-521, also known as secp521r1; +export const p521: *curve = &_p521; +