commit 88ee0e0928dd7d4ea07b0c2051739e46e17fd345
parent 9b4aae71a4adc26c22f85ffbeb596c6a44328403
Author: Armin Preiml <apreiml@strohwolke.at>
Date: Sun, 15 May 2022 18:59:45 +0200
crypto::cipher: implement gcm
Signed-off-by: Armin Preiml <apreiml@strohwolke.at>
Diffstat:
5 files changed, 1041 insertions(+), 11 deletions(-)
diff --git a/crypto/aes/+test/gcm.ha b/crypto/aes/+test/gcm.ha
@@ -0,0 +1,654 @@
+use bufio;
+use bytes;
+use crypto::cipher;
+use errors;
+use io;
+
+type gcmtestcase = struct {
+ key: []u8,
+ iv: []u8,
+ plain: []u8,
+ additional: []u8,
+ cipher: []u8,
+ tag: [16]u8,
+};
+
+// Testcases from Appendix B of https://csrc.nist.rip/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf
+const gcmtestcases: []gcmtestcase = [
+ gcmtestcase {
+ key = [0...]: [16]u8,
+ iv = [0...]: [12]u8,
+ plain = [],
+ cipher = [],
+ tag = [
+ 0x58, 0xe2, 0xfc, 0xce, 0xfa, 0x7e, 0x30, 0x61, 0x36,
+ 0x7f, 0x1d, 0x57, 0xa4, 0xe7, 0x45, 0x5a,
+ ],
+ ...
+ },
+ gcmtestcase {
+ key = [0...]: [16]u8,
+ iv = [0...]: [12]u8,
+ plain = [0...]: [16]u8,
+ cipher = [
+ 0x03, 0x88, 0xda, 0xce, 0x60, 0xb6, 0xa3, 0x92, 0xf3,
+ 0x28, 0xc2, 0xb9, 0x71, 0xb2, 0xfe, 0x78,
+ ],
+ tag = [
+ 0xab, 0x6e, 0x47, 0xd4, 0x2c, 0xec, 0x13, 0xbd, 0xf5,
+ 0x3a, 0x67, 0xb2, 0x12, 0x57, 0xbd, 0xdf,
+ ],
+ ...
+ },
+ gcmtestcase {
+ key = [
+ 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, 0x6d,
+ 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08,
+ ],
+ plain = [
+ 0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, 0xa5,
+ 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a, 0x86, 0xa7,
+ 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda, 0x2e, 0x4c, 0x30,
+ 0x3d, 0x8a, 0x31, 0x8a, 0x72, 0x1c, 0x3c, 0x0c, 0x95,
+ 0x95, 0x68, 0x09, 0x53, 0x2f, 0xcf, 0x0e, 0x24, 0x49,
+ 0xa6, 0xb5, 0x25, 0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d,
+ 0xe6, 0x57, 0xba, 0x63, 0x7b, 0x39, 0x1a, 0xaf, 0xd2,
+ 0x55,
+ ],
+ iv = [
+ 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad, 0xde,
+ 0xca, 0xf8, 0x88,
+ ],
+ cipher = [
+ 0x42, 0x83, 0x1e, 0xc2, 0x21, 0x77, 0x74, 0x24, 0x4b,
+ 0x72, 0x21, 0xb7, 0x84, 0xd0, 0xd4, 0x9c, 0xe3, 0xaa,
+ 0x21, 0x2f, 0x2c, 0x02, 0xa4, 0xe0, 0x35, 0xc1, 0x7e,
+ 0x23, 0x29, 0xac, 0xa1, 0x2e, 0x21, 0xd5, 0x14, 0xb2,
+ 0x54, 0x66, 0x93, 0x1c, 0x7d, 0x8f, 0x6a, 0x5a, 0xac,
+ 0x84, 0xaa, 0x05, 0x1b, 0xa3, 0x0b, 0x39, 0x6a, 0x0a,
+ 0xac, 0x97, 0x3d, 0x58, 0xe0, 0x91, 0x47, 0x3f, 0x59,
+ 0x85,
+ ],
+ tag = [
+ 0x4d, 0x5c, 0x2a, 0xf3, 0x27, 0xcd, 0x64, 0xa6, 0x2c,
+ 0xf3, 0x5a, 0xbd, 0x2b, 0xa6, 0xfa, 0xb4,
+ ],
+ ...
+ },
+ gcmtestcase {
+ key = [
+ 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, 0x6d,
+ 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08,
+ ],
+ plain = [
+ 0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, 0xa5,
+ 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a, 0x86, 0xa7,
+ 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda, 0x2e, 0x4c, 0x30,
+ 0x3d, 0x8a, 0x31, 0x8a, 0x72, 0x1c, 0x3c, 0x0c, 0x95,
+ 0x95, 0x68, 0x09, 0x53, 0x2f, 0xcf, 0x0e, 0x24, 0x49,
+ 0xa6, 0xb5, 0x25, 0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d,
+ 0xe6, 0x57, 0xba, 0x63, 0x7b, 0x39,
+ ],
+ additional = [
+ 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, 0xfe,
+ 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, 0xab, 0xad,
+ 0xda, 0xd2,
+ ],
+ iv = [
+ 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad, 0xde,
+ 0xca, 0xf8, 0x88,
+ ],
+ cipher = [
+ 0x42, 0x83, 0x1e, 0xc2, 0x21, 0x77, 0x74, 0x24, 0x4b,
+ 0x72, 0x21, 0xb7, 0x84, 0xd0, 0xd4, 0x9c, 0xe3, 0xaa,
+ 0x21, 0x2f, 0x2c, 0x02, 0xa4, 0xe0, 0x35, 0xc1, 0x7e,
+ 0x23, 0x29, 0xac, 0xa1, 0x2e, 0x21, 0xd5, 0x14, 0xb2,
+ 0x54, 0x66, 0x93, 0x1c, 0x7d, 0x8f, 0x6a, 0x5a, 0xac,
+ 0x84, 0xaa, 0x05, 0x1b, 0xa3, 0x0b, 0x39, 0x6a, 0x0a,
+ 0xac, 0x97, 0x3d, 0x58, 0xe0, 0x91,
+ ],
+ tag = [
+ 0x5b, 0xc9, 0x4f, 0xbc, 0x32, 0x21, 0xa5, 0xdb, 0x94,
+ 0xfa, 0xe9, 0x5a, 0xe7, 0x12, 0x1a, 0x47,
+ ],
+ },
+ gcmtestcase {
+ key = [
+ 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, 0x6d,
+ 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08,
+ ],
+ plain = [
+ 0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, 0xa5,
+ 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a, 0x86, 0xa7,
+ 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda, 0x2e, 0x4c, 0x30,
+ 0x3d, 0x8a, 0x31, 0x8a, 0x72, 0x1c, 0x3c, 0x0c, 0x95,
+ 0x95, 0x68, 0x09, 0x53, 0x2f, 0xcf, 0x0e, 0x24, 0x49,
+ 0xa6, 0xb5, 0x25, 0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d,
+ 0xe6, 0x57, 0xba, 0x63, 0x7b, 0x39,
+ ],
+ additional = [
+ 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, 0xfe,
+ 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, 0xab, 0xad,
+ 0xda, 0xd2,
+ ],
+ iv = [
+ 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad,
+ ],
+ cipher = [
+ 0x61, 0x35, 0x3b, 0x4c, 0x28, 0x06, 0x93, 0x4a, 0x77,
+ 0x7f, 0xf5, 0x1f, 0xa2, 0x2a, 0x47, 0x55, 0x69, 0x9b,
+ 0x2a, 0x71, 0x4f, 0xcd, 0xc6, 0xf8, 0x37, 0x66, 0xe5,
+ 0xf9, 0x7b, 0x6c, 0x74, 0x23, 0x73, 0x80, 0x69, 0x00,
+ 0xe4, 0x9f, 0x24, 0xb2, 0x2b, 0x09, 0x75, 0x44, 0xd4,
+ 0x89, 0x6b, 0x42, 0x49, 0x89, 0xb5, 0xe1, 0xeb, 0xac,
+ 0x0f, 0x07, 0xc2, 0x3f, 0x45, 0x98,
+ ],
+ tag = [
+ 0x36, 0x12, 0xd2, 0xe7, 0x9e, 0x3b, 0x07, 0x85, 0x56,
+ 0x1b, 0xe1, 0x4a, 0xac, 0xa2, 0xfc, 0xcb,
+ ],
+ ...
+ },
+ gcmtestcase {
+ key = [
+ 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, 0x6d,
+ 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08,
+ ],
+ plain = [
+ 0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, 0xa5,
+ 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a, 0x86, 0xa7,
+ 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda, 0x2e, 0x4c, 0x30,
+ 0x3d, 0x8a, 0x31, 0x8a, 0x72, 0x1c, 0x3c, 0x0c, 0x95,
+ 0x95, 0x68, 0x09, 0x53, 0x2f, 0xcf, 0x0e, 0x24, 0x49,
+ 0xa6, 0xb5, 0x25, 0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d,
+ 0xe6, 0x57, 0xba, 0x63, 0x7b, 0x39,
+ ],
+ additional = [
+ 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, 0xfe,
+ 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, 0xab, 0xad,
+ 0xda, 0xd2,
+ ],
+ iv = [
+ 0x93, 0x13, 0x22, 0x5d, 0xf8, 0x84, 0x06, 0xe5, 0x55,
+ 0x90, 0x9c, 0x5a, 0xff, 0x52, 0x69, 0xaa, 0x6a, 0x7a,
+ 0x95, 0x38, 0x53, 0x4f, 0x7d, 0xa1, 0xe4, 0xc3, 0x03,
+ 0xd2, 0xa3, 0x18, 0xa7, 0x28, 0xc3, 0xc0, 0xc9, 0x51,
+ 0x56, 0x80, 0x95, 0x39, 0xfc, 0xf0, 0xe2, 0x42, 0x9a,
+ 0x6b, 0x52, 0x54, 0x16, 0xae, 0xdb, 0xf5, 0xa0, 0xde,
+ 0x6a, 0x57, 0xa6, 0x37, 0xb3, 0x9b,
+ ],
+ cipher = [
+ 0x8c, 0xe2, 0x49, 0x98, 0x62, 0x56, 0x15, 0xb6, 0x03,
+ 0xa0, 0x33, 0xac, 0xa1, 0x3f, 0xb8, 0x94, 0xbe, 0x91,
+ 0x12, 0xa5, 0xc3, 0xa2, 0x11, 0xa8, 0xba, 0x26, 0x2a,
+ 0x3c, 0xca, 0x7e, 0x2c, 0xa7, 0x01, 0xe4, 0xa9, 0xa4,
+ 0xfb, 0xa4, 0x3c, 0x90, 0xcc, 0xdc, 0xb2, 0x81, 0xd4,
+ 0x8c, 0x7c, 0x6f, 0xd6, 0x28, 0x75, 0xd2, 0xac, 0xa4,
+ 0x17, 0x03, 0x4c, 0x34, 0xae, 0xe5,
+ ],
+ tag = [
+ 0x61, 0x9c, 0xc5, 0xae, 0xff, 0xfe, 0x0b, 0xfa, 0x46,
+ 0x2a, 0xf4, 0x3c, 0x16, 0x99, 0xd0, 0x50,
+ ],
+ },
+ gcmtestcase {
+ key = [
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ ],
+ iv = [
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00,
+ ],
+ tag = [
+ 0xcd, 0x33, 0xb2, 0x8a, 0xc7, 0x73, 0xf7, 0x4b, 0xa0,
+ 0x0e, 0xd1, 0xf3, 0x12, 0x57, 0x24, 0x35,
+ ],
+ ...
+ },
+ gcmtestcase {
+ key = [
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ ],
+ plain = [
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ ],
+ iv = [
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00,
+ ],
+ cipher = [
+ 0x98, 0xe7, 0x24, 0x7c, 0x07, 0xf0, 0xfe, 0x41, 0x1c,
+ 0x26, 0x7e, 0x43, 0x84, 0xb0, 0xf6, 0x00,
+ ],
+ tag = [
+ 0x2f, 0xf5, 0x8d, 0x80, 0x03, 0x39, 0x27, 0xab, 0x8e,
+ 0xf4, 0xd4, 0x58, 0x75, 0x14, 0xf0, 0xfb,
+ ],
+ ...
+ },
+ gcmtestcase {
+ key = [
+ 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, 0x6d,
+ 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08, 0xfe, 0xff,
+ 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
+ ],
+ plain = [
+ 0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, 0xa5,
+ 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a, 0x86, 0xa7,
+ 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda, 0x2e, 0x4c, 0x30,
+ 0x3d, 0x8a, 0x31, 0x8a, 0x72, 0x1c, 0x3c, 0x0c, 0x95,
+ 0x95, 0x68, 0x09, 0x53, 0x2f, 0xcf, 0x0e, 0x24, 0x49,
+ 0xa6, 0xb5, 0x25, 0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d,
+ 0xe6, 0x57, 0xba, 0x63, 0x7b, 0x39, 0x1a, 0xaf, 0xd2,
+ 0x55,
+ ],
+ iv = [
+ 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad, 0xde,
+ 0xca, 0xf8, 0x88,
+ ],
+ cipher = [
+ 0x39, 0x80, 0xca, 0x0b, 0x3c, 0x00, 0xe8, 0x41, 0xeb,
+ 0x06, 0xfa, 0xc4, 0x87, 0x2a, 0x27, 0x57, 0x85, 0x9e,
+ 0x1c, 0xea, 0xa6, 0xef, 0xd9, 0x84, 0x62, 0x85, 0x93,
+ 0xb4, 0x0c, 0xa1, 0xe1, 0x9c, 0x7d, 0x77, 0x3d, 0x00,
+ 0xc1, 0x44, 0xc5, 0x25, 0xac, 0x61, 0x9d, 0x18, 0xc8,
+ 0x4a, 0x3f, 0x47, 0x18, 0xe2, 0x44, 0x8b, 0x2f, 0xe3,
+ 0x24, 0xd9, 0xcc, 0xda, 0x27, 0x10, 0xac, 0xad, 0xe2,
+ 0x56,
+ ],
+ tag = [
+ 0x99, 0x24, 0xa7, 0xc8, 0x58, 0x73, 0x36, 0xbf, 0xb1,
+ 0x18, 0x02, 0x4d, 0xb8, 0x67, 0x4a, 0x14,
+ ],
+ ...
+ },
+ gcmtestcase {
+ key = [
+ 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, 0x6d,
+ 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08, 0xfe, 0xff,
+ 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
+ ],
+ plain = [
+ 0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, 0xa5,
+ 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a, 0x86, 0xa7,
+ 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda, 0x2e, 0x4c, 0x30,
+ 0x3d, 0x8a, 0x31, 0x8a, 0x72, 0x1c, 0x3c, 0x0c, 0x95,
+ 0x95, 0x68, 0x09, 0x53, 0x2f, 0xcf, 0x0e, 0x24, 0x49,
+ 0xa6, 0xb5, 0x25, 0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d,
+ 0xe6, 0x57, 0xba, 0x63, 0x7b, 0x39,
+ ],
+ additional = [
+ 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, 0xfe,
+ 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, 0xab, 0xad,
+ 0xda, 0xd2,
+ ],
+ iv = [
+ 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad, 0xde,
+ 0xca, 0xf8, 0x88,
+ ],
+ cipher = [
+ 0x39, 0x80, 0xca, 0x0b, 0x3c, 0x00, 0xe8, 0x41, 0xeb,
+ 0x06, 0xfa, 0xc4, 0x87, 0x2a, 0x27, 0x57, 0x85, 0x9e,
+ 0x1c, 0xea, 0xa6, 0xef, 0xd9, 0x84, 0x62, 0x85, 0x93,
+ 0xb4, 0x0c, 0xa1, 0xe1, 0x9c, 0x7d, 0x77, 0x3d, 0x00,
+ 0xc1, 0x44, 0xc5, 0x25, 0xac, 0x61, 0x9d, 0x18, 0xc8,
+ 0x4a, 0x3f, 0x47, 0x18, 0xe2, 0x44, 0x8b, 0x2f, 0xe3,
+ 0x24, 0xd9, 0xcc, 0xda, 0x27, 0x10,
+ ],
+ tag = [
+ 0x25, 0x19, 0x49, 0x8e, 0x80, 0xf1, 0x47, 0x8f, 0x37,
+ 0xba, 0x55, 0xbd, 0x6d, 0x27, 0x61, 0x8c,
+ ],
+ ...
+ },
+ gcmtestcase {
+ key = [
+ 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, 0x6d,
+ 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08, 0xfe, 0xff,
+ 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
+ ],
+ plain = [
+ 0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, 0xa5,
+ 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a, 0x86, 0xa7,
+ 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda, 0x2e, 0x4c, 0x30,
+ 0x3d, 0x8a, 0x31, 0x8a, 0x72, 0x1c, 0x3c, 0x0c, 0x95,
+ 0x95, 0x68, 0x09, 0x53, 0x2f, 0xcf, 0x0e, 0x24, 0x49,
+ 0xa6, 0xb5, 0x25, 0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d,
+ 0xe6, 0x57, 0xba, 0x63, 0x7b, 0x39,
+ ],
+ additional = [
+ 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, 0xfe,
+ 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, 0xab, 0xad,
+ 0xda, 0xd2,
+ ],
+ iv = [
+ 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad,
+ ],
+ cipher = [
+ 0x0f, 0x10, 0xf5, 0x99, 0xae, 0x14, 0xa1, 0x54, 0xed,
+ 0x24, 0xb3, 0x6e, 0x25, 0x32, 0x4d, 0xb8, 0xc5, 0x66,
+ 0x63, 0x2e, 0xf2, 0xbb, 0xb3, 0x4f, 0x83, 0x47, 0x28,
+ 0x0f, 0xc4, 0x50, 0x70, 0x57, 0xfd, 0xdc, 0x29, 0xdf,
+ 0x9a, 0x47, 0x1f, 0x75, 0xc6, 0x65, 0x41, 0xd4, 0xd4,
+ 0xda, 0xd1, 0xc9, 0xe9, 0x3a, 0x19, 0xa5, 0x8e, 0x8b,
+ 0x47, 0x3f, 0xa0, 0xf0, 0x62, 0xf7,
+ ],
+ tag = [
+ 0x65, 0xdc, 0xc5, 0x7f, 0xcf, 0x62, 0x3a, 0x24, 0x09,
+ 0x4f, 0xcc, 0xa4, 0x0d, 0x35, 0x33, 0xf8,
+ ],
+ ...
+ },
+ gcmtestcase {
+ key = [
+ 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, 0x6d,
+ 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08, 0xfe, 0xff,
+ 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
+ ],
+ plain = [
+ 0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, 0xa5,
+ 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a, 0x86, 0xa7,
+ 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda, 0x2e, 0x4c, 0x30,
+ 0x3d, 0x8a, 0x31, 0x8a, 0x72, 0x1c, 0x3c, 0x0c, 0x95,
+ 0x95, 0x68, 0x09, 0x53, 0x2f, 0xcf, 0x0e, 0x24, 0x49,
+ 0xa6, 0xb5, 0x25, 0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d,
+ 0xe6, 0x57, 0xba, 0x63, 0x7b, 0x39,
+ ],
+ additional = [
+ 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, 0xfe,
+ 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, 0xab, 0xad,
+ 0xda, 0xd2,
+ ],
+ iv = [
+ 0x93, 0x13, 0x22, 0x5d, 0xf8, 0x84, 0x06, 0xe5, 0x55,
+ 0x90, 0x9c, 0x5a, 0xff, 0x52, 0x69, 0xaa, 0x6a, 0x7a,
+ 0x95, 0x38, 0x53, 0x4f, 0x7d, 0xa1, 0xe4, 0xc3, 0x03,
+ 0xd2, 0xa3, 0x18, 0xa7, 0x28, 0xc3, 0xc0, 0xc9, 0x51,
+ 0x56, 0x80, 0x95, 0x39, 0xfc, 0xf0, 0xe2, 0x42, 0x9a,
+ 0x6b, 0x52, 0x54, 0x16, 0xae, 0xdb, 0xf5, 0xa0, 0xde,
+ 0x6a, 0x57, 0xa6, 0x37, 0xb3, 0x9b,
+ ],
+ cipher = [
+ 0xd2, 0x7e, 0x88, 0x68, 0x1c, 0xe3, 0x24, 0x3c, 0x48,
+ 0x30, 0x16, 0x5a, 0x8f, 0xdc, 0xf9, 0xff, 0x1d, 0xe9,
+ 0xa1, 0xd8, 0xe6, 0xb4, 0x47, 0xef, 0x6e, 0xf7, 0xb7,
+ 0x98, 0x28, 0x66, 0x6e, 0x45, 0x81, 0xe7, 0x90, 0x12,
+ 0xaf, 0x34, 0xdd, 0xd9, 0xe2, 0xf0, 0x37, 0x58, 0x9b,
+ 0x29, 0x2d, 0xb3, 0xe6, 0x7c, 0x03, 0x67, 0x45, 0xfa,
+ 0x22, 0xe7, 0xe9, 0xb7, 0x37, 0x3b,
+ ],
+ tag = [
+ 0xdc, 0xf5, 0x66, 0xff, 0x29, 0x1c, 0x25, 0xbb, 0xb8,
+ 0x56, 0x8f, 0xc3, 0xd3, 0x76, 0xa6, 0xd9,
+ ],
+ ...
+ },
+ gcmtestcase {
+ key = [
+ 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,
+ ],
+ iv = [
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00,
+ ],
+ tag = [
+ 0x53, 0x0f, 0x8a, 0xfb, 0xc7, 0x45, 0x36, 0xb9, 0xa9,
+ 0x63, 0xb4, 0xf1, 0xc4, 0xcb, 0x73, 0x8b,
+ ],
+ ...
+ },
+ gcmtestcase {
+ key = [
+ 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,
+ ],
+ plain = [
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ ],
+ iv = [
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00,
+ ],
+ cipher = [
+ 0xce, 0xa7, 0x40, 0x3d, 0x4d, 0x60, 0x6b, 0x6e, 0x07,
+ 0x4e, 0xc5, 0xd3, 0xba, 0xf3, 0x9d, 0x18,
+ ],
+ tag = [
+ 0xd0, 0xd1, 0xc8, 0xa7, 0x99, 0x99, 0x6b, 0xf0, 0x26,
+ 0x5b, 0x98, 0xb5, 0xd4, 0x8a, 0xb9, 0x19,
+ ],
+ ...
+ },
+ gcmtestcase {
+ key = [
+ 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, 0x6d,
+ 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08, 0xfe, 0xff,
+ 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, 0x6d, 0x6a, 0x8f,
+ 0x94, 0x67, 0x30, 0x83, 0x08,
+ ],
+ plain = [
+ 0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, 0xa5,
+ 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a, 0x86, 0xa7,
+ 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda, 0x2e, 0x4c, 0x30,
+ 0x3d, 0x8a, 0x31, 0x8a, 0x72, 0x1c, 0x3c, 0x0c, 0x95,
+ 0x95, 0x68, 0x09, 0x53, 0x2f, 0xcf, 0x0e, 0x24, 0x49,
+ 0xa6, 0xb5, 0x25, 0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d,
+ 0xe6, 0x57, 0xba, 0x63, 0x7b, 0x39, 0x1a, 0xaf, 0xd2,
+ 0x55,
+ ],
+ iv = [
+ 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad, 0xde,
+ 0xca, 0xf8, 0x88,
+ ],
+ cipher = [
+ 0x52, 0x2d, 0xc1, 0xf0, 0x99, 0x56, 0x7d, 0x07, 0xf4,
+ 0x7f, 0x37, 0xa3, 0x2a, 0x84, 0x42, 0x7d, 0x64, 0x3a,
+ 0x8c, 0xdc, 0xbf, 0xe5, 0xc0, 0xc9, 0x75, 0x98, 0xa2,
+ 0xbd, 0x25, 0x55, 0xd1, 0xaa, 0x8c, 0xb0, 0x8e, 0x48,
+ 0x59, 0x0d, 0xbb, 0x3d, 0xa7, 0xb0, 0x8b, 0x10, 0x56,
+ 0x82, 0x88, 0x38, 0xc5, 0xf6, 0x1e, 0x63, 0x93, 0xba,
+ 0x7a, 0x0a, 0xbc, 0xc9, 0xf6, 0x62, 0x89, 0x80, 0x15,
+ 0xad,
+ ],
+ tag = [
+ 0xb0, 0x94, 0xda, 0xc5, 0xd9, 0x34, 0x71, 0xbd, 0xec,
+ 0x1a, 0x50, 0x22, 0x70, 0xe3, 0xcc, 0x6c,
+ ],
+ ...
+ },
+ gcmtestcase {
+ key = [
+ 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, 0x6d,
+ 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08, 0xfe, 0xff,
+ 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, 0x6d, 0x6a, 0x8f,
+ 0x94, 0x67, 0x30, 0x83, 0x08,
+ ],
+ plain = [
+ 0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, 0xa5,
+ 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a, 0x86, 0xa7,
+ 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda, 0x2e, 0x4c, 0x30,
+ 0x3d, 0x8a, 0x31, 0x8a, 0x72, 0x1c, 0x3c, 0x0c, 0x95,
+ 0x95, 0x68, 0x09, 0x53, 0x2f, 0xcf, 0x0e, 0x24, 0x49,
+ 0xa6, 0xb5, 0x25, 0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d,
+ 0xe6, 0x57, 0xba, 0x63, 0x7b, 0x39,
+ ],
+ additional = [
+ 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, 0xfe,
+ 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, 0xab, 0xad,
+ 0xda, 0xd2,
+ ],
+ iv = [
+ 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad, 0xde,
+ 0xca, 0xf8, 0x88,
+ ],
+ cipher = [
+ 0x52, 0x2d, 0xc1, 0xf0, 0x99, 0x56, 0x7d, 0x07, 0xf4,
+ 0x7f, 0x37, 0xa3, 0x2a, 0x84, 0x42, 0x7d, 0x64, 0x3a,
+ 0x8c, 0xdc, 0xbf, 0xe5, 0xc0, 0xc9, 0x75, 0x98, 0xa2,
+ 0xbd, 0x25, 0x55, 0xd1, 0xaa, 0x8c, 0xb0, 0x8e, 0x48,
+ 0x59, 0x0d, 0xbb, 0x3d, 0xa7, 0xb0, 0x8b, 0x10, 0x56,
+ 0x82, 0x88, 0x38, 0xc5, 0xf6, 0x1e, 0x63, 0x93, 0xba,
+ 0x7a, 0x0a, 0xbc, 0xc9, 0xf6, 0x62,
+ ],
+ tag = [
+ 0x76, 0xfc, 0x6e, 0xce, 0x0f, 0x4e, 0x17, 0x68, 0xcd,
+ 0xdf, 0x88, 0x53, 0xbb, 0x2d, 0x55, 0x1b,
+ ],
+ ...
+ },
+ gcmtestcase {
+ key = [
+ 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, 0x6d,
+ 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08, 0xfe, 0xff,
+ 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, 0x6d, 0x6a, 0x8f,
+ 0x94, 0x67, 0x30, 0x83, 0x08,
+ ],
+ plain = [
+ 0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, 0xa5,
+ 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a, 0x86, 0xa7,
+ 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda, 0x2e, 0x4c, 0x30,
+ 0x3d, 0x8a, 0x31, 0x8a, 0x72, 0x1c, 0x3c, 0x0c, 0x95,
+ 0x95, 0x68, 0x09, 0x53, 0x2f, 0xcf, 0x0e, 0x24, 0x49,
+ 0xa6, 0xb5, 0x25, 0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d,
+ 0xe6, 0x57, 0xba, 0x63, 0x7b, 0x39,
+ ],
+ additional = [
+ 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, 0xfe,
+ 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, 0xab, 0xad,
+ 0xda, 0xd2,
+ ],
+ iv = [
+ 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad,
+ ],
+ cipher = [
+ 0xc3, 0x76, 0x2d, 0xf1, 0xca, 0x78, 0x7d, 0x32, 0xae,
+ 0x47, 0xc1, 0x3b, 0xf1, 0x98, 0x44, 0xcb, 0xaf, 0x1a,
+ 0xe1, 0x4d, 0x0b, 0x97, 0x6a, 0xfa, 0xc5, 0x2f, 0xf7,
+ 0xd7, 0x9b, 0xba, 0x9d, 0xe0, 0xfe, 0xb5, 0x82, 0xd3,
+ 0x39, 0x34, 0xa4, 0xf0, 0x95, 0x4c, 0xc2, 0x36, 0x3b,
+ 0xc7, 0x3f, 0x78, 0x62, 0xac, 0x43, 0x0e, 0x64, 0xab,
+ 0xe4, 0x99, 0xf4, 0x7c, 0x9b, 0x1f,
+ ],
+ tag = [
+ 0x3a, 0x33, 0x7d, 0xbf, 0x46, 0xa7, 0x92, 0xc4, 0x5e,
+ 0x45, 0x49, 0x13, 0xfe, 0x2e, 0xa8, 0xf2,
+ ],
+ ...
+ },
+ gcmtestcase {
+ key = [
+ 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, 0x6d,
+ 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08, 0xfe, 0xff,
+ 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, 0x6d, 0x6a, 0x8f,
+ 0x94, 0x67, 0x30, 0x83, 0x08,
+ ],
+ plain = [
+ 0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, 0xa5,
+ 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a, 0x86, 0xa7,
+ 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda, 0x2e, 0x4c, 0x30,
+ 0x3d, 0x8a, 0x31, 0x8a, 0x72, 0x1c, 0x3c, 0x0c, 0x95,
+ 0x95, 0x68, 0x09, 0x53, 0x2f, 0xcf, 0x0e, 0x24, 0x49,
+ 0xa6, 0xb5, 0x25, 0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d,
+ 0xe6, 0x57, 0xba, 0x63, 0x7b, 0x39,
+ ],
+ additional = [
+ 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, 0xfe,
+ 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, 0xab, 0xad,
+ 0xda, 0xd2,
+ ],
+ iv = [
+ 0x93, 0x13, 0x22, 0x5d, 0xf8, 0x84, 0x06, 0xe5, 0x55,
+ 0x90, 0x9c, 0x5a, 0xff, 0x52, 0x69, 0xaa, 0x6a, 0x7a,
+ 0x95, 0x38, 0x53, 0x4f, 0x7d, 0xa1, 0xe4, 0xc3, 0x03,
+ 0xd2, 0xa3, 0x18, 0xa7, 0x28, 0xc3, 0xc0, 0xc9, 0x51,
+ 0x56, 0x80, 0x95, 0x39, 0xfc, 0xf0, 0xe2, 0x42, 0x9a,
+ 0x6b, 0x52, 0x54, 0x16, 0xae, 0xdb, 0xf5, 0xa0, 0xde,
+ 0x6a, 0x57, 0xa6, 0x37, 0xb3, 0x9b,
+ ],
+ cipher = [
+ 0x5a, 0x8d, 0xef, 0x2f, 0x0c, 0x9e, 0x53, 0xf1, 0xf7,
+ 0x5d, 0x78, 0x53, 0x65, 0x9e, 0x2a, 0x20, 0xee, 0xb2,
+ 0xb2, 0x2a, 0xaf, 0xde, 0x64, 0x19, 0xa0, 0x58, 0xab,
+ 0x4f, 0x6f, 0x74, 0x6b, 0xf4, 0x0f, 0xc0, 0xc3, 0xb7,
+ 0x80, 0xf2, 0x44, 0x45, 0x2d, 0xa3, 0xeb, 0xf1, 0xc5,
+ 0xd8, 0x2c, 0xde, 0xa2, 0x41, 0x89, 0x97, 0x20, 0x0e,
+ 0xf8, 0x2e, 0x44, 0xae, 0x7e, 0x3f,
+ ],
+ tag = [
+ 0xa4, 0x4a, 0x82, 0x66, 0xee, 0x1c, 0x8e, 0xb0, 0xc8,
+ 0xb5, 0xd4, 0xcf, 0x5a, 0xe9, 0xf1, 0x9a,
+ ],
+ ...
+ },
+];
+
+@test fn gcm_encrypt() void = {
+ for (let i = 0z; i < len(gcmtestcases); i += 1) {
+ const t = gcmtestcases[i];
+
+ let b = ct64();
+ ct64_init(&b, t.key);
+
+ let result: []u8 = if (len(t.cipher) == 0) {
+ yield [];
+ } else {
+ yield alloc([0...], len(t.cipher));
+ };
+ let resultbuf = bufio::fixed(result, io::mode::WRITE);
+
+ let gstream = cipher::gcm();
+ cipher::gcm_init(&gstream, &resultbuf, &b, t.iv, t.additional);
+ defer io::close(&gstream)!;
+
+ io::writeall(&gstream, t.plain)!;
+ const tag = cipher::gcm_seal(&gstream);
+
+ assert(bytes::equal(t.cipher, result));
+ assert(bytes::equal(t.tag, tag));
+ };
+};
+
+@test fn gcm_decrypt() void = {
+ for (let i = 0z; i < len(gcmtestcases); i += 1) {
+ const t = gcmtestcases[i];
+
+ let b = ct64();
+ ct64_init(&b, t.key);
+
+ let result: []u8 = if (len(t.cipher) == 0) {
+ yield [];
+ } else {
+ yield alloc([0...], len(t.cipher));
+ };
+ let cipherbuf = bufio::fixed(t.cipher, io::mode::READ);
+
+ let gstream = cipher::gcm();
+ cipher::gcm_init(&gstream, &cipherbuf, &b, t.iv, t.additional);
+ defer io::close(&gstream)!;
+
+ io::readall(&gstream, result)!;
+
+ assert(bytes::equal(t.plain, result));
+ cipher::gcm_verify(&gstream, t.tag)!;
+
+ let wrongtag: [16]u8 = t.tag;
+ wrongtag[0] += 1;
+ match (cipher::gcm_verify(&gstream, wrongtag)) {
+ case errors::invalid =>
+ yield;
+ case =>
+ assert(false);
+ };
+ };
+};
diff --git a/crypto/cipher/gcm.ha b/crypto/cipher/gcm.ha
@@ -0,0 +1,244 @@
+// License: MPL-2.0
+// (c) 2022 Armin Preiml <apreiml@strohwolke.at>
+use bytes;
+use crypto::math::{xor,cmpslice};
+use endian::{beputu64, beputu32, begetu32};
+use errors;
+use io;
+use types;
+
+def GCMBLOCKSIZE: size = 16;
+
+export type gcmstream = struct {
+ stream: io::stream,
+ block: *block,
+ handle: io::handle,
+ tagbuf: [GCMBLOCKSIZE]u8,
+ xorbuf: [GCMBLOCKSIZE]u8,
+ cipherbuf: [GCMBLOCKSIZE]u8,
+ y0: [GCMBLOCKSIZE]u8,
+ h: [GCMBLOCKSIZE]u8,
+ y: u32,
+ xorbufpos: size,
+ adlen: u64,
+ clen: u64,
+};
+
+const gcm_vtable: io::vtable = io::vtable {
+ writer = &gcm_writer,
+ reader = &gcm_reader,
+ closer = &gcm_closer,
+ ...
+};
+
+// Creates a Galois Counter Mode (GCM) io::stream which can be used for
+// encryption (by encrypting writes to the underlying handle) or decryption (or
+// by decrypting reads from the underlying handle), but not both. [[gcm_init]]
+// must be called to initialize the stream, before reading or writing. To
+// authenticate the encrypted data an authentication tag must be created using
+// [[gcm_seal]] after the encryption step. The authentication tag must be passed
+// to [[gcm_verify]] after decryption to make sure that the encrypted and
+// additional data were not modified. In case of a verification fail the
+// decrypted data must not be trusted and hence discarded.
+//
+// A maximum of 2**36-32 bytes may be encrypted.
+//
+// The user must call [[io::close]] when they are done using the stream to
+// securely erase secret information stored in the stream state. Close will
+// also finish the 'block' provided by [[gcm_init]].
+export fn gcm() gcmstream = {
+ return gcmstream {
+ stream = &gcm_vtable,
+ ...
+ };
+};
+
+// Initialises the gcmstream. The data will be encrypted to or encrypted from
+// the given 'handle' The implementation only supports a block cipher 'b' with a
+// block size of 16 bytes. The initialization vector (nonce) 'iv' may have any
+// size up to 2**61 bytes. 12 bytes is the recommended size, if efficiency is
+// critical. The additional data 'ad' will be authenticated but not encrypted
+// and may have a maximum length of 2**61 - 1 bytes. 'ad' will not be written to
+// the underlying handle.
+export fn gcm_init(
+ s: *gcmstream,
+ handle: io::handle,
+ b: *block,
+ iv: const []u8,
+ ad: const []u8
+) void = {
+ assert(blocksz(b) == GCMBLOCKSIZE);
+ assert(len(iv): u64 <= (types::U64_MAX >> 3));
+
+ s.handle = handle;
+ s.block = b;
+ s.adlen = len(ad);
+ s.xorbufpos = GCMBLOCKSIZE; // to force fill xorbuf at start
+
+ encrypt(b, s.h, s.h);
+
+ if (len(iv) == 12) {
+ s.y0[..12] = iv[..];
+ s.y0[15] |= 1;
+ } else {
+ let ivlen = s.tagbuf;
+ beputu64(ivlen[8..], len(iv) << 3);
+ ghash_ctmul64(s.y0, s.h, iv);
+ ghash_ctmul64(s.y0, s.h, ivlen);
+ bytes::zero(ivlen);
+ };
+
+ s.y = begetu32(s.y0[12..]) + 1;
+
+ let ad = ad[..];
+ for (len(ad) > 0) {
+ const max = if (len(ad) >= GCMBLOCKSIZE) {
+ yield GCMBLOCKSIZE;
+ } else {
+ yield len(ad);
+ };
+
+ ghash_ctmul64(s.tagbuf, s.h, ad[..max]);
+ ad = ad[max..];
+ };
+};
+
+fn gcm_writer(s: *io::stream, buf: const []u8) (size | io::error) = {
+ let s = s: *gcmstream;
+ if (len(buf) == 0) {
+ return 0z;
+ };
+
+ if (s.xorbufpos == GCMBLOCKSIZE) {
+ // current key block is depleted, prepare the next one
+ fillxorbuf(s);
+ };
+
+ let buf = buf[..];
+
+ let n: size = 0;
+ const max = if (s.xorbufpos + len(buf) > len(s.cipherbuf)) {
+ yield len(s.cipherbuf) - s.xorbufpos;
+ } else {
+ yield len(buf);
+ };
+
+ let cipher = s.cipherbuf[s.xorbufpos..s.xorbufpos + max];
+ let key = s.xorbuf[s.xorbufpos..s.xorbufpos + max];
+ xor(cipher, key, buf[..max]);
+
+ const n = io::write(s.handle, cipher)?;
+ s.xorbufpos += n;
+ s.clen += n;
+
+ if (s.xorbufpos == GCMBLOCKSIZE) {
+ ghash_ctmul64(s.tagbuf, s.h, s.cipherbuf);
+ };
+
+ return n;
+};
+
+fn fillxorbuf(s: *gcmstream) void = {
+ let y: [GCMBLOCKSIZE]u8 = [0...];
+ s.xorbuf[..] = s.y0[..];
+ beputu32(s.xorbuf[12..], s.y);
+ encrypt(s.block, s.xorbuf, s.xorbuf);
+ s.y += 1;
+ s.xorbufpos = 0;
+};
+
+fn gcm_reader(s: *io::stream, buf: []u8) (size | io::EOF | io::error) = {
+ let s = s: *gcmstream;
+
+ const n = match (io::read(s.handle, buf)?) {
+ case io::EOF =>
+ return io::EOF;
+ case let s: size =>
+ yield s;
+ };
+
+ for (let i = n; i > 0) {
+ if (s.xorbufpos == GCMBLOCKSIZE) {
+ fillxorbuf(s);
+ };
+
+ const max = if (s.xorbufpos + i > GCMBLOCKSIZE) {
+ yield len(s.cipherbuf) - s.xorbufpos;
+ } else {
+ yield i;
+ };
+
+ let cipher = s.cipherbuf[s.xorbufpos..s.xorbufpos + max];
+ let key = s.xorbuf[s.xorbufpos..s.xorbufpos + max];
+
+ cipher[..] = buf[..max];
+ xor(buf[..max], buf[..max], key);
+
+ buf = buf[max..];
+ i -= max;
+
+ s.xorbufpos += max;
+ s.clen += max;
+
+ if (s.xorbufpos == len(s.cipherbuf)) {
+ ghash_ctmul64(s.tagbuf, s.h, s.cipherbuf);
+ };
+ };
+
+ return n;
+};
+
+// Finishes encryption and returns the authentication tag. After calling seal,
+// the user must not write any more data to the stream.
+export fn gcm_seal(s: *gcmstream) [16]u8 = {
+ if (s.xorbufpos > 0 && s.xorbufpos < GCMBLOCKSIZE) {
+ // last block was is not full, therefore the content was not
+ // hashed yet.
+ ghash_ctmul64(s.tagbuf, s.h, s.cipherbuf[..s.xorbufpos]);
+ };
+
+ let tmp: [16]u8 = [0...];
+ beputu64(tmp, s.adlen << 3);
+ beputu64(tmp[8..], s.clen << 3);
+ ghash_ctmul64(s.tagbuf, s.h, tmp);
+
+ // use tmp to store the resulting tag
+ encrypt(s.block, tmp, s.y0);
+ xor(tmp, tmp, s.tagbuf);
+
+ return tmp;
+};
+
+// Verifies the authentication tag against the decrypted data. Must be called
+// after reading all data from the stream to ensure that the data was not
+// modified. If the data was modified, [[errors::invalid]] will be returned and
+// the data must not be trusted.
+export fn gcm_verify(s: *gcmstream, tag: [16]u8) (void | errors::invalid) = {
+ if (s.xorbufpos > 0 && s.xorbufpos < GCMBLOCKSIZE) {
+ ghash_ctmul64(s.tagbuf, s.h, s.cipherbuf[..s.xorbufpos]);
+ };
+
+ let tmp: [16]u8 = [0...];
+ beputu64(tmp, s.adlen << 3);
+ beputu64(tmp[8..], s.clen << 3);
+
+ ghash_ctmul64(s.tagbuf, s.h, tmp);
+
+ encrypt(s.block, tmp, s.y0);
+ xor(tmp, tmp, s.tagbuf);
+
+ if (cmpslice(tag, tmp) == 0) {
+ return errors::invalid;
+ };
+};
+
+fn gcm_closer(s: *io::stream) (void | io::error) = {
+ let s = s: *gcmstream;
+ bytes::zero(s.tagbuf);
+ bytes::zero(s.xorbuf);
+ bytes::zero(s.cipherbuf);
+ bytes::zero(s.y0);
+ bytes::zero(s.h);
+
+ finish(s.block);
+};
diff --git a/crypto/cipher/ghash.ha b/crypto/cipher/ghash.ha
@@ -0,0 +1,124 @@
+// The following code was initially ported from BearSSL.
+//
+// Copyright (c) 2016 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 endian::{begetu64,beputu64};
+
+fn bmul64(x: u64, y: u64) u64 = {
+ const x0 = x & 0x1111111111111111;
+ const x1 = x & 0x2222222222222222;
+ const x2 = x & 0x4444444444444444;
+ const x3 = x & 0x8888888888888888;
+ const y0 = y & 0x1111111111111111;
+ const y1 = y & 0x2222222222222222;
+ const y2 = y & 0x4444444444444444;
+ const y3 = y & 0x8888888888888888;
+ let z0 = (x0 * y0) ^ (x1 * y3) ^ (x2 * y2) ^ (x3 * y1);
+ let z1 = (x0 * y1) ^ (x1 * y0) ^ (x2 * y3) ^ (x3 * y2);
+ let z2 = (x0 * y2) ^ (x1 * y1) ^ (x2 * y0) ^ (x3 * y3);
+ let z3 = (x0 * y3) ^ (x1 * y2) ^ (x2 * y1) ^ (x3 * y0);
+ z0 &= 0x1111111111111111;
+ z1 &= 0x2222222222222222;
+ z2 &= 0x4444444444444444;
+ z3 &= 0x8888888888888888;
+ return z0 | z1 | z2 | z3;
+};
+
+
+fn rev64(x: u64) u64 = {
+ x = ((x & 0x5555555555555555) << 1) | ((x >> 1) & 0x5555555555555555);
+ x = ((x & 0x3333333333333333) << 2) | ((x >> 2) & 0x3333333333333333);
+ x = ((x & 0x0F0F0F0F0F0F0F0F) << 4) | ((x >> 4) & 0x0F0F0F0F0F0F0F0F);
+ x = ((x & 0x00FF00FF00FF00FF) << 8) | ((x >> 8) & 0x00FF00FF00FF00FF);
+ x = ((x & 0x0000FFFF0000FFFF) << 16) | ((x >> 16) & 0x0000FFFF0000FFFF);
+ return (x << 32) | (x >> 32);
+};
+
+// GHASH implementation that relies on constant time 64bit multiplication
+fn ghash_ctmul64(y: []u8, h: const []u8, data: const []u8) void = {
+ let buf = data[..];
+ let tmp: [16]u8 = [0...];
+
+ let y1 = begetu64(y);
+ let y0 = begetu64(y[8..]);
+ const h1 = begetu64(h);
+ const h0 = begetu64(h[8..]);
+ const h0r = rev64(h0);
+ const h1r = rev64(h1);
+ const h2 = h0 ^ h1;
+ const h2r = h0r ^ h1r;
+
+ for (len(buf) > 0) {
+ let src: []u8 = [];
+
+ if (len(buf) >= 16) {
+ src = buf[..16];
+ buf = buf[16..];
+ } else {
+ tmp[..len(buf)] = buf[..];
+ bytes::zero(tmp[len(buf)..len(tmp)]);
+ src = tmp;
+ buf = [];
+ };
+ y1 ^= begetu64(src);
+ y0 ^= begetu64(src[8..]);
+
+ const y0r = rev64(y0);
+ const y1r = rev64(y1);
+ const y2 = y0 ^ y1;
+ const y2r = y0r ^ y1r;
+
+ const z0 = bmul64(y0, h0);
+ const z1 = bmul64(y1, h1);
+ let z2 = bmul64(y2, h2);
+ let z0h = bmul64(y0r, h0r);
+ let z1h = bmul64(y1r, h1r);
+ let z2h = bmul64(y2r, h2r);
+ z2 ^= z0 ^ z1;
+ z2h ^= z0h ^ z1h;
+ z0h = rev64(z0h) >> 1;
+ z1h = rev64(z1h) >> 1;
+ z2h = rev64(z2h) >> 1;
+
+
+ let v0 = z0;
+ let v1 = z0h ^ z2;
+ let v2 = z1 ^ z2h;
+ let v3 = z1h;
+
+ v3 = (v3 << 1) | (v2 >> 63);
+ v2 = (v2 << 1) | (v1 >> 63);
+ v1 = (v1 << 1) | (v0 >> 63);
+ v0 = (v0 << 1);
+
+ v2 ^= v0 ^ (v0 >> 1) ^ (v0 >> 2) ^ (v0 >> 7);
+ v1 ^= (v0 << 63) ^ (v0 << 62) ^ (v0 << 57);
+ v3 ^= v1 ^ (v1 >> 1) ^ (v1 >> 2) ^ (v1 >> 7);
+ v2 ^= (v1 << 63) ^ (v1 << 62) ^ (v1 << 57);
+
+ y0 = v2;
+ y1 = v3;
+ };
+
+ beputu64(y, y1);
+ beputu64(y[8..], y0);
+};
+
diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib
@@ -242,9 +242,10 @@ crypto_aes() {
gensrcs_crypto_aes \
ct64+test.ha \
cbc+test.ha \
- ctr+test.ha
- gen_ssa crypto::aes bytes crypto::cipher crypto::math endian \
- rt io bufio errors
+ ctr+test.ha \
+ +test/gcm.ha
+ gen_ssa crypto::aes bufio bytes crypto::cipher crypto::math \
+ endian errors io rt
fi
}
@@ -337,8 +338,10 @@ crypto_cipher() {
block.ha \
cbc.ha \
ctr.ha \
- stream.ha
- gen_ssa crypto::cipher crypto::math bytes io
+ stream.ha \
+ gcm.ha \
+ ghash.ha
+ gen_ssa crypto::cipher crypto::math bytes endian errors io types
}
crypto_hmac() {
diff --git a/stdlib.mk b/stdlib.mk
@@ -846,9 +846,11 @@ stdlib_crypto_cipher_any_srcs = \
$(STDLIB)/crypto/cipher/block.ha \
$(STDLIB)/crypto/cipher/cbc.ha \
$(STDLIB)/crypto/cipher/ctr.ha \
- $(STDLIB)/crypto/cipher/stream.ha
+ $(STDLIB)/crypto/cipher/stream.ha \
+ $(STDLIB)/crypto/cipher/gcm.ha \
+ $(STDLIB)/crypto/cipher/ghash.ha
-$(HARECACHE)/crypto/cipher/crypto_cipher-any.ssa: $(stdlib_crypto_cipher_any_srcs) $(stdlib_rt) $(stdlib_crypto_math_$(PLATFORM)) $(stdlib_bytes_$(PLATFORM)) $(stdlib_io_$(PLATFORM))
+$(HARECACHE)/crypto/cipher/crypto_cipher-any.ssa: $(stdlib_crypto_cipher_any_srcs) $(stdlib_rt) $(stdlib_crypto_math_$(PLATFORM)) $(stdlib_bytes_$(PLATFORM)) $(stdlib_endian_$(PLATFORM)) $(stdlib_errors_$(PLATFORM)) $(stdlib_io_$(PLATFORM)) $(stdlib_types_$(PLATFORM))
@printf 'HAREC \t$@\n'
@mkdir -p $(HARECACHE)/crypto/cipher
@HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Ncrypto::cipher \
@@ -2871,9 +2873,10 @@ testlib_crypto_aes_any_srcs = \
$(STDLIB)/crypto/aes/aes_ct64.ha \
$(STDLIB)/crypto/aes/ct64+test.ha \
$(STDLIB)/crypto/aes/cbc+test.ha \
- $(STDLIB)/crypto/aes/ctr+test.ha
+ $(STDLIB)/crypto/aes/ctr+test.ha \
+ $(STDLIB)/crypto/aes/+test/gcm.ha
-$(TESTCACHE)/crypto/aes/crypto_aes-any.ssa: $(testlib_crypto_aes_any_srcs) $(testlib_rt) $(testlib_bytes_$(PLATFORM)) $(testlib_crypto_cipher_$(PLATFORM)) $(testlib_crypto_math_$(PLATFORM)) $(testlib_endian_$(PLATFORM)) $(testlib_rt_$(PLATFORM)) $(testlib_io_$(PLATFORM)) $(testlib_bufio_$(PLATFORM)) $(testlib_errors_$(PLATFORM))
+$(TESTCACHE)/crypto/aes/crypto_aes-any.ssa: $(testlib_crypto_aes_any_srcs) $(testlib_rt) $(testlib_bufio_$(PLATFORM)) $(testlib_bytes_$(PLATFORM)) $(testlib_crypto_cipher_$(PLATFORM)) $(testlib_crypto_math_$(PLATFORM)) $(testlib_endian_$(PLATFORM)) $(testlib_errors_$(PLATFORM)) $(testlib_io_$(PLATFORM)) $(testlib_rt_$(PLATFORM))
@printf 'HAREC \t$@\n'
@mkdir -p $(TESTCACHE)/crypto/aes
@HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ncrypto::aes \
@@ -2954,9 +2957,11 @@ testlib_crypto_cipher_any_srcs = \
$(STDLIB)/crypto/cipher/block.ha \
$(STDLIB)/crypto/cipher/cbc.ha \
$(STDLIB)/crypto/cipher/ctr.ha \
- $(STDLIB)/crypto/cipher/stream.ha
+ $(STDLIB)/crypto/cipher/stream.ha \
+ $(STDLIB)/crypto/cipher/gcm.ha \
+ $(STDLIB)/crypto/cipher/ghash.ha
-$(TESTCACHE)/crypto/cipher/crypto_cipher-any.ssa: $(testlib_crypto_cipher_any_srcs) $(testlib_rt) $(testlib_crypto_math_$(PLATFORM)) $(testlib_bytes_$(PLATFORM)) $(testlib_io_$(PLATFORM))
+$(TESTCACHE)/crypto/cipher/crypto_cipher-any.ssa: $(testlib_crypto_cipher_any_srcs) $(testlib_rt) $(testlib_crypto_math_$(PLATFORM)) $(testlib_bytes_$(PLATFORM)) $(testlib_endian_$(PLATFORM)) $(testlib_errors_$(PLATFORM)) $(testlib_io_$(PLATFORM)) $(testlib_types_$(PLATFORM))
@printf 'HAREC \t$@\n'
@mkdir -p $(TESTCACHE)/crypto/cipher
@HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ncrypto::cipher \