Cipherpack v1.2.0-dirty
A Cryprographic Stream Processor
Cipherpack.java
Go to the documentation of this file.
1/**
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2022 Gothel Software e.K.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24package org.cipherpack;
25
26import java.io.BufferedWriter;
27import java.io.File;
28import java.io.FileWriter;
29import java.nio.ByteBuffer;
30import java.util.List;
31
32import org.jau.io.ByteInStream;
33import org.jau.io.ByteInStream_Feed;
34import org.jau.io.PrintUtil;
35import org.jau.util.BasicTypes;
36
37/**
38 * @anchor cipherpack_overview
39 * ### Cipherpack Overview
40 * *Cipherpack*, a secure stream processor utilizing public-key signatures to
41 * authenticate the sender and public-key encryption of a symmetric-key for multiple receiver
42 * ensuring their privacy and high-performance message encryption.
43 *
44 * *Cipherpack* securely streams messages through any media,
45 * via file using [ByteInStream_File](https://jausoft.com/projects/jaulib/build/documentation/java/html/classorg_1_1jau_1_1io_1_1ByteInStream__File.html)
46 * and via all [*libcurl* network protocols](https://curl.se/docs/url-syntax.html)
47 * using [ByteInStream_URL](https://jausoft.com/projects/jaulib/build/documentation/java/html/classorg_1_1jau_1_1io_1_1ByteInStream__URL.html)
48 * are *build-in* and supported. <br/>
49 * Note: *libcurl* must be enabled via `-DUSE_LIBCURL=ON` at build.
50 *
51 * A user may use the media agnostic
52 * [ByteInStream_Feed](https://jausoft.com/projects/jaulib/build/documentation/java/html/classorg_1_1jau_1_1io_1_1ByteInStream__Feed.html)
53 * to produce the input stream by injecting data off-thread and a CipherpackListener to receive the processed output stream.
54 *
55 * *Cipherpack* is implemented using C++17 and accessible via C++ and Java.
56 *
57 * ### Cipherpack Implementation
58 * #### Implementation Status
59 * READY TO USE
60 *
61 * #### Cipherpack Operations
62 * The following public-key signature and encryption, as well as symmetric-key message encryption operations are performed:
63 * - Writing a DER Header-1, containing the general message information and receiver count, see {@link PackHeader} details.
64 * - Writing a DER Header for each recevr, containing the fingerprint, encrypted symmetric-key and encrypted symmetric-nonce.
65 * - Writing a DER Header-2, containing the sender's signature over the whole header
66 * - Writing the symmetrically encrypted message, using the symmetric-key for encryption + MAC via AEAD `ChaCha20Poly1305`.
67 *
68 * Implementation performs all operation `in-place` without redundant copies, processing the stream.
69 *
70 * @anchor cipherpack_stream
71 * #### Cipherpack Data Stream
72 * The stream's header contains the sender's public-key fingerprint
73 * and its signature for authentication by the receiving parties.
74 *
75 * Further, the stream contains triples per receiver, its public-key fingerprint,
76 * the encrypted symmetric-key and the encrypted symmetric-nonce for each receiver,
77 * allowing a secure messaging between multiple parties:
78 * - Symmetric encryption of the plaintext message ensures high-performance processing.
79 * - Symmetric stream-key is unique for each message
80 *
81 * Implementation uses an Authenticated Encryption with Additional Data (AEAD) encryption+MAC cipher algo,
82 * i.e. {@link cipherpack::constants::aead_cipher_algo}.
83 *
84 * The random nonce, unique for one message and used for the symmetric encryption is not a secret and doesn't have to be confidential.
85 * However, since we already encrypt the symmetric-key for each receiver, we transmit the nonce with it, encrypted.
86 *
87 * The cipherpack stream will be produced as follows:
88 * ```
89 * DER Header 1 {
90 * ASN1_Type::OctetString stream_magic // simple stream identifier to be matched
91 * ASN1_Type::OctetString target_path // optional target path for the plaintext message, user application specific.
92 * ASN1_Type::Integer plaintext_size // size in bytes of plaintext message, zero if not determined at start of streaming
93 * ASN1_Type::Integer creation_timestamp_sec // message creation timestamp, second component
94 * ASN1_Type::Integer creation_timestamp_nsec // message creation timestamp, nanoseconds component
95 * ASN1_Type::OctetString subject // optional subject of message, user application specific.
96 * ASN1_Type::OctetString plaintext_version // version of this plaintext message, user application specific.
97 * ASN1_Type::OctetString plaintext_version_parent // version of this plaintext message's preceding message, user application specific.
98 * ASN1_Type::OctetString pk_type // public-key type. Default `RSA`.
99 * ASN1_Type::OctetString pk_fingerprt_hash_algo // public-key fingerprint hash. Default `SHA-256`.
100 * ASN1_Type::OctetString pk_enc_padding_algo // public-key encryption padding. Default `OAEP`.
101 * ASN1_Type::OctetString pk_enc_hash_algo // public-key encryption hash. Default `SHA-256`.
102 * ASN1_Type::OctetString pk_sign_algo // public-key signature algorithm. Default `EMSA1(SHA-256)`.
103 * ASN1_Type::ObjectId sym_enc_mac_oid // symmetric-key encryption+MAC algorithm. Default `ChaCha20Poly1305`.
104 * ASN1_Type::OctetString fingerprt_sender // fingerprint of public sender key used for header signature
105 * ASN1_Type::Integer receiver_count, // number of receiver triples { fingerprint, encrypted-symmetric-keys, encrypted-nonce }
106 * }
107 * DER Header recevr_1 {
108 * ASN1_Type::OctetString fingerprt_recevr_1, // fingerprint of receiver's public-key_1 used for encrypted_skey_recevr_1
109 * ASN1_Type::OctetString encrypted_skey_recevr_1, // encrypted symmetric-key with receiver's public-key_1
110 * ASN1_Type::OctetString encrypted_nonce_recevr_1, // encrypted symmetric-encryption nonce with receiver's public-key_1
111 * },
112 * DER Header recevr_2 {
113 * ASN1_Type::OctetString fingerprt_recevr_2, // fingerprint of receiver's public-key_1 used for encrypted_skey_recevr_2
114 * ASN1_Type::OctetString encrypted_skey_recevr_2, // encrypted symmetric-key with receiver's public-key_2
115 * ASN1_Type::OctetString encrypted_nonce_recevr_2, // encrypted symmetric-encryption nonce with receiver's public-key_2
116 * } ...
117 * DER Header 2 {
118 * ASN1_Type::OctetString sign_sender // sender's signature over whole header, matching fingerprt_sender
119 * },
120 * uint8_t encrypted_data[ciphertext_size] // the encrypted message, `ciphertext_size` bytes resulting to `plaintext_size` plaintext message
121 * ```
122 *
123 * @see encryptThenSign()
124 * @see checkSignThenDecrypt()
125 */
126public final class Cipherpack {
127
128 /** Intermediate copy buffer size of {@code 16384 bytes}, usually the 4 x 4096 bytes page-size. */
129 public static final int buffer_size = 16384;
130
131 /**
132 * Name of default hash algo for the plaintext message,
133 * e.g. for {@link #encryptThenSign(CryptoConfig, List, String, ByteBuffer, ByteInStream, String, String, String, String, CipherpackListener, String, String) encryptThenSign()}
134 * and {@link #checkSignThenDecrypt(List, String, ByteBuffer, ByteInStream, CipherpackListener, String, String) checkSignThenDecrypt()}.
135 *
136 * Value is `BLAKE2b(512)`.
137 *
138 * Note:
139 * - SHA-256 performs 64 rounds over 512 bits (blocks size) at a time.
140 * - Often better optimized and hardware implemented.
141 * - SHA-512 performs 80 rounds over 1024 bits (blocks size) at a time.
142 * - Requires double storage size than SHA-256, i.e. 512/256 bits.
143 * - 25% more rounds, i.e. calculations than SHA-256
144 * - Operating on 64-bit words instead of SHA-256's 32-bit words
145 * - Theoretically shall outperform SHA-256 by 2 / 1.25 = 1.6 on 64-bit architectures,
146 * however, SHA-256 is often better optimized and hardware implemented.
147 * - BLAKE2b(512) usually beats both, SHA-256 and SHA-512 on 64-bit machines.
148 * - It even matches their performance if using hardware accelerated implementations.
149 */
150 public static final String default_hash_algo() { return "BLAKE2b(512)"; }
151
152 /**
153 * Encrypt then sign the source producing a cipherpack stream passed to the CipherpackListener if opt-in and also optionally store into destination_fname.
154 *
155 * @param crypto_cfg Used CryptoConfig, consider using CryptoConfig::getDefault()
156 * @param enc_pub_keys Public keys of the receiver, used to encrypt the symmetric-key for multiple parties.
157 * @param sign_sec_key_fname Private key of the sender, used to sign the DER-Header-1 incl encrypted symmetric-key for authenticity.
158 * @param passphrase Passphrase for `sign_sec_key_fname`, may be null or empty for no passphrase.
159 * @param source The source ByteInStream of the plaintext message.
160 * @param target_path Optional target path for the message, user application specific.
161 * @param subject Optional subject of message from sender, user application specific.
162 * @param plaintext_version Version of this plaintext message, user application specific.
163 * @param plaintext_version_parent Version of this plaintext message's preceding message, user application specific.
164 * @param listener CipherpackListener listener used for notifications and optionally
165 * to send the ciphertext destination bytes via CipherpackListener::contentProcessed()
166 * @param plaintext_hash_algo Optional hash algorithm for the plaintext message, produced for convenience and not wired. See {@link Cipherpack#default_hash_algo()}.
167 * Pass an empty string to disable.
168 * @param destination_fname Optional filename of the plaintext destination file, not used if null or empty (default). If not empty and file already exists, file will be overwritten.
169 * @return PackHeader, where true == PackHeader::isValid() if successful, otherwise not.
170 *
171 * @see @ref cipherpack_overview "Cipherpack Overview"
172 * @see @ref cipherpack_stream "Cipherpack Data Stream"
173 * @see checkSignThenDecrypt()
174 * @see CipherpackListener
175 * @see ByteInStream_Feed
176 * @see ByteInStream_URL
177 * @see ByteInStream
178 */
179 public static PackHeader encryptThenSign(final CryptoConfig crypto_cfg,
180 final List<String> enc_pub_keys,
181 final String sign_sec_key_fname, final ByteBuffer passphrase,
182 final ByteInStream source,
183 final String target_path, final String subject,
184 final String plaintext_version,
185 final String plaintext_version_parent,
186 final CipherpackListener listener,
187 final String plaintext_hash_algo,
188 final String destination_fname) {
189 return encryptThenSignImpl1(crypto_cfg,
190 enc_pub_keys,
191 sign_sec_key_fname, passphrase,
192 source,
193 target_path, subject,
194 plaintext_version,
195 plaintext_version_parent,
196 listener,
197 plaintext_hash_algo,
198 destination_fname);
199 }
200 private static native PackHeader encryptThenSignImpl1(final CryptoConfig crypto_cfg,
201 final List<String> enc_pub_keys,
202 final String sign_sec_key_fname,
203 final ByteBuffer passphrase,
204 final ByteInStream source,
205 final String target_path, final String subject,
206 final String plaintext_version,
207 final String plaintext_version_parent,
208 final CipherpackListener listener,
209 final String plaintext_hash_algo,
210 final String destination_fname);
211
212
213 /**
214 * Verify signature then decrypt the source passing to the CipherpackListener if opt-in and also optionally store into destination file.
215 *
216 * @param sign_pub_keys Authorized sender public-keys to verify the sender's signature
217 * and hence the authenticity of the message incl. encrypted symmetric-key and ciphertext message.
218 * @param dec_sec_key_fname Private key of the receiver, used to decrypt the symmetric-key.
219 * It shall match one of the keys used to encrypt.
220 * @param passphrase The passphrase for `dec_sec_key_fname`, may be null or empty for no passphrase.
221 * @param source The source ByteInStream of the cipherpack containing the encrypted message.
222 * @param listener The CipherpackListener listener used for notifications and optionally
223 * to send the plaintext destination bytes via CipherpackListener::contentProcessed()
224 * @param plaintext_hash_algo Optional hash algorithm for the plaintext message, produced for convenience and not wired. See {@link Cipherpack#default_hash_algo()}.
225 * Pass an empty string to disable.
226 * @param destination_fname Optional filename of the plaintext destination file, not used if empty (default). If not empty and file already exists, file will be overwritten.
227 * @return PackHeader, where true == PackHeader::isValid() if successful, otherwise not.
228 *
229 * @see @ref cipherpack_overview "Cipherpack Overview"
230 * @see @ref cipherpack_stream "Cipherpack Data Stream"
231 * @see encryptThenSign()
232 * @see CipherpackListener
233 * @see ByteInStream_Feed
234 * @see ByteInStream_URL
235 * @see ByteInStream
236 */
237 public static PackHeader checkSignThenDecrypt(final List<String> sign_pub_keys,
238 final String dec_sec_key_fname, final ByteBuffer passphrase,
239 final ByteInStream source,
240 final CipherpackListener listener,
241 final String plaintext_hash_algo,
242 final String destination_fname) {
243 return checkSignThenDecrypt1(sign_pub_keys,
244 dec_sec_key_fname, passphrase,
245 source,
246 listener,
247 plaintext_hash_algo,
248 destination_fname);
249 }
250 private static native PackHeader checkSignThenDecrypt1(final List<String> sign_pub_keys,
251 final String dec_sec_key_fname, final ByteBuffer passphrase,
252 final ByteInStream source,
253 final CipherpackListener listener,
254 final String plaintext_hash_algo,
255 final String destination_fname);
256
257 /**
258 * Hash utility functions to produce a hash file compatible to `sha256sum`
259 * as well as to produce the hash value itself for validation.
260 */
261 public static final class HashUtil {
262 /** Return a lower-case file suffix used to store a `sha256sum` compatible hash signature w/o dot and w/o dashes. */
263 public static String fileSuffix(final String algo) {
264 return algo.toLowerCase().replace("-", "");
265 }
266
267 /**
268 * Append the hash signature to the text file out_file
269 *
270 * The hash signature is composed as follows
271 * - hash algo name
272 * - space
273 * - hash value
274 * - space
275 * - `*` to denote binary processing
276 * - hashed file name
277 *
278 * The hash signature is similar to `sha256sum` output, but the added hash algo name upfront.
279 *
280 * @param outFileName the text file to append hash signature of hashed_file.
281 * @param hashedFile the file of the hash signature
282 * @param hashAlgo the hash algo name used
283 * @param hashValue the hash value of hashed_file
284 * @return true if successful, otherwise false
285 */
286 public static boolean appendToFile(final String outFileName, final String hashedFile, final String hashAlgo, final byte[] hashValue) {
287 final String hash_str = BasicTypes.bytesHexString(hashValue, 0, hashValue.length, true /* lsbFirst */);
288 final String space = new String(" ");
289 final String seperator = new String(" *");
290 final File file = new File( outFileName );
291
292 try( BufferedWriter out = new BufferedWriter( new FileWriter(file, true) ); ) {
293 out.write(hashAlgo);
294 out.write(space);
295 out.write(hash_str);
296 out.write(seperator);
297 out.write(hashedFile);
298 out.newLine();
299 return true;
300 } catch (final Exception ex) {
301 PrintUtil.println(System.err, "Write hash to file failed: "+outFileName+": "+ex.getMessage());
302 ex.printStackTrace();
303 }
304 return false;
305 }
306
307 /**
308 * Return the calculated hash value using given algo name and byte input stream.
309 * @param algo the hash algo name
310 * @param source the byte input stream
311 * @return the calculated hash value or null in case of error
312 */
313 public static byte[] calc(final String algo, final ByteInStream source) {
314 return calcImpl1(algo, source);
315 }
316 private static native byte[] calcImpl1(final String algo, final ByteInStream source);
317
318 /**
319 * Return the calculated hash value using given algo name and the bytes of a single file or all files if denoting a directory.
320 * @param algo the hash algo name
321 * @param path_or_uri given path or uri, either a URI denoting a single file, a single file path or directory path for which all files (not symbolic links) are considered
322 * @param bytes_hashed returns overall bytes hashed
323 * @param timeoutMS in case `path_or_uri` refers to an URI, timeout is being used as maximum duration in milliseconds to wait for next bytes. Defaults to 20_s.
324 * @return the calculated hash value or nullptr in case of error
325 * @see #calc(String, String, long[])
326 */
327 public static byte[] calc(final String algo, final String path_or_uri, final long bytes_hashed[/*1*/], final long timeoutMS) {
328 return calcImpl2(algo, path_or_uri, bytes_hashed, timeoutMS);
329 }
330 /**
331 * Return the calculated hash value using given algo name and the bytes of a single file or all files if denoting a directory.
332 *
333 * This variant uses a 20_s timeout to wait for next bytes.
334 *
335 * @param algo the hash algo name
336 * @param path_or_uri given path or uri, either a URI denoting a single file, a single file path or directory path for which all files (not symbolic links) are considered
337 * @param bytes_hashed returns overall bytes hashed
338 * @return the calculated hash value or nullptr in case of error
339 * @see #calc(String, String, long[], long)
340 */
341 public static byte[] calc(final String algo, final String path_or_uri, final long bytes_hashed[/*1*/]) {
342 return calcImpl2(algo, path_or_uri, bytes_hashed, 20000);
343 }
344 private static native byte[] calcImpl2(final String algo, final String path_or_uri, final long bytes_hashed[/*1*/], final long timeout);
345 }
346}
Listener for events occurring while processing a cipherpack message via encryptThenSign() and checkSi...
Hash utility functions to produce a hash file compatible to sha256sum as well as to produce the hash ...
static boolean appendToFile(final String outFileName, final String hashedFile, final String hashAlgo, final byte[] hashValue)
Append the hash signature to the text file out_file.
static byte[] calc(final String algo, final String path_or_uri, final long bytes_hashed[])
Return the calculated hash value using given algo name and the bytes of a single file or all files if...
static byte[] calc(final String algo, final ByteInStream source)
Return the calculated hash value using given algo name and byte input stream.
static byte[] calc(final String algo, final String path_or_uri, final long bytes_hashed[], final long timeoutMS)
Return the calculated hash value using given algo name and the bytes of a single file or all files if...
static String fileSuffix(final String algo)
Return a lower-case file suffix used to store a sha256sum compatible hash signature w/o dot and w/o d...
static PackHeader encryptThenSign(final CryptoConfig crypto_cfg, final List< String > enc_pub_keys, final String sign_sec_key_fname, final ByteBuffer passphrase, final ByteInStream source, final String target_path, final String subject, final String plaintext_version, final String plaintext_version_parent, final CipherpackListener listener, final String plaintext_hash_algo, final String destination_fname)
Encrypt then sign the source producing a cipherpack stream passed to the CipherpackListener if opt-in...
static PackHeader checkSignThenDecrypt(final List< String > sign_pub_keys, final String dec_sec_key_fname, final ByteBuffer passphrase, final ByteInStream source, final CipherpackListener listener, final String plaintext_hash_algo, final String destination_fname)
Verify signature then decrypt the source passing to the CipherpackListener if opt-in and also optiona...
static final int buffer_size
Intermediate copy buffer size of 16384 bytes, usually the 4 x 4096 bytes page-size.
static final String default_hash_algo()
Name of default hash algo for the plaintext message, e.g.
CryptoConfig, contains crypto algorithms settings given at encryption wired via the Cipherpack Data S...
Cipherpack header less encrypted keys or signatures as described in Cipherpack Data Stream.
Definition: PackHeader.java:40