├── requirements.txt ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── gcmcrypt.py └── gcmcrypt.cpp /requirements.txt: -------------------------------------------------------------------------------- 1 | cryptography>=0.8 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | v_env 3 | openssl-* 4 | gcmcrypt 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 sailorfred 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | OPENSSL_VERSION=1.0.2c 2 | OPENSSL=openssl-$(OPENSSL_VERSION) 3 | OPENSSL_TGZ=$(OPENSSL).tar.gz 4 | OPENSSL_URL=https://www.openssl.org/source/$(OPENSSL_TGZ) 5 | OPENSSL_MD5=8c8d81a9ae7005276e486702edbcd4b6 6 | OPENSSL_SHA1=6e4a5e91159eb32383296c7c83ac0e59b83a0a44 7 | INSTALL_BIN_DIR=$(PREFIX)/usr/local/bin 8 | 9 | ifeq ($(shell uname), Darwin) 10 | CONFIG_OPENSSL=./Configure darwin64-x86_64-cc enable-ec_nistp_64_gcc_128 no-ssl2 no-ssl3 no-comp --openssldir=/usr/local/ssl/macos-x86_64 11 | else 12 | CONFIG_OPENSSL=./config 13 | endif 14 | 15 | gcmcrypt: gcmcrypt.cpp $(OPENSSL)/libcrypto.a 16 | g++ -o $@ gcmcrypt.cpp -I$(OPENSSL)/include -L$(OPENSSL) -lcrypto -ldl 17 | 18 | $(OPENSSL)/libcrypto.a: $(OPENSSL)/Makefile 19 | cd $(OPENSSL) && $(CONFIG_OPENSSL) && make 20 | 21 | $(OPENSSL)/Makefile: $(OPENSSL_TGZ) 22 | tar zxvf $(OPENSSL_TGZ) 23 | 24 | $(OPENSSL_TGZ): 25 | curl -O $(OPENSSL_URL) 26 | test `openssl md5 < $(OPENSSL_TGZ) | sed -e 's/^.*= //'` = $(OPENSSL_MD5) 27 | test `openssl sha1 < $(OPENSSL_TGZ)| sed -e 's/^.*= //'` = $(OPENSSL_SHA1) 28 | 29 | $(INSTALL_BIN_DIR): 30 | mkdir -p $@ 31 | 32 | install: $(INSTALL_BIN_DIR) gcmcrypt 33 | install gcmcrypt $(INSTALL_BIN_DIR)/gcmcrypt 34 | 35 | clean: 36 | rm -rf openssl-* 37 | rm -rf *.o 38 | rm -rf gcmcrypt 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gcmcrypt.py 2 | Command line AES256-GCM file encryption with 96 bit IV and 128 bit 3 | authentication tag 4 | 5 | The IV and authentication tag are stored in the output file, using this 6 | format: 7 | 8 | 9 | 10 | There is no additional authenticated data. 11 | 12 | Installation: 13 | 14 | For python 2.6+. If you have 2.6, you must also install argparse: 15 | 16 | pip install argparse 17 | 18 | cryptography requires the ffi development libraries. On Ubuntu, do: 19 | 20 | sudo apt-get install libffi-dev 21 | 22 | Then install cryptography via: 23 | 24 | pip install -r requirements.txt 25 | 26 | Usage: 27 | 28 | Encryption: 29 | 30 | gcmcrypt.py [ -d ] [ -k | -p ] [ -n ] [ -o ] 31 | 32 | For security against other users on the system using ps, stdin 33 | must consist of the key or passphrase. It will have whitespace 34 | stripped from the end so that line terminators on different platforms 35 | don't change the key. 36 | 37 | -d or --decrypt says to decrypt the file instead of encrypting it 38 | 39 | -k or --key says to get a hex encoded 32 byte (256 bit) key from stdin. 40 | Be sure to use a cryptographically secure method for generating keys. 41 | 42 | -p or --passphrase passphrase says to read a passphrase from stdin. 43 | Trailing white space will be stripped from it, and it will be run through 44 | PBKDF2 to provide the key. 45 | 46 | -n or --noprompt doesn't prompt the user for the key or passphrase on 47 | stderr. 48 | 49 | -o or --out specifies the output file to use instead of stdout 50 | 51 | Caveats: 52 | 53 | Under python 2.x, two ^Ds are required to start the encryption. 54 | 55 | In the interests of minimizing memory usage during decryption of large files, 56 | unauthenticated plaintext is output to stdout. Callers must verify a zero 57 | (success) status code. Example: 58 | 59 | gcmcrypt.py -d -k file.gcm > file || rm file && false 60 | 61 | If --out is used, the file will be removed when there's a bad status. 62 | 63 | C++ Version: 64 | 65 | The Python CFFI makes the cryptography library load very slowly, so there 66 | is also a C++ version for performance. 67 | 68 | It doesn't currently understand these switches: 69 | 70 | -b (--bits) 71 | -p (--passphrase) 72 | 73 | To build: 74 | 75 | make 76 | 77 | To install to /usr/local/bin: 78 | 79 | sudo make install 80 | 81 | To install to other location that ends in .../usr/local/bin: 82 | 83 | sudo make PREFIX=/chroot install 84 | 85 | To install to other directory: 86 | 87 | sudo make INSTALL_BIN_DIR=/opt/bin install 88 | -------------------------------------------------------------------------------- /gcmcrypt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import argparse 4 | import binascii 5 | from cryptography.hazmat.backends import default_backend 6 | from cryptography.hazmat.primitives import hashes 7 | from cryptography.hazmat.primitives.ciphers import ( 8 | Cipher, algorithms, modes) 9 | from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC 10 | import os 11 | import sys 12 | 13 | 14 | def read_key(noprompt=False): 15 | key = b'' 16 | if not noprompt: 17 | os.write(2, b'Enter key or passphrase, ending with EOF: ') 18 | for l in sys.stdin: 19 | l = l.rstrip() 20 | if sys.version_info[0] > 2: # bytes for python 3 21 | l = l.encode('latin-1') 22 | key += l 23 | 24 | return key 25 | 26 | 27 | def encrypt(key, f, outfd, bytes, key_is_passphrase=True): 28 | rnd = os.urandom(bytes) # Either salt or iv, depending on key_is_passphrase 29 | os.write(outfd, rnd) # Write to fd for Python 2, 3 portability 30 | if key_is_passphrase: 31 | iv, key = iv_and_key_from_salt(rnd, key) 32 | else: 33 | iv = rnd 34 | # Construct an AES-GCM Cipher object with the given key and a 35 | # randomly generated IV. 36 | encryptor = Cipher( 37 | algorithms.AES(key), 38 | modes.GCM(iv), 39 | backend=default_backend()).encryptor() 40 | 41 | # associated_data will be authenticated but not encrypted, 42 | # it must also be passed in on decryption. 43 | encryptor.authenticate_additional_data(b'') 44 | 45 | while True: 46 | line = f.read(65536) 47 | if len(line) == 0: # Finished 48 | os.write(outfd, encryptor.finalize() + encryptor.tag) 49 | break 50 | os.write(outfd, encryptor.update(line)) 51 | 52 | 53 | def decrypt(iv, key, f, outfd): 54 | # Check file has enough data to be encrypted 55 | f.seek(0, 2) 56 | assert f.tell() >= (len(iv) + 16), 'File too small to have been encrypted by gcmcrypt' 57 | # Grab the tag from the end of the file 58 | f.seek(-16, 2) 59 | size = f.tell() - len(iv) # Size of ciphertext 60 | tag = f.read(16) 61 | assert len(tag) == 16 62 | f.seek(len(iv), 0) # Back to start of ciphertext 63 | # Cipher object with tag, and empty associated data 64 | decryptor = Cipher( 65 | algorithms.AES(key), 66 | modes.GCM(iv, tag), 67 | backend=default_backend()).decryptor() 68 | decryptor.authenticate_additional_data(b'') 69 | 70 | while True: 71 | if size > 0: 72 | line = f.read(min(65536, size)) 73 | size -= len(line) 74 | else: 75 | line = b'' 76 | if len(line) == 0: # Finished 77 | os.write(outfd, decryptor.finalize()) 78 | break 79 | os.write(outfd, decryptor.update(line)) 80 | 81 | 82 | def iv_and_key_from_salt(salt, passphrase): 83 | default_backend() 84 | kdf = PBKDF2HMAC( 85 | algorithm=hashes.SHA256(), 86 | length=len(salt) + 32, 87 | salt=salt, 88 | iterations=100000, 89 | backend=default_backend()) 90 | h = kdf.derive(passphrase) 91 | 92 | return h[:len(salt)], h[len(salt):] 93 | 94 | if __name__ == '__main__': 95 | parser = argparse.ArgumentParser() 96 | parser.add_argument('-b', '--bits', 97 | help='Number of bits in IV or salt, defaults to 96', 98 | type=int, default=96) 99 | parser.add_argument('-d', '--decrypt', help='Decrypt rather than encrypt', 100 | action='store_true') 101 | parser.add_argument('-k', '--key', help='Hex key from stdin', 102 | action='store_true') 103 | parser.add_argument('-n', '--noprompt', 104 | help='Do not prompt for key or passphrase on stderr', 105 | action='store_true') 106 | parser.add_argument('-o', '--out', 107 | help='Output to file instead of stdout') 108 | parser.add_argument('-p', '--passphrase', help='Passphrase from stdin', 109 | action='store_true') 110 | parser.add_argument('file', help="File to encrypt or decrypt") 111 | args = parser.parse_args() 112 | if args.key == args.passphrase: 113 | raise ValueError('Must specify one of -k or -p') 114 | assert args.bits % 8 == 0, 'bits in IV or salt must be a multiple of 8' 115 | bytes = args.bits // 8 116 | key = read_key(noprompt=args.noprompt) 117 | if args.passphrase: 118 | key_is_passphrase = True 119 | else: 120 | key_is_passphrase = False 121 | key = binascii.unhexlify(key) 122 | assert len(key) == 32, 'Key must be 64 hex digits, got {l}'.format( 123 | l=len(key) * 2) 124 | if args.out is None or args.out == '-': 125 | outfd = 1 # stdout 126 | else: 127 | outfile = open(args.out, 'wb') 128 | outfd = outfile.fileno() 129 | try: 130 | with open(args.file, 'rb') as fd: 131 | if args.decrypt: 132 | salt = fd.read(bytes) 133 | if key_is_passphrase: 134 | assert len(salt) == bytes, "Can't read enough from encrypted file" 135 | iv, key = iv_and_key_from_salt(salt, key) 136 | else: 137 | iv = salt 138 | decrypt(iv, key, fd, outfd) 139 | else: 140 | encrypt(key, fd, outfd, bytes, key_is_passphrase=key_is_passphrase) 141 | except Exception: 142 | if outfd !=1: 143 | outfile.close() 144 | try: 145 | os.unlink(args.out) 146 | except Exception: 147 | pass 148 | raise # Let the caller know what happened 149 | 150 | if outfd != 1: # Not stdout 151 | outfile.close() 152 | 153 | exit(0) 154 | -------------------------------------------------------------------------------- /gcmcrypt.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | using std::cerr; 17 | using std::cout; 18 | using std::endl; 19 | 20 | bool opt_decrypt = false; 21 | bool opt_key = false; 22 | bool opt_noprompt = false; 23 | char *opt_out = 0; 24 | char *infile = 0; 25 | 26 | #define KEY_SIZE 32 27 | 28 | void 29 | parse_args( int argc, char **argv ) 30 | { 31 | const struct option longopts[] = { 32 | { "decrypt", no_argument, 0, 'd'}, 33 | { "key", no_argument, 0, 'k'}, 34 | { "noprompt", no_argument, 0, 'n'}, 35 | { "out", required_argument, 0, 'o'}, 36 | {0,0,0,0}, 37 | }; 38 | 39 | int c = 0; 40 | 41 | while ( c != -1 ) { 42 | c = getopt_long( argc, argv, "dkno:", longopts, 0 ); 43 | switch ( c ) { 44 | case 'd': 45 | opt_decrypt = true; 46 | break; 47 | case 'k': 48 | opt_key = true; 49 | break; 50 | case 'n': 51 | opt_noprompt = true; 52 | break; 53 | case 'o': 54 | opt_out = optarg; 55 | break; 56 | case -1: 57 | break; 58 | default: 59 | cerr << "Unknown option " << char(c) << " " << c << endl; 60 | exit( 1 ); 61 | } 62 | } 63 | if ( ! opt_key ) { 64 | cerr << "Key or passphrase required" << endl; 65 | exit( 1 ); 66 | } 67 | if ( optind == ( argc - 1 ) ) { 68 | infile = argv[optind]; 69 | } else { 70 | cerr << "Missing file name arg" << endl; 71 | exit( 1 ); 72 | } 73 | } 74 | 75 | unsigned char 76 | hex_value( char c ) 77 | { 78 | switch ( c ) { 79 | case '0': 80 | return 0; 81 | case '1': 82 | return 1; 83 | case '2': 84 | return 2; 85 | case '3': 86 | return 3; 87 | case '4': 88 | return 4; 89 | case '5': 90 | return 5; 91 | case '6': 92 | return 6; 93 | case '7': 94 | return 7; 95 | case '8': 96 | return 8; 97 | case '9': 98 | return 9; 99 | case 'a': 100 | case 'A': 101 | return 10; 102 | case 'b': 103 | case 'B': 104 | return 11; 105 | case 'c': 106 | case 'C': 107 | return 12; 108 | case 'd': 109 | case 'D': 110 | return 13; 111 | case 'e': 112 | case 'E': 113 | return 14; 114 | case 'f': 115 | case 'F': 116 | return 15; 117 | default: 118 | cerr << "Illegal hex char: " << c << endl; 119 | exit( 1 ); 120 | } 121 | } 122 | 123 | void 124 | read_key( unsigned char *buf ) 125 | { 126 | char hex_buf[2]; 127 | 128 | if ( ! opt_noprompt ) { 129 | cerr << "Enter key or passphrase, ending with EOF: "; 130 | } 131 | for ( int i = 0 ; i < KEY_SIZE ; ++i ) { 132 | if ( read( 0, hex_buf, sizeof( hex_buf ) ) != sizeof( hex_buf ) ) { 133 | cerr << "Failed to read full key" << endl; 134 | exit( 1 ); 135 | } 136 | buf[i] = hex_value( hex_buf[0] ) << 4 | hex_value( hex_buf[1] ); 137 | } 138 | // Check for extra junk 139 | while ( read( 0, hex_buf, 1 ) > 0 ) { 140 | if ( ! isspace( hex_buf[0] ) ) { 141 | cerr << "Unexpected non-whitespace trailing key" << endl; 142 | exit( 1 ); 143 | } 144 | } 145 | } 146 | 147 | void 148 | gcm_decrypt( int fd, size_t file_len, unsigned char *key_buf ) 149 | { 150 | unsigned char iv[12]; 151 | unsigned char tag[AES_BLOCK_SIZE]; 152 | unsigned char in_buf[65536]; 153 | unsigned char out_buf[65536]; 154 | EVP_CIPHER_CTX ctx; 155 | size_t bytes_read; 156 | int bytes_decrypted; 157 | size_t to_read = file_len - (AES_BLOCK_SIZE + 12); 158 | int out_fd = 1; // stdout, unless --out specified 159 | 160 | if ( file_len < (AES_BLOCK_SIZE + 12) ) { 161 | cerr << "File not long enough to be encrypted" << endl; 162 | exit( 1 ); 163 | } 164 | // Get auth tag 165 | if ( lseek( fd, -AES_BLOCK_SIZE, SEEK_END ) == -1 ) { 166 | cerr << "Failed to seek to auth tag" << endl; 167 | exit( 1 ); 168 | } 169 | if ( read( fd, tag, AES_BLOCK_SIZE ) != AES_BLOCK_SIZE ) { 170 | cerr << "Failed to read auth tag" << endl; 171 | exit( 1 ); 172 | } 173 | // Get IV 174 | if ( lseek( fd, 0, SEEK_SET ) != 0 ) { 175 | cerr << "Failed to seek to start of file" << endl; 176 | exit( 1 ); 177 | } 178 | if ( read( fd, iv, 12 ) != 12 ) { 179 | cerr << "Failed to read IV" << endl; 180 | exit( 1 ); 181 | } 182 | 183 | if ( opt_out ) { 184 | if ( ( out_fd = open( opt_out, O_CREAT | O_WRONLY, 0644 ) ) == -1 ) { 185 | cerr << "Couldn't create output file" << endl; 186 | exit( 1 ); 187 | } 188 | } 189 | EVP_CIPHER_CTX_init( &ctx ); 190 | if ( EVP_DecryptInit_ex( &ctx, EVP_aes_256_gcm(), 0, key_buf, iv ) != 1 ) { 191 | cerr << "Failed to initialize decryptor" << endl; 192 | exit( 1 ); 193 | } 194 | while ( to_read > 0 ) { 195 | bytes_read = read( fd, in_buf, std::min( (size_t) 65536, to_read ) ); 196 | if ( bytes_read < 1 ) { 197 | cerr << "Couldn't read file" << endl; 198 | exit( 1 ); 199 | } 200 | to_read -= bytes_read; 201 | if ( EVP_DecryptUpdate( &ctx, out_buf, &bytes_decrypted, in_buf, bytes_read ) != 1 ) { 202 | cerr << "Failed to decrypt" << endl; 203 | exit( 1 ); 204 | } 205 | if ( write( out_fd, out_buf, bytes_decrypted ) != bytes_decrypted ) { 206 | cerr << "Couldn't write decrypted bytes" << endl; 207 | exit( 1 ); 208 | } 209 | } 210 | // Check auth tag 211 | if ( EVP_CIPHER_CTX_ctrl( &ctx, EVP_CTRL_GCM_SET_TAG, AES_BLOCK_SIZE, tag ) != 1 ) { 212 | cerr << "Failed to initialize set auth tag" << endl; 213 | exit( 1 ); 214 | } 215 | if ( EVP_DecryptFinal( &ctx, out_buf, &bytes_decrypted ) != 1 ) { 216 | cerr << "Failed to finalize decryptor" << endl; 217 | exit( 1 ); 218 | } 219 | if ( bytes_decrypted != 0 ) { 220 | cerr << "Decrypt finalization returned extra stuff" << endl; 221 | exit( 1 ); 222 | } 223 | if ( out_fd != 1 ) { 224 | if ( close( out_fd ) != 0 ) { 225 | cerr << "Failed to close --out file" << endl; 226 | exit( 1 ); 227 | } 228 | } 229 | } 230 | 231 | void gcm_encrypt( int fd, size_t file_len, unsigned char *key_buf ) 232 | { 233 | unsigned char iv[12]; 234 | unsigned char tag[AES_BLOCK_SIZE]; 235 | unsigned char in_buf[65536]; 236 | unsigned char out_buf[65536]; 237 | EVP_CIPHER_CTX ctx; 238 | size_t bytes_read; 239 | int bytes_encrypted; 240 | int out_fd = 1; 241 | 242 | if ( opt_out ) { 243 | if ( ( out_fd = open( opt_out, O_CREAT | O_WRONLY, 0644 ) ) == -1 ) { 244 | cerr << "Couldn't create output file" << endl; 245 | exit( 1 ); 246 | } 247 | } 248 | EVP_CIPHER_CTX_init( &ctx ); 249 | RAND_bytes( iv, sizeof(iv) ); 250 | if ( write( out_fd, iv, sizeof(iv) ) != sizeof(iv) ) { 251 | cerr << "Failed to write IV to stdout" << endl; 252 | exit( 1 ); 253 | } 254 | if ( lseek( fd, 0, SEEK_SET ) != 0 ) { 255 | cerr << "Failed to seek to start of file" << endl; 256 | exit( 1 ); 257 | } 258 | if ( EVP_EncryptInit_ex( &ctx, EVP_aes_256_gcm(), 0, key_buf, iv ) != 1 ) { 259 | cerr << "Failed to initialize encryptor" << endl; 260 | exit( 1 ); 261 | } 262 | while ( ( bytes_read = read( fd, in_buf, 65536 ) ) > 0 ) { 263 | if ( EVP_EncryptUpdate( &ctx, out_buf, &bytes_encrypted, in_buf, bytes_read ) != 1 ) { 264 | cerr << "Failed to encrypt" << endl; 265 | exit( 1 ); 266 | } 267 | if ( write( out_fd, out_buf, bytes_encrypted ) != bytes_encrypted ) { 268 | cerr << "Failed to write encrypted data" << endl; 269 | exit( 1 ); 270 | } 271 | } 272 | if ( EVP_EncryptFinal_ex( &ctx, out_buf, &bytes_encrypted ) != 1 ) { 273 | cerr << "Failed to finalize encryption" << endl; 274 | exit( 1 ); 275 | } 276 | if ( bytes_encrypted != 0 ) { 277 | cerr << "Finalized unexpected " << bytes_encrypted << " bytes output" << endl; 278 | exit( 1 ); 279 | } 280 | if ( EVP_CIPHER_CTX_ctrl( &ctx, EVP_CTRL_GCM_GET_TAG, AES_BLOCK_SIZE, tag ) != 1 ) { 281 | cerr << "Failed to get auth tag" << endl; 282 | exit( 1 ); 283 | } 284 | if ( write( out_fd, tag, AES_BLOCK_SIZE ) != AES_BLOCK_SIZE ) { 285 | cerr << "Failed to write GCM tag" << endl; 286 | exit( 1 ); 287 | } 288 | if ( out_fd != 1 ) { 289 | if ( close( out_fd ) != 0 ) { 290 | cerr << "Failed to close --out file" << endl; 291 | exit( 1 ); 292 | } 293 | } 294 | } 295 | 296 | int 297 | main(int argc, char **argv) 298 | { 299 | unsigned char key_buf[KEY_SIZE]; 300 | 301 | parse_args( argc, argv ); 302 | 303 | // Init PRNG 304 | int read_bytes = RAND_load_file( "/dev/urandom", 1024 ); 305 | 306 | if ( read_bytes != 1024 ) { 307 | cerr << "Could not seed PRNG from /dev/urandom (" << read_bytes << "/1024 read" << endl; 308 | exit( 1 ); 309 | } 310 | // Init ciphers 311 | OpenSSL_add_all_ciphers(); 312 | 313 | // Open input file 314 | int fd = open( infile, O_RDONLY ); 315 | if ( fd == -1 ) { 316 | cerr << "Couldn't open " << infile << endl; 317 | exit( 1 ); 318 | } 319 | // Check its size 320 | off_t file_len = lseek( fd, 0, SEEK_END ); 321 | if ( file_len == (off_t) -1 ) { 322 | cerr << "Couldn't seek to end of file" << endl; 323 | exit( 1 ); 324 | } 325 | if ( opt_key ) { // Read key from stdin 326 | read_key( key_buf ); 327 | } 328 | if ( opt_decrypt ) { 329 | gcm_decrypt( fd, file_len, key_buf ); 330 | } else { 331 | gcm_encrypt( fd, file_len, key_buf ); 332 | } 333 | exit( 0 ); 334 | } 335 | --------------------------------------------------------------------------------