From ceaf581bb861c854ddd7f08d1df61a25fce03d86 Mon Sep 17 00:00:00 2001 From: Nickolay Olshevsky Date: Thu, 17 Aug 2017 11:35:34 +0300 Subject: [PATCH 1/5] Fixed erroneous return from the dearmoring reader. --- src/lib/reader.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/lib/reader.c b/src/lib/reader.c index e4b8841cc0..7b8a3905c9 100644 --- a/src/lib/reader.c +++ b/src/lib/reader.c @@ -1012,11 +1012,11 @@ armoured_data_reader(pgp_stream_t *stream, unsigned first; uint8_t * dest = dest_; char buf[1024]; - int saved; + size_t saved; rnp_result ret; dearmour = pgp_reader_get_arg(readinfo); - saved = (int) length; + saved = length; if (dearmour->eof64 && !dearmour->buffered) { if (dearmour->state != OUTSIDE_BLOCK && dearmour->state != AT_TRAILER_NAME) { (void) fprintf(stderr, "armoured_data_reader: bad dearmour state\n"); @@ -1156,11 +1156,13 @@ armoured_data_reader(pgp_stream_t *stream, "armoured_data_reader: bad dearmour eof64\n"); return 0; } + dearmour->state = AT_TRAILER_NAME; + if (first) { - dearmour->state = AT_TRAILER_NAME; goto reloop; + } else { + return saved - length; } - return -1; } } if (!dearmour->buffered) { From 44c6b5ba81853eff329e4ff3a21ead03907e90a3 Mon Sep 17 00:00:00 2001 From: Nickolay Olshevsky Date: Fri, 18 Aug 2017 18:09:33 +0300 Subject: [PATCH 2/5] Fixed dearmoring reader to correctly work with undefined length packets; Updated CLI tests with armored signed/encrypted messages --- src/lib/reader.c | 12 +++ src/tests/cli_tests.py | 165 ++++++++++++++++++++--------------------- 2 files changed, 94 insertions(+), 83 deletions(-) diff --git a/src/lib/reader.c b/src/lib/reader.c index 7b8a3905c9..995b69d309 100644 --- a/src/lib/reader.c +++ b/src/lib/reader.c @@ -1014,9 +1014,21 @@ armoured_data_reader(pgp_stream_t *stream, char buf[1024]; size_t saved; rnp_result ret; + int n; dearmour = pgp_reader_get_arg(readinfo); saved = length; + + if (!stream->coalescing && stream->virtualc && stream->virtualoff < stream->virtualc) { + n = read_partial_data(stream, dest_, length); + if ((n < 0) || (n == length)) { + return n; + } else { + length -= n; + dest_ = (char*)dest_ + n; + } + } + if (dearmour->eof64 && !dearmour->buffered) { if (dearmour->state != OUTSIDE_BLOCK && dearmour->state != AT_TRAILER_NAME) { (void) fprintf(stderr, "armoured_data_reader: bad dearmour state\n"); diff --git a/src/tests/cli_tests.py b/src/tests/cli_tests.py index 5796ca7490..ae9cfbb52f 100755 --- a/src/tests/cli_tests.py +++ b/src/tests/cli_tests.py @@ -283,8 +283,11 @@ def rnp_genkey_rsa(userid, bits = 2048): if ret != 0: raise_err('rsa key generation failed', err) -def rnp_encrypt_file(recipient, src, dst): - ret, out, err = run_proc(RNP, ['--homedir', RNPDIR, '--userid', recipient, '--encrypt', src, '--output', dst]) +def rnp_encrypt_file(recipient, src, dst, armour = False): + params = ['--homedir', RNPDIR, '--userid', recipient, '--encrypt', src, '--output', dst] + if armour: + params += ['--armor'] + ret, out, err = run_proc(RNP, params) if ret != 0: raise_err('rnp encryption failed', err) @@ -305,9 +308,12 @@ def rnp_sign_file(src, dst, signer, armour = False): if ret != 0: raise_err('rnp signing failed', err) -def rnp_sign_detached(src, signer): +def rnp_sign_detached(src, signer, armour = False): pipe = pswd_pipe(PASSWORD) - ret, out, err = run_proc(RNP, ['--homedir', RNPDIR, '--pass-fd', str(pipe), '--userid', signer, '--sign', '--detach', src]) + params = ['--homedir', RNPDIR, '--pass-fd', str(pipe), '--userid', signer, '--sign', '--detach', src] + if armour: + params += ['--armor'] + ret, out, err = run_proc(RNP, params) os.close(pipe) if ret != 0: raise_err('rnp detached signing failed', err) @@ -349,8 +355,11 @@ def gpg_import_secring(kpath = None): if ret != 0: raise_err('gpg secret key import failed', err) -def gpg_encrypt_file(src, dst, cipher = 'AES', zlevel = 6): - ret, out, err = run_proc(GPG, ['--homedir', GPGDIR, '-e', '-z', str(zlevel), '-r', 'encryption@rnp', '--batch', '--cipher-algo', cipher, '--trust-model', 'always', '--output', dst, src]) +def gpg_encrypt_file(src, dst, cipher = 'AES', zlevel = 6, armour = False): + params = ['--homedir', GPGDIR, '-e', '-z', str(zlevel), '-r', 'encryption@rnp', '--batch', '--cipher-algo', cipher, '--trust-model', 'always', '--output', dst, src] + if armour: + params.insert(2, '--armor') + ret, out, err = run_proc(GPG, params) if ret != 0: raise_err('gpg encryption failed for cipher ' + cipher, err) @@ -381,13 +390,19 @@ def gpg_verify_detached(src, sig, signer = None): if signer and (not match.group(1) == signer): raise_err('gpg detached verification failed, wrong signer') -def gpg_sign_file(src, dst, signer): - ret, out, err = run_proc(GPG, ['--homedir', GPGDIR, '--pinentry-mode=loopback', '--batch', '--yes', '--passphrase', PASSWORD, '--trust-model', 'always', '-u', signer, '-o', dst, '-s', src]) +def gpg_sign_file(src, dst, signer, armour = False): + params = ['--homedir', GPGDIR, '--pinentry-mode=loopback', '--batch', '--yes', '--passphrase', PASSWORD, '--trust-model', 'always', '-u', signer, '-o', dst, '-s', src] + if armour: + params.insert(2, '--armor') + ret, out, err = run_proc(GPG, params) if ret != 0: raise_err('gpg signing failed', err) -def gpg_sign_detached(src, signer): - ret, out, err = run_proc(GPG, ['--homedir', GPGDIR, '--pinentry-mode=loopback', '--batch', '--yes', '--passphrase', PASSWORD, '--trust-model', 'always', '-u', signer, '--detach-sign', src]) +def gpg_sign_detached(src, signer, armour = False): + params = ['--homedir', GPGDIR, '--pinentry-mode=loopback', '--batch', '--yes', '--passphrase', PASSWORD, '--trust-model', 'always', '-u', signer, '--detach-sign', src] + if armour: + params.insert(2, '--armor') + ret, out, err = run_proc(GPG, params) if ret != 0: raise_err('gpg detached signing failed', err) @@ -404,11 +419,13 @@ def rnp_encryption_gpg_to_rnp(cipher, filesize, zlevel = 6): try: # Generate random file of required size random_text(src, filesize) - # Encrypt cleartext file with GPG - gpg_encrypt_file(src, dst, cipher, zlevel) - # Decrypt encrypted file with RNP - rnp_decrypt_file(dst, dec) - compare_files(src, dec, 'rnp decrypted data differs') + for armour in [False, True]: + # Encrypt cleartext file with GPG + gpg_encrypt_file(src, dst, cipher, zlevel, armour) + # Decrypt encrypted file with RNP + rnp_decrypt_file(dst, dec) + compare_files(src, dec, 'rnp decrypted data differs') + remove_files(dst, dec) finally: # Cleanup remove_files(src, dst, dec) @@ -418,20 +435,20 @@ def rnp_encryption_rnp_to_gpg(filesize): try: # Generate random file of required size random_text(src, filesize) - # Encrypt cleartext file with RNP - rnp_encrypt_file('encryption@rnp', src, enc) - # Decrypt encrypted file with GPG - gpg_decrypt_file(enc, dst, PASSWORD) - compare_files(src, dst, 'gpg decrypted data differs') - # Decrypt encrypted file with RNP - rnp_decrypt_file(enc, dst) - compare_files(src, dst, 'rnp decrypted data differs') + for armour in [False, True]: + # Encrypt cleartext file with RNP + rnp_encrypt_file('encryption@rnp', src, enc, armour) + # Decrypt encrypted file with GPG + gpg_decrypt_file(enc, dst, PASSWORD) + compare_files(src, dst, 'gpg decrypted data differs') + # Decrypt encrypted file with RNP + rnp_decrypt_file(enc, dst) + compare_files(src, dst, 'rnp decrypted data differs') + remove_files(enc, dst) finally: # Cleanup remove_files(src, dst, enc) - return - ''' Things to try later: - different public key algorithms @@ -447,9 +464,7 @@ def rnp_encryption(): # Import keyring to the GPG gpg_import_pubring() # Encrypt cleartext file with GPG and decrypt it with RNP, using different ciphers and file sizes - # Could be non working, see #353: IDEA, 3DES, CAST5, BLOWFISH ciphers = ['AES', 'AES192', 'AES256', 'TWOFISH', 'CAMELLIA128', 'CAMELLIA192', 'CAMELLIA256', 'IDEA', '3DES', 'CAST5', 'BLOWFISH'] - #ciphers = ['AES', 'AES192', 'AES256', 'TWOFISH', 'CAMELLIA128', 'CAMELLIA192', 'CAMELLIA256'] sizes = [20, 1000, 2000, 5000, 10000, 20000, 60000, 1000000] for cipher in ciphers: for size in sizes: @@ -465,86 +480,76 @@ def rnp_encryption(): run_test(rnp_encryption_rnp_to_gpg, size) # Cleanup clear_keyrings() - return def rnp_signing_rnp_to_gpg(filesize): - src, sig, asc, ver = map(lambda x: path.join(WORKDIR, 'cleartext' + x), ['.txt', '.sig', '.asc', '.ver']) + src, sig, ver = map(lambda x: path.join(WORKDIR, 'cleartext' + x), ['.txt', '.sig', '.ver']) try: # Generate random file of required size random_text(src, filesize) - # Sign file with RNP - rnp_sign_file(src, sig, 'signing@rnp') - # Verify signed file with RNP - rnp_verify_file(sig, ver, 'signing@rnp') - compare_files(src, ver, 'rnp verified data differs') - remove_files(ver) - # Verify signed message with GPG - gpg_verify_file(sig, ver, 'signing@rnp') - compare_files(src, ver, 'gpg verified data differs') - remove_files(ver) - # Armored signing test - rnp_sign_file(src, asc, 'signing@rnp', armour = True) - # Verify signed file with RNP - rnp_verify_file(asc, ver, 'signing@rnp') - compare_files(src, ver, 'rnp verified data differs') - remove_files(ver) - # Verify signed message with GPG - gpg_verify_file(asc, ver, 'signing@rnp') - compare_files(src, ver, 'gpg verified data differs') - remove_files(ver) + for armour in [False, True]: + # Sign file with RNP + rnp_sign_file(src, sig, 'signing@rnp', armour = armour) + # Verify signed file with RNP + rnp_verify_file(sig, ver, 'signing@rnp') + compare_files(src, ver, 'rnp verified data differs') + remove_files(ver) + # Verify signed message with GPG + gpg_verify_file(sig, ver, 'signing@rnp') + compare_files(src, ver, 'gpg verified data differs') + remove_files(sig, ver) finally: # Cleanup - remove_files(src, sig, asc, ver) - - return + remove_files(src, sig, ver) def rnp_detached_signing_rnp_to_gpg(filesize): - src, sig = map(lambda x: path.join(WORKDIR, 'cleartext' + x), ['.txt', '.txt.sig']) + src, sig, asc = map(lambda x: path.join(WORKDIR, 'cleartext' + x), ['.txt', '.txt.sig', '.txt.asc']) try: # Generate random file of required size random_text(src, filesize) - # Sign file with RNP - rnp_sign_detached(src, 'signing@rnp') - # Verify signature with RNP - rnp_verify_detached(sig, 'signing@rnp') - # Verify signed message with GPG - gpg_verify_detached(src, sig, 'signing@rnp') + for armour in [True, False]: + # Sign file with RNP + rnp_sign_detached(src, 'signing@rnp', armour = armour) + sigpath = asc if armour else sig + # Verify signature with RNP + rnp_verify_detached(sigpath, 'signing@rnp') + # Verify signed message with GPG + gpg_verify_detached(src, sigpath, 'signing@rnp') + remove_files(sigpath) finally: # Cleanup - remove_files(src, sig) - - return + remove_files(src, sig, asc) def rnp_signing_gpg_to_rnp(filesize): src, sig, ver = map(lambda x: path.join(WORKDIR, 'cleartext' + x), ['.txt', '.sig', '.ver']) try: # Generate random file of required size random_text(src, filesize) - # Sign file with GPG - gpg_sign_file(src, sig, 'signing@gpg') - # Verify file with RNP - rnp_verify_file(sig, ver, 'signing@gpg') - compare_files(src, ver, 'rnp verified data differs') + for armour in [True, False]: + # Sign file with GPG + gpg_sign_file(src, sig, 'signing@gpg', armour = armour) + # Verify file with RNP + rnp_verify_file(sig, ver, 'signing@gpg') + compare_files(src, ver, 'rnp verified data differs') + remove_files(sig, ver) finally: # Cleanup remove_files(src, sig, ver) - - return def rnp_detached_signing_gpg_to_rnp(filesize): - src, sig = map(lambda x: path.join(WORKDIR, 'cleartext' + x), ['.txt', '.txt.sig']) + src, sig, asc = map(lambda x: path.join(WORKDIR, 'cleartext' + x), ['.txt', '.txt.sig', '.txt.asc']) try: # Generate random file of required size random_text(src, filesize) - # Sign file with GPG - gpg_sign_detached(src, 'signing@gpg') - # Verify file with RNP - rnp_verify_detached(sig, 'signing@gpg') + for armour in [True, False]: + # Sign file with GPG + gpg_sign_detached(src, 'signing@gpg', armour = armour) + # Verify file with RNP + sigpath = asc if armour else sig + rnp_verify_detached(sigpath, 'signing@gpg') + remove_files(sigpath) finally: # Cleanup - remove_files(src, sig) - - return + remove_files(src, sig, asc) ''' Things to try later: @@ -573,15 +578,11 @@ def rnp_signing(): run_test(rnp_signing_gpg_to_rnp, size) run_test(rnp_detached_signing_gpg_to_rnp, size) - return - def run_rnp_tests(): # 1. Encryption / decryption against GPG rnp_encryption() # 2. Signing / verification against GPG rnp_signing() - - return ''' Things to try here later on: @@ -601,8 +602,6 @@ def run_rnpkeys_tests(): # 5. Generate key with RNP and export it and then import to GnuPG run_test(rnpkey_export_to_gpg) - return - def run_tests(): global DEBUG From 448f12679486dfebb608f8abb474cfdb1a0a0219 Mon Sep 17 00:00:00 2001 From: Nickolay Olshevsky Date: Fri, 18 Aug 2017 22:23:28 +0300 Subject: [PATCH 3/5] Fixed processing of cleartext data to correctly handle line endings and trailing spaces. --- src/lib/reader.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/lib/reader.c b/src/lib/reader.c index b05c04860c..f679212825 100644 --- a/src/lib/reader.c +++ b/src/lib/reader.c @@ -633,34 +633,52 @@ process_dash_escaped(pgp_stream_t *stream, } } if (c == '\n' && body->length) { - if (memchr(body->data + 1, '\n', body->length - 1) != NULL) { + if (memchr(body->data + 2, '\n', body->length - 2) != NULL) { (void) fprintf(stderr, "process_dash_escaped: newline found\n"); return -1; } if (rnp_get_debug(__FILE__)) { - fprintf(stderr, "Got body:\n%s\n", body->data); + fprintf(stderr, "Got body:\n%.*s\n", body->length, body->data); } + + // Removing trailing whitespaces as per RFC + while ((body->length > 0) && ((body->data[body->length - 1] == 0x20) || + (body->data[body->length - 1] == 0x09))) { + body->length--; + } + CALLBACK(PGP_PTAG_CT_SIGNED_CLEARTEXT_BODY, cbinfo, &content); body->length = 0; } - body->data[body->length++] = c; - total += 1; - if (body->length == sizeof(body->data)) { + + if (c != '\r') { + if (c == '\n') { + body->data[body->length++] = '\r'; + total++; + } + body->data[body->length++] = c; + total++; + } + + if (body->length == sizeof(body->data) - 1) { if (rnp_get_debug(__FILE__)) { - (void) fprintf(stderr, "Got body (2):\n%s\n", body->data); + (void) fprintf(stderr, "Got body (2):\n%.*s\n", body->length, body->data); } CALLBACK(PGP_PTAG_CT_SIGNED_CLEARTEXT_BODY, cbinfo, &content); body->length = 0; } } - if (body->data[0] != '\n') { + + if (body->data[1] != '\n') { (void) fprintf(stderr, "process_dash_escaped: no newline in body data\n"); return -1; } - if (body->length != 1) { + + if (body->length != 2) { (void) fprintf(stderr, "process_dash_escaped: bad body length\n"); return -1; } + /* don't send that one character, because it's part of the trailer */ (void) memset(&content2, 0x0, sizeof(content2)); CALLBACK(PGP_PTAG_CT_SIGNED_CLEARTEXT_TRAILER, cbinfo, &content2); From 68169b6d769f6a7b742a8bde892d18c4bd7927ca Mon Sep 17 00:00:00 2001 From: Nickolay Olshevsky Date: Thu, 24 Aug 2017 12:10:14 +0300 Subject: [PATCH 4/5] Fixed cleartext signing and verification issues; Added tests for cleartext signing --- src/lib/reader.c | 25 ++++++++++---- src/lib/writer.c | 19 ++++++++--- src/tests/cli_common.py | 3 +- src/tests/cli_tests.py | 74 +++++++++++++++++++++++++++++++++++++++-- 4 files changed, 107 insertions(+), 14 deletions(-) diff --git a/src/lib/reader.c b/src/lib/reader.c index f679212825..63fd314c0e 100644 --- a/src/lib/reader.c +++ b/src/lib/reader.c @@ -580,6 +580,7 @@ process_dash_escaped(pgp_stream_t *stream, pgp_packet_t content; const char * hashstr; int total; + bool hadcr = false; pgp_hash_alg_t alg = PGP_HASH_MD5; // default body = &content.u.cleartext_body; @@ -632,8 +633,20 @@ process_dash_escaped(pgp_stream_t *stream, return -1; } } + + // treating CR as newline, and then skipping LF + if (c == '\r') { + c = '\n'; + hadcr = true; + } else if ((c == '\n') && hadcr) { + continue; + } else { + hadcr = false; + } + if (c == '\n' && body->length) { - if (memchr(body->data + 2, '\n', body->length - 2) != NULL) { + if ((body->length > 2) && + (memchr(body->data + 2, '\n', body->length - 2) != NULL)) { (void) fprintf(stderr, "process_dash_escaped: newline found\n"); return -1; } @@ -651,14 +664,12 @@ process_dash_escaped(pgp_stream_t *stream, body->length = 0; } - if (c != '\r') { - if (c == '\n') { - body->data[body->length++] = '\r'; - total++; - } - body->data[body->length++] = c; + if (c == '\n') { + body->data[body->length++] = '\r'; total++; } + body->data[body->length++] = c; + total++; if (body->length == sizeof(body->data) - 1) { if (rnp_get_debug(__FILE__)) { diff --git a/src/lib/writer.c b/src/lib/writer.c index 56588589d3..332790e701 100644 --- a/src/lib/writer.c +++ b/src/lib/writer.c @@ -422,6 +422,7 @@ dash_esc_writer(const uint8_t *src, unsigned len, pgp_error_t **errors, pgp_writ { dashesc_t *dash = pgp_writer_get_arg(writer); unsigned n; + bool escape; if (rnp_get_debug(__FILE__)) { unsigned i = 0; @@ -441,11 +442,11 @@ dash_esc_writer(const uint8_t *src, unsigned len, pgp_error_t **errors, pgp_writ for (n = 0; n < len; ++n) { unsigned l; - if (dash->seen_nl) { - if (src[n] == '-' && !stacked_write(writer, "- ", 2, errors)) { - return false; + escape = false; + if (dash->seen_nl || dash->seen_cr) { + if (src[n] == '-') { + escape = true; } - dash->seen_nl = 0; } dash->seen_nl = src[n] == '\n'; @@ -455,8 +456,18 @@ dash_esc_writer(const uint8_t *src, unsigned len, pgp_error_t **errors, pgp_writ } pgp_sig_add_data(dash->sig, "\r", 1); } + if (dash->seen_cr && !dash->seen_nl) { + if (!stacked_write(writer, "\n", 1, errors)) { + return false; + } + pgp_sig_add_data(dash->sig, "\n", 1); + } dash->seen_cr = src[n] == '\r'; + if (escape && !stacked_write(writer, "- ", 2, errors)) { + return false; + } + if (!stacked_write(writer, &src[n], 1, errors)) { return false; } diff --git a/src/tests/cli_common.py b/src/tests/cli_common.py index 7e0ed9645e..6c4990713c 100644 --- a/src/tests/cli_common.py +++ b/src/tests/cli_common.py @@ -29,7 +29,8 @@ def pswd_pipe(passphrase): return pr def random_text(path, size): - st = ''.join(random.choice(string.printable) for _ in range(size)) + #st = ''.join(random.choice(string.printable) for _ in range(size)) + st = ''.join(random.choice(string.ascii_letters + string.digits + " \t\n-,.") for _ in range(size)) with open(path, 'w+') as f: f.write(st) diff --git a/src/tests/cli_tests.py b/src/tests/cli_tests.py index ae9cfbb52f..90d9558ab1 100755 --- a/src/tests/cli_tests.py +++ b/src/tests/cli_tests.py @@ -119,8 +119,11 @@ def check_packets(fname, regexp): return result def clear_keyrings(): - shutil.rmtree(RNPDIR) - shutil.rmtree(GPGDIR) + try: + shutil.rmtree(RNPDIR) + shutil.rmtree(GPGDIR) + except: + pass os.mkdir(RNPDIR, 0700) os.mkdir(GPGDIR, 0700) @@ -318,6 +321,13 @@ def rnp_sign_detached(src, signer, armour = False): if ret != 0: raise_err('rnp detached signing failed', err) +def rnp_sign_cleartext(src, dst, signer): + pipe = pswd_pipe(PASSWORD) + ret, out, err = run_proc(RNP, ['--homedir', RNPDIR, '--pass-fd', str(pipe), '--userid', signer, '--output', dst, '--clearsign', src]) + os.close(pipe) + if ret != 0: + raise_err('rnp cleartext signing failed', err) + def rnp_verify_file(src, dst, signer = None): params = ['--homedir', RNPDIR, '--verify-cat', src, '--output', dst] ret, out, err = run_proc(RNP, params) @@ -341,6 +351,18 @@ def rnp_verify_detached(sig, signer = None): if signer and (not match.group(1).strip() == signer.strip()): raise_err('rnp detached verification failed, wrong signer') +def rnp_verify_cleartext(src, signer = None): + params = ['--homedir', RNPDIR, '--verify', src] + ret, out, err = run_proc(RNP, params) + if ret != 0: + raise_err('rnp verification failed', err + out) + # Check RNP output + match = re.match(RE_RNP_GOOD_SIGNATURE, err) + if not match: + raise_err('wrong rnp verification output', err) + if signer and (not match.group(1).strip() == signer.strip()): + raise_err('rnp verification failed, wrong signer') + def gpg_import_pubring(kpath = None): if not kpath: kpath = path.join(RNPDIR, 'pubring.gpg') @@ -390,6 +412,17 @@ def gpg_verify_detached(src, sig, signer = None): if signer and (not match.group(1) == signer): raise_err('gpg detached verification failed, wrong signer') +def gpg_verify_cleartext(src, signer = None): + ret, out, err = run_proc(GPG, ['--homedir', GPGDIR, '--batch', '--yes', '--trust-model', 'always', '--verify', src]) + if ret != 0: + raise_err('gpg cleartext verification failed', err) + # Check GPG output + match = re.match(RE_GPG_GOOD_SIGNATURE, err) + if not match: + raise_err('wrong gpg verification output', err) + if signer and (not match.group(1) == signer): + raise_err('gpg verification failed, wrong signer') + def gpg_sign_file(src, dst, signer, armour = False): params = ['--homedir', GPGDIR, '--pinentry-mode=loopback', '--batch', '--yes', '--passphrase', PASSWORD, '--trust-model', 'always', '-u', signer, '-o', dst, '-s', src] if armour: @@ -406,6 +439,12 @@ def gpg_sign_detached(src, signer, armour = False): if ret != 0: raise_err('gpg detached signing failed', err) +def gpg_sign_cleartext(src, dst, signer): + params = ['--homedir', GPGDIR, '--pinentry-mode=loopback', '--batch', '--yes', '--passphrase', PASSWORD, '--trust-model', 'always', '-u', signer, '-o', dst, '--clearsign', src] + ret, out, err = run_proc(GPG, params) + if ret != 0: + raise_err('gpg cleartext signing failed', err) + ''' Things to try here later on: - different symmetric algorithms @@ -519,6 +558,21 @@ def rnp_detached_signing_rnp_to_gpg(filesize): # Cleanup remove_files(src, sig, asc) +def rnp_cleartext_signing_rnp_to_gpg(filesize): + src, asc = map(lambda x: path.join(WORKDIR, 'cleartext' + x), ['.txt', '.txt.asc']) + try: + # Generate random file of required size + random_text(src, filesize) + # Sign file with RNP + rnp_sign_cleartext(src, asc, 'signing@rnp') + # Verify signature with RNP + rnp_verify_cleartext(asc, 'signing@rnp') + # Verify signed message with GPG + gpg_verify_cleartext(asc, 'signing@rnp') + finally: + # Cleanup + remove_files(src, asc) + def rnp_signing_gpg_to_rnp(filesize): src, sig, ver = map(lambda x: path.join(WORKDIR, 'cleartext' + x), ['.txt', '.sig', '.ver']) try: @@ -551,6 +605,20 @@ def rnp_detached_signing_gpg_to_rnp(filesize): # Cleanup remove_files(src, sig, asc) +def rnp_cleartext_signing_gpg_to_rnp(filesize): + src, asc = map(lambda x: path.join(WORKDIR, 'cleartext' + x), ['.txt', '.txt.asc']) + try: + # Generate random file of required size + random_text(src, filesize) + # Sign file with GPG + gpg_sign_cleartext(src, asc, 'signing@rnp') + # Verify signature with RNP + rnp_verify_cleartext(asc, 'signing@rnp') + # Verify signed message with GPG + gpg_verify_cleartext(asc, 'signing@rnp') + finally: + remove_files(src, asc) + ''' Things to try later: - different public key algorithms @@ -570,6 +638,7 @@ def rnp_signing(): for size in sizes: run_test(rnp_signing_rnp_to_gpg, size) run_test(rnp_detached_signing_rnp_to_gpg, size) + run_test(rnp_cleartext_signing_rnp_to_gpg, size) # Generate additional keypair in RNP rnp_genkey_rsa('signing@gpg') # Import secret keyring to the GPG @@ -577,6 +646,7 @@ def rnp_signing(): for size in sizes: run_test(rnp_signing_gpg_to_rnp, size) run_test(rnp_detached_signing_gpg_to_rnp, size) + run_test(rnp_cleartext_signing_gpg_to_rnp, size) def run_rnp_tests(): # 1. Encryption / decryption against GPG From 4f388486bd8169e2ca71855a3113d07c5dd0e63e Mon Sep 17 00:00:00 2001 From: Nickolay Olshevsky Date: Thu, 24 Aug 2017 16:29:31 +0300 Subject: [PATCH 5/5] Added defines for CR/LF/DASH --- src/lib/writer.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/lib/writer.c b/src/lib/writer.c index 286557101d..80c9a1b068 100644 --- a/src/lib/writer.c +++ b/src/lib/writer.c @@ -417,6 +417,15 @@ typedef struct { pgp_memory_t * trailing; } dashesc_t; +#define CH_CR ('\r') +#define CH_LF ('\n') +#define CH_DASH ('-') +#define CH_SPACE (' ') +#define CH_TAB ('\t') +#define STR_CR ("\r") +#define STR_LF ("\n") +#define STR_DASHESC ("- ") + static bool dash_esc_writer(const uint8_t *src, unsigned len, pgp_error_t **errors, pgp_writer_t *writer) { @@ -431,12 +440,12 @@ dash_esc_writer(const uint8_t *src, unsigned len, pgp_error_t **errors, pgp_writ for (i = 0; i < len; i++) { fprintf(stderr, "0x%02x ", src[i]); if (((i + 1) % 16) == 0) { - (void) fprintf(stderr, "\n"); + (void) fprintf(stderr, STR_LF); } else if (((i + 1) % 8) == 0) { (void) fprintf(stderr, " "); } } - (void) fprintf(stderr, "\n"); + (void) fprintf(stderr, STR_LF); } /* XXX: make this efficient */ for (n = 0; n < len; ++n) { @@ -444,27 +453,27 @@ dash_esc_writer(const uint8_t *src, unsigned len, pgp_error_t **errors, pgp_writ escape = false; if (dash->seen_nl || dash->seen_cr) { - if (src[n] == '-') { + if (src[n] == CH_DASH) { escape = true; } } - dash->seen_nl = src[n] == '\n'; + dash->seen_nl = src[n] == CH_LF; if (dash->seen_nl && !dash->seen_cr) { - if (!stacked_write(writer, "\r", 1, errors)) { + if (!stacked_write(writer, STR_CR, 1, errors)) { return false; } - pgp_sig_add_data(dash->sig, "\r", 1); + pgp_sig_add_data(dash->sig, STR_CR, 1); } if (dash->seen_cr && !dash->seen_nl) { - if (!stacked_write(writer, "\n", 1, errors)) { + if (!stacked_write(writer, STR_LF, 1, errors)) { return false; } - pgp_sig_add_data(dash->sig, "\n", 1); + pgp_sig_add_data(dash->sig, STR_LF, 1); } - dash->seen_cr = src[n] == '\r'; + dash->seen_cr = src[n] == CH_CR; - if (escape && !stacked_write(writer, "- ", 2, errors)) { + if (escape && !stacked_write(writer, STR_DASHESC, 2, errors)) { return false; } @@ -473,7 +482,7 @@ dash_esc_writer(const uint8_t *src, unsigned len, pgp_error_t **errors, pgp_writ } /* trailing whitespace isn't included in the signature */ - if (src[n] == ' ' || src[n] == '\t') { + if (src[n] == CH_SPACE || src[n] == CH_TAB) { if (!pgp_memory_add(dash->trailing, &src[n], 1)) { return false; }