mirror of
https://github.com/pgpainless/pgpainless.git
synced 2025-09-14 12:49:39 +02:00
Compare commits
No commits in common. "main" and "0.2.11" have entirely different histories.
772 changed files with 29572 additions and 51702 deletions
591
.editorconfig
591
.editorconfig
|
@ -1,51 +1,509 @@
|
|||
# This .editorconfig section approximates ktfmt's formatting rules. You can include it in an
|
||||
# existing .editorconfig file or use it standalone by copying it to <project root>/.editorconfig
|
||||
# and making sure your editor is set to read settings from .editorconfig files.
|
||||
#
|
||||
# It includes editor-specific config options for IntelliJ IDEA.
|
||||
#
|
||||
# If any option is wrong, PR are welcome
|
||||
|
||||
[{*.kt,*.kts}]
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
max_line_length = 100
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 4
|
||||
ij_continuation_indent_size = 4 # was 8
|
||||
ij_java_names_count_to_use_import_on_demand = 9999
|
||||
indent_style = space
|
||||
insert_final_newline = false
|
||||
max_line_length = 120
|
||||
tab_width = 4
|
||||
ij_continuation_indent_size = 8
|
||||
ij_formatter_off_tag = @formatter:off
|
||||
ij_formatter_on_tag = @formatter:on
|
||||
ij_formatter_tags_enabled = false
|
||||
ij_smart_tabs = false
|
||||
ij_visual_guides = none
|
||||
ij_wrap_on_typing = false
|
||||
|
||||
[*.java]
|
||||
ij_java_align_consecutive_assignments = false
|
||||
ij_java_align_consecutive_variable_declarations = false
|
||||
ij_java_align_group_field_declarations = false
|
||||
ij_java_align_multiline_annotation_parameters = false
|
||||
ij_java_align_multiline_array_initializer_expression = false
|
||||
ij_java_align_multiline_assignment = false
|
||||
ij_java_align_multiline_binary_operation = false
|
||||
ij_java_align_multiline_chained_methods = false
|
||||
ij_java_align_multiline_extends_list = false
|
||||
ij_java_align_multiline_for = true
|
||||
ij_java_align_multiline_method_parentheses = false
|
||||
ij_java_align_multiline_parameters = true
|
||||
ij_java_align_multiline_parameters_in_calls = false
|
||||
ij_java_align_multiline_parenthesized_expression = false
|
||||
ij_java_align_multiline_records = true
|
||||
ij_java_align_multiline_resources = true
|
||||
ij_java_align_multiline_ternary_operation = false
|
||||
ij_java_align_multiline_text_blocks = false
|
||||
ij_java_align_multiline_throws_list = false
|
||||
ij_java_align_subsequent_simple_methods = false
|
||||
ij_java_align_throws_keyword = false
|
||||
ij_java_annotation_parameter_wrap = off
|
||||
ij_java_array_initializer_new_line_after_left_brace = false
|
||||
ij_java_array_initializer_right_brace_on_new_line = false
|
||||
ij_java_array_initializer_wrap = off
|
||||
ij_java_assert_statement_colon_on_next_line = false
|
||||
ij_java_assert_statement_wrap = off
|
||||
ij_java_assignment_wrap = off
|
||||
ij_java_binary_operation_sign_on_next_line = false
|
||||
ij_java_binary_operation_wrap = off
|
||||
ij_java_blank_lines_after_anonymous_class_header = 0
|
||||
ij_java_blank_lines_after_class_header = 0
|
||||
ij_java_blank_lines_after_imports = 1
|
||||
ij_java_blank_lines_after_package = 1
|
||||
ij_java_blank_lines_around_class = 1
|
||||
ij_java_blank_lines_around_field = 0
|
||||
ij_java_blank_lines_around_field_in_interface = 0
|
||||
ij_java_blank_lines_around_initializer = 1
|
||||
ij_java_blank_lines_around_method = 1
|
||||
ij_java_blank_lines_around_method_in_interface = 1
|
||||
ij_java_blank_lines_before_class_end = 0
|
||||
ij_java_blank_lines_before_imports = 1
|
||||
ij_java_blank_lines_before_method_body = 0
|
||||
ij_java_blank_lines_before_package = 0
|
||||
ij_java_block_brace_style = end_of_line
|
||||
ij_java_block_comment_at_first_column = true
|
||||
ij_java_builder_methods = none
|
||||
ij_java_call_parameters_new_line_after_left_paren = false
|
||||
ij_java_call_parameters_right_paren_on_new_line = false
|
||||
ij_java_call_parameters_wrap = off
|
||||
ij_java_case_statement_on_separate_line = true
|
||||
ij_java_catch_on_new_line = false
|
||||
ij_java_class_annotation_wrap = split_into_lines
|
||||
ij_java_class_brace_style = end_of_line
|
||||
ij_java_class_count_to_use_import_on_demand = 10000
|
||||
ij_java_class_names_in_javadoc = 1
|
||||
ij_java_do_not_indent_top_level_class_members = false
|
||||
ij_java_do_not_wrap_after_single_annotation = false
|
||||
ij_java_do_while_brace_force = never
|
||||
ij_java_doc_add_blank_line_after_description = true
|
||||
ij_java_doc_add_blank_line_after_param_comments = false
|
||||
ij_java_doc_add_blank_line_after_return = false
|
||||
ij_java_doc_add_p_tag_on_empty_lines = true
|
||||
ij_java_doc_align_exception_comments = true
|
||||
ij_java_doc_align_param_comments = true
|
||||
ij_java_doc_do_not_wrap_if_one_line = false
|
||||
ij_java_doc_enable_formatting = true
|
||||
ij_java_doc_enable_leading_asterisks = true
|
||||
ij_java_doc_indent_on_continuation = false
|
||||
ij_java_doc_keep_empty_lines = true
|
||||
ij_java_doc_keep_empty_parameter_tag = true
|
||||
ij_java_doc_keep_empty_return_tag = true
|
||||
ij_java_doc_keep_empty_throws_tag = true
|
||||
ij_java_doc_keep_invalid_tags = true
|
||||
ij_java_doc_param_description_on_new_line = false
|
||||
ij_java_doc_preserve_line_breaks = false
|
||||
ij_java_doc_use_throws_not_exception_tag = true
|
||||
ij_java_else_on_new_line = false
|
||||
ij_java_enum_constants_wrap = off
|
||||
ij_java_extends_keyword_wrap = off
|
||||
ij_java_extends_list_wrap = off
|
||||
ij_java_field_annotation_wrap = split_into_lines
|
||||
ij_java_finally_on_new_line = false
|
||||
ij_java_for_brace_force = never
|
||||
ij_java_for_statement_new_line_after_left_paren = false
|
||||
ij_java_for_statement_right_paren_on_new_line = false
|
||||
ij_java_for_statement_wrap = off
|
||||
ij_java_generate_final_locals = false
|
||||
ij_java_generate_final_parameters = false
|
||||
ij_java_if_brace_force = never
|
||||
ij_java_imports_layout = $*,|,java.**,javax.**,|,*
|
||||
ij_java_indent_case_from_switch = true
|
||||
ij_java_insert_inner_class_imports = false
|
||||
ij_java_insert_override_annotation = true
|
||||
ij_java_keep_blank_lines_before_right_brace = 2
|
||||
ij_java_keep_blank_lines_between_package_declaration_and_header = 2
|
||||
ij_java_keep_blank_lines_in_code = 2
|
||||
ij_java_keep_blank_lines_in_declarations = 2
|
||||
ij_java_keep_builder_methods_indents = false
|
||||
ij_java_keep_control_statement_in_one_line = true
|
||||
ij_java_keep_first_column_comment = true
|
||||
ij_java_keep_indents_on_empty_lines = false
|
||||
ij_java_keep_line_breaks = true
|
||||
ij_java_keep_multiple_expressions_in_one_line = false
|
||||
ij_java_keep_simple_blocks_in_one_line = false
|
||||
ij_java_keep_simple_classes_in_one_line = false
|
||||
ij_java_keep_simple_lambdas_in_one_line = false
|
||||
ij_java_keep_simple_methods_in_one_line = false
|
||||
ij_java_label_indent_absolute = false
|
||||
ij_java_label_indent_size = 0
|
||||
ij_java_lambda_brace_style = end_of_line
|
||||
ij_java_layout_static_imports_separately = true
|
||||
ij_java_line_comment_add_space = false
|
||||
ij_java_line_comment_at_first_column = true
|
||||
ij_java_method_annotation_wrap = split_into_lines
|
||||
ij_java_method_brace_style = end_of_line
|
||||
ij_java_method_call_chain_wrap = off
|
||||
ij_java_method_parameters_new_line_after_left_paren = false
|
||||
ij_java_method_parameters_right_paren_on_new_line = false
|
||||
ij_java_method_parameters_wrap = off
|
||||
ij_java_modifier_list_wrap = false
|
||||
ij_java_names_count_to_use_import_on_demand = 1000
|
||||
ij_java_new_line_after_lparen_in_record_header = false
|
||||
ij_java_packages_to_use_import_on_demand = java.awt.*,javax.swing.*
|
||||
ij_java_parameter_annotation_wrap = off
|
||||
ij_java_parentheses_expression_new_line_after_left_paren = false
|
||||
ij_java_parentheses_expression_right_paren_on_new_line = false
|
||||
ij_java_place_assignment_sign_on_next_line = false
|
||||
ij_java_prefer_longer_names = true
|
||||
ij_java_prefer_parameters_wrap = false
|
||||
ij_java_record_components_wrap = normal
|
||||
ij_java_repeat_synchronized = true
|
||||
ij_java_replace_instanceof_and_cast = false
|
||||
ij_java_replace_null_check = true
|
||||
ij_java_replace_sum_lambda_with_method_ref = true
|
||||
ij_java_resource_list_new_line_after_left_paren = false
|
||||
ij_java_resource_list_right_paren_on_new_line = false
|
||||
ij_java_resource_list_wrap = off
|
||||
ij_java_rparen_on_new_line_in_record_header = false
|
||||
ij_java_space_after_closing_angle_bracket_in_type_argument = false
|
||||
ij_java_space_after_colon = true
|
||||
ij_java_space_after_comma = true
|
||||
ij_java_space_after_comma_in_type_arguments = true
|
||||
ij_java_space_after_for_semicolon = true
|
||||
ij_java_space_after_quest = true
|
||||
ij_java_space_after_type_cast = true
|
||||
ij_java_space_before_annotation_array_initializer_left_brace = false
|
||||
ij_java_space_before_annotation_parameter_list = false
|
||||
ij_java_space_before_array_initializer_left_brace = false
|
||||
ij_java_space_before_catch_keyword = true
|
||||
ij_java_space_before_catch_left_brace = true
|
||||
ij_java_space_before_catch_parentheses = true
|
||||
ij_java_space_before_class_left_brace = true
|
||||
ij_java_space_before_colon = true
|
||||
ij_java_space_before_colon_in_foreach = true
|
||||
ij_java_space_before_comma = false
|
||||
ij_java_space_before_do_left_brace = true
|
||||
ij_java_space_before_else_keyword = true
|
||||
ij_java_space_before_else_left_brace = true
|
||||
ij_java_space_before_finally_keyword = true
|
||||
ij_java_space_before_finally_left_brace = true
|
||||
ij_java_space_before_for_left_brace = true
|
||||
ij_java_space_before_for_parentheses = true
|
||||
ij_java_space_before_for_semicolon = false
|
||||
ij_java_space_before_if_left_brace = true
|
||||
ij_java_space_before_if_parentheses = true
|
||||
ij_java_space_before_method_call_parentheses = false
|
||||
ij_java_space_before_method_left_brace = true
|
||||
ij_java_space_before_method_parentheses = false
|
||||
ij_java_space_before_opening_angle_bracket_in_type_parameter = false
|
||||
ij_java_space_before_quest = true
|
||||
ij_java_space_before_switch_left_brace = true
|
||||
ij_java_space_before_switch_parentheses = true
|
||||
ij_java_space_before_synchronized_left_brace = true
|
||||
ij_java_space_before_synchronized_parentheses = true
|
||||
ij_java_space_before_try_left_brace = true
|
||||
ij_java_space_before_try_parentheses = true
|
||||
ij_java_space_before_type_parameter_list = false
|
||||
ij_java_space_before_while_keyword = true
|
||||
ij_java_space_before_while_left_brace = true
|
||||
ij_java_space_before_while_parentheses = true
|
||||
ij_java_space_inside_one_line_enum_braces = false
|
||||
ij_java_space_within_empty_array_initializer_braces = false
|
||||
ij_java_space_within_empty_method_call_parentheses = false
|
||||
ij_java_space_within_empty_method_parentheses = false
|
||||
ij_java_spaces_around_additive_operators = true
|
||||
ij_java_spaces_around_assignment_operators = true
|
||||
ij_java_spaces_around_bitwise_operators = true
|
||||
ij_java_spaces_around_equality_operators = true
|
||||
ij_java_spaces_around_lambda_arrow = true
|
||||
ij_java_spaces_around_logical_operators = true
|
||||
ij_java_spaces_around_method_ref_dbl_colon = false
|
||||
ij_java_spaces_around_multiplicative_operators = true
|
||||
ij_java_spaces_around_relational_operators = true
|
||||
ij_java_spaces_around_shift_operators = true
|
||||
ij_java_spaces_around_type_bounds_in_type_parameters = true
|
||||
ij_java_spaces_around_unary_operator = false
|
||||
ij_java_spaces_within_angle_brackets = false
|
||||
ij_java_spaces_within_annotation_parentheses = false
|
||||
ij_java_spaces_within_array_initializer_braces = false
|
||||
ij_java_spaces_within_braces = false
|
||||
ij_java_spaces_within_brackets = false
|
||||
ij_java_spaces_within_cast_parentheses = false
|
||||
ij_java_spaces_within_catch_parentheses = false
|
||||
ij_java_spaces_within_for_parentheses = false
|
||||
ij_java_spaces_within_if_parentheses = false
|
||||
ij_java_spaces_within_method_call_parentheses = false
|
||||
ij_java_spaces_within_method_parentheses = false
|
||||
ij_java_spaces_within_parentheses = false
|
||||
ij_java_spaces_within_record_header = false
|
||||
ij_java_spaces_within_switch_parentheses = false
|
||||
ij_java_spaces_within_synchronized_parentheses = false
|
||||
ij_java_spaces_within_try_parentheses = false
|
||||
ij_java_spaces_within_while_parentheses = false
|
||||
ij_java_special_else_if_treatment = true
|
||||
ij_java_subclass_name_suffix = Impl
|
||||
ij_java_ternary_operation_signs_on_next_line = false
|
||||
ij_java_ternary_operation_wrap = off
|
||||
ij_java_test_name_suffix = Test
|
||||
ij_java_throws_keyword_wrap = off
|
||||
ij_java_throws_list_wrap = off
|
||||
ij_java_use_external_annotations = false
|
||||
ij_java_use_fq_class_names = false
|
||||
ij_java_use_relative_indents = false
|
||||
ij_java_use_single_class_imports = true
|
||||
ij_java_variable_annotation_wrap = off
|
||||
ij_java_visibility = public
|
||||
ij_java_while_brace_force = never
|
||||
ij_java_while_on_new_line = false
|
||||
ij_java_wrap_comments = false
|
||||
ij_java_wrap_first_method_in_call_chain = false
|
||||
ij_java_wrap_long_lines = false
|
||||
|
||||
[*.properties]
|
||||
ij_properties_align_group_field_declarations = false
|
||||
ij_properties_keep_blank_lines = false
|
||||
ij_properties_key_value_delimiter = equals
|
||||
ij_properties_spaces_around_key_value_delimiter = false
|
||||
|
||||
[.editorconfig]
|
||||
ij_editorconfig_align_group_field_declarations = false
|
||||
ij_editorconfig_space_after_colon = false
|
||||
ij_editorconfig_space_after_comma = true
|
||||
ij_editorconfig_space_before_colon = false
|
||||
ij_editorconfig_space_before_comma = false
|
||||
ij_editorconfig_spaces_around_assignment_operators = true
|
||||
|
||||
[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.jspx,*.pom,*.rng,*.tagx,*.tld,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul}]
|
||||
ij_xml_align_attributes = true
|
||||
ij_xml_align_text = false
|
||||
ij_xml_attribute_wrap = normal
|
||||
ij_xml_block_comment_at_first_column = true
|
||||
ij_xml_keep_blank_lines = 2
|
||||
ij_xml_keep_indents_on_empty_lines = false
|
||||
ij_xml_keep_line_breaks = true
|
||||
ij_xml_keep_line_breaks_in_text = true
|
||||
ij_xml_keep_whitespaces = false
|
||||
ij_xml_keep_whitespaces_around_cdata = preserve
|
||||
ij_xml_keep_whitespaces_inside_cdata = false
|
||||
ij_xml_line_comment_at_first_column = true
|
||||
ij_xml_space_after_tag_name = false
|
||||
ij_xml_space_around_equals_in_attribute = false
|
||||
ij_xml_space_inside_empty_tag = false
|
||||
ij_xml_text_wrap = normal
|
||||
ij_xml_use_custom_settings = false
|
||||
|
||||
[{*.bash,*.sh,*.zsh}]
|
||||
indent_size = 2
|
||||
tab_width = 2
|
||||
ij_shell_binary_ops_start_line = false
|
||||
ij_shell_keep_column_alignment_padding = false
|
||||
ij_shell_minify_program = false
|
||||
ij_shell_redirect_followed_by_space = false
|
||||
ij_shell_switch_cases_indented = false
|
||||
ij_shell_use_unix_line_separator = true
|
||||
|
||||
[{*.gant,*.gradle,*.groovy,*.gy}]
|
||||
ij_groovy_align_group_field_declarations = false
|
||||
ij_groovy_align_multiline_array_initializer_expression = false
|
||||
ij_groovy_align_multiline_assignment = false
|
||||
ij_groovy_align_multiline_binary_operation = false
|
||||
ij_groovy_align_multiline_chained_methods = false
|
||||
ij_groovy_align_multiline_extends_list = false
|
||||
ij_groovy_align_multiline_for = true
|
||||
ij_groovy_align_multiline_list_or_map = true
|
||||
ij_groovy_align_multiline_method_parentheses = false
|
||||
ij_groovy_align_multiline_parameters = true
|
||||
ij_groovy_align_multiline_parameters_in_calls = false
|
||||
ij_groovy_align_multiline_resources = true
|
||||
ij_groovy_align_multiline_ternary_operation = false
|
||||
ij_groovy_align_multiline_throws_list = false
|
||||
ij_groovy_align_named_args_in_map = true
|
||||
ij_groovy_align_throws_keyword = false
|
||||
ij_groovy_array_initializer_new_line_after_left_brace = false
|
||||
ij_groovy_array_initializer_right_brace_on_new_line = false
|
||||
ij_groovy_array_initializer_wrap = off
|
||||
ij_groovy_assert_statement_wrap = off
|
||||
ij_groovy_assignment_wrap = off
|
||||
ij_groovy_binary_operation_wrap = off
|
||||
ij_groovy_blank_lines_after_class_header = 0
|
||||
ij_groovy_blank_lines_after_imports = 1
|
||||
ij_groovy_blank_lines_after_package = 1
|
||||
ij_groovy_blank_lines_around_class = 1
|
||||
ij_groovy_blank_lines_around_field = 0
|
||||
ij_groovy_blank_lines_around_field_in_interface = 0
|
||||
ij_groovy_blank_lines_around_method = 1
|
||||
ij_groovy_blank_lines_around_method_in_interface = 1
|
||||
ij_groovy_blank_lines_before_imports = 1
|
||||
ij_groovy_blank_lines_before_method_body = 0
|
||||
ij_groovy_blank_lines_before_package = 0
|
||||
ij_groovy_block_brace_style = end_of_line
|
||||
ij_groovy_block_comment_at_first_column = true
|
||||
ij_groovy_call_parameters_new_line_after_left_paren = false
|
||||
ij_groovy_call_parameters_right_paren_on_new_line = false
|
||||
ij_groovy_call_parameters_wrap = off
|
||||
ij_groovy_catch_on_new_line = false
|
||||
ij_groovy_class_annotation_wrap = split_into_lines
|
||||
ij_groovy_class_brace_style = end_of_line
|
||||
ij_groovy_class_count_to_use_import_on_demand = 5
|
||||
ij_groovy_do_while_brace_force = never
|
||||
ij_groovy_else_on_new_line = false
|
||||
ij_groovy_enum_constants_wrap = off
|
||||
ij_groovy_extends_keyword_wrap = off
|
||||
ij_groovy_extends_list_wrap = off
|
||||
ij_groovy_field_annotation_wrap = split_into_lines
|
||||
ij_groovy_finally_on_new_line = false
|
||||
ij_groovy_for_brace_force = never
|
||||
ij_groovy_for_statement_new_line_after_left_paren = false
|
||||
ij_groovy_for_statement_right_paren_on_new_line = false
|
||||
ij_groovy_for_statement_wrap = off
|
||||
ij_groovy_if_brace_force = never
|
||||
ij_groovy_import_annotation_wrap = 2
|
||||
ij_groovy_imports_layout = *,|,javax.**,java.**,|,$*
|
||||
ij_groovy_indent_case_from_switch = true
|
||||
ij_groovy_indent_label_blocks = true
|
||||
ij_groovy_insert_inner_class_imports = false
|
||||
ij_groovy_keep_blank_lines_before_right_brace = 2
|
||||
ij_groovy_keep_blank_lines_in_code = 2
|
||||
ij_groovy_keep_blank_lines_in_declarations = 2
|
||||
ij_groovy_keep_control_statement_in_one_line = true
|
||||
ij_groovy_keep_first_column_comment = true
|
||||
ij_groovy_keep_indents_on_empty_lines = false
|
||||
ij_groovy_keep_line_breaks = true
|
||||
ij_groovy_keep_multiple_expressions_in_one_line = false
|
||||
ij_groovy_keep_simple_blocks_in_one_line = false
|
||||
ij_groovy_keep_simple_classes_in_one_line = true
|
||||
ij_groovy_keep_simple_lambdas_in_one_line = true
|
||||
ij_groovy_keep_simple_methods_in_one_line = true
|
||||
ij_groovy_label_indent_absolute = false
|
||||
ij_groovy_label_indent_size = 0
|
||||
ij_groovy_lambda_brace_style = end_of_line
|
||||
ij_groovy_layout_static_imports_separately = true
|
||||
ij_groovy_line_comment_add_space = false
|
||||
ij_groovy_line_comment_at_first_column = true
|
||||
ij_groovy_method_annotation_wrap = split_into_lines
|
||||
ij_groovy_method_brace_style = end_of_line
|
||||
ij_groovy_method_call_chain_wrap = off
|
||||
ij_groovy_method_parameters_new_line_after_left_paren = false
|
||||
ij_groovy_method_parameters_right_paren_on_new_line = false
|
||||
ij_groovy_method_parameters_wrap = off
|
||||
ij_groovy_modifier_list_wrap = false
|
||||
ij_groovy_names_count_to_use_import_on_demand = 3
|
||||
ij_groovy_parameter_annotation_wrap = off
|
||||
ij_groovy_parentheses_expression_new_line_after_left_paren = false
|
||||
ij_groovy_parentheses_expression_right_paren_on_new_line = false
|
||||
ij_groovy_prefer_parameters_wrap = false
|
||||
ij_groovy_resource_list_new_line_after_left_paren = false
|
||||
ij_groovy_resource_list_right_paren_on_new_line = false
|
||||
ij_groovy_resource_list_wrap = off
|
||||
ij_groovy_space_after_assert_separator = true
|
||||
ij_groovy_space_after_colon = true
|
||||
ij_groovy_space_after_comma = true
|
||||
ij_groovy_space_after_comma_in_type_arguments = true
|
||||
ij_groovy_space_after_for_semicolon = true
|
||||
ij_groovy_space_after_quest = true
|
||||
ij_groovy_space_after_type_cast = true
|
||||
ij_groovy_space_before_annotation_parameter_list = false
|
||||
ij_groovy_space_before_array_initializer_left_brace = false
|
||||
ij_groovy_space_before_assert_separator = false
|
||||
ij_groovy_space_before_catch_keyword = true
|
||||
ij_groovy_space_before_catch_left_brace = true
|
||||
ij_groovy_space_before_catch_parentheses = true
|
||||
ij_groovy_space_before_class_left_brace = true
|
||||
ij_groovy_space_before_closure_left_brace = true
|
||||
ij_groovy_space_before_colon = true
|
||||
ij_groovy_space_before_comma = false
|
||||
ij_groovy_space_before_do_left_brace = true
|
||||
ij_groovy_space_before_else_keyword = true
|
||||
ij_groovy_space_before_else_left_brace = true
|
||||
ij_groovy_space_before_finally_keyword = true
|
||||
ij_groovy_space_before_finally_left_brace = true
|
||||
ij_groovy_space_before_for_left_brace = true
|
||||
ij_groovy_space_before_for_parentheses = true
|
||||
ij_groovy_space_before_for_semicolon = false
|
||||
ij_groovy_space_before_if_left_brace = true
|
||||
ij_groovy_space_before_if_parentheses = true
|
||||
ij_groovy_space_before_method_call_parentheses = false
|
||||
ij_groovy_space_before_method_left_brace = true
|
||||
ij_groovy_space_before_method_parentheses = false
|
||||
ij_groovy_space_before_quest = true
|
||||
ij_groovy_space_before_switch_left_brace = true
|
||||
ij_groovy_space_before_switch_parentheses = true
|
||||
ij_groovy_space_before_synchronized_left_brace = true
|
||||
ij_groovy_space_before_synchronized_parentheses = true
|
||||
ij_groovy_space_before_try_left_brace = true
|
||||
ij_groovy_space_before_try_parentheses = true
|
||||
ij_groovy_space_before_while_keyword = true
|
||||
ij_groovy_space_before_while_left_brace = true
|
||||
ij_groovy_space_before_while_parentheses = true
|
||||
ij_groovy_space_in_named_argument = true
|
||||
ij_groovy_space_in_named_argument_before_colon = false
|
||||
ij_groovy_space_within_empty_array_initializer_braces = false
|
||||
ij_groovy_space_within_empty_method_call_parentheses = false
|
||||
ij_groovy_spaces_around_additive_operators = true
|
||||
ij_groovy_spaces_around_assignment_operators = true
|
||||
ij_groovy_spaces_around_bitwise_operators = true
|
||||
ij_groovy_spaces_around_equality_operators = true
|
||||
ij_groovy_spaces_around_lambda_arrow = true
|
||||
ij_groovy_spaces_around_logical_operators = true
|
||||
ij_groovy_spaces_around_multiplicative_operators = true
|
||||
ij_groovy_spaces_around_regex_operators = true
|
||||
ij_groovy_spaces_around_relational_operators = true
|
||||
ij_groovy_spaces_around_shift_operators = true
|
||||
ij_groovy_spaces_within_annotation_parentheses = false
|
||||
ij_groovy_spaces_within_array_initializer_braces = false
|
||||
ij_groovy_spaces_within_braces = true
|
||||
ij_groovy_spaces_within_brackets = false
|
||||
ij_groovy_spaces_within_cast_parentheses = false
|
||||
ij_groovy_spaces_within_catch_parentheses = false
|
||||
ij_groovy_spaces_within_for_parentheses = false
|
||||
ij_groovy_spaces_within_gstring_injection_braces = false
|
||||
ij_groovy_spaces_within_if_parentheses = false
|
||||
ij_groovy_spaces_within_list_or_map = false
|
||||
ij_groovy_spaces_within_method_call_parentheses = false
|
||||
ij_groovy_spaces_within_method_parentheses = false
|
||||
ij_groovy_spaces_within_parentheses = false
|
||||
ij_groovy_spaces_within_switch_parentheses = false
|
||||
ij_groovy_spaces_within_synchronized_parentheses = false
|
||||
ij_groovy_spaces_within_try_parentheses = false
|
||||
ij_groovy_spaces_within_tuple_expression = false
|
||||
ij_groovy_spaces_within_while_parentheses = false
|
||||
ij_groovy_special_else_if_treatment = true
|
||||
ij_groovy_ternary_operation_wrap = off
|
||||
ij_groovy_throws_keyword_wrap = off
|
||||
ij_groovy_throws_list_wrap = off
|
||||
ij_groovy_use_flying_geese_braces = false
|
||||
ij_groovy_use_fq_class_names = false
|
||||
ij_groovy_use_fq_class_names_in_javadoc = true
|
||||
ij_groovy_use_relative_indents = false
|
||||
ij_groovy_use_single_class_imports = true
|
||||
ij_groovy_variable_annotation_wrap = off
|
||||
ij_groovy_while_brace_force = never
|
||||
ij_groovy_while_on_new_line = false
|
||||
ij_groovy_wrap_long_lines = false
|
||||
|
||||
[{*.gradle.kts,*.kt,*.kts,*.main.kts,*.space.kts}]
|
||||
ij_kotlin_align_in_columns_case_branch = false
|
||||
ij_kotlin_align_multiline_binary_operation = false
|
||||
ij_kotlin_align_multiline_extends_list = false
|
||||
ij_kotlin_align_multiline_method_parentheses = false
|
||||
ij_kotlin_align_multiline_parameters = true
|
||||
ij_kotlin_align_multiline_parameters_in_calls = false
|
||||
ij_kotlin_allow_trailing_comma = true
|
||||
ij_kotlin_allow_trailing_comma_on_call_site = true
|
||||
ij_kotlin_assignment_wrap = normal
|
||||
ij_kotlin_allow_trailing_comma = false
|
||||
ij_kotlin_allow_trailing_comma_on_call_site = false
|
||||
ij_kotlin_assignment_wrap = off
|
||||
ij_kotlin_blank_lines_after_class_header = 0
|
||||
ij_kotlin_blank_lines_around_block_when_branches = 0
|
||||
ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1
|
||||
ij_kotlin_block_comment_at_first_column = true
|
||||
ij_kotlin_call_parameters_new_line_after_left_paren = true
|
||||
ij_kotlin_call_parameters_new_line_after_left_paren = false
|
||||
ij_kotlin_call_parameters_right_paren_on_new_line = false
|
||||
ij_kotlin_call_parameters_wrap = on_every_item
|
||||
ij_kotlin_call_parameters_wrap = off
|
||||
ij_kotlin_catch_on_new_line = false
|
||||
ij_kotlin_class_annotation_wrap = split_into_lines
|
||||
ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
|
||||
ij_kotlin_continuation_indent_for_chained_calls = true
|
||||
ij_kotlin_continuation_indent_for_expression_bodies = true
|
||||
ij_kotlin_continuation_indent_in_argument_lists = true
|
||||
ij_kotlin_continuation_indent_in_elvis = false
|
||||
ij_kotlin_continuation_indent_in_if_conditions = false
|
||||
ij_kotlin_continuation_indent_in_parameter_lists = false
|
||||
ij_kotlin_continuation_indent_in_supertype_lists = false
|
||||
ij_kotlin_continuation_indent_in_elvis = true
|
||||
ij_kotlin_continuation_indent_in_if_conditions = true
|
||||
ij_kotlin_continuation_indent_in_parameter_lists = true
|
||||
ij_kotlin_continuation_indent_in_supertype_lists = true
|
||||
ij_kotlin_else_on_new_line = false
|
||||
ij_kotlin_enum_constants_wrap = off
|
||||
ij_kotlin_extends_list_wrap = normal
|
||||
ij_kotlin_extends_list_wrap = off
|
||||
ij_kotlin_field_annotation_wrap = split_into_lines
|
||||
ij_kotlin_finally_on_new_line = false
|
||||
ij_kotlin_if_rparen_on_new_line = false
|
||||
ij_kotlin_import_nested_classes = false
|
||||
ij_kotlin_imports_layout = *,java.**,javax.**,kotlin.**,^
|
||||
ij_kotlin_insert_whitespaces_in_simple_one_line_method = true
|
||||
ij_kotlin_keep_blank_lines_before_right_brace = 2
|
||||
ij_kotlin_keep_blank_lines_in_code = 2
|
||||
|
@ -57,13 +515,13 @@ ij_kotlin_lbrace_on_next_line = false
|
|||
ij_kotlin_line_comment_add_space = false
|
||||
ij_kotlin_line_comment_at_first_column = true
|
||||
ij_kotlin_method_annotation_wrap = split_into_lines
|
||||
ij_kotlin_method_call_chain_wrap = normal
|
||||
ij_kotlin_method_parameters_new_line_after_left_paren = true
|
||||
ij_kotlin_method_parameters_right_paren_on_new_line = true
|
||||
ij_kotlin_method_parameters_wrap = on_every_item
|
||||
ij_kotlin_name_count_to_use_star_import = 9999
|
||||
ij_kotlin_name_count_to_use_star_import_for_members = 9999
|
||||
ij_java_names_count_to_use_import_on_demand = 9999
|
||||
ij_kotlin_method_call_chain_wrap = off
|
||||
ij_kotlin_method_parameters_new_line_after_left_paren = false
|
||||
ij_kotlin_method_parameters_right_paren_on_new_line = false
|
||||
ij_kotlin_method_parameters_wrap = off
|
||||
ij_kotlin_name_count_to_use_star_import = 5
|
||||
ij_kotlin_name_count_to_use_star_import_for_members = 3
|
||||
ij_kotlin_packages_to_use_import_on_demand = java.util.*,kotlinx.android.synthetic.**,io.ktor.**
|
||||
ij_kotlin_parameter_annotation_wrap = off
|
||||
ij_kotlin_space_after_comma = true
|
||||
ij_kotlin_space_after_extend_colon = true
|
||||
|
@ -90,5 +548,72 @@ ij_kotlin_spaces_around_when_arrow = true
|
|||
ij_kotlin_variable_annotation_wrap = off
|
||||
ij_kotlin_while_on_new_line = false
|
||||
ij_kotlin_wrap_elvis_expressions = 1
|
||||
ij_kotlin_wrap_expression_body_functions = 1
|
||||
ij_kotlin_wrap_expression_body_functions = 0
|
||||
ij_kotlin_wrap_first_method_in_call_chain = false
|
||||
|
||||
[{*.har,*.json}]
|
||||
indent_size = 2
|
||||
ij_json_keep_blank_lines_in_code = 0
|
||||
ij_json_keep_indents_on_empty_lines = false
|
||||
ij_json_keep_line_breaks = true
|
||||
ij_json_space_after_colon = true
|
||||
ij_json_space_after_comma = true
|
||||
ij_json_space_before_colon = true
|
||||
ij_json_space_before_comma = false
|
||||
ij_json_spaces_within_braces = false
|
||||
ij_json_spaces_within_brackets = false
|
||||
ij_json_wrap_long_lines = false
|
||||
|
||||
[{*.htm,*.html,*.sht,*.shtm,*.shtml}]
|
||||
ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3
|
||||
ij_html_align_attributes = true
|
||||
ij_html_align_text = false
|
||||
ij_html_attribute_wrap = normal
|
||||
ij_html_block_comment_at_first_column = true
|
||||
ij_html_do_not_align_children_of_min_lines = 0
|
||||
ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p
|
||||
ij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot
|
||||
ij_html_enforce_quotes = false
|
||||
ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var
|
||||
ij_html_keep_blank_lines = 2
|
||||
ij_html_keep_indents_on_empty_lines = false
|
||||
ij_html_keep_line_breaks = true
|
||||
ij_html_keep_line_breaks_in_text = true
|
||||
ij_html_keep_whitespaces = false
|
||||
ij_html_keep_whitespaces_inside = span,pre,textarea
|
||||
ij_html_line_comment_at_first_column = true
|
||||
ij_html_new_line_after_last_attribute = never
|
||||
ij_html_new_line_before_first_attribute = never
|
||||
ij_html_quote_style = double
|
||||
ij_html_remove_new_line_before_tags = br
|
||||
ij_html_space_after_tag_name = false
|
||||
ij_html_space_around_equality_in_attribute = false
|
||||
ij_html_space_inside_empty_tag = false
|
||||
ij_html_text_wrap = normal
|
||||
ij_html_uniform_ident = false
|
||||
|
||||
[{*.markdown,*.md}]
|
||||
ij_markdown_force_one_space_after_blockquote_symbol = true
|
||||
ij_markdown_force_one_space_after_header_symbol = true
|
||||
ij_markdown_force_one_space_after_list_bullet = true
|
||||
ij_markdown_force_one_space_between_words = true
|
||||
ij_markdown_keep_indents_on_empty_lines = false
|
||||
ij_markdown_max_lines_around_block_elements = 1
|
||||
ij_markdown_max_lines_around_header = 1
|
||||
ij_markdown_max_lines_between_paragraphs = 1
|
||||
ij_markdown_min_lines_around_block_elements = 1
|
||||
ij_markdown_min_lines_around_header = 1
|
||||
ij_markdown_min_lines_between_paragraphs = 1
|
||||
|
||||
[{*.yaml,*.yml}]
|
||||
indent_size = 2
|
||||
ij_yaml_align_values_properties = do_not_align
|
||||
ij_yaml_autoinsert_sequence_marker = true
|
||||
ij_yaml_block_mapping_on_new_line = false
|
||||
ij_yaml_indent_sequence_value = true
|
||||
ij_yaml_keep_indents_on_empty_lines = false
|
||||
ij_yaml_keep_line_breaks = true
|
||||
ij_yaml_sequence_on_new_line = false
|
||||
ij_yaml_space_before_colon = false
|
||||
ij_yaml_spaces_within_braces = true
|
||||
ij_yaml_spaces_within_brackets = true
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
# Ignore initial spotlessApply using ktfmt
|
||||
51e9bfc67f19e16a69790a8d92bd6b1c86a76a5f
|
17
.github/FUNDING.yml
vendored
17
.github/FUNDING.yml
vendored
|
@ -1,17 +0,0 @@
|
|||
# SPDX-FileCopyrightText: 2021 Paul Schaub <info@pgpainless.org>
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: vanitasvitae # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
#patreon: # Replace with a single Patreon username
|
||||
#open_collective: # Replace with a single Open Collective username
|
||||
#ko_fi: # Replace with a single Ko-fi username
|
||||
#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
#liberapay: # Replace with a single Liberapay username
|
||||
#issuehunt: # Replace with a single IssueHunt username
|
||||
#otechie: # Replace with a single Otechie username
|
||||
#lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
#custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
35
.github/ISSUE_TEMPLATE/cli-application.md
vendored
35
.github/ISSUE_TEMPLATE/cli-application.md
vendored
|
@ -1,35 +0,0 @@
|
|||
---
|
||||
name: CLI Application
|
||||
about: Report an issue with the pgpainless-cli utility
|
||||
title: ''
|
||||
labels: 'module: cli'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
<!-- A clear and concise description of what the bug is. -->
|
||||
|
||||
**Version**
|
||||
<!-- What version of the software are you using? -->
|
||||
- `pgpainless-cli`:
|
||||
|
||||
**Installation Source**
|
||||
<!-- Where did you install / build pgpainless-cli from? -->
|
||||
- Debian Repository
|
||||
- Built locally (`gradle build...`)
|
||||
|
||||
**To Reproduce**
|
||||
<!-- Steps to reproduce the behavior: -->
|
||||
1. `pgpainless-cli foo bar [...]`'
|
||||
2. ...
|
||||
|
||||
**Expected behavior**
|
||||
<!-- A clear and concise description of what you expected to happen. -->
|
||||
|
||||
**Additional context**
|
||||
<!-- Add any other context (test keys, test messages) about the problem here. -->
|
||||
```
|
||||
-----BEGIN PGP FOO BAR-----
|
||||
...
|
||||
```
|
28
.github/ISSUE_TEMPLATE/library.md
vendored
28
.github/ISSUE_TEMPLATE/library.md
vendored
|
@ -1,28 +0,0 @@
|
|||
---
|
||||
name: Library
|
||||
about: Report an issue with the libraries pgpainless-core or pgpainless-sop
|
||||
title: ''
|
||||
labels: 'module: core'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
<!-- A clear and concise description of what the bug is. -->
|
||||
|
||||
**Version**
|
||||
<!-- What version of the software are you using? Delete lines which are not applicable. -->
|
||||
- `pgpainless-core`:
|
||||
- `pgpainless-sop`:
|
||||
|
||||
**To Reproduce**
|
||||
<!-- Steps to reproduce the behavior: -->
|
||||
```
|
||||
Example Code Block with your Code
|
||||
```
|
||||
|
||||
**Expected behavior**
|
||||
<!-- A clear and concise description of what you expected to happen. -->
|
||||
|
||||
**Additional context**
|
||||
<!-- Add any other context about the problem here. -->
|
74
.github/workflows/codeql-analysis.yml
vendored
74
.github/workflows/codeql-analysis.yml
vendored
|
@ -1,74 +0,0 @@
|
|||
# SPDX-FileCopyrightText: 2021 Paul Schaub <info@pgpainless.org>
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, release/* ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ main ]
|
||||
schedule:
|
||||
- cron: '16 10 * * 0'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'java-kotlin' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
27
.github/workflows/depencency-submission.yml
vendored
27
.github/workflows/depencency-submission.yml
vendored
|
@ -1,27 +0,0 @@
|
|||
# SPDX-FileCopyrightText: 2023 Paul Schaub <info@pgpainless.org>
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
name: Dependencies
|
||||
on:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Dependencies
|
||||
runs-on: ubuntu-latest
|
||||
permissions: # The Dependency Submission API requires write permission
|
||||
contents: write
|
||||
steps:
|
||||
- name: 'Checkout Repository'
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Run snapshot action
|
||||
uses: mikepenz/gradle-dependency-submission@v0.8.6
|
||||
with:
|
||||
gradle-build-module: |-
|
||||
:pgpainless-core
|
||||
:pgpainless-sop
|
||||
:pgpainless-cli
|
||||
sub-module-mode: |-
|
||||
INDIVIDUAL_DEEP
|
40
.github/workflows/gradle_push.yml
vendored
40
.github/workflows/gradle_push.yml
vendored
|
@ -1,40 +0,0 @@
|
|||
# SPDX-FileCopyrightText: 2021 Paul Schaub <info@pgpainless.org>
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# This workflow uses actions that are not certified by GitHub.
|
||||
# They are provided by a third-party and are governed by
|
||||
# separate terms of service, privacy policy, and support
|
||||
# documentation.
|
||||
# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
|
||||
|
||||
name: Push
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'temurin'
|
||||
- name: Build and Check
|
||||
uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1
|
||||
with:
|
||||
arguments: check jacocoRootReport
|
||||
- name: Coveralls
|
||||
uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1
|
||||
env:
|
||||
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
|
||||
with:
|
||||
arguments: coveralls
|
33
.github/workflows/pr.yml
vendored
33
.github/workflows/pr.yml
vendored
|
@ -1,33 +0,0 @@
|
|||
# SPDX-FileCopyrightText: 2021 Paul Schaub <info@pgpainless.org>
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# This workflow uses actions that are not certified by GitHub.
|
||||
# They are provided by a third-party and are governed by
|
||||
# separate terms of service, privacy policy, and support
|
||||
# documentation.
|
||||
# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
|
||||
|
||||
name: PR
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'temurin'
|
||||
- name: Build, Check and Coverage
|
||||
uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1
|
||||
with:
|
||||
arguments: check jacocoRootReport
|
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -1,7 +1,3 @@
|
|||
# SPDX-FileCopyrightText: 2021 Paul Schaub <info@pgpainless.org>
|
||||
#
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
.idea
|
||||
.gradle
|
||||
|
||||
|
@ -17,8 +13,6 @@ libs/
|
|||
*.log
|
||||
*.jar
|
||||
|
||||
local.properties
|
||||
|
||||
gradle.properties
|
||||
!gradle-wrapper.jar
|
||||
|
||||
|
@ -31,5 +25,3 @@ pgpainless-core/.project
|
|||
pgpainless-core/.settings/
|
||||
|
||||
push_html.sh
|
||||
|
||||
node_modules
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
# .readthedocs.yaml
|
||||
# Read the Docs configuration file
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Set the version of Python and other tools you might need
|
||||
build:
|
||||
os: ubuntu-20.04
|
||||
# apt_packages:
|
||||
# - libgtk-3-0
|
||||
# - libasound2
|
||||
# - libnss3
|
||||
# - libxss1
|
||||
# - libgbm1
|
||||
# - libxshmfence1
|
||||
tools:
|
||||
python: "3.9"
|
||||
# You can also specify other tool versions:
|
||||
# nodejs: "16"
|
||||
# rust: "1.55"
|
||||
# golang: "1.17"
|
||||
# jobs:
|
||||
# post_install:
|
||||
# - npm install -g @mermaid-js/mermaid-cli
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/source/conf.py
|
||||
|
||||
# If using Sphinx, optionally build your docs in additional formats such as PDF
|
||||
formats:
|
||||
- pdf
|
||||
- epub
|
||||
|
||||
# Optionally declare the Python requirements required to build your docs
|
||||
python:
|
||||
install:
|
||||
- requirements: docs/requirements.txt
|
13
.travis.yml
13
.travis.yml
|
@ -1,16 +1,9 @@
|
|||
# SPDX-FileCopyrightText: 2021 Paul Schaub <info@pgpainless.org>
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
language: java
|
||||
dist: bionic
|
||||
jdk:
|
||||
- openjdk8
|
||||
- openjdk11
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
before_cache:
|
||||
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
||||
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
|
||||
|
@ -23,11 +16,7 @@ before_install:
|
|||
- export GRADLE_VERSION=6.2
|
||||
- wget https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-all.zip
|
||||
- unzip -q gradle-${GRADLE_VERSION}-all.zip
|
||||
- rm gradle-${GRADLE_VERSION}-all.zip
|
||||
- sudo mv gradle-${GRADLE_VERSION} /usr/local/bin/
|
||||
- export PATH="/usr/local/bin/gradle-${GRADLE_VERSION}/bin:$PATH"
|
||||
- docker pull fsfe/reuse:latest
|
||||
- docker run -v ${TRAVIS_BUILD_DIR}:/data fsfe/reuse:latest lint
|
||||
- export PATH="$(pwd)/gradle-${GRADLE_VERSION}/bin:$PATH"
|
||||
|
||||
install: gradle assemble --stacktrace
|
||||
|
||||
|
|
672
CHANGELOG.md
672
CHANGELOG.md
|
@ -1,677 +1,5 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2021 Paul Schaub <info@pgpainless.org>
|
||||
SPDX-License-Identifier: CC0-1.0
|
||||
-->
|
||||
|
||||
# PGPainless Changelog
|
||||
|
||||
## 1.7.7-SNAPSHOT
|
||||
- Bump `bcpg-jdk8on` to `1.81`
|
||||
- Bump `bcprov-jdk18on` to `1.81`
|
||||
|
||||
## 1.7.6
|
||||
- Fix `RevocationSignatureBuilder` properly calculating third-party signatures of type `KeyRevocation` (delegation revocations)
|
||||
- Enable support for native images
|
||||
- Re-enable shadow plugin and build fat-jar
|
||||
|
||||
## 1.7.5
|
||||
- Actually attempt to fix Kotlin desugaring.
|
||||
- Bump javaSourceCompatibility and javaTargetCompatibility to 11
|
||||
- Bump gradle-wrapper to 8.8
|
||||
|
||||
## 1.7.4
|
||||
- Fix proper Kotlin desugaring for Java 8
|
||||
|
||||
## 1.7.3
|
||||
- Bump `bcpg-jdk8on` to `1.80`
|
||||
- Bump `bcprov-jdk18on` to `1.80`
|
||||
- Add dependency on `bcutil-jdk18on` as a workaround
|
||||
- Ignore unknown type signatures on certificates
|
||||
- Fix typo on signature creation bounds check (thanks @elduffy)
|
||||
- Fix superfluous newline added in CRLF encoding (thanks @bjansen)
|
||||
- Bump `sop-java` to `1.10.0`
|
||||
- SOP inline-sign: Do not apply compression
|
||||
|
||||
## 1.7.2
|
||||
- Fix bug in `KeyRingInfo.lastModified` (thanks to @Jerbell, @sosnovsky for reporting)
|
||||
- Bump `sop-java` to `10.0.3`
|
||||
- allow multiple arguments `--with-key-password` in `revoke-key` command
|
||||
- Properly pass `--old-key-password` and `--new-key-password` options as indirect arguments in `change-key-password` command
|
||||
|
||||
## 1.7.1
|
||||
- Bump `sop-java` to `10.0.2`
|
||||
- Downgrade `logback-core` and `logback-classic` to `1.2.13` (fix CLI spam)
|
||||
|
||||
## 1.7.0
|
||||
- Bump `bcpg-jdk8on` to `1.78.1`
|
||||
- Bump `bcprov-jdk18on` to `1.78.1`
|
||||
- Bump `logback-core` and `logback-classic` to `1.4.14`
|
||||
- `pgpainless-core`
|
||||
- Rewrote most of the codebase in Kotlin
|
||||
- Removed `OpenPgpMetadata` (`decryptionStream.getResult()`) in favor of `MessageMetadata` (`decryptionStream.getMetadata()`)
|
||||
- Removed support for generating EC keys over non-standard curve `secp256k1`
|
||||
- Properly feed EOS tokens to the pushdown automaton when reaching the end of stream (thanks @iNPUTmice)
|
||||
- Do not choke on unknown signature subpackets (thanks @Jerbell)
|
||||
- Prevent timing issues resulting in subkey binding signatures predating the subkey (@thanks Jerbell)
|
||||
- Rename LibrePGP-related `Feature` enums:
|
||||
- `GNUPG_AEAD_ENCRYPTED_DATA` -> `LIBREPGP_OCB_ENCRYPTED_DATA`
|
||||
- `GNUPG_VERSION_5_PUBLIC_KEY` -> `LIBREPGP_VERSION_5_PUBLIC_KEY`
|
||||
- Properly reject signatures by non-signing primary keys
|
||||
- Add `EncryptionBuilder.discardOutput()` (useful for detached signing)
|
||||
- Remove support for generation of keys over non-standard `secp256k1` curve
|
||||
- Add base support for padding packets
|
||||
- Do not choke on LibrePGP OED packets
|
||||
- Supersede `addPassphrase()`/`addDecryptionPassphrase()` methods with more clear `addMessagePassphrase()`
|
||||
- `pgpainless-sop`, `pgpainless-cli`
|
||||
- Bump `sop-java` to `10.0.1`, implementing [SOP Spec Revision 10](https://www.ietf.org/archive/id/draft-dkg-openpgp-stateless-cli-10.html)
|
||||
- Change API of `sop.encrypt` to return a `ReadyWithResult<EncryptionResult>` to expose the session key
|
||||
- `decrypt --verify-with`: Fix to not throw `NoSignature` exception (exit code 3) if `VERIFICATIONS` is empty
|
||||
- Separate signature verification operations into `SOPV` interface
|
||||
- Add `version --sopv` option
|
||||
- Throw `BadData` error when passing KEYS where CERTS are expected.
|
||||
- `armor`: Remove `--label` option
|
||||
|
||||
## 1.6.8
|
||||
- Bump `sop-java` to `7.0.2`
|
||||
- SOP `change-key-password`: Fix reading password from indirect parameter instead of erroneously passing filename (fixes #453)
|
||||
- SOP `revoke-key`: Allow for multiple `--with-key-password` options
|
||||
|
||||
## 1.6.7
|
||||
- SOP: Fix OOM error when detached-signing large amounts of data (fix #432)
|
||||
- Move `CachingBcPublicKeyDataDecryptorFactory` from `org.bouncycastle` packet to `org.pgpainless.decryption_verification` to avoid package split (partially addresses #428)
|
||||
- Basic support for Java Modules for `pgpainless-core` and `pgpainless-sop`
|
||||
- Added `Automatic-Module-Name` directive to gradle build files
|
||||
|
||||
## 1.6.6
|
||||
- Downgrade `logback-core` and `logback-classic` to `1.2.13` to fix #426
|
||||
|
||||
## 1.6.5
|
||||
- Add `SecretKeyRingEditor.setExpirationDateOfSubkey()`
|
||||
|
||||
## 1.6.4
|
||||
- Bump `bcpg-jdk8on` to `1.77`
|
||||
- Bump `bcprov-jdk18on` to `1.77`
|
||||
- Bump `logback-core` and `logback-classic` to `1.4.13`
|
||||
- Properly feed EOS tokens to the pushdown automaton when reaching the end of stream (thanks @iNPUTmice)
|
||||
- Do not choke on unknown signature subpackets (thanks @Jerbell)
|
||||
- Prevent timing issues resuting in subkey binding signatures predating the subkey (@thanks Jerbell)
|
||||
|
||||
## 1.6.3
|
||||
- Bump `sop-java` to `7.0.1`
|
||||
- `decrypt --verify-with`: Fix to not throw `NoSignature` exception (exit code 3) if `VERIFICATIONS` is empty
|
||||
|
||||
## 1.6.2
|
||||
- Switch `bcpg` and `bcprov` artifacts from `-jdk15to18`variant to `-jdk18on`
|
||||
- Bump `bcpg-jdk8on` to `1.76`
|
||||
- Bump `bcprov-jdk18on` to `1.76`
|
||||
- Add `EncryptionOptions.setAllowEncryptionWithMissingKeyFlags()` to properly allow
|
||||
encrypting to legacy keys which do not carry any key flags.
|
||||
- Allow overriding of reference time in `EncryptionOptions` and `SigningOptions`.
|
||||
|
||||
## 1.6.1
|
||||
- `KeyRingBuilder`: Require UTF8 when adding user-ID via `addUserId(byte[])`
|
||||
- `pgpainless-sop`: Remove dependency on jetbrains annotations
|
||||
- Add `CertificateAuthority` interface to allow integration with [`pgpainless-wot`](https://github.com/pgpainless/pgpainless-wot)
|
||||
- Add `EncryptionOptions.addAuthenticatableRecipients()` method
|
||||
- Add `MessageMetadata.isAuthenticatablySignedBy()` method
|
||||
|
||||
## 1.6.0
|
||||
- Bump `sop-java` to `7.0.0`, implementing [SOP Spec Revision 07](https://www.ietf.org/archive/id/draft-dkg-openpgp-stateless-cli-07.html)
|
||||
- Implement `revoke-key` subcommand and API
|
||||
- Implement `change-key-password` subcommand and API
|
||||
- `generate-key`: Add support for new `--signing-only` option
|
||||
- Move some methods related to password changing from `SecretKeyRingEditor` to `KeyRingUtils`
|
||||
|
||||
## 1.5.7
|
||||
- Bump `sop-java` to `6.1.1`
|
||||
- `decrypt --verify-with`: Fix to not throw `NoSignature` exception (exit code 3) if `VERIFICATIONS` is empty
|
||||
|
||||
## 1.5.6
|
||||
- Bump `jacoco` to `0.8.8` (thanks @hkos)
|
||||
- Ignore malformed, non-UTF8 user-IDs on certificates
|
||||
- `KeyRingReader.readPublicKeyRingCollection()`: Extract and return public keys from encountered secret keys
|
||||
- Add some utility methods to `KeyRingInfo`:
|
||||
- `getValidSubkeys()` only returns validly bound sub-keys
|
||||
- Add some utility methods to `SignatureUtils`:
|
||||
- `getDelegations()` returns all third-party signatures made over the primary key
|
||||
- `get3rdPartyCertificationsFor(userId)` returns all third-party certification signatures made over the given user-id
|
||||
- Add some utility methods to `SignatureSubpacketsUtil`:
|
||||
- `isExportable()` will return true if the signature is *not* marked as non-exportable
|
||||
- `getTrustDepthOr()` returns the signatures trust-depth, or a default value if there is no trust-signature subpacket
|
||||
- `getTrustAmountOr()` returns the signatures trust-amount, or a default value if there is no trust-signature subpacket
|
||||
|
||||
## 1.5.5
|
||||
- Bump `bcpg-jdk15to18` to `1.75`
|
||||
- Bump `bcprov-jdk15to18` to `1.75`
|
||||
- Bump `checkstyle` to `10.12.1` to fix build dependency on [vulnerable guava](https://github.com/pgpainless/pgpainless/security/dependabot/6).
|
||||
- `SecretKeyRingEditor`:
|
||||
- Rename `createRevocationCertificate()` to `createRevocation()`
|
||||
- Add `createMinimalRevocationCertificate()` method to generate OpenPGP v6-style self-certifying revocation certificates
|
||||
|
||||
## 1.5.4
|
||||
- Bump `bcpg-jdk15to18` to `1.74`
|
||||
- Bump `bcprov-jdk15to18` to `1.74`
|
||||
- Remove unused methods from `SignatureUtils`
|
||||
- Encryption: Allow anonymous recipients using wildcard key-IDs
|
||||
- Add `SignatureSubpacketsUtil.getRegularExpressions()`
|
||||
- Tests, tests, tests
|
||||
|
||||
## 1.5.3
|
||||
- Fix minimal bit-strength check for signing-subkeys accidentally comparing the bit-strength of the primary key
|
||||
- `SigningOptions`: Add new methods to add signatures using a single, chosen signing subkey
|
||||
|
||||
## 1.5.2
|
||||
- Bugfix: Create proper direct-key signatures
|
||||
- `KeyRingTemplates`:
|
||||
- Add `rsaKeyRing()` for generating RSA keys with primary key and dedicated signing, encryption subkeys
|
||||
- Reduce number of template methods by replacing `UserId`, `String` arguments with `CharSequence`
|
||||
- Add `MessageMetadata.getRecipientKeyIds()`
|
||||
- Work towards more null-safe API by annotating methods in `EncryptionOptions`, `SigningOptions`, `KeyRingInfo`, `PGPainless` with `@Nonnull`, `@Nullable`
|
||||
- `KeyRingUtils`: Removed `removeSecretKey()` in favour of `stripSecretKey()`
|
||||
- General code cleanup
|
||||
- SOP: generating keys with `rfc4880` profile now generates key with primary key and subkeys
|
||||
- Deprecate ElGamal key type
|
||||
- Key generation: Set expiration period of 5 years by default
|
||||
- Set AES-128 as default fallback symmetric algorithm
|
||||
- `ProducerOptions`: Allow setting custom version header when encrypting/signing message
|
||||
|
||||
## 1.5.2-rc1
|
||||
- Bump `sop-java` to `6.1.0`
|
||||
- Normalize `OpenPgpMessageInputStream.read()` behaviour when reading past the stream
|
||||
- Instead of throwing a `MalformedOpenPgpMessageException` which could throw off unsuspecting parsers,
|
||||
we now simply return `-1` like every other `InputStream`.
|
||||
|
||||
## 1.5.1
|
||||
- SOP: Emit signature `mode:{binary|text}` in `Verification` results
|
||||
- core: Relax constraints on decryption subkeys to improve interoperability with broken clients
|
||||
- Allow decryption with revoked keys
|
||||
- Allow decryption with expired keys
|
||||
- Allow decryption with erroneously addressed keys without encryption key flags
|
||||
|
||||
## 1.5.0
|
||||
- Bump `bcpg-jdk15to18` to `1.73`
|
||||
- Bump `bcprov-jdk15to18` to `1.73`
|
||||
- Introduce `OpenPgpv6Fingerprint` class
|
||||
- Bump `sop-java` to `5.0.0`, implementing [SOP Spec Revision 05](https://www.ietf.org/archive/id/draft-dkg-openpgp-stateless-cli-05.html)
|
||||
- Add support for `list-profiles` subcommand (`generate-key` only for now)
|
||||
- `generate-key`: Add support for `--profile=` option
|
||||
- Add profile `draft-koch-eddsa-for-openpgp-00` which represents status quo.
|
||||
- Add profile `rfc4880` which generates keys based on 4096-bit RSA.
|
||||
- Bump `sop-java` to `6.0.0`, implementing [SOP Spec Revision 06](https://www.ietf.org/archive/id/draft-dkg-openpgp-stateless-cli-06.html)
|
||||
- `encrypt`: Add support for `--profile=` option
|
||||
- Add profile `rfc4880` to reflect status quo
|
||||
- `version`: Add support for `--sop-spec` option
|
||||
|
||||
## 1.4.6
|
||||
- Bump `sop-java` to `4.1.2`
|
||||
- Fix `decrypt --verify-with` to not throw `NoSignature` exception (exit code 3) if `VERIFICAIONS` is empty.
|
||||
|
||||
## 1.4.5
|
||||
- Bugfix: Direct-Key signatures are calculated over the signee key only, not the signer key + signee key
|
||||
- Security: Fix faulty bit-strength policy check for signing subkeys
|
||||
|
||||
## 1.4.4
|
||||
- Fix expectations on subpackets of v3 signatures (thanks @bjansen)
|
||||
- Properly verify v3 signatures, which do not yet have signature subpackets, yet we required them to have
|
||||
a hashed creation date subpacket.
|
||||
|
||||
## 1.4.3
|
||||
- Bump `sop-java` to `4.1.1`
|
||||
- Reuse shared test suite of `sop-java`
|
||||
- Add `EncryptionOptions.hasEncryptionMethod()`
|
||||
- SOP `encrypt`: Throw `MissingArg` exception if no encryption method was provided
|
||||
- Fix redundant dot in exception message (thanks @DenBond7)
|
||||
|
||||
## 1.4.2
|
||||
- Properly decrypt messages without MDC packets when `ConsumerOptions.setIgnoreMDCErrors(true)` is set
|
||||
- Fix crash in `sop generate-key --with-key-password` when more than one user-id is given
|
||||
- Revert integration with `pgp-certificate-store`
|
||||
- Bump `sop-java` to `4.1.0`
|
||||
|
||||
## 1.4.1
|
||||
- Add `UserId.parse()` method to parse user-ids into their components
|
||||
|
||||
## 1.4.0
|
||||
- `sop generate-key`: Add support for keys without user-ids
|
||||
- `sop inline-sign --as=clearsigned`: Make signature in TEXT mode
|
||||
- Make countermeasures against [KOpenPGP](https://kopenpgp.com/) attacks configurable
|
||||
- Countermeasures are now disabled by default since they are costly and have a specific threat model
|
||||
- Can be enabled by calling `Policy.setEnableKeyParameterValidation(true)`
|
||||
|
||||
## 1.4.0-rc2
|
||||
- Bump `bcpg-jdk15to18` to `1.72.3`
|
||||
- Use BCs `PGPEncryptedDataList.extractSessionKeyEncryptedData()` method
|
||||
to do decryption using session keys. This enables decryption of messages
|
||||
without encrypted session key packets.
|
||||
- Use BCs `PGPEncryptedDataList.isIntegrityProtected()` to check for integrity protection
|
||||
- Depend on `pgp-certificate-store`
|
||||
- Add `ConsumerOptions.addVerificationCerts(PGPCertificateStore)` to allow sourcing certificates from
|
||||
e.g. a [certificate store implementation](https://github.com/pgpainless/cert-d-java).
|
||||
- Make `DecryptionStream.getMetadata()` first class
|
||||
- Deprecate `DecryptionStream.getResult()`
|
||||
|
||||
## 1.4.0-rc1
|
||||
- Reimplement message consumption via new `OpenPgpMessageInputStream`
|
||||
- Fix validation of prepended signatures (#314)
|
||||
- Fix validation of nested signatures (#319)
|
||||
- Reject malformed messages (#237)
|
||||
- Utilize new `PDA` syntax verifier class
|
||||
- Allow for custom message syntax via `Syntax` class
|
||||
- Gracefully handle `UnsupportedPacketVersionException` for signatures
|
||||
- Allow plugin decryption code (e.g. to add support for hardware-backed keys (see #318))
|
||||
- Add `HardwareSecurity` utility class
|
||||
- Add `GnuPGDummyKeyUtil` which can be used to mimic GnuPGs proprietary S2K extensions
|
||||
for keys which were placed on hardware tokens
|
||||
- Add `OpenPgpPacket` enum class to enumerate available packet tags
|
||||
- Remove old decryption classes in favor of new implementation
|
||||
- Removed `DecryptionStream` class and replaced with new abstract class
|
||||
- Removed `DecryptionStreamFactory`
|
||||
- Removed `FinalIOException`
|
||||
- Removed `MissingLiteralDataException` (replaced by `MalformedOpenPgpMessageException`)
|
||||
- Introduce `MessageMetadata` class as potential future replacement for `OpenPgpMetadata`.
|
||||
- can be obtained via `((OpenPgpMessageInputStream) decryptionStream).getMetadata();`
|
||||
- Add `CachingBcPublicKeyDataDecryptorFactory` which can be extended to prevent costly decryption
|
||||
of session keys
|
||||
- Fix: Only verify message integrity once
|
||||
- Remove unnecessary `@throws` declarations on `KeyRingReader` methods
|
||||
- Remove unnecessary `@throws` declarations on `KeyRingUtils` methods
|
||||
- Add `KeyIdUtil.formatKeyId(long id)` to format hexadecimal key-ids.
|
||||
- Add `KeyRingUtils.publicKeys(PGPKeyRing keys)`
|
||||
- Remove `BCUtil` class
|
||||
|
||||
## 1.3.18
|
||||
- Bump `sop-java` to `4.1.2`
|
||||
- Fix `decrypt --verify-with XYZ` not throwing `NoSignature` exception (exit code 3) if `VERIFICATIONS` is empty (#415)
|
||||
|
||||
## 1.3.17
|
||||
- Bugfix: Direct-Key signatures are calculated over the signee key only, not the signer key + signee key
|
||||
- Security: Fix faulty bit-strength policy check for signing subkeys
|
||||
|
||||
## 1.3.16
|
||||
- Bump `sop-java` to `4.1.0`
|
||||
- Bump `gradlew` to `7.5`
|
||||
|
||||
## 1.3.15
|
||||
- Fix crash in `sop generate-key --with-key-password` when more than one user-id is given
|
||||
- `sop generate-key`: Allow key generation without user-ids
|
||||
- `sop inline-sign --as=clearsigned`: Make signatures of type 'text' instead of 'binary'
|
||||
|
||||
## 1.3.14
|
||||
- Bump `bcpg` to `1.72.3`
|
||||
- Fix DSA key parameter check
|
||||
- Use proper method to unlock private signing keys when creating detached signatures
|
||||
|
||||
## 1.3.13
|
||||
- Bump `sop-java` to `4.0.7`
|
||||
|
||||
## 1.3.12
|
||||
- Bump `sop-java` to `4.0.5`
|
||||
- Fix: `sop inline-sign`: Adopt `--as=clearsigned` instead of `--as=cleartextsigned`
|
||||
- SOP: Hide `Version: PGPainless` armor header in all armored outputs
|
||||
- Fix: `sop armor`: Do not re-armor already armored data
|
||||
|
||||
## 1.3.11
|
||||
- Fix: When verifying subkey binding signatures with embedded recycled primary
|
||||
key binding signatures, do not reject signature if primary key binding
|
||||
predates subkey binding
|
||||
- SOP `verify`: Forcefully expect `data()` to be non-OpenPGP data
|
||||
- SOP `sign`: Fix matching of keys and passphrases
|
||||
- CLI: Added tons of tests \o/
|
||||
|
||||
## 1.3.10
|
||||
- Bump `sop-java` to `4.0.3`
|
||||
- Fix: Fix NPE when verifying signature made by key without key flags on direct-key signature
|
||||
|
||||
## 1.3.9
|
||||
- Bump `sop-java` to `4.0.2`
|
||||
- SOP: Improve exception handling
|
||||
|
||||
## 1.3.8
|
||||
- Bump `bcprov` to `1.72`
|
||||
- Bump `bcpg` to `1.72.1`
|
||||
- Add `ProducerOptions.setHideArmorHeaders(boolean)` to hide automatically added armor headers
|
||||
in encrypted messages
|
||||
|
||||
## 1.3.7
|
||||
- Bugfix: Fix signature verification when `DecryptionStream` is drained byte-by-byte using `read()` call
|
||||
- Add `KeyRingUtils.injectCertification(keys, certification)`
|
||||
- Add `PGPainless.asciiArmor(key, outputStream)`
|
||||
- Add `PGPainless.asciiArmor(signature)`
|
||||
|
||||
## 1.3.6
|
||||
- Remove deprecated methods
|
||||
- `ArmorUtils.createArmoredOutputStreamFor()` -> use `ArmorUtils.toAsciiArmoredStream()` instead
|
||||
- `EncryptionResult.getSymmetricKeyAlgorithm()` -> use `EncryptionResult.getEncryptionAlgorithm()` instead
|
||||
- Add `KeyRingInfo.getRevocationState()`
|
||||
- Better way to determine whether a key is revoked
|
||||
- Add `SigningOptions.addDetachedSignature(protector, key)` shortcut method
|
||||
- Add `EncryptionOptions.get()`, `ConsumerOptions.get()` factory methods
|
||||
- Add support for generating keys without user-id (only using `PGPainless.buildKeyRing()` for now)
|
||||
- Switch to `SHA256` as default `S2K` hash algorithm for secret key protection
|
||||
- Allow to set custom reference time when modifying secret keys
|
||||
- Add diagnostic test to explore system PRNG performance
|
||||
|
||||
## 1.3.5
|
||||
- Add `KeyRingInfo.isCapableOfSigning()`
|
||||
- Add `KeyRingReader.readKeyRing(*)` methods that can take both secret- and public keys
|
||||
- Add manpages
|
||||
- Add script to generate manpages from sop-java-picocli
|
||||
- Build website from main branch
|
||||
|
||||
## 1.3.4
|
||||
- Fix `KeyRingInfo.isUsableForEncryption()`, `KeyRingInfo.isUsableForSigning()` not detecting revoked primary keys
|
||||
- Bump `sop-java` and `sop-java-picocli` to `4.0.1`
|
||||
- Fixes help text strings being resolved properly while allowing to override executable name
|
||||
|
||||
## 1.3.3
|
||||
- Improve test compatibility against older JUnit versions
|
||||
- Fix tests that read from jar-embedded resources (thanks @jcharaoui)
|
||||
- `pgpainless-cli help`: Fix i18n strings
|
||||
|
||||
## 1.3.2
|
||||
- Add `KeyRingInfo(Policy)` constructor
|
||||
- Delete unused `KeyRingValidator` class
|
||||
- Add `PGPainless.certify()` API
|
||||
- `certify().userIdOnCertificate()` can be used to certify other users User-IDs
|
||||
- `certify().certificate()` can be used to create direct-key signatures on other users keys
|
||||
- We now have a [User Guide!](https://pgpainless.rtfd.io/)
|
||||
- Fixed build script
|
||||
- `pgpainless-cli`s `gradle build` task no longer builds fat jar
|
||||
- Fat jars are now built by dedicated shadow plugin
|
||||
- Fix third-party assigned user-ids on keys to accidentally get picked up as primary user-id
|
||||
- Add `KeyRingUtils.publicKeyRingCollectionFrom(PGPSecretKeyRingCollection)`
|
||||
- Add `SecretKeyRingEditor.replaceUserId(oldUid, newUid, protector)`
|
||||
- Prevent adding `SymmetricKeyAlgorithm.NULL` (unencrypted) as encryption algo preference when generating keys
|
||||
|
||||
## 1.3.1
|
||||
- Fix reproducibility of builds by setting fixed file permissions in archive task
|
||||
- Improve encryption performance by buffering streams
|
||||
- Fix `OpenPgpMetadata.isEncrypted()` to also return true for symmetrically encrypted messages
|
||||
- SOP changes
|
||||
- decrypt: Do not throw `NoSignatures` if no signatures found
|
||||
- decrypt: Throw `BadData` when ciphertext is not encrypted
|
||||
|
||||
## 1.3.0
|
||||
- Add `RevokedKeyException`
|
||||
- `KeyRingUtils.stripSecretKey()`: Disallow stripping of primary secret key
|
||||
- Remove support for reading compressed detached signatures
|
||||
- Add `PGPainless.generateKeyRing().modernKeyRing(userId)` shortcut method without passphrase
|
||||
- Add `CollectionUtils.addAll(Iterator, Collection)`
|
||||
- Add `SignatureUtils.getSignaturesForUserIdBy(key, userId, keyId)`
|
||||
- Add `OpenPgpFingerprint.parseFromBinary(bytes)`
|
||||
- `SignatureUtils.wasIssuedBy()`: Add support for V5 fingerprints
|
||||
- Prevent integer overflows when setting expiration dates
|
||||
- SOP: Properly throw `KeyCannotDecrypt` exception
|
||||
- Fix performance issues of encrypt and sign operations by using buffering
|
||||
- Fix performance issues of armor and dearmor operations
|
||||
- Bump dependency `sop-java` to `4.0.0`
|
||||
- Add support for SOP specification version 04
|
||||
- Implement `inline-sign`
|
||||
- Implement `inline-verify`
|
||||
- Rename `DetachInbandSignatureAndMessageImpl` to `InlineDetachImpl`
|
||||
- Rename `SignImpl` to `DetachedSignImpl`
|
||||
- Rename `VerifyImpl` to `DetachedVerifyImpl`
|
||||
- Add support for `--with-key-password` option in `GenerateKeyImpl`, `DetachedSignImpl`, `DecryptImpl`, `EncryptImpl`.
|
||||
- `InlineDetachImpl` now supports 3 different message types:
|
||||
- Messages using Cleartext Signature Framework
|
||||
- OpenPGP messages using OnePassSignatures
|
||||
- OpenPGP messages without OnePassSignatures
|
||||
- Introduce `OpenPgpMetadata.isCleartextSigned()`
|
||||
|
||||
## 1.2.2
|
||||
- `EncryptionOptions.addRecipients(collection)`: Disallow empty collections to prevent misuse from resulting in unencrypted messages
|
||||
- Deprecate default policy factory methods in favor of policy factory methods with expressive names
|
||||
- Another fix for OpenPGP data detection
|
||||
- We now inspect the first packet of the data stream to figure out, whether it is plausible OpenPGP data, without exhausting the stream
|
||||
|
||||
## 1.2.1
|
||||
- Bump `sop-java` dependency to `1.2.3`
|
||||
- Bump `slf4j` dependency to `1.7.36`
|
||||
- Bump `logback` dependency to `1.2.11`
|
||||
- Add experimental support for creating signatures over pre-calculated `MessageDigest` objects.
|
||||
- `BcHashContextSigner.signHashContext()` can be used to create OpenPGP signatures over manually hashed data.
|
||||
This allows applications to do the hashing themselves.
|
||||
- Harden detection of binary/ascii armored/non-OpenPGP data
|
||||
- Add `ConsumerOptions.forceNonOpenPgpData()` to force PGPainless to handle data as non-OpenPGP data
|
||||
- This is a workaround for when PGPainless accidentally mistakes non-OpenPGP data for binary OpenPGP data
|
||||
- Implement "smart" hash algorithm policies, which take the 'usage-date' for algorithms into account
|
||||
- This allows for fine-grained signature hash algorithm policing with usage termination dates
|
||||
- Switch to smart signature hash algorithm policies by default
|
||||
- PGPainless now accepts SHA-1 signatures if they were made before 2013-02-01
|
||||
- We also now accept RIPEMD160 signatures if they were made before 2013-02-01
|
||||
- We further accept MD5 signatures made prior to 1997-02-01
|
||||
|
||||
|
||||
## 1.2.0
|
||||
- Improve exception hierarchy for key-related exceptions
|
||||
- See [PR](https://github.com/pgpainless/pgpainless/pull/261) for more information on how to migrate.
|
||||
- Bump Bouncy Castle to `1.71`
|
||||
- Switch from `bcpg-jdk15on:1.70` to `bcpg-jdk15to18:1.71`
|
||||
- Switch from `bcprov-jdk15on:1.70` to `bcprov-jdk15to18:1.71`
|
||||
- Implement merging of certificate copies
|
||||
- can be used to implement updating certificates from key servers
|
||||
- Fix `KeyRingUtils.keysPlusPublicKey()`
|
||||
- Add support for adding `PolicyURI` and `RegularExpression` signature subpackets on signatures
|
||||
|
||||
## 1.1.5
|
||||
- SOP encrypt: match signature type when using `encrypt --as=` option
|
||||
- `ProducerOptions.setEncoding()`: The encoding is henceforth only considered metadata and will no longer trigger CRLF encoding.
|
||||
- This fixes broken signature generation for mismatching (`StreamEncoding`,`DocumentSignatureType`) tuples.
|
||||
- Applications that rely on CRLF-encoding can request PGPainless to apply this encoding by calling `ProducerOptions.applyCRLFEncoding(true)`.
|
||||
- Rename `KeyRingUtils.removeSecretKey()` to `stripSecretKey()`.
|
||||
- Add handy `SignatureOptions.addSignature()` method.
|
||||
- Fix `ClassCastException` when evaluating a certificate with third party signatures. Thanks @p-barabas for the initial report and bug fix!
|
||||
|
||||
## 1.1.4
|
||||
- Add utility method `KeyRingUtils.removeSecretKey()` to remove secret key part from key ring
|
||||
- This can come in handy when using primary keys stored offline
|
||||
- Add `EncryptionResult.isEncryptedFor(certificate)`
|
||||
- `ArmorUtils.toAsciiArmoredString()` methods now print out primary user-id and brief information about further user-ids (thanks @bratkartoffel for the patch)
|
||||
- Methods of `KeyRingUtils` and `ArmorUtils` classes are now annotated with `@Nonnull/@Nullable`
|
||||
- Enums `fromId(code)` methods are now annotated with `@Nullable` and there are now `requireFromId(code)` counterparts which are `@Nonnull`.
|
||||
- `ProducerOptions.setForYourEyesOnly()` is now deprecated (reason is deprecation in the
|
||||
- [crypto-refresh-05](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-05.html#name-special-filename-_console-d) document)
|
||||
- Add `SessionKey.toString()`
|
||||
- Partially fix generation of malformed signature packets when using different combinations of `StreamEncoding` and `DocumentSignatureType` values
|
||||
- Unfortunately PGPainless still produces broken signatures when using either `StreamEncoding.TEXT` or `StreamEncoding.UTF8` in combination with `DocumentSignatureType.BINARY_DOCUMENT`.
|
||||
- Deprecate `ProducerOptions.setEncoding(StreamEncoding)`
|
||||
- Will be removed in a future release
|
||||
- Remove `StreamEncoding.MIME` (was removed from the standard)
|
||||
|
||||
## 1.1.3
|
||||
- Make `SigningOptions.getSigningMethods()` part of internal API
|
||||
- Fix crash when trying to do verification of unmatched `SignersUserId` signature subpacket
|
||||
- For now, verification of `SignersUserId` is disabled but can be enabled via `Policy.setSignerUserIdValidationLevel()`
|
||||
- Initial support for `OpenPgpV5Fingerprint`
|
||||
- Add `OpenPgpFingerprint.parse(string)`
|
||||
- Security: Fix `KeyRingInfo.getValidAndExpiredUserIds()` accidentally including unbound user-ids
|
||||
|
||||
## 1.0.5
|
||||
- Security: Fix `KeyRingInfo.getValidAndExpiredUserIds()` accidentally including unbound user-ids
|
||||
|
||||
## 1.1.2
|
||||
- Fix `keyRingInfo.getEmailAddresses()` incorrectly matching some mail addresses (thanks @bratkartoffel for reporting and initial patch proposal)
|
||||
- Fix generic type of `CertificationSubpackets.Callback`
|
||||
- Add `KeyRingInfo.isUsableForEncryption()`
|
||||
- Add `PGPainless.inspectKeyRing(key, date)`
|
||||
- Allow custom key creation dates during key generation
|
||||
- Reject subkeys with bindings that predate key generation
|
||||
- `EncryptionOptions.addRecipient()`: Transform `NoSuchElementException` into `IllegalArgumentException` with proper error message
|
||||
- Fix `ClassCastException` by preventing accidental verification of 3rd-party-issued user-id revocation with primary key.
|
||||
- Fix `NullPointerException` when trying to verify malformed signature
|
||||
|
||||
## 1.1.1
|
||||
- Add `producerOptions.setComment(string)` to allow adding ASCII armor comments when creating OpenPGP messages (thanks @ferenc-hechler)
|
||||
- Simplify consumption of cleartext-signed data
|
||||
- Change default criticality of signature subpackets
|
||||
- Issuer Fingerprint: critical -> non-critical
|
||||
- Revocable: non-critical -> critical
|
||||
- Issuer KeyID: critical -> non-critical
|
||||
- Preferred Algorithms: critical -> non-critical
|
||||
- Revocation Reason: critical -> non-critical
|
||||
|
||||
## 1.1.0
|
||||
- `pgpainless-sop`: Update `sop-java` to version 1.2.0
|
||||
- Treat passwords and session keys as indirect parameters
|
||||
This means they are no longer treated as string input, but pointers to files or env variables
|
||||
|
||||
## 1.0.4
|
||||
- Yet another patch for faulty ASCII armor detection 😒
|
||||
|
||||
## 1.0.3
|
||||
- Fix detection of unarmored data in signature verification
|
||||
|
||||
## 1.0.2
|
||||
- Update SOP implementation to specification revision 03
|
||||
- Move `sop-java` and `sop-java-picocli` modules to [its own repository](https://github.com/pgpainless/sop-java)
|
||||
- `OpenPGPV4Fingerprint`: Hex decode bytes in constructor
|
||||
- Add `ArmorUtils.toAsciiArmoredString()` for single key
|
||||
- Fix `ClassCastException` when retrieving `RevocationKey` subpackets from signatures
|
||||
- Fix `pgpainless-sop` gradle script
|
||||
- it now automatically pulls in transitive dependencies
|
||||
|
||||
## 1.0.1
|
||||
- Fix sourcing of preferred algorithms by primary user-id when key is located via key-id
|
||||
|
||||
## 1.0.0
|
||||
- Introduce `DateUtil.toSecondsPrecision()`
|
||||
- Clean JUnit tests, fix code style issues and fix typos in documentation
|
||||
|
||||
## 1.0.0-rc9
|
||||
- When key has both direct-key sig + primary user-id sig: resolve expiration date to the earliest expiration
|
||||
- Add `SecretKeyRingEditor.removeUserId()` convenience methods that do soft-revoke the user-id.
|
||||
- Add `SelectUserId.byEmail()` which also matches the plain email address
|
||||
|
||||
## 1.0.0-rc8
|
||||
- `KeyRingInfo.getPrimaryUserId()`: return first user-id when no primary user-id is found
|
||||
- Rename method `getBoundButPossiblyExpiredUserIds` to `getValidAndExpiredUserIds()`
|
||||
- Remove audit resource material
|
||||
|
||||
## 1.0.0-rc7
|
||||
- Make `Passphrase` comparison constant time
|
||||
- Bump Bouncycastle to 1.70
|
||||
- Use new `PGPCanonicalizedDataGenerator` where applicable
|
||||
- Implement decryption with user-provided session key
|
||||
- Remove workaround for invalid signature processing
|
||||
- Remove Blowfish from default symmetric decryption/encryption policy
|
||||
- When adding/generating keys: Check compliance to `PublicKeyAlgorithmPolicy`
|
||||
- Fix `BaseSecretKeyRingProtector` misinterpreting empty passphrases
|
||||
- SOP: Fix NPE when attempting to sign with key with missing signing subkey
|
||||
- Describe Threat Model in [pgpainless-core/README.md]
|
||||
- Fix NPE when attempting to decrypt GNU_DUMMY_S2K key
|
||||
- Validate public key parameters when unlocking secret keys
|
||||
- Introduce iteration limits to prevent resource exhaustion when
|
||||
- reading signatures
|
||||
- reading keys
|
||||
- `CachingSecretKeyRingProtector`: Prevent accidental passphrase overriding via `addPassphrase()`
|
||||
- `EncryptionOptions`: replace method argument type `PGPPublicKeyRingCollection` with `Iterable<PGPPublicKeyRing>` to allow for `Collection<PGPPublicKeyRing>` as argument
|
||||
- `SigningOptions`: replace method argument type `PGPSecretKeyRingCollection` with `Iterable<PGPSecretKeyRing>` to allow for `Collection<PGPSecretKeyRing>` as argument
|
||||
- Prevent message decryption with non-encryption subkey
|
||||
- Rework key modification API to fix inconsistency problems with expiration and primary user-ids.
|
||||
- Remove methods to change expiration dates of subkeys and specific user-ids
|
||||
- Rework primary user-id marking logic to unmark non-primary ids
|
||||
- Added [Cure53 Security Audit Report](https://gh.pgpainless.org/assets/Audit-PGPainless.pdf) to the website
|
||||
- Reworked tests for cryptographic backend to use custom `InvocationContextProvider` implementation
|
||||
- Source `PGPObjectFactory` objects from `ImplementationProvider`
|
||||
- Fix typo `getCommendHeader() -> getCommentHeader()`
|
||||
|
||||
## 1.0.0-rc6
|
||||
- Restructure method arguments in `SecretKeyRingEditor`
|
||||
- Add explanations of revocation reasons to `RevocationAttributes`
|
||||
- Rename `CertificationSignatureBuilder` to `ThirdPartyCertificationSignatureBuilder`
|
||||
- `KeyAccessor.ViaKeyId`: Differentiate between primary key (rely on direct-key sig) and subkey (subkey binding sig)
|
||||
- Expose `SignatureSubpacketsUtil.getKeyLifetimeInSeconds`
|
||||
- Various cleanup steps and new tests
|
||||
|
||||
## 1.0.0-rc5
|
||||
- Fix invalid cursor mark in `BufferedInputStream` when processing large cleartext signed messages
|
||||
- Add `SecretKeyRingEditor.revokeUserIds(SelectUserId, SecretKeyRingProtector, RevocationSignatureSubpackets.Callback)`
|
||||
|
||||
## 1.0.0-rc4
|
||||
- Fix bug where `KeyRingBuilder` would mark additional user-ids as primary
|
||||
|
||||
## 1.0.0-rc3
|
||||
- New Signature builder API for more fine-grained control over key-signatures:
|
||||
- Introduce `CertificationSignatureSubpackets` builder class to wrap `PGPSignatureSubpacketGenerator` for
|
||||
certification style signatures.
|
||||
- Introduce `SelfSignatureSubpackets` builder class for self-signatures.
|
||||
- Introduce `RevocationSignatureSubpackets` builder class for revocation signatures.
|
||||
- Introduce `CertificationSignatureSubpackets.Callback`, `SelfSignatureSubpackets.Callback` and
|
||||
`RevocationSignatureSubpackets.Callback` to allow modification of signature subpackets by the user.
|
||||
- Incorporate `*SignatureSubpackets.Callback` classes as arguments in `SecretKeyRingEditor` and `KeyRingBuilder` methods.
|
||||
- Start working on `ProofUtil` to create KeyOxide style identity proofs (WIP)
|
||||
- Move Signature verification related code to `org.pgpainless.signature.consumer` package
|
||||
- Ensure keyflags and other common subpackets are set in new signatures when adding user-ids
|
||||
- Ensure subkey can carry keyflag when adding it to a key
|
||||
- Refactor `SecretKeyRingProtector` methods and code
|
||||
|
||||
## 1.0.0-rc2
|
||||
- `SecretKeyRingEditor`: Remove support for user-id- and subkey *deletion* in favor of *revocation*
|
||||
- Deletion causes all sorts of problems. Most notably, receiving implementations will not honor deletion of user-ids/subkeys.
|
||||
If you really need to delete user-ids there now is `KeyRingUtils.deleteUserId(keys, userid)`,
|
||||
but its use is highly discouraged and should only (if ever) be used for local manipulations of keys.
|
||||
- `pgpainless-core` & `pgpainless-sop`: Fix accidental compile scope dependency on `logback-classic`
|
||||
- `KeyRingInfo`: Sensible arguments for methods to get preferred algorithms
|
||||
|
||||
## 1.0.0-rc1
|
||||
- First release candidate for a 1.0.0 release! \o/
|
||||
- Rename `EncryptionPurpose.STORAGE_AND_COMMUNICATIONS` to `EncryptionPurpose.ANY`
|
||||
- Hide `PGPainless.verifyCleartextSignedMessage()` behind `PGPainless.decryptAndVerify()`.
|
||||
- the latter now checks whether the message is cleartext-signed or not and automatically calls the proper API
|
||||
- `MultiPassStrategy` objects are now set through `ConsumerOptions.setMultiPassStrategy()`.
|
||||
- Separate key ring generation through templates from custom key ring builder
|
||||
- `PGPainless.generateKeyRing()` now offers to generate keys from templates
|
||||
- `PGPainless.buildKeyRing()` offers a detailed API to build custom keys
|
||||
- Fix detection of non-armored data
|
||||
|
||||
## 0.2.19
|
||||
- Some preparations for OpenPGP V5 keys: `OpenPgpV4Fingerprint` is now an implementation of `OpenPgpFingerprint`
|
||||
- `SignatureVerification` and `Failure` now have `toString()` implementations
|
||||
- Logging: `logback-classic` is now an optional runtime dependency
|
||||
|
||||
## 0.2.18
|
||||
- Fix compatibility with PGPainless < 0.2.10
|
||||
- Fix interoperability with Kleopatra
|
||||
- Decryption: Do not skip over first PKESKs when we have a matching decryption key
|
||||
- MessageInspector: Break from object factory loop after encountering encrypted data (we cannot go deeper)
|
||||
- Move hash algorithm negotiation to own class
|
||||
- Change return value of `EncryptionOptions.overrideEncryptionAlgorithm()`
|
||||
|
||||
## 0.2.17
|
||||
- Fix prematurely throwing `MissingPassphraseException` when decrypting message with multiple possible keys and passphrases
|
||||
|
||||
## 0.2.16
|
||||
- Fix handling of subkey revocation signatures
|
||||
- SOP: improve API usage with byte arrays
|
||||
- Fix `AssertionError` when determining encryption subkeys from set containing unbound key
|
||||
- Add `ConsumerOptions.setMissingKeyPassphraseStrategy(strategy)` to modify behavior when missing key passphrases are encountered during decryption
|
||||
|
||||
## 0.2.15
|
||||
- Add `ConsumerOptions.setIgnoreMDCErrors()` which can be used to consume broken messages. Not recommended!
|
||||
- Add `MessageInspector.isSignedOnly()` which can be used to identify messages created via `gpg --sign --armor`
|
||||
- Workaround for BCs `PGPUtil.getDecoderStream` mistaking plaintext for base64 encoded data
|
||||
- Cleanup of unused internal methods
|
||||
- SOP: Fix `ArmorImpl` writing data to provided output stream instead of `System.out`
|
||||
- Fix hen and egg problem with streams in signature detaching implementation of SOP
|
||||
- Make code [REUSE](https://reuse.software) compliant
|
||||
|
||||
## 0.2.14
|
||||
- Export dependency on Bouncycastle's `bcprov-jdk15on`
|
||||
- Rework Key Generation API
|
||||
- Replace builder-chain structure with single `KeyRingBuilder` class
|
||||
- Change return value of `CleartextSignatureProcessor.process()` to `DecryptionStream`
|
||||
- Rename `CleartextSignatureProcessor.process()` to `CleartextSignatureProcessor.getVerificationStream()`
|
||||
- Add support for creating cleartext signed messages by calling `ProducerOptions.setCleartextSigned()`
|
||||
- Add examples for signing messages in the `examples` package.
|
||||
|
||||
## 0.2.13
|
||||
- Add `MessageInspector` class to determine IDs of recipient keys.
|
||||
- PGPainless now tries decryption using keys with available passphrases first and only then request key passphrases using callbacks.
|
||||
|
||||
## 0.2.12
|
||||
- Fix: Add workaround for BC defaulting to S2K `USAGE_CHECKSUM` by changing S2K usage to `USAGE_SHA1`
|
||||
- Repair keys with `USAGE_CHECKSUM` when changing passphrase
|
||||
|
||||
## 0.2.11
|
||||
- Fix: When changing expiration date of keys, also consider generic and casual certifications
|
||||
|
||||
|
|
1
CNAME
1
CNAME
|
@ -1 +0,0 @@
|
|||
gh.pgpainless.org
|
|
@ -1,9 +1,3 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2021 Paul Schaub <info@pgpainless.org>
|
||||
|
||||
SPDX-License-Identifier: CC0-1.0
|
||||
-->
|
||||
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
|
42
LICENSE
42
LICENSE
|
@ -175,31 +175,27 @@
|
|||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
============================================================================
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
# Licenses for included dependencies
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
## [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0)
|
||||
* info.picocli:picocli
|
||||
* com.google.code.findbugs:jsr305
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
## [Bouncycastle License](https://www.bouncycastle.org/licence.html)
|
||||
* org.bouncycastle:bcprov-jdk15on
|
||||
* org.bouncycastle:bcpg-jdk15on
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
## [Eclipse Public License 1.0](https://www.eclipse.org/legal/epl-1.0/)
|
||||
* ch.qos.logback:logback-classic
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
## [Eclipe Public License 2.0](https://www.eclipse.org/legal/epl-2.0/)
|
||||
* org.junit.jupiter:junit-jupiter-api
|
||||
* org.junit.jupiter:junit-jupiter-params
|
||||
* org.junit.jupiter:junit-jupiter-engine
|
||||
|
||||
## [LPGL-2.1](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.de.html)
|
||||
* ch.qos.logback:logback-classic
|
||||
|
||||
## [MIT License](https://opensource.org/licenses/MIT)
|
||||
* com.ginsberg:junit5-system-exit
|
||||
* org.slf4j:slf4j-api
|
||||
* org.slf4j:slf4j-nop
|
||||
* org.mockito:mockito-core
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -1,93 +0,0 @@
|
|||
Creative Commons Attribution 3.0 Unported
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
|
||||
|
||||
License
|
||||
|
||||
THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
|
||||
|
||||
BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
|
||||
|
||||
1. Definitions
|
||||
|
||||
a. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License.
|
||||
|
||||
b. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License.
|
||||
|
||||
c. "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership.
|
||||
|
||||
d. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License.
|
||||
|
||||
e. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast.
|
||||
|
||||
f. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work.
|
||||
|
||||
g. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
|
||||
|
||||
h. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images.
|
||||
|
||||
i. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium.
|
||||
|
||||
2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws.
|
||||
|
||||
3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
|
||||
|
||||
a. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections;
|
||||
|
||||
b. to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified.";
|
||||
|
||||
c. to Distribute and Publicly Perform the Work including as incorporated in Collections; and,
|
||||
|
||||
d. to Distribute and Publicly Perform Adaptations.
|
||||
|
||||
e. For the avoidance of doubt:
|
||||
|
||||
i. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
|
||||
|
||||
ii. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and,
|
||||
|
||||
iii. Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License.
|
||||
|
||||
The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved.
|
||||
|
||||
4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
|
||||
|
||||
a. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(b), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(b), as requested.
|
||||
|
||||
b. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4 (b) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties.
|
||||
|
||||
c. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise.
|
||||
|
||||
5. Representations, Warranties and Disclaimer
|
||||
|
||||
UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
|
||||
|
||||
6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
7. Termination
|
||||
|
||||
a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
|
||||
|
||||
b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
|
||||
|
||||
8. Miscellaneous
|
||||
|
||||
a. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
|
||||
|
||||
b. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
|
||||
|
||||
c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
|
||||
|
||||
d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You.
|
||||
|
||||
e. This License may not be modified without the mutual written agreement of the Licensor and You.
|
||||
|
||||
f. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law.
|
||||
|
||||
Creative Commons Notice
|
||||
|
||||
Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
|
||||
|
||||
Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of this License.
|
||||
|
||||
Creative Commons may be contacted at http://creativecommons.org/.
|
|
@ -1,99 +0,0 @@
|
|||
Creative Commons Attribution-ShareAlike 3.0 Unported
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
|
||||
|
||||
License
|
||||
|
||||
THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
|
||||
|
||||
BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
|
||||
|
||||
1. Definitions
|
||||
|
||||
a. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License.
|
||||
|
||||
b. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined below) for the purposes of this License.
|
||||
|
||||
c. "Creative Commons Compatible License" means a license that is listed at http://creativecommons.org/compatiblelicenses that has been approved by Creative Commons as being essentially equivalent to this License, including, at a minimum, because that license: (i) contains terms that have the same purpose, meaning and effect as the License Elements of this License; and, (ii) explicitly permits the relicensing of adaptations of works made available under that license under this License or a Creative Commons jurisdiction license with the same License Elements as this License.
|
||||
|
||||
d. "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership.
|
||||
|
||||
e. "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike.
|
||||
|
||||
f. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License.
|
||||
|
||||
g. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast.
|
||||
|
||||
h. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work.
|
||||
|
||||
i. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
|
||||
|
||||
j. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images.
|
||||
|
||||
k. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium.
|
||||
|
||||
2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws.
|
||||
|
||||
3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
|
||||
|
||||
a. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections;
|
||||
|
||||
b. to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified.";
|
||||
|
||||
c. to Distribute and Publicly Perform the Work including as incorporated in Collections; and,
|
||||
|
||||
d. to Distribute and Publicly Perform Adaptations.
|
||||
|
||||
e. For the avoidance of doubt:
|
||||
|
||||
i. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
|
||||
|
||||
ii. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and,
|
||||
|
||||
iii. Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License.
|
||||
|
||||
The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved.
|
||||
|
||||
4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
|
||||
|
||||
a. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(c), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(c), as requested.
|
||||
|
||||
b. You may Distribute or Publicly Perform an Adaptation only under the terms of: (i) this License; (ii) a later version of this License with the same License Elements as this License; (iii) a Creative Commons jurisdiction license (either this or a later license version) that contains the same License Elements as this License (e.g., Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible License. If you license the Adaptation under one of the licenses mentioned in (iv), you must comply with the terms of that license. If you license the Adaptation under the terms of any of the licenses mentioned in (i), (ii) or (iii) (the "Applicable License"), you must comply with the terms of the Applicable License generally and the following provisions: (I) You must include a copy of, or the URI for, the Applicable License with every copy of each Adaptation You Distribute or Publicly Perform; (II) You may not offer or impose any terms on the Adaptation that restrict the terms of the Applicable License or the ability of the recipient of the Adaptation to exercise the rights granted to that recipient under the terms of the Applicable License; (III) You must keep intact all notices that refer to the Applicable License and to the disclaimer of warranties with every copy of the Work as included in the Adaptation You Distribute or Publicly Perform; (IV) when You Distribute or Publicly Perform the Adaptation, You may not impose any effective technological measures on the Adaptation that restrict the ability of a recipient of the Adaptation from You to exercise the rights granted to that recipient under the terms of the Applicable License. This Section 4(b) applies to the Adaptation as incorporated in a Collection, but this does not require the Collection apart from the Adaptation itself to be made subject to the terms of the Applicable License.
|
||||
|
||||
c. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Ssection 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4(c) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties.
|
||||
|
||||
d. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise.
|
||||
|
||||
5. Representations, Warranties and Disclaimer
|
||||
|
||||
UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
|
||||
|
||||
6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
7. Termination
|
||||
|
||||
a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
|
||||
|
||||
b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
|
||||
|
||||
8. Miscellaneous
|
||||
|
||||
a. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
|
||||
|
||||
b. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
|
||||
|
||||
c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
|
||||
|
||||
d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
|
||||
|
||||
e. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
|
||||
|
||||
f. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law.
|
||||
|
||||
Creative Commons Notice
|
||||
|
||||
Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
|
||||
|
||||
Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of the License.
|
||||
|
||||
Creative Commons may be contacted at http://creativecommons.org/.
|
|
@ -1,121 +0,0 @@
|
|||
Creative Commons Legal Code
|
||||
|
||||
CC0 1.0 Universal
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||
HEREUNDER.
|
||||
|
||||
Statement of Purpose
|
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for
|
||||
the purpose of contributing to a commons of creative, cultural and
|
||||
scientific works ("Commons") that the public can reliably and without fear
|
||||
of later claims of infringement build upon, modify, incorporate in other
|
||||
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||
and for any purposes, including without limitation commercial purposes.
|
||||
These owners may contribute to the Commons to promote the ideal of a free
|
||||
culture and the further production of creative, cultural and scientific
|
||||
works, or to gain reputation or greater distribution for their Work in
|
||||
part through the use and efforts of others.
|
||||
|
||||
For these and/or other purposes and motivations, and without any
|
||||
expectation of additional consideration or compensation, the person
|
||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not
|
||||
limited to, the following:
|
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display,
|
||||
communicate, and translate a Work;
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
iii. publicity and privacy rights pertaining to a person's image or
|
||||
likeness depicted in a Work;
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||
in a Work;
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation
|
||||
thereof, including any amended or successor version of such
|
||||
directive); and
|
||||
vii. other similar, equivalent or corresponding rights throughout the
|
||||
world based on applicable law or treaty, and any national
|
||||
implementations thereof.
|
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||
of action, whether now known or unknown (including existing as well as
|
||||
future claims and causes of action), in the Work (i) in all territories
|
||||
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||
treaty (including future time extensions), (iii) in any current or future
|
||||
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||
including without limitation commercial, advertising or promotional
|
||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||
member of the public at large and to the detriment of Affirmer's heirs and
|
||||
successors, fully intending that such Waiver shall not be subject to
|
||||
revocation, rescission, cancellation, termination, or any other legal or
|
||||
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||
as contemplated by Affirmer's express Statement of Purpose.
|
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||
be judged legally invalid or ineffective under applicable law, then the
|
||||
Waiver shall be preserved to the maximum extent permitted taking into
|
||||
account Affirmer's express Statement of Purpose. In addition, to the
|
||||
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||
maximum duration provided by applicable law or treaty (including future
|
||||
time extensions), (iii) in any current or future medium and for any number
|
||||
of copies, and (iv) for any purpose whatsoever, including without
|
||||
limitation commercial, advertising or promotional purposes (the
|
||||
"License"). The License shall be deemed effective as of the date CC0 was
|
||||
applied by Affirmer to the Work. Should any part of the License for any
|
||||
reason be judged legally invalid or ineffective under applicable law, such
|
||||
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||
of the License, and in such case Affirmer hereby affirms that he or she
|
||||
will not (i) exercise any of his or her remaining Copyright and Related
|
||||
Rights in the Work or (ii) assert any associated claims and causes of
|
||||
action with respect to the Work, in either case contrary to Affirmer's
|
||||
express Statement of Purpose.
|
||||
|
||||
4. Limitations and Disclaimers.
|
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
b. Affirmer offers the Work as-is and makes no representations or
|
||||
warranties of any kind concerning the Work, express, implied,
|
||||
statutory or otherwise, including without limitation warranties of
|
||||
title, merchantability, fitness for a particular purpose, non
|
||||
infringement, or the absence of latent or other defects, accuracy, or
|
||||
the present or absence of errors, whether or not discoverable, all to
|
||||
the greatest extent permissible under applicable law.
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without
|
||||
limitation any person's Copyright and Related Rights in the Work.
|
||||
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||
consents, permissions or other rights required for any use of the
|
||||
Work.
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to
|
||||
this CC0 or use of the Work.
|
128
README.md
128
README.md
|
@ -1,64 +1,39 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2021 Paul Schaub <info@pgpainless.org>
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
# PGPainless - Use OpenPGP Painlessly!
|
||||
|
||||
[](https://github.com/pgpainless/pgpainless/actions/workflows/gradle_push.yml)
|
||||
[](https://coveralls.io/github/pgpainless/pgpainless?branch=main)
|
||||
[](https://tests.sequoia-pgp.org/)
|
||||
[](https://keyoxide.org/7F9116FEA90A5983936C7CFAA027DB2F3E1E118A)
|
||||
[](https://api.reuse.software/info/github.com/pgpainless/pgpainless)
|
||||
[](https://pgpainless.readthedocs.io/en/latest/?badge=latest)
|
||||
|
||||
**PGPainless is an easy-to-use OpenPGP library for Java and Android applications**
|
||||
|
||||
[](https://repology.org/project/pgpainless/versions)
|
||||
[](https://travis-ci.com/pgpainless/pgpainless)
|
||||
[](https://search.maven.org/artifact/org.pgpainless/pgpainless-core)
|
||||
|
||||
[](https://coveralls.io/github/pgpainless/pgpainless?branch=master)
|
||||
[](https://pgpainless.org/releases/latest/javadoc/)
|
||||
[](https://tests.sequoia-pgp.org/)
|
||||
[](https://keyoxide.org/7F9116FEA90A5983936C7CFAA027DB2F3E1E118A)
|
||||
## About
|
||||
|
||||
PGPainless aims to make using OpenPGP in Java projects as simple as possible.
|
||||
It does so by introducing an intuitive Builder structure, which allows easy
|
||||
setup of encryption/decryption operations, as well as straight forward key generation.
|
||||
setup of encryptionOptions / decryption operations, as well as straight forward key generation.
|
||||
|
||||
PGPainless is based around the Bouncy Castle java library and can be used on Android down to API level 10.
|
||||
It can be configured to either use the Java Cryptographic Engine (JCE), or Bouncy Castles lightweight reimplementation.
|
||||
PGPainless is based around the Bouncycastle java library and can be used on Android down to API level 10.
|
||||
It can be configured to either use the Java Cryptographic Engine (JCE), or Bouncycastles lightweight reimplementation.
|
||||
|
||||
While signature verification in Bouncy Castle is limited to signature correctness, PGPainless goes much further.
|
||||
While signature verification in Bouncycastle is limited to signature correctness, PGPainless goes much further.
|
||||
It also checks if signing subkeys are properly bound to their primary key, if keys are expired or revoked, as well as
|
||||
if keys are allowed to create signatures in the first place.
|
||||
|
||||
These rigorous checks make PGPainless stand out from other Java-based OpenPGP libraries and are the reason why
|
||||
PGPainless currently [*scores first place* on Sequoia-PGPs Interoperability Test-Suite](https://tests.sequoia-pgp.org).
|
||||
PGPainless currently [*scores second place* on Sequoia-PGPs Interoperability Test-Suite](https://tests.sequoia-pgp.org).
|
||||
|
||||
> At FlowCrypt we are using PGPainless in our Kotlin code bases on Android and on server side.
|
||||
> The ergonomics of legacy PGP tooling on Java is not very good, and PGPainless improves it greatly.
|
||||
> The ergonomy of legacy PGP tooling on Java is not very good, and PGPainless improves it greatly.
|
||||
> We were so happy with our initial tests and with Paul - the maintainer, that we decided to sponsor further development of this library.
|
||||
>
|
||||
> -Tom @ FlowCrypt.com
|
||||
|
||||
> Finally, testing irrefutably confirmed that the library removes many associated difficulties with PGP use in its provision of an approachable and uncomplicated API.
|
||||
> In this regard, Paul Schaub deserves the utmost praise.
|
||||
>
|
||||
> -Mario @ Cure53.de
|
||||
|
||||
## Get Started
|
||||
|
||||
The very easiest way to start using OpenPGP on Java/Kotlin based systems is to use an implementation of [sop-java](https://github.com/pgpainless/sop-java).
|
||||
`sop-java` defines a very stripped down API and is super easy to get started with.
|
||||
Luckily PGPainless provides an implementation for the `sop-java` interface definitions in the form of [pgpainless-sop](pgpainless-sop/README.md).
|
||||
|
||||
If you need more flexibility, directly using `pgpainless-core` is the way to go.
|
||||
|
||||
## Features
|
||||
|
||||
Most of PGPainless' features can be accessed directly from the `PGPainless` class.
|
||||
If you want to get started, this class is your friend :)
|
||||
|
||||
For further details you should check out the [javadoc](https://javadoc.io/doc/org.pgpainless/pgpainless-core)!
|
||||
For further details you should check out the [javadoc](https://pgpainless.org/releases/latest/javadoc/)!
|
||||
|
||||
### Handle Keys
|
||||
Reading keys from ASCII armored strings or from binary files is easy:
|
||||
|
@ -99,20 +74,27 @@ There are some predefined key archetypes, but it is possible to fully customize
|
|||
.modernKeyRing("Romeo <romeo@montague.lit>", "I defy you, stars!");
|
||||
|
||||
// Customized key
|
||||
PGPSecretKeyRing keyRing = PGPainless.buildKeyRing()
|
||||
.setPrimaryKey(KeySpec.getBuilder(
|
||||
RSA.withLength(RsaLength._8192),
|
||||
KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER))
|
||||
.addSubkey(
|
||||
KeySpec.getBuilder(ECDSA.fromCurve(EllipticCurve._P256), KeyFlag.SIGN_DATA)
|
||||
.overrideCompressionAlgorithms(CompressionAlgorithm.ZLIB)
|
||||
).addSubkey(
|
||||
KeySpec.getBuilder(
|
||||
ECDH.fromCurve(EllipticCurve._P256),
|
||||
KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE)
|
||||
).addUserId("Juliet <juliet@montague.lit>")
|
||||
.addUserId("xmpp:juliet@capulet.lit")
|
||||
.setPassphrase(Passphrase.fromPassword("romeo_oh_Romeo<3"))
|
||||
PGPSecretKeyRing keyRing = PGPainless.generateKeyRing()
|
||||
.withSubKey(
|
||||
KeySpec.getBuilder(ECDSA.fromCurve(EllipticCurve._P256))
|
||||
.withKeyFlags(KeyFlag.SIGN_DATA)
|
||||
.withDetailedConfiguration()
|
||||
.withDefaultSymmetricAlgorithms()
|
||||
.withDefaultHashAlgorithms()
|
||||
.withPreferredCompressionAlgorithms(CompressionAlgorithm.ZLIB)
|
||||
.withFeature(Feature.MODIFICATION_DETECTION)
|
||||
.done()
|
||||
).withSubKey(
|
||||
KeySpec.getBuilder(ECDH.fromCurve(EllipticCurve._P256))
|
||||
.withKeyFlags(KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE)
|
||||
.withDefaultAlgorithms()
|
||||
).withMasterKey(
|
||||
KeySpec.getBuilder(RSA.withLength(RsaLength._8192))
|
||||
.withKeyFlags(KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER)
|
||||
.withDefaultAlgorithms()
|
||||
).withPrimaryUserId("Juliet <juliet@montague.lit>")
|
||||
.withAdditionalUserId("xmpp:juliet@capulet.lit")
|
||||
.withPassphrase("romeo_oh_Romeo<3")
|
||||
.build();
|
||||
```
|
||||
|
||||
|
@ -132,7 +114,7 @@ Still it allows you to manually specify which algorithms to use of course.
|
|||
.addRecipient(aliceKey)
|
||||
.addRecipient(bobsKey)
|
||||
// optionally encrypt to a passphrase
|
||||
.addMessagePassphrase(Passphrase.fromPassword("password123"))
|
||||
.addPassphrase(Passphrase.fromPassword("password123"))
|
||||
// optionally override symmetric encryption algorithm
|
||||
.overrideEncryptionAlgorithm(SymmetricKeyAlgorithm.AES_192),
|
||||
new SigningOptions()
|
||||
|
@ -147,9 +129,6 @@ Still it allows you to manually specify which algorithms to use of course.
|
|||
|
||||
Streams.pipeAll(plaintextInputStream, encryptionStream);
|
||||
encryptionStream.close();
|
||||
|
||||
// Information about the encryption (algorithms, detached signatures etc.)
|
||||
EncryptionResult result = encryptionStream.getResult();
|
||||
```
|
||||
|
||||
### Decrypt and Verify Signatures
|
||||
|
@ -172,10 +151,10 @@ This behaviour can be modified though using the `Policy` class.
|
|||
decryptionStream.close();
|
||||
|
||||
// Result contains information like signature status etc.
|
||||
MessageMetadata metadata = decryptionStream.getMetadata();
|
||||
OpenPgpMetadata metadata = decryptionStream.getResult();
|
||||
```
|
||||
|
||||
*After* the `DecryptionStream` was closed, you can get metadata about the processed data by retrieving the `MessageMetadata`.
|
||||
*After* the `DecryptionStream` was closed, you can get metadata about the processed data by retrieving the `OpenPgpMetadata`.
|
||||
Again, this object will contain information about how the message was encrypted, who signed it and so on.
|
||||
|
||||
#### Many more examples can be found in the [examples package](pgpainless-core/src/test/java/org/pgpainless/example)!!!
|
||||
|
@ -191,38 +170,25 @@ repositories {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'org.pgpainless:pgpainless-core:1.7.6'
|
||||
implementation 'org.pgpainless:pgpainless-core:0.2.11'
|
||||
}
|
||||
```
|
||||
|
||||
## Professional Support
|
||||
Do you need a custom feature? Are you unsure of what's the best way to integrate PGPainless into your product?
|
||||
We offer paid professional services. Don't hesitate to send an inquiry to [info@pgpainless.org](mailto:info@pgpainless.org).
|
||||
## About
|
||||
PGPainless is a by-product of my [Summer of Code 2018 project](https://blog.jabberhead.tk/summer-of-code-2018/).
|
||||
For that project I was in need of a simple to use OpenPGP library.
|
||||
|
||||
Originally I was going to use [Bouncy-GPG](https://github.com/neuhalje/bouncy-gpg) for my project,
|
||||
but ultimately I decided to create my own OpenPGP library which better fits my needs.
|
||||
|
||||
However, PGPainless is heavily influenced by Bouncy-GPG.
|
||||
|
||||
To reach out to the development team, feel free to send a mail: info@pgpainless.org
|
||||
|
||||
## Development
|
||||
Join the projects IRC channel [**#pgpainless**](ircs://irc.oftc.net:6697/#pgpainless) on OFTC if you have any questions!
|
||||
|
||||
PGPainless is developed in - and accepts contributions from - the following places:
|
||||
|
||||
* [Github](https://github.com/pgpainless/pgpainless)
|
||||
* [Codeberg](https://codeberg.org/PGPainless/pgpainless)
|
||||
|
||||
We are using SemVer (MAJOR.MINOR.PATCH) versioning, although MINOR releases could contain breaking changes from time to time.
|
||||
|
||||
If you want to contribute a bug fix, please check the `release/X.Y` branches first to see, what the oldest release is
|
||||
which contains the bug you are fixing. That way we can update older revisions of the library easily.
|
||||
|
||||
Please follow the [code of conduct](CODE_OF_CONDUCT.md) if you want to be part of the project.
|
||||
|
||||
## Acknowledgements
|
||||
Development on PGPainless is generously sponsored by [FlowCrypt.com](https://flowcrypt.com). Thank you very very very much!
|
||||
[](https://flowcrypt.com)
|
||||
|
||||
Parts of PGPainless development ([project page](https://nlnet.nl/project/PGPainless/)) will be funded by [NGI Assure](https://nlnet.nl/assure/) through [NLNet](https://nlnet.nl).
|
||||
NGI Assure is made possible with financial support from the [European Commission](https://ec.europa.eu/)'s [Next Generation Internet](https://ngi.eu/) programme, under the aegis of [DG Communications Networks, Content and Technology](https://ec.europa.eu/info/departments/communications-networks-content-and-technology_en).
|
||||
[](https://nlnet.nl/assure/)
|
||||
|
||||
Big thank you also to those who decided to support the work by donating!
|
||||
Notably @msfjarvis
|
||||
|
||||
You make my day!
|
||||
|
|
118
REUSE.toml
118
REUSE.toml
|
@ -1,118 +0,0 @@
|
|||
version = 1
|
||||
SPDX-PackageName = "PGPainless"
|
||||
SPDX-PackageSupplier = "Paul Schaub <info@pgpainless.org>"
|
||||
SPDX-PackageDownloadLocation = "https://pgpainless.org"
|
||||
|
||||
[[annotations]]
|
||||
path = "REUSE.toml"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2025 Paul Schaub <info@pgpainless.org>"
|
||||
SPDX-License-Identifier = "CC0-1.0"
|
||||
|
||||
[[annotations]]
|
||||
path = ".git-blame-ignore-revs"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2023 Paul Schaub <info@pgpainless.org>"
|
||||
SPDX-License-Identifier = "CC0-1.0"
|
||||
|
||||
[[annotations]]
|
||||
path = "docs/**"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2022 Paul Schaub <info@pgpainless.org>"
|
||||
SPDX-License-Identifier = "CC-BY-3.0"
|
||||
|
||||
[[annotations]]
|
||||
path = ".readthedocs.yaml"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2022 Paul Schaub <info@pgpainless.org>"
|
||||
SPDX-License-Identifier = "CC0-1.0"
|
||||
|
||||
[[annotations]]
|
||||
path = "gradle**"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2015 the original author or authors."
|
||||
SPDX-License-Identifier = "Apache-2.0"
|
||||
|
||||
[[annotations]]
|
||||
path = ".editorconfig"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "Facebook"
|
||||
SPDX-License-Identifier = "Apache-2.0"
|
||||
|
||||
[[annotations]]
|
||||
path = "assets/repository-open-graph.png"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2021 Paul Schaub <info@pgpainless.org>"
|
||||
SPDX-License-Identifier = "CC-BY-3.0"
|
||||
|
||||
[[annotations]]
|
||||
path = "assets/pgpainless.svg"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2021 Paul Schaub <info@pgpainless.org>"
|
||||
SPDX-License-Identifier = "CC-BY-3.0"
|
||||
|
||||
[[annotations]]
|
||||
path = "assets/logo.png"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2022 Paul Schaub <info@pgpainless.org>"
|
||||
SPDX-License-Identifier = "CC-BY-3.0"
|
||||
|
||||
[[annotations]]
|
||||
path = "assets/test_vectors/**"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2018 Paul Schaub <info@pgpainless.org>"
|
||||
SPDX-License-Identifier = "CC0-1.0"
|
||||
|
||||
[[annotations]]
|
||||
path = "pgpainless-core/src/test/resources/**"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2020 Paul Schaub <info@pgpainless.org>"
|
||||
SPDX-License-Identifier = "CC0-1.0"
|
||||
|
||||
[[annotations]]
|
||||
path = "audit/**"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2021 Paul Schaub <info@pgpainless.org>"
|
||||
SPDX-License-Identifier = "CC0-1.0"
|
||||
|
||||
[[annotations]]
|
||||
path = "CNAME"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2022 Paul Schaub <info@pgpainless.org>"
|
||||
SPDX-License-Identifier = "CC0-1.0"
|
||||
|
||||
[[annotations]]
|
||||
path = "_config.yml"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2022 Paul Schaub <info@pgpainless.org>"
|
||||
SPDX-License-Identifier = "CC0-1.0"
|
||||
|
||||
[[annotations]]
|
||||
path = "_layouts/**"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2022 Paul Schaub <info@pgpainless.org>, 2017 Steve Smith"
|
||||
SPDX-License-Identifier = "CC-BY-SA-3.0"
|
||||
|
||||
[[annotations]]
|
||||
path = "pgpainless-cli/src/main/resources/META-INF/native-image/**"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2025 Paul Schaub <info@pgpainless.org>"
|
||||
SPDX-License-Identifier = "Apache-2.0"
|
||||
|
||||
[[annotations]]
|
||||
path = "pgpainless-cli/rewriteManPages.sh"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2022 Paul Schaub <info@pgpainless.org>"
|
||||
SPDX-License-Identifier = "Apache-2.0"
|
||||
|
||||
[[annotations]]
|
||||
path = "pgpainless-cli/packaging/man/**"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2022 Paul Schaub <info@pgpainless.org>"
|
||||
SPDX-License-Identifier = "Apache-2.0"
|
||||
|
||||
[[annotations]]
|
||||
path = ".github/ISSUE_TEMPLATE/**"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2024 Paul Schaub <info@pgpainless.org>"
|
||||
SPDX-License-Identifier = "CC0-1.0"
|
37
SECURITY.md
37
SECURITY.md
|
@ -1,37 +0,0 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2021 Paul Schaub <info@pgpainless.org>
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Use this section to tell people about which versions of your project are
|
||||
currently being supported with security updates.
|
||||
|
||||
| Version | Supported | Note |
|
||||
|---------|--------------------|------------|
|
||||
| 1.7.X | :white_check_mark: | |
|
||||
| 1.6.X | :white_check_mark: | LTS branch |
|
||||
| 1.5.X | :white_check_mark: | |
|
||||
| 1.4.X | :white_check_mark: | |
|
||||
| 1.3.X | :white_check_mark: | LTS branch |
|
||||
| < 1.3.X | :x: | |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you find a security relevant vulnerability inside PGPainless, please let me know!
|
||||
[Here](https://keyoxide.org/7F9116FEA90A5983936C7CFAA027DB2F3E1E118A) you can find my OpenPGP key to email me confidentially.
|
||||
|
||||
Valid security issues will be fixed ASAP.
|
||||
|
||||
## Audits
|
||||
|
||||
### Cure53 - FLO-04
|
||||
PGPainless has received a security audit by [cure53.de](https://cure53.de) in late 2021.
|
||||
The [penetrationj test and audit](https://cure53.de/pentest-report_pgpainless.pdf) covered PGPainless
|
||||
release candidate 1.0.0-rc6.
|
||||
Security fixes for discovered flaws were deployed before the final 1.0.0 release.
|
|
@ -1,8 +0,0 @@
|
|||
logo: /assets/logo.png
|
||||
theme: jekyll-theme-minimal
|
||||
|
||||
exclude:
|
||||
- CHANGELOG.md
|
||||
- CODE_OF_CONDUCT.md
|
||||
- SECURITY.md
|
||||
- docs
|
|
@ -1,76 +0,0 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="chrome=1">
|
||||
<title>{{ site.title | default: site.github.repository_name }} by {{
|
||||
site.github.owner_name }}</title>
|
||||
|
||||
<link rel="stylesheet" href="{{ '/assets/css/style.css?v=' | append:
|
||||
site.github.build_revision | relative_url }}">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<!--[if lt IE 9]>
|
||||
<script
|
||||
src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper" style="width: 1060px">
|
||||
<header>
|
||||
{% if site.logo %}
|
||||
<img src="{{site.logo | relative_url}}" alt="Logo" />
|
||||
{% endif %}
|
||||
<p>{{ site.description | default: site.github.project_tagline
|
||||
}}</p>
|
||||
|
||||
<a href="https://pgpainless.org">Home</a>
|
||||
<br>
|
||||
<a href="https://search.maven.org/search?q=g:org.pgpainless%20AND%20a:pgpainless-core&core=gav">Releases</a>
|
||||
<br>
|
||||
<a href="https://pgpainless.rtfd.io">Documentation</a>
|
||||
<br>
|
||||
<a href=" https://javadoc.io/doc/org.pgpainless ">Javadoc</a>
|
||||
<br>
|
||||
<a href="https://coveralls.io/github/pgpainless/pgpainless">Coverage</a>
|
||||
<br>
|
||||
|
||||
|
||||
{% if site.github.is_project_page %}
|
||||
<p class="view"><a href="{{ site.github.repository_url
|
||||
}}">View the Project on GitHub <small>{{ github_name }}</small></a></p>
|
||||
{% endif %}
|
||||
|
||||
{% if site.github.is_user_page %}
|
||||
<p class="view"><a href="{{ site.github.owner_url }}">View My
|
||||
GitHub Profile</a></p>
|
||||
{% endif %}
|
||||
|
||||
{% if site.show_downloads %}
|
||||
<ul>
|
||||
<li><a href="{{ site.github.zip_url }}">Download <strong>ZIP
|
||||
File</strong></a></li>
|
||||
<li><a href="{{ site.github.tar_url }}">Download <strong>TAR
|
||||
Ball</strong></a></li>
|
||||
<li><a href="{{ site.github.repository_url }}">View On
|
||||
<strong>GitHub</strong></a></li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
</header>
|
||||
<section style="width: 780px">
|
||||
|
||||
{{ content }}
|
||||
|
||||
</section>
|
||||
<footer>
|
||||
{% if site.github.is_project_page %}
|
||||
<p>This project is maintained by <a href="{{
|
||||
site.github.owner_url }}">{{ site.github.owner_name }}</a></p>
|
||||
{% endif %}
|
||||
<p><small>Hosted on GitHub Pages — Theme by <a
|
||||
href="https://github.com/orderedlist">orderedlist</a></small></p>
|
||||
</footer>
|
||||
</div>
|
||||
<script src="{{ '/assets/js/scale.fix.js' | relative_url
|
||||
}}"></script>
|
||||
</body>
|
||||
</html>
|
BIN
assets/logo.png
BIN
assets/logo.png
Binary file not shown.
Before Width: | Height: | Size: 14 KiB |
|
@ -1,110 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
height="48px"
|
||||
viewBox="0 0 24 24"
|
||||
width="48px"
|
||||
fill="#000000"
|
||||
version="1.1"
|
||||
id="svg893"
|
||||
sodipodi:docname="pgpainless.svg"
|
||||
inkscape:version="1.0.2 (e86c870879, 2021-01-15)">
|
||||
<metadata
|
||||
id="metadata899">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs897" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1056"
|
||||
id="namedview895"
|
||||
showgrid="true"
|
||||
inkscape:zoom="17.895833"
|
||||
inkscape:cx="24"
|
||||
inkscape:cy="21.764843"
|
||||
inkscape:window-x="1920"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg893">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid1629" />
|
||||
</sodipodi:namedview>
|
||||
<g
|
||||
fill="none"
|
||||
id="g889">
|
||||
<path
|
||||
d="M0 0h24v24H0V0z"
|
||||
id="path885" />
|
||||
<path
|
||||
d="M0 0h24v24H0V0z"
|
||||
opacity=".87"
|
||||
id="path887" />
|
||||
</g>
|
||||
<path
|
||||
d="M 18,8 H 17 V 6 C 17,3.24 14.76,1 12,1 9.24,1 7,3.24 7,6 V 8 H 6 C 4.9,8 4,8.9 4,10 v 10 c 0,1.1 0.9,2 2,2 h 12 c 1.1,0 2,-0.9 2,-2 V 10 C 20,8.9 19.1,8 18,8 Z M 9,6 c 0,-1.66 1.34,-3 3,-3 1.66,0 3,1.34 3,3 V 8 H 9 Z"
|
||||
id="path891"
|
||||
sodipodi:nodetypes="scssscsssssssssssccs" />
|
||||
<g
|
||||
style="fill:#ffffff"
|
||||
id="g1627"
|
||||
transform="matrix(0.93317811,0,0,0.93317811,2.6775507,5.7004657)">
|
||||
<g
|
||||
id="g1594">
|
||||
<rect
|
||||
fill="none"
|
||||
height="20"
|
||||
width="20"
|
||||
x="0"
|
||||
id="rect1592"
|
||||
y="0" />
|
||||
</g>
|
||||
<g
|
||||
id="g1608">
|
||||
<g
|
||||
id="g1596" />
|
||||
<g
|
||||
id="g1606">
|
||||
<path
|
||||
d="m 10,14 c 1.86,0 3.41,-1.28 3.86,-3 H 6.14 c 0.45,1.72 2,3 3.86,3 z"
|
||||
id="path1598" />
|
||||
<path
|
||||
d="M 9.99,3 C 6.13,3 3,6.14 3,10 3,13.86 6.13,17 9.99,17 13.86,17 17,13.86 17,10 17,6.14 13.86,3 9.99,3 Z m 0,13 C 6.69,16 4,13.31 4,10 4,6.69 6.69,4 9.99,4 13.31,4 16,6.69 16,10 c 0,3.31 -2.69,6 -6.01,6 z"
|
||||
id="path1600" />
|
||||
<circle
|
||||
cx="13"
|
||||
cy="8"
|
||||
r="1"
|
||||
id="circle1602" />
|
||||
<circle
|
||||
cx="7"
|
||||
cy="8"
|
||||
r="1"
|
||||
id="circle1604" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 3 KiB |
Binary file not shown.
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 57 KiB |
88
build.gradle
88
build.gradle
|
@ -1,7 +1,3 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <info@pgpainless.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
buildscript {
|
||||
|
||||
repositories {
|
||||
|
@ -18,8 +14,7 @@ buildscript {
|
|||
}
|
||||
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.jvm' version "1.8.10"
|
||||
id 'com.diffplug.spotless' version '6.22.0' apply false
|
||||
id 'ru.vyarus.animalsniffer' version '1.5.3'
|
||||
}
|
||||
|
||||
apply from: 'version.gradle'
|
||||
|
@ -30,58 +25,43 @@ allprojects {
|
|||
apply plugin: 'eclipse'
|
||||
apply plugin: 'jacoco'
|
||||
apply plugin: 'checkstyle'
|
||||
apply plugin: 'kotlin'
|
||||
apply plugin: 'com.diffplug.spotless'
|
||||
|
||||
// Only generate jar for submodules
|
||||
// without this we would generate an empty pgpainless.jar for the project root
|
||||
// https://stackoverflow.com/a/25445035
|
||||
jar {
|
||||
reproducibleFileOrder = true
|
||||
onlyIf { !sourceSets.main.allSource.files.isEmpty() }
|
||||
// For non-sop modules, enable android api compatibility check
|
||||
if (it.name.equals('pgpainless-core') || it.name.equals('sop-java') || it.name.equals('pgpainless-sop')) {
|
||||
// animalsniffer
|
||||
apply plugin: 'ru.vyarus.animalsniffer'
|
||||
dependencies {
|
||||
signature "net.sf.androidscents.signature:android-api-level-${pgpainlessMinAndroidSdk}:2.3.3_r2@signature"
|
||||
}
|
||||
animalsniffer {
|
||||
sourceSets = [sourceSets.main]
|
||||
}
|
||||
}
|
||||
|
||||
// checkstyle
|
||||
checkstyle {
|
||||
toolVersion = '10.25.0'
|
||||
}
|
||||
|
||||
spotless {
|
||||
kotlin {
|
||||
ktfmt().dropboxStyle()
|
||||
}
|
||||
toolVersion = '8.18'
|
||||
}
|
||||
|
||||
group 'org.pgpainless'
|
||||
description = "Simple to use OpenPGP API for Java based on Bouncycastle"
|
||||
version = shortVersion
|
||||
|
||||
sourceCompatibility = javaSourceCompatibility
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
// Reproducible Builds
|
||||
tasks.withType(AbstractArchiveTask) {
|
||||
preserveFileTimestamps = false
|
||||
reproducibleFileOrder = true
|
||||
|
||||
dirMode = 0755
|
||||
fileMode = 0644
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(javaSourceCompatibility)
|
||||
}
|
||||
|
||||
// Compatibility of default implementations in kotlin interfaces with Java implementations.
|
||||
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
|
||||
kotlinOptions {
|
||||
freeCompilerArgs += ["-Xjvm-default=all-compatibility"]
|
||||
}
|
||||
}
|
||||
|
||||
project.ext {
|
||||
slf4jVersion = '1.7.32'
|
||||
junitVersion = '5.7.2'
|
||||
rootConfigDir = new File(rootDir, 'config')
|
||||
gitCommit = getGitCommit()
|
||||
isContinuousIntegrationEnvironment = Boolean.parseBoolean(System.getenv('CI'))
|
||||
|
@ -106,7 +86,7 @@ allprojects {
|
|||
}
|
||||
|
||||
jacoco {
|
||||
toolVersion = "0.8.8"
|
||||
toolVersion = "0.8.7"
|
||||
}
|
||||
|
||||
jacocoTestReport {
|
||||
|
@ -114,7 +94,7 @@ allprojects {
|
|||
sourceDirectories.setFrom(project.files(sourceSets.main.allSource.srcDirs))
|
||||
classDirectories.setFrom(project.files(sourceSets.main.output))
|
||||
reports {
|
||||
xml.required = true
|
||||
xml.enabled true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -132,15 +112,15 @@ subprojects {
|
|||
apply plugin: 'signing'
|
||||
|
||||
task sourcesJar(type: Jar, dependsOn: classes) {
|
||||
archiveClassifier = 'sources'
|
||||
classifier = 'sources'
|
||||
from sourceSets.main.allSource
|
||||
}
|
||||
task javadocJar(type: Jar, dependsOn: javadoc) {
|
||||
archiveClassifier = 'javadoc'
|
||||
classifier = 'javadoc'
|
||||
from javadoc.destinationDir
|
||||
}
|
||||
task testsJar(type: Jar, dependsOn: testClasses) {
|
||||
archiveClassifier = 'tests'
|
||||
classifier = 'tests'
|
||||
from sourceSets.test.output
|
||||
}
|
||||
|
||||
|
@ -237,7 +217,7 @@ task jacocoRootReport(type: JacocoReport) {
|
|||
classDirectories.setFrom(files(subprojects.sourceSets.main.output))
|
||||
executionData.setFrom(files(subprojects.jacocoTestReport.executionData))
|
||||
reports {
|
||||
xml.required = true
|
||||
xml.enabled true
|
||||
xml.destination file("${buildDir}/reports/jacoco/test/jacocoTestReport.xml")
|
||||
}
|
||||
// We could remove the following setOnlyIf line, but then
|
||||
|
@ -248,6 +228,10 @@ task jacocoRootReport(type: JacocoReport) {
|
|||
}
|
||||
|
||||
task javadocAll(type: Javadoc) {
|
||||
def currentJavaVersion = JavaVersion.current()
|
||||
if (currentJavaVersion.compareTo(JavaVersion.VERSION_1_9) >= 0) {
|
||||
options.addStringOption("-release", "8");
|
||||
}
|
||||
source subprojects.collect {project ->
|
||||
project.sourceSets.main.allJava }
|
||||
destinationDir = new File(buildDir, 'javadoc')
|
||||
|
@ -260,23 +244,3 @@ task javadocAll(type: Javadoc) {
|
|||
"https://docs.oracle.com/javase/${sourceCompatibility.getMajorVersion()}/docs/api/",
|
||||
] as String[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch sha256 checksums of artifacts published to maven central.
|
||||
*
|
||||
* Example: gradle -Prelease=1.3.13 mavenCentralChecksums
|
||||
*/
|
||||
task mavenCentralChecksums() {
|
||||
description 'Fetch and display checksums for artifacts published to Maven Central'
|
||||
String ver = project.hasProperty('release') ? release : shortVersion
|
||||
doLast {
|
||||
for (Project p : rootProject.subprojects) {
|
||||
String url = "https://repo1.maven.org/maven2/org/pgpainless/${p.name}/${ver}/${p.name}-${ver}.jar.sha256"
|
||||
Process fetch = "curl -f $url".execute()
|
||||
if (fetch.waitFor() == 0) {
|
||||
print fetch.text.trim()
|
||||
println " ${p.name}/build/libs/${p.name}-${ver}.jar"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2021 Paul Schaub <info@pgpainless.org>
|
||||
|
||||
SPDX-License-Identifier: CC0-1.0
|
||||
-->
|
||||
|
||||
<!DOCTYPE module PUBLIC
|
||||
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
|
||||
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
|
||||
|
@ -15,6 +9,13 @@ SPDX-License-Identifier: CC0-1.0
|
|||
<property name="file" value="${config_loc}/suppressions.xml"/>
|
||||
</module>
|
||||
|
||||
<!-- License Header -->
|
||||
<module name="Header">
|
||||
<property name="headerFile" value="${config_loc}/checkstyleLicenseHeader.txt"/>
|
||||
<property name="ignoreLines" value="2"/>
|
||||
<property name="fileExtensions" value="java"/>
|
||||
</module>
|
||||
|
||||
<module name="NewlineAtEndOfFile">
|
||||
<property name="lineSeparator" value="lf"/>
|
||||
</module>
|
||||
|
@ -114,12 +115,18 @@ SPDX-License-Identifier: CC0-1.0
|
|||
</module>
|
||||
|
||||
<module name="JavadocMethod">
|
||||
<property name="accessModifiers" value="public"/>
|
||||
<!-- TODO stricten those checks -->
|
||||
<property name="scope" value="public"/>
|
||||
<!--<property name="allowUndeclaredRTE" value="true"/>-->
|
||||
<property name="allowMissingParamTags" value="true"/>
|
||||
<property name="allowMissingThrowsTags" value="true"/>
|
||||
<property name="allowMissingReturnTag" value="true"/>
|
||||
<property name="allowMissingJavadoc" value="true"/>
|
||||
<property name="suppressLoadErrors" value="true"/>
|
||||
</module>
|
||||
|
||||
<module name="JavadocStyle">
|
||||
<property name="scope" value="public"/>
|
||||
<property name="checkEmptyJavadoc" value="true"/>
|
||||
<property name="checkHtml" value="false"/>
|
||||
</module>
|
||||
|
|
15
config/checkstyle/checkstyleLicenseHeader.txt
Normal file
15
config/checkstyle/checkstyleLicenseHeader.txt
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright 2018 Author Authorsson.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
|
@ -1,10 +1,4 @@
|
|||
<?xml version="1.0"?>
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2021 Paul Schaub <info@pgpainless.org>
|
||||
|
||||
SPDX-License-Identifier: CC0-1.0
|
||||
-->
|
||||
|
||||
<!DOCTYPE suppressions PUBLIC
|
||||
"-//Puppy Crawl//DTD Suppressions 1.1//EN"
|
||||
"http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = source
|
||||
BUILDDIR = build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
@ -1,31 +0,0 @@
|
|||
# User Guide for PGPainless
|
||||
|
||||
Documentation for PGPainless is built from Markdown using Sphinx and MyST.
|
||||
|
||||
A built version of the documentation is available on http://pgpainless.rtfd.io/
|
||||
|
||||
## Useful resources
|
||||
|
||||
* [Sphix Documentation Generator](https://www.sphinx-doc.org/en/master/)
|
||||
* [MyST Markdown Syntax](https://myst-parser.readthedocs.io/en/latest/index.html)
|
||||
|
||||
## Build the Guide
|
||||
|
||||
To build:
|
||||
|
||||
```shell
|
||||
$ make {html|epub|latexpdf}
|
||||
```
|
||||
|
||||
Note: Diagrams are currently not built from source.
|
||||
Instead, pre-built image files are used directly, because there are issues with mermaid in CLI systems.
|
||||
|
||||
If you want to build the diagrams from source, you need `mermaid-cli` to be installed on your system.
|
||||
```shell
|
||||
$ npm install -g @mermaid-js/mermaid-cli
|
||||
```
|
||||
|
||||
You can then use `mmdc` to build/update single diagram files like this:
|
||||
```shell
|
||||
mmdc --theme default --width 1600 --backgroundColor transparent -i ecosystem_dia.md -o ecosystem_dia.svg
|
||||
```
|
|
@ -1,35 +0,0 @@
|
|||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=source
|
||||
set BUILDDIR=build
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
|
||||
:end
|
||||
popd
|
|
@ -1,3 +0,0 @@
|
|||
myst-parser>=0.17
|
||||
sphinxcontrib-mermaid>=0.7.1
|
||||
sphinx_rtd_theme>=2.0.0
|
|
@ -1,56 +0,0 @@
|
|||
import os
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
|
||||
# -- Project information
|
||||
|
||||
project = 'PGPainless'
|
||||
copyright = '2022, Paul Schaub'
|
||||
author = 'Paul Schaub'
|
||||
|
||||
master_doc = 'index'
|
||||
|
||||
# https://protips.readthedocs.io/git-tag-version.html
|
||||
latest_tag = os.popen('git describe --abbrev=0').read().strip()
|
||||
release = latest_tag
|
||||
version = release
|
||||
|
||||
myst_substitutions = {
|
||||
"repo_host" : "codeberg.org",
|
||||
# "repo_host" : "github.com",
|
||||
"repo_pgpainless_src" : "codeberg.org/pgpainless/pgpainless/src/branch",
|
||||
# "repo_pgpainless_src" : "github.com/pgpainless/pgpainless/tree",
|
||||
}
|
||||
|
||||
# -- General configuration
|
||||
|
||||
extensions = [
|
||||
'myst_parser',
|
||||
'sphinxcontrib.mermaid',
|
||||
'sphinx.ext.duration',
|
||||
'sphinx.ext.doctest',
|
||||
'sphinx.ext.autodoc',
|
||||
'sphinx.ext.autosummary',
|
||||
]
|
||||
|
||||
source_suffix = ['.rst', '.md']
|
||||
|
||||
myst_enable_extensions = [
|
||||
'colon_fence',
|
||||
'substitution',
|
||||
]
|
||||
|
||||
myst_heading_anchors = 3
|
||||
|
||||
templates_path = ['_templates']
|
||||
|
||||
# -- Options for HTML output
|
||||
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
|
||||
# Show URLs as footnotes
|
||||
#epub_show_urls = 'footnote'
|
||||
latex_show_urls = 'footnote'
|
||||
|
||||
# 'raw' does not work for epub and pdf, neither does 'svg'
|
||||
mermaid_output_format = 'png'
|
||||
mermaid_params = ['--theme', 'default', '--width', '1600', '--backgroundColor', 'transparent']
|
|
@ -1,59 +0,0 @@
|
|||
# The PGPainless Ecosystem
|
||||
|
||||
PGPainless consists of an ecosystem of different libraries and projects.
|
||||
|
||||
The diagram below shows, how the different projects relate to one another.
|
||||
|
||||

|
||||
<!--
|
||||
```{include} ecosystem_dia.md
|
||||
```
|
||||
-->
|
||||
|
||||
## Libraries and Tools
|
||||
|
||||
* {{ '[PGPainless](https://{}/pgpainless/pgpainless)'.format(repo_host) }}
|
||||
The main repository contains the following components:
|
||||
* `pgpainless-core` - core implementation - powerful, yet easy to use OpenPGP API
|
||||
* `pgpainless-sop` - super simple OpenPGP implementation. Drop-in for `sop-java`
|
||||
* `pgpainless-cli` - SOP CLI implementation using PGPainless
|
||||
|
||||
* {{ '[SOP-Java](https://{}/pgpainless/sop-java)'.format(repo_host) }}
|
||||
An API definition and CLI implementation of the [Stateless OpenPGP Protocol](https://datatracker.ietf.org/doc/draft-dkg-openpgp-stateless-cli/) (SOP).
|
||||
Consumers of the SOP API can simply depend on `sop-java` and then switch out the backend as they wish.
|
||||
Read more about the [SOP](sop) protocol here.
|
||||
* `sop-java` - generic OpenPGP API definition
|
||||
* `sop-java-picocli` - CLI frontend for `sop-java`
|
||||
|
||||
* {{ '[WKD-Java](https://{}/pgpainless/wkd-java)'.format(repo_host) }}
|
||||
Implementation of the [Web Key Directory](https://www.ietf.org/archive/id/draft-koch-openpgp-webkey-service-13.html).
|
||||
* `wkd-java` - generic WKD discovery implementation
|
||||
* `wkd-java-cli` - CLI frontend for WKD discovery using PGPainless
|
||||
* `wkd-test-suite` - Generator for test vectors for testing WKD implementations
|
||||
|
||||
* {{ '[VKS-Java](https://{}/pgpainless/vks-java)'.format(repo_host) }}
|
||||
Client-side API for communicating with Verifying Key Servers, such as https://keys.openpgp.org/.
|
||||
* `vks-java` - VKS client implementation
|
||||
* `vks-java-cli` - CLI frontend for `vks-java`
|
||||
|
||||
* {{ '[Cert-D-Java](https://{}/pgpainless/cert-d-java)'.format(repo_host) }}
|
||||
Implementations of the [Shared OpenPGP Certificate Directory specification](https://sequoia-pgp.gitlab.io/pgp-cert-d/).
|
||||
* `pgp-certificate-store` - abstract definitions of OpenPGP certificate stores
|
||||
* `pgp-cert-d-java` - implementation of `pgp-certificate-store` following the PGP-CERT-D spec
|
||||
* `pgp-cert-d-java-jdbc-sqlite-lookup` - subkey lookup using sqlite database
|
||||
|
||||
* {{ '[Cert-D-PGPainless](https://{}/pgpainless/cert-d-pgpainless)'.format(repo_host) }}
|
||||
Implementation of the [Shared OpenPGP Certificate Directory specification](https://sequoia-pgp.gitlab.io/pgp-cert-d/) using PGPainless.
|
||||
* `pgpainless-cert-d` - PGPainless-based implementation of `pgp-cert-d-java`
|
||||
* `pgpainless-cert-d-cli` - CLI frontend for `pgpainless-cert-d`
|
||||
|
||||
* {{ '[PGPainless-WOT](https://{}/pgpainless/pgpainless-wot)'.format(repo_host) }}
|
||||
Implementation of the [OpenPGP Web of Trust specification](https://sequoia-pgp.gitlab.io/sequoia-wot/) using PGPainless.
|
||||
* `pgpainless-wot` - Parse OpenPGP keyrings into a generic `Network` object
|
||||
* `wot-dijkstra` - Perform queries to find paths inside a `Network` object
|
||||
* `pgpainless-wot-cli` - CLI frontend for `pgpainless-wot` and `wot-dijkstra`
|
||||
* `wot-test-suite` - Test vectors ported from [Sequoia-PGPs WoT implementation](https://gitlab.com/sequoia-pgp/sequoia-wot/-/tree/main/tests/data)
|
||||
|
||||
* {{ '[PGPeasy](https://{}/pgpainless/pgpeasy)'.format(repo_host) }}
|
||||
Prototypical, comprehensive OpenPGP CLI application
|
||||
* `pgpeasy` - CLI application
|
|
@ -1,45 +0,0 @@
|
|||
```mermaid
|
||||
flowchart LR
|
||||
subgraph SOP-JAVA
|
||||
sop-java-picocli-->sop-java
|
||||
end
|
||||
subgraph PGPAINLESS
|
||||
pgpainless-sop-->pgpainless-core
|
||||
pgpainless-sop-->sop-java
|
||||
pgpainless-cli-->pgpainless-sop
|
||||
pgpainless-cli-->sop-java-picocli
|
||||
end
|
||||
subgraph WKD-JAVA
|
||||
wkd-java-cli-->wkd-java
|
||||
wkd-test-suite-->wkd-java
|
||||
wkd-test-suite-->pgpainless-core
|
||||
end
|
||||
subgraph CERT-D-JAVA
|
||||
pgp-cert-d-java-->pgp-certificate-store
|
||||
pgp-cert-d-java-jdbc-sqlite-lookup-->pgp-cert-d-java
|
||||
end
|
||||
subgraph CERT-D-PGPAINLESS
|
||||
pgpainless-cert-d-->pgpainless-core
|
||||
pgpainless-cert-d-->pgp-cert-d-java
|
||||
pgpainless-cert-d-cli-->pgpainless-cert-d
|
||||
pgpainless-cert-d-cli-->pgp-cert-d-java-jdbc-sqlite-lookup
|
||||
end
|
||||
subgraph VKS-JAVA
|
||||
vks-java-cli-->vks-java
|
||||
end
|
||||
subgraph PGPAINLESS-WOT
|
||||
wot-test-suite-->pgpainless-wot
|
||||
pgpainless-wot-->wot-dijkstra
|
||||
pgpainless-wot-cli-->pgpainless-wot
|
||||
pgpainless-wot-->pgpainless-core
|
||||
pgpainless-wot-cli-->pgpainless-cert-d
|
||||
end
|
||||
subgraph PGPEASY
|
||||
pgpeasy-->pgpainless-cli
|
||||
pgpeasy-->wkd-java-cli
|
||||
pgpeasy-->vks-java-cli
|
||||
pgpeasy-->pgpainless-cert-d-cli
|
||||
end
|
||||
wkd-java-cli-->pgpainless-cert-d
|
||||
wkd-java-->pgp-certificate-store
|
||||
```
|
Binary file not shown.
Before Width: | Height: | Size: 110 KiB |
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 41 KiB |
|
@ -1,38 +0,0 @@
|
|||
PGPainless - Painless OpenPGP
|
||||
=============================
|
||||
|
||||
**OpenPGP** (`RFC 4480 <https://datatracker.ietf.org/doc/rfc4880/>`_) is an Internet Standard mostly used for email
|
||||
encryption.
|
||||
It provides mechanisms to ensure *confidentiality*, *integrity* and *authenticity* of messages.
|
||||
However, OpenPGP can also be used for other purposes, such as secure messaging or as a signature mechanism for
|
||||
software distribution.
|
||||
|
||||
**PGPainless** strives to improve the (currently pretty dire) state of the ecosystem of Java libraries and tooling
|
||||
for OpenPGP.
|
||||
|
||||
The library focuses on being easy and intuitive to use without getting into your way.
|
||||
Common functions such as creating keys, encrypting data, and so on are implemented using a builder structure that
|
||||
guides you through the necessary steps.
|
||||
|
||||
Internally, it is based on `Bouncy Castles <https://www.bouncycastle.org/java.html>`_ mighty, but low-level ``bcpg``
|
||||
OpenPGP API.
|
||||
PGPainless' goal is to empower you to use OpenPGP without needing to write all the boilerplate code required by
|
||||
Bouncy Castle.
|
||||
It aims to be secure by default while allowing customization if required.
|
||||
|
||||
From its inception in 2018 as part of a `Google Summer of Code project <https://summerofcode.withgoogle.com/archive/2018/projects/6037508810866688>`_,
|
||||
the library was steadily advanced.
|
||||
Since 2020, FlowCrypt is the primary sponsor of its development.
|
||||
In 2022, PGPainless received a `grant from NLnet for creating a Web-of-Trust implementation <https://nlnet.nl/project/PGPainless/>`_ as part of NGI Assure.
|
||||
|
||||
|
||||
Contents
|
||||
--------
|
||||
|
||||
.. toctree::
|
||||
|
||||
ecosystem.md
|
||||
quickstart.md
|
||||
pgpainless-cli/usage.md
|
||||
sop.md
|
||||
pgpainless-core/indepth.rst
|
|
@ -1,163 +0,0 @@
|
|||
# User Guide PGPainless-CLI
|
||||
|
||||
The module `pgpainless-cli` contains a command line application which conforms to the
|
||||
[Stateless OpenPGP Command Line Interface](https://datatracker.ietf.org/doc/draft-dkg-openpgp-stateless-cli/).
|
||||
|
||||
You can use it to generate keys, encrypt, sign and decrypt messages, as well as verify signatures.
|
||||
|
||||
## Implementation
|
||||
|
||||
Essentially, `pgpainless-cli` is just a very small composing module, which injects `pgpainless-sop` as a
|
||||
concrete implementation of `sop-java` into `sop-java-picocli`.
|
||||
|
||||
## Install
|
||||
|
||||
The `pgpainless-cli` command line application is available in Debian unstable / Ubuntu 22.10 and can be installed via APT:
|
||||
```shell
|
||||
$ sudo apt install pgpainless-cli
|
||||
```
|
||||
|
||||
This method comes with man-pages:
|
||||
```shell
|
||||
$ man pgpainless-cli
|
||||
```
|
||||
|
||||
## Build
|
||||
|
||||
To build a standalone *fat*-jar:
|
||||
```shell
|
||||
$ cd pgpainless-cli/
|
||||
$ gradle shadowJar
|
||||
```
|
||||
|
||||
The fat-jar can afterwards be found in `build/libs/`.
|
||||
|
||||
To build a [distributable](https://docs.gradle.org/current/userguide/distribution_plugin.html):
|
||||
|
||||
```shell
|
||||
$ cd pgpainless-cli/
|
||||
$ gradle installDist
|
||||
```
|
||||
|
||||
Afterwards, an uncompressed distributable is installed in `build/install/`.
|
||||
To execute the application, you can call `build/install/bin/pgpainless-cli{.bat}`
|
||||
|
||||
Building / updating man pages is a two-step process.
|
||||
The contents of the man pages is largely defined by the `sop-java-picocli` source code.
|
||||
|
||||
In order to generate a fresh set of man pages from the `sop-java-picocli` source, you need to clone that repository
|
||||
next to the `pgpainless` repository:
|
||||
```shell
|
||||
$ ls
|
||||
pgpainless
|
||||
$ git clone https://github.com/pgpainless/sop-java.git
|
||||
$ ls
|
||||
pgpainless sop-java
|
||||
```
|
||||
|
||||
Next, you need to execute the `asciiDoctor` gradle task inside the sop-java repository:
|
||||
```shell
|
||||
$ cd sop-java
|
||||
$ gradle asciiDoctor
|
||||
```
|
||||
|
||||
This will generate generic sop manpages in `sop-java-picocli/build/docs/manpage/`.
|
||||
|
||||
Next, you need to execute a script for converting the `sop` manpages to fit the `pgpainless-cli` command with the help
|
||||
of a script in the `pgpainless` repository:
|
||||
```shell
|
||||
$ cd ../pgpainless/pgpainless-cli
|
||||
$ ./rewriteManPages.sh
|
||||
```
|
||||
|
||||
The resulting updated man pages are placed in `packaging/man/`.
|
||||
|
||||
## Usage
|
||||
|
||||
Hereafter, the program will be referred to as `pgpainless-cli`.
|
||||
|
||||
```
|
||||
$ pgpainless-cli help
|
||||
Stateless OpenPGP Protocol
|
||||
Usage: pgpainless-cli [--stacktrace] [COMMAND]
|
||||
|
||||
Options:
|
||||
--stacktrace Print stacktrace
|
||||
|
||||
Commands:
|
||||
version Display version information about the tool
|
||||
list-profiles Emit a list of profiles supported by the identified
|
||||
subcommand
|
||||
generate-key Generate a secret key
|
||||
change-key-password Update the password of a key
|
||||
revoke-key Generate revocation certificates
|
||||
extract-cert Extract a public key certificate from a secret key
|
||||
sign Create a detached message signature
|
||||
verify Verify a detached signature
|
||||
encrypt Encrypt a message from standard input
|
||||
decrypt Decrypt a message
|
||||
inline-detach Split signatures from a clearsigned message
|
||||
inline-sign Create an inline-signed message
|
||||
inline-verify Verify an inline-signed message
|
||||
armor Add ASCII Armor to standard input
|
||||
dearmor Remove ASCII Armor from standard input
|
||||
help Display usage information for the specified subcommand
|
||||
|
||||
Exit Codes:
|
||||
0 Successful program execution
|
||||
1 Generic program error
|
||||
3 Verification requested but no verifiable signature found
|
||||
13 Unsupported asymmetric algorithm
|
||||
17 Certificate is not encryption capable
|
||||
19 Usage error: Missing argument
|
||||
23 Incomplete verification instructions
|
||||
29 Unable to decrypt
|
||||
31 Password is not human-readable
|
||||
37 Unsupported Option
|
||||
41 Invalid data or data of wrong type encountered
|
||||
53 Non-text input received where text was expected
|
||||
59 Output file already exists
|
||||
61 Input file does not exist
|
||||
67 Cannot unlock password protected secret key
|
||||
69 Unsupported subcommand
|
||||
71 Unsupported special prefix (e.g. "@ENV/@FD") of indirect parameter
|
||||
73 Ambiguous input (a filename matching the designator already exists)
|
||||
79 Key is not signing capable
|
||||
83 Options were supplied that are incompatible with each other
|
||||
89 The requested profile is unsupported, or the indicated subcommand does
|
||||
not accept profiles
|
||||
```
|
||||
|
||||
To get help on a subcommand, e.g. `encrypt`, just call the help subcommand followed by the subcommand you
|
||||
are interested in (e.g. `pgpainless-cli help encrypt`).
|
||||
|
||||
## Examples
|
||||
```shell
|
||||
$ # Generate a key
|
||||
$ pgpainless-cli generate-key "Alice <alice@pgpainless.org>" > key.asc
|
||||
$ # Extract a certificate from a key
|
||||
$ cat key.asc | pgpainless-cli extract-cert > cert.asc
|
||||
$ # Create an encrypted signed message
|
||||
$ echo "Hello, World!" | pgpainless-cli encrypt cert.asc --sign-with key.asc > msg.asc
|
||||
$ # Decrypt an encrypted message and verify the signature
|
||||
$ cat msg.asc | pgpainless-cli decrypt key.asc --verify-with cert.asc --verifications-out verifications.txt
|
||||
Hello, World!
|
||||
$ cat verifications.txt
|
||||
2022-11-15T21:25:48Z 4FF67C69150209ED8139DE22578CB2FABD5D7897 9000235358B8CEA6A368EC86DE56DC2D942ACAA4
|
||||
```
|
||||
|
||||
## Indirect Data Types
|
||||
|
||||
Some commands take options whose arguments are indirect data types. Those are arguments which are not used directly,
|
||||
but instead they point to a place where the argument value can be sourced from, such as a file, an environment variable
|
||||
or a file descriptor.
|
||||
|
||||
It is important to keep in mind, that options like `--with-password` or `--with-key-password` are examples for such
|
||||
indirect data types. If you want to unlock a key whose password is `sw0rdf1sh`, you *cannot* provide the password
|
||||
like `--with-key-password sw0rdf1sh`, but instead you have to either write out the password into a file and provide
|
||||
the file's path (e.g. `--with-key-password /path/to/file`), store the password in an environment variable and pass that
|
||||
(e.g. `--with-key-password @ENV:myvar`), or provide a numbered file descriptor from which the password can be read
|
||||
(e.g. `--with-key-password @FD:4`).
|
||||
|
||||
Note, that environment variables and file descriptors can only be used to pass input data to the program.
|
||||
For output parameters (e.g. `--verifications-out`) only file paths are allowed.
|
|
@ -1 +0,0 @@
|
|||
# Edit Keys
|
|
@ -1,101 +0,0 @@
|
|||
# PGPainless In-Depth: Generate Keys
|
||||
|
||||
There are two API endpoints for generating OpenPGP keys using `pgpainless-core`:
|
||||
|
||||
`PGPainless.generateKeyRing()` presents a selection of pre-configured OpenPGP key archetypes:
|
||||
|
||||
```java
|
||||
// Modern, EC-based OpenPGP key with dedicated primary certification key
|
||||
// This method is recommended by the authors
|
||||
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
|
||||
.modernKeyRing(
|
||||
"Alice <alice@pgpainless.org>",
|
||||
Passphrase.fromPassword("sw0rdf1sh"));
|
||||
|
||||
// Simple, EC-based OpenPGP key with combined certification and signing key
|
||||
// plus encryption subkey
|
||||
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
|
||||
.simpleEcKeyRing(
|
||||
"Alice <alice@pgpainless.org>",
|
||||
Passphrase.fromPassword("0r4ng3"));
|
||||
|
||||
// Simple, RSA OpenPGP key made of a single RSA key used for all operations
|
||||
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
|
||||
.simpleRsaKeyRing(
|
||||
"Alice <alice@pgpainless.org>",
|
||||
RsaLength._4096, Passphrase.fromPassword("m0nk3y")):
|
||||
```
|
||||
|
||||
If you have special requirements on algorithms you can use `PGPainless.buildKeyRing()` instead, which offers more
|
||||
control over parameters:
|
||||
|
||||
```java
|
||||
// Customized key
|
||||
|
||||
// Specification for primary key
|
||||
KeySpecBuilder primaryKeySpec = KeySpec.getBuilder(
|
||||
KeyType.RSA(RsaLength._8192), // 8192 bits RSA key
|
||||
KeyFlag.CERTIFY_OTHER) // used for certification
|
||||
// optionally override algorithm preferences
|
||||
.overridePreferredCompressionAlgorithms(CompressionAlgorithm.ZLIB)
|
||||
.overridePreferredHashAlgorithms(HashAlgorithm.SHA512, HashAlgorithm.SHA384)
|
||||
.overridePreferredSymmetricKeyAlgorithms(SymmetricKeyAlgorithm.AES256);
|
||||
|
||||
// Specification for a signing subkey
|
||||
KeySpecBuilder signingSubKeySpec = KeySpec.getBuilder(
|
||||
KeyType.ECDSA(EllipticCurve._P256), // P-256 ECDSA key
|
||||
KeyFlag.SIGN_DATA); // Used for signing
|
||||
|
||||
// Specification for an encryption subkey
|
||||
KeySpecBuilder encryptionSubKeySpec = KeySpec.getBuilder(
|
||||
KeyType.ECDH(EllipticCurve._P256),
|
||||
KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE);
|
||||
|
||||
// Build the key itself
|
||||
PGPSecretKeyRing secretKey = PGPainless.buildKeyRing()
|
||||
.setPrimaryKey(primaryKeySpec)
|
||||
.addSubkey(signingSubKeySpec)
|
||||
.addSubkey(encryptionSubKeySpec)
|
||||
.addUserId("Juliet <juliet@montague.lit>") // Primary User-ID
|
||||
.addUserId("xmpp:juliet@capulet.lit") // Additional User-ID
|
||||
.setPassphrase(Passphrase.fromPassword("romeo_oh_Romeo<3")) // passphrase protection
|
||||
.build();
|
||||
```
|
||||
|
||||
To specify, which algorithm to use for a single (sub) key, `KeySpec.getBuilder(_)` can be used, passing a `KeyType`,
|
||||
as well as some `KeyFlag`s as argument.
|
||||
|
||||
`KeyType` defines an algorithm and its parameters, e.g. RSA with a certain key size, or ECDH over a certain
|
||||
elliptic curve.
|
||||
Currently, PGPainless supports the following `KeyType`s:
|
||||
* `KeyType.RSA(_)`: Signing, Certification, Encryption
|
||||
* `KeyType.ECDH(_)`: Encryption
|
||||
* `KeyType.ECDSA(_)`: Signing, Certification
|
||||
* `KeyType.EDDSA(_)`: Signing, Certification
|
||||
* `KeyType.XDH(_)`: Encryption
|
||||
|
||||
The `KeyFlag`s are used to specify, how the key will be used later on. A signing key can only be used for signing,
|
||||
if it carries the `KeyFlag.SIGN_DATA`.
|
||||
A key can carry multiple key flags.
|
||||
|
||||
It is possible to override the default algorithm preferences used by PGPainless with custom preferences.
|
||||
An algorithm preference list contains algorithms from most to least preferred.
|
||||
|
||||
Every OpenPGP key MUST have a primary key. The primary key MUST be capable of certification, so you MUST use an
|
||||
algorithm that can be used to generate signatures.
|
||||
The primary key can be set by calling `setPrimaryKey(primaryKeySpec)`.
|
||||
|
||||
Furthermore, an OpenPGP key can contain zero or more subkeys.
|
||||
Those can be set by repeatedly calling `addSubkey(subkeySpec)`.
|
||||
|
||||
OpenPGP keys are usually bound to User-IDs like names and/or email addresses.
|
||||
There can be multiple user-ids bound to a key, in which case the very first User-ID will be marked as primary.
|
||||
To add a User-ID to the key, call `addUserId(userId)`.
|
||||
|
||||
By default, keys do not have an expiration date. This can be changed by setting an expiration date using
|
||||
`setExpirationDate(date)`.
|
||||
|
||||
To enable password protection for the OpenPGP key, you can call `setPassphrase(passphrase)`.
|
||||
If this method is not called, or if the passed in `Passphrase` is empty, the key will be unprotected.
|
||||
|
||||
Finally, calling `build()` will generate a fresh OpenPGP key according to the specifications given.
|
|
@ -1,14 +0,0 @@
|
|||
In-Depth Guide to pgpainless-core
|
||||
=================================
|
||||
|
||||
This is an in-depth introduction to OpenPGP using PGPainless.
|
||||
If you are looking for a quickstart introduction instead, check out [](quickstart.md).
|
||||
|
||||
Contents
|
||||
--------
|
||||
|
||||
.. toctree::
|
||||
generate_keys.md
|
||||
edit_keys.md
|
||||
userids.md
|
||||
passphrase.md
|
|
@ -1,89 +0,0 @@
|
|||
# Passwords
|
||||
|
||||
In Java based applications, passing passwords as `String` objects has the
|
||||
[disadvantage](https://stackoverflow.com/a/8881376/11150851) that you have to rely on garbage collection to clean up
|
||||
once they are no longer used.
|
||||
For that reason, `char[]` is the preferred method for dealing with passwords.
|
||||
Once a password is no longer used, the character array can simply be overwritten to remove the sensitive data from
|
||||
memory.
|
||||
|
||||
## Passphrase
|
||||
PGPainless uses a wrapper class `Passphrase`, which takes care for the wiping of unused passwords:
|
||||
|
||||
```java
|
||||
Passphrase passphrase = new Passphrase(new char[] {'h', 'e', 'l', 'l', 'o'});
|
||||
assertTrue(passphrase.isValid());
|
||||
|
||||
assertArrayEquals(new char[] {'h', 'e', 'l', 'l', 'o'}, passphrase.getChars()):
|
||||
|
||||
// Once we are done, we can clean the data
|
||||
passphrase.clear();
|
||||
|
||||
assertFalse(passphrase.isValid());
|
||||
assertNull(passphrase.getChars());
|
||||
```
|
||||
|
||||
Furthermore, `Passphrase` can also wrap empty passphrases, which increases null-safety of the API:
|
||||
|
||||
```java
|
||||
Passphrase empty = Passphrase.emptyPassphrase();
|
||||
assertTrue(empty.isValid());
|
||||
assertTrue(empty.isEmpty());
|
||||
assertNull(empty.getChars());
|
||||
|
||||
empty.clear();
|
||||
|
||||
assertFalse(empty.isValid());
|
||||
```
|
||||
|
||||
## SecretKeyRingProtector
|
||||
|
||||
There are certain operations that require you to provide the passphrase for a key.
|
||||
Examples are decryption of messages, or creating signatures / certifications.
|
||||
|
||||
The primary way of telling PGPainless, which password to use for a certain key is the `SecretKeyRingProtector`
|
||||
interface which maps `Passphrases` to (sub-)keys.
|
||||
There are multiple implementations of this interface, which may or may not suite your needs:
|
||||
|
||||
```java
|
||||
// If your key is not password protected, this implementation is for you:
|
||||
SecretKeyRingProtector unprotected = SecretKeyRingProtector
|
||||
.unprotectedKeys();
|
||||
|
||||
// If you use a single passphrase for all (sub-) keys, take this:
|
||||
SecretKeyRingProtector singlePassphrase = SecretKeyRingProtector
|
||||
.unlockAnyKeyWith(passphrase);
|
||||
|
||||
// If you want to be flexible, use this:
|
||||
CachingSecretKeyRingProtector flexible = SecretKeyRingProtector
|
||||
.defaultSecretKeyRingProtector(passphraseCallback);
|
||||
```
|
||||
|
||||
`SecretKeyRingProtector.unprotectedKeys()` will return an empty passphrase for any key.
|
||||
It is best used when dealing with unencrypted secret keys.
|
||||
|
||||
`SecretKeyRingProtector.unlockAnyKeyWith(passphrase)` will return the same exact passphrase for any given key.
|
||||
You should use this if you have a single key with a static passphrase.
|
||||
|
||||
The last example shows how to instantiate the `CachingSecretKeyRingProtector` with a `SecretKeyPassphraseProvider`
|
||||
as argument.
|
||||
As the name suggests, the `CachingSecretKeyRingProtector` caches passphrases it knows about in a map.
|
||||
That way, you only have to provide the passphrase for a certain key only once, after which it will be remembered.
|
||||
If you try to unlock a protected secret key for which no passphrase is cached, the `getPassphraseFor()` method of
|
||||
the `SecretKeyPassphraseProvider` callback will be called to interactively ask for the missing passphrase.
|
||||
Afterwards, the acquired passphrase will be cached for future use.
|
||||
|
||||
:::{note}
|
||||
While especially the `CachingSecretKeyRingProtector` can handle multiple keys without problems, it is advised
|
||||
to use individual `SecretKeyRingProtector` objects per key.
|
||||
The reason for this is, that internally the 64bit key-id is used to resolve `Passphrase` objects and collisions are not
|
||||
unlikely in this key-space.
|
||||
Furthermore, multiple OpenPGP keys could contain the same subkey, but with different passphrases set.
|
||||
If the same `SecretKeyRingProtector` is used for two OpenPGP keys with the same subkey, but different passwords,
|
||||
the key-id collision will cause the password to be overwritten for one of the keys, which might result in issues.
|
||||
See `FLO-04-004 WP2` of the [2021 security audit](https://cure53.de/pentest-report_pgpainless.pdf) for more details.
|
||||
:::
|
||||
|
||||
Most `SecretKeyRingProtector` implementations can be instantiated with custom `KeyRingProtectionSettings`.
|
||||
By default, most implementations use `KeyRingProtectionSettings.secureDefaultSettings()` which corresponds to iterated
|
||||
and salted S2K using AES256 and SHA256 with an iteration count of 65536.
|
|
@ -1,505 +0,0 @@
|
|||
## PGPainless API with pgpainless-core
|
||||
|
||||
The `pgpainless-core` module contains the bulk of the actual OpenPGP implementation.
|
||||
|
||||
This is a quickstart guide. For more in-depth exploration of the API, checkout [](indepth.md).
|
||||
|
||||
:::{note}
|
||||
This chapter is work in progress.
|
||||
:::
|
||||
|
||||
### Setup
|
||||
|
||||
PGPainless' releases are published to and can be fetched from Maven Central.
|
||||
To get started, you first need to include `pgpainless-core` in your projects build script:
|
||||
|
||||
```
|
||||
// If you use Gradle
|
||||
...
|
||||
dependencies {
|
||||
...
|
||||
implementation "org.pgpainless:pgpainless-core:XYZ"
|
||||
...
|
||||
}
|
||||
|
||||
// If you use Maven
|
||||
...
|
||||
<dependencies>
|
||||
...
|
||||
<dependency>
|
||||
<groupId>org.pgpainless</groupId>
|
||||
<artifactId>pgpainless-core</artifactId>
|
||||
<version>XYZ</version>
|
||||
</dependency>
|
||||
...
|
||||
</dependencies>
|
||||
```
|
||||
|
||||
This will automatically pull in PGPainless' dependencies, such as Bouncy Castle.
|
||||
|
||||
:::{important}
|
||||
Replace `XYZ` with the current version, in this case {{ env.config.version }}!
|
||||
:::
|
||||
|
||||
The entry point to the API is the `PGPainless` class.
|
||||
For many common use-cases, examples can be found in the
|
||||
{{ '[examples package](https://{}/main/pgpainless-core/src/test/java/org/pgpainless/example)'.format(repo_pgpainless_src) }}.
|
||||
There is a very good chance that you can find code examples there that fit your needs.
|
||||
|
||||
### Read and Write Keys
|
||||
Reading keys from ASCII armored strings or from binary files is easy:
|
||||
|
||||
```java
|
||||
// Secret Keys
|
||||
String key = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n"...;
|
||||
PGPSecretKeyRing secretKey = PGPainless.readKeyRing()
|
||||
.secretKeyRing(key);
|
||||
|
||||
// Certificates (Public Keys)
|
||||
String cert = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n...";
|
||||
PGPPublicKeyRing certificate = PGPainless.readKeyRing()
|
||||
.publicKeyRing(cert);
|
||||
```
|
||||
|
||||
Similarly, keys or certificates can quickly be exported:
|
||||
|
||||
```java
|
||||
// ASCII armored key
|
||||
PGPSecretKeyRing secretKey = ...;
|
||||
String armored = PGPainless.asciiArmor(secretKey);
|
||||
|
||||
// binary (unarmored) key
|
||||
byte[] binary = secretKey.getEncoded();
|
||||
```
|
||||
|
||||
### Generate a Key
|
||||
PGPainless comes with a method to quickly generate modern OpenPGP keys.
|
||||
There are some predefined key archetypes, but it is possible to fully customize the key generation to fit your needs.
|
||||
|
||||
```java
|
||||
// EdDSA primary key with EdDSA signing- and XDH encryption subkeys
|
||||
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
|
||||
.modernKeyRing("Romeo <romeo@montague.lit>", "thisIsAPassword");
|
||||
|
||||
// RSA key without additional subkeys
|
||||
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
|
||||
.simpleRsaKeyRing("Juliet <juliet@montague.lit>", RsaLength._4096);
|
||||
```
|
||||
|
||||
As you can see, it is possible to generate all kinds of different keys.
|
||||
|
||||
### Extract a Certificate
|
||||
If you have a secret key, you might want to extract a public key certificate from it:
|
||||
|
||||
```java
|
||||
PGPSecretKeyRing secretKey = ...;
|
||||
PGPPublicKeyRing certificate = PGPainless.extractCertificate(secretKey);
|
||||
```
|
||||
|
||||
### Apply / Remove ASCII Armor
|
||||
ASCII armor is a layer of radix64 encoding that can be used to wrap binary OpenPGP data in order to make it save to
|
||||
transport via text-based channels (e.g. email bodies).
|
||||
|
||||
The way in which ASCII armor can be applied depends on the type of data that you want to protect.
|
||||
The easies way to ASCII armor an OpenPGP key or certificate is by using PGPainless' `asciiArmor()` method:
|
||||
|
||||
```java
|
||||
PGPPublicKey certificate = ...;
|
||||
String asciiArmored = PGPainless.asciiArmor(certificate);
|
||||
```
|
||||
|
||||
If you want to ASCII armor ciphertext, you can enable ASCII armoring during encrypting/signing by requesting
|
||||
PGPainless to armor the result:
|
||||
|
||||
```java
|
||||
ProducerOptions producerOptions = ...; // prepare as usual (see next section)
|
||||
|
||||
producerOptions.setAsciiArmor(true); // enable armoring
|
||||
|
||||
EncryptionStream encryptionStream = PGPainless.encryptAndOrSign()
|
||||
.onOutputStream(out)
|
||||
.withOptions(producerOptions);
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
If you have an already encrypted / signed binary message and want to add ASCII armoring retrospectively, you need
|
||||
to make use of BouncyCastle's `ArmoredOutputStream` as follows:
|
||||
|
||||
```java
|
||||
InputStream binaryOpenPgpIn = ...; // e.g. new ByteArrayInputStream(binaryMessage);
|
||||
|
||||
OutputStream output = ...; // e.g. new ByteArrayOutputStream();
|
||||
ArmoredOutputStream armorOut = ArmoredOutputStreamFactory.get(output);
|
||||
|
||||
Streams.pipeAll(binaryOpenPgpIn, armorOut);
|
||||
armorOut.close(); // important!
|
||||
```
|
||||
|
||||
The output stream will now contain the ASCII armored representation of the binary data.
|
||||
|
||||
If the data you want to wrap in ASCII armor is non-OpenPGP data (e.g. the String "Hello World!"),
|
||||
you need to use the following code:
|
||||
|
||||
```java
|
||||
InputStream inputStream = ...;
|
||||
OutputStream output = ...;
|
||||
|
||||
EncryptionStream armorStream = PGPainless.encryptAndOrSign()
|
||||
.onOutputStream(output)
|
||||
.withOptions(ProducerOptions.noEncryptionNoSigning()
|
||||
.setAsciiArmor(true));
|
||||
|
||||
Streams.pipeAll(inputStream, armorStream);
|
||||
armorStream.close();
|
||||
```
|
||||
|
||||
To remove ASCII armor, you can make use of BouncyCastle's `ArmoredInputStream` as follows:
|
||||
|
||||
```java
|
||||
InputStream input = ...; // e.g. new ByteArrayInputStream(armoredString.getBytes(StandardCharsets.UTF8));
|
||||
OutputStream output = ...;
|
||||
|
||||
ArmoredInputStream armorIn = new ArmoredInputStream(input);
|
||||
Streams.pipeAll(armorIn, output);
|
||||
armorIn.close();
|
||||
```
|
||||
|
||||
The output stream will now contain the binary OpenPGP data.
|
||||
|
||||
### Encrypt and/or Sign a Message
|
||||
Encrypting and signing messages is done using the same API in PGPainless.
|
||||
The type of action depends on the configuration of the `ProducerOptions` class, which in term accepts
|
||||
`SigningOptions` and `EncryptionOptions` objects:
|
||||
|
||||
```java
|
||||
// Encrypt only
|
||||
ProducerOptions options = ProducerOptions.encrypt(encryptionOptions);
|
||||
|
||||
// Sign only
|
||||
ProducerOptions options = ProducerOptions.sign(signingOptions);
|
||||
|
||||
// Sign and encrypt
|
||||
ProducerOptions options = ProducerOptions.signAndEncrypt(signingOptions, encryptionOptions);
|
||||
```
|
||||
|
||||
The `ProducerOptions` object can then be passed into the `encryptAndOrSign()` API:
|
||||
|
||||
```java
|
||||
InputStream plaintext = ...; // The data that shall be encrypted and/or signed
|
||||
OutputStream ciphertext = ...; // Destination for the ciphertext
|
||||
|
||||
EncryptionStream encryptionStream = PGPainless.encryptAndOrSign()
|
||||
.onOutputStream(ciphertext)
|
||||
.withOptions(options); // pass in the options object
|
||||
|
||||
Streams.pipeAll(plaintext, encryptionStream); // pipe the data through
|
||||
encryptionStream.close(); // important! Close the stream to finish encryption/signing
|
||||
|
||||
EncryptionResult result = encryptionStream.getResult(); // metadata
|
||||
```
|
||||
|
||||
The `ciphertext` output stream now contains the encrypted and/or signed data.
|
||||
|
||||
Now lets take a look at the configuration of the `SigningOptions` object and how to instruct PGPainless to add a simple
|
||||
signature to the message:
|
||||
|
||||
```java
|
||||
PGPSecretKeyRing signingKey = ...; // Key used for signing
|
||||
SecretKeyRingProtector protector = ...; // Protector to unlock the signing key
|
||||
|
||||
SigningOptions signOptions = SigningOptions.get()
|
||||
.addSignature(protector, signingKey);
|
||||
```
|
||||
This will add an inline signature to the message.
|
||||
|
||||
It is possible to add multiple signatures from different keys by repeating the `addSignature()` method call.
|
||||
|
||||
If instead of an inline signature, you want to create a detached signature instead (e.g. because you do not want
|
||||
to alter the data you are signing), you can add the signature as follows:
|
||||
|
||||
```java
|
||||
signOptions.addDetachedSignature(protector, signingKey);
|
||||
```
|
||||
|
||||
Passing in the `SigningOptions` object like this will result in the signature not being added to the message itself.
|
||||
Instead, the signature can later be acquired from the `EncryptionResult` object via `EncryptionResult.getDetachedSignatures()`.
|
||||
That way, it can be distributed independent of the message.
|
||||
|
||||
The `EncryptionOptions` object can be configured in a similar way:
|
||||
|
||||
```java
|
||||
PGPPublicKey certificate = ...;
|
||||
|
||||
EncryptionOptions encOptions = EncryptionOptions.get()
|
||||
.addRecipient(certificate);
|
||||
```
|
||||
|
||||
Once again, it is possible to add multiple recipients by repeating the `addRecipient()` method call.
|
||||
|
||||
In order to prevent metadata leaks, you might want to add recipients anonymously.
|
||||
Anonymous recipients have their key-id hidden by replacing it with a wildcard.
|
||||
That way, it is not easily possible for an attacker to deduce the recipients of a message without further
|
||||
analysis of additional metadata.
|
||||
Anonymous recipients can be added like follows:
|
||||
```java
|
||||
encOptions.addHiddenRecipient(certificate);
|
||||
```
|
||||
|
||||
You can also encrypt a message to a password like this:
|
||||
```java
|
||||
encOptions.addPassphrase(Passphrase.fromPassword("sw0rdf1sh"));
|
||||
```
|
||||
|
||||
Both methods can be used in combination to create a message which can be decrypted with either a recipients secret key
|
||||
or the passphrase.
|
||||
|
||||
### Decrypt and/or Verify a Message
|
||||
Decryption and verification of a message is both done using the same API.
|
||||
Whether a message was actually signed / encrypted can be determined after the message has been processed by checking
|
||||
the `MessageMetadata` object which can be obtained from the `DecryptionStream`.
|
||||
|
||||
To configure the decryption / verification process, the `ConsumerOptions` object is used:
|
||||
|
||||
```java
|
||||
PGPPublicKeyRing verificationCert = ...; // optional, signers certificate for signature verification
|
||||
PGPSecretKeyRing decryptionKey = ...; // optional, decryption key
|
||||
|
||||
ConsumerOptions options = ConsumerOptions.get()
|
||||
.addVerificationCert(verificationCert) // add a verification cert for signature verification
|
||||
.addDecryptionKey(decryptionKey); // add a secret key for message decryption
|
||||
```
|
||||
|
||||
Both verification certificates and decryption keys are optional.
|
||||
If you know the message is signed, but not encrypted you can omit providing a decryption key.
|
||||
Same goes for if you know that the message is encrypted, but not signed.
|
||||
In this case you can omit the verification certificate.
|
||||
|
||||
On the other hand, providing these parameters does not hurt.
|
||||
PGPainless will ignore unused keys / certificates, so if you provide a decryption key and the message is not encrypted,
|
||||
nothing bad will happen.
|
||||
|
||||
It is possible to provide multiple verification certs and decryption keys. PGPainless will pick suitable ones on the fly.
|
||||
If the message is signed with key `0xAAAA` and you provide certificates `0xAAAA` and `0xBBBB`, it will verify
|
||||
with cert `0xAAAA` and ignore `0xBBBB`.
|
||||
|
||||
To do the actual decryption / verification of the message, do the following:
|
||||
|
||||
```java
|
||||
InputStream ciphertext = ...; // encrypted and/or signed message
|
||||
OutputStream plaintext = ...; // destination for the plaintext
|
||||
|
||||
ConsumerOptions options = ...; // see above
|
||||
DecryptionStream consumerStream = PGPainless.decryptAndOrVerify()
|
||||
.onInputStream(ciphertext)
|
||||
.withOptions(options);
|
||||
|
||||
Streams.pipeAll(consumerStream, plaintext);
|
||||
consumerStream.close(); // important!
|
||||
|
||||
// The result will contain metadata of the message
|
||||
MessageMetadata result = consumerStream.getMetadata();
|
||||
```
|
||||
|
||||
After the message has been processed, you can consult the `MessageMetadata` object to determine the nature of the message:
|
||||
|
||||
```java
|
||||
boolean wasEncrypted = result.isEncrypted();
|
||||
SubkeyIdentifier decryptionKey = result.getDecryptionKey();
|
||||
List<SignatureVerification> validSignatures = result.getVerifiedSignatures();
|
||||
boolean wasSignedByCert = result.isVerifiedSignedBy(certificate);
|
||||
|
||||
// For files:
|
||||
String fileName = result.getFileName();
|
||||
Date modificationData = result.getModificationDate();
|
||||
```
|
||||
|
||||
### Verify a Signature
|
||||
In some cases, detached signatures are distributed alongside the message.
|
||||
This is the case for example with Debians `Release` and `Release.gpg` files.
|
||||
Here, `Release` is the plaintext message, which is unaltered by the signing process while `Release.gpg` contains
|
||||
the detached OpenPGP signature.
|
||||
|
||||
To verify a detached signature, you need to call the PGPainless API like this:
|
||||
|
||||
```java
|
||||
InputStream plaintext = ...; // e.g. new FileInputStream(releaseFile);
|
||||
InputStream detachedSignature = ...; // e.g. new FileInputStream(releaseGpgFile);
|
||||
PGPPublicKeyRing certificate = ...; // e.g. debians public signing key
|
||||
|
||||
ConsumerOptions options = ConsumerOptions.get()
|
||||
.addVerificationCert(certificate) // provide certificate for verification
|
||||
.addVerificationOfDetachedSignatures(detachedSignature) // provide detached signature
|
||||
|
||||
DecryptionStream verificationStream = PGPainless.decryptAndOrVerify()
|
||||
.onInputStream(plaintext)
|
||||
.withOptions(options);
|
||||
|
||||
Streams.drain(verificationStream); // push all the data through the stream
|
||||
verificationStream.close(); // finish verification
|
||||
|
||||
MessageMetadata result = verificationStream.getMetadata(); // get metadata of signed message
|
||||
assertTrue(result.isVerifiedSignedBy(certificate)); // check if message was in fact signed
|
||||
```
|
||||
|
||||
### Legacy Compatibility
|
||||
Out of the box, PGPainless is configured to use secure defaults and perform checks for recommended
|
||||
security features. This means that for example messages generated using older OpenPGP
|
||||
implementations which do not follow those best practices might fail to decrypt/verify.
|
||||
|
||||
It is however possible to circumvent certain security checks to allow processing of such messages.
|
||||
|
||||
:::{note}
|
||||
It is not recommended to disable security checks, as that might enable certain attacks on the OpenPGP protocol.
|
||||
:::
|
||||
|
||||
#### Missing / broken MDC (modification detection code)
|
||||
RFC4880 has two different types of encrypted data packets. The *Symmetrically Encrypted Data* packet (SED) and the *Symmetrically Encrypted Integrity-Protected Data* packet.
|
||||
The latter has an added MDC packet which prevents modifications to the ciphertext.
|
||||
|
||||
While implementations are highly encouraged to only use the latter package type, some older implementations still generate
|
||||
encrypted data packets which are not integrity protected.
|
||||
|
||||
To allow PGPainless to decrypt such messages, you need to set a flag in the `ConsumerOptions` object:
|
||||
```java
|
||||
ConsumerOptions options = ConsumerOptions.get()
|
||||
.setIgnoreMDCErrors(true) // <-
|
||||
.setDecryptionKey(secretKey)
|
||||
...
|
||||
|
||||
DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify()
|
||||
.onInputStream(ciphertextIn)
|
||||
.withOptions(options);
|
||||
...
|
||||
```
|
||||
|
||||
:::{note}
|
||||
It is highly advised to only set this flag if you know what you are doing.
|
||||
It might also be a good idea to try decrypting a message without the flag set first and only re-try
|
||||
decryption with the flag set in case of a `MessageNotIntegrityProtectedException` (don't forget to rewind the ciphertextInputStream).
|
||||
:::
|
||||
|
||||
#### Weak keys and broken algorithms
|
||||
Some users might cling on to older keys using weak algorithms / small key sizes.
|
||||
PGPainless refuses to encrypt to weak certificates and sign with weak keys.
|
||||
By default, PGPainless follows the recommendations for acceptable key sizes of [the German BSI in 2021](https://www.bsi.bund.de/SharedDocs/Downloads/EN/BSI/Publications/TechGuidelines/TG02102/BSI-TR-02102-1.pdf).
|
||||
It can however be configured to accept older key material / algorithms too.
|
||||
|
||||
Minimal key lengths can be configured by changing PGPainless' policy:
|
||||
```java
|
||||
Map<PublicKeyAlgorithm, Integer> algorithms = new HashMap<>();
|
||||
// put all acceptable algorithms and their minimal key length
|
||||
algorithms.put(PublicKeyAlgorithm.RSA_GENERAL, 1024);
|
||||
algorithms.put(PublicKeyAlgorithm.ECDSA, 100);
|
||||
...
|
||||
Policy.PublicKeyAlgorithmPolicy pkPolicy =
|
||||
new Policy.PublicKeyAlgorithmPolicy(algorithms);
|
||||
// set the custom algorithm policy
|
||||
PGPainless.getPolicy().setPublicKeyAlgorithmPolicy();
|
||||
```
|
||||
|
||||
Since OpenPGP uses a hybrid encryption scheme of asymmetric and symmetric encryption algorithms,
|
||||
it also comes with a policy for symmetric encryption algorithms.
|
||||
This list can be modified to allow for weaker algorithms like follows:
|
||||
```java
|
||||
// default fallback algorithm for message encryption
|
||||
SymmetricKeyAlgorithm fallbackAlgorithm = SymmetricKeyAlgorithm.AES_256;
|
||||
// acceptable algorithms
|
||||
List<SymmetricKeyAlgorithm> algorithms = new ArrayList<>();
|
||||
algorithms.add(SymmetricKeyAlgorithm.AES_256);
|
||||
algorithms.add(SymmetricKeyAlgorithm.AES_192);
|
||||
algorithms.add(SymmetricKeyAlgorithm.AES_128);
|
||||
algorithms.add(SymmetricKeyAlgorithm.TWOFISH);
|
||||
algorithms.add(SymmetricKeyAlgorithm.BLOWFISH);
|
||||
...
|
||||
Policy.SymmetricKeyAlgorithmPolicy skPolicy =
|
||||
new SymmtricKeyAlgorithmPolicy(fallbackAlgorithm, algorithms);
|
||||
// set the custom algorithm policy
|
||||
// algorithm policy applicable when decrypting messages created by legacy senders:
|
||||
PGPainless.getPolicy()
|
||||
.setSymmetricKeyDecryptionAlgorithmPolicy(skPolicy);
|
||||
// algorithm policy applicable when generating messages for legacy recipients:
|
||||
PGPainless.getPolicy()
|
||||
.setSymmetricKeyEncryptionAlgorithmPolicy(skPolicy);
|
||||
```
|
||||
|
||||
Hash algorithms are used in OpenPGP to create signatures.
|
||||
Since signature verification is an integral part of the OpenPGP protocol, PGPainless comes
|
||||
with multiple policies for acceptable hash algorithms, depending on the use-case.
|
||||
Revocation signatures are critical, so you might want to handle revocation signatures differently from normal signatures.
|
||||
|
||||
By default, PGPainless uses a smart hash algorithm policy for both use-cases, which takes into consideration
|
||||
not only the hash algorithm itself, but also the creation date of the signature.
|
||||
That way, signatures using SHA-1 are acceptable if they were created before February 2013, but are rejected if their
|
||||
creation date is after that point in time.
|
||||
|
||||
A custom hash algorithm policy can be set like this:
|
||||
```java
|
||||
HashAlgorithm fallbackAlgorithm = HashAlgorithm.SHA512;
|
||||
Map<HashAlgorithm, Date> algorithms = new HashMap<>();
|
||||
// Accept MD5 on signatures made before 1997-02-01
|
||||
algorithms.put(HashAlgorithm.MD5,
|
||||
DateUtil.parseUTCDate("1997-02-01 00:00:00 UTC"));
|
||||
// Accept SHA-1, regardless of signature creation time
|
||||
algorithms.put(HashAlgorithm.SHA1, null);
|
||||
...
|
||||
Policy.HashAlgorithmPolicy hPolicy =
|
||||
new Policy.HashAlgorithmPolicy(fallbackAlgorithm, algorithms);
|
||||
// set policy for revocation signatures
|
||||
PGPainless.getPolicy()
|
||||
.setRevocationSignatureHashAlgorithmPolicy(hPolicy);
|
||||
// set policy for normal signatures (certifications and document signatures)
|
||||
PGPainless.getPolicy()
|
||||
.setSignatureHashAlgorithmPolicy(hPolicy);
|
||||
```
|
||||
|
||||
Lastly, PGPainless comes with a policy on acceptable compression algorithms, which currently accepts any
|
||||
compression algorithm.
|
||||
A custom compression algorithm policy can be set in a similar way:
|
||||
```java
|
||||
CompressionAlgorithm fallback = CompressionAlgorithm.ZIP;
|
||||
List<CompressionAlgorithm> algorithms = new ArrayList<>();
|
||||
algorithms.add(CompressionAlgorith.ZIP);
|
||||
algorithms.add(CompressionAlgorithm.BZIP2);
|
||||
...
|
||||
Policy.CompressionAlgorithmPolicy cPolicy =
|
||||
new Policy.CompressionAlgorithmPolicy(fallback, algorithms);
|
||||
PGPainless.getPolicy()
|
||||
.setCompressionAlgorithmPolicy(cPolicy);
|
||||
```
|
||||
|
||||
To prevent a class of attacks described in the [paper](https://www.kopenpgp.com/#paper)
|
||||
"Victory by KO: Attacking OpenPGP Using Key Overwriting",
|
||||
PGPainless offers the option to validate private key material each time before using it,
|
||||
to make sure that an attacker didn't tamper with the corresponding public key parameters.
|
||||
|
||||
These checks are disabled by default, but they can be enabled as follows:
|
||||
```java
|
||||
PGPainless.getPolicy()
|
||||
.setEnableKeyParameterValidation(true);
|
||||
```
|
||||
|
||||
:::{note}
|
||||
Validation checks against KOpenPGP attacks are disabled by default, since they are very costly
|
||||
and only make sense in certain scenarios.
|
||||
Please read and understand the paper to decide, if enabling the checks makes sense for your use-case.
|
||||
:::
|
||||
|
||||
|
||||
### Known Notations
|
||||
In OpenPGP, signatures can contain [notation subpackets](https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.16).
|
||||
A notation can give meaning to a signature, or add additional contextual information.
|
||||
Signature subpackets can be marked as critical, meaning an implementation that does not know about
|
||||
a certain subpacket MUST reject the signature.
|
||||
The same is true for critical notations.
|
||||
|
||||
For that reason, PGPainless comes with a `NotationRegistry` class which can be used to register known notations,
|
||||
such that a signature containing a critical notation of a certain value is not rejected.
|
||||
To register a known notation, you can do the following:
|
||||
|
||||
```java
|
||||
NotationRegistry registry = PGPainless.getPolicy()
|
||||
.getNotationRegistry();
|
||||
|
||||
registry.addKnownNotation("sample@example.com");
|
||||
```
|
|
@ -1,47 +0,0 @@
|
|||
# User-IDs
|
||||
|
||||
User-IDs are identities that users go by. A User-ID might be a name, an email address or both.
|
||||
User-IDs can also contain both and even have a comment.
|
||||
|
||||
In general, the format of a User-ID is not fixed, so it can contain arbitrary strings.
|
||||
However, it is agreed upon to use the
|
||||
Below is a selection of possible User-IDs:
|
||||
|
||||
```
|
||||
Firstname Lastname (Comment) <email@address.tld>
|
||||
Firstname Lastname
|
||||
Firstname Lastname (Comment)
|
||||
<email@address.tld>
|
||||
```
|
||||
|
||||
PGPainless comes with a builder class `UserId`, which can be used to safely construct User-IDs:
|
||||
|
||||
```java
|
||||
UserId nameAndEMail = UserId.nameAndEmail("Jane Doe", "jane@pgpainless.org");
|
||||
assertEquals("Jane Doe <jane@pgpainless.org>", nameAndEmail.toString()):
|
||||
|
||||
UserId onlyEmail = UserId.onlyEmail("john@pgpainless.org");
|
||||
assertEquals("<john@pgpainless.org>", onlyEmail.toString());
|
||||
|
||||
UserId full = UserId.newBuilder()
|
||||
.withName("Peter Pattern")
|
||||
.withEmail("peter@pgpainless.org")
|
||||
.withComment("Work Address")
|
||||
.build();
|
||||
assertEquals("Peter Pattern (Work Address) <peter@pgpainless.org>", full.toString());
|
||||
```
|
||||
|
||||
If you have a User-ID in form of a string (e.g. because a user provided it via a text field),
|
||||
you can parse it into its components like this:
|
||||
|
||||
```java
|
||||
String string = "John Doe <john@doe.corp>";
|
||||
UserId userId = UserId.parse(string);
|
||||
|
||||
// Now you can access the different components
|
||||
assertEquals("John Doe", userId.getName());
|
||||
assertEquals("john@doe.corp", userId.getEmail());
|
||||
assertNull(userId.getComment());
|
||||
```
|
||||
|
||||
The method `UserId.parse(String string)` will throw an `IllegalArgumentException` if the User-ID is malformed.
|
|
@ -1,546 +0,0 @@
|
|||
## SOP API with pgpainless-sop
|
||||
|
||||
The Stateless OpenPGP Protocol (SOP) defines a simplistic interface for the most important OpenPGP operations.
|
||||
It allows you to encrypt, decrypt, sign and verify messages, generate keys and add/remove ASCII armor from data.
|
||||
However, it does not yet provide tools for key management.
|
||||
Furthermore, the implementation is deciding for you, which (secure) algorithms to use, and it doesn't let you
|
||||
change those.
|
||||
|
||||
If you want to read more about the background of the SOP protocol, there is a [whole chapter](../sop) dedicated to it.
|
||||
|
||||
### Setup
|
||||
|
||||
PGPainless' releases are published to and can be fetched from Maven Central.
|
||||
To get started, you first need to include `pgpainless-sop` in your projects build script.
|
||||
```
|
||||
// If you use Gradle
|
||||
...
|
||||
dependencies {
|
||||
...
|
||||
implementation "org.pgpainless:pgpainless-sop:XYZ"
|
||||
...
|
||||
}
|
||||
|
||||
// If you use Maven
|
||||
...
|
||||
<dependencies>
|
||||
...
|
||||
<dependency>
|
||||
<groupId>org.pgpainless</groupId>
|
||||
<artifactId>pgpainless-sop</artifactId>
|
||||
<version>XYZ</version>
|
||||
</dependency>
|
||||
...
|
||||
</dependencies>
|
||||
```
|
||||
|
||||
:::{important}
|
||||
Replace `XYZ` with the current version, in this case {{ env.config.version }}!
|
||||
:::
|
||||
|
||||
The entry point to the API is the `SOP` interface, for which `pgpainless-sop` provides a concrete implementation
|
||||
`SOPImpl`.
|
||||
|
||||
```java
|
||||
// Instantiate the API
|
||||
SOP sop = new SOPImpl();
|
||||
```
|
||||
|
||||
Now you are ready to go!
|
||||
|
||||
### Generate a Key
|
||||
|
||||
To generate a new OpenPGP key, the method `SOP.generateKey()` is your friend:
|
||||
|
||||
```java
|
||||
// generate key
|
||||
byte[] keyBytes = sop.generateKey()
|
||||
.userId("John Doe <john.doe@pgpainless.org>")
|
||||
.withKeyPassword("f00b4r")
|
||||
.generate()
|
||||
.getBytes();
|
||||
```
|
||||
|
||||
The call `userId(String userId)` can be called multiple times to add multiple user-ids to the key, but it MUST
|
||||
be called at least once.
|
||||
The argument given in the first invocation will become the keys primary user-id.
|
||||
|
||||
Optionally, the key can be protected with a password by calling `withKeyPassword(String password)`.
|
||||
If this method is not called, the key will be unprotected.
|
||||
|
||||
The `generate()` method call generates the key and returns a `Ready` object.
|
||||
This in turn can be used to write the result to a stream via `writeTo(OutputStream out)`, or to get the result
|
||||
as bytes via `getBytes()`.
|
||||
In both cases, the resulting output will be the UTF8 encoded, ASCII armored OpenPGP secret key.
|
||||
|
||||
To disable ASCII armoring, call `noArmor()` before calling `generate()`.
|
||||
|
||||
Revision `05` of the Stateless OpenPGP Protocol specification introduced the concept of profiles for
|
||||
certain operations.
|
||||
The key generation feature is the first operation to make use of profiles to specify different key algorithms.
|
||||
To set a profile, simply call `profile(String profileName)` and pass in one of the available profile identifiers.
|
||||
|
||||
To explore, which profiles are available, refer to the dedicated [section](#explore-profiles).
|
||||
|
||||
The default profile used by `pgpainless-sop` is called `draft-koch-eddsa-for-openpgp-00`.
|
||||
If this profile is used, the resulting OpenPGP secret key will consist of a certification-capable 256-bits
|
||||
ed25519 EdDSA primary key, a 256-bits ed25519 EdDSA subkey used for signing, as well as a 256-bits X25519
|
||||
ECDH subkey for encryption.
|
||||
|
||||
Another profile defined by `pgpainless-sop` is `rfc4880`, which changes the key generation behaviour such that
|
||||
the resulting key is a single 4096-bit RSA key capable of certifying, signing and encrypting.
|
||||
|
||||
The whole key does not have an expiration date set.
|
||||
|
||||
### Extract a Certificate
|
||||
|
||||
Now that you generated your secret key, you probably want to share the public key with your contacts.
|
||||
To extract the OpenPGP public key (which we will call *certificate* from now on) from the secret key,
|
||||
use the `SOP.extractCert()` method call:
|
||||
|
||||
```java
|
||||
// extract certificate
|
||||
byte[] certificateBytes = sop.extractCert()
|
||||
.key(keyBytes)
|
||||
.getBytes();
|
||||
```
|
||||
|
||||
The `key(_)` method either takes a byte array (like in the example), or an `InputStream`.
|
||||
In both cases it returns another `Ready` object from which the certificate can be accessed, either via
|
||||
`writeTo(OutputStream out)` or `getBytes()`.
|
||||
|
||||
By default, the resulting certificate will be ASCII armored, regardless of whether the input key was armored or not.
|
||||
To disable ASCII armoring, call `noArmor()` before calling `key(_)`.
|
||||
|
||||
In our example, `certificateBytes` can now safely be shared with anyone.
|
||||
|
||||
### Change Key Password
|
||||
|
||||
OpenPGP keys can (but don't need to) be password protected.
|
||||
The `changeKeyPassword()` API can be used to add, change or remove password protection from OpenPGP keys.
|
||||
While the input to this operation can be keys with different per-subkey passwords, the output will use at most one password.
|
||||
|
||||
Via `oldKeyPassphrase()`, multiple decryption passphrase candidates can be provided.
|
||||
These are tried one after another to unlock protected subkeys.
|
||||
|
||||
In order to successfully change the passphrase of an OpenPGP key, all of its subkeys needs to be successfully decrypted.
|
||||
If one or more subkeys cannot be decrypted, the operation fails with a `KeyIsProtected` exception.
|
||||
The result is either fully encrypted for a single passphrase (passed via `newKeyPassphrase()`),
|
||||
or unprotected if the new key passphrase is omitted.
|
||||
|
||||
|
||||
```java
|
||||
byte[] keyBefore = ...
|
||||
byte[] keyAfter = sop.changeKeyPassword()
|
||||
// Provide old passphrases - all subkeys need to be decryptable,
|
||||
// otherwise KeyIsProtected exception will be thrown
|
||||
.oldKeyPassphrase("4d4m5m1th")
|
||||
.oldKeyPassphrase("d4v1dR1c4rd0")
|
||||
// Provide the new passphrase - if omitted, key will be unprotected
|
||||
.newKeyPassphrase("fr1edr1ch3n93l5")
|
||||
.keys(keyBefore)
|
||||
.getBytes();
|
||||
```
|
||||
|
||||
### Generate Revocation Certificates
|
||||
|
||||
You might want to generate a revocation certificate for your OpenPGP key.
|
||||
This certificate can be published to a key server to let your contacts known that your key is no longer
|
||||
trustworthy.
|
||||
The `revokeKey()` API can be used to generate a "hard-revocation", which retroactively invalidates all
|
||||
signatures previously issued by the key.
|
||||
|
||||
If the input secret key is an OpenPGP v6 key, the result will be a minimal revocation certificate,
|
||||
consisting of only the bare primary public key and a revocation signature. For v4 keys, the result
|
||||
will consist of the whole public certificate plus a revocation signature.
|
||||
|
||||
```java
|
||||
byte[] keys = ...
|
||||
byte[] revoked = sop.revokeKey()
|
||||
// primary key password(s) if the key(s) are protected
|
||||
.withKeyPassword("5w0rdf1sh")
|
||||
// one or more secret keys
|
||||
.keys(keys)
|
||||
.getBytes();
|
||||
```
|
||||
|
||||
### Apply / Remove ASCII Armor
|
||||
|
||||
Perhaps you want to print your secret key onto a piece of paper for backup purposes,
|
||||
but you accidentally called `noArmor()` when generating the key.
|
||||
|
||||
To add ASCII armor to some binary OpenPGP data, the `armor()` API can be used:
|
||||
|
||||
```java
|
||||
// wrap data in ASCII armor
|
||||
byte[] armoredData = sop.armor()
|
||||
.data(binaryData)
|
||||
.getBytes();
|
||||
```
|
||||
|
||||
The `data(_)` method can either be called by providing a byte array, or an `InputStream`.
|
||||
|
||||
To remove ASCII armor from armored data, simply use the `dearmor()` API:
|
||||
|
||||
```java
|
||||
// remove ASCII armor
|
||||
byte[] binaryData = sop.unarmor()
|
||||
.data(armoredData)
|
||||
.getBytes();
|
||||
```
|
||||
|
||||
Once again, the `data(_)` method can be called either with a byte array or an `InputStream` as argument.
|
||||
|
||||
If the input data is not validly armored OpenPGP data, the `data(_)` method call will throw a `BadData` exception.
|
||||
|
||||
### Encrypt a Message
|
||||
|
||||
Now lets get to the juicy part and finally encrypt a message!
|
||||
In this example, we will assume that Alice is the sender that wants to send a message to Bob.
|
||||
Beforehand, Alice acquired Bobs certificate, e.g. by fetching it from a key server.
|
||||
|
||||
To encrypt a message, you can make use of the `encrypt()` API:
|
||||
|
||||
```java
|
||||
// encrypt and sign a message
|
||||
byte[] aliceKey = ...; // Alice' secret key
|
||||
byte[] aliceCert = ...; // Alice' certificate (e.g. via extractCert())
|
||||
byte[] bobCert = ...; // Bobs certificate
|
||||
|
||||
byte[] plaintext = "Hello, World!\n".getBytes(); // plaintext
|
||||
|
||||
byte[] ciphertext = sop.encrypt()
|
||||
// encrypt for each recipient
|
||||
.withCert(bobCert)
|
||||
.withCert(aliceCert)
|
||||
// Optionally: Sign the message
|
||||
.signWith(aliceKey)
|
||||
.withKeyPassword("sw0rdf1sh") // if signing key is protected
|
||||
// provide the plaintext
|
||||
.plaintext(plaintext)
|
||||
.getBytes();
|
||||
```
|
||||
|
||||
Here you encrypt the message for each recipient (Alice probably wants to be able to decrypt the message too!)
|
||||
by calling `withCert(_)` with the recipients certificate as argument. It does not matter, if the certificate
|
||||
is ASCII armored or not, and the method can either be called with a byte array or an `InputStream` as argument.
|
||||
|
||||
The API not only supports asymmetric encryption via OpenPGP certificates, but it can also encrypt messages
|
||||
symmetrically using one or more passwords. Both mechanisms can even be used together in the same message!
|
||||
To (additionally or exclusively) encrypt the message for a password, simply call `withPassword(String password)`
|
||||
before the `plaintext(_)` method call.
|
||||
|
||||
It is recommended (but not required) to sign encrypted messages.
|
||||
In order to sign the message before encryption is applied, call `signWith(_)` with the signing key as argument.
|
||||
This method call can be repeated multiple times to sign the message with multiple signing keys.
|
||||
|
||||
If any keys used for signing are password protected, you need to provide the signing key password via
|
||||
`withKeyPassword(_)`.
|
||||
It does not matter in which order signing keys and key passwords are provided, the implementation will figure out
|
||||
matches on its own. If different key passwords are used, the `withKeyPassword(_)` method can be called multiple times.
|
||||
|
||||
You can modify the behaviour of the encrypt operation by switching between different profiles via the
|
||||
`profile(String profileName)` method.
|
||||
At the time of writing, the only available profile for this operation is `rfc4880` which applies encryption
|
||||
as defined in [rfc4880](https://datatracker.ietf.org/doc/html/rfc4880).
|
||||
|
||||
To explore, which profiles are available, refer to the dedicated [section](#explore-profiles).
|
||||
|
||||
By default, the encrypted message will be ASCII armored. To disable ASCII armor, call `noArmor()` before the
|
||||
`plaintext(_)` method call.
|
||||
|
||||
Lastly, you need to provide the plaintext by calling `plaintext(_)` with either a byte array or an `InputStream`
|
||||
as argument.
|
||||
The ciphertext can then be accessed from the resulting `Ready` object as usual.
|
||||
|
||||
### Decrypt a Message
|
||||
|
||||
Now let's switch perspective and help Bob decrypt the message from Alice.
|
||||
|
||||
Decrypting encrypted messages is done in a similar fashion using the `decrypt()` API:
|
||||
|
||||
```java
|
||||
// decrypt a message and verify its signature(s)
|
||||
byte[] aliceCert = ...; // Alice' certificate
|
||||
byte[] bobKey = ...; // Bobs secret key
|
||||
byte[] bobCert = ...; // Bobs certificate
|
||||
|
||||
byte[] ciphertext = ...; // the encrypted message
|
||||
|
||||
ReadyWithResult<DecryptionResult> readyWithResult = sop.decrypt()
|
||||
.withKey(bobKey)
|
||||
.verifyWithCert(aliceCert)
|
||||
.withKeyPassword("password123") // if decryption key is protected
|
||||
.ciphertext(ciphertext);
|
||||
```
|
||||
|
||||
The `ReadyWithResult<DecryptionResult>` can now be processed in two different ways, depending on whether you want the
|
||||
plaintext as bytes or simply write it out to an `OutputStream`.
|
||||
|
||||
To get the plaintext bytes directly, you shall proceed as follows:
|
||||
|
||||
```java
|
||||
ByteArrayAndResult<DecryptionResult> bytesAndResult = readyWithResult.toByteArrayAndResult();
|
||||
DecryptionResult result = bytesAndResult.getResult();
|
||||
byte[] plaintext = bytesAndResult.getBytes();
|
||||
```
|
||||
|
||||
If you instead want to write the plaintext out to an `OutputStream`, the following code can be used:
|
||||
|
||||
```java
|
||||
OutputStream out = ...;
|
||||
DecryptionResult result = readyWithResult.writeTo(out);
|
||||
```
|
||||
|
||||
Note, that in both cases you acquire a `DecryptionResult` object. This contains information about the message,
|
||||
such as which signatures could successfully be verified.
|
||||
|
||||
If you provided the senders certificate for the purpose of signature verification via `verifyWith(_)`, you now
|
||||
probably want to check, if the message was actually signed by the sender by checking `result.getVerifications()`.
|
||||
|
||||
:::{note}
|
||||
Signature verification will be discussed in more detail in section "Verifications".
|
||||
:::
|
||||
|
||||
If the message was encrypted symmetrically using a password, you can also decrypt is symmetrically by calling
|
||||
`withPassword(String password)` before the `ciphertext(_)` method call. This method call can be repeated multiple
|
||||
times. The implementation will try different passwords until it finds a matching one.
|
||||
|
||||
### Sign a Message
|
||||
|
||||
There are three different main ways of signing a message:
|
||||
* Inline Signatures
|
||||
* Cleartext Signatures
|
||||
* Detached Signatures
|
||||
|
||||
An inline-signature will be part of the message itself (e.g. like with messages that are encrypted *and* signed).
|
||||
Inline-signed messages are not human-readable without prior processing.
|
||||
|
||||
A cleartext signature makes use of the [cleartext signature framework](https://datatracker.ietf.org/doc/html/rfc4880#section-7).
|
||||
Messages signed in this way do have an ASCII armor header and footer, yet the content of the message is still
|
||||
human-readable without special software.
|
||||
|
||||
Lastly, a detached signature can be distributed as an extra file alongside the message without altering it.
|
||||
This is useful if the plaintext itself cannot be modified (e.g. if a binary file is signed).
|
||||
|
||||
The SOP API can generate all of those signature types.
|
||||
|
||||
#### Inline-Signatures
|
||||
|
||||
Let's start with an inline signature:
|
||||
|
||||
```java
|
||||
byte[] signingKey = ...;
|
||||
byte[] message = ...;
|
||||
|
||||
byte[] inlineSignedMessage = sop.inlineSign()
|
||||
.mode(InlineSignAs.Text) // or 'Binary'
|
||||
.key(signingKey)
|
||||
.withKeyPassword("fnord")
|
||||
.data(message)
|
||||
.getBytes();
|
||||
```
|
||||
|
||||
You can choose between two different signature formats which can be set using `mode(InlineSignAs mode)`.
|
||||
The default value is `Binary`. You can also set it to `Text` which signals to the receiver that the data is
|
||||
UTF8 text.
|
||||
|
||||
:::{note}
|
||||
For inline signatures, do NOT set the `mode()` to `CleartextSigned`, as that will create message which uses the
|
||||
cleartext signature framework (see further below).
|
||||
:::
|
||||
|
||||
You must provide at least one signing key using `key(_)` in order to be able to sign the message.
|
||||
|
||||
If any key is password protected, you need to provide its password using `withKeyPassword(_)` which
|
||||
can be called multiple times to provide multiple passwords.
|
||||
|
||||
Once you provide the plaintext using `data(_)` with either a byte array or an `InputStream` as argument,
|
||||
you will get a `Ready` object back, from which the signed message can be retrieved as usual.
|
||||
|
||||
By default, the signed message will be ASCII armored. This can be disabled by calling `noArmor()`
|
||||
before the `data(_)` method call.
|
||||
|
||||
#### Cleartext Signatures
|
||||
|
||||
A cleartext-signed message can be generated in a similar way to an inline-signed message, however,
|
||||
there are is one subtle difference:
|
||||
|
||||
```java
|
||||
byte[] signingKey = ...;
|
||||
byte[] message = ...;
|
||||
|
||||
byte[] cleartextSignedMessage = sop.inlineSign()
|
||||
.mode(InlineSignAs.CleartextSigned) // This MUST be set
|
||||
.key(signingKey)
|
||||
.withKeyPassword("fnord")
|
||||
.data(message)
|
||||
.getBytes();
|
||||
```
|
||||
|
||||
:::{important}
|
||||
In order to produce a cleartext-signed message, the signature mode MUST be set to `CleartextSigned`
|
||||
by calling `mode(InlineSignAs.CleartextSigned)`.
|
||||
:::
|
||||
|
||||
:::{note}
|
||||
Calling `noArmor()` will have no effect for cleartext-signed messages, so such method call will be ignored.
|
||||
:::
|
||||
|
||||
#### Detached Signatures
|
||||
|
||||
As the name suggests, detached signatures are detached from the message itself and can be distributed separately.
|
||||
|
||||
To produce a detached signature, the `detachedSign()` API is used:
|
||||
|
||||
```java
|
||||
byte[] signingKey = ...;
|
||||
byte[] message = ...;
|
||||
|
||||
ReadyWithResult<SigningResult> readyWithResult = sop.detachedSign()
|
||||
.key(signingKey)
|
||||
.withKeyPassword("fnord")
|
||||
.data(message);
|
||||
```
|
||||
|
||||
Here you have the choice, how you want to write out the signature.
|
||||
If you want to write the signature to an `OutputStream`, you can do the following:
|
||||
|
||||
```java
|
||||
OutputStream out = ...;
|
||||
SigningResult result = readyWithResult.writeTo(out);
|
||||
```
|
||||
|
||||
If instead you want to get the signature as a byte array, do this instead:
|
||||
|
||||
```java
|
||||
ByteArrayAndResult<SigningResult> bytesAndResult = readyWithResult.toByteArrayAndResult();
|
||||
SigningResult result = bytesAndResult.getResult();
|
||||
byte[] detachedSignature = bytesAndResult.getBytes();
|
||||
```
|
||||
|
||||
In any case, the detached signature can now be distributed alongside the original message.
|
||||
|
||||
By default, the resulting detached signature will be ASCII armored. This can be disabled by calling `noArmor()`
|
||||
prior to calling `data(_)`.
|
||||
|
||||
The `SigningResult` object you got back in both cases contains information about the signature.
|
||||
|
||||
### Verify a Signature
|
||||
|
||||
In order to verify signed messages, there are two API endpoints available.
|
||||
|
||||
#### Inline and Cleartext Signatures
|
||||
|
||||
To verify inline-signed messages, or messages that make use of the cleartext signature framework,
|
||||
use the `inlineVerify()` API:
|
||||
|
||||
```java
|
||||
byte[] signingCert = ...;
|
||||
byte[] signedMessage = ...;
|
||||
|
||||
ReadyWithResult<List<Verification>> readyWithResult = sop.inlineVerify()
|
||||
.cert(signingCert)
|
||||
.data(signedMessage);
|
||||
```
|
||||
|
||||
The `cert(_)` method MUST be called at least once. It takes either a byte array or an `InputStream` containing
|
||||
an OpenPGP certificate.
|
||||
If you are not sure, which certificate was used to sign the message, you can provide multiple certificates.
|
||||
|
||||
It is also possible to reject signatures that were not made within a certain time window by calling
|
||||
`notBefore(Date timestamp)` and/or `notAfter(Date timestamp)`.
|
||||
Signatures made before the `notBefore(_)` or after the `notAfter(_)` constraints will be rejected.
|
||||
|
||||
You can now either write out the plaintext message to an `OutputStream`...
|
||||
|
||||
```java
|
||||
OutputStream out = ...;
|
||||
List<Verifications> verifications = readyWithResult.writeTo(out);
|
||||
```
|
||||
|
||||
... or you can acquire the plaintext message as a byte array directly:
|
||||
|
||||
```java
|
||||
ByteArrayAndResult<List<Verifications>> bytesAndResult = readyWithResult.toByteArrayAndResult();
|
||||
byte[] plaintextMessage = bytesAndResult.getBytes();
|
||||
List<Verifications> verifications = bytesAndResult.getResult();
|
||||
```
|
||||
|
||||
In both cases, the plaintext message will have the signatures stripped.
|
||||
|
||||
#### Detached Signatures
|
||||
|
||||
To verify detached signatures (signatures that come separate from the message itself), you can use the
|
||||
`detachedVerify()` API:
|
||||
|
||||
```java
|
||||
byte[] signingCert = ...;
|
||||
byte[] message = ...;
|
||||
byte[] detachedSignature = ...;
|
||||
|
||||
List<Verification> verifications = sop.detachedVerify()
|
||||
.cert(signingCert)
|
||||
.signatures(detachedSignature)
|
||||
.data(signedMessage);
|
||||
```
|
||||
|
||||
You can provide one or more OpenPGP certificates using `cert(_)`, providing either a byte array or an `InputStream`.
|
||||
|
||||
The detached signatures need to be provided separately using the `signatures(_)` method call.
|
||||
You can provide as many detached signatures as you like, and those can be binary or ASCII armored.
|
||||
|
||||
Like with Inline Signatures, you can constrain the time window for signature validity using
|
||||
`notAfter(_)` and `notBefore(_)`.
|
||||
|
||||
#### Verifications
|
||||
|
||||
In all above cases, the `verifications` list will contain `Verification` objects for each verifiable, valid signature.
|
||||
Those objects contain information about the signatures:
|
||||
`verification.getSigningCertFingerprint()` will return the fingerprint of the certificate that created the signature.
|
||||
`verification.getSigningKeyFingerprint()` will return the fingerprint of the used signing subkey within that certificate.
|
||||
|
||||
### Detach Signatures from Messages
|
||||
|
||||
It is also possible, to detach inline or cleartext signatures from signed messages to transform them into
|
||||
detached signatures.
|
||||
The same way you can turn inline or cleartext signed messages into plaintext messages.
|
||||
|
||||
To detach signatures from messages, use the `inlineDetach()` API:
|
||||
|
||||
```java
|
||||
byte[] signedMessage = ...;
|
||||
|
||||
ReadyWithResult<Signatures> readyWithResult = sop.inlineDetach()
|
||||
.message(signedMessage);
|
||||
ByteArrayAndResult<Signatures> bytesAndResult = readyWithResult.toByteArrayAndResult();
|
||||
|
||||
byte[] plaintext = bytesAndResult.getBytes();
|
||||
Signatures signatures = bytesAndResult.getResult();
|
||||
byte[] encodedSignatures = signatures.getBytes();
|
||||
```
|
||||
|
||||
By default, the signatures output will be ASCII armored. This can be disabled by calling `noArmor()`
|
||||
prior to `message(_)`.
|
||||
|
||||
The detached signatures can now be verified like in the section above.
|
||||
|
||||
### Explore Profiles
|
||||
|
||||
Certain operations allow modification of their behaviour by selecting between different profiles.
|
||||
An example for this is the `generateKey()` operation, where different profiles result in different algorithms used
|
||||
during key generation.
|
||||
|
||||
To explore, which profiles are supported by a certain operation, you can use the `listProfiles()` operation.
|
||||
For example, this is how you can get a list of profiles supported by the `generateKey()` operation:
|
||||
|
||||
```java
|
||||
List<Profile> profiles = sop.listProfiles().subcommand("generate-key");
|
||||
```
|
||||
|
||||
:::{note}
|
||||
As you can see, the argument passed into the `subcommand()` method must match the operation name as defined in the
|
||||
[Stateless OpenPGP Protocol specification](https://datatracker.ietf.org/doc/draft-dkg-openpgp-stateless-cli/).
|
||||
:::
|
||||
|
||||
At the time of writing (the latest revision of the SOP spec is 06), only `generate-key` and `encrypt` accept profiles.
|
|
@ -1,20 +0,0 @@
|
|||
# Quickstart Guide
|
||||
|
||||
In this guide, we will get you started with OpenPGP using PGPainless as quickly as possible.
|
||||
|
||||
At first though, you need to decide which API you want to use;
|
||||
|
||||
* PGPainless' core API is powerful and heavily customizable
|
||||
* The SOP API is a bit less powerful, but *dead* simple to use
|
||||
|
||||
The SOP API is the recommended way to go if you just want to get started already.
|
||||
|
||||
In case you need more technical documentation, Javadoc can be found in the following places:
|
||||
* For the core API: {{ '[pgpainless-core](https://javadoc.io/doc/org.pgpainless/pgpainless-core/{}/index.html)'.format(env.config.version) }}
|
||||
* For the SOP API: {{ '[pgpainless-sop](https://javadoc.io/doc/org.pgpainless/pgpainless-sop/{}/index.html)'.format(env.config.version) }}
|
||||
|
||||
```{include} pgpainless-sop/quickstart.md
|
||||
```
|
||||
|
||||
```{include} pgpainless-core/quickstart.md
|
||||
```
|
|
@ -1,10 +0,0 @@
|
|||
# Stateless OpenPGP Protocol (SOP)
|
||||
|
||||
The [Stateless OpenPGP Protocol](https://datatracker.ietf.org/doc/draft-dkg-openpgp-stateless-cli/)
|
||||
(short *SOP*) is a specification of a standardized command line interface for a limited set of OpenPGP operations.
|
||||
|
||||
By standardizing the interface, users are able to choose between different, compatible implementations.
|
||||
|
||||
:::{note}
|
||||
This chapter is work in progress.
|
||||
:::
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,5 +1,5 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.4-rc-1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
16
gradlew
vendored
16
gradlew
vendored
|
@ -1,5 +1,21 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
|
|
206
gradlew.bat
vendored
206
gradlew.bat
vendored
|
@ -1,103 +1,103 @@
|
|||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,26 +1,18 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
# PGPainless-CLI
|
||||
|
||||
[](https://javadoc.io/doc/org.pgpainless/pgpainless-cli)
|
||||
|
||||
PGPainless-CLI is an implementation of the [Stateless OpenPGP Command Line Interface](https://datatracker.ietf.org/doc/draft-dkg-openpgp-stateless-cli/) specification based on PGPainless.
|
||||
PGPainless-CLI is an implementation of the [Stateless OpenPGP Command Line Interface](https://tools.ietf.org/html/draft-dkg-openpgp-stateless-cli-01) specification based on PGPainless.
|
||||
|
||||
It plugs `pgpainless-sop` into `sop-java-picocli`.
|
||||
|
||||
## Build
|
||||
To build an executable, `gradle shadowJar` should be sufficient. The resulting jar file can be found in `pgpainless-cli/build/libs/`.
|
||||
To build an executable, `gradle jar` should be sufficient. The resulting jar file can be found in `pgpainless-sop/build/libs/`.
|
||||
|
||||
## Execute
|
||||
|
||||
The jar file produced in the step above is executable as is.
|
||||
|
||||
```
|
||||
java -jar pgpainless-cli-XXX-all.jar help
|
||||
java -jar pgpainless-cli-XXX.jar help
|
||||
```
|
||||
|
||||
Alternatively you can use the provided `./pgpainless-cli` script to directly build and execute PGPainless' Stateless Command Line Interface from within Gradle.
|
||||
|
|
|
@ -1,32 +1,53 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
plugins {
|
||||
id 'application'
|
||||
id 'org.graalvm.buildtools.native' version '0.10.6'
|
||||
id 'com.gradleup.shadow' version '8.3.6'
|
||||
}
|
||||
def generatedVersionDir = "${buildDir}/generated-version"
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
output.dir(generatedVersionDir, builtBy: 'generateVersionProperties')
|
||||
}
|
||||
}
|
||||
|
||||
graalvmNative {
|
||||
toolchainDetection = true
|
||||
task generateVersionProperties {
|
||||
doLast {
|
||||
def propertiesFile = file "$generatedVersionDir/version.properties"
|
||||
propertiesFile.parentFile.mkdirs()
|
||||
propertiesFile.createNewFile()
|
||||
// Instead of using a Properties object here, we directly write to the file
|
||||
// since Properties adds a timestamp, ruining reproducibility
|
||||
propertiesFile.write("version="+rootProject.version.toString())
|
||||
}
|
||||
}
|
||||
processResources.dependsOn generateVersionProperties
|
||||
|
||||
dependencies {
|
||||
implementation(project(":pgpainless-sop"))
|
||||
implementation(project(":sop-java"))
|
||||
|
||||
implementation(project(":sop-java-picocli"))
|
||||
implementation 'info.picocli:picocli:4.5.2'
|
||||
|
||||
testImplementation(project(":pgpainless-core"))
|
||||
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
|
||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
|
||||
|
||||
// implementation "ch.qos.logback:logback-core:1.2.6"
|
||||
// We want logback logging in tests and in the app
|
||||
testImplementation "ch.qos.logback:logback-classic:$logbackVersion"
|
||||
// implementation "ch.qos.logback:logback-classic:$logbackVersion"
|
||||
// https://todd.ginsberg.com/post/testing-system-exit/
|
||||
testImplementation 'com.ginsberg:junit5-system-exit:1.1.1'
|
||||
|
||||
// We want logback logging in tests
|
||||
testImplementation 'ch.qos.logback:logback-classic:1.2.5'
|
||||
|
||||
// We don't want logging in the application itself
|
||||
implementation "org.slf4j:slf4j-nop:$slf4jVersion"
|
||||
|
||||
implementation(project(":pgpainless-sop"))
|
||||
implementation "org.pgpainless:sop-java-picocli:$sopJavaVersion"
|
||||
/*
|
||||
implementation "org.bouncycastle:bcprov-debug-jdk15on:$bouncyCastleVersion"
|
||||
/*/
|
||||
implementation "org.bouncycastle:bcprov-jdk15on:$bouncyCastleVersion"
|
||||
//*/
|
||||
implementation "org.bouncycastle:bcpg-jdk15on:$bouncyCastleVersion"
|
||||
|
||||
// https://mvnrepository.com/artifact/com.google.code.findbugs/jsr305
|
||||
implementation group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.2'
|
||||
|
@ -34,8 +55,18 @@ dependencies {
|
|||
|
||||
mainClassName = 'org.pgpainless.cli.PGPainlessCLI'
|
||||
|
||||
application {
|
||||
mainClass = mainClassName
|
||||
jar {
|
||||
manifest {
|
||||
attributes 'Main-Class': "$mainClassName"
|
||||
}
|
||||
|
||||
from {
|
||||
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
|
||||
} {
|
||||
exclude "META-INF/*.SF"
|
||||
exclude "META-INF/*.DSA"
|
||||
exclude "META-INF/*.RSA"
|
||||
}
|
||||
}
|
||||
|
||||
run {
|
||||
|
@ -46,3 +77,5 @@ run {
|
|||
args Eval.me(appArgs)
|
||||
}
|
||||
}
|
||||
|
||||
tasks."jar".dependsOn(":pgpainless-core:assemble")
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
'\" t
|
||||
.\" Title: pgpainless-cli-armor
|
||||
.\" Author: [see the "AUTHOR(S)" section]
|
||||
.\" Generator: Asciidoctor 2.0.10
|
||||
.\" Manual: PGPainless-CLI Manual
|
||||
.\" Source:
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "PGPAINLESS\-CLI\-ARMOR" "1" "" "" "PGPainless\-CLI Manual"
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.ss \n[.ss] 0
|
||||
.nh
|
||||
.ad l
|
||||
.de URL
|
||||
\fI\\$2\fP <\\$1>\\$3
|
||||
..
|
||||
.als MTO URL
|
||||
.if \n[.g] \{\
|
||||
. mso www.tmac
|
||||
. am URL
|
||||
. ad l
|
||||
. .
|
||||
. am MTO
|
||||
. ad l
|
||||
. .
|
||||
. LINKSTYLE blue R < >
|
||||
.\}
|
||||
.SH "NAME"
|
||||
pgpainless\-cli\-armor \- Add ASCII Armor to standard input
|
||||
.SH "SYNOPSIS"
|
||||
.sp
|
||||
\fBpgpainless\-cli armor\fP [\fB\-\-stacktrace\fP]
|
||||
.SH "DESCRIPTION"
|
||||
|
||||
.SH "OPTIONS"
|
||||
.sp
|
||||
\fB\-\-stacktrace\fP
|
||||
.RS 4
|
||||
Print stacktrace
|
||||
.RE
|
|
@ -1,67 +0,0 @@
|
|||
'\" t
|
||||
.\" Title: pgpainless-cli-change-key-password
|
||||
.\" Author: [see the "AUTHOR(S)" section]
|
||||
.\" Generator: Asciidoctor 2.0.10
|
||||
.\" Manual: PGPainless-CLI Manual
|
||||
.\" Source:
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "PGPAINLESS\-CLI\-CHANGE\-KEY\-PASSWORD" "1" "" "" "PGPainless\-CLI Manual"
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.ss \n[.ss] 0
|
||||
.nh
|
||||
.ad l
|
||||
.de URL
|
||||
\fI\\$2\fP <\\$1>\\$3
|
||||
..
|
||||
.als MTO URL
|
||||
.if \n[.g] \{\
|
||||
. mso www.tmac
|
||||
. am URL
|
||||
. ad l
|
||||
. .
|
||||
. am MTO
|
||||
. ad l
|
||||
. .
|
||||
. LINKSTYLE blue R < >
|
||||
.\}
|
||||
.SH "NAME"
|
||||
pgpainless\-cli\-change\-key\-password \- Update the password of a key
|
||||
.SH "SYNOPSIS"
|
||||
.sp
|
||||
\fBpgpainless\-cli change\-key\-password\fP [\fB\-\-[no\-]armor\fP] [\fB\-\-stacktrace\fP] [\fB\-\-new\-key\-password\fP
|
||||
[=\fIPASSWORD\fP]] [\fB\-\-old\-key\-password\fP=\fIPASSWORD\fP]...
|
||||
.SH "DESCRIPTION"
|
||||
.sp
|
||||
Unlock all secret keys from STDIN using the given old passwords and emit them re\-locked using the new password to STDOUT.
|
||||
If any (sub\-) key cannot be unlocked, this operation will exit with error code 67.
|
||||
.SH "OPTIONS"
|
||||
.sp
|
||||
\fB\-\-new\-key\-password\fP[=\fIPASSWORD\fP]
|
||||
.RS 4
|
||||
New password to lock the keys with.
|
||||
.sp
|
||||
If no new password is passed in, the keys will be emitted unlocked.
|
||||
.sp
|
||||
Is an INDIRECT data type (e.g. file, environment variable, file descriptor...).
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-[no\-]armor\fP
|
||||
.RS 4
|
||||
ASCII armor the output
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-old\-key\-password\fP=\fIPASSWORD\fP
|
||||
.RS 4
|
||||
Old passwords to unlock the keys with.
|
||||
.sp
|
||||
Multiple passwords can be passed in, which are tested sequentially to unlock locked subkeys.
|
||||
.sp
|
||||
Is an INDIRECT data type (e.g. file, environment variable, file descriptor...).
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-stacktrace\fP
|
||||
.RS 4
|
||||
Print stacktrace
|
||||
.RE
|
|
@ -1,41 +0,0 @@
|
|||
'\" t
|
||||
.\" Title: pgpainless-cli-dearmor
|
||||
.\" Author: [see the "AUTHOR(S)" section]
|
||||
.\" Generator: Asciidoctor 2.0.10
|
||||
.\" Manual: PGPainless-CLI Manual
|
||||
.\" Source:
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "PGPAINLESS\-CLI\-DEARMOR" "1" "" "" "PGPainless\-CLI Manual"
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.ss \n[.ss] 0
|
||||
.nh
|
||||
.ad l
|
||||
.de URL
|
||||
\fI\\$2\fP <\\$1>\\$3
|
||||
..
|
||||
.als MTO URL
|
||||
.if \n[.g] \{\
|
||||
. mso www.tmac
|
||||
. am URL
|
||||
. ad l
|
||||
. .
|
||||
. am MTO
|
||||
. ad l
|
||||
. .
|
||||
. LINKSTYLE blue R < >
|
||||
.\}
|
||||
.SH "NAME"
|
||||
pgpainless\-cli\-dearmor \- Remove ASCII Armor from standard input
|
||||
.SH "SYNOPSIS"
|
||||
.sp
|
||||
\fBpgpainless\-cli dearmor\fP [\fB\-\-stacktrace\fP]
|
||||
.SH "DESCRIPTION"
|
||||
|
||||
.SH "OPTIONS"
|
||||
.sp
|
||||
\fB\-\-stacktrace\fP
|
||||
.RS 4
|
||||
Print stacktrace
|
||||
.RE
|
|
@ -1,106 +0,0 @@
|
|||
'\" t
|
||||
.\" Title: pgpainless-cli-decrypt
|
||||
.\" Author: [see the "AUTHOR(S)" section]
|
||||
.\" Generator: Asciidoctor 2.0.10
|
||||
.\" Manual: PGPainless-CLI Manual
|
||||
.\" Source:
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "PGPAINLESS\-CLI\-DECRYPT" "1" "" "" "PGPainless\-CLI Manual"
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.ss \n[.ss] 0
|
||||
.nh
|
||||
.ad l
|
||||
.de URL
|
||||
\fI\\$2\fP <\\$1>\\$3
|
||||
..
|
||||
.als MTO URL
|
||||
.if \n[.g] \{\
|
||||
. mso www.tmac
|
||||
. am URL
|
||||
. ad l
|
||||
. .
|
||||
. am MTO
|
||||
. ad l
|
||||
. .
|
||||
. LINKSTYLE blue R < >
|
||||
.\}
|
||||
.SH "NAME"
|
||||
pgpainless\-cli\-decrypt \- Decrypt a message
|
||||
.SH "SYNOPSIS"
|
||||
.sp
|
||||
\fBpgpainless\-cli decrypt\fP [\fB\-\-stacktrace\fP] [\fB\-\-session\-key\-out\fP=\fISESSIONKEY\fP]
|
||||
[\fB\-\-verifications\-out\fP=\fIVERIFICATIONS\fP] [\fB\-\-verify\-not\-after\fP=\fIDATE\fP]
|
||||
[\fB\-\-verify\-not\-before\fP=\fIDATE\fP] [\fB\-\-verify\-with\fP=\fICERT\fP]...
|
||||
[\fB\-\-with\-key\-password\fP=\fIPASSWORD\fP]... [\fB\-\-with\-password\fP=\fIPASSWORD\fP]...
|
||||
[\fB\-\-with\-session\-key\fP=\fISESSIONKEY\fP]... [\fIKEY\fP...]
|
||||
.SH "DESCRIPTION"
|
||||
|
||||
.SH "OPTIONS"
|
||||
.sp
|
||||
\fB\-\-session\-key\-out\fP=\fISESSIONKEY\fP
|
||||
.RS 4
|
||||
Can be used to learn the session key on successful decryption
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-stacktrace\fP
|
||||
.RS 4
|
||||
Print stacktrace
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-verifications\-out\fP=\fIVERIFICATIONS\fP, \fB\-\-verify\-not\-after\fP=\fIDATE\fP
|
||||
.RS 4
|
||||
ISO\-8601 formatted UTC date (e.g. \(aq2020\-11\-23T16:35Z)
|
||||
.sp
|
||||
Reject signatures with a creation date not in range.
|
||||
.sp
|
||||
Defaults to current system time (\(aqnow\(aq).
|
||||
.sp
|
||||
Accepts special value \(aq\-\(aq for end of time.
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-verify\-not\-before\fP=\fIDATE\fP
|
||||
.RS 4
|
||||
ISO\-8601 formatted UTC date (e.g. \(aq2020\-11\-23T16:35Z)
|
||||
.sp
|
||||
Reject signatures with a creation date not in range.
|
||||
.sp
|
||||
Defaults to beginning of time (\(aq\-\(aq).
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-verify\-with\fP=\fICERT\fP
|
||||
.RS 4
|
||||
Certificates for signature verification
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-with\-key\-password\fP=\fIPASSWORD\fP
|
||||
.RS 4
|
||||
Passphrase to unlock the secret key(s).
|
||||
.sp
|
||||
Is an INDIRECT data type (e.g. file, environment variable, file descriptor...).
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-with\-password\fP=\fIPASSWORD\fP
|
||||
.RS 4
|
||||
Symmetric passphrase to decrypt the message with.
|
||||
.sp
|
||||
Enables decryption based on any "SKESK" packets in the "CIPHERTEXT".
|
||||
.sp
|
||||
Is an INDIRECT data type (e.g. file, environment variable, file descriptor...).
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-with\-session\-key\fP=\fISESSIONKEY\fP
|
||||
.RS 4
|
||||
Symmetric message key (session key).
|
||||
.sp
|
||||
Enables decryption of the "CIPHERTEXT" using the session key directly against the "SEIPD" packet.
|
||||
.sp
|
||||
Is an INDIRECT data type (e.g. file, environment variable, file descriptor...).
|
||||
.RE
|
||||
.SH "ARGUMENTS"
|
||||
.sp
|
||||
[\fIKEY\fP...]
|
||||
.RS 4
|
||||
Secret keys to attempt decryption with
|
||||
.RE
|
|
@ -1,84 +0,0 @@
|
|||
'\" t
|
||||
.\" Title: pgpainless-cli-encrypt
|
||||
.\" Author: [see the "AUTHOR(S)" section]
|
||||
.\" Generator: Asciidoctor 2.0.10
|
||||
.\" Manual: PGPainless-CLI Manual
|
||||
.\" Source:
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "PGPAINLESS\-CLI\-ENCRYPT" "1" "" "" "PGPainless\-CLI Manual"
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.ss \n[.ss] 0
|
||||
.nh
|
||||
.ad l
|
||||
.de URL
|
||||
\fI\\$2\fP <\\$1>\\$3
|
||||
..
|
||||
.als MTO URL
|
||||
.if \n[.g] \{\
|
||||
. mso www.tmac
|
||||
. am URL
|
||||
. ad l
|
||||
. .
|
||||
. am MTO
|
||||
. ad l
|
||||
. .
|
||||
. LINKSTYLE blue R < >
|
||||
.\}
|
||||
.SH "NAME"
|
||||
pgpainless\-cli\-encrypt \- Encrypt a message from standard input
|
||||
.SH "SYNOPSIS"
|
||||
.sp
|
||||
\fBpgpainless\-cli encrypt\fP [\fB\-\-[no\-]armor\fP] [\fB\-\-stacktrace\fP] [\fB\-\-as\fP=\fI{binary|text}\fP]
|
||||
[\fB\-\-profile\fP=\fIPROFILE\fP] [\fB\-\-session\-key\-out\fP=\fISESSIONKEY\fP]
|
||||
[\fB\-\-sign\-with\fP=\fIKEY\fP]... [\fB\-\-with\-key\-password\fP=\fIPASSWORD\fP]...
|
||||
[\fB\-\-with\-password\fP=\fIPASSWORD\fP]... [\fICERTS\fP...]
|
||||
.SH "DESCRIPTION"
|
||||
|
||||
.SH "OPTIONS"
|
||||
.sp
|
||||
\fB\-\-as\fP=\fI{binary|text}\fP
|
||||
.RS 4
|
||||
Type of the input data. Defaults to \(aqbinary\(aq
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-[no\-]armor\fP
|
||||
.RS 4
|
||||
ASCII armor the output
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-profile\fP=\fIPROFILE\fP
|
||||
.RS 4
|
||||
Profile identifier to switch between profiles
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-session\-key\-out\fP=\fISESSIONKEY\fP, \fB\-\-sign\-with\fP=\fIKEY\fP
|
||||
.RS 4
|
||||
Sign the output with a private key
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-stacktrace\fP
|
||||
.RS 4
|
||||
Print stacktrace
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-with\-key\-password\fP=\fIPASSWORD\fP
|
||||
.RS 4
|
||||
Passphrase to unlock the secret key(s).
|
||||
.sp
|
||||
Is an INDIRECT data type (e.g. file, environment variable, file descriptor...).
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-with\-password\fP=\fIPASSWORD\fP
|
||||
.RS 4
|
||||
Encrypt the message with a password.
|
||||
.sp
|
||||
Is an INDIRECT data type (e.g. file, environment variable, file descriptor...).
|
||||
.RE
|
||||
.SH "ARGUMENTS"
|
||||
.sp
|
||||
[\fICERTS\fP...]
|
||||
.RS 4
|
||||
Certificates the message gets encrypted to
|
||||
.RE
|
|
@ -1,47 +0,0 @@
|
|||
'\" t
|
||||
.\" Title: pgpainless-cli-extract-cert
|
||||
.\" Author: [see the "AUTHOR(S)" section]
|
||||
.\" Generator: Asciidoctor 2.0.10
|
||||
.\" Manual: PGPainless-CLI Manual
|
||||
.\" Source:
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "PGPAINLESS\-CLI\-EXTRACT\-CERT" "1" "" "" "PGPainless\-CLI Manual"
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.ss \n[.ss] 0
|
||||
.nh
|
||||
.ad l
|
||||
.de URL
|
||||
\fI\\$2\fP <\\$1>\\$3
|
||||
..
|
||||
.als MTO URL
|
||||
.if \n[.g] \{\
|
||||
. mso www.tmac
|
||||
. am URL
|
||||
. ad l
|
||||
. .
|
||||
. am MTO
|
||||
. ad l
|
||||
. .
|
||||
. LINKSTYLE blue R < >
|
||||
.\}
|
||||
.SH "NAME"
|
||||
pgpainless\-cli\-extract\-cert \- Extract a public key certificate from a secret key
|
||||
.SH "SYNOPSIS"
|
||||
.sp
|
||||
\fBpgpainless\-cli extract\-cert\fP [\fB\-\-[no\-]armor\fP] [\fB\-\-stacktrace\fP]
|
||||
.SH "DESCRIPTION"
|
||||
.sp
|
||||
Read a secret key from STDIN and emit the public key certificate to STDOUT.
|
||||
.SH "OPTIONS"
|
||||
.sp
|
||||
\fB\-\-[no\-]armor\fP
|
||||
.RS 4
|
||||
ASCII armor the output
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-stacktrace\fP
|
||||
.RS 4
|
||||
Print stacktrace
|
||||
.RE
|
|
@ -1,165 +0,0 @@
|
|||
'\" t
|
||||
.\" Title: pgpainless-cli-generate-completion
|
||||
.\" Author: [see the "AUTHOR(S)" section]
|
||||
.\" Generator: Asciidoctor 2.0.10
|
||||
.\" Manual: PGPainless-CLI Manual
|
||||
.\" Source: generate-completion 4.6.3
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "PGPAINLESS\-CLI\-GENERATE\-COMPLETION" "1" "" "generate\-completion 4.6.3" "PGPainless\-CLI Manual"
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.ss \n[.ss] 0
|
||||
.nh
|
||||
.ad l
|
||||
.de URL
|
||||
\fI\\$2\fP <\\$1>\\$3
|
||||
..
|
||||
.als MTO URL
|
||||
.if \n[.g] \{\
|
||||
. mso www.tmac
|
||||
. am URL
|
||||
. ad l
|
||||
. .
|
||||
. am MTO
|
||||
. ad l
|
||||
. .
|
||||
. LINKSTYLE blue R < >
|
||||
.\}
|
||||
.SH "NAME"
|
||||
pgpainless\-cli\-generate\-completion \- Stateless OpenPGP Protocol
|
||||
.SH "SYNOPSIS"
|
||||
.sp
|
||||
\fBpgpainless\-cli generate\-completion\fP [\fB\-hV\fP] [\fB\-\-stacktrace\fP]
|
||||
.SH "DESCRIPTION"
|
||||
.sp
|
||||
Generate bash/zsh completion script for pgpainless\-cli.
|
||||
Run the following command to give \f(CRpgpainless\-cli\fP TAB completion in the current shell:
|
||||
.sp
|
||||
.if n .RS 4
|
||||
.nf
|
||||
source <(pgpainless\-cli generate\-completion)
|
||||
.fi
|
||||
.if n .RE
|
||||
.SH "OPTIONS"
|
||||
.sp
|
||||
\fB\-h\fP, \fB\-\-help\fP
|
||||
.RS 4
|
||||
Show this help message and exit.
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-stacktrace\fP
|
||||
.RS 4
|
||||
Print stacktrace
|
||||
.RE
|
||||
.sp
|
||||
\fB\-V\fP, \fB\-\-version\fP
|
||||
.RS 4
|
||||
Print version information and exit.
|
||||
.RE
|
||||
.SH "EXIT CODES:"
|
||||
.sp
|
||||
\fB0\fP
|
||||
.RS 4
|
||||
Successful program execution
|
||||
.RE
|
||||
.sp
|
||||
\fB1\fP
|
||||
.RS 4
|
||||
Generic program error
|
||||
.RE
|
||||
.sp
|
||||
\fB3\fP
|
||||
.RS 4
|
||||
Verification requested but no verifiable signature found
|
||||
.RE
|
||||
.sp
|
||||
\fB13\fP
|
||||
.RS 4
|
||||
Unsupported asymmetric algorithm
|
||||
.RE
|
||||
.sp
|
||||
\fB17\fP
|
||||
.RS 4
|
||||
Certificate is not encryption capable
|
||||
.RE
|
||||
.sp
|
||||
\fB19\fP
|
||||
.RS 4
|
||||
Usage error: Missing argument
|
||||
.RE
|
||||
.sp
|
||||
\fB23\fP
|
||||
.RS 4
|
||||
Incomplete verification instructions
|
||||
.RE
|
||||
.sp
|
||||
\fB29\fP
|
||||
.RS 4
|
||||
Unable to decrypt
|
||||
.RE
|
||||
.sp
|
||||
\fB31\fP
|
||||
.RS 4
|
||||
Password is not human\-readable
|
||||
.RE
|
||||
.sp
|
||||
\fB37\fP
|
||||
.RS 4
|
||||
Unsupported Option
|
||||
.RE
|
||||
.sp
|
||||
\fB41\fP
|
||||
.RS 4
|
||||
Invalid data or data of wrong type encountered
|
||||
.RE
|
||||
.sp
|
||||
\fB53\fP
|
||||
.RS 4
|
||||
Non\-text input received where text was expected
|
||||
.RE
|
||||
.sp
|
||||
\fB59\fP
|
||||
.RS 4
|
||||
Output file already exists
|
||||
.RE
|
||||
.sp
|
||||
\fB61\fP
|
||||
.RS 4
|
||||
Input file does not exist
|
||||
.RE
|
||||
.sp
|
||||
\fB67\fP
|
||||
.RS 4
|
||||
Cannot unlock password protected secret key
|
||||
.RE
|
||||
.sp
|
||||
\fB69\fP
|
||||
.RS 4
|
||||
Unsupported subcommand
|
||||
.RE
|
||||
.sp
|
||||
\fB71\fP
|
||||
.RS 4
|
||||
Unsupported special prefix (e.g. "@ENV/@FD") of indirect parameter
|
||||
.RE
|
||||
.sp
|
||||
\fB73\fP
|
||||
.RS 4
|
||||
Ambiguous input (a filename matching the designator already exists)
|
||||
.RE
|
||||
.sp
|
||||
\fB79\fP
|
||||
.RS 4
|
||||
Key is not signing capable
|
||||
.RE
|
||||
.sp
|
||||
\fB83\fP
|
||||
.RS 4
|
||||
Options were supplied that are incompatible with each other
|
||||
.RE
|
||||
.sp
|
||||
\fB89\fP
|
||||
.RS 4
|
||||
The requested profile is unsupported, or the indicated subcommand does not accept profiles
|
||||
.RE
|
|
@ -1,71 +0,0 @@
|
|||
'\" t
|
||||
.\" Title: pgpainless-cli-generate-key
|
||||
.\" Author: [see the "AUTHOR(S)" section]
|
||||
.\" Generator: Asciidoctor 2.0.10
|
||||
.\" Manual: PGPainless-CLI Manual
|
||||
.\" Source:
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "PGPAINLESS\-CLI\-GENERATE\-KEY" "1" "" "" "PGPainless\-CLI Manual"
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.ss \n[.ss] 0
|
||||
.nh
|
||||
.ad l
|
||||
.de URL
|
||||
\fI\\$2\fP <\\$1>\\$3
|
||||
..
|
||||
.als MTO URL
|
||||
.if \n[.g] \{\
|
||||
. mso www.tmac
|
||||
. am URL
|
||||
. ad l
|
||||
. .
|
||||
. am MTO
|
||||
. ad l
|
||||
. .
|
||||
. LINKSTYLE blue R < >
|
||||
.\}
|
||||
.SH "NAME"
|
||||
pgpainless\-cli\-generate\-key \- Generate a secret key
|
||||
.SH "SYNOPSIS"
|
||||
.sp
|
||||
\fBpgpainless\-cli generate\-key\fP [\fB\-\-[no\-]armor\fP] [\fB\-\-signing\-only\fP] [\fB\-\-stacktrace\fP]
|
||||
[\fB\-\-profile\fP=\fIPROFILE\fP] [\fB\-\-with\-key\-password\fP=\fIPASSWORD\fP] [\fIUSERID\fP...]
|
||||
.SH "DESCRIPTION"
|
||||
|
||||
.SH "OPTIONS"
|
||||
.sp
|
||||
\fB\-\-[no\-]armor\fP
|
||||
.RS 4
|
||||
ASCII armor the output
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-profile\fP=\fIPROFILE\fP
|
||||
.RS 4
|
||||
Profile identifier to switch between profiles
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-signing\-only\fP
|
||||
.RS 4
|
||||
Generate a key that can only be used for signing
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-stacktrace\fP
|
||||
.RS 4
|
||||
Print stacktrace
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-with\-key\-password\fP=\fIPASSWORD\fP
|
||||
.RS 4
|
||||
Password to protect the private key with
|
||||
.sp
|
||||
Is an INDIRECT data type (e.g. file, environment variable, file descriptor...).
|
||||
.RE
|
||||
.SH "ARGUMENTS"
|
||||
.sp
|
||||
[\fIUSERID\fP...]
|
||||
.RS 4
|
||||
User\-ID, e.g. "Alice <\c
|
||||
.MTO "alice\(atexample.com" "" ">""
|
||||
.RE
|
|
@ -1,160 +0,0 @@
|
|||
'\" t
|
||||
.\" Title: pgpainless-cli-help
|
||||
.\" Author: [see the "AUTHOR(S)" section]
|
||||
.\" Generator: Asciidoctor 2.0.10
|
||||
.\" Manual: PGPainless-CLI Manual
|
||||
.\" Source:
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "PGPAINLESS\-CLI\-HELP" "1" "" "" "PGPainless\-CLI Manual"
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.ss \n[.ss] 0
|
||||
.nh
|
||||
.ad l
|
||||
.de URL
|
||||
\fI\\$2\fP <\\$1>\\$3
|
||||
..
|
||||
.als MTO URL
|
||||
.if \n[.g] \{\
|
||||
. mso www.tmac
|
||||
. am URL
|
||||
. ad l
|
||||
. .
|
||||
. am MTO
|
||||
. ad l
|
||||
. .
|
||||
. LINKSTYLE blue R < >
|
||||
.\}
|
||||
.SH "NAME"
|
||||
pgpainless\-cli\-help \- Stateless OpenPGP Protocol
|
||||
.SH "SYNOPSIS"
|
||||
.sp
|
||||
\fBpgpainless\-cli help\fP [\fB\-h\fP] [\fB\-\-stacktrace\fP] [\fICOMMAND\fP]
|
||||
.SH "DESCRIPTION"
|
||||
.sp
|
||||
When no COMMAND is given, the usage help for the main command is displayed.
|
||||
If a COMMAND is specified, the help for that command is shown.
|
||||
.SH "OPTIONS"
|
||||
.sp
|
||||
\fB\-h\fP, \fB\-\-help\fP
|
||||
.RS 4
|
||||
Show usage help for the help command and exit.
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-stacktrace\fP
|
||||
.RS 4
|
||||
Print stacktrace
|
||||
.RE
|
||||
.SH "ARGUMENTS"
|
||||
.sp
|
||||
[\fICOMMAND\fP]
|
||||
.RS 4
|
||||
The COMMAND to display the usage help message for.
|
||||
.RE
|
||||
.SH "EXIT CODES:"
|
||||
.sp
|
||||
\fB0\fP
|
||||
.RS 4
|
||||
Successful program execution
|
||||
.RE
|
||||
.sp
|
||||
\fB1\fP
|
||||
.RS 4
|
||||
Generic program error
|
||||
.RE
|
||||
.sp
|
||||
\fB3\fP
|
||||
.RS 4
|
||||
Verification requested but no verifiable signature found
|
||||
.RE
|
||||
.sp
|
||||
\fB13\fP
|
||||
.RS 4
|
||||
Unsupported asymmetric algorithm
|
||||
.RE
|
||||
.sp
|
||||
\fB17\fP
|
||||
.RS 4
|
||||
Certificate is not encryption capable
|
||||
.RE
|
||||
.sp
|
||||
\fB19\fP
|
||||
.RS 4
|
||||
Usage error: Missing argument
|
||||
.RE
|
||||
.sp
|
||||
\fB23\fP
|
||||
.RS 4
|
||||
Incomplete verification instructions
|
||||
.RE
|
||||
.sp
|
||||
\fB29\fP
|
||||
.RS 4
|
||||
Unable to decrypt
|
||||
.RE
|
||||
.sp
|
||||
\fB31\fP
|
||||
.RS 4
|
||||
Password is not human\-readable
|
||||
.RE
|
||||
.sp
|
||||
\fB37\fP
|
||||
.RS 4
|
||||
Unsupported Option
|
||||
.RE
|
||||
.sp
|
||||
\fB41\fP
|
||||
.RS 4
|
||||
Invalid data or data of wrong type encountered
|
||||
.RE
|
||||
.sp
|
||||
\fB53\fP
|
||||
.RS 4
|
||||
Non\-text input received where text was expected
|
||||
.RE
|
||||
.sp
|
||||
\fB59\fP
|
||||
.RS 4
|
||||
Output file already exists
|
||||
.RE
|
||||
.sp
|
||||
\fB61\fP
|
||||
.RS 4
|
||||
Input file does not exist
|
||||
.RE
|
||||
.sp
|
||||
\fB67\fP
|
||||
.RS 4
|
||||
Cannot unlock password protected secret key
|
||||
.RE
|
||||
.sp
|
||||
\fB69\fP
|
||||
.RS 4
|
||||
Unsupported subcommand
|
||||
.RE
|
||||
.sp
|
||||
\fB71\fP
|
||||
.RS 4
|
||||
Unsupported special prefix (e.g. "@ENV/@FD") of indirect parameter
|
||||
.RE
|
||||
.sp
|
||||
\fB73\fP
|
||||
.RS 4
|
||||
Ambiguous input (a filename matching the designator already exists)
|
||||
.RE
|
||||
.sp
|
||||
\fB79\fP
|
||||
.RS 4
|
||||
Key is not signing capable
|
||||
.RE
|
||||
.sp
|
||||
\fB83\fP
|
||||
.RS 4
|
||||
Options were supplied that are incompatible with each other
|
||||
.RE
|
||||
.sp
|
||||
\fB89\fP
|
||||
.RS 4
|
||||
The requested profile is unsupported, or the indicated subcommand does not accept profiles
|
||||
.RE
|
|
@ -1,51 +0,0 @@
|
|||
'\" t
|
||||
.\" Title: pgpainless-cli-inline-detach
|
||||
.\" Author: [see the "AUTHOR(S)" section]
|
||||
.\" Generator: Asciidoctor 2.0.10
|
||||
.\" Manual: PGPainless-CLI Manual
|
||||
.\" Source:
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "PGPAINLESS\-CLI\-INLINE\-DETACH" "1" "" "" "PGPainless\-CLI Manual"
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.ss \n[.ss] 0
|
||||
.nh
|
||||
.ad l
|
||||
.de URL
|
||||
\fI\\$2\fP <\\$1>\\$3
|
||||
..
|
||||
.als MTO URL
|
||||
.if \n[.g] \{\
|
||||
. mso www.tmac
|
||||
. am URL
|
||||
. ad l
|
||||
. .
|
||||
. am MTO
|
||||
. ad l
|
||||
. .
|
||||
. LINKSTYLE blue R < >
|
||||
.\}
|
||||
.SH "NAME"
|
||||
pgpainless\-cli\-inline\-detach \- Split signatures from a clearsigned message
|
||||
.SH "SYNOPSIS"
|
||||
.sp
|
||||
\fBpgpainless\-cli inline\-detach\fP [\fB\-\-[no\-]armor\fP] [\fB\-\-stacktrace\fP] [\fB\-\-signatures\-out\fP=\fISIGNATURES\fP]
|
||||
.SH "DESCRIPTION"
|
||||
|
||||
.SH "OPTIONS"
|
||||
.sp
|
||||
\fB\-\-[no\-]armor\fP
|
||||
.RS 4
|
||||
ASCII armor the output
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-signatures\-out\fP=\fISIGNATURES\fP
|
||||
.RS 4
|
||||
Destination to which a detached signatures block will be written
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-stacktrace\fP
|
||||
.RS 4
|
||||
Print stacktrace
|
||||
.RE
|
|
@ -1,73 +0,0 @@
|
|||
'\" t
|
||||
.\" Title: pgpainless-cli-inline-sign
|
||||
.\" Author: [see the "AUTHOR(S)" section]
|
||||
.\" Generator: Asciidoctor 2.0.10
|
||||
.\" Manual: PGPainless-CLI Manual
|
||||
.\" Source:
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "PGPAINLESS\-CLI\-INLINE\-SIGN" "1" "" "" "PGPainless\-CLI Manual"
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.ss \n[.ss] 0
|
||||
.nh
|
||||
.ad l
|
||||
.de URL
|
||||
\fI\\$2\fP <\\$1>\\$3
|
||||
..
|
||||
.als MTO URL
|
||||
.if \n[.g] \{\
|
||||
. mso www.tmac
|
||||
. am URL
|
||||
. ad l
|
||||
. .
|
||||
. am MTO
|
||||
. ad l
|
||||
. .
|
||||
. LINKSTYLE blue R < >
|
||||
.\}
|
||||
.SH "NAME"
|
||||
pgpainless\-cli\-inline\-sign \- Create an inline\-signed message
|
||||
.SH "SYNOPSIS"
|
||||
.sp
|
||||
\fBpgpainless\-cli inline\-sign\fP [\fB\-\-[no\-]armor\fP] [\fB\-\-stacktrace\fP] [\fB\-\-as\fP=\fI{binary|text|clearsigned}\fP]
|
||||
[\fB\-\-with\-key\-password\fP=\fIPASSWORD\fP]... [\fIKEYS\fP...]
|
||||
.SH "DESCRIPTION"
|
||||
|
||||
.SH "OPTIONS"
|
||||
.sp
|
||||
\fB\-\-as\fP=\fI{binary|text|clearsigned}\fP
|
||||
.RS 4
|
||||
Specify the signature format of the signed message.
|
||||
.sp
|
||||
\(aqtext\(aq and \(aqbinary\(aq will produce inline\-signed messages.
|
||||
.sp
|
||||
\(aqclearsigned\(aq will make use of the cleartext signature framework.
|
||||
.sp
|
||||
Defaults to \(aqbinary\(aq.
|
||||
.sp
|
||||
If \(aq\-\-as=text\(aq and the input data is not valid UTF\-8, inline\-sign fails with return code 53.
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-[no\-]armor\fP
|
||||
.RS 4
|
||||
ASCII armor the output
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-stacktrace\fP
|
||||
.RS 4
|
||||
Print stacktrace
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-with\-key\-password\fP=\fIPASSWORD\fP
|
||||
.RS 4
|
||||
Passphrase to unlock the secret key(s).
|
||||
.sp
|
||||
Is an INDIRECT data type (e.g. file, environment variable, file descriptor...).
|
||||
.RE
|
||||
.SH "ARGUMENTS"
|
||||
.sp
|
||||
[\fIKEYS\fP...]
|
||||
.RS 4
|
||||
Secret keys used for signing
|
||||
.RE
|
|
@ -1,73 +0,0 @@
|
|||
'\" t
|
||||
.\" Title: pgpainless-cli-inline-verify
|
||||
.\" Author: [see the "AUTHOR(S)" section]
|
||||
.\" Generator: Asciidoctor 2.0.10
|
||||
.\" Manual: PGPainless-CLI Manual
|
||||
.\" Source:
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "PGPAINLESS\-CLI\-INLINE\-VERIFY" "1" "" "" "PGPainless\-CLI Manual"
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.ss \n[.ss] 0
|
||||
.nh
|
||||
.ad l
|
||||
.de URL
|
||||
\fI\\$2\fP <\\$1>\\$3
|
||||
..
|
||||
.als MTO URL
|
||||
.if \n[.g] \{\
|
||||
. mso www.tmac
|
||||
. am URL
|
||||
. ad l
|
||||
. .
|
||||
. am MTO
|
||||
. ad l
|
||||
. .
|
||||
. LINKSTYLE blue R < >
|
||||
.\}
|
||||
.SH "NAME"
|
||||
pgpainless\-cli\-inline\-verify \- Verify an inline\-signed message
|
||||
.SH "SYNOPSIS"
|
||||
.sp
|
||||
\fBpgpainless\-cli inline\-verify\fP [\fB\-\-stacktrace\fP] [\fB\-\-not\-after\fP=\fIDATE\fP] [\fB\-\-not\-before\fP=\fIDATE\fP]
|
||||
[\fB\-\-verifications\-out\fP=\fIVERIFICATIONS\fP] [\fICERT\fP...]
|
||||
.SH "DESCRIPTION"
|
||||
|
||||
.SH "OPTIONS"
|
||||
.sp
|
||||
\fB\-\-not\-after\fP=\fIDATE\fP
|
||||
.RS 4
|
||||
ISO\-8601 formatted UTC date (e.g. \(aq2020\-11\-23T16:35Z)
|
||||
.sp
|
||||
Reject signatures with a creation date not in range.
|
||||
.sp
|
||||
Defaults to current system time ("now").
|
||||
.sp
|
||||
Accepts special value "\-" for end of time.
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-not\-before\fP=\fIDATE\fP
|
||||
.RS 4
|
||||
ISO\-8601 formatted UTC date (e.g. \(aq2020\-11\-23T16:35Z)
|
||||
.sp
|
||||
Reject signatures with a creation date not in range.
|
||||
.sp
|
||||
Defaults to beginning of time ("\-").
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-stacktrace\fP
|
||||
.RS 4
|
||||
Print stacktrace
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-verifications\-out\fP=\fIVERIFICATIONS\fP
|
||||
.RS 4
|
||||
File to write details over successful verifications to
|
||||
.RE
|
||||
.SH "ARGUMENTS"
|
||||
.sp
|
||||
[\fICERT\fP...]
|
||||
.RS 4
|
||||
Public key certificates for signature verification
|
||||
.RE
|
|
@ -1,47 +0,0 @@
|
|||
'\" t
|
||||
.\" Title: pgpainless-cli-list-profiles
|
||||
.\" Author: [see the "AUTHOR(S)" section]
|
||||
.\" Generator: Asciidoctor 2.0.10
|
||||
.\" Manual: PGPainless-CLI Manual
|
||||
.\" Source:
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "PGPAINLESS\-CLI\-LIST\-PROFILES" "1" "" "" "PGPainless\-CLI Manual"
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.ss \n[.ss] 0
|
||||
.nh
|
||||
.ad l
|
||||
.de URL
|
||||
\fI\\$2\fP <\\$1>\\$3
|
||||
..
|
||||
.als MTO URL
|
||||
.if \n[.g] \{\
|
||||
. mso www.tmac
|
||||
. am URL
|
||||
. ad l
|
||||
. .
|
||||
. am MTO
|
||||
. ad l
|
||||
. .
|
||||
. LINKSTYLE blue R < >
|
||||
.\}
|
||||
.SH "NAME"
|
||||
pgpainless\-cli\-list\-profiles \- Emit a list of profiles supported by the identified subcommand
|
||||
.SH "SYNOPSIS"
|
||||
.sp
|
||||
\fBpgpainless\-cli list\-profiles\fP [\fB\-\-stacktrace\fP] \fICOMMAND\fP
|
||||
.SH "DESCRIPTION"
|
||||
|
||||
.SH "OPTIONS"
|
||||
.sp
|
||||
\fB\-\-stacktrace\fP
|
||||
.RS 4
|
||||
Print stacktrace
|
||||
.RE
|
||||
.SH "ARGUMENTS"
|
||||
.sp
|
||||
\fICOMMAND\fP
|
||||
.RS 4
|
||||
Subcommand for which to list profiles
|
||||
.RE
|
|
@ -1,54 +0,0 @@
|
|||
'\" t
|
||||
.\" Title: pgpainless-cli-revoke-key
|
||||
.\" Author: [see the "AUTHOR(S)" section]
|
||||
.\" Generator: Asciidoctor 2.0.10
|
||||
.\" Manual: PGPainless-CLI Manual
|
||||
.\" Source:
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "PGPAINLESS\-CLI\-REVOKE\-KEY" "1" "" "" "PGPainless\-CLI Manual"
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.ss \n[.ss] 0
|
||||
.nh
|
||||
.ad l
|
||||
.de URL
|
||||
\fI\\$2\fP <\\$1>\\$3
|
||||
..
|
||||
.als MTO URL
|
||||
.if \n[.g] \{\
|
||||
. mso www.tmac
|
||||
. am URL
|
||||
. ad l
|
||||
. .
|
||||
. am MTO
|
||||
. ad l
|
||||
. .
|
||||
. LINKSTYLE blue R < >
|
||||
.\}
|
||||
.SH "NAME"
|
||||
pgpainless\-cli\-revoke\-key \- Generate revocation certificates
|
||||
.SH "SYNOPSIS"
|
||||
.sp
|
||||
\fBpgpainless\-cli revoke\-key\fP [\fB\-\-[no\-]armor\fP] [\fB\-\-stacktrace\fP] [\fB\-\-with\-key\-password\fP=\fIPASSWORD\fP]
|
||||
.SH "DESCRIPTION"
|
||||
.sp
|
||||
Emit revocation certificates for secret keys from STDIN to STDOUT.
|
||||
.SH "OPTIONS"
|
||||
.sp
|
||||
\fB\-\-[no\-]armor\fP
|
||||
.RS 4
|
||||
ASCII armor the output
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-stacktrace\fP
|
||||
.RS 4
|
||||
Print stacktrace
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-with\-key\-password\fP=\fIPASSWORD\fP
|
||||
.RS 4
|
||||
Passphrase to unlock the secret key(s).
|
||||
.sp
|
||||
Is an INDIRECT data type (e.g. file, environment variable, file descriptor...).
|
||||
.RE
|
|
@ -1,74 +0,0 @@
|
|||
'\" t
|
||||
.\" Title: pgpainless-cli-sign
|
||||
.\" Author: [see the "AUTHOR(S)" section]
|
||||
.\" Generator: Asciidoctor 2.0.10
|
||||
.\" Manual: PGPainless-CLI Manual
|
||||
.\" Source:
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "PGPAINLESS\-CLI\-SIGN" "1" "" "" "PGPainless\-CLI Manual"
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.ss \n[.ss] 0
|
||||
.nh
|
||||
.ad l
|
||||
.de URL
|
||||
\fI\\$2\fP <\\$1>\\$3
|
||||
..
|
||||
.als MTO URL
|
||||
.if \n[.g] \{\
|
||||
. mso www.tmac
|
||||
. am URL
|
||||
. ad l
|
||||
. .
|
||||
. am MTO
|
||||
. ad l
|
||||
. .
|
||||
. LINKSTYLE blue R < >
|
||||
.\}
|
||||
.SH "NAME"
|
||||
pgpainless\-cli\-sign \- Create a detached message signature
|
||||
.SH "SYNOPSIS"
|
||||
.sp
|
||||
\fBpgpainless\-cli sign\fP [\fB\-\-[no\-]armor\fP] [\fB\-\-stacktrace\fP] [\fB\-\-as\fP=\fI{binary|text}\fP]
|
||||
[\fB\-\-micalg\-out\fP=\fIMICALG\fP] [\fB\-\-with\-key\-password\fP=\fIPASSWORD\fP]... [\fIKEYS\fP...]
|
||||
.SH "DESCRIPTION"
|
||||
|
||||
.SH "OPTIONS"
|
||||
.sp
|
||||
\fB\-\-as\fP=\fI{binary|text}\fP
|
||||
.RS 4
|
||||
Specify the output format of the signed message.
|
||||
.sp
|
||||
Defaults to \(aqbinary\(aq.
|
||||
.sp
|
||||
If \(aq\-\-as=text\(aq and the input data is not valid UTF\-8, sign fails with return code 53.
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-micalg\-out\fP=\fIMICALG\fP
|
||||
.RS 4
|
||||
Emits the digest algorithm used to the specified file in a way that can be used to populate the micalg parameter for the PGP/MIME Content\-Type (RFC3156).
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-[no\-]armor\fP
|
||||
.RS 4
|
||||
ASCII armor the output
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-stacktrace\fP
|
||||
.RS 4
|
||||
Print stacktrace
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-with\-key\-password\fP=\fIPASSWORD\fP
|
||||
.RS 4
|
||||
Passphrase to unlock the secret key(s).
|
||||
.sp
|
||||
Is an INDIRECT data type (e.g. file, environment variable, file descriptor...).
|
||||
.RE
|
||||
.SH "ARGUMENTS"
|
||||
.sp
|
||||
[\fIKEYS\fP...]
|
||||
.RS 4
|
||||
Secret keys used for signing
|
||||
.RE
|
|
@ -1,74 +0,0 @@
|
|||
'\" t
|
||||
.\" Title: pgpainless-cli-verify
|
||||
.\" Author: [see the "AUTHOR(S)" section]
|
||||
.\" Generator: Asciidoctor 2.0.10
|
||||
.\" Manual: PGPainless-CLI Manual
|
||||
.\" Source:
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "PGPAINLESS\-CLI\-VERIFY" "1" "" "" "PGPainless\-CLI Manual"
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.ss \n[.ss] 0
|
||||
.nh
|
||||
.ad l
|
||||
.de URL
|
||||
\fI\\$2\fP <\\$1>\\$3
|
||||
..
|
||||
.als MTO URL
|
||||
.if \n[.g] \{\
|
||||
. mso www.tmac
|
||||
. am URL
|
||||
. ad l
|
||||
. .
|
||||
. am MTO
|
||||
. ad l
|
||||
. .
|
||||
. LINKSTYLE blue R < >
|
||||
.\}
|
||||
.SH "NAME"
|
||||
pgpainless\-cli\-verify \- Verify a detached signature
|
||||
.SH "SYNOPSIS"
|
||||
.sp
|
||||
\fBpgpainless\-cli verify\fP [\fB\-\-stacktrace\fP] [\fB\-\-not\-after\fP=\fIDATE\fP] [\fB\-\-not\-before\fP=\fIDATE\fP] \fISIGNATURE\fP
|
||||
\fICERT\fP...
|
||||
.SH "DESCRIPTION"
|
||||
.sp
|
||||
Verify a detached signature over some data from STDIN.
|
||||
.SH "OPTIONS"
|
||||
.sp
|
||||
\fB\-\-not\-after\fP=\fIDATE\fP
|
||||
.RS 4
|
||||
ISO\-8601 formatted UTC date (e.g. \(aq2020\-11\-23T16:35Z)
|
||||
.sp
|
||||
Reject signatures with a creation date not in range.
|
||||
.sp
|
||||
Defaults to current system time ("now").
|
||||
.sp
|
||||
Accepts special value "\-" for end of time.
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-not\-before\fP=\fIDATE\fP
|
||||
.RS 4
|
||||
ISO\-8601 formatted UTC date (e.g. \(aq2020\-11\-23T16:35Z)
|
||||
.sp
|
||||
Reject signatures with a creation date not in range.
|
||||
.sp
|
||||
Defaults to beginning of time ("\-").
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-stacktrace\fP
|
||||
.RS 4
|
||||
Print stacktrace
|
||||
.RE
|
||||
.SH "ARGUMENTS"
|
||||
.sp
|
||||
\fISIGNATURE\fP
|
||||
.RS 4
|
||||
Detached signature
|
||||
.RE
|
||||
.sp
|
||||
\fICERT\fP...
|
||||
.RS 4
|
||||
Public key certificates for signature verification
|
||||
.RE
|
|
@ -1,56 +0,0 @@
|
|||
'\" t
|
||||
.\" Title: pgpainless-cli-version
|
||||
.\" Author: [see the "AUTHOR(S)" section]
|
||||
.\" Generator: Asciidoctor 2.0.10
|
||||
.\" Manual: PGPainless-CLI Manual
|
||||
.\" Source:
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "PGPAINLESS\-CLI\-VERSION" "1" "" "" "PGPainless\-CLI Manual"
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.ss \n[.ss] 0
|
||||
.nh
|
||||
.ad l
|
||||
.de URL
|
||||
\fI\\$2\fP <\\$1>\\$3
|
||||
..
|
||||
.als MTO URL
|
||||
.if \n[.g] \{\
|
||||
. mso www.tmac
|
||||
. am URL
|
||||
. ad l
|
||||
. .
|
||||
. am MTO
|
||||
. ad l
|
||||
. .
|
||||
. LINKSTYLE blue R < >
|
||||
.\}
|
||||
.SH "NAME"
|
||||
pgpainless\-cli\-version \- Display version information about the tool
|
||||
.SH "SYNOPSIS"
|
||||
.sp
|
||||
\fBpgpainless\-cli version\fP [\fB\-\-stacktrace\fP] [\fB\-\-extended\fP | \fB\-\-backend\fP | \fB\-\-pgpainless\-cli\-spec\fP | \fB\-\-sopv\fP]
|
||||
.SH "DESCRIPTION"
|
||||
|
||||
.SH "OPTIONS"
|
||||
.sp
|
||||
\fB\-\-backend\fP
|
||||
.RS 4
|
||||
Print information about the cryptographic backend
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-extended\fP
|
||||
.RS 4
|
||||
Print an extended version string
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-pgpainless\-cli\-spec\fP
|
||||
.RS 4
|
||||
Print the latest revision of the SOP specification targeted by the implementation
|
||||
.RE
|
||||
.sp
|
||||
\fB\-\-sopv\fP, \fB\-\-stacktrace\fP
|
||||
.RS 4
|
||||
Print stacktrace
|
||||
.RE
|
|
@ -1,233 +0,0 @@
|
|||
'\" t
|
||||
.\" Title: pgpainless-cli
|
||||
.\" Author: [see the "AUTHOR(S)" section]
|
||||
.\" Generator: Asciidoctor 2.0.10
|
||||
.\" Manual: PGPainless-CLI Manual
|
||||
.\" Source:
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "PGPAINLESS\-CLI" "1" "" "" "PGPainless\-CLI Manual"
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.ss \n[.ss] 0
|
||||
.nh
|
||||
.ad l
|
||||
.de URL
|
||||
\fI\\$2\fP <\\$1>\\$3
|
||||
..
|
||||
.als MTO URL
|
||||
.if \n[.g] \{\
|
||||
. mso www.tmac
|
||||
. am URL
|
||||
. ad l
|
||||
. .
|
||||
. am MTO
|
||||
. ad l
|
||||
. .
|
||||
. LINKSTYLE blue R < >
|
||||
.\}
|
||||
.SH "NAME"
|
||||
pgpainless\-cli \- Stateless OpenPGP Protocol
|
||||
.SH "SYNOPSIS"
|
||||
.sp
|
||||
\fBpgpainless\-cli\fP [\fB\-\-stacktrace\fP] [COMMAND]
|
||||
.SH "DESCRIPTION"
|
||||
|
||||
.SH "OPTIONS"
|
||||
.sp
|
||||
\fB\-\-stacktrace\fP
|
||||
.RS 4
|
||||
Print stacktrace
|
||||
.RE
|
||||
.SH "COMMANDS"
|
||||
.sp
|
||||
\fBversion\fP
|
||||
.RS 4
|
||||
Display version information about the tool
|
||||
.RE
|
||||
.sp
|
||||
\fBlist\-profiles\fP
|
||||
.RS 4
|
||||
Emit a list of profiles supported by the identified subcommand
|
||||
.RE
|
||||
.sp
|
||||
\fBgenerate\-key\fP
|
||||
.RS 4
|
||||
Generate a secret key
|
||||
.RE
|
||||
.sp
|
||||
\fBchange\-key\-password\fP
|
||||
.RS 4
|
||||
Update the password of a key
|
||||
.RE
|
||||
.sp
|
||||
\fBrevoke\-key\fP
|
||||
.RS 4
|
||||
Generate revocation certificates
|
||||
.RE
|
||||
.sp
|
||||
\fBextract\-cert\fP
|
||||
.RS 4
|
||||
Extract a public key certificate from a secret key
|
||||
.RE
|
||||
.sp
|
||||
\fBsign\fP
|
||||
.RS 4
|
||||
Create a detached message signature
|
||||
.RE
|
||||
.sp
|
||||
\fBverify\fP
|
||||
.RS 4
|
||||
Verify a detached signature
|
||||
.RE
|
||||
.sp
|
||||
\fBencrypt\fP
|
||||
.RS 4
|
||||
Encrypt a message from standard input
|
||||
.RE
|
||||
.sp
|
||||
\fBdecrypt\fP
|
||||
.RS 4
|
||||
Decrypt a message
|
||||
.RE
|
||||
.sp
|
||||
\fBinline\-detach\fP
|
||||
.RS 4
|
||||
Split signatures from a clearsigned message
|
||||
.RE
|
||||
.sp
|
||||
\fBinline\-sign\fP
|
||||
.RS 4
|
||||
Create an inline\-signed message
|
||||
.RE
|
||||
.sp
|
||||
\fBinline\-verify\fP
|
||||
.RS 4
|
||||
Verify an inline\-signed message
|
||||
.RE
|
||||
.sp
|
||||
\fBarmor\fP
|
||||
.RS 4
|
||||
Add ASCII Armor to standard input
|
||||
.RE
|
||||
.sp
|
||||
\fBdearmor\fP
|
||||
.RS 4
|
||||
Remove ASCII Armor from standard input
|
||||
.RE
|
||||
.sp
|
||||
\fBhelp\fP
|
||||
.RS 4
|
||||
Stateless OpenPGP Protocol
|
||||
.RE
|
||||
.sp
|
||||
\fBgenerate\-completion\fP
|
||||
.RS 4
|
||||
Stateless OpenPGP Protocol
|
||||
.RE
|
||||
.SH "EXIT CODES:"
|
||||
.sp
|
||||
\fB0\fP
|
||||
.RS 4
|
||||
Successful program execution
|
||||
.RE
|
||||
.sp
|
||||
\fB1\fP
|
||||
.RS 4
|
||||
Generic program error
|
||||
.RE
|
||||
.sp
|
||||
\fB3\fP
|
||||
.RS 4
|
||||
Verification requested but no verifiable signature found
|
||||
.RE
|
||||
.sp
|
||||
\fB13\fP
|
||||
.RS 4
|
||||
Unsupported asymmetric algorithm
|
||||
.RE
|
||||
.sp
|
||||
\fB17\fP
|
||||
.RS 4
|
||||
Certificate is not encryption capable
|
||||
.RE
|
||||
.sp
|
||||
\fB19\fP
|
||||
.RS 4
|
||||
Usage error: Missing argument
|
||||
.RE
|
||||
.sp
|
||||
\fB23\fP
|
||||
.RS 4
|
||||
Incomplete verification instructions
|
||||
.RE
|
||||
.sp
|
||||
\fB29\fP
|
||||
.RS 4
|
||||
Unable to decrypt
|
||||
.RE
|
||||
.sp
|
||||
\fB31\fP
|
||||
.RS 4
|
||||
Password is not human\-readable
|
||||
.RE
|
||||
.sp
|
||||
\fB37\fP
|
||||
.RS 4
|
||||
Unsupported Option
|
||||
.RE
|
||||
.sp
|
||||
\fB41\fP
|
||||
.RS 4
|
||||
Invalid data or data of wrong type encountered
|
||||
.RE
|
||||
.sp
|
||||
\fB53\fP
|
||||
.RS 4
|
||||
Non\-text input received where text was expected
|
||||
.RE
|
||||
.sp
|
||||
\fB59\fP
|
||||
.RS 4
|
||||
Output file already exists
|
||||
.RE
|
||||
.sp
|
||||
\fB61\fP
|
||||
.RS 4
|
||||
Input file does not exist
|
||||
.RE
|
||||
.sp
|
||||
\fB67\fP
|
||||
.RS 4
|
||||
Cannot unlock password protected secret key
|
||||
.RE
|
||||
.sp
|
||||
\fB69\fP
|
||||
.RS 4
|
||||
Unsupported subcommand
|
||||
.RE
|
||||
.sp
|
||||
\fB71\fP
|
||||
.RS 4
|
||||
Unsupported special prefix (e.g. "@ENV/@FD") of indirect parameter
|
||||
.RE
|
||||
.sp
|
||||
\fB73\fP
|
||||
.RS 4
|
||||
Ambiguous input (a filename matching the designator already exists)
|
||||
.RE
|
||||
.sp
|
||||
\fB79\fP
|
||||
.RS 4
|
||||
Key is not signing capable
|
||||
.RE
|
||||
.sp
|
||||
\fB83\fP
|
||||
.RS 4
|
||||
Options were supplied that are incompatible with each other
|
||||
.RE
|
||||
.sp
|
||||
\fB89\fP
|
||||
.RS 4
|
||||
The requested profile is unsupported, or the indicated subcommand does not accept profiles
|
||||
.RE
|
|
@ -1,7 +1,4 @@
|
|||
#!/usr/bin/env bash
|
||||
# SPDX-FileCopyrightText: 2021 Paul Schaub <info@pgpainless.org>
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Pretty fancy method to get reliable the absolute path of a shell
|
||||
# script, *even if it is sourced*. Credits go to GreenFox on
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||
SOP_DIR=$(realpath $SCRIPT_DIR/../../sop-java)
|
||||
[ ! -d "$SOP_DIR" ] && echo "sop-java repository MUST be cloned next to pgpainless repo" && exit 1;
|
||||
SRC_DIR=$SOP_DIR/sop-java-picocli/build/docs/manpage
|
||||
[ ! -d "$SRC_DIR" ] && echo "No sop manpages found. Please run 'gradle asciidoctor' in the sop-java repo." && exit 1;
|
||||
DEST_DIR=$SCRIPT_DIR/packaging/man
|
||||
mkdir -p $DEST_DIR
|
||||
|
||||
for page in $SRC_DIR/*
|
||||
do
|
||||
SRC="${page##*/}"
|
||||
DEST="${SRC/sop/pgpainless-cli}"
|
||||
sed \
|
||||
-e 's/sopv/PLACEHOLDERV/g' \
|
||||
-e 's#.\\" Title: sop#.\\" Title: pgpainless-cli#g' \
|
||||
-e 's/Manual: Sop Manual/Manual: PGPainless-CLI Manual/g' \
|
||||
-e 's/.TH "SOP/.TH "PGPAINLESS\\-CLI/g' \
|
||||
-e 's/"Sop Manual"/"PGPainless\\-CLI Manual"/g' \
|
||||
-e 's/\\fBsop/\\fBpgpainless\\-cli/g' \
|
||||
-e 's/sop/pgpainless\\-cli/g' \
|
||||
-e 's/PLACEHOLDERV/sopv/g' \
|
||||
$page > $DEST_DIR/$DEST
|
||||
done
|
||||
|
|
@ -1,31 +1,30 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/*
|
||||
* Copyright 2020 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pgpainless.cli;
|
||||
|
||||
import org.pgpainless.sop.SOPImpl;
|
||||
import sop.cli.picocli.SopCLI;
|
||||
|
||||
/**
|
||||
* This class merely binds PGPainless to {@link SopCLI} by injecting a {@link SOPImpl} instance.
|
||||
* CLI command calls are then simply forwarded to {@link SopCLI#execute(String[])}.
|
||||
*/
|
||||
public class PGPainlessCLI {
|
||||
|
||||
static {
|
||||
// Prevent slf4j initialization logging
|
||||
// https://github.com/qos-ch/slf4j/issues/422#issuecomment-2277280185
|
||||
System.setProperty("slf4j.internal.verbosity", "WARN");
|
||||
|
||||
SopCLI.EXECUTABLE_NAME = "pgpainless-cli";
|
||||
SopCLI.setSopInstance(new SOPImpl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Main method of the CLI application.
|
||||
* @param args arguments
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
int result = execute(args);
|
||||
if (result != 0) {
|
||||
|
@ -33,12 +32,6 @@ public class PGPainlessCLI {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given command and return the exit code of the program.
|
||||
*
|
||||
* @param args command string array (e.g. ["pgpainless-cli", "generate-key", "Alice"])
|
||||
* @return exit code
|
||||
*/
|
||||
public static int execute(String... args) {
|
||||
return SopCLI.execute(args);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,18 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/*
|
||||
* Copyright 2020 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* PGPainless SOP implementing a Stateless OpenPGP Command Line Interface.
|
||||
* @see <a href="https://tools.ietf.org/html/draft-dkg-openpgp-stateless-cli-01">
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
[
|
||||
]
|
|
@ -1,7 +0,0 @@
|
|||
[
|
||||
{
|
||||
"type":"agent-extracted",
|
||||
"classes":[
|
||||
]
|
||||
}
|
||||
]
|
|
@ -1,2 +0,0 @@
|
|||
[
|
||||
]
|
|
@ -1,891 +0,0 @@
|
|||
[
|
||||
{
|
||||
"name":"[Ljava.lang.Object;"
|
||||
},
|
||||
{
|
||||
"name":"ch.qos.logback.classic.encoder.PatternLayoutEncoder",
|
||||
"queryAllPublicMethods":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"ch.qos.logback.classic.joran.SerializedModelConfigurator",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"ch.qos.logback.classic.util.DefaultJoranConfigurator",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"ch.qos.logback.core.ConsoleAppender",
|
||||
"queryAllPublicMethods":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setTarget","parameterTypes":["java.lang.String"] }]
|
||||
},
|
||||
{
|
||||
"name":"ch.qos.logback.core.OutputStreamAppender",
|
||||
"methods":[{"name":"setEncoder","parameterTypes":["ch.qos.logback.core.encoder.Encoder"] }]
|
||||
},
|
||||
{
|
||||
"name":"ch.qos.logback.core.encoder.Encoder",
|
||||
"methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }]
|
||||
},
|
||||
{
|
||||
"name":"ch.qos.logback.core.encoder.LayoutWrappingEncoder",
|
||||
"methods":[{"name":"setParent","parameterTypes":["ch.qos.logback.core.spi.ContextAware"] }]
|
||||
},
|
||||
{
|
||||
"name":"ch.qos.logback.core.pattern.PatternLayoutEncoderBase",
|
||||
"methods":[{"name":"setPattern","parameterTypes":["java.lang.String"] }]
|
||||
},
|
||||
{
|
||||
"name":"ch.qos.logback.core.spi.ContextAware",
|
||||
"methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }]
|
||||
},
|
||||
{
|
||||
"name":"com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"groovy.lang.Closure"
|
||||
},
|
||||
{
|
||||
"name":"java.io.FilePermission"
|
||||
},
|
||||
{
|
||||
"name":"java.lang.Enum"
|
||||
},
|
||||
{
|
||||
"name":"java.lang.Object",
|
||||
"allDeclaredFields":true,
|
||||
"queryAllDeclaredMethods":true
|
||||
},
|
||||
{
|
||||
"name":"java.lang.RuntimePermission"
|
||||
},
|
||||
{
|
||||
"name":"java.lang.System",
|
||||
"methods":[{"name":"console","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"java.lang.invoke.MethodHandle"
|
||||
},
|
||||
{
|
||||
"name":"java.net.NetPermission"
|
||||
},
|
||||
{
|
||||
"name":"java.net.SocketPermission"
|
||||
},
|
||||
{
|
||||
"name":"java.net.URLPermission",
|
||||
"methods":[{"name":"<init>","parameterTypes":["java.lang.String","java.lang.String"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.nio.channels.SelectionKey",
|
||||
"fields":[{"name":"attachment"}]
|
||||
},
|
||||
{
|
||||
"name":"java.nio.file.Path"
|
||||
},
|
||||
{
|
||||
"name":"java.nio.file.Paths",
|
||||
"methods":[{"name":"get","parameterTypes":["java.lang.String","java.lang.String[]"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.security.AllPermission"
|
||||
},
|
||||
{
|
||||
"name":"java.security.MessageDigestSpi"
|
||||
},
|
||||
{
|
||||
"name":"java.security.SecureRandomParameters"
|
||||
},
|
||||
{
|
||||
"name":"java.security.SecurityPermission"
|
||||
},
|
||||
{
|
||||
"name":"java.security.cert.PKIXRevocationChecker"
|
||||
},
|
||||
{
|
||||
"name":"java.sql.Connection"
|
||||
},
|
||||
{
|
||||
"name":"java.sql.Driver"
|
||||
},
|
||||
{
|
||||
"name":"java.sql.DriverManager",
|
||||
"methods":[{"name":"getConnection","parameterTypes":["java.lang.String"] }, {"name":"getDriver","parameterTypes":["java.lang.String"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.sql.Time",
|
||||
"methods":[{"name":"<init>","parameterTypes":["long"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.sql.Timestamp",
|
||||
"methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.time.Duration",
|
||||
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.time.Instant",
|
||||
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.time.LocalDate",
|
||||
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.time.LocalDateTime",
|
||||
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.time.LocalTime",
|
||||
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.time.MonthDay",
|
||||
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.time.OffsetDateTime",
|
||||
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.time.OffsetTime",
|
||||
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.time.Period",
|
||||
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.time.Year",
|
||||
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.time.YearMonth",
|
||||
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.time.ZoneId",
|
||||
"methods":[{"name":"of","parameterTypes":["java.lang.String"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.time.ZoneOffset",
|
||||
"methods":[{"name":"of","parameterTypes":["java.lang.String"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.time.ZonedDateTime",
|
||||
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.util.HashSet"
|
||||
},
|
||||
{
|
||||
"name":"java.util.LinkedHashSet"
|
||||
},
|
||||
{
|
||||
"name":"java.util.PropertyPermission"
|
||||
},
|
||||
{
|
||||
"name":"java.util.concurrent.ArrayBlockingQueue"
|
||||
},
|
||||
{
|
||||
"name":"java.util.concurrent.atomic.AtomicReference",
|
||||
"fields":[{"name":"value"}]
|
||||
},
|
||||
{
|
||||
"name":"java.util.concurrent.locks.AbstractOwnableSynchronizer"
|
||||
},
|
||||
{
|
||||
"name":"java.util.concurrent.locks.AbstractQueuedSynchronizer"
|
||||
},
|
||||
{
|
||||
"name":"java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject"
|
||||
},
|
||||
{
|
||||
"name":"java.util.concurrent.locks.ReentrantLock"
|
||||
},
|
||||
{
|
||||
"name":"java.util.concurrent.locks.ReentrantLock$NonfairSync"
|
||||
},
|
||||
{
|
||||
"name":"java.util.concurrent.locks.ReentrantLock$Sync"
|
||||
},
|
||||
{
|
||||
"name":"javax.smartcardio.CardPermission"
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.COMPOSITE$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.CONTEXT$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.CompositeSignatures$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.DH$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.DSA$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.DSTU4145$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.Dilithium$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.EC$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.ECGOST$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.EXTERNAL$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.EdEC$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.ElGamal$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.Falcon$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.GM$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.GOST$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.IES$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.LMS$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.MLDSA$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.MLKEM$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.NTRU$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.NoSig$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.RSA$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.SLHDSA$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.SPHINCSPlus$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.X509$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.edec.KeyPairGeneratorSpi$EdDSA",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.edec.KeyPairGeneratorSpi$XDH",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.asymmetric.rsa.KeyPairGeneratorSpi",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.digest.Blake2b$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.digest.Blake2s$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.digest.Blake3$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.digest.DSTU7564$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.digest.GOST3411$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.digest.Haraka$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.digest.Keccak$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.digest.MD2$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.digest.MD4$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.digest.MD5$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.digest.RIPEMD128$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.digest.RIPEMD160$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.digest.RIPEMD256$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.digest.RIPEMD320$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.digest.SHA1$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.digest.SHA224$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.digest.SHA256$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.digest.SHA3$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.digest.SHA384$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.digest.SHA512$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.digest.SM3$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.digest.Skein$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.digest.Tiger$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.digest.Whirlpool$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.drbg.DRBG$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.keystore.BC$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.keystore.BCFKS$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.keystore.PKCS12$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.AES$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.ARC4$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.ARIA$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.Blowfish$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.CAST5$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.CAST6$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.Camellia$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.ChaCha$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.DES$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.DESede$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.DSTU7624$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.GOST28147$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.GOST3412_2015$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.Grain128$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.Grainv1$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.HC128$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.HC256$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.IDEA$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.Noekeon$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.OpenSSLPBKDF$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF1$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.PBEPKCS12$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.Poly1305$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.RC2$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.RC5$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.RC6$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.Rijndael$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.SCRYPT$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.SEED$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.SM4$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.Salsa20$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.Serpent$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.Shacal2$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.SipHash$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.SipHash128$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.Skipjack$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.TEA$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.TLSKDF$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.Threefish$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.Twofish$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.VMPC$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.VMPCKSA3$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.XSalsa20$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.XTEA$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.bouncycastle.jcajce.provider.symmetric.Zuc$Mappings",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.pgpainless.cli.ExitCodeTest",
|
||||
"allDeclaredFields":true,
|
||||
"allDeclaredClasses":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"queryAllPublicMethods":true,
|
||||
"queryAllDeclaredConstructors":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"successfulExecutionDoesNotTerminateJVM","parameterTypes":[] }, {"name":"testCommandWithUnknownOption_37","parameterTypes":[] }, {"name":"testUnknownCommand_69","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.pgpainless.cli.TestUtils",
|
||||
"allDeclaredClasses":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"queryAllPublicMethods":true
|
||||
},
|
||||
{
|
||||
"name":"org.pgpainless.cli.commands.ArmorCmdTest",
|
||||
"allDeclaredFields":true,
|
||||
"allDeclaredClasses":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"queryAllPublicMethods":true,
|
||||
"queryAllDeclaredConstructors":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"armorAlreadyArmoredDataIsIdempotent","parameterTypes":[] }, {"name":"armorMessage","parameterTypes":[] }, {"name":"armorPublicKey","parameterTypes":[] }, {"name":"armorSecretKey","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.pgpainless.cli.commands.CLITest",
|
||||
"allDeclaredFields":true,
|
||||
"allDeclaredClasses":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"queryAllPublicMethods":true,
|
||||
"methods":[{"name":"cleanup","parameterTypes":[] }, {"name":"setup","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.pgpainless.cli.commands.DearmorCmdTest",
|
||||
"allDeclaredFields":true,
|
||||
"allDeclaredClasses":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"queryAllPublicMethods":true,
|
||||
"queryAllDeclaredConstructors":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"dearmorBrokenArmoredKeyFails","parameterTypes":[] }, {"name":"dearmorCertificate","parameterTypes":[] }, {"name":"dearmorGarbageEmitsEmpty","parameterTypes":[] }, {"name":"dearmorMessage","parameterTypes":[] }, {"name":"dearmorSecretKey","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.pgpainless.cli.commands.ExtractCertCmdTest",
|
||||
"allDeclaredFields":true,
|
||||
"allDeclaredClasses":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"queryAllPublicMethods":true,
|
||||
"queryAllDeclaredConstructors":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"extractCertFromGarbageFails","parameterTypes":[] }, {"name":"testExtractCert","parameterTypes":[] }, {"name":"testExtractCertFromCertFails","parameterTypes":[] }, {"name":"testExtractCertUnarmored","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.pgpainless.cli.commands.GenerateKeyCmdTest",
|
||||
"allDeclaredFields":true,
|
||||
"allDeclaredClasses":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"queryAllPublicMethods":true,
|
||||
"queryAllDeclaredConstructors":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"testGenerateBinaryKey","parameterTypes":[] }, {"name":"testGenerateKey","parameterTypes":[] }, {"name":"testGenerateKeyWithMultipleUserIds","parameterTypes":[] }, {"name":"testGeneratePasswordProtectedKey_missingPasswordFile","parameterTypes":[] }, {"name":"testPasswordProtectedKey","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.pgpainless.cli.commands.InlineDetachCmdTest",
|
||||
"allDeclaredFields":true,
|
||||
"allDeclaredClasses":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"queryAllPublicMethods":true,
|
||||
"queryAllDeclaredConstructors":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"detachInbandSignatureAndMessage","parameterTypes":[] }, {"name":"detachInbandSignatureAndMessageNoArmor","parameterTypes":[] }, {"name":"detachMissingSignaturesFromCleartextSignedMessageFails","parameterTypes":[] }, {"name":"detachNonOpenPgpDataFails","parameterTypes":[] }, {"name":"existingSignatureOutCausesException","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.pgpainless.cli.commands.ListProfilesCmdTest",
|
||||
"allDeclaredFields":true,
|
||||
"allDeclaredClasses":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"queryAllPublicMethods":true,
|
||||
"queryAllDeclaredConstructors":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"listProfileOfGenerateKey","parameterTypes":[] }, {"name":"listProfilesOfEncrypt","parameterTypes":[] }, {"name":"listProfilesWithoutCommand","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.pgpainless.cli.commands.RoundTripEncryptDecryptCmdTest",
|
||||
"allDeclaredFields":true,
|
||||
"allDeclaredClasses":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"queryAllPublicMethods":true,
|
||||
"queryAllDeclaredConstructors":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"decryptGarbageFails","parameterTypes":[] }, {"name":"decryptMalformedMessageYieldsBadData","parameterTypes":[] }, {"name":"decryptMessageWithSessionKey","parameterTypes":[] }, {"name":"decryptMessageWithWrongKeyFails","parameterTypes":[] }, {"name":"decryptWithPasswordWithPendingWhitespaceWorks","parameterTypes":[] }, {"name":"decryptWithWhitespacePasswordWorks","parameterTypes":[] }, {"name":"decrypt_verifyWithGarbageCertFails","parameterTypes":[] }, {"name":"decrypt_withGarbageKeyFails","parameterTypes":[] }, {"name":"encryptAndDecryptAMessage","parameterTypes":[] }, {"name":"encryptAndDecryptMessageWithPassphrase","parameterTypes":[] }, {"name":"encryptWithGarbageCertFails","parameterTypes":[] }, {"name":"encryptWithPasswordADecryptWithPasswordBFails","parameterTypes":[] }, {"name":"encryptWithProtectedKey_wrongPassphraseFails","parameterTypes":[] }, {"name":"encryptWithTrailingWhitespaceDecryptWithoutWorks","parameterTypes":[] }, {"name":"encrypt_signWithGarbageKeyFails","parameterTypes":[] }, {"name":"testDecryptVerifyOut_withoutVerifyWithFails","parameterTypes":[] }, {"name":"testDecryptWithSessionKeyVerifyWithYieldsExpectedVerifications","parameterTypes":[] }, {"name":"testDecryptWithoutDecryptionOptionFails","parameterTypes":[] }, {"name":"testEncryptDecryptRoundTripWithPasswordProtectedKey","parameterTypes":[] }, {"name":"testEncryptDecryptWithFreshRSAKey","parameterTypes":[] }, {"name":"testEncryptWithIncapableCert","parameterTypes":[] }, {"name":"testEncrypt_SignWithCertFails","parameterTypes":[] }, {"name":"testMissingArgumentsIfNoArgsSupplied","parameterTypes":[] }, {"name":"testSessionKeyOutWritesSessionKeyOut","parameterTypes":[] }, {"name":"testSignWithIncapableKey","parameterTypes":[] }, {"name":"testVerificationsOutAlreadyExistFails","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.pgpainless.cli.commands.RoundTripInlineSignInlineVerifyCmdTest",
|
||||
"allDeclaredFields":true,
|
||||
"allDeclaredClasses":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"queryAllPublicMethods":true,
|
||||
"queryAllDeclaredConstructors":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"cannotVerifyEncryptedMessage","parameterTypes":[] }, {"name":"cannotVerifyMalformedMessage","parameterTypes":[] }, {"name":"createAndVerifyCleartextSignedMessage","parameterTypes":[] }, {"name":"createAndVerifyMultiKeyBinarySignedMessage","parameterTypes":[] }, {"name":"createAndVerifyTextSignedMessage","parameterTypes":[] }, {"name":"createCleartextSignedMessage","parameterTypes":[] }, {"name":"createMalformedMessage","parameterTypes":[] }, {"name":"createSignedMessageWithKeyAAndVerifyWithKeyBFails","parameterTypes":[] }, {"name":"createTextSignedMessageInlineDetachAndDetachedVerify","parameterTypes":[] }, {"name":"signWithProtectedKeyWithWrongPassphraseFails","parameterTypes":[] }, {"name":"testInlineSignWithMissingSecretKeysFails","parameterTypes":[] }, {"name":"testUnlockKeyWithOneOfMultiplePasswords","parameterTypes":[] }, {"name":"verifyPrependedSignedMessage","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.pgpainless.cli.commands.RoundTripInlineSignVerifyCmdTest",
|
||||
"allDeclaredFields":true,
|
||||
"allDeclaredClasses":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"queryAllPublicMethods":true,
|
||||
"queryAllDeclaredConstructors":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"encryptAndDecryptAMessage","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.pgpainless.cli.commands.RoundTripSignVerifyCmdTest",
|
||||
"allDeclaredFields":true,
|
||||
"allDeclaredClasses":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"queryAllPublicMethods":true,
|
||||
"queryAllDeclaredConstructors":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"createArmoredSignature","parameterTypes":[] }, {"name":"createUnarmoredSignature","parameterTypes":[] }, {"name":"signWithProtectedKey","parameterTypes":[] }, {"name":"signWithProtectedKey_missingPassphraseFails","parameterTypes":[] }, {"name":"signWithProtectedKey_wrongPassphraseFails","parameterTypes":[] }, {"name":"testNotAfter","parameterTypes":[] }, {"name":"testNotBefore","parameterTypes":[] }, {"name":"testSignWithIncapableKey","parameterTypes":[] }, {"name":"testSignatureCreationAndVerification","parameterTypes":[] }, {"name":"unarmorArmoredSigAndVerify","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.pgpainless.cli.commands.VersionCmdTest",
|
||||
"allDeclaredFields":true,
|
||||
"allDeclaredClasses":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"queryAllPublicMethods":true,
|
||||
"queryAllDeclaredConstructors":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"testExtendedVersion","parameterTypes":[] }, {"name":"testGetBackendVersion","parameterTypes":[] }, {"name":"testSopSpecVersion","parameterTypes":[] }, {"name":"testVersion","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"org.pgpainless.cli.misc.SignUsingPublicKeyBehaviorTest",
|
||||
"allDeclaredFields":true,
|
||||
"allDeclaredClasses":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"queryAllPublicMethods":true,
|
||||
"queryAllDeclaredConstructors":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"testSignatureCreationAndVerification","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"picocli.AutoComplete$GenerateCompletion",
|
||||
"allDeclaredFields":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"picocli.CommandLine$AutoHelpMixin",
|
||||
"allDeclaredFields":true,
|
||||
"queryAllDeclaredMethods":true
|
||||
},
|
||||
{
|
||||
"name":"picocli.CommandLine$HelpCommand",
|
||||
"allDeclaredFields":true,
|
||||
"queryAllDeclaredMethods":true
|
||||
},
|
||||
{
|
||||
"name":"sop.cli.picocli.SopCLI",
|
||||
"allDeclaredFields":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"sop.cli.picocli.SopCLI$InitLocale",
|
||||
"allDeclaredFields":true,
|
||||
"queryAllDeclaredMethods":true
|
||||
},
|
||||
{
|
||||
"name":"sop.cli.picocli.commands.AbstractSopCmd",
|
||||
"allDeclaredFields":true,
|
||||
"queryAllDeclaredMethods":true
|
||||
},
|
||||
{
|
||||
"name":"sop.cli.picocli.commands.ArmorCmd",
|
||||
"allDeclaredFields":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"sop.cli.picocli.commands.ChangeKeyPasswordCmd",
|
||||
"allDeclaredFields":true,
|
||||
"queryAllDeclaredMethods":true
|
||||
},
|
||||
{
|
||||
"name":"sop.cli.picocli.commands.DearmorCmd",
|
||||
"allDeclaredFields":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"sop.cli.picocli.commands.DecryptCmd",
|
||||
"allDeclaredFields":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"sop.cli.picocli.commands.EncryptCmd",
|
||||
"allDeclaredFields":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"sop.cli.picocli.commands.ExtractCertCmd",
|
||||
"allDeclaredFields":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"sop.cli.picocli.commands.GenerateKeyCmd",
|
||||
"allDeclaredFields":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"sop.cli.picocli.commands.InlineDetachCmd",
|
||||
"allDeclaredFields":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"sop.cli.picocli.commands.InlineSignCmd",
|
||||
"allDeclaredFields":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"sop.cli.picocli.commands.InlineVerifyCmd",
|
||||
"allDeclaredFields":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"sop.cli.picocli.commands.ListProfilesCmd",
|
||||
"allDeclaredFields":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"sop.cli.picocli.commands.RevokeKeyCmd",
|
||||
"allDeclaredFields":true,
|
||||
"queryAllDeclaredMethods":true
|
||||
},
|
||||
{
|
||||
"name":"sop.cli.picocli.commands.SignCmd",
|
||||
"allDeclaredFields":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"sop.cli.picocli.commands.VerifyCmd",
|
||||
"allDeclaredFields":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"sop.cli.picocli.commands.VersionCmd",
|
||||
"allDeclaredFields":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"sop.cli.picocli.commands.VersionCmd$Exclusive",
|
||||
"allDeclaredFields":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"sun.security.provider.NativePRNG",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"<init>","parameterTypes":["java.security.SecureRandomParameters"] }]
|
||||
},
|
||||
{
|
||||
"name":"sun.security.provider.SHA",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
}
|
||||
]
|
|
@ -1,93 +0,0 @@
|
|||
{
|
||||
"resources":{
|
||||
"includes":[{
|
||||
"pattern":"\\QMETA-INF/services/ch.qos.logback.classic.spi.Configurator\\E"
|
||||
}, {
|
||||
"pattern":"\\QMETA-INF/services/java.lang.System$LoggerFinder\\E"
|
||||
}, {
|
||||
"pattern":"\\QMETA-INF/services/java.nio.channels.spi.SelectorProvider\\E"
|
||||
}, {
|
||||
"pattern":"\\QMETA-INF/services/java.time.zone.ZoneRulesProvider\\E"
|
||||
}, {
|
||||
"pattern":"\\QMETA-INF/services/java.util.spi.ResourceBundleControlProvider\\E"
|
||||
}, {
|
||||
"pattern":"\\QMETA-INF/services/javax.xml.parsers.SAXParserFactory\\E"
|
||||
}, {
|
||||
"pattern":"\\QMETA-INF/services/org.junit.platform.engine.TestEngine\\E"
|
||||
}, {
|
||||
"pattern":"\\QMETA-INF/services/org.junit.platform.launcher.LauncherDiscoveryListener\\E"
|
||||
}, {
|
||||
"pattern":"\\QMETA-INF/services/org.junit.platform.launcher.LauncherSessionListener\\E"
|
||||
}, {
|
||||
"pattern":"\\QMETA-INF/services/org.junit.platform.launcher.PostDiscoveryFilter\\E"
|
||||
}, {
|
||||
"pattern":"\\QMETA-INF/services/org.junit.platform.launcher.TestExecutionListener\\E"
|
||||
}, {
|
||||
"pattern":"\\QMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider\\E"
|
||||
}, {
|
||||
"pattern":"\\Qjunit-platform.properties\\E"
|
||||
}, {
|
||||
"pattern":"\\Qlogback-test.scmo\\E"
|
||||
}, {
|
||||
"pattern":"\\Qlogback-test.xml\\E"
|
||||
}, {
|
||||
"pattern":"\\Qlogback.scmo\\E"
|
||||
}, {
|
||||
"pattern":"\\Qorg/slf4j/impl/StaticLoggerBinder.class\\E"
|
||||
}, {
|
||||
"pattern":"\\Qpgpainless-sop.properties\\E"
|
||||
}, {
|
||||
"pattern":"\\Qsop-java-version.properties\\E"
|
||||
}, {
|
||||
"pattern":"java.base:\\Qsun/text/resources/LineBreakIteratorData\\E"
|
||||
}]},
|
||||
"bundles":[{
|
||||
"name":"msg_armor",
|
||||
"locales":["de", "und"]
|
||||
}, {
|
||||
"name":"msg_change-key-password",
|
||||
"locales":["de", "und"]
|
||||
}, {
|
||||
"name":"msg_dearmor",
|
||||
"locales":["de", "und"]
|
||||
}, {
|
||||
"name":"msg_decrypt",
|
||||
"locales":["de", "und"]
|
||||
}, {
|
||||
"name":"msg_detached-sign",
|
||||
"locales":["de", "und"]
|
||||
}, {
|
||||
"name":"msg_detached-verify",
|
||||
"locales":["de", "und"]
|
||||
}, {
|
||||
"name":"msg_encrypt",
|
||||
"locales":["de", "und"]
|
||||
}, {
|
||||
"name":"msg_extract-cert",
|
||||
"locales":["de", "und"]
|
||||
}, {
|
||||
"name":"msg_generate-key",
|
||||
"locales":["de", "und"]
|
||||
}, {
|
||||
"name":"msg_inline-detach",
|
||||
"locales":["de", "und"]
|
||||
}, {
|
||||
"name":"msg_inline-sign",
|
||||
"locales":["de", "und"]
|
||||
}, {
|
||||
"name":"msg_inline-verify",
|
||||
"locales":["de", "und"]
|
||||
}, {
|
||||
"name":"msg_list-profiles",
|
||||
"locales":["de", "und"]
|
||||
}, {
|
||||
"name":"msg_revoke-key",
|
||||
"locales":["de", "und"]
|
||||
}, {
|
||||
"name":"msg_sop",
|
||||
"locales":["de", "und"]
|
||||
}, {
|
||||
"name":"msg_version",
|
||||
"locales":["de", "und"]
|
||||
}]
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
{
|
||||
"types":[
|
||||
{
|
||||
"name":"java.lang.Enum"
|
||||
},
|
||||
{
|
||||
"name":"java.lang.Object[]"
|
||||
},
|
||||
{
|
||||
"name":"java.util.HashSet"
|
||||
},
|
||||
{
|
||||
"name":"java.util.LinkedHashSet"
|
||||
},
|
||||
{
|
||||
"name":"java.util.concurrent.ArrayBlockingQueue"
|
||||
},
|
||||
{
|
||||
"name":"java.util.concurrent.locks.AbstractOwnableSynchronizer"
|
||||
},
|
||||
{
|
||||
"name":"java.util.concurrent.locks.AbstractQueuedSynchronizer"
|
||||
},
|
||||
{
|
||||
"name":"java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject"
|
||||
},
|
||||
{
|
||||
"name":"java.util.concurrent.locks.ReentrantLock"
|
||||
},
|
||||
{
|
||||
"name":"java.util.concurrent.locks.ReentrantLock$NonfairSync"
|
||||
},
|
||||
{
|
||||
"name":"java.util.concurrent.locks.ReentrantLock$Sync"
|
||||
}
|
||||
],
|
||||
"lambdaCapturingTypes":[
|
||||
],
|
||||
"proxies":[
|
||||
]
|
||||
}
|
|
@ -1,9 +1,12 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
<configuration>
|
||||
<appender name="STDERR" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<target>System.err</target>
|
||||
<encoder>
|
||||
<pattern>%blue(%-5level) %green(%logger{35}) - %msg %n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<configuration debug="false">
|
||||
<statusListener class="ch.qos.logback.core.status.NopStatusListener" />
|
||||
<logger name="org.apache" level="INFO ">
|
||||
<appender-ref ref="STDERR"/>
|
||||
</logger>
|
||||
</configuration>
|
|
@ -1,38 +1,41 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/*
|
||||
* Copyright 2021 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pgpainless.cli;
|
||||
|
||||
import com.ginsberg.junit.exit.ExpectSystemExitWithStatus;
|
||||
import com.ginsberg.junit.exit.FailOnSystemExit;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.pgpainless.cli.commands.CLITest;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sop.exception.SOPGPException;
|
||||
|
||||
import java.io.IOException;
|
||||
public class ExitCodeTest {
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class ExitCodeTest extends CLITest {
|
||||
|
||||
public ExitCodeTest() {
|
||||
super(LoggerFactory.getLogger(ExitCodeTest.class));
|
||||
@Test
|
||||
@ExpectSystemExitWithStatus(69)
|
||||
public void testUnknownCommand_69() {
|
||||
PGPainlessCLI.main(new String[] {"generate-kex"});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnknownCommand_69() throws IOException {
|
||||
assertEquals(SOPGPException.UnsupportedSubcommand.EXIT_CODE,
|
||||
executeCommand("unsupported-subcommand"));
|
||||
@ExpectSystemExitWithStatus(37)
|
||||
public void testCommandWithUnknownOption_37() {
|
||||
PGPainlessCLI.main(new String[] {"generate-key", "-k", "\"k is unknown\""});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommandWithUnknownOption_37() throws IOException {
|
||||
assertEquals(SOPGPException.UnsupportedOption.EXIT_CODE,
|
||||
executeCommand("generate-key", "-k", "\"k is unknown\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void successfulExecutionDoesNotTerminateJVM() throws IOException {
|
||||
assertSuccess(executeCommand("version"));
|
||||
@FailOnSystemExit
|
||||
public void successfulExecutionDoesNotTerminateJVM() {
|
||||
PGPainlessCLI.main(new String[] {"version"});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,18 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/*
|
||||
* Copyright 2021 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pgpainless.cli;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
|
@ -11,7 +22,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
|||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
@ -41,17 +51,6 @@ public class TestUtils {
|
|||
return dir;
|
||||
}
|
||||
|
||||
public static File writeTempFile(File tempDir, byte[] value) throws IOException {
|
||||
File tempFile = new File(tempDir, randomString(10));
|
||||
tempFile.createNewFile();
|
||||
tempFile.deleteOnExit();
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(tempFile);
|
||||
fileOutputStream.write(value);
|
||||
fileOutputStream.flush();
|
||||
fileOutputStream.close();
|
||||
return tempFile;
|
||||
}
|
||||
|
||||
private static String randomString(int length) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < length; i++) {
|
||||
|
|
|
@ -1,100 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.cli.commands;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ArmorCmdTest extends CLITest {
|
||||
|
||||
public ArmorCmdTest() {
|
||||
super(LoggerFactory.getLogger(ArmorCmdTest.class));
|
||||
}
|
||||
|
||||
private static final String key = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
|
||||
"Version: PGPainless\n" +
|
||||
"Comment: 62E9 DDA4 F20F 8341 D2BC 4B4C 8B07 5177 01F9 534C\n" +
|
||||
"Comment: alice@pgpainless.org\n" +
|
||||
"\n" +
|
||||
"lFgEY2vOkhYJKwYBBAHaRw8BAQdAqGOtLd1tKnuwaYYcdr2/7C0cPiCCggRMKG+W\n" +
|
||||
"t32QQdEAAP9VaBzjk/AaAqyykZnQHmS1HByEvRLv5/4yJMSr22451BFjtBRhbGlj\n" +
|
||||
"ZUBwZ3BhaW5sZXNzLm9yZ4iOBBMWCgBBBQJja86SCRCLB1F3AflTTBYhBGLp3aTy\n" +
|
||||
"D4NB0rxLTIsHUXcB+VNMAp4BApsBBRYCAwEABAsJCAcFFQoJCAsCmQEAACZhAP4s\n" +
|
||||
"8hn/RBDvyLvGROOd15EYATnWlgyi+b5WXP6cELalJwD1FZy3RROhfNtZWcJPS43f\n" +
|
||||
"G03pYNyb0NXoitIMAaXEB5xdBGNrzpISCisGAQQBl1UBBQEBB0CqCcYethOynfni\n" +
|
||||
"8uRO+r/cZWp9hCLy8pRIExKqzcyEFAMBCAcAAP9sRRLoZkLpDaTNNrtIBovXu2AN\n" +
|
||||
"hL8keUMWtVcuEHnkQA6iiHUEGBYKAB0FAmNrzpICngECmwwFFgIDAQAECwkIBwUV\n" +
|
||||
"CgkICwAKCRCLB1F3AflTTBVpAP491etrjqCMWx2bBaw3K1vP0Mix6U0vF3J4kP9U\n" +
|
||||
"eZm6owEA4kX9VAGESvLgIc7CEiswmxdWjxnLQyCRtWXfjgFmYQucWARja86SFgkr\n" +
|
||||
"BgEEAdpHDwEBB0DBslhDpWC6CV3xJUSo071NSO5Cf4fgOwOj+QHs8mpFbwABAPkQ\n" +
|
||||
"ioSydYiMi04LyfPohyrhhcdJDHallQg+jYHHUb2pEJCI1QQYFgoAfQUCY2vOkgKe\n" +
|
||||
"AQKbAgUWAgMBAAQLCQgHBRUKCQgLXyAEGRYKAAYFAmNrzpIACgkQiHlkvEXh+f1e\n" +
|
||||
"ywEA9A2GLU9LxCJxZf2X4qcZY//YJDChIZHPnY0Vaek1DsMBAN1YILrH2rxQeCXj\n" +
|
||||
"m4bUKfJIRrGt6ZJscwORgNI1dFQFAAoJEIsHUXcB+VNMK3gA/3vvPm57JsHA860w\n" +
|
||||
"lB4D1II71oFNL8TFnJqTAvpSKe1AAP49S4mKB4PE0ElcDo7n+nEYt6ba8IMRDlMo\n" +
|
||||
"rsH85mUgCw==\n" +
|
||||
"=EMKf\n" +
|
||||
"-----END PGP PRIVATE KEY BLOCK-----\n";
|
||||
|
||||
@Test
|
||||
public void armorSecretKey() throws IOException {
|
||||
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(key);
|
||||
byte[] binary = secretKeys.getEncoded();
|
||||
|
||||
pipeBytesToStdin(binary);
|
||||
ByteArrayOutputStream armorOut = pipeStdoutToStream();
|
||||
assertSuccess(executeCommand("armor"));
|
||||
|
||||
PGPSecretKeyRing armored = PGPainless.readKeyRing().secretKeyRing(armorOut.toString());
|
||||
assertArrayEquals(secretKeys.getEncoded(), armored.getEncoded());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void armorPublicKey() throws IOException {
|
||||
PGPSecretKeyRing secretKey = PGPainless.readKeyRing().secretKeyRing(key);
|
||||
PGPPublicKeyRing publicKey = PGPainless.extractCertificate(secretKey);
|
||||
byte[] bytes = publicKey.getEncoded();
|
||||
|
||||
pipeBytesToStdin(bytes);
|
||||
ByteArrayOutputStream armorOut = pipeStdoutToStream();
|
||||
assertSuccess(executeCommand("armor"));
|
||||
|
||||
PGPPublicKeyRing armored = PGPainless.readKeyRing().publicKeyRing(armorOut.toString());
|
||||
assertArrayEquals(publicKey.getEncoded(), armored.getEncoded());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void armorMessage() throws IOException {
|
||||
String message = "Hello, World!\n";
|
||||
|
||||
pipeStringToStdin(message);
|
||||
ByteArrayOutputStream armorOut = pipeStdoutToStream();
|
||||
assertSuccess(executeCommand("armor"));
|
||||
|
||||
String armored = armorOut.toString();
|
||||
assertTrue(armored.startsWith("-----BEGIN PGP MESSAGE-----\n"));
|
||||
assertTrue(armored.contains("SGVsbG8sIFdvcmxkIQo="));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void armorAlreadyArmoredDataIsIdempotent() throws IOException {
|
||||
pipeStringToStdin(key);
|
||||
ByteArrayOutputStream armorOut = pipeStdoutToStream();
|
||||
assertSuccess(executeCommand("armor"));
|
||||
|
||||
String armored = armorOut.toString();
|
||||
assertEquals(key, armored);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright 2021 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pgpainless.cli.commands;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import com.ginsberg.junit.exit.FailOnSystemExit;
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.cli.PGPainlessCLI;
|
||||
|
||||
public class ArmorTest {
|
||||
|
||||
private static PrintStream originalSout;
|
||||
|
||||
@BeforeEach
|
||||
public void saveSout() {
|
||||
originalSout = System.out;
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void restoreSout() {
|
||||
System.setOut(originalSout);
|
||||
}
|
||||
|
||||
@Test
|
||||
@FailOnSystemExit
|
||||
public void armorSecretKey() throws IOException, PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException {
|
||||
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
|
||||
.modernKeyRing("alice@pgpainless.org", null);
|
||||
byte[] bytes = secretKey.getEncoded();
|
||||
|
||||
System.setIn(new ByteArrayInputStream(bytes));
|
||||
ByteArrayOutputStream armorOut = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(armorOut));
|
||||
PGPainlessCLI.execute("armor");
|
||||
|
||||
PGPSecretKeyRing armored = PGPainless.readKeyRing().secretKeyRing(armorOut.toString());
|
||||
assertArrayEquals(secretKey.getEncoded(), armored.getEncoded());
|
||||
}
|
||||
|
||||
@Test
|
||||
@FailOnSystemExit
|
||||
public void armorPublicKey() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
|
||||
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
|
||||
.modernKeyRing("alice@pgpainless.org", null);
|
||||
PGPPublicKeyRing publicKey = PGPainless.extractCertificate(secretKey);
|
||||
byte[] bytes = publicKey.getEncoded();
|
||||
|
||||
System.setIn(new ByteArrayInputStream(bytes));
|
||||
ByteArrayOutputStream armorOut = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(armorOut));
|
||||
PGPainlessCLI.execute("armor");
|
||||
|
||||
PGPPublicKeyRing armored = PGPainless.readKeyRing().publicKeyRing(armorOut.toString());
|
||||
assertArrayEquals(publicKey.getEncoded(), armored.getEncoded());
|
||||
}
|
||||
|
||||
@Test
|
||||
@FailOnSystemExit
|
||||
public void armorMessage() {
|
||||
String message = "Hello, World!\n";
|
||||
|
||||
System.setIn(new ByteArrayInputStream(message.getBytes(StandardCharsets.UTF_8)));
|
||||
ByteArrayOutputStream armorOut = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(armorOut));
|
||||
PGPainlessCLI.execute("armor");
|
||||
|
||||
String armored = armorOut.toString();
|
||||
|
||||
assertTrue(armored.startsWith("-----BEGIN PGP MESSAGE-----\n"));
|
||||
assertTrue(armored.contains("SGVsbG8sIFdvcmxkIQo="));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,167 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.cli.commands;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.bouncycastle.util.io.Streams;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.opentest4j.TestAbortedException;
|
||||
import org.pgpainless.cli.TestUtils;
|
||||
import org.pgpainless.sop.SOPImpl;
|
||||
import org.slf4j.Logger;
|
||||
import sop.cli.picocli.SopCLI;
|
||||
|
||||
public abstract class CLITest {
|
||||
|
||||
protected File testDirectory;
|
||||
protected InputStream stdin;
|
||||
protected PrintStream stdout;
|
||||
|
||||
protected final Logger LOGGER;
|
||||
|
||||
|
||||
public CLITest(@Nonnull Logger logger) {
|
||||
LOGGER = logger;
|
||||
SopCLI.setSopInstance(new SOPImpl());
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void setup() throws IOException {
|
||||
testDirectory = TestUtils.createTempDirectory();
|
||||
testDirectory.deleteOnExit();
|
||||
LOGGER.debug(testDirectory.getAbsolutePath());
|
||||
stdin = System.in;
|
||||
stdout = System.out;
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void cleanup() throws IOException {
|
||||
resetStreams();
|
||||
}
|
||||
|
||||
public File nonExistentFile(String name) {
|
||||
File file = new File(testDirectory, name);
|
||||
if (file.exists()) {
|
||||
throw new TestAbortedException("File " + file.getAbsolutePath() + " already exists.");
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
public File pipeStdoutToFile(String name) throws IOException {
|
||||
File file = new File(testDirectory, name);
|
||||
file.deleteOnExit();
|
||||
if (!file.createNewFile()) {
|
||||
throw new TestAbortedException("Cannot create new file " + file.getAbsolutePath());
|
||||
}
|
||||
System.setOut(new PrintStream(Files.newOutputStream(file.toPath())));
|
||||
return file;
|
||||
}
|
||||
|
||||
public ByteArrayOutputStream pipeStdoutToStream() {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
pipeStdoutToStream(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
public void pipeStdoutToStream(OutputStream stream) {
|
||||
System.setOut(new PrintStream(stream));
|
||||
}
|
||||
|
||||
public void pipeFileToStdin(File file) throws IOException {
|
||||
System.setIn(Files.newInputStream(file.toPath()));
|
||||
}
|
||||
|
||||
public void pipeBytesToStdin(byte[] bytes) {
|
||||
System.setIn(new ByteArrayInputStream(bytes));
|
||||
}
|
||||
|
||||
public void pipeStringToStdin(String string) {
|
||||
System.setIn(new ByteArrayInputStream(string.getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
|
||||
public void resetStdout() {
|
||||
if (System.out != stdout) {
|
||||
System.out.flush();
|
||||
System.out.close();
|
||||
}
|
||||
System.setOut(stdout);
|
||||
}
|
||||
|
||||
public void resetStdin() throws IOException {
|
||||
if (System.in != stdin) {
|
||||
System.in.close();
|
||||
}
|
||||
System.setIn(stdin);
|
||||
}
|
||||
|
||||
public void resetStreams() throws IOException {
|
||||
resetStdout();
|
||||
resetStdin();
|
||||
}
|
||||
|
||||
public File writeFile(String name, String data) throws IOException {
|
||||
return writeFile(name, data.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public File writeFile(String name, byte[] bytes) throws IOException {
|
||||
return writeFile(name, new ByteArrayInputStream(bytes));
|
||||
}
|
||||
|
||||
public File writeFile(String name, InputStream data) throws IOException {
|
||||
File file = new File(testDirectory, name);
|
||||
if (!file.createNewFile()) {
|
||||
throw new TestAbortedException("Cannot create new file " + file.getAbsolutePath());
|
||||
}
|
||||
file.deleteOnExit();
|
||||
try (FileOutputStream fileOut = new FileOutputStream(file)) {
|
||||
Streams.pipeAll(data, fileOut);
|
||||
fileOut.flush();
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
public byte[] readBytesFromFile(File file) {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
try (FileInputStream fileIn = new FileInputStream(file)) {
|
||||
Streams.pipeAll(fileIn, buffer);
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new TestAbortedException("File " + file.getAbsolutePath() + " does not exist!", e);
|
||||
} catch (IOException e) {
|
||||
throw new TestAbortedException("Cannot read from file " + file.getAbsolutePath(), e);
|
||||
}
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
public String readStringFromFile(File file) {
|
||||
return new String(readBytesFromFile(file), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public int executeCommand(String... command) throws IOException {
|
||||
int exitCode = SopCLI.execute(command);
|
||||
resetStreams();
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
public void assertSuccess(int exitCode) {
|
||||
assertEquals(0, exitCode,
|
||||
"Expected successful program execution");
|
||||
}
|
||||
}
|
|
@ -1,113 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.cli.commands;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sop.exception.SOPGPException;
|
||||
|
||||
public class DearmorCmdTest extends CLITest {
|
||||
|
||||
public DearmorCmdTest() {
|
||||
super(LoggerFactory.getLogger(DearmorCmdTest.class));
|
||||
}
|
||||
|
||||
private static final String key = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
|
||||
"Version: PGPainless\n" +
|
||||
"Comment: 62E9 DDA4 F20F 8341 D2BC 4B4C 8B07 5177 01F9 534C\n" +
|
||||
"Comment: alice@pgpainless.org\n" +
|
||||
"\n" +
|
||||
"lFgEY2vOkhYJKwYBBAHaRw8BAQdAqGOtLd1tKnuwaYYcdr2/7C0cPiCCggRMKG+W\n" +
|
||||
"t32QQdEAAP9VaBzjk/AaAqyykZnQHmS1HByEvRLv5/4yJMSr22451BFjtBRhbGlj\n" +
|
||||
"ZUBwZ3BhaW5sZXNzLm9yZ4iOBBMWCgBBBQJja86SCRCLB1F3AflTTBYhBGLp3aTy\n" +
|
||||
"D4NB0rxLTIsHUXcB+VNMAp4BApsBBRYCAwEABAsJCAcFFQoJCAsCmQEAACZhAP4s\n" +
|
||||
"8hn/RBDvyLvGROOd15EYATnWlgyi+b5WXP6cELalJwD1FZy3RROhfNtZWcJPS43f\n" +
|
||||
"G03pYNyb0NXoitIMAaXEB5xdBGNrzpISCisGAQQBl1UBBQEBB0CqCcYethOynfni\n" +
|
||||
"8uRO+r/cZWp9hCLy8pRIExKqzcyEFAMBCAcAAP9sRRLoZkLpDaTNNrtIBovXu2AN\n" +
|
||||
"hL8keUMWtVcuEHnkQA6iiHUEGBYKAB0FAmNrzpICngECmwwFFgIDAQAECwkIBwUV\n" +
|
||||
"CgkICwAKCRCLB1F3AflTTBVpAP491etrjqCMWx2bBaw3K1vP0Mix6U0vF3J4kP9U\n" +
|
||||
"eZm6owEA4kX9VAGESvLgIc7CEiswmxdWjxnLQyCRtWXfjgFmYQucWARja86SFgkr\n" +
|
||||
"BgEEAdpHDwEBB0DBslhDpWC6CV3xJUSo071NSO5Cf4fgOwOj+QHs8mpFbwABAPkQ\n" +
|
||||
"ioSydYiMi04LyfPohyrhhcdJDHallQg+jYHHUb2pEJCI1QQYFgoAfQUCY2vOkgKe\n" +
|
||||
"AQKbAgUWAgMBAAQLCQgHBRUKCQgLXyAEGRYKAAYFAmNrzpIACgkQiHlkvEXh+f1e\n" +
|
||||
"ywEA9A2GLU9LxCJxZf2X4qcZY//YJDChIZHPnY0Vaek1DsMBAN1YILrH2rxQeCXj\n" +
|
||||
"m4bUKfJIRrGt6ZJscwORgNI1dFQFAAoJEIsHUXcB+VNMK3gA/3vvPm57JsHA860w\n" +
|
||||
"lB4D1II71oFNL8TFnJqTAvpSKe1AAP49S4mKB4PE0ElcDo7n+nEYt6ba8IMRDlMo\n" +
|
||||
"rsH85mUgCw==\n" +
|
||||
"=EMKf\n" +
|
||||
"-----END PGP PRIVATE KEY BLOCK-----\n";
|
||||
|
||||
@Test
|
||||
public void dearmorSecretKey() throws IOException {
|
||||
PGPSecretKeyRing secretKey = PGPainless.readKeyRing().secretKeyRing(key);
|
||||
|
||||
pipeStringToStdin(key);
|
||||
ByteArrayOutputStream dearmored = pipeStdoutToStream();
|
||||
assertSuccess(executeCommand("dearmor"));
|
||||
|
||||
assertArrayEquals(secretKey.getEncoded(), dearmored.toByteArray());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dearmorBrokenArmoredKeyFails() throws IOException {
|
||||
// contains a "-"
|
||||
String invalidBase64 = "lFgEY2vOkhYJKwYBBAHaRw8BAQdAqGOtLd1tKnuwaYYcdr2/7C0cPiCCggRMKG+Wt32QQdEAAP9VaBzjk/AaAqyykZnQHmS1HByEvRLv5/4yJMSr22451BFjtBRhbGljZUBwZ3BhaW5sZXNzLm9yZ4iOBBMWCgBBBQJja86SCRCLB1F3AflTTBYhBGLp3aTyD4NB0rxLT-IsHUXcB+VNMAp4BApsBBRYCAwEABAsJCAcFFQoJCAsCmQEAACZhAP4s8hn/RBDvyLvGROOd15EYATnWlgyi+b5WXP6cELalJwD1FZy3RROhfNtZWcJPS43fG03pYNyb0NXoitIMAaXEB5xdBGNrzpISCisGAQQBl1UBBQEBB0CqCcYethOynfni8uRO+r/cZWp9hCLy8pRIExKqzcyEFAMBCAcAAP9sRRLoZkLpDaTNNrtIBovXu2ANhL8keUMWtVcuEHnkQA6iiHUEGBYKAB0FAmNrzpICngECmwwFFgIDAQAECwkIBwUVCgkICwAKCRCLB1F3AflTTBVpAP491etrjqCMWx2bBaw3K1vP0Mix6U0vF3J4kP9UeZm6owEA4kX9VAGESvLgIc7CEiswmxdWjxnLQyCRtWXfjgFmYQucWARja86SFgkrBgEEAdpHDwEBB0DBslhDpWC6CV3xJUSo071NSO5Cf4fgOwOj+QHs8mpFbwABAPkQioSydYiMi04LyfPohyrhhcdJDHallQg+jYHHUb2pEJCI1QQYFgoAfQUCY2vOkgKeAQKbAgUWAgMBAAQLCQgHBRUKCQgLXyAEGRYKAAYFAmNrzpIACgkQiHlkvEXh+f1eywEA9A2GLU9LxCJxZf2X4qcZY//YJDChIZHPnY0Vaek1DsMBAN1YILrH2rxQeCXjm4bUKfJIRrGt6ZJscwORgNI1dFQFAAoJEIsHUXcB+VNMK3gA/3vvPm57JsHA860wlB4D1II71oFNL8TFnJqTAvpSKe1AAP49S4mKB4PE0ElcDo7n+nEYt6ba8IMRDlMorsH85mUgCw==";
|
||||
pipeStringToStdin(invalidBase64);
|
||||
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||
int exitCode = executeCommand("dearmor");
|
||||
|
||||
assertEquals(SOPGPException.BadData.EXIT_CODE, exitCode);
|
||||
assertEquals(0, out.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dearmorCertificate() throws IOException {
|
||||
PGPSecretKeyRing secretKey = PGPainless.readKeyRing().secretKeyRing(key);
|
||||
PGPPublicKeyRing certificate = PGPainless.extractCertificate(secretKey);
|
||||
String armoredCert = PGPainless.asciiArmor(certificate);
|
||||
|
||||
pipeStringToStdin(armoredCert);
|
||||
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||
assertSuccess(executeCommand("dearmor"));
|
||||
|
||||
assertArrayEquals(certificate.getEncoded(), out.toByteArray());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dearmorMessage() throws IOException {
|
||||
String armored = "-----BEGIN PGP MESSAGE-----\n" +
|
||||
"Version: BCPG v1.69\n" +
|
||||
"\n" +
|
||||
"SGVsbG8sIFdvcmxkCg==\n" +
|
||||
"=fkLo\n" +
|
||||
"-----END PGP MESSAGE-----";
|
||||
|
||||
pipeStringToStdin(armored);
|
||||
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||
assertSuccess(executeCommand("dearmor"));
|
||||
|
||||
assertEquals("Hello, World\n", out.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dearmorGarbageEmitsEmpty() throws IOException {
|
||||
String noArmoredData = "This is not armored.";
|
||||
pipeStringToStdin(noArmoredData);
|
||||
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||
assertSuccess(executeCommand("dearmor"));
|
||||
assertTrue(out.toString().isEmpty());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright 2021 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pgpainless.cli.commands;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import com.ginsberg.junit.exit.FailOnSystemExit;
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.cli.PGPainlessCLI;
|
||||
|
||||
public class DearmorTest {
|
||||
|
||||
private PrintStream originalSout;
|
||||
|
||||
@BeforeEach
|
||||
public void saveSout() {
|
||||
this.originalSout = System.out;
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void restoreSout() {
|
||||
System.setOut(originalSout);
|
||||
}
|
||||
|
||||
@Test
|
||||
@FailOnSystemExit
|
||||
public void dearmorSecretKey() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
|
||||
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
|
||||
.modernKeyRing("alice@pgpainless.org", null);
|
||||
String armored = PGPainless.asciiArmor(secretKey);
|
||||
|
||||
System.setIn(new ByteArrayInputStream(armored.getBytes(StandardCharsets.UTF_8)));
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(out));
|
||||
PGPainlessCLI.execute("dearmor");
|
||||
|
||||
assertArrayEquals(secretKey.getEncoded(), out.toByteArray());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@FailOnSystemExit
|
||||
public void dearmorCertificate() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
|
||||
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
|
||||
.modernKeyRing("alice@pgpainless.org", null);
|
||||
PGPPublicKeyRing certificate = PGPainless.extractCertificate(secretKey);
|
||||
String armored = PGPainless.asciiArmor(certificate);
|
||||
|
||||
System.setIn(new ByteArrayInputStream(armored.getBytes(StandardCharsets.UTF_8)));
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(out));
|
||||
PGPainlessCLI.execute("dearmor");
|
||||
|
||||
assertArrayEquals(certificate.getEncoded(), out.toByteArray());
|
||||
}
|
||||
|
||||
@Test
|
||||
@FailOnSystemExit
|
||||
public void dearmorMessage() {
|
||||
String armored = "-----BEGIN PGP MESSAGE-----\n" +
|
||||
"Version: BCPG v1.69\n" +
|
||||
"\n" +
|
||||
"SGVsbG8sIFdvcmxkCg==\n" +
|
||||
"=fkLo\n" +
|
||||
"-----END PGP MESSAGE-----";
|
||||
|
||||
System.setIn(new ByteArrayInputStream(armored.getBytes(StandardCharsets.UTF_8)));
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(out));
|
||||
PGPainlessCLI.execute("dearmor");
|
||||
|
||||
assertEquals("Hello, World\n", out.toString());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
* Copyright 2021 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pgpainless.cli.commands;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import com.ginsberg.junit.exit.ExpectSystemExitWithStatus;
|
||||
import org.bouncycastle.util.io.Streams;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.pgpainless.cli.PGPainlessCLI;
|
||||
import org.pgpainless.cli.TestUtils;
|
||||
import sop.exception.SOPGPException;
|
||||
|
||||
public class DetachInbandSignatureAndMessageTest {
|
||||
|
||||
private PrintStream originalSout;
|
||||
private static File tempDir;
|
||||
private static File certFile;
|
||||
|
||||
private static final String CERT = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||
"Version: BCPG v1.64\n" +
|
||||
"\n" +
|
||||
"mFIEXhtfCBMIKoZIzj0DAQcCAwTGSFMBUOSLusXS8hdNHbdK3gN8hS7jd4ky7Czl\n" +
|
||||
"mSti+oVyRJUwQAFZJ1NMsg1H8flSJP1/9YbHd9FBU4bHKGKPtBE8ZW1pbEBlbWFp\n" +
|
||||
"bC51c2VyPoh1BBMTCgAdBQJeG18IAhsjBRYCAwEABAsJCAcFFQoJCAsCHgEACgkQ\n" +
|
||||
"VzbmkxrPNwz8rAD/S/VCQc5NJLArgTDkgrt3Q573HiYfrIQo1uk3dwV15WIBAMiq\n" +
|
||||
"oDmRMb8jzOBv6FGW4P5WAubPdnAvDD7XmArD+TSeuFYEXhtfCBIIKoZIzj0DAQcC\n" +
|
||||
"AwTgWDWmHJLQUQ35Qg/rINmUhkUhj1E4O5t6Y2PipbqlGfDufLmIKnX40BoJPS4G\n" +
|
||||
"HW7U0QXfwSaTXa1BAaNsMUomAwEIB4h1BBgTCgAdBQJeG18IAhsMBRYCAwEABAsJ\n" +
|
||||
"CAcFFQoJCAsCHgEACgkQVzbmkxrPNwxOcwEA19Fnhw7XwpQoT61Fqg54vroAwTZ3\n" +
|
||||
"T5A+LOdevAtzNOUA/RWeKfOGk6D+vKYRNpMJyqsHi/vBeKwXoeN0n6HuExVF\n" +
|
||||
"=a1W7\n" +
|
||||
"-----END PGP PUBLIC KEY BLOCK-----";
|
||||
|
||||
@BeforeAll
|
||||
public static void createTempDir() throws IOException {
|
||||
tempDir = TestUtils.createTempDirectory();
|
||||
|
||||
certFile = new File(tempDir, "cert.asc");
|
||||
assertTrue(certFile.createNewFile());
|
||||
try (FileOutputStream out = new FileOutputStream(certFile)) {
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(CERT.getBytes(StandardCharsets.UTF_8));
|
||||
Streams.pipeAll(in, out);
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void saveSout() {
|
||||
this.originalSout = System.out;
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void restoreSout() {
|
||||
System.setOut(originalSout);
|
||||
}
|
||||
|
||||
private static final String CLEAR_SIGNED_MESSAGE = "-----BEGIN PGP SIGNED MESSAGE-----\n" +
|
||||
"Hash: SHA512\n" +
|
||||
"\n" +
|
||||
"Ah, Juliet, if the measure of thy joy\n" +
|
||||
"Be heaped like mine, and that thy skill be more\n" +
|
||||
"To blazon it, then sweeten with thy breath\n" +
|
||||
"This neighbor air, and let rich music’s tongue\n" +
|
||||
"Unfold the imagined happiness that both\n" +
|
||||
"Receive in either by this dear encounter.\n" +
|
||||
"-----BEGIN PGP SIGNATURE-----\n" +
|
||||
"\n" +
|
||||
"iHUEARMKAB0WIQRPZlxNwsRmC8ZCXkFXNuaTGs83DAUCYJ/x5gAKCRBXNuaTGs83\n" +
|
||||
"DFRwAP9/4wMvV3WcX59Clo7mkRce6iwW3VBdiN+yMu3tjmHB2wD/RfE28Q1v4+eo\n" +
|
||||
"ySNgbyvqYYsNr0fnBwaG3aaj+u5ExiE=\n" +
|
||||
"=Z2SO\n" +
|
||||
"-----END PGP SIGNATURE-----";
|
||||
|
||||
private static final String CLEAR_SIGNED_SIGNATURE = "-----BEGIN PGP SIGNATURE-----\n" +
|
||||
"\n" +
|
||||
"iHUEARMKAB0WIQRPZlxNwsRmC8ZCXkFXNuaTGs83DAUCYJ/x5gAKCRBXNuaTGs83\n" +
|
||||
"DFRwAP9/4wMvV3WcX59Clo7mkRce6iwW3VBdiN+yMu3tjmHB2wD/RfE28Q1v4+eo\n" +
|
||||
"ySNgbyvqYYsNr0fnBwaG3aaj+u5ExiE=\n" +
|
||||
"=Z2SO\n" +
|
||||
"-----END PGP SIGNATURE-----";
|
||||
|
||||
private static final String CLEAR_SIGNED_BODY = "Ah, Juliet, if the measure of thy joy\n" +
|
||||
"Be heaped like mine, and that thy skill be more\n" +
|
||||
"To blazon it, then sweeten with thy breath\n" +
|
||||
"This neighbor air, and let rich music’s tongue\n" +
|
||||
"Unfold the imagined happiness that both\n" +
|
||||
"Receive in either by this dear encounter.";
|
||||
|
||||
@Test
|
||||
public void detachInbandSignatureAndMessage() throws IOException {
|
||||
// Clearsigned In
|
||||
ByteArrayInputStream clearSignedIn = new ByteArrayInputStream(CLEAR_SIGNED_MESSAGE.getBytes(StandardCharsets.UTF_8));
|
||||
System.setIn(clearSignedIn);
|
||||
|
||||
// Plaintext Out
|
||||
ByteArrayOutputStream msgOut = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(msgOut));
|
||||
|
||||
// Detach
|
||||
File tempSigFile = new File(tempDir, "sig.out");
|
||||
PGPainlessCLI.main(new String[] {"detach-inband-signature-and-message", "--signatures-out=" + tempSigFile.getAbsolutePath()});
|
||||
|
||||
// Test equality with expected values
|
||||
assertEquals(CLEAR_SIGNED_BODY, msgOut.toString());
|
||||
try (FileInputStream sigIn = new FileInputStream(tempSigFile)) {
|
||||
ByteArrayOutputStream sigBytes = new ByteArrayOutputStream();
|
||||
Streams.pipeAll(sigIn, sigBytes);
|
||||
String sig = sigBytes.toString();
|
||||
TestUtils.assertSignatureIsArmored(sigBytes.toByteArray());
|
||||
TestUtils.assertSignatureEquals(CLEAR_SIGNED_SIGNATURE, sig);
|
||||
} catch (FileNotFoundException e) {
|
||||
fail("Signature File must have been written.", e);
|
||||
}
|
||||
|
||||
// Check if produced signature still checks out
|
||||
System.setIn(new ByteArrayInputStream(msgOut.toByteArray()));
|
||||
ByteArrayOutputStream verifyOut = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(verifyOut));
|
||||
PGPainlessCLI.main(new String[] {"verify", tempSigFile.getAbsolutePath(), certFile.getAbsolutePath()});
|
||||
|
||||
assertEquals("2021-05-15T16:08:06Z 4F665C4DC2C4660BC6425E415736E6931ACF370C 4F665C4DC2C4660BC6425E415736E6931ACF370C\n", verifyOut.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void detachInbandSignatureAndMessageNoArmor() throws IOException {
|
||||
// Clearsigned In
|
||||
ByteArrayInputStream clearSignedIn = new ByteArrayInputStream(CLEAR_SIGNED_MESSAGE.getBytes(StandardCharsets.UTF_8));
|
||||
System.setIn(clearSignedIn);
|
||||
|
||||
// Plaintext Out
|
||||
ByteArrayOutputStream msgOut = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(msgOut));
|
||||
|
||||
// Detach
|
||||
File tempSigFile = new File(tempDir, "sig.asc");
|
||||
PGPainlessCLI.main(new String[] {"detach-inband-signature-and-message", "--signatures-out=" + tempSigFile.getAbsolutePath(), "--no-armor"});
|
||||
|
||||
// Test equality with expected values
|
||||
assertEquals(CLEAR_SIGNED_BODY, msgOut.toString());
|
||||
try (FileInputStream sigIn = new FileInputStream(tempSigFile)) {
|
||||
ByteArrayOutputStream sigBytes = new ByteArrayOutputStream();
|
||||
Streams.pipeAll(sigIn, sigBytes);
|
||||
byte[] sig = sigBytes.toByteArray();
|
||||
TestUtils.assertSignatureIsNotArmored(sig);
|
||||
TestUtils.assertSignatureEquals(CLEAR_SIGNED_SIGNATURE.getBytes(StandardCharsets.UTF_8), sig);
|
||||
} catch (FileNotFoundException e) {
|
||||
fail("Signature File must have been written.", e);
|
||||
}
|
||||
|
||||
// Check if produced signature still checks out
|
||||
System.setIn(new ByteArrayInputStream(msgOut.toByteArray()));
|
||||
ByteArrayOutputStream verifyOut = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(verifyOut));
|
||||
PGPainlessCLI.main(new String[] {"verify", tempSigFile.getAbsolutePath(), certFile.getAbsolutePath()});
|
||||
|
||||
assertEquals("2021-05-15T16:08:06Z 4F665C4DC2C4660BC6425E415736E6931ACF370C 4F665C4DC2C4660BC6425E415736E6931ACF370C\n", verifyOut.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ExpectSystemExitWithStatus(SOPGPException.OutputExists.EXIT_CODE)
|
||||
public void existingSignatureOutCausesException() throws IOException {
|
||||
// Clearsigned In
|
||||
ByteArrayInputStream clearSignedIn = new ByteArrayInputStream(CLEAR_SIGNED_MESSAGE.getBytes(StandardCharsets.UTF_8));
|
||||
System.setIn(clearSignedIn);
|
||||
|
||||
// Plaintext Out
|
||||
ByteArrayOutputStream msgOut = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(msgOut));
|
||||
|
||||
// Detach
|
||||
File existingSigFile = new File(tempDir, "sig.existing");
|
||||
assertTrue(existingSigFile.createNewFile());
|
||||
PGPainlessCLI.main(new String[] {"detach-inband-signature-and-message", "--signatures-out=" + existingSigFile.getAbsolutePath()});
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* Copyright 2021 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pgpainless.cli.commands;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import com.ginsberg.junit.exit.FailOnSystemExit;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.pgpainless.cli.PGPainlessCLI;
|
||||
import org.pgpainless.cli.TestUtils;
|
||||
|
||||
public class EncryptDecryptTest {
|
||||
|
||||
private static File tempDir;
|
||||
private static PrintStream originalSout;
|
||||
|
||||
@BeforeAll
|
||||
public static void prepare() throws IOException {
|
||||
tempDir = TestUtils.createTempDirectory();
|
||||
}
|
||||
|
||||
@Test
|
||||
@FailOnSystemExit
|
||||
public void encryptAndDecryptAMessage() throws IOException {
|
||||
originalSout = System.out;
|
||||
File julietKeyFile = new File(tempDir, "juliet.key");
|
||||
assertTrue(julietKeyFile.createNewFile());
|
||||
|
||||
File julietCertFile = new File(tempDir, "juliet.asc");
|
||||
assertTrue(julietCertFile.createNewFile());
|
||||
|
||||
File romeoKeyFile = new File(tempDir, "romeo.key");
|
||||
assertTrue(romeoKeyFile.createNewFile());
|
||||
|
||||
File romeoCertFile = new File(tempDir, "romeo.asc");
|
||||
assertTrue(romeoCertFile.createNewFile());
|
||||
|
||||
File msgAscFile = new File(tempDir, "msg.asc");
|
||||
assertTrue(msgAscFile.createNewFile());
|
||||
|
||||
OutputStream julietKeyOut = new FileOutputStream(julietKeyFile);
|
||||
System.setOut(new PrintStream(julietKeyOut));
|
||||
PGPainlessCLI.execute("generate-key", "Juliet Capulet <juliet@capulet.lit>");
|
||||
julietKeyOut.close();
|
||||
|
||||
FileInputStream julietKeyIn = new FileInputStream(julietKeyFile);
|
||||
System.setIn(julietKeyIn);
|
||||
OutputStream julietCertOut = new FileOutputStream(julietCertFile);
|
||||
System.setOut(new PrintStream(julietCertOut));
|
||||
PGPainlessCLI.execute("extract-cert");
|
||||
julietKeyIn.close();
|
||||
julietCertOut.close();
|
||||
|
||||
OutputStream romeoKeyOut = new FileOutputStream(romeoKeyFile);
|
||||
System.setOut(new PrintStream(romeoKeyOut));
|
||||
PGPainlessCLI.execute("generate-key", "Romeo Montague <romeo@montague.lit>");
|
||||
romeoKeyOut.close();
|
||||
|
||||
FileInputStream romeoKeyIn = new FileInputStream(romeoKeyFile);
|
||||
System.setIn(romeoKeyIn);
|
||||
OutputStream romeoCertOut = new FileOutputStream(romeoCertFile);
|
||||
System.setOut(new PrintStream(romeoCertOut));
|
||||
PGPainlessCLI.execute("extract-cert");
|
||||
romeoKeyIn.close();
|
||||
romeoCertOut.close();
|
||||
|
||||
String msg = "Hello World!\n";
|
||||
ByteArrayInputStream msgIn = new ByteArrayInputStream(msg.getBytes(StandardCharsets.UTF_8));
|
||||
System.setIn(msgIn);
|
||||
OutputStream msgAscOut = new FileOutputStream(msgAscFile);
|
||||
System.setOut(new PrintStream(msgAscOut));
|
||||
PGPainlessCLI.execute("encrypt",
|
||||
"--sign-with", romeoKeyFile.getAbsolutePath(),
|
||||
julietCertFile.getAbsolutePath());
|
||||
msgAscOut.close();
|
||||
|
||||
File verifyFile = new File(tempDir, "verify.txt");
|
||||
|
||||
FileInputStream msgAscIn = new FileInputStream(msgAscFile);
|
||||
System.setIn(msgAscIn);
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
PrintStream pOut = new PrintStream(out);
|
||||
System.setOut(pOut);
|
||||
PGPainlessCLI.execute("decrypt",
|
||||
"--verify-out", verifyFile.getAbsolutePath(),
|
||||
"--verify-with", romeoCertFile.getAbsolutePath(),
|
||||
julietKeyFile.getAbsolutePath());
|
||||
msgAscIn.close();
|
||||
|
||||
assertEquals(msg, out.toString());
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void after() {
|
||||
System.setOut(originalSout);
|
||||
// CHECKSTYLE:OFF
|
||||
System.out.println(tempDir.getAbsolutePath());
|
||||
// CHECKSTYLE:ON
|
||||
}
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.cli.commands;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.key.info.KeyRingInfo;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sop.exception.SOPGPException;
|
||||
|
||||
public class ExtractCertCmdTest extends CLITest {
|
||||
|
||||
public ExtractCertCmdTest() {
|
||||
super(LoggerFactory.getLogger(ExtractCertCmdTest.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractCert()
|
||||
throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, IOException {
|
||||
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
|
||||
.simpleEcKeyRing("Juliet Capulet <juliet@capulet.lit>");
|
||||
|
||||
pipeBytesToStdin(secretKeys.getEncoded());
|
||||
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||
assertSuccess(executeCommand("extract-cert", "--armor"));
|
||||
|
||||
assertTrue(out.toString().startsWith("-----BEGIN PGP PUBLIC KEY BLOCK-----\n"));
|
||||
|
||||
PGPPublicKeyRing publicKeys = PGPainless.readKeyRing().publicKeyRing(out.toByteArray());
|
||||
KeyRingInfo info = PGPainless.inspectKeyRing(publicKeys);
|
||||
assertFalse(info.isSecretKey());
|
||||
assertTrue(info.isUserIdValid("Juliet Capulet <juliet@capulet.lit>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractCertFromCertFails() throws IOException {
|
||||
// Generate key
|
||||
File keyFile = pipeStdoutToFile("key.asc");
|
||||
assertSuccess(executeCommand("generate-key", "Alice <alice@pgpainless.org>"));
|
||||
|
||||
// extract cert from key (success)
|
||||
pipeFileToStdin(keyFile);
|
||||
File certFile = pipeStdoutToFile("cert.asc");
|
||||
assertSuccess(executeCommand("extract-cert"));
|
||||
|
||||
// extract cert from cert (fail)
|
||||
pipeFileToStdin(certFile);
|
||||
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||
int exitCode = executeCommand("extract-cert");
|
||||
|
||||
assertEquals(SOPGPException.BadData.EXIT_CODE, exitCode);
|
||||
assertEquals(0, out.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractCertFromGarbageFails() throws IOException {
|
||||
pipeStringToStdin("This is a bunch of garbage!");
|
||||
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||
int exitCode = executeCommand("extract-cert");
|
||||
|
||||
assertEquals(SOPGPException.BadData.EXIT_CODE, exitCode);
|
||||
assertEquals(0, out.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractCertUnarmored() throws IOException {
|
||||
// Generate key
|
||||
File keyFile = pipeStdoutToFile("key.asc");
|
||||
assertSuccess(executeCommand("generate-key", "Alice <alice@pgpainless.org>"));
|
||||
|
||||
// extract cert from key (success)
|
||||
pipeFileToStdin(keyFile);
|
||||
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||
assertSuccess(executeCommand("extract-cert", "--no-armor"));
|
||||
|
||||
assertFalse(out.toString().startsWith("-----BEGIN PGP PUBLIC KEY BLOCK-----\n"));
|
||||
|
||||
pipeBytesToStdin(out.toByteArray());
|
||||
ByteArrayOutputStream armored = pipeStdoutToStream();
|
||||
assertSuccess(executeCommand("armor"));
|
||||
|
||||
assertTrue(armored.toString().startsWith("-----BEGIN PGP PUBLIC KEY BLOCK-----\n"));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright 2021 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pgpainless.cli.commands;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import com.ginsberg.junit.exit.FailOnSystemExit;
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.cli.PGPainlessCLI;
|
||||
import org.pgpainless.key.info.KeyRingInfo;
|
||||
|
||||
public class ExtractCertTest {
|
||||
|
||||
@Test
|
||||
@FailOnSystemExit
|
||||
public void testExtractCert() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, IOException {
|
||||
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
|
||||
.simpleEcKeyRing("Juliet Capulet <juliet@capulet.lit>");
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(secretKeys.getEncoded());
|
||||
System.setIn(inputStream);
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(out));
|
||||
|
||||
PGPainlessCLI.execute("extract-cert");
|
||||
PGPPublicKeyRing publicKeys = PGPainless.readKeyRing().publicKeyRing(out.toByteArray());
|
||||
KeyRingInfo info = PGPainless.inspectKeyRing(publicKeys);
|
||||
assertFalse(info.isSecretKey());
|
||||
assertTrue(info.isUserIdValid("Juliet Capulet <juliet@capulet.lit>"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright 2021 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pgpainless.cli.commands;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.pgpainless.cli.TestUtils.ARMOR_PRIVATE_KEY_HEADER_BYTES;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.ginsberg.junit.exit.FailOnSystemExit;
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.cli.PGPainlessCLI;
|
||||
import org.pgpainless.cli.TestUtils;
|
||||
import org.pgpainless.key.info.KeyRingInfo;
|
||||
|
||||
public class GenerateCertTest {
|
||||
|
||||
private static File tempDir;
|
||||
|
||||
|
||||
@BeforeAll
|
||||
public static void setup() throws IOException {
|
||||
tempDir = TestUtils.createTempDirectory();
|
||||
}
|
||||
|
||||
@Test
|
||||
@FailOnSystemExit
|
||||
public void testKeyGeneration() throws IOException, PGPException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(out));
|
||||
PGPainlessCLI.execute("generate-key", "--armor", "Juliet Capulet <juliet@capulet.lit>");
|
||||
|
||||
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(out.toByteArray());
|
||||
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
|
||||
assertTrue(info.isUserIdValid("Juliet Capulet <juliet@capulet.lit>"));
|
||||
|
||||
byte[] outBegin = new byte[37];
|
||||
System.arraycopy(out.toByteArray(), 0, outBegin, 0, 37);
|
||||
assertArrayEquals(outBegin, ARMOR_PRIVATE_KEY_HEADER_BYTES);
|
||||
}
|
||||
|
||||
@Test
|
||||
@FailOnSystemExit
|
||||
public void testNoArmor() {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(out));
|
||||
PGPainlessCLI.execute("generate-key", "--no-armor", "Test <test@test.test>");
|
||||
|
||||
byte[] outBegin = new byte[37];
|
||||
System.arraycopy(out.toByteArray(), 0, outBegin, 0, 37);
|
||||
assertFalse(Arrays.equals(outBegin, ARMOR_PRIVATE_KEY_HEADER_BYTES));
|
||||
}
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.cli.commands;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.key.info.KeyRingInfo;
|
||||
import org.pgpainless.key.protection.UnlockSecretKey;
|
||||
import org.pgpainless.util.Passphrase;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sop.exception.SOPGPException;
|
||||
|
||||
public class GenerateKeyCmdTest extends CLITest {
|
||||
|
||||
public GenerateKeyCmdTest() {
|
||||
super(LoggerFactory.getLogger(GenerateKeyCmdTest.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateKey() throws IOException {
|
||||
File keyFile = pipeStdoutToFile("key.asc");
|
||||
assertSuccess(executeCommand("generate-key", "Alice <alice@pgpainless.org>"));
|
||||
|
||||
String key = readStringFromFile(keyFile);
|
||||
assertTrue(key.startsWith("-----BEGIN PGP PRIVATE KEY BLOCK-----\n"));
|
||||
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(key);
|
||||
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
|
||||
assertTrue(info.isFullyDecrypted());
|
||||
assertEquals(Collections.singletonList("Alice <alice@pgpainless.org>"), info.getUserIds());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateBinaryKey() throws IOException {
|
||||
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||
assertSuccess(executeCommand("generate-key", "--no-armor",
|
||||
"Alice <alice@pgpainless.org>"));
|
||||
|
||||
byte[] key = out.toByteArray();
|
||||
String firstHexOctet = Hex.toHexString(key, 0, 1);
|
||||
assertTrue(firstHexOctet.equals("c5") || firstHexOctet.equals("94"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateKeyWithMultipleUserIds() throws IOException {
|
||||
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||
assertSuccess(executeCommand("generate-key",
|
||||
"Alice <alice@pgpainless.org>", "Alice <alice@openpgp.org>"));
|
||||
|
||||
String key = out.toString();
|
||||
assertTrue(key.startsWith("-----BEGIN PGP PRIVATE KEY BLOCK-----\n"));
|
||||
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(key);
|
||||
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
|
||||
assertTrue(info.isFullyDecrypted());
|
||||
assertEquals(Arrays.asList("Alice <alice@pgpainless.org>", "Alice <alice@openpgp.org>"), info.getUserIds());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPasswordProtectedKey() throws IOException, PGPException {
|
||||
File passwordFile = writeFile("password", "sw0rdf1sh");
|
||||
passwordFile.deleteOnExit();
|
||||
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||
assertSuccess(executeCommand("generate-key",
|
||||
"--with-key-password", passwordFile.getAbsolutePath(), "Alice <alice@pgpainless.org>"));
|
||||
|
||||
String key = out.toString();
|
||||
assertTrue(key.startsWith("-----BEGIN PGP PRIVATE KEY BLOCK-----\n"));
|
||||
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(key);
|
||||
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
|
||||
assertTrue(info.isFullyEncrypted());
|
||||
|
||||
assertNotNull(UnlockSecretKey
|
||||
.unlockSecretKey(secretKeys.getSecretKey(), Passphrase.fromPassword("sw0rdf1sh")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGeneratePasswordProtectedKey_missingPasswordFile() throws IOException {
|
||||
int exit = executeCommand("generate-key",
|
||||
"--with-key-password", "nonexistent", "Alice <alice@pgpainless.org>");
|
||||
|
||||
assertEquals(SOPGPException.MissingInput.EXIT_CODE, exit,
|
||||
"Expected MISSING_INPUT (" + SOPGPException.MissingInput.EXIT_CODE + ")");
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue