hare

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

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:
Acrypto/aes/+test/gcm.ha | 654+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrypto/cipher/gcm.ha | 244+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrypto/cipher/ghash.ha | 124+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mscripts/gen-stdlib | 13++++++++-----
Mstdlib.mk | 17+++++++++++------
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 \