Start moving config stuff into Ansible role
This commit is contained in:
parent
211cc72e11
commit
0a705abcca
@ -56,6 +56,7 @@ fi
|
|||||||
copy_firefox=0
|
copy_firefox=0
|
||||||
|
|
||||||
# handle arguments
|
# handle arguments
|
||||||
|
# TODO
|
||||||
if [[ "$#" -eq 1 ]]; then
|
if [[ "$#" -eq 1 ]]; then
|
||||||
if [[ "$1" == "-f" || "$1" == "--firefox" ]]; then
|
if [[ "$1" == "-f" || "$1" == "--firefox" ]]; then
|
||||||
copy_firefox=1
|
copy_firefox=1
|
||||||
@ -166,6 +167,7 @@ cp -r "$tempdir/arch-config/.local/" ~/
|
|||||||
cp -r "$tempdir/arch-config/.ssh" ~/
|
cp -r "$tempdir/arch-config/.ssh" ~/
|
||||||
|
|
||||||
# copy firefox only if "-f" or "--firefox" is given as argument
|
# copy firefox only if "-f" or "--firefox" is given as argument
|
||||||
|
# TODO
|
||||||
if [[ copy_firefox -eq 1 ]]; then
|
if [[ copy_firefox -eq 1 ]]; then
|
||||||
if [[ -d ~/.mozilla/firefox ]]; then
|
if [[ -d ~/.mozilla/firefox ]]; then
|
||||||
# NOTE check if firefox default-release directory exists. 1 is good, 0 is bad
|
# NOTE check if firefox default-release directory exists. 1 is good, 0 is bad
|
||||||
@ -195,6 +197,7 @@ sudo cp -r "$tempdir/arch-config/etc" /
|
|||||||
|
|
||||||
# Copy pacman config depending on system architecture
|
# Copy pacman config depending on system architecture
|
||||||
# This is required due to differences in available repos between Arch on x86_64 and ALARM for aarch64
|
# This is required due to differences in available repos between Arch on x86_64 and ALARM for aarch64
|
||||||
|
# TODO
|
||||||
if [ "$(uname --machine)" == "x86_64" ]; then
|
if [ "$(uname --machine)" == "x86_64" ]; then
|
||||||
sudo cp "/etc/pacman-x86_64.conf" "/etc/pacman.conf"
|
sudo cp "/etc/pacman-x86_64.conf" "/etc/pacman.conf"
|
||||||
fi
|
fi
|
||||||
@ -224,6 +227,8 @@ cat <<EOF
|
|||||||
########################################
|
########################################
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
# TODO continue here
|
||||||
|
|
||||||
# reload systemd user scripts
|
# reload systemd user scripts
|
||||||
systemctl --user daemon-reload
|
systemctl --user daemon-reload
|
||||||
|
|
||||||
|
@ -1,186 +0,0 @@
|
|||||||
---
|
|
||||||
- hosts: localhost
|
|
||||||
become: yes
|
|
||||||
tasks:
|
|
||||||
- name: Enable multilib repository
|
|
||||||
ansible.builtin.blockinfile:
|
|
||||||
path: /etc/pacman.conf
|
|
||||||
prepend_newline: true
|
|
||||||
block: |
|
|
||||||
[multilib]
|
|
||||||
Include = /etc/pacman.d/mirrorlist
|
|
||||||
|
|
||||||
- name: Update packages
|
|
||||||
community.general.pacman:
|
|
||||||
update_cache: true
|
|
||||||
upgrade: true
|
|
||||||
|
|
||||||
- name: Install official packages
|
|
||||||
community.general.pacman:
|
|
||||||
state: present
|
|
||||||
update_cache: true
|
|
||||||
# select the lines and use 'M-x sort-lines'
|
|
||||||
name:
|
|
||||||
#- gnome-keyring
|
|
||||||
#- hdparm
|
|
||||||
#- libaacs
|
|
||||||
#- libbluray
|
|
||||||
#- libcdio
|
|
||||||
#- libdvdcss
|
|
||||||
#- libdvdnav
|
|
||||||
#- libdvdread
|
|
||||||
#- libsecret
|
|
||||||
#- phonon-qt5-gstreamer
|
|
||||||
#- seahorse
|
|
||||||
- aardvark-dns # containers
|
|
||||||
- amd-ucode # microcode
|
|
||||||
- ansible # iac
|
|
||||||
- base-devel # devel stuff
|
|
||||||
- bind # dnsutils (nslookup, dig)
|
|
||||||
- btrfs-progs # btrfs
|
|
||||||
- buildah # containers
|
|
||||||
- clang
|
|
||||||
- cmake
|
|
||||||
- composer
|
|
||||||
- discord
|
|
||||||
- dnsmasq # virtualization
|
|
||||||
- dosfstools # fat
|
|
||||||
- e2fsprogs # ext4
|
|
||||||
- easyeffects # audio effects
|
|
||||||
- edk2-ovmf # virtualization
|
|
||||||
- emacs-wayland
|
|
||||||
- exfatprogs # exfat
|
|
||||||
- f2fs-tools # f2fs
|
|
||||||
- fd
|
|
||||||
- ffmpeg # multimedia
|
|
||||||
- firefox # browser
|
|
||||||
- firewalld # firewall
|
|
||||||
- fish # shell
|
|
||||||
- flatpak # packages
|
|
||||||
- freetype2 # fonts
|
|
||||||
- fuse-overlayfs # containers
|
|
||||||
- fwupd # firmware
|
|
||||||
- fwupd-efi # firmware
|
|
||||||
- gdb
|
|
||||||
- git # code
|
|
||||||
- git-lfs # code
|
|
||||||
- hunspell # spelling
|
|
||||||
- hunspell-de # spelling
|
|
||||||
- hunspell-en_gb # spelling
|
|
||||||
- hunspell-en_us # spelling
|
|
||||||
- hyphen # spelling
|
|
||||||
- hyphen-de # spelling
|
|
||||||
- hyphen-en # spelling
|
|
||||||
- imv # image viewer
|
|
||||||
- intel-media-driver # VAAPI
|
|
||||||
- intel-ucode # microcode
|
|
||||||
- jre-openjdk # java
|
|
||||||
- just # command runner
|
|
||||||
- kde-applications-meta # kde
|
|
||||||
- kid3 # music metadata
|
|
||||||
- krita
|
|
||||||
- languagetool
|
|
||||||
- lib32-mesa # video driver
|
|
||||||
- lib32-vulkan-intel # video driver
|
|
||||||
- lib32-vulkan-radeon # video driver
|
|
||||||
- libreoffice-fresh # office
|
|
||||||
- libreoffice-fresh-de # office
|
|
||||||
- libva-intel-driver # VAAPI
|
|
||||||
- libva-mesa-driver # VAAPI
|
|
||||||
- libva-utils # VAAPI
|
|
||||||
- libvirt # virtualization
|
|
||||||
- linux # kernel
|
|
||||||
- linux-firmware # kernel
|
|
||||||
- linux-headers # kernel
|
|
||||||
- lldb
|
|
||||||
- lldb-mi
|
|
||||||
- lutris # games
|
|
||||||
- man-db # docs
|
|
||||||
- mesa # video driver
|
|
||||||
- mesa-vdpau # VDPAU
|
|
||||||
- mosh # remote shell
|
|
||||||
- mtools # fat
|
|
||||||
- mythes-de # thesaurus
|
|
||||||
- mythes-en # thesaurus
|
|
||||||
- nerd-fonts # fonts
|
|
||||||
- nextcloud-client # cloud
|
|
||||||
- nodejs
|
|
||||||
- noto-fonts # fonts
|
|
||||||
- noto-fonts-cjk # fonts
|
|
||||||
- noto-fonts-emoji # fonts
|
|
||||||
- ntfs-3g # ntfs
|
|
||||||
- nvme-cli # nvme ssd
|
|
||||||
- obs-studio # recording
|
|
||||||
- okular # pdf
|
|
||||||
- openssh # remote shell
|
|
||||||
- opentofu # iac
|
|
||||||
- otf-font-awesome # fonts
|
|
||||||
- p7zip # compression
|
|
||||||
- packagekit-qt6
|
|
||||||
- pacman-contrib # scripts
|
|
||||||
- pandoc
|
|
||||||
- php # code
|
|
||||||
- piper # mouse
|
|
||||||
- pipewire # audio
|
|
||||||
- pipewire-alsa # audio
|
|
||||||
- pipewire-jack # audio
|
|
||||||
- pipewire-pulse # audio
|
|
||||||
- pkgstats
|
|
||||||
- podman # containers
|
|
||||||
- power-profiles-daemon # power management
|
|
||||||
- prettier # code
|
|
||||||
- pyright # code
|
|
||||||
- python-black # code
|
|
||||||
- python-debugpy # code
|
|
||||||
- python-isort # code
|
|
||||||
- python-pip # code
|
|
||||||
- python-pipenv # code
|
|
||||||
- python-pyflakes # code
|
|
||||||
- python-pytest # code
|
|
||||||
- qemu-full # virtualization
|
|
||||||
- rebuild-detector # packages
|
|
||||||
- reflector # mirrors
|
|
||||||
- restic # backup
|
|
||||||
- ripgrep # doom emacs
|
|
||||||
- rsync # file sync
|
|
||||||
- ruby-sass # code
|
|
||||||
- shellcheck # code
|
|
||||||
- shfmt # code
|
|
||||||
- smartmontools # disks
|
|
||||||
- steam # games
|
|
||||||
- stress # benchmark
|
|
||||||
- stylelint # code
|
|
||||||
- tesseract-data-deu
|
|
||||||
- tesseract-data-eng
|
|
||||||
- texlive-core
|
|
||||||
- thunderbird # email
|
|
||||||
- tidy
|
|
||||||
- traceroute # network
|
|
||||||
- transmission-remote-gtk # torrent
|
|
||||||
- ttf-fira-code # fonts
|
|
||||||
- ttf-fira-sans # fonts
|
|
||||||
- udftools # udf
|
|
||||||
- unrar # compression
|
|
||||||
- unzip # compression
|
|
||||||
- util-linux # various utils
|
|
||||||
- vdpauinfo # VDPAU
|
|
||||||
- vim # editor
|
|
||||||
- virt-manager # virtualization
|
|
||||||
- vscode-css-languageserver
|
|
||||||
- vulkan-intel # video driver
|
|
||||||
- vulkan-radeon # video driver
|
|
||||||
- wget # network
|
|
||||||
- wine # windows
|
|
||||||
- wireguard-tools # vpn
|
|
||||||
- wireplumber # audio
|
|
||||||
- xdg-desktop-portal-gtk # flatpak theming
|
|
||||||
- xdg-user-dirs # standards
|
|
||||||
- xdg-utils # standards
|
|
||||||
- xfsprogs # xfs
|
|
||||||
- yt-dlp
|
|
||||||
|
|
||||||
- name: Install config
|
|
||||||
ansible.builtin.script: ../arch-config/scripts/arch-config.sh
|
|
||||||
# - dockerfile-language-server # code
|
|
||||||
# - dockfmt
|
|
||||||
# - refind-theme-nord # bootmenu
|
|
0
galaxy.yml
Normal file
0
galaxy.yml
Normal file
0
roles/config/README.md
Normal file
0
roles/config/README.md
Normal file
0
roles/config/files/.config/.gitkeep
Executable file
0
roles/config/files/.config/.gitkeep
Executable file
500
roles/config/files/.config/Kvantum/Sweet#/Sweet#.kvconfig
Normal file
500
roles/config/files/.config/Kvantum/Sweet#/Sweet#.kvconfig
Normal file
@ -0,0 +1,500 @@
|
|||||||
|
[%General]
|
||||||
|
author=Eliver Lara
|
||||||
|
comment=A dark and modern theme for Kvantum.
|
||||||
|
x11drag=menubar_and_primary_toolbar
|
||||||
|
alt_mnemonic=true
|
||||||
|
left_tabs=true
|
||||||
|
attach_active_tab=true
|
||||||
|
mirror_doc_tabs=true
|
||||||
|
group_toolbar_buttons=false
|
||||||
|
toolbar_item_spacing=1
|
||||||
|
toolbar_interior_spacing=3
|
||||||
|
spread_progressbar=true
|
||||||
|
composite=true
|
||||||
|
menu_shadow_depth=5
|
||||||
|
menu_separator_height=6
|
||||||
|
tooltip_shadow_depth=6
|
||||||
|
splitter_width=4
|
||||||
|
scroll_width=6
|
||||||
|
scroll_arrows=false
|
||||||
|
scroll_min_extent=60
|
||||||
|
slider_width=2
|
||||||
|
slider_handle_width=18
|
||||||
|
slider_handle_length=18
|
||||||
|
center_toolbar_handle=true
|
||||||
|
check_size=14
|
||||||
|
textless_progressbar=false
|
||||||
|
progressbar_thickness=2
|
||||||
|
menubar_mouse_tracking=true
|
||||||
|
toolbutton_style=0
|
||||||
|
double_click=false
|
||||||
|
translucent_windows=false
|
||||||
|
blurring=false
|
||||||
|
popup_blurring=true
|
||||||
|
vertical_spin_indicators=false
|
||||||
|
spin_button_width=32
|
||||||
|
fill_rubberband=false
|
||||||
|
merge_menubar_with_toolbar=true
|
||||||
|
small_icon_size=16
|
||||||
|
large_icon_size=32
|
||||||
|
button_icon_size=16
|
||||||
|
toolbar_icon_size=22
|
||||||
|
combo_as_lineedit=true
|
||||||
|
animate_states=false
|
||||||
|
button_contents_shift=false
|
||||||
|
combo_menu=true
|
||||||
|
hide_combo_checkboxes=true
|
||||||
|
combo_focus_rect=true
|
||||||
|
groupbox_top_label=true
|
||||||
|
inline_spin_indicators=false
|
||||||
|
joined_inactive_tabs=false
|
||||||
|
layout_spacing=6
|
||||||
|
layout_margin=9
|
||||||
|
scrollbar_in_view=true
|
||||||
|
transient_scrollbar=true
|
||||||
|
transient_groove=false
|
||||||
|
submenu_overlap=0
|
||||||
|
tooltip_delay=-1
|
||||||
|
tree_branch_line=true
|
||||||
|
dark_titlebar=true
|
||||||
|
opaque=QMPlay2,kaffeine,kmplayer,subtitlecomposer,kdenlive,vlc,avidemux,avidemux2_qt4,avidemux3_qt4,avidemux3_qt5,kamoso,QtCreator,VirtualBox,trojita,dragon,digikam,virtualboxvm
|
||||||
|
reduce_window_opacity=18
|
||||||
|
scrollable_menu=false
|
||||||
|
submenu_delay=250
|
||||||
|
no_inactiveness=false
|
||||||
|
no_window_pattern=false
|
||||||
|
reduce_menu_opacity=0
|
||||||
|
respect_DE=true
|
||||||
|
click_behavior=0
|
||||||
|
contrast=1.00
|
||||||
|
dialog_button_layout=0
|
||||||
|
drag_from_buttons=false
|
||||||
|
intensity=1.00
|
||||||
|
menu_blur_radius=0
|
||||||
|
saturation=1.00
|
||||||
|
shadowless_popup=false
|
||||||
|
tooltip_blur_radius=0
|
||||||
|
|
||||||
|
[GeneralColors]
|
||||||
|
window.color=#161925
|
||||||
|
base.color=#181b28
|
||||||
|
alt.base.color=#252a3f78
|
||||||
|
button.color=#1e1e20
|
||||||
|
light.color=#0C0E15
|
||||||
|
mid.light.color=#0C0E15
|
||||||
|
dark.color=#0C0E15
|
||||||
|
mid.color=#0C0E15
|
||||||
|
highlight.color=#c50ed2
|
||||||
|
inactive.highlight.color=#654ea3
|
||||||
|
text.color=#aaaaac
|
||||||
|
window.text.color=#aaaaac
|
||||||
|
button.text.color=#aaaaac
|
||||||
|
disabled.text.color=#aaaaac78
|
||||||
|
tooltip.text.color=#aaaaac
|
||||||
|
highlight.text.color=#dadadc
|
||||||
|
link.color=#646464
|
||||||
|
link.visited.color=#7f8c8d
|
||||||
|
progress.indicator.text.color=#aaaaac
|
||||||
|
|
||||||
|
[Hacks]
|
||||||
|
transparent_ktitle_label=false
|
||||||
|
transparent_dolphin_view=false
|
||||||
|
transparent_pcmanfm_sidepane=false
|
||||||
|
blur_translucent=true
|
||||||
|
transparent_menutitle=false
|
||||||
|
respect_darkness=true
|
||||||
|
kcapacitybar_as_progressbar=true
|
||||||
|
force_size_grip=true
|
||||||
|
iconless_pushbutton=false
|
||||||
|
iconless_menu=false
|
||||||
|
disabled_icon_opacity=70
|
||||||
|
lxqtmainmenu_iconsize=22
|
||||||
|
normal_default_pushbutton=true
|
||||||
|
single_top_toolbar=true
|
||||||
|
tint_on_mouseover=0
|
||||||
|
transparent_pcmanfm_view=false
|
||||||
|
middle_click_scroll=false
|
||||||
|
no_selection_tint=false
|
||||||
|
opaque_colors=false
|
||||||
|
blur_only_active_window=false
|
||||||
|
centered_forms=false
|
||||||
|
kinetic_scrolling=false
|
||||||
|
noninteger_translucency=false
|
||||||
|
style_vertical_toolbars=false
|
||||||
|
|
||||||
|
[PanelButtonCommand]
|
||||||
|
frame=true
|
||||||
|
frame.element=button
|
||||||
|
frame.top=3
|
||||||
|
frame.bottom=3
|
||||||
|
frame.left=3
|
||||||
|
frame.right=3
|
||||||
|
interior=true
|
||||||
|
interior.element=button
|
||||||
|
indicator.size=8
|
||||||
|
text.normal.color=#aaaaac
|
||||||
|
text.focus.color=#c8c8ca
|
||||||
|
text.press.color=#d2d2d4
|
||||||
|
text.toggle.color=#aaaaac
|
||||||
|
text.shadow=0
|
||||||
|
text.margin=1
|
||||||
|
text.iconspacing=4
|
||||||
|
indicator.element=arrow
|
||||||
|
text.margin.top=2
|
||||||
|
text.margin.bottom=2
|
||||||
|
text.margin.left=2
|
||||||
|
text.margin.right=2
|
||||||
|
min_width=+0.3font
|
||||||
|
min_height=+0.3font
|
||||||
|
frame.expansion=6
|
||||||
|
|
||||||
|
[PanelButtonTool]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
|
||||||
|
[Dock]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
interior.element=dock
|
||||||
|
frame.element=dock
|
||||||
|
frame.top=1
|
||||||
|
frame.bottom=1
|
||||||
|
frame.left=1
|
||||||
|
frame.right=1
|
||||||
|
text.normal.color=#aaaaac
|
||||||
|
|
||||||
|
[DockTitle]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
frame=false
|
||||||
|
interior=false
|
||||||
|
text.normal.color=#aaaaac
|
||||||
|
text.focus.color=#c8c8ca
|
||||||
|
text.bold=false
|
||||||
|
|
||||||
|
[IndicatorSpinBox]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
frame=true
|
||||||
|
interior=true
|
||||||
|
frame.left=1
|
||||||
|
indicator.element=spin
|
||||||
|
indicator.size=10
|
||||||
|
text.normal.color=#aaaaac
|
||||||
|
|
||||||
|
[RadioButton]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
frame=false
|
||||||
|
interior.element=radio
|
||||||
|
text.normal.color=#aaaaac
|
||||||
|
text.focus.color=#c8c8ca
|
||||||
|
|
||||||
|
[CheckBox]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
frame=false
|
||||||
|
interior.element=checkbox
|
||||||
|
text.normal.color=#aaaaac
|
||||||
|
text.focus.color=#c8c8ca
|
||||||
|
|
||||||
|
[Focus]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
frame=true
|
||||||
|
frame.element=focus
|
||||||
|
frame.top=1
|
||||||
|
frame.bottom=1
|
||||||
|
frame.left=1
|
||||||
|
frame.right=1
|
||||||
|
frame.patternsize=20
|
||||||
|
|
||||||
|
[GenericFrame]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
frame=true
|
||||||
|
interior=false
|
||||||
|
frame.element=common
|
||||||
|
interior.element=common
|
||||||
|
frame.top=1
|
||||||
|
frame.bottom=1
|
||||||
|
frame.left=1
|
||||||
|
frame.right=1
|
||||||
|
|
||||||
|
[LineEdit]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
frame.element=lineedit
|
||||||
|
interior.element=lineedit
|
||||||
|
text.margin.left=0
|
||||||
|
text.margin.right=0
|
||||||
|
|
||||||
|
[DropDownButton]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
indicator.element=arrow-down
|
||||||
|
|
||||||
|
[IndicatorArrow]
|
||||||
|
indicator.element=arrow
|
||||||
|
indicator.size=8
|
||||||
|
|
||||||
|
[ToolboxTab]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
text.normal.color=#aaaaac
|
||||||
|
text.focus.color=#c8c8ca
|
||||||
|
text.press.color=#d2d2d4
|
||||||
|
|
||||||
|
[Tab]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
interior.element=tab
|
||||||
|
text.margin.left=8
|
||||||
|
text.margin.right=8
|
||||||
|
text.margin.top=2
|
||||||
|
text.margin.bottom=2
|
||||||
|
frame.element=tab
|
||||||
|
indicator.element=tab
|
||||||
|
frame.top=4
|
||||||
|
frame.bottom=4
|
||||||
|
frame.left=4
|
||||||
|
frame.right=4
|
||||||
|
text.normal.color=#aaaaac
|
||||||
|
text.focus.color=#aaaaac
|
||||||
|
text.toggle.color=#aaaaac
|
||||||
|
frame.expansion=0
|
||||||
|
|
||||||
|
[TabFrame]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
frame.element=tabframe
|
||||||
|
interior.element=tabframe
|
||||||
|
frame.top=4
|
||||||
|
frame.bottom=4
|
||||||
|
frame.left=4
|
||||||
|
frame.right=4
|
||||||
|
|
||||||
|
[TreeExpander]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
indicator.size=12
|
||||||
|
indicator.element=tree
|
||||||
|
|
||||||
|
[HeaderSection]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
interior.element=header
|
||||||
|
frame.element=header
|
||||||
|
frame.top=1
|
||||||
|
frame.bottom=1
|
||||||
|
frame.left=1
|
||||||
|
frame.right=1
|
||||||
|
text.bold=false
|
||||||
|
text.normal.color=#aaaaac
|
||||||
|
text.focus.color=#c8c8ca
|
||||||
|
text.press.color=#d2d2d4
|
||||||
|
text.toggle.color=#d2d2d4
|
||||||
|
frame.expansion=0
|
||||||
|
|
||||||
|
[SizeGrip]
|
||||||
|
indicator.element=resize-grip
|
||||||
|
|
||||||
|
[Toolbar]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
indicator.element=toolbar
|
||||||
|
indicator.size=5
|
||||||
|
text.margin=0
|
||||||
|
frame=true
|
||||||
|
interior.element=menubar
|
||||||
|
frame.element=menubar
|
||||||
|
text.normal.color=#aaaaac
|
||||||
|
text.focus.color=#c8c8ca
|
||||||
|
frame.left=0
|
||||||
|
frame.right=0
|
||||||
|
frame.top=0
|
||||||
|
frame.bottom=1
|
||||||
|
frame.expansion=0
|
||||||
|
|
||||||
|
[Slider]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
frame.element=slider
|
||||||
|
interior.element=slider
|
||||||
|
frame.top=3
|
||||||
|
frame.bottom=3
|
||||||
|
frame.left=3
|
||||||
|
frame.right=3
|
||||||
|
|
||||||
|
[SliderCursor]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
frame=false
|
||||||
|
interior.element=slidercursor
|
||||||
|
|
||||||
|
[Progressbar]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
frame.element=progress
|
||||||
|
interior.element=progress
|
||||||
|
text.margin=0
|
||||||
|
text.normal.color=#aaaaac
|
||||||
|
text.focus.color=#c8c8ca
|
||||||
|
text.press.color=#d2d2d4
|
||||||
|
text.toggle.color=#aaaaac
|
||||||
|
text.bold=false
|
||||||
|
frame.expansion=8
|
||||||
|
|
||||||
|
[ProgressbarContents]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
frame=true
|
||||||
|
frame.element=progress-pattern
|
||||||
|
interior.element=progress-pattern
|
||||||
|
|
||||||
|
[ItemView]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
text.margin=0
|
||||||
|
frame.element=itemview
|
||||||
|
interior.element=itemview
|
||||||
|
frame.top=2
|
||||||
|
frame.bottom=2
|
||||||
|
frame.left=2
|
||||||
|
frame.right=2
|
||||||
|
text.margin.top=2
|
||||||
|
text.margin.bottom=2
|
||||||
|
text.margin.left=4
|
||||||
|
text.margin.right=4
|
||||||
|
text.normal.color=#aaaaac
|
||||||
|
text.focus.color=#c8c8ca
|
||||||
|
text.press.color=#d2d2d4
|
||||||
|
text.toggle.color=#d2d2d4
|
||||||
|
min_width=+0.3font
|
||||||
|
min_height=+0.3font
|
||||||
|
frame.expansion=0
|
||||||
|
|
||||||
|
[Splitter]
|
||||||
|
indicator.size=48
|
||||||
|
|
||||||
|
[Scrollbar]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
indicator.element=arrow
|
||||||
|
indicator.size=8
|
||||||
|
|
||||||
|
[ScrollbarSlider]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
interior.element=scrollbarslider
|
||||||
|
interior=true
|
||||||
|
frame=false
|
||||||
|
frame.expansion=48
|
||||||
|
|
||||||
|
[ScrollbarGroove]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
interior.element=scrollbargroove
|
||||||
|
interior=true
|
||||||
|
frame=false
|
||||||
|
frame.expansion=48
|
||||||
|
|
||||||
|
[MenuItem]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
frame=true
|
||||||
|
frame.element=menuitem
|
||||||
|
interior.element=menuitem
|
||||||
|
indicator.element=menuitem
|
||||||
|
text.normal.color=#aaaaac
|
||||||
|
text.focus.color=#c8c8ca
|
||||||
|
text.margin.top=1
|
||||||
|
text.margin.bottom=1
|
||||||
|
text.margin.left=4
|
||||||
|
text.margin.right=4
|
||||||
|
frame.top=3
|
||||||
|
frame.bottom=3
|
||||||
|
frame.left=3
|
||||||
|
frame.right=3
|
||||||
|
text.bold=false
|
||||||
|
min_width=+0.3font
|
||||||
|
min_height=+0.3font
|
||||||
|
frame.expansion=0
|
||||||
|
|
||||||
|
[MenuBar]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
frame.element=menubar
|
||||||
|
interior.element=menubar
|
||||||
|
text.normal.color=#aaaaac
|
||||||
|
frame.bottom=0
|
||||||
|
frame.expansion=0
|
||||||
|
|
||||||
|
[MenuBarItem]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
interior=true
|
||||||
|
interior.element=menubaritem
|
||||||
|
frame.element=menubaritem
|
||||||
|
frame.top=2
|
||||||
|
frame.bottom=2
|
||||||
|
frame.left=2
|
||||||
|
frame.right=2
|
||||||
|
text.margin.left=4
|
||||||
|
text.margin.right=4
|
||||||
|
text.margin.top=0
|
||||||
|
text.margin.bottom=0
|
||||||
|
text.normal.color=#aaaaac
|
||||||
|
text.focus.color=#c8c8ca
|
||||||
|
frame.expansion=0
|
||||||
|
|
||||||
|
[TitleBar]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
frame=false
|
||||||
|
text.margin.top=2
|
||||||
|
text.margin.bottom=2
|
||||||
|
text.margin.left=2
|
||||||
|
text.margin.right=2
|
||||||
|
interior.element=titlebar
|
||||||
|
indicator.size=16
|
||||||
|
indicator.element=mdi
|
||||||
|
text.normal.color=#aaaaac
|
||||||
|
text.focus.color=#c8c8ca
|
||||||
|
text.bold=true
|
||||||
|
text.italic=false
|
||||||
|
frame.expansion=0
|
||||||
|
|
||||||
|
[ComboBox]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
interior.element=combo
|
||||||
|
frame.element=combo
|
||||||
|
text.press.color=#d2d2d4
|
||||||
|
indicator.element=carrow
|
||||||
|
|
||||||
|
[Menu]
|
||||||
|
inherits=PanelButtonCommand
|
||||||
|
frame.top=1
|
||||||
|
frame.bottom=1
|
||||||
|
frame.left=1
|
||||||
|
frame.right=1
|
||||||
|
frame.element=menu
|
||||||
|
interior.element=menu
|
||||||
|
text.normal.color=#aaaaac
|
||||||
|
text.shadow=false
|
||||||
|
frame.expansion=0
|
||||||
|
|
||||||
|
[GroupBox]
|
||||||
|
inherits=GenericFrame
|
||||||
|
frame=false
|
||||||
|
text.shadow=0
|
||||||
|
text.margin=0
|
||||||
|
text.normal.color=#aaaaac
|
||||||
|
text.focus.color=#c8c8ca
|
||||||
|
text.bold=true
|
||||||
|
frame.expansion=0
|
||||||
|
|
||||||
|
[TabBarFrame]
|
||||||
|
inherits=GenericFrame
|
||||||
|
frame=true
|
||||||
|
frame.element=tabBarFrame
|
||||||
|
interior=false
|
||||||
|
frame.top=4
|
||||||
|
frame.bottom=4
|
||||||
|
frame.left=4
|
||||||
|
frame.right=4
|
||||||
|
|
||||||
|
[ToolTip]
|
||||||
|
inherits=GenericFrame
|
||||||
|
frame.top=3
|
||||||
|
frame.bottom=3
|
||||||
|
frame.left=3
|
||||||
|
frame.right=3
|
||||||
|
interior=true
|
||||||
|
text.shadow=0
|
||||||
|
text.margin=0
|
||||||
|
interior.element=tooltip
|
||||||
|
frame.element=tooltip
|
||||||
|
frame.expansion=0
|
||||||
|
|
||||||
|
[StatusBar]
|
||||||
|
inherits=GenericFrame
|
||||||
|
frame=false
|
||||||
|
interior=false
|
||||||
|
interior=false
|
||||||
|
|
||||||
|
[Window]
|
||||||
|
interior=true
|
||||||
|
interior.element=window
|
2
roles/config/files/.config/Kvantum/kvantum.kvconfig
Normal file
2
roles/config/files/.config/Kvantum/kvantum.kvconfig
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[General]
|
||||||
|
theme=Sweet#
|
0
roles/config/files/.config/MangoHud/.gitkeep
Executable file
0
roles/config/files/.config/MangoHud/.gitkeep
Executable file
71
roles/config/files/.config/MangoHud/MangoHud.conf
Normal file
71
roles/config/files/.config/MangoHud/MangoHud.conf
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
### MangoHud configuration file
|
||||||
|
### Uncomment any options you wish to enable. Default options are left uncommented
|
||||||
|
### Use some_parameter=0 to disable a parameter (only works with on/off parameters)
|
||||||
|
### Everything below can be used / overridden with the environment variable MANGOHUD_CONFIG instead
|
||||||
|
|
||||||
|
################ PERFORMANCE #################
|
||||||
|
|
||||||
|
### Limit the application FPS
|
||||||
|
# fps_limit=
|
||||||
|
|
||||||
|
### VSYNC [0-3] 0 = adaptive; 1 = off; 2 = mailbox; 3 = on
|
||||||
|
# vsync=
|
||||||
|
|
||||||
|
################### VISUAL ###################
|
||||||
|
|
||||||
|
### Display the current CPU information
|
||||||
|
cpu_stats
|
||||||
|
cpu_temp
|
||||||
|
|
||||||
|
### Display the current GPU information
|
||||||
|
gpu_name
|
||||||
|
gpu_stats
|
||||||
|
gpu_temp
|
||||||
|
gpu_power
|
||||||
|
|
||||||
|
### Display the frametime line graph
|
||||||
|
frame_timing
|
||||||
|
|
||||||
|
### Display the current system time
|
||||||
|
# time
|
||||||
|
|
||||||
|
### Change the hud font size (default is 24)
|
||||||
|
font_size=20
|
||||||
|
|
||||||
|
### Change the hud position (default is top-left)
|
||||||
|
position=top-left
|
||||||
|
|
||||||
|
### Display the current CPU load & frequency for each core
|
||||||
|
core_load
|
||||||
|
|
||||||
|
### Display system ram / vram usage
|
||||||
|
ram
|
||||||
|
vram
|
||||||
|
|
||||||
|
### Disable / hide the hud by deafult
|
||||||
|
# no_display
|
||||||
|
|
||||||
|
### Hud position offset
|
||||||
|
# offset_x=
|
||||||
|
# offset_y=
|
||||||
|
|
||||||
|
### Hud dimensions
|
||||||
|
# width=
|
||||||
|
# height=
|
||||||
|
|
||||||
|
### Hud transparency / alpha
|
||||||
|
background_alpha=0.5
|
||||||
|
|
||||||
|
### Crosshair overlay (default size is 30)
|
||||||
|
# crosshair
|
||||||
|
# crosshair_size=
|
||||||
|
# crosshair_color=RRGGBB
|
||||||
|
|
||||||
|
### Output file
|
||||||
|
output_file /home/marc/Dokumente/mangohud.log
|
||||||
|
|
||||||
|
################## INTERACTION #################
|
||||||
|
|
||||||
|
### Change toggle keybinds for the hud & logging
|
||||||
|
toggle_hud=Shift_L+F12
|
||||||
|
toggle_logging=Shift_L+F2
|
@ -0,0 +1 @@
|
|||||||
|
<mxfile host="Electron" modified="2022-09-16T06:39:11.723Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/20.3.0 Chrome/104.0.5112.114 Electron/20.1.3 Safari/537.36" etag="3qCqwMKNhbMENT5RahAg" version="20.3.0" type="device"><diagram id="sNatUqG7UvmBRNr3f2vi" name="Seite-1">dZFND8IgDIZ/DfcBRvE8vy6edvBMRh0kbF0YZtNf7xaYk0xPlOd9S2lLeF4PZydbfUUFlrBMDYQfCGN0Q8V4TOQZiBA0gMoZFU0LKMwLIswifRgFXWL0iNabNoUlNg2UPmHSOexT2x1tWrWVFaxAUUq7pjejvI6UbveLcAFT6VhasF0QajmbYyedlgr7L8SPhOcO0YeoHnKw0/DmuYS80x/18zEHjf+RMAbL2+Ml2RA/vgE=</diagram></mxfile>
|
BIN
roles/config/files/.config/Vorlagen/Libreoffice Calc.ods
Normal file
BIN
roles/config/files/.config/Vorlagen/Libreoffice Calc.ods
Normal file
Binary file not shown.
BIN
roles/config/files/.config/Vorlagen/Libreoffice Impress.odp
Normal file
BIN
roles/config/files/.config/Vorlagen/Libreoffice Impress.odp
Normal file
Binary file not shown.
BIN
roles/config/files/.config/Vorlagen/Libreoffice Writer.odt
Normal file
BIN
roles/config/files/.config/Vorlagen/Libreoffice Writer.odt
Normal file
Binary file not shown.
3
roles/config/files/.config/Vorlagen/Markdown.md
Normal file
3
roles/config/files/.config/Vorlagen/Markdown.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<link href="/home/marc/GitProjects/markdown-css/markdown.css" rel="stylesheet"></link>
|
||||||
|
|
||||||
|
# Document Title
|
BIN
roles/config/files/.config/Vorlagen/Microsoft Excel.xlsx
Normal file
BIN
roles/config/files/.config/Vorlagen/Microsoft Excel.xlsx
Normal file
Binary file not shown.
BIN
roles/config/files/.config/Vorlagen/Microsoft PowerPoint.pptx
Normal file
BIN
roles/config/files/.config/Vorlagen/Microsoft PowerPoint.pptx
Normal file
Binary file not shown.
BIN
roles/config/files/.config/Vorlagen/Microsoft Word.docx
Normal file
BIN
roles/config/files/.config/Vorlagen/Microsoft Word.docx
Normal file
Binary file not shown.
@ -0,0 +1,8 @@
|
|||||||
|
json:{
|
||||||
|
"prefer_author_sort": false,
|
||||||
|
"toc_title": null,
|
||||||
|
"mobi_toc_at_start": false,
|
||||||
|
"dont_compress": false,
|
||||||
|
"no_inline_toc": false,
|
||||||
|
"share_not_sync": false
|
||||||
|
}
|
17
roles/config/files/.config/calibre/conversion/comic_input.py
Normal file
17
roles/config/files/.config/calibre/conversion/comic_input.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
json:{
|
||||||
|
"colors": 0,
|
||||||
|
"dont_normalize": false,
|
||||||
|
"keep_aspect_ratio": false,
|
||||||
|
"right2left": false,
|
||||||
|
"despeckle": false,
|
||||||
|
"no_sort": false,
|
||||||
|
"no_process": false,
|
||||||
|
"landscape": false,
|
||||||
|
"dont_sharpen": false,
|
||||||
|
"disable_trim": false,
|
||||||
|
"wide": false,
|
||||||
|
"output_format": "png",
|
||||||
|
"dont_grayscale": false,
|
||||||
|
"comic_image_size": null,
|
||||||
|
"dont_add_comic_pages_to_toc": false
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
json:{
|
||||||
|
"docx_no_cover": false,
|
||||||
|
"docx_no_pagebreaks_between_notes": false,
|
||||||
|
"docx_inline_subsup": false
|
||||||
|
}
|
11
roles/config/files/.config/calibre/conversion/docx_output.py
Normal file
11
roles/config/files/.config/calibre/conversion/docx_output.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
json:{
|
||||||
|
"docx_page_size": "letter",
|
||||||
|
"docx_custom_page_size": null,
|
||||||
|
"docx_no_cover": false,
|
||||||
|
"docx_no_toc": false,
|
||||||
|
"docx_page_margin_left": 72.0,
|
||||||
|
"docx_page_margin_top": 72.0,
|
||||||
|
"docx_page_margin_right": 72.0,
|
||||||
|
"docx_page_margin_bottom": 72.0,
|
||||||
|
"preserve_cover_aspect_ratio": false
|
||||||
|
}
|
12
roles/config/files/.config/calibre/conversion/epub_output.py
Normal file
12
roles/config/files/.config/calibre/conversion/epub_output.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
json:{
|
||||||
|
"dont_split_on_page_breaks": false,
|
||||||
|
"flow_size": 0,
|
||||||
|
"no_default_epub_cover": false,
|
||||||
|
"no_svg_cover": false,
|
||||||
|
"epub_inline_toc": false,
|
||||||
|
"epub_toc_at_end": false,
|
||||||
|
"toc_title": null,
|
||||||
|
"preserve_cover_aspect_ratio": true,
|
||||||
|
"epub_flatten": false,
|
||||||
|
"epub_version": "3"
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
json:{
|
||||||
|
"no_inline_fb2_toc": false
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
json:{
|
||||||
|
"sectionize": "files",
|
||||||
|
"fb2_genre": "antique"
|
||||||
|
}
|
13
roles/config/files/.config/calibre/conversion/heuristics.py
Normal file
13
roles/config/files/.config/calibre/conversion/heuristics.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
json:{
|
||||||
|
"enable_heuristics": false,
|
||||||
|
"markup_chapter_headings": true,
|
||||||
|
"italicize_common_cases": true,
|
||||||
|
"fix_indents": true,
|
||||||
|
"html_unwrap_factor": 0.4,
|
||||||
|
"unwrap_lines": true,
|
||||||
|
"delete_blank_paragraphs": true,
|
||||||
|
"format_scene_breaks": true,
|
||||||
|
"replace_scene_breaks": "",
|
||||||
|
"dehyphenate": true,
|
||||||
|
"renumber_headings": true
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
json:{
|
||||||
|
"htmlz_css_type": "class",
|
||||||
|
"htmlz_class_style": "external",
|
||||||
|
"htmlz_title_filename": false
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
json:{
|
||||||
|
"allow_conversion_with_errors": true
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
json:{
|
||||||
|
"change_justification": "original",
|
||||||
|
"extra_css": null,
|
||||||
|
"base_font_size": 0.0,
|
||||||
|
"font_size_mapping": null,
|
||||||
|
"line_height": 0.0,
|
||||||
|
"minimum_line_height": 120.0,
|
||||||
|
"embed_font_family": null,
|
||||||
|
"embed_all_fonts": false,
|
||||||
|
"subset_embedded_fonts": false,
|
||||||
|
"smarten_punctuation": false,
|
||||||
|
"unsmarten_punctuation": false,
|
||||||
|
"disable_font_rescaling": false,
|
||||||
|
"insert_blank_line": false,
|
||||||
|
"remove_paragraph_spacing": false,
|
||||||
|
"remove_paragraph_spacing_indent_size": 1.5,
|
||||||
|
"insert_blank_line_size": 0.5,
|
||||||
|
"input_encoding": null,
|
||||||
|
"filter_css": "",
|
||||||
|
"expand_css": false,
|
||||||
|
"asciiize": false,
|
||||||
|
"keep_ligatures": false,
|
||||||
|
"linearize_tables": false,
|
||||||
|
"transform_css_rules": "[]",
|
||||||
|
"transform_html_rules": "[]"
|
||||||
|
}
|
13
roles/config/files/.config/calibre/conversion/lrf_output.py
Normal file
13
roles/config/files/.config/calibre/conversion/lrf_output.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
json:{
|
||||||
|
"wordspace": 2.5,
|
||||||
|
"header": false,
|
||||||
|
"header_format": "%t by %a",
|
||||||
|
"minimum_indent": 0.0,
|
||||||
|
"serif_family": null,
|
||||||
|
"render_tables_as_images": false,
|
||||||
|
"sans_family": null,
|
||||||
|
"mono_family": null,
|
||||||
|
"text_size_multiplier_for_rendered_tables": 1.0,
|
||||||
|
"autorotation": false,
|
||||||
|
"header_separation": 0.0
|
||||||
|
}
|
12
roles/config/files/.config/calibre/conversion/mobi_output.py
Normal file
12
roles/config/files/.config/calibre/conversion/mobi_output.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
json:{
|
||||||
|
"prefer_author_sort": false,
|
||||||
|
"toc_title": null,
|
||||||
|
"mobi_keep_original_images": false,
|
||||||
|
"mobi_ignore_margins": false,
|
||||||
|
"mobi_toc_at_start": false,
|
||||||
|
"dont_compress": false,
|
||||||
|
"no_inline_toc": false,
|
||||||
|
"share_not_sync": false,
|
||||||
|
"personal_doc": "[PDOC]",
|
||||||
|
"mobi_file_type": "old"
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
json:{
|
||||||
|
"margin_top": 5.0,
|
||||||
|
"margin_left": 5.0,
|
||||||
|
"margin_right": 5.0,
|
||||||
|
"margin_bottom": 5.0,
|
||||||
|
"input_profile": "default",
|
||||||
|
"output_profile": "tablet"
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
json:{
|
||||||
|
"format": "doc",
|
||||||
|
"inline_toc": false,
|
||||||
|
"pdb_output_encoding": "cp1252"
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
json:{
|
||||||
|
"no_images": false,
|
||||||
|
"unwrap_factor": 0.45
|
||||||
|
}
|
26
roles/config/files/.config/calibre/conversion/pdf_output.py
Normal file
26
roles/config/files/.config/calibre/conversion/pdf_output.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
json:{
|
||||||
|
"use_profile_size": false,
|
||||||
|
"paper_size": "letter",
|
||||||
|
"custom_size": null,
|
||||||
|
"pdf_hyphenate": false,
|
||||||
|
"preserve_cover_aspect_ratio": false,
|
||||||
|
"pdf_serif_family": "Nimbus Roman",
|
||||||
|
"unit": "inch",
|
||||||
|
"pdf_sans_family": "Nimbus Sans [UKWN]",
|
||||||
|
"pdf_mono_family": "Nimbus Mono PS",
|
||||||
|
"pdf_standard_font": "serif",
|
||||||
|
"pdf_default_font_size": 20,
|
||||||
|
"pdf_mono_font_size": 16,
|
||||||
|
"pdf_page_numbers": false,
|
||||||
|
"pdf_footer_template": null,
|
||||||
|
"pdf_header_template": null,
|
||||||
|
"pdf_add_toc": false,
|
||||||
|
"toc_title": null,
|
||||||
|
"pdf_page_margin_left": 72.0,
|
||||||
|
"pdf_page_margin_top": 72.0,
|
||||||
|
"pdf_page_margin_right": 72.0,
|
||||||
|
"pdf_page_margin_bottom": 72.0,
|
||||||
|
"pdf_use_document_margins": false,
|
||||||
|
"pdf_page_number_map": null,
|
||||||
|
"pdf_odd_even_offset": 0.0
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
json:{
|
||||||
|
"inline_toc": false,
|
||||||
|
"full_image_depth": false,
|
||||||
|
"pml_output_encoding": "cp1252"
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
json:{
|
||||||
|
"inline_toc": false
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
json:{
|
||||||
|
"ignore_wmf": false
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
json:{
|
||||||
|
"search_replace": "[]",
|
||||||
|
"sr1_search": null,
|
||||||
|
"sr1_replace": null,
|
||||||
|
"sr2_search": null,
|
||||||
|
"sr2_replace": null,
|
||||||
|
"sr3_search": null,
|
||||||
|
"sr3_replace": null
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
json:{
|
||||||
|
"snb_insert_empty_line": false,
|
||||||
|
"snb_dont_indent_first_line": false,
|
||||||
|
"snb_hide_chapter_name": false,
|
||||||
|
"snb_full_screen": false
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
json:{
|
||||||
|
"chapter": "//*[((name()='h1' or name()='h2') and re:test(., '\\s*((chapter|book|section|part)\\s+)|((prolog|prologue|epilogue)(\\s+|$))', 'i')) or @class = 'chapter']",
|
||||||
|
"chapter_mark": "pagebreak",
|
||||||
|
"start_reading_at": null,
|
||||||
|
"remove_first_image": false,
|
||||||
|
"remove_fake_margins": true,
|
||||||
|
"insert_metadata": false,
|
||||||
|
"page_breaks_before": "//*[name()='h1' or name()='h2']"
|
||||||
|
}
|
11
roles/config/files/.config/calibre/conversion/toc.py
Normal file
11
roles/config/files/.config/calibre/conversion/toc.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
json:{
|
||||||
|
"level1_toc": null,
|
||||||
|
"level2_toc": null,
|
||||||
|
"level3_toc": null,
|
||||||
|
"toc_threshold": 6,
|
||||||
|
"max_toc_links": 50,
|
||||||
|
"no_chapters_in_toc": false,
|
||||||
|
"use_auto_toc": false,
|
||||||
|
"toc_filter": null,
|
||||||
|
"duplicate_links_in_toc": false
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
json:{
|
||||||
|
"paragraph_type": "auto",
|
||||||
|
"formatting_type": "auto",
|
||||||
|
"markdown_extensions": "footnotes, tables, toc",
|
||||||
|
"preserve_spaces": false,
|
||||||
|
"txt_in_remove_indents": false
|
||||||
|
}
|
11
roles/config/files/.config/calibre/conversion/txt_output.py
Normal file
11
roles/config/files/.config/calibre/conversion/txt_output.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
json:{
|
||||||
|
"newline": "system",
|
||||||
|
"max_line_length": 0,
|
||||||
|
"force_max_line_length": false,
|
||||||
|
"inline_toc": false,
|
||||||
|
"txt_output_formatting": "plain",
|
||||||
|
"keep_links": false,
|
||||||
|
"keep_image_references": false,
|
||||||
|
"keep_color": false,
|
||||||
|
"txt_output_encoding": "utf-8"
|
||||||
|
}
|
11
roles/config/files/.config/calibre/conversion/txtz_output.py
Normal file
11
roles/config/files/.config/calibre/conversion/txtz_output.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
json:{
|
||||||
|
"newline": "system",
|
||||||
|
"max_line_length": 0,
|
||||||
|
"force_max_line_length": false,
|
||||||
|
"inline_toc": false,
|
||||||
|
"txt_output_formatting": "plain",
|
||||||
|
"keep_links": false,
|
||||||
|
"keep_image_references": false,
|
||||||
|
"keep_color": false,
|
||||||
|
"txt_output_encoding": "utf-8"
|
||||||
|
}
|
4
roles/config/files/.config/calibre/cover_generation.json
Normal file
4
roles/config/files/.config/calibre/cover_generation.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"last_used_colors": "Grass",
|
||||||
|
"last_used_style": "Banner"
|
||||||
|
}
|
20
roles/config/files/.config/calibre/customize.py.json
Normal file
20
roles/config/files/.config/calibre/customize.py.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"disabled_plugins": {
|
||||||
|
"__class__": "set",
|
||||||
|
"__value__": []
|
||||||
|
},
|
||||||
|
"enabled_plugins": {
|
||||||
|
"__class__": "set",
|
||||||
|
"__value__": [
|
||||||
|
"DeDRM"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"filetype_mapping": {},
|
||||||
|
"plugin_customization": {},
|
||||||
|
"plugins": {
|
||||||
|
"DeACSM": "/home/marc/.config/calibre/plugins/DeACSM.zip",
|
||||||
|
"DeDRM": "/home/marc/.config/calibre/plugins/DeDRM.zip",
|
||||||
|
"KFX Input": "/home/exu/.config/calibre/plugins/KFX Input.zip",
|
||||||
|
"Obok DeDRM": "/home/marc/.config/calibre/plugins/Obok DeDRM.zip"
|
||||||
|
}
|
||||||
|
}
|
43
roles/config/files/.config/calibre/dynamic.pickle.json
Normal file
43
roles/config/files/.config/calibre/dynamic.pickle.json
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"DeDRMexport_Kindle_for_Mac_and_PC_Key_keys": "/home/marc/Downloads/default_key.k4i",
|
||||||
|
"DeDRMimport_Adobe_Digital_Editions_Key_keys": "/home/marc/Nextcloud/backups",
|
||||||
|
"Export ADE activation files": "/home/marc/Nextcloud/backups/adobe_account_backup_uuid_2d6cfbec-33fd-43ca-bcf9-e8b281114a17.zip",
|
||||||
|
"Export ADE keys": "/home/marc/Nextcloud/backups/adobe_uuid_2d6cfbec-33fd-43ca-bcf9-e8b281114a17.der",
|
||||||
|
"add a plugin dialog": "/home/marc/Downloads/DeDRM_tools_10.0.3",
|
||||||
|
"add books dialog dir": "/home/exu/Nextcloud/Reading/books/Sanderson, Brandon/Oathbringer",
|
||||||
|
"add books dialog dir-last-used-filter-spec-all-files": false,
|
||||||
|
"choose calibre library": "/home/exu/.local/share/Calibre-Library",
|
||||||
|
"database location dialog": "/home/marc/Nextcloud/Books",
|
||||||
|
"library_delete_books_again": false,
|
||||||
|
"notified-version-updates": {
|
||||||
|
"__class__": "set",
|
||||||
|
"__value__": [
|
||||||
|
"6.0",
|
||||||
|
"5.24",
|
||||||
|
"5.25",
|
||||||
|
"5.28"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"recursive book import root dir dialog": "/home/exu/Nextcloud/Reading/books",
|
||||||
|
"save to disk dialog": "/home/exu/Downloads/newBooks",
|
||||||
|
"sort_history": [
|
||||||
|
[
|
||||||
|
"series",
|
||||||
|
true
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"authors",
|
||||||
|
true
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"series",
|
||||||
|
true
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"timestamp",
|
||||||
|
false
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"welcome_wizard_device": "default",
|
||||||
|
"welcome_wizard_was_run": true
|
||||||
|
}
|
10
roles/config/files/.config/calibre/edit-book-history.plist
Normal file
10
roles/config/files/.config/calibre/edit-book-history.plist
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>lineedit_history_tweak_book_find_edit</key>
|
||||||
|
<array>
|
||||||
|
<string>font</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
38885
roles/config/files/.config/calibre/fonts/scanner_cache.json
Normal file
38885
roles/config/files/.config/calibre/fonts/scanner_cache.json
Normal file
File diff suppressed because it is too large
Load Diff
51
roles/config/files/.config/calibre/global.py.json
Normal file
51
roles/config/files/.config/calibre/global.py.json
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"add_formats_to_existing": false,
|
||||||
|
"case_sensitive": false,
|
||||||
|
"check_for_dupes_on_ctl": false,
|
||||||
|
"database_path": "/home/marc/library1.db",
|
||||||
|
"filename_pattern": "(?P<title>.+) - (?P<author>[^_]+)",
|
||||||
|
"input_format_order": [
|
||||||
|
"EPUB",
|
||||||
|
"AZW3",
|
||||||
|
"MOBI",
|
||||||
|
"LIT",
|
||||||
|
"PRC",
|
||||||
|
"FB2",
|
||||||
|
"HTML",
|
||||||
|
"HTM",
|
||||||
|
"XHTM",
|
||||||
|
"SHTML",
|
||||||
|
"XHTML",
|
||||||
|
"ZIP",
|
||||||
|
"DOCX",
|
||||||
|
"ODT",
|
||||||
|
"RTF",
|
||||||
|
"PDF",
|
||||||
|
"TXT"
|
||||||
|
],
|
||||||
|
"installation_uuid": "95258752-0a69-416a-90ff-c20df0267b24",
|
||||||
|
"isbndb_com_key": "",
|
||||||
|
"language": "de",
|
||||||
|
"library_path": "/home/exu/.local/share/Calibre-Library",
|
||||||
|
"limit_search_columns": false,
|
||||||
|
"limit_search_columns_to": [
|
||||||
|
"title",
|
||||||
|
"authors",
|
||||||
|
"tags",
|
||||||
|
"series",
|
||||||
|
"publisher"
|
||||||
|
],
|
||||||
|
"manage_device_metadata": "manual",
|
||||||
|
"mark_new_books": false,
|
||||||
|
"migrated": false,
|
||||||
|
"network_timeout": 5,
|
||||||
|
"new_book_tags": [],
|
||||||
|
"numeric_collation": false,
|
||||||
|
"output_format": "epub",
|
||||||
|
"read_file_metadata": true,
|
||||||
|
"saved_searches": {},
|
||||||
|
"swap_author_names": false,
|
||||||
|
"use_primary_find_in_search": true,
|
||||||
|
"user_categories": {},
|
||||||
|
"worker_process_priority": "normal"
|
||||||
|
}
|
933
roles/config/files/.config/calibre/gui.json
Normal file
933
roles/config/files/.config/calibre/gui.json
Normal file
@ -0,0 +1,933 @@
|
|||||||
|
{
|
||||||
|
"Plugin Updater plugin:plugin updater dialog": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AdnQywADAAAAAAHHAAABFQAABdkAAAMKAAAByQAAARcAAAXXAAADCAAAAAAAAAAAB4AAAAHJAAABFwAABdcAAAMI"
|
||||||
|
},
|
||||||
|
"action-layout-toolbar": [
|
||||||
|
"Add Books",
|
||||||
|
"Edit Metadata",
|
||||||
|
null,
|
||||||
|
"Convert Books",
|
||||||
|
"View",
|
||||||
|
null,
|
||||||
|
"Store",
|
||||||
|
"Donate",
|
||||||
|
"Fetch News",
|
||||||
|
"Help",
|
||||||
|
null,
|
||||||
|
"Remove Books",
|
||||||
|
"Choose Library",
|
||||||
|
"Save To Disk",
|
||||||
|
"Connect Share",
|
||||||
|
"Tweak ePub",
|
||||||
|
"Preferences",
|
||||||
|
"Obok DeDRM"
|
||||||
|
],
|
||||||
|
"action-layout-toolbar-device": [
|
||||||
|
"Add Books",
|
||||||
|
"Edit Metadata",
|
||||||
|
null,
|
||||||
|
"Convert Books",
|
||||||
|
"View",
|
||||||
|
"Send To Device",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"Location Manager",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"Fetch News",
|
||||||
|
"Save To Disk",
|
||||||
|
"Store",
|
||||||
|
"Connect Share",
|
||||||
|
null,
|
||||||
|
"Remove Books",
|
||||||
|
null,
|
||||||
|
"Help",
|
||||||
|
"Preferences",
|
||||||
|
"Obok DeDRM"
|
||||||
|
],
|
||||||
|
"advanced search dialog current tab": 3,
|
||||||
|
"advanced_search_template_tab_program_field": "",
|
||||||
|
"advanced_search_template_tab_test_field": "0",
|
||||||
|
"advanced_search_template_tab_value_field": "",
|
||||||
|
"ask_to_manage_device": [
|
||||||
|
"9d273cd5"
|
||||||
|
],
|
||||||
|
"basic_metadata_widget_splitter_state": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AAAA/wAAAAEAAAADAAAA6wAAAdoAAAEKAf////8BAAAAAQA="
|
||||||
|
},
|
||||||
|
"book_details_splitter_horizontal_state": [
|
||||||
|
true,
|
||||||
|
448
|
||||||
|
],
|
||||||
|
"book_info_dialog_layout": [
|
||||||
|
{
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AdnQywADAAAAAAH3AAAAJAAABYgAAAP7AAAB+QAAACYAAAWGAAAD+QAAAAAAAAAAB4AAAAH5AAAAJgAABYYAAAP5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AAAA/wAAAAEAAAACAAABXgAAAV4B/////wEAAAABAA=="
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"book_list_pin_splitter_state": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AAAA/wAAAAEAAAACAAABAAAAAEYA/////wEAAAABAA=="
|
||||||
|
},
|
||||||
|
"bulk_metadata_window_geometry": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AdnQywADAAAAAAAbAAAAEgAAB1gAAAQbAAAAHQAAABQAAAdWAAAEGQAAAAAAAAAAB4AAAAAdAAAAFAAAB1YAAAQZ"
|
||||||
|
},
|
||||||
|
"bulk_metadata_window_tab": 0,
|
||||||
|
"choose-merge-dialog-geometry": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AdnQywADAAAAAAKEAAABYgAABPsAAAK9AAAChgAAAWQAAAT5AAACuwAAAAAAAAAAB4AAAAKGAAABZAAABPkAAAK7"
|
||||||
|
},
|
||||||
|
"choose-merge-dialog-splitter-state": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AAAA/wAAAAEAAAACAAABXgAAAQAA/////wEAAAABAA=="
|
||||||
|
},
|
||||||
|
"convert_bulk_dialog_geom": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AdnQywADAAAAAAAeAAAAJAAAB1sAAAP7AAAAIAAAACYAAAdZAAAD+QAAAAAAAAAAB4AAAAAgAAAAJgAAB1kAAAP5"
|
||||||
|
},
|
||||||
|
"convert_single_dialog_geom": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AdnQywADAAAAAAA4AAAAEgAAB3UAAAPpAAAAOgAAABQAAAdzAAAD5wAAAAAAAAAAB4AAAAA6AAAAFAAAB3MAAAPn"
|
||||||
|
},
|
||||||
|
"cover_browser_splitter_vertical_state": [
|
||||||
|
false,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"custom_colors_for_color_dialog": [
|
||||||
|
[
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
[
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
[
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
[
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
[
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
[
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
[
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
[
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
[
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
[
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
[
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
[
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
[
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
[
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
[
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
[
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"diff_dialog_geom": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AdnQywADAAAAAAJnAAAAJgAABv8AAAP9AAACaQAAACgAAAb9AAAD+wAAAAAAAAAAB4AAAAJpAAAAKAAABv0AAAP7"
|
||||||
|
},
|
||||||
|
"duplicates-question-dialog-geometry": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AdnQywADAAAAAACOAAABTAAAA2gAAALPAAAAkAAAAU4AAANmAAACzQAAAAAAAAAAB4AAAACQAAABTgAAA2YAAALN"
|
||||||
|
},
|
||||||
|
"geometry-of-Plugin Updater plugin:plugin updater dialog": {
|
||||||
|
"frame_geometry": {
|
||||||
|
"height": 498,
|
||||||
|
"width": 1039,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"full_screened": false,
|
||||||
|
"geometry": {
|
||||||
|
"height": 498,
|
||||||
|
"width": 1039,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"maximized": false,
|
||||||
|
"normal_geometry": {
|
||||||
|
"height": 498,
|
||||||
|
"width": 1039,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"qt": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AdnQywADAAAAAAAAAAAAAAAABA4AAAHxAAAAAAAAAAAAAAQOAAAB8QAAAAAAAAAACFUAAAAAAAAAAAAABA4AAAHx"
|
||||||
|
},
|
||||||
|
"screen": {
|
||||||
|
"depth": 32,
|
||||||
|
"device_pixel_ratio": 2.0,
|
||||||
|
"geometry_in_logical_pixels": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 2133,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"index_in_screens_list": 0,
|
||||||
|
"manufacturer": "Acer Technologies",
|
||||||
|
"model": "XV272U V",
|
||||||
|
"name": "DP-1",
|
||||||
|
"serial": "",
|
||||||
|
"size_in_logical_pixels": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 2133
|
||||||
|
},
|
||||||
|
"virtual_geometry": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 4266,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"geometry-of-bulk_metadata_window_geometry": {
|
||||||
|
"frame_geometry": {
|
||||||
|
"height": 1030,
|
||||||
|
"width": 1850,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"full_screened": false,
|
||||||
|
"geometry": {
|
||||||
|
"height": 1030,
|
||||||
|
"width": 1850,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"maximized": false,
|
||||||
|
"normal_geometry": {
|
||||||
|
"height": 1030,
|
||||||
|
"width": 1850,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"qt": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AdnQywADAAAAAAAAAAAAAAAABzkAAAQFAAAAAAAAAAAAAAc5AAAEBQAAAAEAAAAACFUAAAAAAAAAAAAABzkAAAQF"
|
||||||
|
},
|
||||||
|
"screen": {
|
||||||
|
"depth": 32,
|
||||||
|
"device_pixel_ratio": 2.0,
|
||||||
|
"geometry_in_logical_pixels": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 2133,
|
||||||
|
"x": 2133,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"index_in_screens_list": 1,
|
||||||
|
"manufacturer": "HP Inc.",
|
||||||
|
"model": "OMEN 27q",
|
||||||
|
"name": "DP-2",
|
||||||
|
"serial": "",
|
||||||
|
"size_in_logical_pixels": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 2133
|
||||||
|
},
|
||||||
|
"virtual_geometry": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 4266,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"geometry-of-calibre_main_window_geometry": {
|
||||||
|
"frame_geometry": {
|
||||||
|
"height": 1154,
|
||||||
|
"width": 2129,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"full_screened": false,
|
||||||
|
"geometry": {
|
||||||
|
"height": 1154,
|
||||||
|
"width": 2129,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"maximized": false,
|
||||||
|
"normal_geometry": {
|
||||||
|
"height": 1154,
|
||||||
|
"width": 2129,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"qt": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AdnQywADAAAAAAAAAAAAAAAACFAAAASBAAAAAAAAAAAAAAhQAAAEgQAAAAAAAAAACFUAAAAAAAAAAAAACFAAAASB"
|
||||||
|
},
|
||||||
|
"screen": {
|
||||||
|
"depth": 32,
|
||||||
|
"device_pixel_ratio": 2.0,
|
||||||
|
"geometry_in_logical_pixels": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 2133,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"index_in_screens_list": 0,
|
||||||
|
"manufacturer": "Acer Technologies",
|
||||||
|
"model": "XV272U V",
|
||||||
|
"name": "DP-1",
|
||||||
|
"serial": "",
|
||||||
|
"size_in_logical_pixels": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 2133
|
||||||
|
},
|
||||||
|
"virtual_geometry": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 4266,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"geometry-of-convert_single_dialog_geom": {
|
||||||
|
"frame_geometry": {
|
||||||
|
"height": 980,
|
||||||
|
"width": 1850,
|
||||||
|
"x": 2133,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"full_screened": false,
|
||||||
|
"geometry": {
|
||||||
|
"height": 980,
|
||||||
|
"width": 1850,
|
||||||
|
"x": 2133,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"maximized": false,
|
||||||
|
"normal_geometry": {
|
||||||
|
"height": 980,
|
||||||
|
"width": 1850,
|
||||||
|
"x": 2133,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"qt": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AdnQywADAAAAAAhVAAAAAAAAD44AAAPTAAAIVQAAAAAAAA+OAAAD0wAAAAEAAAAACFUAAAhVAAAAAAAAD44AAAPT"
|
||||||
|
},
|
||||||
|
"screen": {
|
||||||
|
"depth": 32,
|
||||||
|
"device_pixel_ratio": 2.0,
|
||||||
|
"geometry_in_logical_pixels": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 2133,
|
||||||
|
"x": 2133,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"index_in_screens_list": 1,
|
||||||
|
"manufacturer": "HP Inc.",
|
||||||
|
"model": "OMEN 27q",
|
||||||
|
"name": "DP-2",
|
||||||
|
"serial": "",
|
||||||
|
"size_in_logical_pixels": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 2133
|
||||||
|
},
|
||||||
|
"virtual_geometry": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 4266,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"geometry-of-diff_dialog_geom": {
|
||||||
|
"frame_geometry": {
|
||||||
|
"height": 980,
|
||||||
|
"width": 1306,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"full_screened": false,
|
||||||
|
"geometry": {
|
||||||
|
"height": 980,
|
||||||
|
"width": 1306,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"maximized": false,
|
||||||
|
"normal_geometry": {
|
||||||
|
"height": 980,
|
||||||
|
"width": 1306,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"qt": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AdnQywADAAAAAAAAAAAAAAAABRkAAAPTAAAAAAAAAAAAAAUZAAAD0wAAAAAAAAAACFUAAAAAAAAAAAAABRkAAAPT"
|
||||||
|
},
|
||||||
|
"screen": {
|
||||||
|
"depth": 32,
|
||||||
|
"device_pixel_ratio": 2.0,
|
||||||
|
"geometry_in_logical_pixels": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 2133,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"index_in_screens_list": 0,
|
||||||
|
"manufacturer": "Acer Technologies",
|
||||||
|
"model": "XV272U V",
|
||||||
|
"name": "DP-1",
|
||||||
|
"serial": "",
|
||||||
|
"size_in_logical_pixels": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 2133
|
||||||
|
},
|
||||||
|
"virtual_geometry": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 4266,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"geometry-of-duplicates-question-dialog-geometry": {
|
||||||
|
"frame_geometry": {
|
||||||
|
"height": 384,
|
||||||
|
"width": 748,
|
||||||
|
"x": 2133,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"full_screened": false,
|
||||||
|
"geometry": {
|
||||||
|
"height": 384,
|
||||||
|
"width": 748,
|
||||||
|
"x": 2133,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"maximized": false,
|
||||||
|
"normal_geometry": {
|
||||||
|
"height": 384,
|
||||||
|
"width": 748,
|
||||||
|
"x": 2133,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"qt": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AdnQywADAAAAAAhVAAAAAAAAC0AAAAF/AAAIVQAAAAAAAAtAAAABfwAAAAAAAAAACFUAAAhVAAAAAAAAC0AAAAF/"
|
||||||
|
},
|
||||||
|
"screen": {
|
||||||
|
"depth": 32,
|
||||||
|
"device_pixel_ratio": 2.0,
|
||||||
|
"geometry_in_logical_pixels": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 2133,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"index_in_screens_list": 0,
|
||||||
|
"manufacturer": "Acer Technologies",
|
||||||
|
"model": "XV272U V",
|
||||||
|
"name": "DP-1",
|
||||||
|
"serial": "",
|
||||||
|
"size_in_logical_pixels": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 2133
|
||||||
|
},
|
||||||
|
"virtual_geometry": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 4266,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"geometry-of-jobs_dialog_geometry": {
|
||||||
|
"frame_geometry": {
|
||||||
|
"height": 542,
|
||||||
|
"width": 883,
|
||||||
|
"x": 2133,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"full_screened": false,
|
||||||
|
"geometry": {
|
||||||
|
"height": 542,
|
||||||
|
"width": 883,
|
||||||
|
"x": 2133,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"maximized": false,
|
||||||
|
"normal_geometry": {
|
||||||
|
"height": 542,
|
||||||
|
"width": 883,
|
||||||
|
"x": 2133,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"qt": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AdnQywADAAAAAAhVAAAAAAAAC8cAAAIdAAAIVQAAAAAAAAvHAAACHQAAAAEAAAAACFUAAAhVAAAAAAAAC8cAAAId"
|
||||||
|
},
|
||||||
|
"screen": {
|
||||||
|
"depth": 32,
|
||||||
|
"device_pixel_ratio": 2.0,
|
||||||
|
"geometry_in_logical_pixels": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 2133,
|
||||||
|
"x": 2133,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"index_in_screens_list": 1,
|
||||||
|
"manufacturer": "HP Inc.",
|
||||||
|
"model": "OMEN 27q",
|
||||||
|
"name": "DP-2",
|
||||||
|
"serial": "",
|
||||||
|
"size_in_logical_pixels": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 2133
|
||||||
|
},
|
||||||
|
"virtual_geometry": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 4266,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"geometry-of-metadata_single_gui_geom": {
|
||||||
|
"frame_geometry": {
|
||||||
|
"height": 600,
|
||||||
|
"width": 850,
|
||||||
|
"x": 2133,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"full_screened": false,
|
||||||
|
"geometry": {
|
||||||
|
"height": 600,
|
||||||
|
"width": 850,
|
||||||
|
"x": 2133,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"maximized": false,
|
||||||
|
"normal_geometry": {
|
||||||
|
"height": 600,
|
||||||
|
"width": 850,
|
||||||
|
"x": 2133,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"qt": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AdnQywADAAAAAAhVAAAAAAAAC6YAAAJXAAAIVQAAAAAAAAumAAACVwAAAAEAAAAACFUAAAhVAAAAAAAAC6YAAAJX"
|
||||||
|
},
|
||||||
|
"screen": {
|
||||||
|
"depth": 32,
|
||||||
|
"device_pixel_ratio": 2.0,
|
||||||
|
"geometry_in_logical_pixels": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 2133,
|
||||||
|
"x": 2133,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"index_in_screens_list": 1,
|
||||||
|
"manufacturer": "HP Inc.",
|
||||||
|
"model": "OMEN 27q",
|
||||||
|
"name": "DP-2",
|
||||||
|
"serial": "",
|
||||||
|
"size_in_logical_pixels": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 2133
|
||||||
|
},
|
||||||
|
"virtual_geometry": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 4266,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"geometry-of-metasingle_window_geometry3": {
|
||||||
|
"frame_geometry": {
|
||||||
|
"height": 1030,
|
||||||
|
"width": 1850,
|
||||||
|
"x": 2133,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"full_screened": false,
|
||||||
|
"geometry": {
|
||||||
|
"height": 1030,
|
||||||
|
"width": 1850,
|
||||||
|
"x": 2133,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"maximized": false,
|
||||||
|
"normal_geometry": {
|
||||||
|
"height": 1030,
|
||||||
|
"width": 1850,
|
||||||
|
"x": 2133,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"qt": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AdnQywADAAAAAAhVAAAAAAAAD44AAAQFAAAIVQAAAAAAAA+OAAAEBQAAAAEAAAAACFUAAAhVAAAAAAAAD44AAAQF"
|
||||||
|
},
|
||||||
|
"screen": {
|
||||||
|
"depth": 32,
|
||||||
|
"device_pixel_ratio": 2.0,
|
||||||
|
"geometry_in_logical_pixels": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 2133,
|
||||||
|
"x": 2133,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"index_in_screens_list": 1,
|
||||||
|
"manufacturer": "HP Inc.",
|
||||||
|
"model": "OMEN 27q",
|
||||||
|
"name": "DP-2",
|
||||||
|
"serial": "",
|
||||||
|
"size_in_logical_pixels": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 2133
|
||||||
|
},
|
||||||
|
"virtual_geometry": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 4266,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"geometry-of-preferences dialog geometry": {
|
||||||
|
"frame_geometry": {
|
||||||
|
"height": 720,
|
||||||
|
"width": 1295,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"full_screened": false,
|
||||||
|
"geometry": {
|
||||||
|
"height": 720,
|
||||||
|
"width": 1295,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"maximized": false,
|
||||||
|
"normal_geometry": {
|
||||||
|
"height": 720,
|
||||||
|
"width": 1295,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"qt": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AdnQywADAAAAAAAAAAAAAAAABQ4AAALPAAAAAAAAAAAAAAUOAAACzwAAAAAAAAAACFUAAAAAAAAAAAAABQ4AAALP"
|
||||||
|
},
|
||||||
|
"screen": {
|
||||||
|
"depth": 32,
|
||||||
|
"device_pixel_ratio": 2.0,
|
||||||
|
"geometry_in_logical_pixels": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 2133,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"index_in_screens_list": 0,
|
||||||
|
"manufacturer": "Acer Technologies",
|
||||||
|
"model": "XV272U V",
|
||||||
|
"name": "DP-1",
|
||||||
|
"serial": "",
|
||||||
|
"size_in_logical_pixels": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 2133
|
||||||
|
},
|
||||||
|
"virtual_geometry": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 4266,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"geometry-of-single-cover-fetch-dialog-geometry": {
|
||||||
|
"frame_geometry": {
|
||||||
|
"height": 600,
|
||||||
|
"width": 850,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"full_screened": false,
|
||||||
|
"geometry": {
|
||||||
|
"height": 600,
|
||||||
|
"width": 850,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"maximized": false,
|
||||||
|
"normal_geometry": {
|
||||||
|
"height": 600,
|
||||||
|
"width": 850,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"qt": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AdnQywADAAAAAAAAAAAAAAAAA1EAAAJXAAAAAAAAAAAAAANRAAACVwAAAAEAAAAACFUAAAAAAAAAAAAAA1EAAAJX"
|
||||||
|
},
|
||||||
|
"screen": {
|
||||||
|
"depth": 32,
|
||||||
|
"device_pixel_ratio": 2.0,
|
||||||
|
"geometry_in_logical_pixels": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 2133,
|
||||||
|
"x": 2133,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"index_in_screens_list": 1,
|
||||||
|
"manufacturer": "HP Inc.",
|
||||||
|
"model": "OMEN 27q",
|
||||||
|
"name": "DP-2",
|
||||||
|
"serial": "",
|
||||||
|
"size_in_logical_pixels": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 2133
|
||||||
|
},
|
||||||
|
"virtual_geometry": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 4266,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"geometry-of-template_editor_dialog_geometry": {
|
||||||
|
"frame_geometry": {
|
||||||
|
"height": 478,
|
||||||
|
"width": 706,
|
||||||
|
"x": 2133,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"full_screened": false,
|
||||||
|
"geometry": {
|
||||||
|
"height": 478,
|
||||||
|
"width": 706,
|
||||||
|
"x": 2133,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"maximized": false,
|
||||||
|
"normal_geometry": {
|
||||||
|
"height": 478,
|
||||||
|
"width": 706,
|
||||||
|
"x": 2133,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"qt": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AdnQywADAAAAAAhVAAAAAAAACxYAAAHdAAAIVQAAAAAAAAsWAAAB3QAAAAEAAAAACFUAAAhVAAAAAAAACxYAAAHd"
|
||||||
|
},
|
||||||
|
"screen": {
|
||||||
|
"depth": 32,
|
||||||
|
"device_pixel_ratio": 2.0,
|
||||||
|
"geometry_in_logical_pixels": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 2133,
|
||||||
|
"x": 2133,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"index_in_screens_list": 1,
|
||||||
|
"manufacturer": "HP Inc.",
|
||||||
|
"model": "OMEN 27q",
|
||||||
|
"name": "DP-2",
|
||||||
|
"serial": "",
|
||||||
|
"size_in_logical_pixels": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 2133
|
||||||
|
},
|
||||||
|
"virtual_geometry": {
|
||||||
|
"height": 1200,
|
||||||
|
"width": 4266,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gpm_template_editor_font": "Fira Sans",
|
||||||
|
"gpm_template_editor_word_wrap_mode": true,
|
||||||
|
"grid view visible": false,
|
||||||
|
"jobs view column layout3": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AAAA/wAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA04AAAAFAQEBAAAAAAAAAAAAAAAAAGT/////AAAAhAAAAAAAAAAFAAABRAAAAAEAAAAAAAAAtgAAAAEAAAAAAAAAjAAAAAEAAAAAAAAAZAAAAAEAAAAAAAAAZAAAAAEAAAAAAAAD6AAAAAAAAAAAAA=="
|
||||||
|
},
|
||||||
|
"jobs_dialog_geometry": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AdnQywADAAAAAAGYAAABEQAABQ4AAAMyAAABmgAAARMAAAUMAAADMAAAAAAAAAAAB4AAAAGaAAABEwAABQwAAAMw"
|
||||||
|
},
|
||||||
|
"library_usage_stats": {
|
||||||
|
"/home/exu/.local/share/Calibre-Library": 26,
|
||||||
|
"/home/marc/Calibre-Bibliothek": 200
|
||||||
|
},
|
||||||
|
"main_window_central_widget_state": {
|
||||||
|
"layout": "wide",
|
||||||
|
"narrow_desires": {
|
||||||
|
"book_details_height": 0.23066202090592333,
|
||||||
|
"cover_browser_width": 0.34992954438703616,
|
||||||
|
"quick_view_height": 0.2564459930313589,
|
||||||
|
"tag_browser_width": 0.2498825739783936
|
||||||
|
},
|
||||||
|
"narrow_visibility": {
|
||||||
|
"book_details": true,
|
||||||
|
"book_list": true,
|
||||||
|
"cover_browser": false,
|
||||||
|
"quick_view": false,
|
||||||
|
"tag_browser": true
|
||||||
|
},
|
||||||
|
"wide_desires": {
|
||||||
|
"book_details_width": 0.21042743071864725,
|
||||||
|
"cover_browser_height": 0.21367521367521367,
|
||||||
|
"quick_view_height": 0.2621082621082621,
|
||||||
|
"tag_browser_width": 0.09394081728511038
|
||||||
|
},
|
||||||
|
"wide_visibility": {
|
||||||
|
"book_details": true,
|
||||||
|
"book_list": true,
|
||||||
|
"cover_browser": false,
|
||||||
|
"quick_view": false,
|
||||||
|
"tag_browser": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"metadata-download-identify-widget-splitter-state": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AAAA/wAAAAEAAAACAAACAAAAAQAA/////wEAAAABAA=="
|
||||||
|
},
|
||||||
|
"metadata_single_gui_geom": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AdnQywADAAAAAAIiAAAA6QAABXcAAANEAAACJAAAAOsAAAV1AAADQgAAAAAAAAAAB4AAAAIkAAAA6wAABXUAAANC"
|
||||||
|
},
|
||||||
|
"metasingle_window_geometry3": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AdnQywADAAAAAAA+AAAAEgAAB3sAAAQbAAAAQAAAABQAAAd5AAAEGQAAAAAAAAAAB4AAAABAAAAAFAAAB3kAAAQZ"
|
||||||
|
},
|
||||||
|
"plugin config dialog:Dateityp:DeACSM": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AdnQywADAAAAAAMkAAABRAAABKIAAAL9AAADJgAAAUYAAASgAAAC+wAAAAAAAAAAB4AAAAMmAAABRgAABKAAAAL7"
|
||||||
|
},
|
||||||
|
"plugin config dialog:Dateityp:DeDRM": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AdnQywADAAAAAAQAAAAApAAABRoAAAKaAAAEAgAAAKYAAAUYAAACmAAAAAAAAAAAB4AAAAQCAAAApgAABRgAAAKY"
|
||||||
|
},
|
||||||
|
"preferences dialog geometry": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AdnQywADAAAAAAHtAAAApgAABZIAAAN5AAAB7wAAAKgAAAWQAAADdwAAAAAAAAAAB4AAAAHvAAAAqAAABZAAAAN3"
|
||||||
|
},
|
||||||
|
"previous_sort_order_BooksView": {
|
||||||
|
"authors": true,
|
||||||
|
"rating": true,
|
||||||
|
"series": true,
|
||||||
|
"timestamp": false,
|
||||||
|
"title": true
|
||||||
|
},
|
||||||
|
"quick_start_guide_added": true,
|
||||||
|
"quickview visible": false,
|
||||||
|
"qv_open_at_shutdown": false,
|
||||||
|
"recently_used_languages": [
|
||||||
|
"Englisch"
|
||||||
|
],
|
||||||
|
"replace_scene_breaks_history": [
|
||||||
|
"",
|
||||||
|
"<hr />",
|
||||||
|
"∗ ∗ ∗",
|
||||||
|
"• • •",
|
||||||
|
"♦ ♦ ♦",
|
||||||
|
"† †",
|
||||||
|
"‡ ‡ ‡",
|
||||||
|
"∞ ∞ ∞",
|
||||||
|
"¤ ¤ ¤"
|
||||||
|
],
|
||||||
|
"search bar visible": true,
|
||||||
|
"show_files_after_save": false,
|
||||||
|
"single-cover-fetch-dialog-geometry": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AdnQywADAAAAAAIhAAAA5wAABXYAAANCAAACIwAAAOkAAAV0AAADQAAAAAAAAAAAB4AAAAIjAAAA6QAABXQAAANA"
|
||||||
|
},
|
||||||
|
"tag browser search box visible": false,
|
||||||
|
"tag_browser_splitter_horizontal_state": [
|
||||||
|
true,
|
||||||
|
200
|
||||||
|
],
|
||||||
|
"tag_editor_geometry": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AdnQywADAAAAAAKTAAAAyAAABRYAAANlAAAClQAAAMoAAAUUAAADYwAAAAAAAAAAB4AAAAKVAAAAygAABRQAAANj"
|
||||||
|
},
|
||||||
|
"tag_editor_last_filter": "add_tag_input",
|
||||||
|
"template_editor_table_widths": [
|
||||||
|
90,
|
||||||
|
551
|
||||||
|
]
|
||||||
|
}
|
81
roles/config/files/.config/calibre/gui.py.json
Normal file
81
roles/config/files/.config/calibre/gui.py.json
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
{
|
||||||
|
"LRF_conversion_defaults": [],
|
||||||
|
"LRF_ebook_viewer_options": null,
|
||||||
|
"asked_library_thing_password": false,
|
||||||
|
"auto_download_cover": false,
|
||||||
|
"autolaunch_server": false,
|
||||||
|
"column_map": [
|
||||||
|
"title",
|
||||||
|
"ondevice",
|
||||||
|
"authors",
|
||||||
|
"size",
|
||||||
|
"timestamp",
|
||||||
|
"rating",
|
||||||
|
"publisher",
|
||||||
|
"tags",
|
||||||
|
"series",
|
||||||
|
"pubdate"
|
||||||
|
],
|
||||||
|
"confirm_delete": false,
|
||||||
|
"cover_flow_queue_length": 6,
|
||||||
|
"default_send_to_device_action": "DeviceAction:main::False:False",
|
||||||
|
"delete_news_from_library_on_upload": false,
|
||||||
|
"disable_animations": false,
|
||||||
|
"disable_tray_notification": false,
|
||||||
|
"enforce_cpu_limit": true,
|
||||||
|
"get_social_metadata": true,
|
||||||
|
"gui_layout": "wide",
|
||||||
|
"highlight_search_matches": false,
|
||||||
|
"internally_viewed_formats": [
|
||||||
|
"LRF",
|
||||||
|
"EPUB",
|
||||||
|
"LIT",
|
||||||
|
"MOBI",
|
||||||
|
"PRC",
|
||||||
|
"POBI",
|
||||||
|
"AZW",
|
||||||
|
"AZW3",
|
||||||
|
"HTML",
|
||||||
|
"FB2",
|
||||||
|
"PDB",
|
||||||
|
"RB",
|
||||||
|
"SNB",
|
||||||
|
"HTMLZ",
|
||||||
|
"KEPUB"
|
||||||
|
],
|
||||||
|
"jobs_search_history": [],
|
||||||
|
"lrf_viewer_search_history": [],
|
||||||
|
"main_search_history": [],
|
||||||
|
"main_window_geometry": {
|
||||||
|
"__class__": "bytearray",
|
||||||
|
"__value__": "AdnQywADAAAAAAAAAAAAAAAAB38AAAQjAAAAAgAAAAIAAAd9AAAEIQAAAAAAAAAAB4AAAAACAAAAAgAAB30AAAQh"
|
||||||
|
},
|
||||||
|
"match_tags_type": "any",
|
||||||
|
"new_version_notification": true,
|
||||||
|
"oldest_news": 60,
|
||||||
|
"overwrite_author_title_metadata": true,
|
||||||
|
"plugin_search_history": [],
|
||||||
|
"save_to_disk_template_history": [
|
||||||
|
"{authors} - {series}/{series} {series_index} - {title}",
|
||||||
|
"{author_sort}/{title}/{title} - {authors}"
|
||||||
|
],
|
||||||
|
"scheduler_search_history": [],
|
||||||
|
"search_as_you_type": false,
|
||||||
|
"send_to_device_template_history": [],
|
||||||
|
"send_to_storage_card_by_default": false,
|
||||||
|
"separate_cover_flow": false,
|
||||||
|
"shortcuts_search_history": [],
|
||||||
|
"show_avg_rating": true,
|
||||||
|
"sort_tags_by": "name",
|
||||||
|
"systray_icon": false,
|
||||||
|
"tag_browser_hidden_categories": {
|
||||||
|
"__class__": "set",
|
||||||
|
"__value__": []
|
||||||
|
},
|
||||||
|
"tweaks_search_history": [],
|
||||||
|
"upload_news_to_device": true,
|
||||||
|
"use_roman_numerals_for_series_number": true,
|
||||||
|
"viewer_search_history": [],
|
||||||
|
"viewer_toc_search_history": [],
|
||||||
|
"worker_limit": 6
|
||||||
|
}
|
10
roles/config/files/.config/calibre/history.plist
Normal file
10
roles/config/files/.config/calibre/history.plist
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>lineedit_history_xpath_edit_opt_chapter</key>
|
||||||
|
<array>
|
||||||
|
<string>//*[((name()='h1' or name()='h2') and re:test(., '\s*((chapter|book|section|part)\s+)|((prolog|prologue|epilogue)(\s+|$))', 'i')) or @class = 'chapter']</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"domain": "uk",
|
||||||
|
"ignore_fields": [],
|
||||||
|
"prefer_kindle_edition": true,
|
||||||
|
"server": "auto",
|
||||||
|
"use_mobi_asin": true
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"ignore_fields": [],
|
||||||
|
"max_covers": 5
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"ignore_fields": []
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"ignore_fields": []
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"ignore_fields": []
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"cover_priorities": {
|
||||||
|
"Big Book Search": 2,
|
||||||
|
"Goodreads": 3,
|
||||||
|
"Google": 2,
|
||||||
|
"Google Images": 2
|
||||||
|
},
|
||||||
|
"fewer_tags": true,
|
||||||
|
"ignore_fields": [],
|
||||||
|
"max_tags": 10
|
||||||
|
}
|
11
roles/config/files/.config/calibre/mtp_devices.json
Normal file
11
roles/config/files/.config/calibre/mtp_devices.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"blacklist": [
|
||||||
|
"9d273cd5"
|
||||||
|
],
|
||||||
|
"history": {
|
||||||
|
"9d273cd5": [
|
||||||
|
"ONEPLUS A3003",
|
||||||
|
"2021-05-15T18:17:07.571225+00:00"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
BIN
roles/config/files/.config/calibre/plugins/DeACSM.zip
Normal file
BIN
roles/config/files/.config/calibre/plugins/DeACSM.zip
Normal file
Binary file not shown.
@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<activationInfo xmlns="http://ns.adobe.com/adept">
|
||||||
|
<adept:activationServiceInfo xmlns:adept="http://ns.adobe.com/adept">
|
||||||
|
<adept:authURL>http://adeactivate.adobe.com/adept</adept:authURL>
|
||||||
|
<adept:userInfoURL>http://adeactivate.adobe.com/adept</adept:userInfoURL>
|
||||||
|
<adept:activationURL>http://adeactivate.adobe.com/adept</adept:activationURL>
|
||||||
|
<adept:certificate>MIIEsjCCA5qgAwIBAgIER2q5eDANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCVVMxIzAhBgNVBAoTGkFkb2JlIFN5c3RlbXMgSW5jb3Jwb3JhdGVkMRswGQYDVQQLExJEaWdpdGFsIFB1Ymxpc2hpbmcxMzAxBgNVBAMTKkFkb2JlIENvbnRlbnQgU2VydmVyIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0wODAxMDkxODM3NDVaFw0xMzAxMDkxOTA3NDVaMH0xCzAJBgNVBAYTAlVTMSMwIQYDVQQKExpBZG9iZSBTeXN0ZW1zIEluY29ycG9yYXRlZDEbMBkGA1UECxMSRGlnaXRhbCBQdWJsaXNoaW5nMSwwKgYDVQQDEyNodHRwOi8vYWRlYWN0aXZhdGUuYWRvYmUuY29tL2FkZXB0LzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAyXpCCWFh0Q3Bi1S7xf+CJfMd+cZz3HB0NknDScB1Cs8KdU0ygO7iqAgdiAdPliITkUTVEgUPvK+4yYCUderzBjq13/IrKlwEAyWeNgssJekpYgqNywo7Md1OApXzM47wVThNePNydhGYuNEEDDxzO+0JxucfhfArwnp7kIWA6q8CAwEAAaOCAbQwggGwMAsGA1UdDwQEAwIFoDBYBglghkgBhvprHgEESwxJVGhlIHByaXZhdGUga2V5IGNvcnJlc3BvbmRpbmcgdG8gdGhpcyBjZXJ0aWZpY2F0ZSBtYXkgaGF2ZSBiZWVuIGV4cG9ydGVkLjAUBgNVHSUEDTALBgkqhkiG9y8CAQQwgbIGA1UdIASBqjCBpzCBpAYJKoZIhvcvAQIDMIGWMIGTBggrBgEFBQcCAjCBhhqBg1lvdSBhcmUgbm90IHBlcm1pdHRlZCB0byB1c2UgdGhpcyBMaWNlbnNlIENlcnRpZmljYXRlIGV4Y2VwdCBhcyBwZXJtaXR0ZWQgYnkgdGhlIGxpY2Vuc2UgYWdyZWVtZW50IGFjY29tcGFueWluZyB0aGUgQWRvYmUgc29mdHdhcmUuMDEGA1UdHwQqMCgwJqAkoCKGIGh0dHA6Ly9jcmwuYWRvYmUuY29tL2Fkb2JlQ1MuY3JsMB8GA1UdIwQYMBaAFIvu8IFgyaLaHg5SwVgMBLBD94/oMB0GA1UdDgQWBBT9A+kXOPL6N57MN/zovbCGEx2+BTAJBgNVHRMEAjAAMA0GCSqGSIb3DQEBBQUAA4IBAQBVjUalliql3VjpLdT8si7OwPU1wQODllwlgfLH7tI/Ubq5wHDlprGtbf3jZm6tXY1qmh9mz1WnTmQHU3uPk8qgpihrpx4HJTjhAhLP0CXU1rd/t5whwhgT1lYfw77RRG2lZ5BzpHb/XjnY5yc3awd6F3Dli6kTkbcPyOCNoXlW4wiF+jkL+jBImY8xo2EewiJioY/iTYZH5HF+PjHF5mffANiLK/Q43l4f0YF8UagTfAJkD3iQV9lrTOWxKBgpfdyvekGqFCDq9AKzfpllqctxsC29W5bXU0cVYzf6Bj5ALs6tyi7r5fsIPSwszH/i4ixsuD0qccIgTXCwMNbt9zQu</adept:certificate>
|
||||||
|
<adept:authenticationCertificate>MIIEYDCCA0igAwIBAgIER2q5eTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCVVMxIzAhBgNVBAoTGkFkb2JlIFN5c3RlbXMgSW5jb3Jwb3JhdGVkMRswGQYDVQQLExJEaWdpdGFsIFB1Ymxpc2hpbmcxMzAxBgNVBAMTKkFkb2JlIENvbnRlbnQgU2VydmVyIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0wODAxMDkxODQzNDNaFw0xODAxMzEwODAwMDBaMHwxKzApBgNVBAMTImh0dHA6Ly9hZGVhY3RpdmF0ZS5hZG9iZS5jb20vYWRlcHQxGzAZBgNVBAsTEkRpZ2l0YWwgUHVibGlzaGluZzEjMCEGA1UEChMaQWRvYmUgU3lzdGVtcyBJbmNvcnBvcmF0ZWQxCzAJBgNVBAYTAlVTMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDZAxpzOZ7N38ZGlQjfMY/lfu4Ta4xK3FRm069VwdqGZIwrfTTRxnLE4A9i1X00BnNk/5z7C0pQX435ylIEQPxIFBKTH+ip5rfDNh/Iu6cIlB0N4I/t7Pac8cIDwbc9HxcGTvXg3BFqPjaGVbmVZmoUtSVOsphdA43sZc6j1iFfOQIDAQABo4IBYzCCAV8wEgYDVR0TAQH/BAgwBgEB/wIBATAUBgNVHSUEDTALBgkqhkiG9y8CAQUwgbIGA1UdIASBqjCBpzCBpAYJKoZIhvcvAQIDMIGWMIGTBggrBgEFBQcCAjCBhhqBg1lvdSBhcmUgbm90IHBlcm1pdHRlZCB0byB1c2UgdGhpcyBMaWNlbnNlIENlcnRpZmljYXRlIGV4Y2VwdCBhcyBwZXJtaXR0ZWQgYnkgdGhlIGxpY2Vuc2UgYWdyZWVtZW50IGFjY29tcGFueWluZyB0aGUgQWRvYmUgc29mdHdhcmUuMDEGA1UdHwQqMCgwJqAkoCKGIGh0dHA6Ly9jcmwuYWRvYmUuY29tL2Fkb2JlQ1MuY3JsMAsGA1UdDwQEAwIBBjAfBgNVHSMEGDAWgBSL7vCBYMmi2h4OUsFYDASwQ/eP6DAdBgNVHQ4EFgQU9RP19K+lzF03he+0T47hCVkPhdAwDQYJKoZIhvcNAQEFBQADggEBAJoqOj+bUa+bDYyOSljs6SVzWH2BN2ylIeZKpTQYEo7jA62tRqW/rBZcNIgCudFvEYa7vH8lHhvQak1s95g+NaNidb5tpgbS8Q7/XTyEGS/4Q2HYWHD/8ydKFROGbMhfxpdJgkgn21mb7dbsfq5AZVGS3M4PP1xrMDYm50+Sip9QIm1RJuSaKivDa/piA5p8/cv6w44YBefLzGUN674Y7WS5u656MjdyJsN/7Oup+12fHGiye5QS5mToujGd6LpU80gfhNxhrphASiEBYQ/BUhWjHkSi0j4WOiGvGpT1Xvntcj0rf6XV6lNrOddOYUL+KdC1uDIe8PUI+naKI+nWgrs=</adept:authenticationCertificate>
|
||||||
|
</adept:activationServiceInfo>
|
||||||
|
|
||||||
|
<adept:credentials xmlns:adept="http://ns.adobe.com/adept">
|
||||||
|
<adept:user>urn:uuid:2d6cfbec-33fd-43ca-bcf9-e8b281114a17</adept:user>
|
||||||
|
<adept:username method="AdobeID">adobe@frm01.net</adept:username>
|
||||||
|
<adept:pkcs12>MIIICgIBAzCCB8MGCSqGSIb3DQEHAaCCB7QEggewMIIHrDCCA3AGCSqGSIb3DQEHAaCCA2EEggNdMIIDWTCCA1UGCyqGSIb3DQEMCgECoIICszCCAq8wKQYKKoZIhvcNAQwBAzAbBBQYnc0dHYbt/zeyPvEbYrhgbYyxgwIDAMNQBIICgFqwh/cekpmgYtd57bU3rUEJEohVOC1OrXVh3j9b8UE7RHwiI04O9D0TZtZv0y6IH4VotY/t0j71JAHpXQwVnyQgSB0zrqi7inwJ8p/xFCPvrS/brzZk4hGmSRMfaeyoaqZhTYHThAFw3Hyz7FqmqU5p8bfggSwQ/xOviU7Ct+nBAfkrpaiNfeTQdY63lMKHghYL4IwmZMs6omaVN0ngMuN4/Nhfp7Ij0FK8SmpMMsRJCXSAAk6VjxNBKlLHwgkA4C+qxdyK7LXLd5JO3lGWpR2x+mBHalYt07xaq6OuuNWQKv0Ho1o75Rv3Blibnj8dvcweb3/3aEP0G3p+BfU2bFKrb3pAn3ClsPO1JJnncYdOLmNdkUlmbHK2RIAgAIqE/aJy1QblRcN34drbbV7FMEHScMdaUf6HVTvEj2TkZRT56GxpI4nhIE9KVzzUrj03COLYkRsfkp1NhfBJC1W324Q9Qd4veQWgpAGzYrpN9KwfQsMvNAEijuV0ExYcDxhcp+8cPcCcyjvm8DICAHKCnHtyDHFTutXR6fMQ8Jphu7uz4Sm++YDXby5M+Kb4luK5u8+PDlqfJ5LGAJ54MlzqGUK0OK/NN9U76ga5ekpFeU5wq2DTCQIZ/M4QuHcaTXpme8YempZzcnFWLh5HD/HDQOddCPoxvinGcjiPxC7MWOMKvWPu/oETHNUWYvCz12O8EycWIi4a0dW6Sa0DlC2S0wBBP2lgvd41/M/CksFuqUjiww9CsBMwzbFlmv0ebi9ZKD7IHVz68s+DV3swIGVq7EKSlCGrHNbthnAeOIlk1lwRUTn09a8cRDo8tomY6cBxxdlyqN24mq0vFhwTfqO2CBgxgY4waQYJKoZIhvcNAQkUMVweWgB1AHIAbgA6AHUAdQBpAGQAOgAyAGQANgBjAGYAYgBlAGMALQAzADMAZgBkAC0ANAAzAGMAYQAtAGIAYwBmADkALQBlADgAYgAyADgAMQAxADEANABhADEANzAhBgkqhkiG9w0BCRUxFAQSVGltZSAxNjU4ODU5ODkzNjE4MIIENAYJKoZIhvcNAQcGoIIEJTCCBCECAQAwggQaBgkqhkiG9w0BBwEwKQYKKoZIhvcNAQwBBjAbBBS6J/ZgKvOxIDg+c1iftQoiDAEjXQIDAMNQgIID4Hvb0y7+KcU6CEWDRPRqY6v9CGWmZxT2Ih2P0azuqio0N3tOb+fl4vymhvcJzdGAG1wTX9c2BpZBM7hMVq1YZuM1/rpdXSnbizBYa+ZzcbbYoNU77yQujwQtcJG1JwZI+VCJMJ+rXCKP+0ebIuCmh7AvcfN2tj48h3TFIeRbeic+YjvqB7RifbnaoVkV56L4k2TeKy1yrTqE8K5I7lICEVJA/ouXWOHdpMcse4SGRZ6n3JmZnC6aCoZTwKydQ0HGW7EvkmK+b3DS8gf3SGLvDEz3k3H41oYvr8zAN40ID7a71wdKHIlDcSeLkzZt6YNZBMXeH8ZlNf3s2qiCX79lRHuKlH7GYjNoUoLlAKO1OSwluxr6qKsLizOrmj7pgledFGYW5iCxVUooXneou/fDJtpOZGVKugPmwT5hL4ouiDI7R7gzWaGDjK0EIorthBTOymwj+hMx5wJ+2g42a0UyD0n73G5e1zRE7hT5axOXULsYMiKTJMCEopRVV70fjAsInBmDOCjIzRsPKtVIrVpOVJIr0zULLqyPucnaeJHpr9sb9hojUqRRZy55wnb5L7JbNLWnRTGLApnmhTx5KmbBTo7UdpyhWUqsApPrhRf3pMXPPBzkEZgeSEp2un3lnPMNhKVpo9lH2Ox206UXlzCYtrK+i+bH1wJdgdoDXzF01ysad8hHAcZJSUNF/i09DaoaFkX3uuPRbiqt5hgYdXb3E3aA5E/ChA80jFjaMLRBbsXSwvn7Ok9LD+kEUOoFvMyz9HrTS3j7fKQRvu20fn3uzyIkUyva46WlNWP/3KGcq1fWarJAuhYgwZbw3o9LB09w87uISN71Wm5pnYpAiFUo48oiTO/PB6F9F8SPiqR8h40Ghjvp7dFm9HzyXjq14T0GBkyG8RaJn9umc4yz1vOt7wPCXyf92cDltlSstpoY+SKrECTMcmnY//b9fyjRiXU4KJ7idSeKmF6thz1SBZ7FzUSoeozPHs+WOmIrVRJDez7J766oJBstqycIq15UVAPGt09ND/WXmhxDyHfZPlrR49YW87CLTFY8MmiOgd/fr+1gTsZqxLLb7L303MKeOQ8Lb0s4bMqGuCLsW/WcniHFhezN7YwyyAsdFhrI/Ugro2uDFlZ3BgAsipaqyzc4kyfGxjkSuIarzxV7huswH8COHSd0Jf9wj8iCLQSw4gX627C9F9dNy0AqT8uwXa/Z78leKUfSbbUB06Veram5y0kXnWGM97WyYKezxpmUHXUagZb5v6zG3o62/ia43tr8YmmUsJ9eyrl3xA+7RkERta/vryBuse5wQhpjd8F/2IxHnSG0MD4wITAJBgUrDgMCGgUABBSD2oMtYGOzFgykxeJda8+qoJQ7BAQUdwkJW7jL0vGmUxZNCAB2k3wM828CAwGGoA==</adept:pkcs12>
|
||||||
|
<adept:licenseCertificate>MIIDGDCCAoGgAwIBAgIGAYI7wTNyMA0GCSqGSIb3DQEBBQUAMHwxKzApBgNVBAMTImh0dHA6Ly9hZGVhY3RpdmF0ZS5hZG9iZS5jb20vYWRlcHQxGzAZBgNVBAsTEkRpZ2l0YWwgUHVibGlzaGluZzEjMCEGA1UEChMaQWRvYmUgU3lzdGVtcyBJbmNvcnBvcmF0ZWQxCzAJBgNVBAYTAlVTMB4XDTIyMDcyNjE4MjQ1M1oXDTMyMDcyNjE4MjQ1M1owODE2MDQGA1UEAxMtdXJuOnV1aWQ6MmQ2Y2ZiZWMtMzNmZC00M2NhLWJjZjktZThiMjgxMTE0YTE3MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDf+bPePv4GMbGBRssk/3dXktw0nIou8bhWCXha1i+rRi3VpuhHkj02KgBjOaEWEZy3Xkjkv7JGedv2cT0ZShIC6wOvGTsKT2Y3IJpNgWoZzniaWPz1zAGKffe41vjhrVj+8Vbtmt0MhxUbM3uA47echS7Kg9Cp036ydHYX70EeVQIDAQABo4HoMIHlMIG0BgNVHSMEgawwgamAFPUT9fSvpcxdN4XvtE+O4QlZD4XQoYGKpIGHMIGEMQswCQYDVQQGEwJVUzEjMCEGA1UEChMaQWRvYmUgU3lzdGVtcyBJbmNvcnBvcmF0ZWQxGzAZBgNVBAsTEkRpZ2l0YWwgUHVibGlzaGluZzEzMDEGA1UEAxMqQWRvYmUgQ29udGVudCBTZXJ2ZXIgQ2VydGlmaWNhdGUgQXV0aG9yaXR5ggRHarl5MAkGA1UdEwQCMAAwFAYDVR0lBA0wCwYJKoZIhvcvAgEHMAsGA1UdDwQEAwIFIDANBgkqhkiG9w0BAQUFAAOBgQC0lvq2fT3XCu8dB4au2kdQQvMPSVPLet9cS5bF5YSmox4YhLjF8BzBojNinU7fmxAnr5DL4Z4JL2OSf70SL6BOZAZUP8LTf2m3ES5096GGLvGryqNmHIeyublHhAa4sp7ya51NpkAi/Cj765WxORTMY+sF9D92R23Jj+Y8QslG1A==</adept:licenseCertificate>
|
||||||
|
<adept:privateLicenseKey>MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAN/5s94+/gYxsYFGyyT/d1eS3DScii7xuFYJeFrWL6tGLdWm6EeSPTYqAGM5oRYRnLdeSOS/skZ52/ZxPRlKEgLrA68ZOwpPZjcgmk2BahnOeJpY/PXMAYp997jW+OGtWP7xVu2a3QyHFRsze4Djt5yFLsqD0KnTfrJ0dhfvQR5VAgMBAAECgYBfw07xhnNsSJEBmjg/YG8xZVx7rjay7a0INFJeXFfTXlU4lX2ZJGDBqOGziy9h1TPxfwGhtIjP80hmLXKXPoFGKyTRhf1Z2QsLefX1hhpHhuWI6NxEtQiUiN4oD+igvIWQnPYkRJtth14hvOkl9wtQM6zFG1IV+8hkZf6gJ4c8gQJBAPq3K/UfSjHz1YmIo86wGU8bZHnsdo2uOX0biH3cQ20WsLv2cj6wo/DmFgVAE8hbYkW2yfrfN/ddL1skXTOHnSECQQDksj6mcZyzROW+DGC6csNEMuKVez7/DlWak4M4XwWa8wpQZPAqilNPjmrdK13Bsmxp8TrQDAJt4h/16GrWaEa1AkEAjdgQAJCBU52WVEeAFbG/v+fJgslrkWDemY94O2zgoNlTiCQ4IouhVOt3zeSgzJwXD0YJI+wiJ8sKvc/nAv5YwQJBAJVqp2gTnm+5ueh7Kc9nH5C1Nji3tybo9KDzc64m1wCvfbOc3xTMHzZBNCygIrdknVRyWRyIXCXysTL20KaYpmkCQHNYn681QtlOYC1AyMFcn/w78DmQwTDqlKIyx9oyaRJlEcq6KSeBgu1LJ0pGYq/5EGMYrp0KqMn/qXQ/1OSTY9M=</adept:privateLicenseKey>
|
||||||
|
<adept:authenticationCertificate>MIIEYDCCA0igAwIBAgIER2q5eTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCVVMxIzAhBgNVBAoTGkFkb2JlIFN5c3RlbXMgSW5jb3Jwb3JhdGVkMRswGQYDVQQLExJEaWdpdGFsIFB1Ymxpc2hpbmcxMzAxBgNVBAMTKkFkb2JlIENvbnRlbnQgU2VydmVyIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0wODAxMDkxODQzNDNaFw0xODAxMzEwODAwMDBaMHwxKzApBgNVBAMTImh0dHA6Ly9hZGVhY3RpdmF0ZS5hZG9iZS5jb20vYWRlcHQxGzAZBgNVBAsTEkRpZ2l0YWwgUHVibGlzaGluZzEjMCEGA1UEChMaQWRvYmUgU3lzdGVtcyBJbmNvcnBvcmF0ZWQxCzAJBgNVBAYTAlVTMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDZAxpzOZ7N38ZGlQjfMY/lfu4Ta4xK3FRm069VwdqGZIwrfTTRxnLE4A9i1X00BnNk/5z7C0pQX435ylIEQPxIFBKTH+ip5rfDNh/Iu6cIlB0N4I/t7Pac8cIDwbc9HxcGTvXg3BFqPjaGVbmVZmoUtSVOsphdA43sZc6j1iFfOQIDAQABo4IBYzCCAV8wEgYDVR0TAQH/BAgwBgEB/wIBATAUBgNVHSUEDTALBgkqhkiG9y8CAQUwgbIGA1UdIASBqjCBpzCBpAYJKoZIhvcvAQIDMIGWMIGTBggrBgEFBQcCAjCBhhqBg1lvdSBhcmUgbm90IHBlcm1pdHRlZCB0byB1c2UgdGhpcyBMaWNlbnNlIENlcnRpZmljYXRlIGV4Y2VwdCBhcyBwZXJtaXR0ZWQgYnkgdGhlIGxpY2Vuc2UgYWdyZWVtZW50IGFjY29tcGFueWluZyB0aGUgQWRvYmUgc29mdHdhcmUuMDEGA1UdHwQqMCgwJqAkoCKGIGh0dHA6Ly9jcmwuYWRvYmUuY29tL2Fkb2JlQ1MuY3JsMAsGA1UdDwQEAwIBBjAfBgNVHSMEGDAWgBSL7vCBYMmi2h4OUsFYDASwQ/eP6DAdBgNVHQ4EFgQU9RP19K+lzF03he+0T47hCVkPhdAwDQYJKoZIhvcNAQEFBQADggEBAJoqOj+bUa+bDYyOSljs6SVzWH2BN2ylIeZKpTQYEo7jA62tRqW/rBZcNIgCudFvEYa7vH8lHhvQak1s95g+NaNidb5tpgbS8Q7/XTyEGS/4Q2HYWHD/8ydKFROGbMhfxpdJgkgn21mb7dbsfq5AZVGS3M4PP1xrMDYm50+Sip9QIm1RJuSaKivDa/piA5p8/cv6w44YBefLzGUN674Y7WS5u656MjdyJsN/7Oup+12fHGiye5QS5mToujGd6LpU80gfhNxhrphASiEBYQ/BUhWjHkSi0j4WOiGvGpT1Xvntcj0rf6XV6lNrOddOYUL+KdC1uDIe8PUI+naKI+nWgrs=</adept:authenticationCertificate>
|
||||||
|
</adept:credentials>
|
||||||
|
|
||||||
|
<activationToken xmlns="http://ns.adobe.com/adept">
|
||||||
|
<device>urn:uuid:32095968-696a-46d1-95ef-e76097c33051</device>
|
||||||
|
<fingerprint>Pa7vI/H67wVERB/TsVjesFE6Kws=</fingerprint>
|
||||||
|
<deviceType>standalone</deviceType>
|
||||||
|
<activationURL>http://adeactivate.adobe.com/adept</activationURL>
|
||||||
|
<user>urn:uuid:2d6cfbec-33fd-43ca-bcf9-e8b281114a17</user>
|
||||||
|
<signature>B+Y6HxQ3o203HDb/5rSnal6Ca9tE8FEmVyiFcVpE9R7QzHo2NbpFzFHssFd2L+C7HKdFQ4pg+SFxyBLrDpLEdzILfPu+gRsDOvk/AGSisXEvdHsTFK9Yc5Cjkz8WkmWM1N6rgJ30V8AW6/d0mHj81g+Iue8VO8soBPkFwXGX1u4=</signature>
|
||||||
|
</activationToken>
|
||||||
|
<adept:operatorURLList xmlns:adept="http://ns.adobe.com/adept"><adept:user>urn:uuid:2d6cfbec-33fd-43ca-bcf9-e8b281114a17</adept:user><adept:operatorURL>https://acs4.kobo.com/fulfillment/Fulfill</adept:operatorURL></adept:operatorURLList><adept:licenseServices xmlns:adept="http://ns.adobe.com/adept"><adept:licenseServiceInfo><adept:licenseURL>https://nasigningservice.adobe.com/licensesign</adept:licenseURL><adept:certificate>MIIEvjCCA6agAwIBAgIER2q5ljANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCVVMxIzAhBgNVBAoTGkFkb2JlIFN5c3RlbXMgSW5jb3Jwb3JhdGVkMRswGQYDVQQLExJEaWdpdGFsIFB1Ymxpc2hpbmcxMzAxBgNVBAMTKkFkb2JlIENvbnRlbnQgU2VydmVyIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0wODA4MTExNjMzNDhaFw0xMzA4MTEwNzAwMDBaMIGIMQswCQYDVQQGEwJVUzEjMCEGA1UEChMaQWRvYmUgU3lzdGVtcyBJbmNvcnBvcmF0ZWQxGzAZBgNVBAsTEkRpZ2l0YWwgUHVibGlzaGluZzE3MDUGA1UEAxMuaHR0cHM6Ly9uYXNpZ25pbmdzZXJ2aWNlLmFkb2JlLmNvbS9saWNlbnNlc2lnbjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAs9GRZ1f5UTRySgZ2xAL7TaDKQBfdpIS9ei9Orica0N72BB/WE+82G5lfsZ2HdeCFDZG/oz2WPLXovcuUAbFKSIXVLyc7ONOd4sczeXQYPixeAvqzGtsyMArIzaeJcriGVPRnbD/spbuHR0BHhJEakIiDtQLJz+xgVYHlicx2H/kCAwEAAaOCAbQwggGwMAsGA1UdDwQEAwIFoDBYBglghkgBhvprHgEESwxJVGhlIHByaXZhdGUga2V5IGNvcnJlc3BvbmRpbmcgdG8gdGhpcyBjZXJ0aWZpY2F0ZSBtYXkgaGF2ZSBiZWVuIGV4cG9ydGVkLjAUBgNVHSUEDTALBgkqhkiG9y8CAQIwgbIGA1UdIASBqjCBpzCBpAYJKoZIhvcvAQIDMIGWMIGTBggrBgEFBQcCAjCBhhqBg1lvdSBhcmUgbm90IHBlcm1pdHRlZCB0byB1c2UgdGhpcyBMaWNlbnNlIENlcnRpZmljYXRlIGV4Y2VwdCBhcyBwZXJtaXR0ZWQgYnkgdGhlIGxpY2Vuc2UgYWdyZWVtZW50IGFjY29tcGFueWluZyB0aGUgQWRvYmUgc29mdHdhcmUuMDEGA1UdHwQqMCgwJqAkoCKGIGh0dHA6Ly9jcmwuYWRvYmUuY29tL2Fkb2JlQ1MuY3JsMB8GA1UdIwQYMBaAFIvu8IFgyaLaHg5SwVgMBLBD94/oMB0GA1UdDgQWBBSQ5K+bvggI6Rbh2u9nPhH8bcYTITAJBgNVHRMEAjAAMA0GCSqGSIb3DQEBBQUAA4IBAQC0l1L+BRCccZdb2d9zQBJ7JHkXWt1x/dUydU9I/na+QPFE5x+fGK4cRwaIfp6fNviGyvtJ6Wnxe6du/wlarC1o26UNpyWpnAltcy47LpVXsmcV5rUlhBx10l4lecuX0nx8/xF8joRz2BvvAusK+kxgKeiAjJg2W20wbJKh0Otct1ZihruQsEtGbZJ1L55xfNhrm6CKAHuGuTDYQ/S6W20dUaDUiNFhA2n2eEySLwUwgOuuhfVUPb8amQQKbF4rOQ2rdjAskEl/0CiavW6Xv0LGihThf6CjEbNSdy+vXQ7K9wFbKsE843DflpuSPfj2Aagtyrv/j1HsBjsf03e0uVu5</adept:certificate></adept:licenseServiceInfo></adept:licenseServices></activationInfo>
|
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<adept:deviceInfo xmlns:adept="http://ns.adobe.com/adept">
|
||||||
|
<adept:deviceType>standalone</adept:deviceType>
|
||||||
|
<adept:deviceClass>Desktop</adept:deviceClass>
|
||||||
|
<adept:deviceSerial>84abdfab8a0837c803a405f01b2fe493ae7b8c10</adept:deviceSerial>
|
||||||
|
<adept:deviceName>lupusregina</adept:deviceName>
|
||||||
|
<adept:version name="hobbes" value="9.3.58046"/>
|
||||||
|
<adept:version name="clientOS" value="Windows Vista"/>
|
||||||
|
<adept:version name="clientLocale" value="de"/>
|
||||||
|
<adept:fingerprint>Pa7vI/H67wVERB/TsVjesFE6Kws=</adept:fingerprint>
|
||||||
|
</adept:deviceInfo>
|
@ -0,0 +1 @@
|
|||||||
|
<EFBFBD>6(8[hK4<4B><03><>w,<2C>
|
13
roles/config/files/.config/calibre/plugins/DeACSM/modules/asn1crypto/.gitignore
vendored
Normal file
13
roles/config/files/.config/calibre/plugins/DeACSM/modules/asn1crypto/.gitignore
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
.tox/
|
||||||
|
__pycache__/
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
tests/output/
|
||||||
|
tmp/
|
||||||
|
*.egg-info/
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
.python-version
|
||||||
|
.DS_Store
|
||||||
|
.coverage
|
||||||
|
coverage.xml
|
@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2015-2022 Will Bond <will@wbond.net>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
@ -0,0 +1,305 @@
|
|||||||
|
Metadata-Version: 2.1
|
||||||
|
Name: asn1crypto
|
||||||
|
Version: 1.5.1
|
||||||
|
Summary: Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP
|
||||||
|
Home-page: https://github.com/wbond/asn1crypto
|
||||||
|
Author: wbond
|
||||||
|
Author-email: will@wbond.net
|
||||||
|
License: MIT
|
||||||
|
Description: # asn1crypto
|
||||||
|
|
||||||
|
A fast, pure Python library for parsing and serializing ASN.1 structures.
|
||||||
|
|
||||||
|
- [Features](#features)
|
||||||
|
- [Why Another Python ASN.1 Library?](#why-another-python-asn1-library)
|
||||||
|
- [Related Crypto Libraries](#related-crypto-libraries)
|
||||||
|
- [Current Release](#current-release)
|
||||||
|
- [Dependencies](#dependencies)
|
||||||
|
- [Installation](#installation)
|
||||||
|
- [License](#license)
|
||||||
|
- [Security Policy](#security-policy)
|
||||||
|
- [Documentation](#documentation)
|
||||||
|
- [Continuous Integration](#continuous-integration)
|
||||||
|
- [Testing](#testing)
|
||||||
|
- [Development](#development)
|
||||||
|
- [CI Tasks](#ci-tasks)
|
||||||
|
|
||||||
|
[![GitHub Actions CI](https://github.com/wbond/asn1crypto/workflows/CI/badge.svg)](https://github.com/wbond/asn1crypto/actions?workflow=CI)
|
||||||
|
[![CircleCI](https://circleci.com/gh/wbond/asn1crypto.svg?style=shield)](https://circleci.com/gh/wbond/asn1crypto)
|
||||||
|
[![PyPI](https://img.shields.io/pypi/v/asn1crypto.svg)](https://pypi.org/project/asn1crypto/)
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
In addition to an ASN.1 BER/DER decoder and DER serializer, the project includes
|
||||||
|
a bunch of ASN.1 structures for use with various common cryptography standards:
|
||||||
|
|
||||||
|
| Standard | Module | Source |
|
||||||
|
| ---------------------- | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| X.509 | [`asn1crypto.x509`](asn1crypto/x509.py) | [RFC 5280](https://tools.ietf.org/html/rfc5280) |
|
||||||
|
| CRL | [`asn1crypto.crl`](asn1crypto/crl.py) | [RFC 5280](https://tools.ietf.org/html/rfc5280) |
|
||||||
|
| CSR | [`asn1crypto.csr`](asn1crypto/csr.py) | [RFC 2986](https://tools.ietf.org/html/rfc2986), [RFC 2985](https://tools.ietf.org/html/rfc2985) |
|
||||||
|
| OCSP | [`asn1crypto.ocsp`](asn1crypto/ocsp.py) | [RFC 6960](https://tools.ietf.org/html/rfc6960) |
|
||||||
|
| PKCS#12 | [`asn1crypto.pkcs12`](asn1crypto/pkcs12.py) | [RFC 7292](https://tools.ietf.org/html/rfc7292) |
|
||||||
|
| PKCS#8 | [`asn1crypto.keys`](asn1crypto/keys.py) | [RFC 5208](https://tools.ietf.org/html/rfc5208) |
|
||||||
|
| PKCS#1 v2.1 (RSA keys) | [`asn1crypto.keys`](asn1crypto/keys.py) | [RFC 3447](https://tools.ietf.org/html/rfc3447) |
|
||||||
|
| DSA keys | [`asn1crypto.keys`](asn1crypto/keys.py) | [RFC 3279](https://tools.ietf.org/html/rfc3279) |
|
||||||
|
| Elliptic curve keys | [`asn1crypto.keys`](asn1crypto/keys.py) | [SECG SEC1 V2](http://www.secg.org/sec1-v2.pdf) |
|
||||||
|
| PKCS#3 v1.4 | [`asn1crypto.algos`](asn1crypto/algos.py) | [PKCS#3 v1.4](ftp://ftp.rsasecurity.com/pub/pkcs/ascii/pkcs-3.asc) |
|
||||||
|
| PKCS#5 v2.1 | [`asn1crypto.algos`](asn1crypto/algos.py) | [PKCS#5 v2.1](http://www.emc.com/collateral/white-papers/h11302-pkcs5v2-1-password-based-cryptography-standard-wp.pdf) |
|
||||||
|
| CMS (and PKCS#7) | [`asn1crypto.cms`](asn1crypto/cms.py) | [RFC 5652](https://tools.ietf.org/html/rfc5652), [RFC 2315](https://tools.ietf.org/html/rfc2315) |
|
||||||
|
| TSP | [`asn1crypto.tsp`](asn1crypto/tsp.py) | [RFC 3161](https://tools.ietf.org/html/rfc3161) |
|
||||||
|
| PDF signatures | [`asn1crypto.pdf`](asn1crypto/pdf.py) | [PDF 1.7](http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf) |
|
||||||
|
|
||||||
|
## Why Another Python ASN.1 Library?
|
||||||
|
|
||||||
|
Python has long had the [pyasn1](https://pypi.org/project/pyasn1/) and
|
||||||
|
[pyasn1_modules](https://pypi.org/project/pyasn1-modules/) available for
|
||||||
|
parsing and serializing ASN.1 structures. While the project does include a
|
||||||
|
comprehensive set of tools for parsing and serializing, the performance of the
|
||||||
|
library can be very poor, especially when dealing with bit fields and parsing
|
||||||
|
large structures such as CRLs.
|
||||||
|
|
||||||
|
After spending extensive time using *pyasn1*, the following issues were
|
||||||
|
identified:
|
||||||
|
|
||||||
|
1. Poor performance
|
||||||
|
2. Verbose, non-pythonic API
|
||||||
|
3. Out-dated and incomplete definitions in *pyasn1-modules*
|
||||||
|
4. No simple way to map data to native Python data structures
|
||||||
|
5. No mechanism for overridden universal ASN.1 types
|
||||||
|
|
||||||
|
The *pyasn1* API is largely method driven, and uses extensive configuration
|
||||||
|
objects and lowerCamelCase names. There were no consistent options for
|
||||||
|
converting types of native Python data structures. Since the project supports
|
||||||
|
out-dated versions of Python, many newer language features are unavailable
|
||||||
|
for use.
|
||||||
|
|
||||||
|
Time was spent trying to profile issues with the performance, however the
|
||||||
|
architecture made it hard to pin down the primary source of the poor
|
||||||
|
performance. Attempts were made to improve performance by utilizing unreleased
|
||||||
|
patches and delaying parsing using the `Any` type. Even with such changes, the
|
||||||
|
performance was still unacceptably slow.
|
||||||
|
|
||||||
|
Finally, a number of structures in the cryptographic space use universal data
|
||||||
|
types such as `BitString` and `OctetString`, but interpret the data as other
|
||||||
|
types. For instance, signatures are really byte strings, but are encoded as
|
||||||
|
`BitString`. Elliptic curve keys use both `BitString` and `OctetString` to
|
||||||
|
represent integers. Parsing these structures as the base universal types and
|
||||||
|
then re-interpreting them wastes computation.
|
||||||
|
|
||||||
|
*asn1crypto* uses the following techniques to improve performance, especially
|
||||||
|
when extracting one or two fields from large, complex structures:
|
||||||
|
|
||||||
|
- Delayed parsing of byte string values
|
||||||
|
- Persistence of original ASN.1 encoded data until a value is changed
|
||||||
|
- Lazy loading of child fields
|
||||||
|
- Utilization of high-level Python stdlib modules
|
||||||
|
|
||||||
|
While there is no extensive performance test suite, the
|
||||||
|
`CRLTests.test_parse_crl` test case was used to parse a 21MB CRL file on a
|
||||||
|
late 2013 rMBP. *asn1crypto* parsed the certificate serial numbers in just
|
||||||
|
under 8 seconds. With *pyasn1*, using definitions from *pyasn1-modules*, the
|
||||||
|
same parsing took over 4,100 seconds.
|
||||||
|
|
||||||
|
For smaller structures the performance difference can range from a few times
|
||||||
|
faster to an order of magnitude or more.
|
||||||
|
|
||||||
|
## Related Crypto Libraries
|
||||||
|
|
||||||
|
*asn1crypto* is part of the modularcrypto family of Python packages:
|
||||||
|
|
||||||
|
- [asn1crypto](https://github.com/wbond/asn1crypto)
|
||||||
|
- [oscrypto](https://github.com/wbond/oscrypto)
|
||||||
|
- [csrbuilder](https://github.com/wbond/csrbuilder)
|
||||||
|
- [certbuilder](https://github.com/wbond/certbuilder)
|
||||||
|
- [crlbuilder](https://github.com/wbond/crlbuilder)
|
||||||
|
- [ocspbuilder](https://github.com/wbond/ocspbuilder)
|
||||||
|
- [certvalidator](https://github.com/wbond/certvalidator)
|
||||||
|
|
||||||
|
## Current Release
|
||||||
|
|
||||||
|
1.5.0 - [changelog](changelog.md)
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
Python 2.6, 2.7, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10 or pypy. *No third-party
|
||||||
|
packages required.*
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install asn1crypto
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
*asn1crypto* is licensed under the terms of the MIT license. See the
|
||||||
|
[LICENSE](LICENSE) file for the exact license text.
|
||||||
|
|
||||||
|
## Security Policy
|
||||||
|
|
||||||
|
The security policies for this project are covered in
|
||||||
|
[SECURITY.md](https://github.com/wbond/asn1crypto/blob/master/SECURITY.md).
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
The documentation for *asn1crypto* is composed of tutorials on basic usage and
|
||||||
|
links to the source for the various pre-defined type classes.
|
||||||
|
|
||||||
|
### Tutorials
|
||||||
|
|
||||||
|
- [Universal Types with BER/DER Decoder and DER Encoder](docs/universal_types.md)
|
||||||
|
- [PEM Encoder and Decoder](docs/pem.md)
|
||||||
|
|
||||||
|
### Reference
|
||||||
|
|
||||||
|
- [Universal types](asn1crypto/core.py), `asn1crypto.core`
|
||||||
|
- [Digest, HMAC, signed digest and encryption algorithms](asn1crypto/algos.py), `asn1crypto.algos`
|
||||||
|
- [Private and public keys](asn1crypto/keys.py), `asn1crypto.keys`
|
||||||
|
- [X509 certificates](asn1crypto/x509.py), `asn1crypto.x509`
|
||||||
|
- [Certificate revocation lists (CRLs)](asn1crypto/crl.py), `asn1crypto.crl`
|
||||||
|
- [Online certificate status protocol (OCSP)](asn1crypto/ocsp.py), `asn1crypto.ocsp`
|
||||||
|
- [Certificate signing requests (CSRs)](asn1crypto/csr.py), `asn1crypto.csr`
|
||||||
|
- [Private key/certificate containers (PKCS#12)](asn1crypto/pkcs12.py), `asn1crypto.pkcs12`
|
||||||
|
- [Cryptographic message syntax (CMS, PKCS#7)](asn1crypto/cms.py), `asn1crypto.cms`
|
||||||
|
- [Time stamp protocol (TSP)](asn1crypto/tsp.py), `asn1crypto.tsp`
|
||||||
|
- [PDF signatures](asn1crypto/pdf.py), `asn1crypto.pdf`
|
||||||
|
|
||||||
|
## Continuous Integration
|
||||||
|
|
||||||
|
Various combinations of platforms and versions of Python are tested via:
|
||||||
|
|
||||||
|
- [macOS, Linux, Windows](https://github.com/wbond/asn1crypto/actions/workflows/ci.yml) via GitHub Actions
|
||||||
|
- [arm64](https://circleci.com/gh/wbond/asn1crypto) via CircleCI
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
Tests are written using `unittest` and require no third-party packages.
|
||||||
|
|
||||||
|
Depending on what type of source is available for the package, the following
|
||||||
|
commands can be used to run the test suite.
|
||||||
|
|
||||||
|
### Git Repository
|
||||||
|
|
||||||
|
When working within a Git working copy, or an archive of the Git repository,
|
||||||
|
the full test suite is run via:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python run.py tests
|
||||||
|
```
|
||||||
|
|
||||||
|
To run only some tests, pass a regular expression as a parameter to `tests`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python run.py tests ocsp
|
||||||
|
```
|
||||||
|
|
||||||
|
### PyPi Source Distribution
|
||||||
|
|
||||||
|
When working within an extracted source distribution (aka `.tar.gz`) from
|
||||||
|
PyPi, the full test suite is run via:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python setup.py test
|
||||||
|
```
|
||||||
|
|
||||||
|
### Package
|
||||||
|
|
||||||
|
When the package has been installed via pip (or another method), the package
|
||||||
|
`asn1crypto_tests` may be installed and invoked to run the full test suite:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install asn1crypto_tests
|
||||||
|
python -m asn1crypto_tests
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
To install the package used for linting, execute:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install --user -r requires/lint
|
||||||
|
```
|
||||||
|
|
||||||
|
The following command will run the linter:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python run.py lint
|
||||||
|
```
|
||||||
|
|
||||||
|
Support for code coverage can be installed via:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install --user -r requires/coverage
|
||||||
|
```
|
||||||
|
|
||||||
|
Coverage is measured by running:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python run.py coverage
|
||||||
|
```
|
||||||
|
|
||||||
|
To change the version number of the package, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python run.py version {pep440_version}
|
||||||
|
```
|
||||||
|
|
||||||
|
To install the necessary packages for releasing a new version on PyPI, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install --user -r requires/release
|
||||||
|
```
|
||||||
|
|
||||||
|
Releases are created by:
|
||||||
|
|
||||||
|
- Making a git tag in [PEP 440](https://www.python.org/dev/peps/pep-0440/#examples-of-compliant-version-schemes) format
|
||||||
|
- Running the command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python run.py release
|
||||||
|
```
|
||||||
|
|
||||||
|
Existing releases can be found at https://pypi.org/project/asn1crypto/.
|
||||||
|
|
||||||
|
## CI Tasks
|
||||||
|
|
||||||
|
A task named `deps` exists to download and stage all necessary testing
|
||||||
|
dependencies. On posix platforms, `curl` is used for downloads and on Windows
|
||||||
|
PowerShell with `Net.WebClient` is used. This configuration sidesteps issues
|
||||||
|
related to getting pip to work properly and messing with `site-packages` for
|
||||||
|
the version of Python being used.
|
||||||
|
|
||||||
|
The `ci` task runs `lint` (if flake8 is available for the version of Python) and
|
||||||
|
`coverage` (or `tests` if coverage is not available for the version of Python).
|
||||||
|
If the current directory is a clean git working copy, the coverage data is
|
||||||
|
submitted to codecov.io.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python run.py deps
|
||||||
|
python run.py ci
|
||||||
|
```
|
||||||
|
|
||||||
|
Keywords: asn1 crypto pki x509 certificate rsa dsa ec dh
|
||||||
|
Platform: UNKNOWN
|
||||||
|
Classifier: Development Status :: 5 - Production/Stable
|
||||||
|
Classifier: Intended Audience :: Developers
|
||||||
|
Classifier: License :: OSI Approved :: MIT License
|
||||||
|
Classifier: Programming Language :: Python
|
||||||
|
Classifier: Programming Language :: Python :: 2
|
||||||
|
Classifier: Programming Language :: Python :: 2.6
|
||||||
|
Classifier: Programming Language :: Python :: 2.7
|
||||||
|
Classifier: Programming Language :: Python :: 3
|
||||||
|
Classifier: Programming Language :: Python :: 3.2
|
||||||
|
Classifier: Programming Language :: Python :: 3.3
|
||||||
|
Classifier: Programming Language :: Python :: 3.4
|
||||||
|
Classifier: Programming Language :: Python :: 3.5
|
||||||
|
Classifier: Programming Language :: Python :: 3.6
|
||||||
|
Classifier: Programming Language :: Python :: 3.7
|
||||||
|
Classifier: Programming Language :: Python :: 3.8
|
||||||
|
Classifier: Programming Language :: Python :: 3.9
|
||||||
|
Classifier: Programming Language :: Python :: 3.10
|
||||||
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
||||||
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
||||||
|
Classifier: Topic :: Security :: Cryptography
|
||||||
|
Description-Content-Type: text/markdown
|
@ -0,0 +1,37 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## How to Report
|
||||||
|
|
||||||
|
If you believe you've found an issue that has security implications, please do
|
||||||
|
not post a public issue on GitHub. Instead, email the project lead, Will Bond,
|
||||||
|
at will@wbond.net.
|
||||||
|
|
||||||
|
You should receive a response within two business days, and follow up emails
|
||||||
|
during the process of confirming the potential issue.
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
The asn1crypto project only provides security patches for the most recent
|
||||||
|
release. This is primarily a function of available resources.
|
||||||
|
|
||||||
|
## Disclosure Process
|
||||||
|
|
||||||
|
The following process is used when handling a potential secuirty issue:
|
||||||
|
|
||||||
|
1. The report should be emailed to will@wbond.net, and NOT posted on the
|
||||||
|
GitHub issue tracker.
|
||||||
|
2. Confirmation of receipt of the report should happen within two business
|
||||||
|
days.
|
||||||
|
3. Information will be collected and an investigation will be performed to
|
||||||
|
determine if a security issue exists.
|
||||||
|
4. If no security issue is found, the process will end.
|
||||||
|
5. A fix for the issue and announcement will be drafted.
|
||||||
|
6. A release schedule and accouncement will be negotiated between the
|
||||||
|
reporter and the project
|
||||||
|
7. The security contacts for Arch Linux, Conda, Debian, Fedora, FreeBSD,
|
||||||
|
Ubuntu, and Tidelift will be contacted to notify them of an upcoming
|
||||||
|
security release.
|
||||||
|
8. Fixes for all vulnerabilities will be performed, and new releases made,
|
||||||
|
but without mention of a security issue. These changes and releases will
|
||||||
|
be published before the announcement.
|
||||||
|
9. An announcement will be made disclosing the vulnerability and the fix.
|
@ -0,0 +1,47 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals, division, absolute_import, print_function
|
||||||
|
|
||||||
|
from .version import __version__, __version_info__
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'__version__',
|
||||||
|
'__version_info__',
|
||||||
|
'load_order',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def load_order():
|
||||||
|
"""
|
||||||
|
Returns a list of the module and sub-module names for asn1crypto in
|
||||||
|
dependency load order, for the sake of live reloading code
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A list of unicode strings of module names, as they would appear in
|
||||||
|
sys.modules, ordered by which module should be reloaded first
|
||||||
|
"""
|
||||||
|
|
||||||
|
return [
|
||||||
|
'asn1crypto._errors',
|
||||||
|
'asn1crypto._int',
|
||||||
|
'asn1crypto._ordereddict',
|
||||||
|
'asn1crypto._teletex_codec',
|
||||||
|
'asn1crypto._types',
|
||||||
|
'asn1crypto._inet',
|
||||||
|
'asn1crypto._iri',
|
||||||
|
'asn1crypto.version',
|
||||||
|
'asn1crypto.pem',
|
||||||
|
'asn1crypto.util',
|
||||||
|
'asn1crypto.parser',
|
||||||
|
'asn1crypto.core',
|
||||||
|
'asn1crypto.algos',
|
||||||
|
'asn1crypto.keys',
|
||||||
|
'asn1crypto.x509',
|
||||||
|
'asn1crypto.crl',
|
||||||
|
'asn1crypto.csr',
|
||||||
|
'asn1crypto.ocsp',
|
||||||
|
'asn1crypto.cms',
|
||||||
|
'asn1crypto.pdf',
|
||||||
|
'asn1crypto.pkcs12',
|
||||||
|
'asn1crypto.tsp',
|
||||||
|
'asn1crypto',
|
||||||
|
]
|
@ -0,0 +1,54 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
"""
|
||||||
|
Exports the following items:
|
||||||
|
|
||||||
|
- unwrap()
|
||||||
|
- APIException()
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import unicode_literals, division, absolute_import, print_function
|
||||||
|
|
||||||
|
import re
|
||||||
|
import textwrap
|
||||||
|
|
||||||
|
|
||||||
|
class APIException(Exception):
|
||||||
|
"""
|
||||||
|
An exception indicating an API has been removed from asn1crypto
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def unwrap(string, *params):
|
||||||
|
"""
|
||||||
|
Takes a multi-line string and does the following:
|
||||||
|
|
||||||
|
- dedents
|
||||||
|
- converts newlines with text before and after into a single line
|
||||||
|
- strips leading and trailing whitespace
|
||||||
|
|
||||||
|
:param string:
|
||||||
|
The string to format
|
||||||
|
|
||||||
|
:param *params:
|
||||||
|
Params to interpolate into the string
|
||||||
|
|
||||||
|
:return:
|
||||||
|
The formatted string
|
||||||
|
"""
|
||||||
|
|
||||||
|
output = textwrap.dedent(string)
|
||||||
|
|
||||||
|
# Unwrap lines, taking into account bulleted lists, ordered lists and
|
||||||
|
# underlines consisting of = signs
|
||||||
|
if output.find('\n') != -1:
|
||||||
|
output = re.sub('(?<=\\S)\n(?=[^ \n\t\\d\\*\\-=])', ' ', output)
|
||||||
|
|
||||||
|
if params:
|
||||||
|
output = output % params
|
||||||
|
|
||||||
|
output = output.strip()
|
||||||
|
|
||||||
|
return output
|
@ -0,0 +1,170 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals, division, absolute_import, print_function
|
||||||
|
|
||||||
|
import socket
|
||||||
|
import struct
|
||||||
|
|
||||||
|
from ._errors import unwrap
|
||||||
|
from ._types import byte_cls, bytes_to_list, str_cls, type_name
|
||||||
|
|
||||||
|
|
||||||
|
def inet_ntop(address_family, packed_ip):
|
||||||
|
"""
|
||||||
|
Windows compatibility shim for socket.inet_ntop().
|
||||||
|
|
||||||
|
:param address_family:
|
||||||
|
socket.AF_INET for IPv4 or socket.AF_INET6 for IPv6
|
||||||
|
|
||||||
|
:param packed_ip:
|
||||||
|
A byte string of the network form of an IP address
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A unicode string of the IP address
|
||||||
|
"""
|
||||||
|
|
||||||
|
if address_family not in set([socket.AF_INET, socket.AF_INET6]):
|
||||||
|
raise ValueError(unwrap(
|
||||||
|
'''
|
||||||
|
address_family must be socket.AF_INET (%s) or socket.AF_INET6 (%s),
|
||||||
|
not %s
|
||||||
|
''',
|
||||||
|
repr(socket.AF_INET),
|
||||||
|
repr(socket.AF_INET6),
|
||||||
|
repr(address_family)
|
||||||
|
))
|
||||||
|
|
||||||
|
if not isinstance(packed_ip, byte_cls):
|
||||||
|
raise TypeError(unwrap(
|
||||||
|
'''
|
||||||
|
packed_ip must be a byte string, not %s
|
||||||
|
''',
|
||||||
|
type_name(packed_ip)
|
||||||
|
))
|
||||||
|
|
||||||
|
required_len = 4 if address_family == socket.AF_INET else 16
|
||||||
|
if len(packed_ip) != required_len:
|
||||||
|
raise ValueError(unwrap(
|
||||||
|
'''
|
||||||
|
packed_ip must be %d bytes long - is %d
|
||||||
|
''',
|
||||||
|
required_len,
|
||||||
|
len(packed_ip)
|
||||||
|
))
|
||||||
|
|
||||||
|
if address_family == socket.AF_INET:
|
||||||
|
return '%d.%d.%d.%d' % tuple(bytes_to_list(packed_ip))
|
||||||
|
|
||||||
|
octets = struct.unpack(b'!HHHHHHHH', packed_ip)
|
||||||
|
|
||||||
|
runs_of_zero = {}
|
||||||
|
longest_run = 0
|
||||||
|
zero_index = None
|
||||||
|
for i, octet in enumerate(octets + (-1,)):
|
||||||
|
if octet != 0:
|
||||||
|
if zero_index is not None:
|
||||||
|
length = i - zero_index
|
||||||
|
if length not in runs_of_zero:
|
||||||
|
runs_of_zero[length] = zero_index
|
||||||
|
longest_run = max(longest_run, length)
|
||||||
|
zero_index = None
|
||||||
|
elif zero_index is None:
|
||||||
|
zero_index = i
|
||||||
|
|
||||||
|
hexed = [hex(o)[2:] for o in octets]
|
||||||
|
|
||||||
|
if longest_run < 2:
|
||||||
|
return ':'.join(hexed)
|
||||||
|
|
||||||
|
zero_start = runs_of_zero[longest_run]
|
||||||
|
zero_end = zero_start + longest_run
|
||||||
|
|
||||||
|
return ':'.join(hexed[:zero_start]) + '::' + ':'.join(hexed[zero_end:])
|
||||||
|
|
||||||
|
|
||||||
|
def inet_pton(address_family, ip_string):
|
||||||
|
"""
|
||||||
|
Windows compatibility shim for socket.inet_ntop().
|
||||||
|
|
||||||
|
:param address_family:
|
||||||
|
socket.AF_INET for IPv4 or socket.AF_INET6 for IPv6
|
||||||
|
|
||||||
|
:param ip_string:
|
||||||
|
A unicode string of an IP address
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A byte string of the network form of the IP address
|
||||||
|
"""
|
||||||
|
|
||||||
|
if address_family not in set([socket.AF_INET, socket.AF_INET6]):
|
||||||
|
raise ValueError(unwrap(
|
||||||
|
'''
|
||||||
|
address_family must be socket.AF_INET (%s) or socket.AF_INET6 (%s),
|
||||||
|
not %s
|
||||||
|
''',
|
||||||
|
repr(socket.AF_INET),
|
||||||
|
repr(socket.AF_INET6),
|
||||||
|
repr(address_family)
|
||||||
|
))
|
||||||
|
|
||||||
|
if not isinstance(ip_string, str_cls):
|
||||||
|
raise TypeError(unwrap(
|
||||||
|
'''
|
||||||
|
ip_string must be a unicode string, not %s
|
||||||
|
''',
|
||||||
|
type_name(ip_string)
|
||||||
|
))
|
||||||
|
|
||||||
|
if address_family == socket.AF_INET:
|
||||||
|
octets = ip_string.split('.')
|
||||||
|
error = len(octets) != 4
|
||||||
|
if not error:
|
||||||
|
ints = []
|
||||||
|
for o in octets:
|
||||||
|
o = int(o)
|
||||||
|
if o > 255 or o < 0:
|
||||||
|
error = True
|
||||||
|
break
|
||||||
|
ints.append(o)
|
||||||
|
|
||||||
|
if error:
|
||||||
|
raise ValueError(unwrap(
|
||||||
|
'''
|
||||||
|
ip_string must be a dotted string with four integers in the
|
||||||
|
range of 0 to 255, got %s
|
||||||
|
''',
|
||||||
|
repr(ip_string)
|
||||||
|
))
|
||||||
|
|
||||||
|
return struct.pack(b'!BBBB', *ints)
|
||||||
|
|
||||||
|
error = False
|
||||||
|
omitted = ip_string.count('::')
|
||||||
|
if omitted > 1:
|
||||||
|
error = True
|
||||||
|
elif omitted == 0:
|
||||||
|
octets = ip_string.split(':')
|
||||||
|
error = len(octets) != 8
|
||||||
|
else:
|
||||||
|
begin, end = ip_string.split('::')
|
||||||
|
begin_octets = begin.split(':')
|
||||||
|
end_octets = end.split(':')
|
||||||
|
missing = 8 - len(begin_octets) - len(end_octets)
|
||||||
|
octets = begin_octets + (['0'] * missing) + end_octets
|
||||||
|
|
||||||
|
if not error:
|
||||||
|
ints = []
|
||||||
|
for o in octets:
|
||||||
|
o = int(o, 16)
|
||||||
|
if o > 65535 or o < 0:
|
||||||
|
error = True
|
||||||
|
break
|
||||||
|
ints.append(o)
|
||||||
|
|
||||||
|
return struct.pack(b'!HHHHHHHH', *ints)
|
||||||
|
|
||||||
|
raise ValueError(unwrap(
|
||||||
|
'''
|
||||||
|
ip_string must be a valid ipv6 string, got %s
|
||||||
|
''',
|
||||||
|
repr(ip_string)
|
||||||
|
))
|
@ -0,0 +1,22 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals, division, absolute_import, print_function
|
||||||
|
|
||||||
|
|
||||||
|
def fill_width(bytes_, width):
|
||||||
|
"""
|
||||||
|
Ensure a byte string representing a positive integer is a specific width
|
||||||
|
(in bytes)
|
||||||
|
|
||||||
|
:param bytes_:
|
||||||
|
The integer byte string
|
||||||
|
|
||||||
|
:param width:
|
||||||
|
The desired width as an integer
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A byte string of the width specified
|
||||||
|
"""
|
||||||
|
|
||||||
|
while len(bytes_) < width:
|
||||||
|
bytes_ = b'\x00' + bytes_
|
||||||
|
return bytes_
|
@ -0,0 +1,291 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
"""
|
||||||
|
Functions to convert unicode IRIs into ASCII byte string URIs and back. Exports
|
||||||
|
the following items:
|
||||||
|
|
||||||
|
- iri_to_uri()
|
||||||
|
- uri_to_iri()
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import unicode_literals, division, absolute_import, print_function
|
||||||
|
|
||||||
|
from encodings import idna # noqa
|
||||||
|
import codecs
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from ._errors import unwrap
|
||||||
|
from ._types import byte_cls, str_cls, type_name, bytes_to_list, int_types
|
||||||
|
|
||||||
|
if sys.version_info < (3,):
|
||||||
|
from urlparse import urlsplit, urlunsplit
|
||||||
|
from urllib import (
|
||||||
|
quote as urlquote,
|
||||||
|
unquote as unquote_to_bytes,
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
from urllib.parse import (
|
||||||
|
quote as urlquote,
|
||||||
|
unquote_to_bytes,
|
||||||
|
urlsplit,
|
||||||
|
urlunsplit,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def iri_to_uri(value, normalize=False):
|
||||||
|
"""
|
||||||
|
Encodes a unicode IRI into an ASCII byte string URI
|
||||||
|
|
||||||
|
:param value:
|
||||||
|
A unicode string of an IRI
|
||||||
|
|
||||||
|
:param normalize:
|
||||||
|
A bool that controls URI normalization
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A byte string of the ASCII-encoded URI
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not isinstance(value, str_cls):
|
||||||
|
raise TypeError(unwrap(
|
||||||
|
'''
|
||||||
|
value must be a unicode string, not %s
|
||||||
|
''',
|
||||||
|
type_name(value)
|
||||||
|
))
|
||||||
|
|
||||||
|
scheme = None
|
||||||
|
# Python 2.6 doesn't split properly is the URL doesn't start with http:// or https://
|
||||||
|
if sys.version_info < (2, 7) and not value.startswith('http://') and not value.startswith('https://'):
|
||||||
|
real_prefix = None
|
||||||
|
prefix_match = re.match('^[^:]*://', value)
|
||||||
|
if prefix_match:
|
||||||
|
real_prefix = prefix_match.group(0)
|
||||||
|
value = 'http://' + value[len(real_prefix):]
|
||||||
|
parsed = urlsplit(value)
|
||||||
|
if real_prefix:
|
||||||
|
value = real_prefix + value[7:]
|
||||||
|
scheme = _urlquote(real_prefix[:-3])
|
||||||
|
else:
|
||||||
|
parsed = urlsplit(value)
|
||||||
|
|
||||||
|
if scheme is None:
|
||||||
|
scheme = _urlquote(parsed.scheme)
|
||||||
|
hostname = parsed.hostname
|
||||||
|
if hostname is not None:
|
||||||
|
hostname = hostname.encode('idna')
|
||||||
|
# RFC 3986 allows userinfo to contain sub-delims
|
||||||
|
username = _urlquote(parsed.username, safe='!$&\'()*+,;=')
|
||||||
|
password = _urlquote(parsed.password, safe='!$&\'()*+,;=')
|
||||||
|
port = parsed.port
|
||||||
|
if port is not None:
|
||||||
|
port = str_cls(port).encode('ascii')
|
||||||
|
|
||||||
|
netloc = b''
|
||||||
|
if username is not None:
|
||||||
|
netloc += username
|
||||||
|
if password:
|
||||||
|
netloc += b':' + password
|
||||||
|
netloc += b'@'
|
||||||
|
if hostname is not None:
|
||||||
|
netloc += hostname
|
||||||
|
if port is not None:
|
||||||
|
default_http = scheme == b'http' and port == b'80'
|
||||||
|
default_https = scheme == b'https' and port == b'443'
|
||||||
|
if not normalize or (not default_http and not default_https):
|
||||||
|
netloc += b':' + port
|
||||||
|
|
||||||
|
# RFC 3986 allows a path to contain sub-delims, plus "@" and ":"
|
||||||
|
path = _urlquote(parsed.path, safe='/!$&\'()*+,;=@:')
|
||||||
|
# RFC 3986 allows the query to contain sub-delims, plus "@", ":" , "/" and "?"
|
||||||
|
query = _urlquote(parsed.query, safe='/?!$&\'()*+,;=@:')
|
||||||
|
# RFC 3986 allows the fragment to contain sub-delims, plus "@", ":" , "/" and "?"
|
||||||
|
fragment = _urlquote(parsed.fragment, safe='/?!$&\'()*+,;=@:')
|
||||||
|
|
||||||
|
if normalize and query is None and fragment is None and path == b'/':
|
||||||
|
path = None
|
||||||
|
|
||||||
|
# Python 2.7 compat
|
||||||
|
if path is None:
|
||||||
|
path = ''
|
||||||
|
|
||||||
|
output = urlunsplit((scheme, netloc, path, query, fragment))
|
||||||
|
if isinstance(output, str_cls):
|
||||||
|
output = output.encode('latin1')
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def uri_to_iri(value):
|
||||||
|
"""
|
||||||
|
Converts an ASCII URI byte string into a unicode IRI
|
||||||
|
|
||||||
|
:param value:
|
||||||
|
An ASCII-encoded byte string of the URI
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A unicode string of the IRI
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not isinstance(value, byte_cls):
|
||||||
|
raise TypeError(unwrap(
|
||||||
|
'''
|
||||||
|
value must be a byte string, not %s
|
||||||
|
''',
|
||||||
|
type_name(value)
|
||||||
|
))
|
||||||
|
|
||||||
|
parsed = urlsplit(value)
|
||||||
|
|
||||||
|
scheme = parsed.scheme
|
||||||
|
if scheme is not None:
|
||||||
|
scheme = scheme.decode('ascii')
|
||||||
|
|
||||||
|
username = _urlunquote(parsed.username, remap=[':', '@'])
|
||||||
|
password = _urlunquote(parsed.password, remap=[':', '@'])
|
||||||
|
hostname = parsed.hostname
|
||||||
|
if hostname:
|
||||||
|
hostname = hostname.decode('idna')
|
||||||
|
port = parsed.port
|
||||||
|
if port and not isinstance(port, int_types):
|
||||||
|
port = port.decode('ascii')
|
||||||
|
|
||||||
|
netloc = ''
|
||||||
|
if username is not None:
|
||||||
|
netloc += username
|
||||||
|
if password:
|
||||||
|
netloc += ':' + password
|
||||||
|
netloc += '@'
|
||||||
|
if hostname is not None:
|
||||||
|
netloc += hostname
|
||||||
|
if port is not None:
|
||||||
|
netloc += ':' + str_cls(port)
|
||||||
|
|
||||||
|
path = _urlunquote(parsed.path, remap=['/'], preserve=True)
|
||||||
|
query = _urlunquote(parsed.query, remap=['&', '='], preserve=True)
|
||||||
|
fragment = _urlunquote(parsed.fragment)
|
||||||
|
|
||||||
|
return urlunsplit((scheme, netloc, path, query, fragment))
|
||||||
|
|
||||||
|
|
||||||
|
def _iri_utf8_errors_handler(exc):
|
||||||
|
"""
|
||||||
|
Error handler for decoding UTF-8 parts of a URI into an IRI. Leaves byte
|
||||||
|
sequences encoded in %XX format, but as part of a unicode string.
|
||||||
|
|
||||||
|
:param exc:
|
||||||
|
The UnicodeDecodeError exception
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A 2-element tuple of (replacement unicode string, integer index to
|
||||||
|
resume at)
|
||||||
|
"""
|
||||||
|
|
||||||
|
bytes_as_ints = bytes_to_list(exc.object[exc.start:exc.end])
|
||||||
|
replacements = ['%%%02x' % num for num in bytes_as_ints]
|
||||||
|
return (''.join(replacements), exc.end)
|
||||||
|
|
||||||
|
|
||||||
|
codecs.register_error('iriutf8', _iri_utf8_errors_handler)
|
||||||
|
|
||||||
|
|
||||||
|
def _urlquote(string, safe=''):
|
||||||
|
"""
|
||||||
|
Quotes a unicode string for use in a URL
|
||||||
|
|
||||||
|
:param string:
|
||||||
|
A unicode string
|
||||||
|
|
||||||
|
:param safe:
|
||||||
|
A unicode string of character to not encode
|
||||||
|
|
||||||
|
:return:
|
||||||
|
None (if string is None) or an ASCII byte string of the quoted string
|
||||||
|
"""
|
||||||
|
|
||||||
|
if string is None or string == '':
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Anything already hex quoted is pulled out of the URL and unquoted if
|
||||||
|
# possible
|
||||||
|
escapes = []
|
||||||
|
if re.search('%[0-9a-fA-F]{2}', string):
|
||||||
|
# Try to unquote any percent values, restoring them if they are not
|
||||||
|
# valid UTF-8. Also, requote any safe chars since encoded versions of
|
||||||
|
# those are functionally different than the unquoted ones.
|
||||||
|
def _try_unescape(match):
|
||||||
|
byte_string = unquote_to_bytes(match.group(0))
|
||||||
|
unicode_string = byte_string.decode('utf-8', 'iriutf8')
|
||||||
|
for safe_char in list(safe):
|
||||||
|
unicode_string = unicode_string.replace(safe_char, '%%%02x' % ord(safe_char))
|
||||||
|
return unicode_string
|
||||||
|
string = re.sub('(?:%[0-9a-fA-F]{2})+', _try_unescape, string)
|
||||||
|
|
||||||
|
# Once we have the minimal set of hex quoted values, removed them from
|
||||||
|
# the string so that they are not double quoted
|
||||||
|
def _extract_escape(match):
|
||||||
|
escapes.append(match.group(0).encode('ascii'))
|
||||||
|
return '\x00'
|
||||||
|
string = re.sub('%[0-9a-fA-F]{2}', _extract_escape, string)
|
||||||
|
|
||||||
|
output = urlquote(string.encode('utf-8'), safe=safe.encode('utf-8'))
|
||||||
|
if not isinstance(output, byte_cls):
|
||||||
|
output = output.encode('ascii')
|
||||||
|
|
||||||
|
# Restore the existing quoted values that we extracted
|
||||||
|
if len(escapes) > 0:
|
||||||
|
def _return_escape(_):
|
||||||
|
return escapes.pop(0)
|
||||||
|
output = re.sub(b'%00', _return_escape, output)
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def _urlunquote(byte_string, remap=None, preserve=None):
|
||||||
|
"""
|
||||||
|
Unquotes a URI portion from a byte string into unicode using UTF-8
|
||||||
|
|
||||||
|
:param byte_string:
|
||||||
|
A byte string of the data to unquote
|
||||||
|
|
||||||
|
:param remap:
|
||||||
|
A list of characters (as unicode) that should be re-mapped to a
|
||||||
|
%XX encoding. This is used when characters are not valid in part of a
|
||||||
|
URL.
|
||||||
|
|
||||||
|
:param preserve:
|
||||||
|
A bool - indicates that the chars to be remapped if they occur in
|
||||||
|
non-hex form, should be preserved. E.g. / for URL path.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A unicode string
|
||||||
|
"""
|
||||||
|
|
||||||
|
if byte_string is None:
|
||||||
|
return byte_string
|
||||||
|
|
||||||
|
if byte_string == b'':
|
||||||
|
return ''
|
||||||
|
|
||||||
|
if preserve:
|
||||||
|
replacements = ['\x1A', '\x1C', '\x1D', '\x1E', '\x1F']
|
||||||
|
preserve_unmap = {}
|
||||||
|
for char in remap:
|
||||||
|
replacement = replacements.pop(0)
|
||||||
|
preserve_unmap[replacement] = char
|
||||||
|
byte_string = byte_string.replace(char.encode('ascii'), replacement.encode('ascii'))
|
||||||
|
|
||||||
|
byte_string = unquote_to_bytes(byte_string)
|
||||||
|
|
||||||
|
if remap:
|
||||||
|
for char in remap:
|
||||||
|
byte_string = byte_string.replace(char.encode('ascii'), ('%%%02x' % ord(char)).encode('ascii'))
|
||||||
|
|
||||||
|
output = byte_string.decode('utf-8', 'iriutf8')
|
||||||
|
|
||||||
|
if preserve:
|
||||||
|
for replacement, original in preserve_unmap.items():
|
||||||
|
output = output.replace(replacement, original)
|
||||||
|
|
||||||
|
return output
|
@ -0,0 +1,135 @@
|
|||||||
|
# Copyright (c) 2009 Raymond Hettinger
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person
|
||||||
|
# obtaining a copy of this software and associated documentation files
|
||||||
|
# (the "Software"), to deal in the Software without restriction,
|
||||||
|
# including without limitation the rights to use, copy, modify, merge,
|
||||||
|
# publish, distribute, sublicense, and/or sell copies of the Software,
|
||||||
|
# and to permit persons to whom the Software is furnished to do so,
|
||||||
|
# subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be
|
||||||
|
# included in all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
# OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
if not sys.version_info < (2, 7):
|
||||||
|
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
from UserDict import DictMixin
|
||||||
|
|
||||||
|
class OrderedDict(dict, DictMixin):
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwds):
|
||||||
|
if len(args) > 1:
|
||||||
|
raise TypeError('expected at most 1 arguments, got %d' % len(args))
|
||||||
|
try:
|
||||||
|
self.__end
|
||||||
|
except AttributeError:
|
||||||
|
self.clear()
|
||||||
|
self.update(*args, **kwds)
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
self.__end = end = []
|
||||||
|
end += [None, end, end] # sentinel node for doubly linked list
|
||||||
|
self.__map = {} # key --> [key, prev, next]
|
||||||
|
dict.clear(self)
|
||||||
|
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
if key not in self:
|
||||||
|
end = self.__end
|
||||||
|
curr = end[1]
|
||||||
|
curr[2] = end[1] = self.__map[key] = [key, curr, end]
|
||||||
|
dict.__setitem__(self, key, value)
|
||||||
|
|
||||||
|
def __delitem__(self, key):
|
||||||
|
dict.__delitem__(self, key)
|
||||||
|
key, prev, next_ = self.__map.pop(key)
|
||||||
|
prev[2] = next_
|
||||||
|
next_[1] = prev
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
end = self.__end
|
||||||
|
curr = end[2]
|
||||||
|
while curr is not end:
|
||||||
|
yield curr[0]
|
||||||
|
curr = curr[2]
|
||||||
|
|
||||||
|
def __reversed__(self):
|
||||||
|
end = self.__end
|
||||||
|
curr = end[1]
|
||||||
|
while curr is not end:
|
||||||
|
yield curr[0]
|
||||||
|
curr = curr[1]
|
||||||
|
|
||||||
|
def popitem(self, last=True):
|
||||||
|
if not self:
|
||||||
|
raise KeyError('dictionary is empty')
|
||||||
|
if last:
|
||||||
|
key = reversed(self).next()
|
||||||
|
else:
|
||||||
|
key = iter(self).next()
|
||||||
|
value = self.pop(key)
|
||||||
|
return key, value
|
||||||
|
|
||||||
|
def __reduce__(self):
|
||||||
|
items = [[k, self[k]] for k in self]
|
||||||
|
tmp = self.__map, self.__end
|
||||||
|
del self.__map, self.__end
|
||||||
|
inst_dict = vars(self).copy()
|
||||||
|
self.__map, self.__end = tmp
|
||||||
|
if inst_dict:
|
||||||
|
return (self.__class__, (items,), inst_dict)
|
||||||
|
return self.__class__, (items,)
|
||||||
|
|
||||||
|
def keys(self):
|
||||||
|
return list(self)
|
||||||
|
|
||||||
|
setdefault = DictMixin.setdefault
|
||||||
|
update = DictMixin.update
|
||||||
|
pop = DictMixin.pop
|
||||||
|
values = DictMixin.values
|
||||||
|
items = DictMixin.items
|
||||||
|
iterkeys = DictMixin.iterkeys
|
||||||
|
itervalues = DictMixin.itervalues
|
||||||
|
iteritems = DictMixin.iteritems
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
if not self:
|
||||||
|
return '%s()' % (self.__class__.__name__,)
|
||||||
|
return '%s(%r)' % (self.__class__.__name__, self.items())
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
return self.__class__(self)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def fromkeys(cls, iterable, value=None):
|
||||||
|
d = cls()
|
||||||
|
for key in iterable:
|
||||||
|
d[key] = value
|
||||||
|
return d
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if isinstance(other, OrderedDict):
|
||||||
|
if len(self) != len(other):
|
||||||
|
return False
|
||||||
|
for p, q in zip(self.items(), other.items()):
|
||||||
|
if p != q:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
return dict.__eq__(self, other)
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return not self == other
|
@ -0,0 +1,331 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
"""
|
||||||
|
Implementation of the teletex T.61 codec. Exports the following items:
|
||||||
|
|
||||||
|
- register()
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import unicode_literals, division, absolute_import, print_function
|
||||||
|
|
||||||
|
import codecs
|
||||||
|
|
||||||
|
|
||||||
|
class TeletexCodec(codecs.Codec):
|
||||||
|
|
||||||
|
def encode(self, input_, errors='strict'):
|
||||||
|
return codecs.charmap_encode(input_, errors, ENCODING_TABLE)
|
||||||
|
|
||||||
|
def decode(self, input_, errors='strict'):
|
||||||
|
return codecs.charmap_decode(input_, errors, DECODING_TABLE)
|
||||||
|
|
||||||
|
|
||||||
|
class TeletexIncrementalEncoder(codecs.IncrementalEncoder):
|
||||||
|
|
||||||
|
def encode(self, input_, final=False):
|
||||||
|
return codecs.charmap_encode(input_, self.errors, ENCODING_TABLE)[0]
|
||||||
|
|
||||||
|
|
||||||
|
class TeletexIncrementalDecoder(codecs.IncrementalDecoder):
|
||||||
|
|
||||||
|
def decode(self, input_, final=False):
|
||||||
|
return codecs.charmap_decode(input_, self.errors, DECODING_TABLE)[0]
|
||||||
|
|
||||||
|
|
||||||
|
class TeletexStreamWriter(TeletexCodec, codecs.StreamWriter):
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TeletexStreamReader(TeletexCodec, codecs.StreamReader):
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def teletex_search_function(name):
|
||||||
|
"""
|
||||||
|
Search function for teletex codec that is passed to codecs.register()
|
||||||
|
"""
|
||||||
|
|
||||||
|
if name != 'teletex':
|
||||||
|
return None
|
||||||
|
|
||||||
|
return codecs.CodecInfo(
|
||||||
|
name='teletex',
|
||||||
|
encode=TeletexCodec().encode,
|
||||||
|
decode=TeletexCodec().decode,
|
||||||
|
incrementalencoder=TeletexIncrementalEncoder,
|
||||||
|
incrementaldecoder=TeletexIncrementalDecoder,
|
||||||
|
streamreader=TeletexStreamReader,
|
||||||
|
streamwriter=TeletexStreamWriter,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
"""
|
||||||
|
Registers the teletex codec
|
||||||
|
"""
|
||||||
|
|
||||||
|
codecs.register(teletex_search_function)
|
||||||
|
|
||||||
|
|
||||||
|
# http://en.wikipedia.org/wiki/ITU_T.61
|
||||||
|
DECODING_TABLE = (
|
||||||
|
'\u0000'
|
||||||
|
'\u0001'
|
||||||
|
'\u0002'
|
||||||
|
'\u0003'
|
||||||
|
'\u0004'
|
||||||
|
'\u0005'
|
||||||
|
'\u0006'
|
||||||
|
'\u0007'
|
||||||
|
'\u0008'
|
||||||
|
'\u0009'
|
||||||
|
'\u000A'
|
||||||
|
'\u000B'
|
||||||
|
'\u000C'
|
||||||
|
'\u000D'
|
||||||
|
'\u000E'
|
||||||
|
'\u000F'
|
||||||
|
'\u0010'
|
||||||
|
'\u0011'
|
||||||
|
'\u0012'
|
||||||
|
'\u0013'
|
||||||
|
'\u0014'
|
||||||
|
'\u0015'
|
||||||
|
'\u0016'
|
||||||
|
'\u0017'
|
||||||
|
'\u0018'
|
||||||
|
'\u0019'
|
||||||
|
'\u001A'
|
||||||
|
'\u001B'
|
||||||
|
'\u001C'
|
||||||
|
'\u001D'
|
||||||
|
'\u001E'
|
||||||
|
'\u001F'
|
||||||
|
'\u0020'
|
||||||
|
'\u0021'
|
||||||
|
'\u0022'
|
||||||
|
'\ufffe'
|
||||||
|
'\ufffe'
|
||||||
|
'\u0025'
|
||||||
|
'\u0026'
|
||||||
|
'\u0027'
|
||||||
|
'\u0028'
|
||||||
|
'\u0029'
|
||||||
|
'\u002A'
|
||||||
|
'\u002B'
|
||||||
|
'\u002C'
|
||||||
|
'\u002D'
|
||||||
|
'\u002E'
|
||||||
|
'\u002F'
|
||||||
|
'\u0030'
|
||||||
|
'\u0031'
|
||||||
|
'\u0032'
|
||||||
|
'\u0033'
|
||||||
|
'\u0034'
|
||||||
|
'\u0035'
|
||||||
|
'\u0036'
|
||||||
|
'\u0037'
|
||||||
|
'\u0038'
|
||||||
|
'\u0039'
|
||||||
|
'\u003A'
|
||||||
|
'\u003B'
|
||||||
|
'\u003C'
|
||||||
|
'\u003D'
|
||||||
|
'\u003E'
|
||||||
|
'\u003F'
|
||||||
|
'\u0040'
|
||||||
|
'\u0041'
|
||||||
|
'\u0042'
|
||||||
|
'\u0043'
|
||||||
|
'\u0044'
|
||||||
|
'\u0045'
|
||||||
|
'\u0046'
|
||||||
|
'\u0047'
|
||||||
|
'\u0048'
|
||||||
|
'\u0049'
|
||||||
|
'\u004A'
|
||||||
|
'\u004B'
|
||||||
|
'\u004C'
|
||||||
|
'\u004D'
|
||||||
|
'\u004E'
|
||||||
|
'\u004F'
|
||||||
|
'\u0050'
|
||||||
|
'\u0051'
|
||||||
|
'\u0052'
|
||||||
|
'\u0053'
|
||||||
|
'\u0054'
|
||||||
|
'\u0055'
|
||||||
|
'\u0056'
|
||||||
|
'\u0057'
|
||||||
|
'\u0058'
|
||||||
|
'\u0059'
|
||||||
|
'\u005A'
|
||||||
|
'\u005B'
|
||||||
|
'\ufffe'
|
||||||
|
'\u005D'
|
||||||
|
'\ufffe'
|
||||||
|
'\u005F'
|
||||||
|
'\ufffe'
|
||||||
|
'\u0061'
|
||||||
|
'\u0062'
|
||||||
|
'\u0063'
|
||||||
|
'\u0064'
|
||||||
|
'\u0065'
|
||||||
|
'\u0066'
|
||||||
|
'\u0067'
|
||||||
|
'\u0068'
|
||||||
|
'\u0069'
|
||||||
|
'\u006A'
|
||||||
|
'\u006B'
|
||||||
|
'\u006C'
|
||||||
|
'\u006D'
|
||||||
|
'\u006E'
|
||||||
|
'\u006F'
|
||||||
|
'\u0070'
|
||||||
|
'\u0071'
|
||||||
|
'\u0072'
|
||||||
|
'\u0073'
|
||||||
|
'\u0074'
|
||||||
|
'\u0075'
|
||||||
|
'\u0076'
|
||||||
|
'\u0077'
|
||||||
|
'\u0078'
|
||||||
|
'\u0079'
|
||||||
|
'\u007A'
|
||||||
|
'\ufffe'
|
||||||
|
'\u007C'
|
||||||
|
'\ufffe'
|
||||||
|
'\ufffe'
|
||||||
|
'\u007F'
|
||||||
|
'\u0080'
|
||||||
|
'\u0081'
|
||||||
|
'\u0082'
|
||||||
|
'\u0083'
|
||||||
|
'\u0084'
|
||||||
|
'\u0085'
|
||||||
|
'\u0086'
|
||||||
|
'\u0087'
|
||||||
|
'\u0088'
|
||||||
|
'\u0089'
|
||||||
|
'\u008A'
|
||||||
|
'\u008B'
|
||||||
|
'\u008C'
|
||||||
|
'\u008D'
|
||||||
|
'\u008E'
|
||||||
|
'\u008F'
|
||||||
|
'\u0090'
|
||||||
|
'\u0091'
|
||||||
|
'\u0092'
|
||||||
|
'\u0093'
|
||||||
|
'\u0094'
|
||||||
|
'\u0095'
|
||||||
|
'\u0096'
|
||||||
|
'\u0097'
|
||||||
|
'\u0098'
|
||||||
|
'\u0099'
|
||||||
|
'\u009A'
|
||||||
|
'\u009B'
|
||||||
|
'\u009C'
|
||||||
|
'\u009D'
|
||||||
|
'\u009E'
|
||||||
|
'\u009F'
|
||||||
|
'\u00A0'
|
||||||
|
'\u00A1'
|
||||||
|
'\u00A2'
|
||||||
|
'\u00A3'
|
||||||
|
'\u0024'
|
||||||
|
'\u00A5'
|
||||||
|
'\u0023'
|
||||||
|
'\u00A7'
|
||||||
|
'\u00A4'
|
||||||
|
'\ufffe'
|
||||||
|
'\ufffe'
|
||||||
|
'\u00AB'
|
||||||
|
'\ufffe'
|
||||||
|
'\ufffe'
|
||||||
|
'\ufffe'
|
||||||
|
'\ufffe'
|
||||||
|
'\u00B0'
|
||||||
|
'\u00B1'
|
||||||
|
'\u00B2'
|
||||||
|
'\u00B3'
|
||||||
|
'\u00D7'
|
||||||
|
'\u00B5'
|
||||||
|
'\u00B6'
|
||||||
|
'\u00B7'
|
||||||
|
'\u00F7'
|
||||||
|
'\ufffe'
|
||||||
|
'\ufffe'
|
||||||
|
'\u00BB'
|
||||||
|
'\u00BC'
|
||||||
|
'\u00BD'
|
||||||
|
'\u00BE'
|
||||||
|
'\u00BF'
|
||||||
|
'\ufffe'
|
||||||
|
'\u0300'
|
||||||
|
'\u0301'
|
||||||
|
'\u0302'
|
||||||
|
'\u0303'
|
||||||
|
'\u0304'
|
||||||
|
'\u0306'
|
||||||
|
'\u0307'
|
||||||
|
'\u0308'
|
||||||
|
'\ufffe'
|
||||||
|
'\u030A'
|
||||||
|
'\u0327'
|
||||||
|
'\u0332'
|
||||||
|
'\u030B'
|
||||||
|
'\u0328'
|
||||||
|
'\u030C'
|
||||||
|
'\ufffe'
|
||||||
|
'\ufffe'
|
||||||
|
'\ufffe'
|
||||||
|
'\ufffe'
|
||||||
|
'\ufffe'
|
||||||
|
'\ufffe'
|
||||||
|
'\ufffe'
|
||||||
|
'\ufffe'
|
||||||
|
'\ufffe'
|
||||||
|
'\ufffe'
|
||||||
|
'\ufffe'
|
||||||
|
'\ufffe'
|
||||||
|
'\ufffe'
|
||||||
|
'\ufffe'
|
||||||
|
'\ufffe'
|
||||||
|
'\ufffe'
|
||||||
|
'\u2126'
|
||||||
|
'\u00C6'
|
||||||
|
'\u00D0'
|
||||||
|
'\u00AA'
|
||||||
|
'\u0126'
|
||||||
|
'\ufffe'
|
||||||
|
'\u0132'
|
||||||
|
'\u013F'
|
||||||
|
'\u0141'
|
||||||
|
'\u00D8'
|
||||||
|
'\u0152'
|
||||||
|
'\u00BA'
|
||||||
|
'\u00DE'
|
||||||
|
'\u0166'
|
||||||
|
'\u014A'
|
||||||
|
'\u0149'
|
||||||
|
'\u0138'
|
||||||
|
'\u00E6'
|
||||||
|
'\u0111'
|
||||||
|
'\u00F0'
|
||||||
|
'\u0127'
|
||||||
|
'\u0131'
|
||||||
|
'\u0133'
|
||||||
|
'\u0140'
|
||||||
|
'\u0142'
|
||||||
|
'\u00F8'
|
||||||
|
'\u0153'
|
||||||
|
'\u00DF'
|
||||||
|
'\u00FE'
|
||||||
|
'\u0167'
|
||||||
|
'\u014B'
|
||||||
|
'\ufffe'
|
||||||
|
)
|
||||||
|
ENCODING_TABLE = codecs.charmap_build(DECODING_TABLE)
|
@ -0,0 +1,46 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals, division, absolute_import, print_function
|
||||||
|
|
||||||
|
import inspect
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
if sys.version_info < (3,):
|
||||||
|
str_cls = unicode # noqa
|
||||||
|
byte_cls = str
|
||||||
|
int_types = (int, long) # noqa
|
||||||
|
|
||||||
|
def bytes_to_list(byte_string):
|
||||||
|
return [ord(b) for b in byte_string]
|
||||||
|
|
||||||
|
chr_cls = chr
|
||||||
|
|
||||||
|
else:
|
||||||
|
str_cls = str
|
||||||
|
byte_cls = bytes
|
||||||
|
int_types = int
|
||||||
|
|
||||||
|
bytes_to_list = list
|
||||||
|
|
||||||
|
def chr_cls(num):
|
||||||
|
return bytes([num])
|
||||||
|
|
||||||
|
|
||||||
|
def type_name(value):
|
||||||
|
"""
|
||||||
|
Returns a user-readable name for the type of an object
|
||||||
|
|
||||||
|
:param value:
|
||||||
|
A value to get the type name of
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A unicode string of the object's type name
|
||||||
|
"""
|
||||||
|
|
||||||
|
if inspect.isclass(value):
|
||||||
|
cls = value
|
||||||
|
else:
|
||||||
|
cls = value.__class__
|
||||||
|
if cls.__module__ in set(['builtins', '__builtin__']):
|
||||||
|
return cls.__name__
|
||||||
|
return '%s.%s' % (cls.__module__, cls.__name__)
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,536 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
"""
|
||||||
|
ASN.1 type classes for certificate revocation lists (CRL). Exports the
|
||||||
|
following items:
|
||||||
|
|
||||||
|
- CertificateList()
|
||||||
|
|
||||||
|
Other type classes are defined that help compose the types listed above.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import unicode_literals, division, absolute_import, print_function
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
from .algos import SignedDigestAlgorithm
|
||||||
|
from .core import (
|
||||||
|
Boolean,
|
||||||
|
Enumerated,
|
||||||
|
GeneralizedTime,
|
||||||
|
Integer,
|
||||||
|
ObjectIdentifier,
|
||||||
|
OctetBitString,
|
||||||
|
ParsableOctetString,
|
||||||
|
Sequence,
|
||||||
|
SequenceOf,
|
||||||
|
)
|
||||||
|
from .x509 import (
|
||||||
|
AuthorityInfoAccessSyntax,
|
||||||
|
AuthorityKeyIdentifier,
|
||||||
|
CRLDistributionPoints,
|
||||||
|
DistributionPointName,
|
||||||
|
GeneralNames,
|
||||||
|
Name,
|
||||||
|
ReasonFlags,
|
||||||
|
Time,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# The structures in this file are taken from https://tools.ietf.org/html/rfc5280
|
||||||
|
|
||||||
|
|
||||||
|
class Version(Integer):
|
||||||
|
_map = {
|
||||||
|
0: 'v1',
|
||||||
|
1: 'v2',
|
||||||
|
2: 'v3',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class IssuingDistributionPoint(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('distribution_point', DistributionPointName, {'explicit': 0, 'optional': True}),
|
||||||
|
('only_contains_user_certs', Boolean, {'implicit': 1, 'default': False}),
|
||||||
|
('only_contains_ca_certs', Boolean, {'implicit': 2, 'default': False}),
|
||||||
|
('only_some_reasons', ReasonFlags, {'implicit': 3, 'optional': True}),
|
||||||
|
('indirect_crl', Boolean, {'implicit': 4, 'default': False}),
|
||||||
|
('only_contains_attribute_certs', Boolean, {'implicit': 5, 'default': False}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class TBSCertListExtensionId(ObjectIdentifier):
|
||||||
|
_map = {
|
||||||
|
'2.5.29.18': 'issuer_alt_name',
|
||||||
|
'2.5.29.20': 'crl_number',
|
||||||
|
'2.5.29.27': 'delta_crl_indicator',
|
||||||
|
'2.5.29.28': 'issuing_distribution_point',
|
||||||
|
'2.5.29.35': 'authority_key_identifier',
|
||||||
|
'2.5.29.46': 'freshest_crl',
|
||||||
|
'1.3.6.1.5.5.7.1.1': 'authority_information_access',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TBSCertListExtension(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('extn_id', TBSCertListExtensionId),
|
||||||
|
('critical', Boolean, {'default': False}),
|
||||||
|
('extn_value', ParsableOctetString),
|
||||||
|
]
|
||||||
|
|
||||||
|
_oid_pair = ('extn_id', 'extn_value')
|
||||||
|
_oid_specs = {
|
||||||
|
'issuer_alt_name': GeneralNames,
|
||||||
|
'crl_number': Integer,
|
||||||
|
'delta_crl_indicator': Integer,
|
||||||
|
'issuing_distribution_point': IssuingDistributionPoint,
|
||||||
|
'authority_key_identifier': AuthorityKeyIdentifier,
|
||||||
|
'freshest_crl': CRLDistributionPoints,
|
||||||
|
'authority_information_access': AuthorityInfoAccessSyntax,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TBSCertListExtensions(SequenceOf):
|
||||||
|
_child_spec = TBSCertListExtension
|
||||||
|
|
||||||
|
|
||||||
|
class CRLReason(Enumerated):
|
||||||
|
_map = {
|
||||||
|
0: 'unspecified',
|
||||||
|
1: 'key_compromise',
|
||||||
|
2: 'ca_compromise',
|
||||||
|
3: 'affiliation_changed',
|
||||||
|
4: 'superseded',
|
||||||
|
5: 'cessation_of_operation',
|
||||||
|
6: 'certificate_hold',
|
||||||
|
8: 'remove_from_crl',
|
||||||
|
9: 'privilege_withdrawn',
|
||||||
|
10: 'aa_compromise',
|
||||||
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def human_friendly(self):
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
A unicode string with revocation description that is suitable to
|
||||||
|
show to end-users. Starts with a lower case letter and phrased in
|
||||||
|
such a way that it makes sense after the phrase "because of" or
|
||||||
|
"due to".
|
||||||
|
"""
|
||||||
|
|
||||||
|
return {
|
||||||
|
'unspecified': 'an unspecified reason',
|
||||||
|
'key_compromise': 'a compromised key',
|
||||||
|
'ca_compromise': 'the CA being compromised',
|
||||||
|
'affiliation_changed': 'an affiliation change',
|
||||||
|
'superseded': 'certificate supersession',
|
||||||
|
'cessation_of_operation': 'a cessation of operation',
|
||||||
|
'certificate_hold': 'a certificate hold',
|
||||||
|
'remove_from_crl': 'removal from the CRL',
|
||||||
|
'privilege_withdrawn': 'privilege withdrawl',
|
||||||
|
'aa_compromise': 'the AA being compromised',
|
||||||
|
}[self.native]
|
||||||
|
|
||||||
|
|
||||||
|
class CRLEntryExtensionId(ObjectIdentifier):
|
||||||
|
_map = {
|
||||||
|
'2.5.29.21': 'crl_reason',
|
||||||
|
'2.5.29.23': 'hold_instruction_code',
|
||||||
|
'2.5.29.24': 'invalidity_date',
|
||||||
|
'2.5.29.29': 'certificate_issuer',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CRLEntryExtension(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('extn_id', CRLEntryExtensionId),
|
||||||
|
('critical', Boolean, {'default': False}),
|
||||||
|
('extn_value', ParsableOctetString),
|
||||||
|
]
|
||||||
|
|
||||||
|
_oid_pair = ('extn_id', 'extn_value')
|
||||||
|
_oid_specs = {
|
||||||
|
'crl_reason': CRLReason,
|
||||||
|
'hold_instruction_code': ObjectIdentifier,
|
||||||
|
'invalidity_date': GeneralizedTime,
|
||||||
|
'certificate_issuer': GeneralNames,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CRLEntryExtensions(SequenceOf):
|
||||||
|
_child_spec = CRLEntryExtension
|
||||||
|
|
||||||
|
|
||||||
|
class RevokedCertificate(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('user_certificate', Integer),
|
||||||
|
('revocation_date', Time),
|
||||||
|
('crl_entry_extensions', CRLEntryExtensions, {'optional': True}),
|
||||||
|
]
|
||||||
|
|
||||||
|
_processed_extensions = False
|
||||||
|
_critical_extensions = None
|
||||||
|
_crl_reason_value = None
|
||||||
|
_invalidity_date_value = None
|
||||||
|
_certificate_issuer_value = None
|
||||||
|
_issuer_name = False
|
||||||
|
|
||||||
|
def _set_extensions(self):
|
||||||
|
"""
|
||||||
|
Sets common named extensions to private attributes and creates a list
|
||||||
|
of critical extensions
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._critical_extensions = set()
|
||||||
|
|
||||||
|
for extension in self['crl_entry_extensions']:
|
||||||
|
name = extension['extn_id'].native
|
||||||
|
attribute_name = '_%s_value' % name
|
||||||
|
if hasattr(self, attribute_name):
|
||||||
|
setattr(self, attribute_name, extension['extn_value'].parsed)
|
||||||
|
if extension['critical'].native:
|
||||||
|
self._critical_extensions.add(name)
|
||||||
|
|
||||||
|
self._processed_extensions = True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def critical_extensions(self):
|
||||||
|
"""
|
||||||
|
Returns a set of the names (or OID if not a known extension) of the
|
||||||
|
extensions marked as critical
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A set of unicode strings
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not self._processed_extensions:
|
||||||
|
self._set_extensions()
|
||||||
|
return self._critical_extensions
|
||||||
|
|
||||||
|
@property
|
||||||
|
def crl_reason_value(self):
|
||||||
|
"""
|
||||||
|
This extension indicates the reason that a certificate was revoked.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
None or a CRLReason object
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._processed_extensions is False:
|
||||||
|
self._set_extensions()
|
||||||
|
return self._crl_reason_value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def invalidity_date_value(self):
|
||||||
|
"""
|
||||||
|
This extension indicates the suspected date/time the private key was
|
||||||
|
compromised or the certificate became invalid. This would usually be
|
||||||
|
before the revocation date, which is when the CA processed the
|
||||||
|
revocation.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
None or a GeneralizedTime object
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._processed_extensions is False:
|
||||||
|
self._set_extensions()
|
||||||
|
return self._invalidity_date_value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def certificate_issuer_value(self):
|
||||||
|
"""
|
||||||
|
This extension indicates the issuer of the certificate in question,
|
||||||
|
and is used in indirect CRLs. CRL entries without this extension are
|
||||||
|
for certificates issued from the last seen issuer.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
None or an x509.GeneralNames object
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._processed_extensions is False:
|
||||||
|
self._set_extensions()
|
||||||
|
return self._certificate_issuer_value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def issuer_name(self):
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
None, or an asn1crypto.x509.Name object for the issuer of the cert
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._issuer_name is False:
|
||||||
|
self._issuer_name = None
|
||||||
|
if self.certificate_issuer_value:
|
||||||
|
for general_name in self.certificate_issuer_value:
|
||||||
|
if general_name.name == 'directory_name':
|
||||||
|
self._issuer_name = general_name.chosen
|
||||||
|
break
|
||||||
|
return self._issuer_name
|
||||||
|
|
||||||
|
|
||||||
|
class RevokedCertificates(SequenceOf):
|
||||||
|
_child_spec = RevokedCertificate
|
||||||
|
|
||||||
|
|
||||||
|
class TbsCertList(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('version', Version, {'optional': True}),
|
||||||
|
('signature', SignedDigestAlgorithm),
|
||||||
|
('issuer', Name),
|
||||||
|
('this_update', Time),
|
||||||
|
('next_update', Time, {'optional': True}),
|
||||||
|
('revoked_certificates', RevokedCertificates, {'optional': True}),
|
||||||
|
('crl_extensions', TBSCertListExtensions, {'explicit': 0, 'optional': True}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class CertificateList(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('tbs_cert_list', TbsCertList),
|
||||||
|
('signature_algorithm', SignedDigestAlgorithm),
|
||||||
|
('signature', OctetBitString),
|
||||||
|
]
|
||||||
|
|
||||||
|
_processed_extensions = False
|
||||||
|
_critical_extensions = None
|
||||||
|
_issuer_alt_name_value = None
|
||||||
|
_crl_number_value = None
|
||||||
|
_delta_crl_indicator_value = None
|
||||||
|
_issuing_distribution_point_value = None
|
||||||
|
_authority_key_identifier_value = None
|
||||||
|
_freshest_crl_value = None
|
||||||
|
_authority_information_access_value = None
|
||||||
|
_issuer_cert_urls = None
|
||||||
|
_delta_crl_distribution_points = None
|
||||||
|
_sha1 = None
|
||||||
|
_sha256 = None
|
||||||
|
|
||||||
|
def _set_extensions(self):
|
||||||
|
"""
|
||||||
|
Sets common named extensions to private attributes and creates a list
|
||||||
|
of critical extensions
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._critical_extensions = set()
|
||||||
|
|
||||||
|
for extension in self['tbs_cert_list']['crl_extensions']:
|
||||||
|
name = extension['extn_id'].native
|
||||||
|
attribute_name = '_%s_value' % name
|
||||||
|
if hasattr(self, attribute_name):
|
||||||
|
setattr(self, attribute_name, extension['extn_value'].parsed)
|
||||||
|
if extension['critical'].native:
|
||||||
|
self._critical_extensions.add(name)
|
||||||
|
|
||||||
|
self._processed_extensions = True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def critical_extensions(self):
|
||||||
|
"""
|
||||||
|
Returns a set of the names (or OID if not a known extension) of the
|
||||||
|
extensions marked as critical
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A set of unicode strings
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not self._processed_extensions:
|
||||||
|
self._set_extensions()
|
||||||
|
return self._critical_extensions
|
||||||
|
|
||||||
|
@property
|
||||||
|
def issuer_alt_name_value(self):
|
||||||
|
"""
|
||||||
|
This extension allows associating one or more alternative names with
|
||||||
|
the issuer of the CRL.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
None or an x509.GeneralNames object
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._processed_extensions is False:
|
||||||
|
self._set_extensions()
|
||||||
|
return self._issuer_alt_name_value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def crl_number_value(self):
|
||||||
|
"""
|
||||||
|
This extension adds a monotonically increasing number to the CRL and is
|
||||||
|
used to distinguish different versions of the CRL.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
None or an Integer object
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._processed_extensions is False:
|
||||||
|
self._set_extensions()
|
||||||
|
return self._crl_number_value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def delta_crl_indicator_value(self):
|
||||||
|
"""
|
||||||
|
This extension indicates a CRL is a delta CRL, and contains the CRL
|
||||||
|
number of the base CRL that it is a delta from.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
None or an Integer object
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._processed_extensions is False:
|
||||||
|
self._set_extensions()
|
||||||
|
return self._delta_crl_indicator_value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def issuing_distribution_point_value(self):
|
||||||
|
"""
|
||||||
|
This extension includes information about what types of revocations
|
||||||
|
and certificates are part of the CRL.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
None or an IssuingDistributionPoint object
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._processed_extensions is False:
|
||||||
|
self._set_extensions()
|
||||||
|
return self._issuing_distribution_point_value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def authority_key_identifier_value(self):
|
||||||
|
"""
|
||||||
|
This extension helps in identifying the public key with which to
|
||||||
|
validate the authenticity of the CRL.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
None or an AuthorityKeyIdentifier object
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._processed_extensions is False:
|
||||||
|
self._set_extensions()
|
||||||
|
return self._authority_key_identifier_value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def freshest_crl_value(self):
|
||||||
|
"""
|
||||||
|
This extension is used in complete CRLs to indicate where a delta CRL
|
||||||
|
may be located.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
None or a CRLDistributionPoints object
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._processed_extensions is False:
|
||||||
|
self._set_extensions()
|
||||||
|
return self._freshest_crl_value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def authority_information_access_value(self):
|
||||||
|
"""
|
||||||
|
This extension is used to provide a URL with which to download the
|
||||||
|
certificate used to sign this CRL.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
None or an AuthorityInfoAccessSyntax object
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._processed_extensions is False:
|
||||||
|
self._set_extensions()
|
||||||
|
return self._authority_information_access_value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def issuer(self):
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
An asn1crypto.x509.Name object for the issuer of the CRL
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self['tbs_cert_list']['issuer']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def authority_key_identifier(self):
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
None or a byte string of the key_identifier from the authority key
|
||||||
|
identifier extension
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not self.authority_key_identifier_value:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return self.authority_key_identifier_value['key_identifier'].native
|
||||||
|
|
||||||
|
@property
|
||||||
|
def issuer_cert_urls(self):
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
A list of unicode strings that are URLs that should contain either
|
||||||
|
an individual DER-encoded X.509 certificate, or a DER-encoded CMS
|
||||||
|
message containing multiple certificates
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._issuer_cert_urls is None:
|
||||||
|
self._issuer_cert_urls = []
|
||||||
|
if self.authority_information_access_value:
|
||||||
|
for entry in self.authority_information_access_value:
|
||||||
|
if entry['access_method'].native == 'ca_issuers':
|
||||||
|
location = entry['access_location']
|
||||||
|
if location.name != 'uniform_resource_identifier':
|
||||||
|
continue
|
||||||
|
url = location.native
|
||||||
|
if url.lower()[0:7] == 'http://':
|
||||||
|
self._issuer_cert_urls.append(url)
|
||||||
|
return self._issuer_cert_urls
|
||||||
|
|
||||||
|
@property
|
||||||
|
def delta_crl_distribution_points(self):
|
||||||
|
"""
|
||||||
|
Returns delta CRL URLs - only applies to complete CRLs
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A list of zero or more DistributionPoint objects
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._delta_crl_distribution_points is None:
|
||||||
|
self._delta_crl_distribution_points = []
|
||||||
|
|
||||||
|
if self.freshest_crl_value is not None:
|
||||||
|
for distribution_point in self.freshest_crl_value:
|
||||||
|
distribution_point_name = distribution_point['distribution_point']
|
||||||
|
# RFC 5280 indicates conforming CA should not use the relative form
|
||||||
|
if distribution_point_name.name == 'name_relative_to_crl_issuer':
|
||||||
|
continue
|
||||||
|
# This library is currently only concerned with HTTP-based CRLs
|
||||||
|
for general_name in distribution_point_name.chosen:
|
||||||
|
if general_name.name == 'uniform_resource_identifier':
|
||||||
|
self._delta_crl_distribution_points.append(distribution_point)
|
||||||
|
|
||||||
|
return self._delta_crl_distribution_points
|
||||||
|
|
||||||
|
@property
|
||||||
|
def signature(self):
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
A byte string of the signature
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self['signature'].native
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sha1(self):
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
The SHA1 hash of the DER-encoded bytes of this certificate list
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._sha1 is None:
|
||||||
|
self._sha1 = hashlib.sha1(self.dump()).digest()
|
||||||
|
return self._sha1
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sha256(self):
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
The SHA-256 hash of the DER-encoded bytes of this certificate list
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._sha256 is None:
|
||||||
|
self._sha256 = hashlib.sha256(self.dump()).digest()
|
||||||
|
return self._sha256
|
@ -0,0 +1,133 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
"""
|
||||||
|
ASN.1 type classes for certificate signing requests (CSR). Exports the
|
||||||
|
following items:
|
||||||
|
|
||||||
|
- CertificationRequest()
|
||||||
|
|
||||||
|
Other type classes are defined that help compose the types listed above.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import unicode_literals, division, absolute_import, print_function
|
||||||
|
|
||||||
|
from .algos import SignedDigestAlgorithm
|
||||||
|
from .core import (
|
||||||
|
Any,
|
||||||
|
BitString,
|
||||||
|
BMPString,
|
||||||
|
Integer,
|
||||||
|
ObjectIdentifier,
|
||||||
|
OctetBitString,
|
||||||
|
Sequence,
|
||||||
|
SetOf,
|
||||||
|
UTF8String
|
||||||
|
)
|
||||||
|
from .keys import PublicKeyInfo
|
||||||
|
from .x509 import DirectoryString, Extensions, Name
|
||||||
|
|
||||||
|
|
||||||
|
# The structures in this file are taken from https://tools.ietf.org/html/rfc2986
|
||||||
|
# and https://tools.ietf.org/html/rfc2985
|
||||||
|
|
||||||
|
|
||||||
|
class Version(Integer):
|
||||||
|
_map = {
|
||||||
|
0: 'v1',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CSRAttributeType(ObjectIdentifier):
|
||||||
|
_map = {
|
||||||
|
'1.2.840.113549.1.9.7': 'challenge_password',
|
||||||
|
'1.2.840.113549.1.9.9': 'extended_certificate_attributes',
|
||||||
|
'1.2.840.113549.1.9.14': 'extension_request',
|
||||||
|
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wcce/a5eaae36-e9f3-4dc5-a687-bfa7115954f1
|
||||||
|
'1.3.6.1.4.1.311.13.2.2': 'microsoft_enrollment_csp_provider',
|
||||||
|
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wcce/7c677cba-030d-48be-ba2b-01e407705f34
|
||||||
|
'1.3.6.1.4.1.311.13.2.3': 'microsoft_os_version',
|
||||||
|
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wcce/64e5ff6d-c6dd-4578-92f7-b3d895f9b9c7
|
||||||
|
'1.3.6.1.4.1.311.21.20': 'microsoft_request_client_info',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class SetOfDirectoryString(SetOf):
|
||||||
|
_child_spec = DirectoryString
|
||||||
|
|
||||||
|
|
||||||
|
class Attribute(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('type', ObjectIdentifier),
|
||||||
|
('values', SetOf, {'spec': Any}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class SetOfAttributes(SetOf):
|
||||||
|
_child_spec = Attribute
|
||||||
|
|
||||||
|
|
||||||
|
class SetOfExtensions(SetOf):
|
||||||
|
_child_spec = Extensions
|
||||||
|
|
||||||
|
|
||||||
|
class MicrosoftEnrollmentCSProvider(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('keyspec', Integer),
|
||||||
|
('cspname', BMPString), # cryptographic service provider name
|
||||||
|
('signature', BitString),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class SetOfMicrosoftEnrollmentCSProvider(SetOf):
|
||||||
|
_child_spec = MicrosoftEnrollmentCSProvider
|
||||||
|
|
||||||
|
|
||||||
|
class MicrosoftRequestClientInfo(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('clientid', Integer),
|
||||||
|
('machinename', UTF8String),
|
||||||
|
('username', UTF8String),
|
||||||
|
('processname', UTF8String),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class SetOfMicrosoftRequestClientInfo(SetOf):
|
||||||
|
_child_spec = MicrosoftRequestClientInfo
|
||||||
|
|
||||||
|
|
||||||
|
class CRIAttribute(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('type', CSRAttributeType),
|
||||||
|
('values', Any),
|
||||||
|
]
|
||||||
|
|
||||||
|
_oid_pair = ('type', 'values')
|
||||||
|
_oid_specs = {
|
||||||
|
'challenge_password': SetOfDirectoryString,
|
||||||
|
'extended_certificate_attributes': SetOfAttributes,
|
||||||
|
'extension_request': SetOfExtensions,
|
||||||
|
'microsoft_enrollment_csp_provider': SetOfMicrosoftEnrollmentCSProvider,
|
||||||
|
'microsoft_os_version': SetOfDirectoryString,
|
||||||
|
'microsoft_request_client_info': SetOfMicrosoftRequestClientInfo,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CRIAttributes(SetOf):
|
||||||
|
_child_spec = CRIAttribute
|
||||||
|
|
||||||
|
|
||||||
|
class CertificationRequestInfo(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('version', Version),
|
||||||
|
('subject', Name),
|
||||||
|
('subject_pk_info', PublicKeyInfo),
|
||||||
|
('attributes', CRIAttributes, {'implicit': 0, 'optional': True}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class CertificationRequest(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('certification_request_info', CertificationRequestInfo),
|
||||||
|
('signature_algorithm', SignedDigestAlgorithm),
|
||||||
|
('signature', OctetBitString),
|
||||||
|
]
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,703 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
"""
|
||||||
|
ASN.1 type classes for the online certificate status protocol (OCSP). Exports
|
||||||
|
the following items:
|
||||||
|
|
||||||
|
- OCSPRequest()
|
||||||
|
- OCSPResponse()
|
||||||
|
|
||||||
|
Other type classes are defined that help compose the types listed above.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import unicode_literals, division, absolute_import, print_function
|
||||||
|
|
||||||
|
from ._errors import unwrap
|
||||||
|
from .algos import DigestAlgorithm, SignedDigestAlgorithm
|
||||||
|
from .core import (
|
||||||
|
Boolean,
|
||||||
|
Choice,
|
||||||
|
Enumerated,
|
||||||
|
GeneralizedTime,
|
||||||
|
IA5String,
|
||||||
|
Integer,
|
||||||
|
Null,
|
||||||
|
ObjectIdentifier,
|
||||||
|
OctetBitString,
|
||||||
|
OctetString,
|
||||||
|
ParsableOctetString,
|
||||||
|
Sequence,
|
||||||
|
SequenceOf,
|
||||||
|
)
|
||||||
|
from .crl import AuthorityInfoAccessSyntax, CRLReason
|
||||||
|
from .keys import PublicKeyAlgorithm
|
||||||
|
from .x509 import Certificate, GeneralName, GeneralNames, Name
|
||||||
|
|
||||||
|
|
||||||
|
# The structures in this file are taken from https://tools.ietf.org/html/rfc6960
|
||||||
|
|
||||||
|
|
||||||
|
class Version(Integer):
|
||||||
|
_map = {
|
||||||
|
0: 'v1'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CertId(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('hash_algorithm', DigestAlgorithm),
|
||||||
|
('issuer_name_hash', OctetString),
|
||||||
|
('issuer_key_hash', OctetString),
|
||||||
|
('serial_number', Integer),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceLocator(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('issuer', Name),
|
||||||
|
('locator', AuthorityInfoAccessSyntax),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class RequestExtensionId(ObjectIdentifier):
|
||||||
|
_map = {
|
||||||
|
'1.3.6.1.5.5.7.48.1.7': 'service_locator',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class RequestExtension(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('extn_id', RequestExtensionId),
|
||||||
|
('critical', Boolean, {'default': False}),
|
||||||
|
('extn_value', ParsableOctetString),
|
||||||
|
]
|
||||||
|
|
||||||
|
_oid_pair = ('extn_id', 'extn_value')
|
||||||
|
_oid_specs = {
|
||||||
|
'service_locator': ServiceLocator,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class RequestExtensions(SequenceOf):
|
||||||
|
_child_spec = RequestExtension
|
||||||
|
|
||||||
|
|
||||||
|
class Request(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('req_cert', CertId),
|
||||||
|
('single_request_extensions', RequestExtensions, {'explicit': 0, 'optional': True}),
|
||||||
|
]
|
||||||
|
|
||||||
|
_processed_extensions = False
|
||||||
|
_critical_extensions = None
|
||||||
|
_service_locator_value = None
|
||||||
|
|
||||||
|
def _set_extensions(self):
|
||||||
|
"""
|
||||||
|
Sets common named extensions to private attributes and creates a list
|
||||||
|
of critical extensions
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._critical_extensions = set()
|
||||||
|
|
||||||
|
for extension in self['single_request_extensions']:
|
||||||
|
name = extension['extn_id'].native
|
||||||
|
attribute_name = '_%s_value' % name
|
||||||
|
if hasattr(self, attribute_name):
|
||||||
|
setattr(self, attribute_name, extension['extn_value'].parsed)
|
||||||
|
if extension['critical'].native:
|
||||||
|
self._critical_extensions.add(name)
|
||||||
|
|
||||||
|
self._processed_extensions = True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def critical_extensions(self):
|
||||||
|
"""
|
||||||
|
Returns a set of the names (or OID if not a known extension) of the
|
||||||
|
extensions marked as critical
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A set of unicode strings
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not self._processed_extensions:
|
||||||
|
self._set_extensions()
|
||||||
|
return self._critical_extensions
|
||||||
|
|
||||||
|
@property
|
||||||
|
def service_locator_value(self):
|
||||||
|
"""
|
||||||
|
This extension is used when communicating with an OCSP responder that
|
||||||
|
acts as a proxy for OCSP requests
|
||||||
|
|
||||||
|
:return:
|
||||||
|
None or a ServiceLocator object
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._processed_extensions is False:
|
||||||
|
self._set_extensions()
|
||||||
|
return self._service_locator_value
|
||||||
|
|
||||||
|
|
||||||
|
class Requests(SequenceOf):
|
||||||
|
_child_spec = Request
|
||||||
|
|
||||||
|
|
||||||
|
class ResponseType(ObjectIdentifier):
|
||||||
|
_map = {
|
||||||
|
'1.3.6.1.5.5.7.48.1.1': 'basic_ocsp_response',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class AcceptableResponses(SequenceOf):
|
||||||
|
_child_spec = ResponseType
|
||||||
|
|
||||||
|
|
||||||
|
class PreferredSignatureAlgorithm(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('sig_identifier', SignedDigestAlgorithm),
|
||||||
|
('cert_identifier', PublicKeyAlgorithm, {'optional': True}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class PreferredSignatureAlgorithms(SequenceOf):
|
||||||
|
_child_spec = PreferredSignatureAlgorithm
|
||||||
|
|
||||||
|
|
||||||
|
class TBSRequestExtensionId(ObjectIdentifier):
|
||||||
|
_map = {
|
||||||
|
'1.3.6.1.5.5.7.48.1.2': 'nonce',
|
||||||
|
'1.3.6.1.5.5.7.48.1.4': 'acceptable_responses',
|
||||||
|
'1.3.6.1.5.5.7.48.1.8': 'preferred_signature_algorithms',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TBSRequestExtension(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('extn_id', TBSRequestExtensionId),
|
||||||
|
('critical', Boolean, {'default': False}),
|
||||||
|
('extn_value', ParsableOctetString),
|
||||||
|
]
|
||||||
|
|
||||||
|
_oid_pair = ('extn_id', 'extn_value')
|
||||||
|
_oid_specs = {
|
||||||
|
'nonce': OctetString,
|
||||||
|
'acceptable_responses': AcceptableResponses,
|
||||||
|
'preferred_signature_algorithms': PreferredSignatureAlgorithms,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TBSRequestExtensions(SequenceOf):
|
||||||
|
_child_spec = TBSRequestExtension
|
||||||
|
|
||||||
|
|
||||||
|
class TBSRequest(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('version', Version, {'explicit': 0, 'default': 'v1'}),
|
||||||
|
('requestor_name', GeneralName, {'explicit': 1, 'optional': True}),
|
||||||
|
('request_list', Requests),
|
||||||
|
('request_extensions', TBSRequestExtensions, {'explicit': 2, 'optional': True}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Certificates(SequenceOf):
|
||||||
|
_child_spec = Certificate
|
||||||
|
|
||||||
|
|
||||||
|
class Signature(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('signature_algorithm', SignedDigestAlgorithm),
|
||||||
|
('signature', OctetBitString),
|
||||||
|
('certs', Certificates, {'explicit': 0, 'optional': True}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class OCSPRequest(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('tbs_request', TBSRequest),
|
||||||
|
('optional_signature', Signature, {'explicit': 0, 'optional': True}),
|
||||||
|
]
|
||||||
|
|
||||||
|
_processed_extensions = False
|
||||||
|
_critical_extensions = None
|
||||||
|
_nonce_value = None
|
||||||
|
_acceptable_responses_value = None
|
||||||
|
_preferred_signature_algorithms_value = None
|
||||||
|
|
||||||
|
def _set_extensions(self):
|
||||||
|
"""
|
||||||
|
Sets common named extensions to private attributes and creates a list
|
||||||
|
of critical extensions
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._critical_extensions = set()
|
||||||
|
|
||||||
|
for extension in self['tbs_request']['request_extensions']:
|
||||||
|
name = extension['extn_id'].native
|
||||||
|
attribute_name = '_%s_value' % name
|
||||||
|
if hasattr(self, attribute_name):
|
||||||
|
setattr(self, attribute_name, extension['extn_value'].parsed)
|
||||||
|
if extension['critical'].native:
|
||||||
|
self._critical_extensions.add(name)
|
||||||
|
|
||||||
|
self._processed_extensions = True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def critical_extensions(self):
|
||||||
|
"""
|
||||||
|
Returns a set of the names (or OID if not a known extension) of the
|
||||||
|
extensions marked as critical
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A set of unicode strings
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not self._processed_extensions:
|
||||||
|
self._set_extensions()
|
||||||
|
return self._critical_extensions
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nonce_value(self):
|
||||||
|
"""
|
||||||
|
This extension is used to prevent replay attacks by including a unique,
|
||||||
|
random value with each request/response pair
|
||||||
|
|
||||||
|
:return:
|
||||||
|
None or an OctetString object
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._processed_extensions is False:
|
||||||
|
self._set_extensions()
|
||||||
|
return self._nonce_value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def acceptable_responses_value(self):
|
||||||
|
"""
|
||||||
|
This extension is used to allow the client and server to communicate
|
||||||
|
with alternative response formats other than just basic_ocsp_response,
|
||||||
|
although no other formats are defined in the standard.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
None or an AcceptableResponses object
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._processed_extensions is False:
|
||||||
|
self._set_extensions()
|
||||||
|
return self._acceptable_responses_value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def preferred_signature_algorithms_value(self):
|
||||||
|
"""
|
||||||
|
This extension is used by the client to define what signature algorithms
|
||||||
|
are preferred, including both the hash algorithm and the public key
|
||||||
|
algorithm, with a level of detail down to even the public key algorithm
|
||||||
|
parameters, such as curve name.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
None or a PreferredSignatureAlgorithms object
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._processed_extensions is False:
|
||||||
|
self._set_extensions()
|
||||||
|
return self._preferred_signature_algorithms_value
|
||||||
|
|
||||||
|
|
||||||
|
class OCSPResponseStatus(Enumerated):
|
||||||
|
_map = {
|
||||||
|
0: 'successful',
|
||||||
|
1: 'malformed_request',
|
||||||
|
2: 'internal_error',
|
||||||
|
3: 'try_later',
|
||||||
|
5: 'sign_required',
|
||||||
|
6: 'unauthorized',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ResponderId(Choice):
|
||||||
|
_alternatives = [
|
||||||
|
('by_name', Name, {'explicit': 1}),
|
||||||
|
('by_key', OctetString, {'explicit': 2}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# Custom class to return a meaningful .native attribute from CertStatus()
|
||||||
|
class StatusGood(Null):
|
||||||
|
def set(self, value):
|
||||||
|
"""
|
||||||
|
Sets the value of the object
|
||||||
|
|
||||||
|
:param value:
|
||||||
|
None or 'good'
|
||||||
|
"""
|
||||||
|
|
||||||
|
if value is not None and value != 'good' and not isinstance(value, Null):
|
||||||
|
raise ValueError(unwrap(
|
||||||
|
'''
|
||||||
|
value must be one of None, "good", not %s
|
||||||
|
''',
|
||||||
|
repr(value)
|
||||||
|
))
|
||||||
|
|
||||||
|
self.contents = b''
|
||||||
|
|
||||||
|
@property
|
||||||
|
def native(self):
|
||||||
|
return 'good'
|
||||||
|
|
||||||
|
|
||||||
|
# Custom class to return a meaningful .native attribute from CertStatus()
|
||||||
|
class StatusUnknown(Null):
|
||||||
|
def set(self, value):
|
||||||
|
"""
|
||||||
|
Sets the value of the object
|
||||||
|
|
||||||
|
:param value:
|
||||||
|
None or 'unknown'
|
||||||
|
"""
|
||||||
|
|
||||||
|
if value is not None and value != 'unknown' and not isinstance(value, Null):
|
||||||
|
raise ValueError(unwrap(
|
||||||
|
'''
|
||||||
|
value must be one of None, "unknown", not %s
|
||||||
|
''',
|
||||||
|
repr(value)
|
||||||
|
))
|
||||||
|
|
||||||
|
self.contents = b''
|
||||||
|
|
||||||
|
@property
|
||||||
|
def native(self):
|
||||||
|
return 'unknown'
|
||||||
|
|
||||||
|
|
||||||
|
class RevokedInfo(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('revocation_time', GeneralizedTime),
|
||||||
|
('revocation_reason', CRLReason, {'explicit': 0, 'optional': True}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class CertStatus(Choice):
|
||||||
|
_alternatives = [
|
||||||
|
('good', StatusGood, {'implicit': 0}),
|
||||||
|
('revoked', RevokedInfo, {'implicit': 1}),
|
||||||
|
('unknown', StatusUnknown, {'implicit': 2}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class CrlId(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('crl_url', IA5String, {'explicit': 0, 'optional': True}),
|
||||||
|
('crl_num', Integer, {'explicit': 1, 'optional': True}),
|
||||||
|
('crl_time', GeneralizedTime, {'explicit': 2, 'optional': True}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class SingleResponseExtensionId(ObjectIdentifier):
|
||||||
|
_map = {
|
||||||
|
'1.3.6.1.5.5.7.48.1.3': 'crl',
|
||||||
|
'1.3.6.1.5.5.7.48.1.6': 'archive_cutoff',
|
||||||
|
# These are CRLEntryExtension values from
|
||||||
|
# https://tools.ietf.org/html/rfc5280
|
||||||
|
'2.5.29.21': 'crl_reason',
|
||||||
|
'2.5.29.24': 'invalidity_date',
|
||||||
|
'2.5.29.29': 'certificate_issuer',
|
||||||
|
# https://tools.ietf.org/html/rfc6962.html#page-13
|
||||||
|
'1.3.6.1.4.1.11129.2.4.5': 'signed_certificate_timestamp_list',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class SingleResponseExtension(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('extn_id', SingleResponseExtensionId),
|
||||||
|
('critical', Boolean, {'default': False}),
|
||||||
|
('extn_value', ParsableOctetString),
|
||||||
|
]
|
||||||
|
|
||||||
|
_oid_pair = ('extn_id', 'extn_value')
|
||||||
|
_oid_specs = {
|
||||||
|
'crl': CrlId,
|
||||||
|
'archive_cutoff': GeneralizedTime,
|
||||||
|
'crl_reason': CRLReason,
|
||||||
|
'invalidity_date': GeneralizedTime,
|
||||||
|
'certificate_issuer': GeneralNames,
|
||||||
|
'signed_certificate_timestamp_list': OctetString,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class SingleResponseExtensions(SequenceOf):
|
||||||
|
_child_spec = SingleResponseExtension
|
||||||
|
|
||||||
|
|
||||||
|
class SingleResponse(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('cert_id', CertId),
|
||||||
|
('cert_status', CertStatus),
|
||||||
|
('this_update', GeneralizedTime),
|
||||||
|
('next_update', GeneralizedTime, {'explicit': 0, 'optional': True}),
|
||||||
|
('single_extensions', SingleResponseExtensions, {'explicit': 1, 'optional': True}),
|
||||||
|
]
|
||||||
|
|
||||||
|
_processed_extensions = False
|
||||||
|
_critical_extensions = None
|
||||||
|
_crl_value = None
|
||||||
|
_archive_cutoff_value = None
|
||||||
|
_crl_reason_value = None
|
||||||
|
_invalidity_date_value = None
|
||||||
|
_certificate_issuer_value = None
|
||||||
|
|
||||||
|
def _set_extensions(self):
|
||||||
|
"""
|
||||||
|
Sets common named extensions to private attributes and creates a list
|
||||||
|
of critical extensions
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._critical_extensions = set()
|
||||||
|
|
||||||
|
for extension in self['single_extensions']:
|
||||||
|
name = extension['extn_id'].native
|
||||||
|
attribute_name = '_%s_value' % name
|
||||||
|
if hasattr(self, attribute_name):
|
||||||
|
setattr(self, attribute_name, extension['extn_value'].parsed)
|
||||||
|
if extension['critical'].native:
|
||||||
|
self._critical_extensions.add(name)
|
||||||
|
|
||||||
|
self._processed_extensions = True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def critical_extensions(self):
|
||||||
|
"""
|
||||||
|
Returns a set of the names (or OID if not a known extension) of the
|
||||||
|
extensions marked as critical
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A set of unicode strings
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not self._processed_extensions:
|
||||||
|
self._set_extensions()
|
||||||
|
return self._critical_extensions
|
||||||
|
|
||||||
|
@property
|
||||||
|
def crl_value(self):
|
||||||
|
"""
|
||||||
|
This extension is used to locate the CRL that a certificate's revocation
|
||||||
|
is contained within.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
None or a CrlId object
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._processed_extensions is False:
|
||||||
|
self._set_extensions()
|
||||||
|
return self._crl_value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def archive_cutoff_value(self):
|
||||||
|
"""
|
||||||
|
This extension is used to indicate the date at which an archived
|
||||||
|
(historical) certificate status entry will no longer be available.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
None or a GeneralizedTime object
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._processed_extensions is False:
|
||||||
|
self._set_extensions()
|
||||||
|
return self._archive_cutoff_value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def crl_reason_value(self):
|
||||||
|
"""
|
||||||
|
This extension indicates the reason that a certificate was revoked.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
None or a CRLReason object
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._processed_extensions is False:
|
||||||
|
self._set_extensions()
|
||||||
|
return self._crl_reason_value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def invalidity_date_value(self):
|
||||||
|
"""
|
||||||
|
This extension indicates the suspected date/time the private key was
|
||||||
|
compromised or the certificate became invalid. This would usually be
|
||||||
|
before the revocation date, which is when the CA processed the
|
||||||
|
revocation.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
None or a GeneralizedTime object
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._processed_extensions is False:
|
||||||
|
self._set_extensions()
|
||||||
|
return self._invalidity_date_value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def certificate_issuer_value(self):
|
||||||
|
"""
|
||||||
|
This extension indicates the issuer of the certificate in question.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
None or an x509.GeneralNames object
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._processed_extensions is False:
|
||||||
|
self._set_extensions()
|
||||||
|
return self._certificate_issuer_value
|
||||||
|
|
||||||
|
|
||||||
|
class Responses(SequenceOf):
|
||||||
|
_child_spec = SingleResponse
|
||||||
|
|
||||||
|
|
||||||
|
class ResponseDataExtensionId(ObjectIdentifier):
|
||||||
|
_map = {
|
||||||
|
'1.3.6.1.5.5.7.48.1.2': 'nonce',
|
||||||
|
'1.3.6.1.5.5.7.48.1.9': 'extended_revoke',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ResponseDataExtension(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('extn_id', ResponseDataExtensionId),
|
||||||
|
('critical', Boolean, {'default': False}),
|
||||||
|
('extn_value', ParsableOctetString),
|
||||||
|
]
|
||||||
|
|
||||||
|
_oid_pair = ('extn_id', 'extn_value')
|
||||||
|
_oid_specs = {
|
||||||
|
'nonce': OctetString,
|
||||||
|
'extended_revoke': Null,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ResponseDataExtensions(SequenceOf):
|
||||||
|
_child_spec = ResponseDataExtension
|
||||||
|
|
||||||
|
|
||||||
|
class ResponseData(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('version', Version, {'explicit': 0, 'default': 'v1'}),
|
||||||
|
('responder_id', ResponderId),
|
||||||
|
('produced_at', GeneralizedTime),
|
||||||
|
('responses', Responses),
|
||||||
|
('response_extensions', ResponseDataExtensions, {'explicit': 1, 'optional': True}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class BasicOCSPResponse(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('tbs_response_data', ResponseData),
|
||||||
|
('signature_algorithm', SignedDigestAlgorithm),
|
||||||
|
('signature', OctetBitString),
|
||||||
|
('certs', Certificates, {'explicit': 0, 'optional': True}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ResponseBytes(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('response_type', ResponseType),
|
||||||
|
('response', ParsableOctetString),
|
||||||
|
]
|
||||||
|
|
||||||
|
_oid_pair = ('response_type', 'response')
|
||||||
|
_oid_specs = {
|
||||||
|
'basic_ocsp_response': BasicOCSPResponse,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class OCSPResponse(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('response_status', OCSPResponseStatus),
|
||||||
|
('response_bytes', ResponseBytes, {'explicit': 0, 'optional': True}),
|
||||||
|
]
|
||||||
|
|
||||||
|
_processed_extensions = False
|
||||||
|
_critical_extensions = None
|
||||||
|
_nonce_value = None
|
||||||
|
_extended_revoke_value = None
|
||||||
|
|
||||||
|
def _set_extensions(self):
|
||||||
|
"""
|
||||||
|
Sets common named extensions to private attributes and creates a list
|
||||||
|
of critical extensions
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._critical_extensions = set()
|
||||||
|
|
||||||
|
for extension in self['response_bytes']['response'].parsed['tbs_response_data']['response_extensions']:
|
||||||
|
name = extension['extn_id'].native
|
||||||
|
attribute_name = '_%s_value' % name
|
||||||
|
if hasattr(self, attribute_name):
|
||||||
|
setattr(self, attribute_name, extension['extn_value'].parsed)
|
||||||
|
if extension['critical'].native:
|
||||||
|
self._critical_extensions.add(name)
|
||||||
|
|
||||||
|
self._processed_extensions = True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def critical_extensions(self):
|
||||||
|
"""
|
||||||
|
Returns a set of the names (or OID if not a known extension) of the
|
||||||
|
extensions marked as critical
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A set of unicode strings
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not self._processed_extensions:
|
||||||
|
self._set_extensions()
|
||||||
|
return self._critical_extensions
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nonce_value(self):
|
||||||
|
"""
|
||||||
|
This extension is used to prevent replay attacks on the request/response
|
||||||
|
exchange
|
||||||
|
|
||||||
|
:return:
|
||||||
|
None or an OctetString object
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._processed_extensions is False:
|
||||||
|
self._set_extensions()
|
||||||
|
return self._nonce_value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def extended_revoke_value(self):
|
||||||
|
"""
|
||||||
|
This extension is used to signal that the responder will return a
|
||||||
|
"revoked" status for non-issued certificates.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
None or a Null object (if present)
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._processed_extensions is False:
|
||||||
|
self._set_extensions()
|
||||||
|
return self._extended_revoke_value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def basic_ocsp_response(self):
|
||||||
|
"""
|
||||||
|
A shortcut into the BasicOCSPResponse sequence
|
||||||
|
|
||||||
|
:return:
|
||||||
|
None or an asn1crypto.ocsp.BasicOCSPResponse object
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self['response_bytes']['response'].parsed
|
||||||
|
|
||||||
|
@property
|
||||||
|
def response_data(self):
|
||||||
|
"""
|
||||||
|
A shortcut into the parsed, ResponseData sequence
|
||||||
|
|
||||||
|
:return:
|
||||||
|
None or an asn1crypto.ocsp.ResponseData object
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self['response_bytes']['response'].parsed['tbs_response_data']
|
@ -0,0 +1,292 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
"""
|
||||||
|
Functions for parsing and dumping using the ASN.1 DER encoding. Exports the
|
||||||
|
following items:
|
||||||
|
|
||||||
|
- emit()
|
||||||
|
- parse()
|
||||||
|
- peek()
|
||||||
|
|
||||||
|
Other type classes are defined that help compose the types listed above.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import unicode_literals, division, absolute_import, print_function
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from ._types import byte_cls, chr_cls, type_name
|
||||||
|
from .util import int_from_bytes, int_to_bytes
|
||||||
|
|
||||||
|
_PY2 = sys.version_info <= (3,)
|
||||||
|
_INSUFFICIENT_DATA_MESSAGE = 'Insufficient data - %s bytes requested but only %s available'
|
||||||
|
_MAX_DEPTH = 10
|
||||||
|
|
||||||
|
|
||||||
|
def emit(class_, method, tag, contents):
|
||||||
|
"""
|
||||||
|
Constructs a byte string of an ASN.1 DER-encoded value
|
||||||
|
|
||||||
|
This is typically not useful. Instead, use one of the standard classes from
|
||||||
|
asn1crypto.core, or construct a new class with specific fields, and call the
|
||||||
|
.dump() method.
|
||||||
|
|
||||||
|
:param class_:
|
||||||
|
An integer ASN.1 class value: 0 (universal), 1 (application),
|
||||||
|
2 (context), 3 (private)
|
||||||
|
|
||||||
|
:param method:
|
||||||
|
An integer ASN.1 method value: 0 (primitive), 1 (constructed)
|
||||||
|
|
||||||
|
:param tag:
|
||||||
|
An integer ASN.1 tag value
|
||||||
|
|
||||||
|
:param contents:
|
||||||
|
A byte string of the encoded byte contents
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A byte string of the ASN.1 DER value (header and contents)
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not isinstance(class_, int):
|
||||||
|
raise TypeError('class_ must be an integer, not %s' % type_name(class_))
|
||||||
|
|
||||||
|
if class_ < 0 or class_ > 3:
|
||||||
|
raise ValueError('class_ must be one of 0, 1, 2 or 3, not %s' % class_)
|
||||||
|
|
||||||
|
if not isinstance(method, int):
|
||||||
|
raise TypeError('method must be an integer, not %s' % type_name(method))
|
||||||
|
|
||||||
|
if method < 0 or method > 1:
|
||||||
|
raise ValueError('method must be 0 or 1, not %s' % method)
|
||||||
|
|
||||||
|
if not isinstance(tag, int):
|
||||||
|
raise TypeError('tag must be an integer, not %s' % type_name(tag))
|
||||||
|
|
||||||
|
if tag < 0:
|
||||||
|
raise ValueError('tag must be greater than zero, not %s' % tag)
|
||||||
|
|
||||||
|
if not isinstance(contents, byte_cls):
|
||||||
|
raise TypeError('contents must be a byte string, not %s' % type_name(contents))
|
||||||
|
|
||||||
|
return _dump_header(class_, method, tag, contents) + contents
|
||||||
|
|
||||||
|
|
||||||
|
def parse(contents, strict=False):
|
||||||
|
"""
|
||||||
|
Parses a byte string of ASN.1 BER/DER-encoded data.
|
||||||
|
|
||||||
|
This is typically not useful. Instead, use one of the standard classes from
|
||||||
|
asn1crypto.core, or construct a new class with specific fields, and call the
|
||||||
|
.load() class method.
|
||||||
|
|
||||||
|
:param contents:
|
||||||
|
A byte string of BER/DER-encoded data
|
||||||
|
|
||||||
|
:param strict:
|
||||||
|
A boolean indicating if trailing data should be forbidden - if so, a
|
||||||
|
ValueError will be raised when trailing data exists
|
||||||
|
|
||||||
|
:raises:
|
||||||
|
ValueError - when the contents do not contain an ASN.1 header or are truncated in some way
|
||||||
|
TypeError - when contents is not a byte string
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A 6-element tuple:
|
||||||
|
- 0: integer class (0 to 3)
|
||||||
|
- 1: integer method
|
||||||
|
- 2: integer tag
|
||||||
|
- 3: byte string header
|
||||||
|
- 4: byte string content
|
||||||
|
- 5: byte string trailer
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not isinstance(contents, byte_cls):
|
||||||
|
raise TypeError('contents must be a byte string, not %s' % type_name(contents))
|
||||||
|
|
||||||
|
contents_len = len(contents)
|
||||||
|
info, consumed = _parse(contents, contents_len)
|
||||||
|
if strict and consumed != contents_len:
|
||||||
|
raise ValueError('Extra data - %d bytes of trailing data were provided' % (contents_len - consumed))
|
||||||
|
return info
|
||||||
|
|
||||||
|
|
||||||
|
def peek(contents):
|
||||||
|
"""
|
||||||
|
Parses a byte string of ASN.1 BER/DER-encoded data to find the length
|
||||||
|
|
||||||
|
This is typically used to look into an encoded value to see how long the
|
||||||
|
next chunk of ASN.1-encoded data is. Primarily it is useful when a
|
||||||
|
value is a concatenation of multiple values.
|
||||||
|
|
||||||
|
:param contents:
|
||||||
|
A byte string of BER/DER-encoded data
|
||||||
|
|
||||||
|
:raises:
|
||||||
|
ValueError - when the contents do not contain an ASN.1 header or are truncated in some way
|
||||||
|
TypeError - when contents is not a byte string
|
||||||
|
|
||||||
|
:return:
|
||||||
|
An integer with the number of bytes occupied by the ASN.1 value
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not isinstance(contents, byte_cls):
|
||||||
|
raise TypeError('contents must be a byte string, not %s' % type_name(contents))
|
||||||
|
|
||||||
|
info, consumed = _parse(contents, len(contents))
|
||||||
|
return consumed
|
||||||
|
|
||||||
|
|
||||||
|
def _parse(encoded_data, data_len, pointer=0, lengths_only=False, depth=0):
|
||||||
|
"""
|
||||||
|
Parses a byte string into component parts
|
||||||
|
|
||||||
|
:param encoded_data:
|
||||||
|
A byte string that contains BER-encoded data
|
||||||
|
|
||||||
|
:param data_len:
|
||||||
|
The integer length of the encoded data
|
||||||
|
|
||||||
|
:param pointer:
|
||||||
|
The index in the byte string to parse from
|
||||||
|
|
||||||
|
:param lengths_only:
|
||||||
|
A boolean to cause the call to return a 2-element tuple of the integer
|
||||||
|
number of bytes in the header and the integer number of bytes in the
|
||||||
|
contents. Internal use only.
|
||||||
|
|
||||||
|
:param depth:
|
||||||
|
The recursion depth when evaluating indefinite-length encoding.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A 2-element tuple:
|
||||||
|
- 0: A tuple of (class_, method, tag, header, content, trailer)
|
||||||
|
- 1: An integer indicating how many bytes were consumed
|
||||||
|
"""
|
||||||
|
|
||||||
|
if depth > _MAX_DEPTH:
|
||||||
|
raise ValueError('Indefinite-length recursion limit exceeded')
|
||||||
|
|
||||||
|
start = pointer
|
||||||
|
|
||||||
|
if data_len < pointer + 1:
|
||||||
|
raise ValueError(_INSUFFICIENT_DATA_MESSAGE % (1, data_len - pointer))
|
||||||
|
first_octet = ord(encoded_data[pointer]) if _PY2 else encoded_data[pointer]
|
||||||
|
|
||||||
|
pointer += 1
|
||||||
|
|
||||||
|
tag = first_octet & 31
|
||||||
|
constructed = (first_octet >> 5) & 1
|
||||||
|
# Base 128 length using 8th bit as continuation indicator
|
||||||
|
if tag == 31:
|
||||||
|
tag = 0
|
||||||
|
while True:
|
||||||
|
if data_len < pointer + 1:
|
||||||
|
raise ValueError(_INSUFFICIENT_DATA_MESSAGE % (1, data_len - pointer))
|
||||||
|
num = ord(encoded_data[pointer]) if _PY2 else encoded_data[pointer]
|
||||||
|
pointer += 1
|
||||||
|
if num == 0x80 and tag == 0:
|
||||||
|
raise ValueError('Non-minimal tag encoding')
|
||||||
|
tag *= 128
|
||||||
|
tag += num & 127
|
||||||
|
if num >> 7 == 0:
|
||||||
|
break
|
||||||
|
if tag < 31:
|
||||||
|
raise ValueError('Non-minimal tag encoding')
|
||||||
|
|
||||||
|
if data_len < pointer + 1:
|
||||||
|
raise ValueError(_INSUFFICIENT_DATA_MESSAGE % (1, data_len - pointer))
|
||||||
|
length_octet = ord(encoded_data[pointer]) if _PY2 else encoded_data[pointer]
|
||||||
|
pointer += 1
|
||||||
|
trailer = b''
|
||||||
|
|
||||||
|
if length_octet >> 7 == 0:
|
||||||
|
contents_end = pointer + (length_octet & 127)
|
||||||
|
|
||||||
|
else:
|
||||||
|
length_octets = length_octet & 127
|
||||||
|
if length_octets:
|
||||||
|
if data_len < pointer + length_octets:
|
||||||
|
raise ValueError(_INSUFFICIENT_DATA_MESSAGE % (length_octets, data_len - pointer))
|
||||||
|
pointer += length_octets
|
||||||
|
contents_end = pointer + int_from_bytes(encoded_data[pointer - length_octets:pointer], signed=False)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# To properly parse indefinite length values, we need to scan forward
|
||||||
|
# parsing headers until we find a value with a length of zero. If we
|
||||||
|
# just scanned looking for \x00\x00, nested indefinite length values
|
||||||
|
# would not work.
|
||||||
|
if not constructed:
|
||||||
|
raise ValueError('Indefinite-length element must be constructed')
|
||||||
|
contents_end = pointer
|
||||||
|
while data_len < contents_end + 2 or encoded_data[contents_end:contents_end+2] != b'\x00\x00':
|
||||||
|
_, contents_end = _parse(encoded_data, data_len, contents_end, lengths_only=True, depth=depth+1)
|
||||||
|
contents_end += 2
|
||||||
|
trailer = b'\x00\x00'
|
||||||
|
|
||||||
|
if contents_end > data_len:
|
||||||
|
raise ValueError(_INSUFFICIENT_DATA_MESSAGE % (contents_end - pointer, data_len - pointer))
|
||||||
|
|
||||||
|
if lengths_only:
|
||||||
|
return (pointer, contents_end)
|
||||||
|
|
||||||
|
return (
|
||||||
|
(
|
||||||
|
first_octet >> 6,
|
||||||
|
constructed,
|
||||||
|
tag,
|
||||||
|
encoded_data[start:pointer],
|
||||||
|
encoded_data[pointer:contents_end-len(trailer)],
|
||||||
|
trailer
|
||||||
|
),
|
||||||
|
contents_end
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _dump_header(class_, method, tag, contents):
|
||||||
|
"""
|
||||||
|
Constructs the header bytes for an ASN.1 object
|
||||||
|
|
||||||
|
:param class_:
|
||||||
|
An integer ASN.1 class value: 0 (universal), 1 (application),
|
||||||
|
2 (context), 3 (private)
|
||||||
|
|
||||||
|
:param method:
|
||||||
|
An integer ASN.1 method value: 0 (primitive), 1 (constructed)
|
||||||
|
|
||||||
|
:param tag:
|
||||||
|
An integer ASN.1 tag value
|
||||||
|
|
||||||
|
:param contents:
|
||||||
|
A byte string of the encoded byte contents
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A byte string of the ASN.1 DER header
|
||||||
|
"""
|
||||||
|
|
||||||
|
header = b''
|
||||||
|
|
||||||
|
id_num = 0
|
||||||
|
id_num |= class_ << 6
|
||||||
|
id_num |= method << 5
|
||||||
|
|
||||||
|
if tag >= 31:
|
||||||
|
cont_bit = 0
|
||||||
|
while tag > 0:
|
||||||
|
header = chr_cls(cont_bit | (tag & 0x7f)) + header
|
||||||
|
if not cont_bit:
|
||||||
|
cont_bit = 0x80
|
||||||
|
tag = tag >> 7
|
||||||
|
header = chr_cls(id_num | 31) + header
|
||||||
|
else:
|
||||||
|
header += chr_cls(id_num | tag)
|
||||||
|
|
||||||
|
length = len(contents)
|
||||||
|
if length <= 127:
|
||||||
|
header += chr_cls(length)
|
||||||
|
else:
|
||||||
|
length_bytes = int_to_bytes(length)
|
||||||
|
header += chr_cls(0x80 | len(length_bytes))
|
||||||
|
header += length_bytes
|
||||||
|
|
||||||
|
return header
|
@ -0,0 +1,84 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
"""
|
||||||
|
ASN.1 type classes for PDF signature structures. Adds extra oid mapping and
|
||||||
|
value parsing to asn1crypto.x509.Extension() and asn1crypto.xms.CMSAttribute().
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import unicode_literals, division, absolute_import, print_function
|
||||||
|
|
||||||
|
from .cms import CMSAttributeType, CMSAttribute
|
||||||
|
from .core import (
|
||||||
|
Boolean,
|
||||||
|
Integer,
|
||||||
|
Null,
|
||||||
|
ObjectIdentifier,
|
||||||
|
OctetString,
|
||||||
|
Sequence,
|
||||||
|
SequenceOf,
|
||||||
|
SetOf,
|
||||||
|
)
|
||||||
|
from .crl import CertificateList
|
||||||
|
from .ocsp import OCSPResponse
|
||||||
|
from .x509 import (
|
||||||
|
Extension,
|
||||||
|
ExtensionId,
|
||||||
|
GeneralName,
|
||||||
|
KeyPurposeId,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class AdobeArchiveRevInfo(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('version', Integer)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class AdobeTimestamp(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('version', Integer),
|
||||||
|
('location', GeneralName),
|
||||||
|
('requires_auth', Boolean, {'optional': True, 'default': False}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class OtherRevInfo(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('type', ObjectIdentifier),
|
||||||
|
('value', OctetString),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class SequenceOfCertificateList(SequenceOf):
|
||||||
|
_child_spec = CertificateList
|
||||||
|
|
||||||
|
|
||||||
|
class SequenceOfOCSPResponse(SequenceOf):
|
||||||
|
_child_spec = OCSPResponse
|
||||||
|
|
||||||
|
|
||||||
|
class SequenceOfOtherRevInfo(SequenceOf):
|
||||||
|
_child_spec = OtherRevInfo
|
||||||
|
|
||||||
|
|
||||||
|
class RevocationInfoArchival(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('crl', SequenceOfCertificateList, {'explicit': 0, 'optional': True}),
|
||||||
|
('ocsp', SequenceOfOCSPResponse, {'explicit': 1, 'optional': True}),
|
||||||
|
('other_rev_info', SequenceOfOtherRevInfo, {'explicit': 2, 'optional': True}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class SetOfRevocationInfoArchival(SetOf):
|
||||||
|
_child_spec = RevocationInfoArchival
|
||||||
|
|
||||||
|
|
||||||
|
ExtensionId._map['1.2.840.113583.1.1.9.2'] = 'adobe_archive_rev_info'
|
||||||
|
ExtensionId._map['1.2.840.113583.1.1.9.1'] = 'adobe_timestamp'
|
||||||
|
ExtensionId._map['1.2.840.113583.1.1.10'] = 'adobe_ppklite_credential'
|
||||||
|
Extension._oid_specs['adobe_archive_rev_info'] = AdobeArchiveRevInfo
|
||||||
|
Extension._oid_specs['adobe_timestamp'] = AdobeTimestamp
|
||||||
|
Extension._oid_specs['adobe_ppklite_credential'] = Null
|
||||||
|
KeyPurposeId._map['1.2.840.113583.1.1.5'] = 'pdf_signing'
|
||||||
|
CMSAttributeType._map['1.2.840.113583.1.1.8'] = 'adobe_revocation_info_archival'
|
||||||
|
CMSAttribute._oid_specs['adobe_revocation_info_archival'] = SetOfRevocationInfoArchival
|
@ -0,0 +1,222 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
"""
|
||||||
|
Encoding DER to PEM and decoding PEM to DER. Exports the following items:
|
||||||
|
|
||||||
|
- armor()
|
||||||
|
- detect()
|
||||||
|
- unarmor()
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import unicode_literals, division, absolute_import, print_function
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from ._errors import unwrap
|
||||||
|
from ._types import type_name as _type_name, str_cls, byte_cls
|
||||||
|
|
||||||
|
if sys.version_info < (3,):
|
||||||
|
from cStringIO import StringIO as BytesIO
|
||||||
|
else:
|
||||||
|
from io import BytesIO
|
||||||
|
|
||||||
|
|
||||||
|
def detect(byte_string):
|
||||||
|
"""
|
||||||
|
Detect if a byte string seems to contain a PEM-encoded block
|
||||||
|
|
||||||
|
:param byte_string:
|
||||||
|
A byte string to look through
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A boolean, indicating if a PEM-encoded block is contained in the byte
|
||||||
|
string
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not isinstance(byte_string, byte_cls):
|
||||||
|
raise TypeError(unwrap(
|
||||||
|
'''
|
||||||
|
byte_string must be a byte string, not %s
|
||||||
|
''',
|
||||||
|
_type_name(byte_string)
|
||||||
|
))
|
||||||
|
|
||||||
|
return byte_string.find(b'-----BEGIN') != -1 or byte_string.find(b'---- BEGIN') != -1
|
||||||
|
|
||||||
|
|
||||||
|
def armor(type_name, der_bytes, headers=None):
|
||||||
|
"""
|
||||||
|
Armors a DER-encoded byte string in PEM
|
||||||
|
|
||||||
|
:param type_name:
|
||||||
|
A unicode string that will be capitalized and placed in the header
|
||||||
|
and footer of the block. E.g. "CERTIFICATE", "PRIVATE KEY", etc. This
|
||||||
|
will appear as "-----BEGIN CERTIFICATE-----" and
|
||||||
|
"-----END CERTIFICATE-----".
|
||||||
|
|
||||||
|
:param der_bytes:
|
||||||
|
A byte string to be armored
|
||||||
|
|
||||||
|
:param headers:
|
||||||
|
An OrderedDict of the header lines to write after the BEGIN line
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A byte string of the PEM block
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not isinstance(der_bytes, byte_cls):
|
||||||
|
raise TypeError(unwrap(
|
||||||
|
'''
|
||||||
|
der_bytes must be a byte string, not %s
|
||||||
|
''' % _type_name(der_bytes)
|
||||||
|
))
|
||||||
|
|
||||||
|
if not isinstance(type_name, str_cls):
|
||||||
|
raise TypeError(unwrap(
|
||||||
|
'''
|
||||||
|
type_name must be a unicode string, not %s
|
||||||
|
''',
|
||||||
|
_type_name(type_name)
|
||||||
|
))
|
||||||
|
|
||||||
|
type_name = type_name.upper().encode('ascii')
|
||||||
|
|
||||||
|
output = BytesIO()
|
||||||
|
output.write(b'-----BEGIN ')
|
||||||
|
output.write(type_name)
|
||||||
|
output.write(b'-----\n')
|
||||||
|
if headers:
|
||||||
|
for key in headers:
|
||||||
|
output.write(key.encode('ascii'))
|
||||||
|
output.write(b': ')
|
||||||
|
output.write(headers[key].encode('ascii'))
|
||||||
|
output.write(b'\n')
|
||||||
|
output.write(b'\n')
|
||||||
|
b64_bytes = base64.b64encode(der_bytes)
|
||||||
|
b64_len = len(b64_bytes)
|
||||||
|
i = 0
|
||||||
|
while i < b64_len:
|
||||||
|
output.write(b64_bytes[i:i + 64])
|
||||||
|
output.write(b'\n')
|
||||||
|
i += 64
|
||||||
|
output.write(b'-----END ')
|
||||||
|
output.write(type_name)
|
||||||
|
output.write(b'-----\n')
|
||||||
|
|
||||||
|
return output.getvalue()
|
||||||
|
|
||||||
|
|
||||||
|
def _unarmor(pem_bytes):
|
||||||
|
"""
|
||||||
|
Convert a PEM-encoded byte string into one or more DER-encoded byte strings
|
||||||
|
|
||||||
|
:param pem_bytes:
|
||||||
|
A byte string of the PEM-encoded data
|
||||||
|
|
||||||
|
:raises:
|
||||||
|
ValueError - when the pem_bytes do not appear to be PEM-encoded bytes
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A generator of 3-element tuples in the format: (object_type, headers,
|
||||||
|
der_bytes). The object_type is a unicode string of what is between
|
||||||
|
"-----BEGIN " and "-----". Examples include: "CERTIFICATE",
|
||||||
|
"PUBLIC KEY", "PRIVATE KEY". The headers is a dict containing any lines
|
||||||
|
in the form "Name: Value" that are right after the begin line.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not isinstance(pem_bytes, byte_cls):
|
||||||
|
raise TypeError(unwrap(
|
||||||
|
'''
|
||||||
|
pem_bytes must be a byte string, not %s
|
||||||
|
''',
|
||||||
|
_type_name(pem_bytes)
|
||||||
|
))
|
||||||
|
|
||||||
|
# Valid states include: "trash", "headers", "body"
|
||||||
|
state = 'trash'
|
||||||
|
headers = {}
|
||||||
|
base64_data = b''
|
||||||
|
object_type = None
|
||||||
|
|
||||||
|
found_start = False
|
||||||
|
found_end = False
|
||||||
|
|
||||||
|
for line in pem_bytes.splitlines(False):
|
||||||
|
if line == b'':
|
||||||
|
continue
|
||||||
|
|
||||||
|
if state == "trash":
|
||||||
|
# Look for a starting line since some CA cert bundle show the cert
|
||||||
|
# into in a parsed format above each PEM block
|
||||||
|
type_name_match = re.match(b'^(?:---- |-----)BEGIN ([A-Z0-9 ]+)(?: ----|-----)', line)
|
||||||
|
if not type_name_match:
|
||||||
|
continue
|
||||||
|
object_type = type_name_match.group(1).decode('ascii')
|
||||||
|
|
||||||
|
found_start = True
|
||||||
|
state = 'headers'
|
||||||
|
continue
|
||||||
|
|
||||||
|
if state == 'headers':
|
||||||
|
if line.find(b':') == -1:
|
||||||
|
state = 'body'
|
||||||
|
else:
|
||||||
|
decoded_line = line.decode('ascii')
|
||||||
|
name, value = decoded_line.split(':', 1)
|
||||||
|
headers[name] = value.strip()
|
||||||
|
continue
|
||||||
|
|
||||||
|
if state == 'body':
|
||||||
|
if line[0:5] in (b'-----', b'---- '):
|
||||||
|
der_bytes = base64.b64decode(base64_data)
|
||||||
|
|
||||||
|
yield (object_type, headers, der_bytes)
|
||||||
|
|
||||||
|
state = 'trash'
|
||||||
|
headers = {}
|
||||||
|
base64_data = b''
|
||||||
|
object_type = None
|
||||||
|
found_end = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
base64_data += line
|
||||||
|
|
||||||
|
if not found_start or not found_end:
|
||||||
|
raise ValueError(unwrap(
|
||||||
|
'''
|
||||||
|
pem_bytes does not appear to contain PEM-encoded data - no
|
||||||
|
BEGIN/END combination found
|
||||||
|
'''
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
def unarmor(pem_bytes, multiple=False):
|
||||||
|
"""
|
||||||
|
Convert a PEM-encoded byte string into a DER-encoded byte string
|
||||||
|
|
||||||
|
:param pem_bytes:
|
||||||
|
A byte string of the PEM-encoded data
|
||||||
|
|
||||||
|
:param multiple:
|
||||||
|
If True, function will return a generator
|
||||||
|
|
||||||
|
:raises:
|
||||||
|
ValueError - when the pem_bytes do not appear to be PEM-encoded bytes
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A 3-element tuple (object_name, headers, der_bytes). The object_name is
|
||||||
|
a unicode string of what is between "-----BEGIN " and "-----". Examples
|
||||||
|
include: "CERTIFICATE", "PUBLIC KEY", "PRIVATE KEY". The headers is a
|
||||||
|
dict containing any lines in the form "Name: Value" that are right
|
||||||
|
after the begin line.
|
||||||
|
"""
|
||||||
|
|
||||||
|
generator = _unarmor(pem_bytes)
|
||||||
|
|
||||||
|
if not multiple:
|
||||||
|
return next(generator)
|
||||||
|
|
||||||
|
return generator
|
@ -0,0 +1,193 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
"""
|
||||||
|
ASN.1 type classes for PKCS#12 files. Exports the following items:
|
||||||
|
|
||||||
|
- CertBag()
|
||||||
|
- CrlBag()
|
||||||
|
- Pfx()
|
||||||
|
- SafeBag()
|
||||||
|
- SecretBag()
|
||||||
|
|
||||||
|
Other type classes are defined that help compose the types listed above.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import unicode_literals, division, absolute_import, print_function
|
||||||
|
|
||||||
|
from .algos import DigestInfo
|
||||||
|
from .cms import ContentInfo, SignedData
|
||||||
|
from .core import (
|
||||||
|
Any,
|
||||||
|
BMPString,
|
||||||
|
Integer,
|
||||||
|
ObjectIdentifier,
|
||||||
|
OctetString,
|
||||||
|
ParsableOctetString,
|
||||||
|
Sequence,
|
||||||
|
SequenceOf,
|
||||||
|
SetOf,
|
||||||
|
)
|
||||||
|
from .keys import PrivateKeyInfo, EncryptedPrivateKeyInfo
|
||||||
|
from .x509 import Certificate, KeyPurposeId
|
||||||
|
|
||||||
|
|
||||||
|
# The structures in this file are taken from https://tools.ietf.org/html/rfc7292
|
||||||
|
|
||||||
|
class MacData(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('mac', DigestInfo),
|
||||||
|
('mac_salt', OctetString),
|
||||||
|
('iterations', Integer, {'default': 1}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Version(Integer):
|
||||||
|
_map = {
|
||||||
|
3: 'v3'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class AttributeType(ObjectIdentifier):
|
||||||
|
_map = {
|
||||||
|
# https://tools.ietf.org/html/rfc2985#page-18
|
||||||
|
'1.2.840.113549.1.9.20': 'friendly_name',
|
||||||
|
'1.2.840.113549.1.9.21': 'local_key_id',
|
||||||
|
# https://support.microsoft.com/en-us/kb/287547
|
||||||
|
'1.3.6.1.4.1.311.17.1': 'microsoft_local_machine_keyset',
|
||||||
|
# https://github.com/frohoff/jdk8u-dev-jdk/blob/master/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java
|
||||||
|
# this is a set of OIDs, representing key usage, the usual value is a SET of one element OID 2.5.29.37.0
|
||||||
|
'2.16.840.1.113894.746875.1.1': 'trusted_key_usage',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class SetOfAny(SetOf):
|
||||||
|
_child_spec = Any
|
||||||
|
|
||||||
|
|
||||||
|
class SetOfBMPString(SetOf):
|
||||||
|
_child_spec = BMPString
|
||||||
|
|
||||||
|
|
||||||
|
class SetOfOctetString(SetOf):
|
||||||
|
_child_spec = OctetString
|
||||||
|
|
||||||
|
|
||||||
|
class SetOfKeyPurposeId(SetOf):
|
||||||
|
_child_spec = KeyPurposeId
|
||||||
|
|
||||||
|
|
||||||
|
class Attribute(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('type', AttributeType),
|
||||||
|
('values', None),
|
||||||
|
]
|
||||||
|
|
||||||
|
_oid_specs = {
|
||||||
|
'friendly_name': SetOfBMPString,
|
||||||
|
'local_key_id': SetOfOctetString,
|
||||||
|
'microsoft_csp_name': SetOfBMPString,
|
||||||
|
'trusted_key_usage': SetOfKeyPurposeId,
|
||||||
|
}
|
||||||
|
|
||||||
|
def _values_spec(self):
|
||||||
|
return self._oid_specs.get(self['type'].native, SetOfAny)
|
||||||
|
|
||||||
|
_spec_callbacks = {
|
||||||
|
'values': _values_spec
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Attributes(SetOf):
|
||||||
|
_child_spec = Attribute
|
||||||
|
|
||||||
|
|
||||||
|
class Pfx(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('version', Version),
|
||||||
|
('auth_safe', ContentInfo),
|
||||||
|
('mac_data', MacData, {'optional': True})
|
||||||
|
]
|
||||||
|
|
||||||
|
_authenticated_safe = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def authenticated_safe(self):
|
||||||
|
if self._authenticated_safe is None:
|
||||||
|
content = self['auth_safe']['content']
|
||||||
|
if isinstance(content, SignedData):
|
||||||
|
content = content['content_info']['content']
|
||||||
|
self._authenticated_safe = AuthenticatedSafe.load(content.native)
|
||||||
|
return self._authenticated_safe
|
||||||
|
|
||||||
|
|
||||||
|
class AuthenticatedSafe(SequenceOf):
|
||||||
|
_child_spec = ContentInfo
|
||||||
|
|
||||||
|
|
||||||
|
class BagId(ObjectIdentifier):
|
||||||
|
_map = {
|
||||||
|
'1.2.840.113549.1.12.10.1.1': 'key_bag',
|
||||||
|
'1.2.840.113549.1.12.10.1.2': 'pkcs8_shrouded_key_bag',
|
||||||
|
'1.2.840.113549.1.12.10.1.3': 'cert_bag',
|
||||||
|
'1.2.840.113549.1.12.10.1.4': 'crl_bag',
|
||||||
|
'1.2.840.113549.1.12.10.1.5': 'secret_bag',
|
||||||
|
'1.2.840.113549.1.12.10.1.6': 'safe_contents',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CertId(ObjectIdentifier):
|
||||||
|
_map = {
|
||||||
|
'1.2.840.113549.1.9.22.1': 'x509',
|
||||||
|
'1.2.840.113549.1.9.22.2': 'sdsi',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CertBag(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('cert_id', CertId),
|
||||||
|
('cert_value', ParsableOctetString, {'explicit': 0}),
|
||||||
|
]
|
||||||
|
|
||||||
|
_oid_pair = ('cert_id', 'cert_value')
|
||||||
|
_oid_specs = {
|
||||||
|
'x509': Certificate,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CrlBag(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('crl_id', ObjectIdentifier),
|
||||||
|
('crl_value', OctetString, {'explicit': 0}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class SecretBag(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('secret_type_id', ObjectIdentifier),
|
||||||
|
('secret_value', OctetString, {'explicit': 0}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class SafeContents(SequenceOf):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class SafeBag(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('bag_id', BagId),
|
||||||
|
('bag_value', Any, {'explicit': 0}),
|
||||||
|
('bag_attributes', Attributes, {'optional': True}),
|
||||||
|
]
|
||||||
|
|
||||||
|
_oid_pair = ('bag_id', 'bag_value')
|
||||||
|
_oid_specs = {
|
||||||
|
'key_bag': PrivateKeyInfo,
|
||||||
|
'pkcs8_shrouded_key_bag': EncryptedPrivateKeyInfo,
|
||||||
|
'cert_bag': CertBag,
|
||||||
|
'crl_bag': CrlBag,
|
||||||
|
'secret_bag': SecretBag,
|
||||||
|
'safe_contents': SafeContents
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SafeContents._child_spec = SafeBag
|
@ -0,0 +1,310 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
"""
|
||||||
|
ASN.1 type classes for the time stamp protocol (TSP). Exports the following
|
||||||
|
items:
|
||||||
|
|
||||||
|
- TimeStampReq()
|
||||||
|
- TimeStampResp()
|
||||||
|
|
||||||
|
Also adds TimeStampedData() support to asn1crypto.cms.ContentInfo(),
|
||||||
|
TimeStampedData() and TSTInfo() support to
|
||||||
|
asn1crypto.cms.EncapsulatedContentInfo() and some oids and value parsers to
|
||||||
|
asn1crypto.cms.CMSAttribute().
|
||||||
|
|
||||||
|
Other type classes are defined that help compose the types listed above.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import unicode_literals, division, absolute_import, print_function
|
||||||
|
|
||||||
|
from .algos import DigestAlgorithm
|
||||||
|
from .cms import (
|
||||||
|
CMSAttribute,
|
||||||
|
CMSAttributeType,
|
||||||
|
ContentInfo,
|
||||||
|
ContentType,
|
||||||
|
EncapsulatedContentInfo,
|
||||||
|
)
|
||||||
|
from .core import (
|
||||||
|
Any,
|
||||||
|
BitString,
|
||||||
|
Boolean,
|
||||||
|
Choice,
|
||||||
|
GeneralizedTime,
|
||||||
|
IA5String,
|
||||||
|
Integer,
|
||||||
|
ObjectIdentifier,
|
||||||
|
OctetString,
|
||||||
|
Sequence,
|
||||||
|
SequenceOf,
|
||||||
|
SetOf,
|
||||||
|
UTF8String,
|
||||||
|
)
|
||||||
|
from .crl import CertificateList
|
||||||
|
from .x509 import (
|
||||||
|
Attributes,
|
||||||
|
CertificatePolicies,
|
||||||
|
GeneralName,
|
||||||
|
GeneralNames,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# The structures in this file are based on https://tools.ietf.org/html/rfc3161,
|
||||||
|
# https://tools.ietf.org/html/rfc4998, https://tools.ietf.org/html/rfc5544,
|
||||||
|
# https://tools.ietf.org/html/rfc5035, https://tools.ietf.org/html/rfc2634
|
||||||
|
|
||||||
|
class Version(Integer):
|
||||||
|
_map = {
|
||||||
|
0: 'v0',
|
||||||
|
1: 'v1',
|
||||||
|
2: 'v2',
|
||||||
|
3: 'v3',
|
||||||
|
4: 'v4',
|
||||||
|
5: 'v5',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class MessageImprint(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('hash_algorithm', DigestAlgorithm),
|
||||||
|
('hashed_message', OctetString),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Accuracy(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('seconds', Integer, {'optional': True}),
|
||||||
|
('millis', Integer, {'implicit': 0, 'optional': True}),
|
||||||
|
('micros', Integer, {'implicit': 1, 'optional': True}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Extension(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('extn_id', ObjectIdentifier),
|
||||||
|
('critical', Boolean, {'default': False}),
|
||||||
|
('extn_value', OctetString),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Extensions(SequenceOf):
|
||||||
|
_child_spec = Extension
|
||||||
|
|
||||||
|
|
||||||
|
class TSTInfo(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('version', Version),
|
||||||
|
('policy', ObjectIdentifier),
|
||||||
|
('message_imprint', MessageImprint),
|
||||||
|
('serial_number', Integer),
|
||||||
|
('gen_time', GeneralizedTime),
|
||||||
|
('accuracy', Accuracy, {'optional': True}),
|
||||||
|
('ordering', Boolean, {'default': False}),
|
||||||
|
('nonce', Integer, {'optional': True}),
|
||||||
|
('tsa', GeneralName, {'explicit': 0, 'optional': True}),
|
||||||
|
('extensions', Extensions, {'implicit': 1, 'optional': True}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class TimeStampReq(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('version', Version),
|
||||||
|
('message_imprint', MessageImprint),
|
||||||
|
('req_policy', ObjectIdentifier, {'optional': True}),
|
||||||
|
('nonce', Integer, {'optional': True}),
|
||||||
|
('cert_req', Boolean, {'default': False}),
|
||||||
|
('extensions', Extensions, {'implicit': 0, 'optional': True}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class PKIStatus(Integer):
|
||||||
|
_map = {
|
||||||
|
0: 'granted',
|
||||||
|
1: 'granted_with_mods',
|
||||||
|
2: 'rejection',
|
||||||
|
3: 'waiting',
|
||||||
|
4: 'revocation_warning',
|
||||||
|
5: 'revocation_notification',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PKIFreeText(SequenceOf):
|
||||||
|
_child_spec = UTF8String
|
||||||
|
|
||||||
|
|
||||||
|
class PKIFailureInfo(BitString):
|
||||||
|
_map = {
|
||||||
|
0: 'bad_alg',
|
||||||
|
2: 'bad_request',
|
||||||
|
5: 'bad_data_format',
|
||||||
|
14: 'time_not_available',
|
||||||
|
15: 'unaccepted_policy',
|
||||||
|
16: 'unaccepted_extensions',
|
||||||
|
17: 'add_info_not_available',
|
||||||
|
25: 'system_failure',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PKIStatusInfo(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('status', PKIStatus),
|
||||||
|
('status_string', PKIFreeText, {'optional': True}),
|
||||||
|
('fail_info', PKIFailureInfo, {'optional': True}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class TimeStampResp(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('status', PKIStatusInfo),
|
||||||
|
('time_stamp_token', ContentInfo),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class MetaData(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('hash_protected', Boolean),
|
||||||
|
('file_name', UTF8String, {'optional': True}),
|
||||||
|
('media_type', IA5String, {'optional': True}),
|
||||||
|
('other_meta_data', Attributes, {'optional': True}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class TimeStampAndCRL(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('time_stamp', EncapsulatedContentInfo),
|
||||||
|
('crl', CertificateList, {'optional': True}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class TimeStampTokenEvidence(SequenceOf):
|
||||||
|
_child_spec = TimeStampAndCRL
|
||||||
|
|
||||||
|
|
||||||
|
class DigestAlgorithms(SequenceOf):
|
||||||
|
_child_spec = DigestAlgorithm
|
||||||
|
|
||||||
|
|
||||||
|
class EncryptionInfo(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('encryption_info_type', ObjectIdentifier),
|
||||||
|
('encryption_info_value', Any),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class PartialHashtree(SequenceOf):
|
||||||
|
_child_spec = OctetString
|
||||||
|
|
||||||
|
|
||||||
|
class PartialHashtrees(SequenceOf):
|
||||||
|
_child_spec = PartialHashtree
|
||||||
|
|
||||||
|
|
||||||
|
class ArchiveTimeStamp(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('digest_algorithm', DigestAlgorithm, {'implicit': 0, 'optional': True}),
|
||||||
|
('attributes', Attributes, {'implicit': 1, 'optional': True}),
|
||||||
|
('reduced_hashtree', PartialHashtrees, {'implicit': 2, 'optional': True}),
|
||||||
|
('time_stamp', ContentInfo),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ArchiveTimeStampSequence(SequenceOf):
|
||||||
|
_child_spec = ArchiveTimeStamp
|
||||||
|
|
||||||
|
|
||||||
|
class EvidenceRecord(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('version', Version),
|
||||||
|
('digest_algorithms', DigestAlgorithms),
|
||||||
|
('crypto_infos', Attributes, {'implicit': 0, 'optional': True}),
|
||||||
|
('encryption_info', EncryptionInfo, {'implicit': 1, 'optional': True}),
|
||||||
|
('archive_time_stamp_sequence', ArchiveTimeStampSequence),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class OtherEvidence(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('oe_type', ObjectIdentifier),
|
||||||
|
('oe_value', Any),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Evidence(Choice):
|
||||||
|
_alternatives = [
|
||||||
|
('tst_evidence', TimeStampTokenEvidence, {'implicit': 0}),
|
||||||
|
('ers_evidence', EvidenceRecord, {'implicit': 1}),
|
||||||
|
('other_evidence', OtherEvidence, {'implicit': 2}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class TimeStampedData(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('version', Version),
|
||||||
|
('data_uri', IA5String, {'optional': True}),
|
||||||
|
('meta_data', MetaData, {'optional': True}),
|
||||||
|
('content', OctetString, {'optional': True}),
|
||||||
|
('temporal_evidence', Evidence),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class IssuerSerial(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('issuer', GeneralNames),
|
||||||
|
('serial_number', Integer),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ESSCertID(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('cert_hash', OctetString),
|
||||||
|
('issuer_serial', IssuerSerial, {'optional': True}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ESSCertIDs(SequenceOf):
|
||||||
|
_child_spec = ESSCertID
|
||||||
|
|
||||||
|
|
||||||
|
class SigningCertificate(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('certs', ESSCertIDs),
|
||||||
|
('policies', CertificatePolicies, {'optional': True}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class SetOfSigningCertificates(SetOf):
|
||||||
|
_child_spec = SigningCertificate
|
||||||
|
|
||||||
|
|
||||||
|
class ESSCertIDv2(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('hash_algorithm', DigestAlgorithm, {'default': {'algorithm': 'sha256'}}),
|
||||||
|
('cert_hash', OctetString),
|
||||||
|
('issuer_serial', IssuerSerial, {'optional': True}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ESSCertIDv2s(SequenceOf):
|
||||||
|
_child_spec = ESSCertIDv2
|
||||||
|
|
||||||
|
|
||||||
|
class SigningCertificateV2(Sequence):
|
||||||
|
_fields = [
|
||||||
|
('certs', ESSCertIDv2s),
|
||||||
|
('policies', CertificatePolicies, {'optional': True}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class SetOfSigningCertificatesV2(SetOf):
|
||||||
|
_child_spec = SigningCertificateV2
|
||||||
|
|
||||||
|
|
||||||
|
EncapsulatedContentInfo._oid_specs['tst_info'] = TSTInfo
|
||||||
|
EncapsulatedContentInfo._oid_specs['timestamped_data'] = TimeStampedData
|
||||||
|
ContentInfo._oid_specs['timestamped_data'] = TimeStampedData
|
||||||
|
ContentType._map['1.2.840.113549.1.9.16.1.4'] = 'tst_info'
|
||||||
|
ContentType._map['1.2.840.113549.1.9.16.1.31'] = 'timestamped_data'
|
||||||
|
CMSAttributeType._map['1.2.840.113549.1.9.16.2.12'] = 'signing_certificate'
|
||||||
|
CMSAttribute._oid_specs['signing_certificate'] = SetOfSigningCertificates
|
||||||
|
CMSAttributeType._map['1.2.840.113549.1.9.16.2.47'] = 'signing_certificate_v2'
|
||||||
|
CMSAttribute._oid_specs['signing_certificate_v2'] = SetOfSigningCertificatesV2
|
@ -0,0 +1,878 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
"""
|
||||||
|
Miscellaneous data helpers, including functions for converting integers to and
|
||||||
|
from bytes and UTC timezone. Exports the following items:
|
||||||
|
|
||||||
|
- OrderedDict()
|
||||||
|
- int_from_bytes()
|
||||||
|
- int_to_bytes()
|
||||||
|
- timezone.utc
|
||||||
|
- utc_with_dst
|
||||||
|
- create_timezone()
|
||||||
|
- inet_ntop()
|
||||||
|
- inet_pton()
|
||||||
|
- uri_to_iri()
|
||||||
|
- iri_to_uri()
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import unicode_literals, division, absolute_import, print_function
|
||||||
|
|
||||||
|
import math
|
||||||
|
import sys
|
||||||
|
from datetime import datetime, date, timedelta, tzinfo
|
||||||
|
|
||||||
|
from ._errors import unwrap
|
||||||
|
from ._iri import iri_to_uri, uri_to_iri # noqa
|
||||||
|
from ._ordereddict import OrderedDict # noqa
|
||||||
|
from ._types import type_name
|
||||||
|
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
from ._inet import inet_ntop, inet_pton
|
||||||
|
else:
|
||||||
|
from socket import inet_ntop, inet_pton # noqa
|
||||||
|
|
||||||
|
|
||||||
|
# Python 2
|
||||||
|
if sys.version_info <= (3,):
|
||||||
|
|
||||||
|
def int_to_bytes(value, signed=False, width=None):
|
||||||
|
"""
|
||||||
|
Converts an integer to a byte string
|
||||||
|
|
||||||
|
:param value:
|
||||||
|
The integer to convert
|
||||||
|
|
||||||
|
:param signed:
|
||||||
|
If the byte string should be encoded using two's complement
|
||||||
|
|
||||||
|
:param width:
|
||||||
|
If None, the minimal possible size (but at least 1),
|
||||||
|
otherwise an integer of the byte width for the return value
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A byte string
|
||||||
|
"""
|
||||||
|
|
||||||
|
if value == 0 and width == 0:
|
||||||
|
return b''
|
||||||
|
|
||||||
|
# Handle negatives in two's complement
|
||||||
|
is_neg = False
|
||||||
|
if signed and value < 0:
|
||||||
|
is_neg = True
|
||||||
|
bits = int(math.ceil(len('%x' % abs(value)) / 2.0) * 8)
|
||||||
|
value = (value + (1 << bits)) % (1 << bits)
|
||||||
|
|
||||||
|
hex_str = '%x' % value
|
||||||
|
if len(hex_str) & 1:
|
||||||
|
hex_str = '0' + hex_str
|
||||||
|
|
||||||
|
output = hex_str.decode('hex')
|
||||||
|
|
||||||
|
if signed and not is_neg and ord(output[0:1]) & 0x80:
|
||||||
|
output = b'\x00' + output
|
||||||
|
|
||||||
|
if width is not None:
|
||||||
|
if len(output) > width:
|
||||||
|
raise OverflowError('int too big to convert')
|
||||||
|
if is_neg:
|
||||||
|
pad_char = b'\xFF'
|
||||||
|
else:
|
||||||
|
pad_char = b'\x00'
|
||||||
|
output = (pad_char * (width - len(output))) + output
|
||||||
|
elif is_neg and ord(output[0:1]) & 0x80 == 0:
|
||||||
|
output = b'\xFF' + output
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
||||||
|
def int_from_bytes(value, signed=False):
|
||||||
|
"""
|
||||||
|
Converts a byte string to an integer
|
||||||
|
|
||||||
|
:param value:
|
||||||
|
The byte string to convert
|
||||||
|
|
||||||
|
:param signed:
|
||||||
|
If the byte string should be interpreted using two's complement
|
||||||
|
|
||||||
|
:return:
|
||||||
|
An integer
|
||||||
|
"""
|
||||||
|
|
||||||
|
if value == b'':
|
||||||
|
return 0
|
||||||
|
|
||||||
|
num = long(value.encode("hex"), 16) # noqa
|
||||||
|
|
||||||
|
if not signed:
|
||||||
|
return num
|
||||||
|
|
||||||
|
# Check for sign bit and handle two's complement
|
||||||
|
if ord(value[0:1]) & 0x80:
|
||||||
|
bit_len = len(value) * 8
|
||||||
|
return num - (1 << bit_len)
|
||||||
|
|
||||||
|
return num
|
||||||
|
|
||||||
|
class timezone(tzinfo): # noqa
|
||||||
|
"""
|
||||||
|
Implements datetime.timezone for py2.
|
||||||
|
Only full minute offsets are supported.
|
||||||
|
DST is not supported.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, offset, name=None):
|
||||||
|
"""
|
||||||
|
:param offset:
|
||||||
|
A timedelta with this timezone's offset from UTC
|
||||||
|
|
||||||
|
:param name:
|
||||||
|
Name of the timezone; if None, generate one.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not timedelta(hours=-24) < offset < timedelta(hours=24):
|
||||||
|
raise ValueError('Offset must be in [-23:59, 23:59]')
|
||||||
|
|
||||||
|
if offset.seconds % 60 or offset.microseconds:
|
||||||
|
raise ValueError('Offset must be full minutes')
|
||||||
|
|
||||||
|
self._offset = offset
|
||||||
|
|
||||||
|
if name is not None:
|
||||||
|
self._name = name
|
||||||
|
elif not offset:
|
||||||
|
self._name = 'UTC'
|
||||||
|
else:
|
||||||
|
self._name = 'UTC' + _format_offset(offset)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
"""
|
||||||
|
Compare two timezones
|
||||||
|
|
||||||
|
:param other:
|
||||||
|
The other timezone to compare to
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A boolean
|
||||||
|
"""
|
||||||
|
|
||||||
|
if type(other) != timezone:
|
||||||
|
return False
|
||||||
|
return self._offset == other._offset
|
||||||
|
|
||||||
|
def __getinitargs__(self):
|
||||||
|
"""
|
||||||
|
Called by tzinfo.__reduce__ to support pickle and copy.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
offset and name, to be used for __init__
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self._offset, self._name
|
||||||
|
|
||||||
|
def tzname(self, dt):
|
||||||
|
"""
|
||||||
|
:param dt:
|
||||||
|
A datetime object; ignored.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
Name of this timezone
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
def utcoffset(self, dt):
|
||||||
|
"""
|
||||||
|
:param dt:
|
||||||
|
A datetime object; ignored.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A timedelta object with the offset from UTC
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self._offset
|
||||||
|
|
||||||
|
def dst(self, dt):
|
||||||
|
"""
|
||||||
|
:param dt:
|
||||||
|
A datetime object; ignored.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
Zero timedelta
|
||||||
|
"""
|
||||||
|
|
||||||
|
return timedelta(0)
|
||||||
|
|
||||||
|
timezone.utc = timezone(timedelta(0))
|
||||||
|
|
||||||
|
# Python 3
|
||||||
|
else:
|
||||||
|
|
||||||
|
from datetime import timezone # noqa
|
||||||
|
|
||||||
|
def int_to_bytes(value, signed=False, width=None):
|
||||||
|
"""
|
||||||
|
Converts an integer to a byte string
|
||||||
|
|
||||||
|
:param value:
|
||||||
|
The integer to convert
|
||||||
|
|
||||||
|
:param signed:
|
||||||
|
If the byte string should be encoded using two's complement
|
||||||
|
|
||||||
|
:param width:
|
||||||
|
If None, the minimal possible size (but at least 1),
|
||||||
|
otherwise an integer of the byte width for the return value
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A byte string
|
||||||
|
"""
|
||||||
|
|
||||||
|
if width is None:
|
||||||
|
if signed:
|
||||||
|
if value < 0:
|
||||||
|
bits_required = abs(value + 1).bit_length()
|
||||||
|
else:
|
||||||
|
bits_required = value.bit_length()
|
||||||
|
if bits_required % 8 == 0:
|
||||||
|
bits_required += 1
|
||||||
|
else:
|
||||||
|
bits_required = value.bit_length()
|
||||||
|
width = math.ceil(bits_required / 8) or 1
|
||||||
|
return value.to_bytes(width, byteorder='big', signed=signed)
|
||||||
|
|
||||||
|
def int_from_bytes(value, signed=False):
|
||||||
|
"""
|
||||||
|
Converts a byte string to an integer
|
||||||
|
|
||||||
|
:param value:
|
||||||
|
The byte string to convert
|
||||||
|
|
||||||
|
:param signed:
|
||||||
|
If the byte string should be interpreted using two's complement
|
||||||
|
|
||||||
|
:return:
|
||||||
|
An integer
|
||||||
|
"""
|
||||||
|
|
||||||
|
return int.from_bytes(value, 'big', signed=signed)
|
||||||
|
|
||||||
|
|
||||||
|
def _format_offset(off):
|
||||||
|
"""
|
||||||
|
Format a timedelta into "[+-]HH:MM" format or "" for None
|
||||||
|
"""
|
||||||
|
|
||||||
|
if off is None:
|
||||||
|
return ''
|
||||||
|
mins = off.days * 24 * 60 + off.seconds // 60
|
||||||
|
sign = '-' if mins < 0 else '+'
|
||||||
|
return sign + '%02d:%02d' % divmod(abs(mins), 60)
|
||||||
|
|
||||||
|
|
||||||
|
class _UtcWithDst(tzinfo):
|
||||||
|
"""
|
||||||
|
Utc class where dst does not return None; required for astimezone
|
||||||
|
"""
|
||||||
|
|
||||||
|
def tzname(self, dt):
|
||||||
|
return 'UTC'
|
||||||
|
|
||||||
|
def utcoffset(self, dt):
|
||||||
|
return timedelta(0)
|
||||||
|
|
||||||
|
def dst(self, dt):
|
||||||
|
return timedelta(0)
|
||||||
|
|
||||||
|
|
||||||
|
utc_with_dst = _UtcWithDst()
|
||||||
|
|
||||||
|
_timezone_cache = {}
|
||||||
|
|
||||||
|
|
||||||
|
def create_timezone(offset):
|
||||||
|
"""
|
||||||
|
Returns a new datetime.timezone object with the given offset.
|
||||||
|
Uses cached objects if possible.
|
||||||
|
|
||||||
|
:param offset:
|
||||||
|
A datetime.timedelta object; It needs to be in full minutes and between -23:59 and +23:59.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A datetime.timezone object
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
tz = _timezone_cache[offset]
|
||||||
|
except KeyError:
|
||||||
|
tz = _timezone_cache[offset] = timezone(offset)
|
||||||
|
return tz
|
||||||
|
|
||||||
|
|
||||||
|
class extended_date(object):
|
||||||
|
"""
|
||||||
|
A datetime.datetime-like object that represents the year 0. This is just
|
||||||
|
to handle 0000-01-01 found in some certificates. Python's datetime does
|
||||||
|
not support year 0.
|
||||||
|
|
||||||
|
The proleptic gregorian calendar repeats itself every 400 years. Therefore,
|
||||||
|
the simplest way to format is to substitute year 2000.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, year, month, day):
|
||||||
|
"""
|
||||||
|
:param year:
|
||||||
|
The integer 0
|
||||||
|
|
||||||
|
:param month:
|
||||||
|
An integer from 1 to 12
|
||||||
|
|
||||||
|
:param day:
|
||||||
|
An integer from 1 to 31
|
||||||
|
"""
|
||||||
|
|
||||||
|
if year != 0:
|
||||||
|
raise ValueError('year must be 0')
|
||||||
|
|
||||||
|
self._y2k = date(2000, month, day)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def year(self):
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
The integer 0
|
||||||
|
"""
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def month(self):
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
An integer from 1 to 12
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self._y2k.month
|
||||||
|
|
||||||
|
@property
|
||||||
|
def day(self):
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
An integer from 1 to 31
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self._y2k.day
|
||||||
|
|
||||||
|
def strftime(self, format):
|
||||||
|
"""
|
||||||
|
Formats the date using strftime()
|
||||||
|
|
||||||
|
:param format:
|
||||||
|
A strftime() format string
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A str, the formatted date as a unicode string
|
||||||
|
in Python 3 and a byte string in Python 2
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Format the date twice, once with year 2000, once with year 4000.
|
||||||
|
# The only differences in the result will be in the millennium. Find them and replace by zeros.
|
||||||
|
y2k = self._y2k.strftime(format)
|
||||||
|
y4k = self._y2k.replace(year=4000).strftime(format)
|
||||||
|
return ''.join('0' if (c2, c4) == ('2', '4') else c2 for c2, c4 in zip(y2k, y4k))
|
||||||
|
|
||||||
|
def isoformat(self):
|
||||||
|
"""
|
||||||
|
Formats the date as %Y-%m-%d
|
||||||
|
|
||||||
|
:return:
|
||||||
|
The date formatted to %Y-%m-%d as a unicode string in Python 3
|
||||||
|
and a byte string in Python 2
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.strftime('0000-%m-%d')
|
||||||
|
|
||||||
|
def replace(self, year=None, month=None, day=None):
|
||||||
|
"""
|
||||||
|
Returns a new datetime.date or asn1crypto.util.extended_date
|
||||||
|
object with the specified components replaced
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A datetime.date or asn1crypto.util.extended_date object
|
||||||
|
"""
|
||||||
|
|
||||||
|
if year is None:
|
||||||
|
year = self.year
|
||||||
|
if month is None:
|
||||||
|
month = self.month
|
||||||
|
if day is None:
|
||||||
|
day = self.day
|
||||||
|
|
||||||
|
if year > 0:
|
||||||
|
cls = date
|
||||||
|
else:
|
||||||
|
cls = extended_date
|
||||||
|
|
||||||
|
return cls(
|
||||||
|
year,
|
||||||
|
month,
|
||||||
|
day
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
A str representing this extended_date, e.g. "0000-01-01"
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.strftime('%Y-%m-%d')
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
"""
|
||||||
|
Compare two extended_date objects
|
||||||
|
|
||||||
|
:param other:
|
||||||
|
The other extended_date to compare to
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A boolean
|
||||||
|
"""
|
||||||
|
|
||||||
|
# datetime.date object wouldn't compare equal because it can't be year 0
|
||||||
|
if not isinstance(other, self.__class__):
|
||||||
|
return False
|
||||||
|
return self.__cmp__(other) == 0
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
"""
|
||||||
|
Compare two extended_date objects
|
||||||
|
|
||||||
|
:param other:
|
||||||
|
The other extended_date to compare to
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A boolean
|
||||||
|
"""
|
||||||
|
|
||||||
|
return not self.__eq__(other)
|
||||||
|
|
||||||
|
def _comparison_error(self, other):
|
||||||
|
raise TypeError(unwrap(
|
||||||
|
'''
|
||||||
|
An asn1crypto.util.extended_date object can only be compared to
|
||||||
|
an asn1crypto.util.extended_date or datetime.date object, not %s
|
||||||
|
''',
|
||||||
|
type_name(other)
|
||||||
|
))
|
||||||
|
|
||||||
|
def __cmp__(self, other):
|
||||||
|
"""
|
||||||
|
Compare two extended_date or datetime.date objects
|
||||||
|
|
||||||
|
:param other:
|
||||||
|
The other extended_date object to compare to
|
||||||
|
|
||||||
|
:return:
|
||||||
|
An integer smaller than, equal to, or larger than 0
|
||||||
|
"""
|
||||||
|
|
||||||
|
# self is year 0, other is >= year 1
|
||||||
|
if isinstance(other, date):
|
||||||
|
return -1
|
||||||
|
|
||||||
|
if not isinstance(other, self.__class__):
|
||||||
|
self._comparison_error(other)
|
||||||
|
|
||||||
|
if self._y2k < other._y2k:
|
||||||
|
return -1
|
||||||
|
if self._y2k > other._y2k:
|
||||||
|
return 1
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def __lt__(self, other):
|
||||||
|
return self.__cmp__(other) < 0
|
||||||
|
|
||||||
|
def __le__(self, other):
|
||||||
|
return self.__cmp__(other) <= 0
|
||||||
|
|
||||||
|
def __gt__(self, other):
|
||||||
|
return self.__cmp__(other) > 0
|
||||||
|
|
||||||
|
def __ge__(self, other):
|
||||||
|
return self.__cmp__(other) >= 0
|
||||||
|
|
||||||
|
|
||||||
|
class extended_datetime(object):
|
||||||
|
"""
|
||||||
|
A datetime.datetime-like object that represents the year 0. This is just
|
||||||
|
to handle 0000-01-01 found in some certificates. Python's datetime does
|
||||||
|
not support year 0.
|
||||||
|
|
||||||
|
The proleptic gregorian calendar repeats itself every 400 years. Therefore,
|
||||||
|
the simplest way to format is to substitute year 2000.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# There are 97 leap days during 400 years.
|
||||||
|
DAYS_IN_400_YEARS = 400 * 365 + 97
|
||||||
|
DAYS_IN_2000_YEARS = 5 * DAYS_IN_400_YEARS
|
||||||
|
|
||||||
|
def __init__(self, year, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
:param year:
|
||||||
|
The integer 0
|
||||||
|
|
||||||
|
:param args:
|
||||||
|
Other positional arguments; see datetime.datetime.
|
||||||
|
|
||||||
|
:param kwargs:
|
||||||
|
Other keyword arguments; see datetime.datetime.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if year != 0:
|
||||||
|
raise ValueError('year must be 0')
|
||||||
|
|
||||||
|
self._y2k = datetime(2000, *args, **kwargs)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def year(self):
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
The integer 0
|
||||||
|
"""
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def month(self):
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
An integer from 1 to 12
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self._y2k.month
|
||||||
|
|
||||||
|
@property
|
||||||
|
def day(self):
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
An integer from 1 to 31
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self._y2k.day
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hour(self):
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
An integer from 1 to 24
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self._y2k.hour
|
||||||
|
|
||||||
|
@property
|
||||||
|
def minute(self):
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
An integer from 1 to 60
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self._y2k.minute
|
||||||
|
|
||||||
|
@property
|
||||||
|
def second(self):
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
An integer from 1 to 60
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self._y2k.second
|
||||||
|
|
||||||
|
@property
|
||||||
|
def microsecond(self):
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
An integer from 0 to 999999
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self._y2k.microsecond
|
||||||
|
|
||||||
|
@property
|
||||||
|
def tzinfo(self):
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
If object is timezone aware, a datetime.tzinfo object, else None.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self._y2k.tzinfo
|
||||||
|
|
||||||
|
def utcoffset(self):
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
If object is timezone aware, a datetime.timedelta object, else None.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self._y2k.utcoffset()
|
||||||
|
|
||||||
|
def time(self):
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
A datetime.time object
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self._y2k.time()
|
||||||
|
|
||||||
|
def date(self):
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
An asn1crypto.util.extended_date of the date
|
||||||
|
"""
|
||||||
|
|
||||||
|
return extended_date(0, self.month, self.day)
|
||||||
|
|
||||||
|
def strftime(self, format):
|
||||||
|
"""
|
||||||
|
Performs strftime(), always returning a str
|
||||||
|
|
||||||
|
:param format:
|
||||||
|
A strftime() format string
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A str of the formatted datetime
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Format the datetime twice, once with year 2000, once with year 4000.
|
||||||
|
# The only differences in the result will be in the millennium. Find them and replace by zeros.
|
||||||
|
y2k = self._y2k.strftime(format)
|
||||||
|
y4k = self._y2k.replace(year=4000).strftime(format)
|
||||||
|
return ''.join('0' if (c2, c4) == ('2', '4') else c2 for c2, c4 in zip(y2k, y4k))
|
||||||
|
|
||||||
|
def isoformat(self, sep='T'):
|
||||||
|
"""
|
||||||
|
Formats the date as "%Y-%m-%d %H:%M:%S" with the sep param between the
|
||||||
|
date and time portions
|
||||||
|
|
||||||
|
:param set:
|
||||||
|
A single character of the separator to place between the date and
|
||||||
|
time
|
||||||
|
|
||||||
|
:return:
|
||||||
|
The formatted datetime as a unicode string in Python 3 and a byte
|
||||||
|
string in Python 2
|
||||||
|
"""
|
||||||
|
|
||||||
|
s = '0000-%02d-%02d%c%02d:%02d:%02d' % (self.month, self.day, sep, self.hour, self.minute, self.second)
|
||||||
|
if self.microsecond:
|
||||||
|
s += '.%06d' % self.microsecond
|
||||||
|
return s + _format_offset(self.utcoffset())
|
||||||
|
|
||||||
|
def replace(self, year=None, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Returns a new datetime.datetime or asn1crypto.util.extended_datetime
|
||||||
|
object with the specified components replaced
|
||||||
|
|
||||||
|
:param year:
|
||||||
|
The new year to substitute. None to keep it.
|
||||||
|
|
||||||
|
:param args:
|
||||||
|
Other positional arguments; see datetime.datetime.replace.
|
||||||
|
|
||||||
|
:param kwargs:
|
||||||
|
Other keyword arguments; see datetime.datetime.replace.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A datetime.datetime or asn1crypto.util.extended_datetime object
|
||||||
|
"""
|
||||||
|
|
||||||
|
if year:
|
||||||
|
return self._y2k.replace(year, *args, **kwargs)
|
||||||
|
|
||||||
|
return extended_datetime.from_y2k(self._y2k.replace(2000, *args, **kwargs))
|
||||||
|
|
||||||
|
def astimezone(self, tz):
|
||||||
|
"""
|
||||||
|
Convert this extended_datetime to another timezone.
|
||||||
|
|
||||||
|
:param tz:
|
||||||
|
A datetime.tzinfo object.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A new extended_datetime or datetime.datetime object
|
||||||
|
"""
|
||||||
|
|
||||||
|
return extended_datetime.from_y2k(self._y2k.astimezone(tz))
|
||||||
|
|
||||||
|
def timestamp(self):
|
||||||
|
"""
|
||||||
|
Return POSIX timestamp. Only supported in python >= 3.3
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A float representing the seconds since 1970-01-01 UTC. This will be a negative value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self._y2k.timestamp() - self.DAYS_IN_2000_YEARS * 86400
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
A str representing this extended_datetime, e.g. "0000-01-01 00:00:00.000001-10:00"
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.isoformat(sep=' ')
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
"""
|
||||||
|
Compare two extended_datetime objects
|
||||||
|
|
||||||
|
:param other:
|
||||||
|
The other extended_datetime to compare to
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A boolean
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Only compare against other datetime or extended_datetime objects
|
||||||
|
if not isinstance(other, (self.__class__, datetime)):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Offset-naive and offset-aware datetimes are never the same
|
||||||
|
if (self.tzinfo is None) != (other.tzinfo is None):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return self.__cmp__(other) == 0
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
"""
|
||||||
|
Compare two extended_datetime objects
|
||||||
|
|
||||||
|
:param other:
|
||||||
|
The other extended_datetime to compare to
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A boolean
|
||||||
|
"""
|
||||||
|
|
||||||
|
return not self.__eq__(other)
|
||||||
|
|
||||||
|
def _comparison_error(self, other):
|
||||||
|
"""
|
||||||
|
Raises a TypeError about the other object not being suitable for
|
||||||
|
comparison
|
||||||
|
|
||||||
|
:param other:
|
||||||
|
The object being compared to
|
||||||
|
"""
|
||||||
|
|
||||||
|
raise TypeError(unwrap(
|
||||||
|
'''
|
||||||
|
An asn1crypto.util.extended_datetime object can only be compared to
|
||||||
|
an asn1crypto.util.extended_datetime or datetime.datetime object,
|
||||||
|
not %s
|
||||||
|
''',
|
||||||
|
type_name(other)
|
||||||
|
))
|
||||||
|
|
||||||
|
def __cmp__(self, other):
|
||||||
|
"""
|
||||||
|
Compare two extended_datetime or datetime.datetime objects
|
||||||
|
|
||||||
|
:param other:
|
||||||
|
The other extended_datetime or datetime.datetime object to compare to
|
||||||
|
|
||||||
|
:return:
|
||||||
|
An integer smaller than, equal to, or larger than 0
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not isinstance(other, (self.__class__, datetime)):
|
||||||
|
self._comparison_error(other)
|
||||||
|
|
||||||
|
if (self.tzinfo is None) != (other.tzinfo is None):
|
||||||
|
raise TypeError("can't compare offset-naive and offset-aware datetimes")
|
||||||
|
|
||||||
|
diff = self - other
|
||||||
|
zero = timedelta(0)
|
||||||
|
if diff < zero:
|
||||||
|
return -1
|
||||||
|
if diff > zero:
|
||||||
|
return 1
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def __lt__(self, other):
|
||||||
|
return self.__cmp__(other) < 0
|
||||||
|
|
||||||
|
def __le__(self, other):
|
||||||
|
return self.__cmp__(other) <= 0
|
||||||
|
|
||||||
|
def __gt__(self, other):
|
||||||
|
return self.__cmp__(other) > 0
|
||||||
|
|
||||||
|
def __ge__(self, other):
|
||||||
|
return self.__cmp__(other) >= 0
|
||||||
|
|
||||||
|
def __add__(self, other):
|
||||||
|
"""
|
||||||
|
Adds a timedelta
|
||||||
|
|
||||||
|
:param other:
|
||||||
|
A datetime.timedelta object to add.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A new extended_datetime or datetime.datetime object.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return extended_datetime.from_y2k(self._y2k + other)
|
||||||
|
|
||||||
|
def __sub__(self, other):
|
||||||
|
"""
|
||||||
|
Subtracts a timedelta or another datetime.
|
||||||
|
|
||||||
|
:param other:
|
||||||
|
A datetime.timedelta or datetime.datetime or extended_datetime object to subtract.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
If a timedelta is passed, a new extended_datetime or datetime.datetime object.
|
||||||
|
Else a datetime.timedelta object.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if isinstance(other, timedelta):
|
||||||
|
return extended_datetime.from_y2k(self._y2k - other)
|
||||||
|
|
||||||
|
if isinstance(other, extended_datetime):
|
||||||
|
return self._y2k - other._y2k
|
||||||
|
|
||||||
|
if isinstance(other, datetime):
|
||||||
|
return self._y2k - other - timedelta(days=self.DAYS_IN_2000_YEARS)
|
||||||
|
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __rsub__(self, other):
|
||||||
|
return -(self - other)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_y2k(cls, value):
|
||||||
|
"""
|
||||||
|
Revert substitution of year 2000.
|
||||||
|
|
||||||
|
:param value:
|
||||||
|
A datetime.datetime object which is 2000 years in the future.
|
||||||
|
:return:
|
||||||
|
A new extended_datetime or datetime.datetime object.
|
||||||
|
"""
|
||||||
|
|
||||||
|
year = value.year - 2000
|
||||||
|
|
||||||
|
if year > 0:
|
||||||
|
new_cls = datetime
|
||||||
|
else:
|
||||||
|
new_cls = cls
|
||||||
|
|
||||||
|
return new_cls(
|
||||||
|
year,
|
||||||
|
value.month,
|
||||||
|
value.day,
|
||||||
|
value.hour,
|
||||||
|
value.minute,
|
||||||
|
value.second,
|
||||||
|
value.microsecond,
|
||||||
|
value.tzinfo
|
||||||
|
)
|
@ -0,0 +1,6 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals, division, absolute_import, print_function
|
||||||
|
|
||||||
|
|
||||||
|
__version__ = '1.5.1'
|
||||||
|
__version_info__ = (1, 5, 1)
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,496 @@
|
|||||||
|
# changelog
|
||||||
|
|
||||||
|
## 1.5.1
|
||||||
|
|
||||||
|
- Handle RSASSA-PSS in `keys.PrivateKeyInfo.bit_size` and
|
||||||
|
`keys.PublicKeyInfo.bit_size`
|
||||||
|
- Handle RSASSA-PSS in `keys.PrivateKeyInfo.wrap` and
|
||||||
|
`keys.PublicKeyInfo.wrap`
|
||||||
|
- Updated docs for `keys.PrivateKeyInfo.algorithm` and
|
||||||
|
`keys.PublicKeyInfo.algorithm` to reflect that they can return
|
||||||
|
`"rsassa_pss"`
|
||||||
|
|
||||||
|
## 1.5.0
|
||||||
|
|
||||||
|
- Fix `tsp.TimeStampAndCRL` to be a `core.Sequence` instead of a
|
||||||
|
`core.SequenceOf` *via @joernheissler*
|
||||||
|
- Added OIDs for Edwards curves from RFC 8410 - via @MatthiasValvekens
|
||||||
|
- Fixed convenience attributes on `algos.EncryptionAlgorithm` when the
|
||||||
|
algorithm is RC2 *via @joernheissler*
|
||||||
|
- Added Microsoft OIDs `microsoft_enrollment_csp_provider`
|
||||||
|
(`1.3.6.1.4.1.311.13.2.2`), `microsoft_os_version`
|
||||||
|
(`1.3.6.1.4.1.311.13.2.3`) and `microsoft_request_client_info`
|
||||||
|
(`1.3.6.1.4.1.311.21.20`)
|
||||||
|
to `csr.CSRAttributeType` along with supporting extension structures
|
||||||
|
*via @qha*
|
||||||
|
- Added Microsoft OID `microsoft_enroll_certtype` (`1.3.6.1.4.1.311.20.2`)
|
||||||
|
to `x509.ExtensionId` *via @qha*
|
||||||
|
- Fixed a few bugs with parsing indefinite-length encodings *via @davidben*
|
||||||
|
- Added various bounds checks to parsing engine *via @davidben*
|
||||||
|
- Fixed a bug with tags not always being minimally encoded *via @davidben*
|
||||||
|
- Fixed `cms.RoleSyntax`, `cms.SecurityCategory` and `cms.AttCertIssuer` to
|
||||||
|
have explicit instead of implicit tagging *via @MatthiasValvekens*
|
||||||
|
- Fixed tagging of, and default value for fields in `cms.Clearance` *via
|
||||||
|
@MatthiasValvekens*
|
||||||
|
- Fixed calling `.dump(force=True)` when the value has undefined/unknown
|
||||||
|
`core.Sequence` fields. Previously the value would be truncated, now
|
||||||
|
the existing encoding is preserved.
|
||||||
|
- Added sMIME capabilities (`1.2.840.113549.1.9.15`) support from RFC 2633
|
||||||
|
to `cms.CMSAttribute` *via Hellzed*
|
||||||
|
|
||||||
|
## 1.4.0
|
||||||
|
|
||||||
|
- `core.ObjectIdentifier` and all derived classes now obey X.660 §7.6 and
|
||||||
|
thus restrict the first arc to 0 to 2, and the second arc to less than
|
||||||
|
40 if the first arc is 0 or 1. This also fixes parsing of OIDs where the
|
||||||
|
first arc is 2 and the second arc is greater than 39.
|
||||||
|
- Fixed `keys.PublicKeyInfo.bit_size` to return an int rather than a float
|
||||||
|
on Python 3 when working with elliptic curve keys
|
||||||
|
- Fixed the `asn1crypto-tests` sdist on PyPi to work properly to generate a
|
||||||
|
.whl
|
||||||
|
|
||||||
|
## 1.3.0
|
||||||
|
|
||||||
|
- Added `encrypt_key_pref` (`1.2.840.113549.1.9.16.2.11`) to
|
||||||
|
`cms.CMSAttributeType()`, along with related structures
|
||||||
|
- Added Brainpool curves from RFC 5639 to `keys.NamedCurve()`
|
||||||
|
- Fixed `x509.Certificate().subject_directory_attributes_value`
|
||||||
|
- Fixed some incorrectly computed minimum elliptic curve primary key
|
||||||
|
encoding sizes in `keys.NamedCurve()`
|
||||||
|
- Fixed a `TypeError` when trying to call `.untag()` or `.copy()` on a
|
||||||
|
`core.UTCTime()` or `core.GeneralizedTime()`, or a value containing one,
|
||||||
|
when using Python 2
|
||||||
|
|
||||||
|
## 1.2.0
|
||||||
|
|
||||||
|
- Added `asn1crypto.load_order()`, which returns a `list` of unicode strings
|
||||||
|
of the names of the fully-qualified module names for all of submodules of
|
||||||
|
the package. The module names are listed in their dependency load order.
|
||||||
|
This is primarily intended for the sake of implementing hot reloading.
|
||||||
|
|
||||||
|
## 1.1.0
|
||||||
|
|
||||||
|
- Added User ID (`0.9.2342.19200300.100.1.1`) to `x509.NameType()`
|
||||||
|
- Added various EC named curves to `keys.NamedCurve()`
|
||||||
|
|
||||||
|
## 1.0.1
|
||||||
|
|
||||||
|
- Fix an absolute import in `keys` to a relative import
|
||||||
|
|
||||||
|
## 1.0.0
|
||||||
|
|
||||||
|
- Backwards Compatibility Breaks
|
||||||
|
- `cms.KeyEncryptionAlgorithmId().native` now returns the value
|
||||||
|
`"rsaes_pkcs1v15"` for OID `1.2.840.113549.1.1.1` instead of `"rsa"`
|
||||||
|
- Removed functionality to calculate public key values from private key
|
||||||
|
values. Alternatives have been added to oscrypto.
|
||||||
|
- `keys.PrivateKeyInfo().unwrap()` is now
|
||||||
|
`oscrypto.asymmetric.PrivateKey().unwrap()`
|
||||||
|
- `keys.PrivateKeyInfo().public_key` is now
|
||||||
|
`oscrypto.asymmetric.PrivateKey().public_key.unwrap()`
|
||||||
|
- `keys.PrivateKeyInfo().public_key_info` is now
|
||||||
|
`oscrypto.asymmetric.PrivateKey().public_key.asn1`
|
||||||
|
- `keys.PrivateKeyInfo().fingerprint` is now
|
||||||
|
`oscrypto.asymmetric.PrivateKey().fingerprint`
|
||||||
|
- `keys.PublicKeyInfo().unwrap()` is now
|
||||||
|
`oscrypto.asymmetric.PublicKey().unwrap()`
|
||||||
|
- `keys.PublicKeyInfo().fingerprint` is now
|
||||||
|
`oscrypto.asymmetric.PublicKey().fingerprint`
|
||||||
|
- Enhancements
|
||||||
|
- Significantly improved parsing of `core.UTCTime()` and
|
||||||
|
`core.GeneralizedTime()` values that include timezones and fractional
|
||||||
|
seconds
|
||||||
|
- `util.timezone` has a more complete implementation
|
||||||
|
- `core.Choice()` may now be constructed by a 2-element tuple or a 1-key
|
||||||
|
dict
|
||||||
|
- Added `x509.Certificate().not_valid_before` and
|
||||||
|
`x509.Certificate().not_valid_after`
|
||||||
|
- Added `core.BitString().unused_bits`
|
||||||
|
- Added `keys.NamedCurve.register()` for non-mainstream curve OIDs
|
||||||
|
- No longer try to load optional performance dependency, `libcrypto`,
|
||||||
|
on Mac or Linux
|
||||||
|
- `ocsp.CertStatus().native` will now return meaningful unicode string
|
||||||
|
values when the status choice is `"good"` or `"unknown"`. Previously
|
||||||
|
both returned `None` due to the way the structure was designed.
|
||||||
|
- Add support for explicit RSA SSA PSS (`1.2.840.113549.1.1.10`) to
|
||||||
|
`keys.PublicKeyInfo()` and `keys.PrivateKeyInfo()`
|
||||||
|
- Added structures for nested SHA-256 Windows PE signatures to
|
||||||
|
`cms.CMSAttribute()`
|
||||||
|
- Added RC4 (`1.2.840.113549.3.4`) to `algos.EncryptionAlgorithmId()`
|
||||||
|
- Added secp256k1 (`1.3.132.0.10`) to `keys.NamedCurve()`
|
||||||
|
- Added SHA-3 and SHAKE OIDs to `algos.DigestAlgorithmId()` and
|
||||||
|
`algos.HmacAlgorithmId()`
|
||||||
|
- Added RSA ES OAEP (`1.2.840.113549.1.1.7`) to
|
||||||
|
`cms.KeyEncryptionAlgorithmId()`
|
||||||
|
- Add IKE Intermediate (`1.3.6.1.5.5.8.2.2`) to `x509.KeyPurposeId()`
|
||||||
|
- `x509.EmailAddress()` and `x509.DNSName()` now handle invalidly-encoded
|
||||||
|
values using tags for `core.PrintableString()` and `core.UTF8String()`
|
||||||
|
- Add parameter structue from RFC 5084 for AES-CCM to
|
||||||
|
`algos.EncryptionAlgorithm()`
|
||||||
|
- Improved robustness of parsing broken `core.Sequence()` and
|
||||||
|
`core.SequenceOf()` values
|
||||||
|
- Bug Fixes
|
||||||
|
- Fixed encoding of tag values over 30
|
||||||
|
- `core.IntegerBitString()` and `core.IntegerOctetString()` now restrict
|
||||||
|
values to non-negative integers since negative values are not
|
||||||
|
implemented
|
||||||
|
- When copying or dumping a BER-encoded indefinite-length value,
|
||||||
|
automatically force re-encoding to DER. *To ensure all nested values are
|
||||||
|
always DER-encoded, `.dump(True)` must be called.*
|
||||||
|
- Fix `UnboundLocalError` when calling `x509.IPAddress().native` on an
|
||||||
|
encoded value that has a length of zero
|
||||||
|
- Fixed passing `class_` via unicode string name to `core.Asn1Value()`
|
||||||
|
- Fixed a bug where EC private keys with leading null bytes would be
|
||||||
|
encoded in `keys.ECPrivateKey()` more narrowly than RFC 5915 requires
|
||||||
|
- Fixed some edge-case bugs in `util.int_to_bytes()`
|
||||||
|
- `x509.URI()` now only normalizes values when comparing
|
||||||
|
- Fixed BER-decoding of indefinite length `core.BitString()`
|
||||||
|
- Fixed DER-encoding of empty `core.BitString()`
|
||||||
|
- Fixed a missing return value for `core.Choice().parse()`
|
||||||
|
- Fixed `core.Choice().contents` working when the chosen alternative is a
|
||||||
|
`core.Choice()` also
|
||||||
|
- Fixed parsing and encoding of nested `core.Choice()` objects
|
||||||
|
- Fixed a bug causing `core.ObjectIdentifier().native` to sometimes not
|
||||||
|
map the OID
|
||||||
|
- Packaging
|
||||||
|
- `wheel`, `sdist` and `bdist_egg` releases now all include LICENSE,
|
||||||
|
`sdist` includes docs
|
||||||
|
- Added `asn1crypto_tests` package to PyPi
|
||||||
|
|
||||||
|
## 0.24.0
|
||||||
|
|
||||||
|
- `x509.Certificate().self_signed` will no longer return `"yes"` under any
|
||||||
|
circumstances. This helps prevent confusion since the library does not
|
||||||
|
verify the signature. Instead a library like oscrypto should be used
|
||||||
|
to confirm if a certificate is self-signed.
|
||||||
|
- Added various OIDs to `x509.KeyPurposeId()`
|
||||||
|
- Added `x509.Certificate().private_key_usage_period_value`
|
||||||
|
- Added structures for parsing common subject directory attributes for
|
||||||
|
X.509 certificates, including `x509.SubjectDirectoryAttribute()`
|
||||||
|
- Added `algos.AnyAlgorithmIdentifier()` for situations where an
|
||||||
|
algorithm identifier may contain a digest, signed digest or encryption
|
||||||
|
algorithm OID
|
||||||
|
- Fixed a bug with `x509.Certificate().subject_directory_attributes_value`
|
||||||
|
not returning the correct value
|
||||||
|
- Fixed a bug where explicitly-tagged fields in a `core.Sequence()` would
|
||||||
|
not function properly when the field had a default value
|
||||||
|
- Fixed a bug with type checking in `pem.armor()`
|
||||||
|
|
||||||
|
## 0.23.0
|
||||||
|
|
||||||
|
- Backwards compatibility break: the `tag_type`, `explicit_tag` and
|
||||||
|
`explicit_class` attributes on `core.Asn1Value` no longer exist and were
|
||||||
|
replaced by the `implicit` and `explicit` attributes. Field param dicts
|
||||||
|
may use the new `explicit` and `implicit` keys, or the old `tag_type` and
|
||||||
|
`tag` keys. The attribute changes will likely to have little to no impact
|
||||||
|
since they were primarily an implementation detail.
|
||||||
|
- Teletex strings used inside of X.509 certificates are now interpreted
|
||||||
|
using Windows-1252 (a superset of ISO-8859-1). This enables compatibility
|
||||||
|
with certificates generated by OpenSSL. Strict parsing of Teletex strings
|
||||||
|
can be retained by using the `x509.strict_teletex()` context manager.
|
||||||
|
- Added support for nested explicit tagging, supporting values that are
|
||||||
|
defined with explicit tagging and then added as a field of another
|
||||||
|
structure using explicit tagging.
|
||||||
|
- Fixed a `UnicodeDecodeError` when trying to find the (optional) dependency
|
||||||
|
OpenSSL on Python 2
|
||||||
|
- Fixed `next_update` field of `crl.TbsCertList` to be optional
|
||||||
|
- Added the `x509.Certificate.sha256_fingerprint` property
|
||||||
|
- `x509.Certificate.ocsp_urls` and `x509.DistributionPoint.url` will now
|
||||||
|
return `https://`, `ldap://` and `ldaps://` URLs in addition to `http://`.
|
||||||
|
- Added CMS Attribute Protection definitions from RFC 6211
|
||||||
|
- Added OIDs from RFC 6962
|
||||||
|
|
||||||
|
## 0.22.0
|
||||||
|
|
||||||
|
- Added `parser.peek()`
|
||||||
|
- Implemented proper support for BER-encoded indefinite length strings of
|
||||||
|
all kinds - `core.BitString`, `core.OctetString` and all of the `core`
|
||||||
|
classes that are natively represented as Python unicode strings
|
||||||
|
- Fixed a bug with encoding LDAP URLs in `x509.URI`
|
||||||
|
- Correct `x509.DNSName` to allow a leading `.`, such as when used with
|
||||||
|
`x509.NameConstraints`
|
||||||
|
- Fixed an issue with dumping the parsed contents of `core.Any` when
|
||||||
|
explicitly tagged
|
||||||
|
- Custom `setup.py clean` now accepts the short `-a` flag for compatibility
|
||||||
|
|
||||||
|
## 0.21.1
|
||||||
|
|
||||||
|
- Fixed a regression where explicit tagging of a field containing a
|
||||||
|
`core.Choice` would result in an incorrect header
|
||||||
|
- Fixed a bug where an `IndexError` was being raised instead of a `ValueError`
|
||||||
|
when a value was truncated to not include enough bytes for the header
|
||||||
|
- Corrected the spec for the `value` field of `pkcs12.Attribute`
|
||||||
|
- Added support for `2.16.840.1.113894.746875.1.1` OID to
|
||||||
|
`pkcs12.AttributeType`
|
||||||
|
|
||||||
|
## 0.21.0
|
||||||
|
|
||||||
|
- Added `core.load()` for loading standard, universal types without knowing
|
||||||
|
the spec beforehand
|
||||||
|
- Added a `strict` keyword arg to the various `load()` methods and functions in
|
||||||
|
`core` that checks for trailing data and raises a `ValueError` when found
|
||||||
|
- Added `asn1crypto.parser` submodule with `emit()` and `parse()` functions for
|
||||||
|
low-level integration
|
||||||
|
- Added `asn1crypto.version` for version introspection without side-effects
|
||||||
|
- Added `algos.DSASignature`
|
||||||
|
- Fixed a bug with the `_header` attribute of explicitly-tagged values only
|
||||||
|
containing the explicit tag header instead of both the explicit tag header
|
||||||
|
and the encapsulated value header
|
||||||
|
|
||||||
|
## 0.20.0
|
||||||
|
|
||||||
|
- Added support for year 0
|
||||||
|
- Added the OID for unique identifier to `x509.NameType`
|
||||||
|
- Fixed a bug creating the native representation of a `core.BitString` with
|
||||||
|
leading null bytes
|
||||||
|
- Added a `.cast()` method to allow converting between different
|
||||||
|
representations of the same data, e.g. `core.BitString` and
|
||||||
|
`core.OctetBitString`
|
||||||
|
|
||||||
|
## 0.19.0
|
||||||
|
|
||||||
|
- Force `algos.DigestAlgorithm` to encoding `parameters` as `Null` when the
|
||||||
|
`algorithm` is `sha1`, `sha224`, `sha256`, `sha384` or `sha512` per RFC 4055
|
||||||
|
- Resolved an issue where a BER-encoded indefinite-length value could not be
|
||||||
|
properly parsed when embedded inside of a `core.Sequence` or `core.Set`
|
||||||
|
- Fix `x509.Name.build()` to properly handle dotted OID type values
|
||||||
|
- `core.Choice` can now be constructed from a single-element `dict` or a
|
||||||
|
two-element `tuple` to allow for better usability when constructing values
|
||||||
|
from native Python values
|
||||||
|
- All `core` objects can now be passed to `print()` with an exception being
|
||||||
|
raised
|
||||||
|
|
||||||
|
## 0.18.5
|
||||||
|
|
||||||
|
- Don't fail importing if `ctypes` or `_ctypes` is not available
|
||||||
|
|
||||||
|
## 0.18.4
|
||||||
|
|
||||||
|
- `core.Sequence` will now raise an exception when an unknown field is provided
|
||||||
|
- Prevent `UnicodeDecodeError` on Python 2 when calling
|
||||||
|
`core.OctetString.debug()`
|
||||||
|
- Corrected the default value for the `hash_algorithm` field of
|
||||||
|
`tsp.ESSCertIDv2`
|
||||||
|
- Fixed a bug constructing a `cms.SignedData` object
|
||||||
|
- Ensure that specific RSA OIDs are always paired with `parameters` set to
|
||||||
|
`core.Null`
|
||||||
|
|
||||||
|
## 0.18.3
|
||||||
|
|
||||||
|
- Fixed DER encoding of `core.BitString` when a `_map` is specified (i.e. a
|
||||||
|
"named bit list") to omit trailing zero bits. This fixes compliance of
|
||||||
|
various `x509` structures with RFC 5280.
|
||||||
|
- Corrected a side effect in `keys.PrivateKeyInfo.wrap()` that would cause the
|
||||||
|
original `keys.ECPrivateKey` structure to become corrupt
|
||||||
|
- `core.IntegerOctetString` now correctly encodes the integer as an unsigned
|
||||||
|
value when converting to bytes. Previously decoding was unsigned, but
|
||||||
|
encoding was signed.
|
||||||
|
- Fix `util.int_from_bytes()` on Python 2 to return `0` from an empty byte
|
||||||
|
string
|
||||||
|
|
||||||
|
## 0.18.2
|
||||||
|
|
||||||
|
- Allow `_perf` submodule to be removed from source tree when embedding
|
||||||
|
|
||||||
|
## 0.18.1
|
||||||
|
|
||||||
|
- Fixed DER encoding of `core.Set` and `core.SetOf`
|
||||||
|
- Fixed a bug in `x509.Name.build()` that could generate invalid DER encoding
|
||||||
|
- Improved exception messages when parsing nested structures via the `.native`
|
||||||
|
attribute
|
||||||
|
- `algos.SignedDigestAlgorithm` now ensures the `parameters` are set to
|
||||||
|
`Null` when `algorithm` is `sha224_rsa`, `sha256_rsa`, `sha384_rsa` or
|
||||||
|
`sha512_rsa`, per RFC 4055
|
||||||
|
- Corrected the definition of `pdf.AdobeTimestamp` to mark the
|
||||||
|
`requires_auth` field as optional
|
||||||
|
- Add support for the OID `1.2.840.113549.1.9.16.2.14` to
|
||||||
|
`cms.CMSAttributeType`
|
||||||
|
- Improve attribute support for `cms.AttributeCertificateV2`
|
||||||
|
- Handle `cms.AttributeCertificateV2` when incorrectly tagged as
|
||||||
|
`cms.AttributeCertificateV1` in `cms.CertificateChoices`
|
||||||
|
|
||||||
|
## 0.18.0
|
||||||
|
|
||||||
|
- Improved general parsing performance by 10-15%
|
||||||
|
- Add support for Windows XP
|
||||||
|
- Added `core.ObjectIdentifier.dotted` attribute to always return dotted
|
||||||
|
integer unicode string
|
||||||
|
- Added `core.ObjectIdentifier.map()` and `core.ObjectIdentifier.unmap()`
|
||||||
|
class methods to map dotted integer unicode strings to user-friendly unicode
|
||||||
|
strings and back
|
||||||
|
- Added various Apple OIDs to `x509.KeyPurposeId`
|
||||||
|
- Fixed a bug parsing nested indefinite-length-encoded values
|
||||||
|
- Fixed a bug with `x509.Certificate.issuer_alt_name_value` if it is the first
|
||||||
|
extension queried
|
||||||
|
- `keys.PublicKeyInfo.bit_size` and `keys.PrivateKeyInfo.bit_size` values are
|
||||||
|
now rounded up to the next closest multiple of 8
|
||||||
|
|
||||||
|
## 0.17.1
|
||||||
|
|
||||||
|
- Fix a bug in `x509.URI` parsing IRIs containing explicit port numbers on
|
||||||
|
Python 3.x
|
||||||
|
|
||||||
|
## 0.17.0
|
||||||
|
|
||||||
|
- Added `x509.TrustedCertificate` for handling OpenSSL auxiliary certificate
|
||||||
|
information appended after a certificate
|
||||||
|
- Added `core.Concat` class for situations such as `x509.TrustedCertificate`
|
||||||
|
- Allow "broken" X.509 certificates to use `core.IA5String` where an
|
||||||
|
`x509.DirectoryString` should be used instead
|
||||||
|
- Added `keys.PrivateKeyInfo.public_key_info` attribute
|
||||||
|
- Added a bunch of OIDs to `x509.KeyPurposeId`
|
||||||
|
|
||||||
|
## 0.16.0
|
||||||
|
|
||||||
|
- Added DH key exchange structures: `algos.KeyExchangeAlgorithm`,
|
||||||
|
`algos.KeyExchangeAlgorithmId` and `algos.DHParameters`.
|
||||||
|
- Added DH public key support to `keys.PublicKeyInfo`,
|
||||||
|
`keys.PublicKeyAlgorithm` and `keys.PublicKeyAlgorithmId`. New structures
|
||||||
|
include `keys.DomainParameters` and `keys.ValidationParms`.
|
||||||
|
|
||||||
|
## 0.15.1
|
||||||
|
|
||||||
|
- Fixed `cms.CMSAttributes` to be a `core.SetOf` instead of `core.SequenceOf`
|
||||||
|
- `cms.CMSAttribute` can now parse unknown attribute contrustruct without an
|
||||||
|
exception being raised
|
||||||
|
- `x509.PolicyMapping` now uses `x509.PolicyIdentifier` for field types
|
||||||
|
- Fixed `pdf.RevocationInfoArchival` so that all fields are now of the type
|
||||||
|
`core.SequenceOf` instead of a single value
|
||||||
|
- Added support for the `name_distinguisher`, `telephone_number` and
|
||||||
|
`organization_identifier` OIDs to `x509.Name`
|
||||||
|
- Fixed `x509.Name.native` to not accidentally create nested lists when three
|
||||||
|
of more values for a single type are part of the name
|
||||||
|
- `x509.Name.human_friendly` now reverses the order of fields when the data
|
||||||
|
in an `x509.Name` was encoded in most-specific to least-specific order, which
|
||||||
|
is the opposite of the standard way of least-specific to most-specific.
|
||||||
|
- `x509.NameType.human_friendly` no longer raises an exception when an
|
||||||
|
unknown OID is encountered
|
||||||
|
- Raise a `ValueError` when parsing a `core.Set` and an unknown field is
|
||||||
|
encountered
|
||||||
|
|
||||||
|
## 0.15.0
|
||||||
|
|
||||||
|
- Added support for the TLS feature extension from RFC 7633
|
||||||
|
- `x509.Name.build()` now accepts a keyword parameter `use_printable` to force
|
||||||
|
string encoding to be `core.PrintableString` instead of `core.UTF8String`
|
||||||
|
- Added the functions `util.uri_to_iri()` and `util.iri_to_uri()`
|
||||||
|
- Changed `algos.SignedDigestAlgorithmId` to use the preferred OIDs when
|
||||||
|
mapping a unicode string name to an OID. Previously there were multiple OIDs
|
||||||
|
for some algorithms, and different OIDs would sometimes be selected due to
|
||||||
|
the fact that the `_map` `dict` is not ordered.
|
||||||
|
|
||||||
|
## 0.14.1
|
||||||
|
|
||||||
|
- Fixed a bug generating `x509.Certificate.sha1_fingerprint` on Python 2
|
||||||
|
|
||||||
|
## 0.14.0
|
||||||
|
|
||||||
|
- Added the `x509.Certificate.sha1_fingerprint` attribute
|
||||||
|
|
||||||
|
## 0.13.0
|
||||||
|
|
||||||
|
- Backwards compatibility break: the native representation of some
|
||||||
|
`algos.EncryptionAlgorithmId` values changed. `aes128` became `aes128_cbc`,
|
||||||
|
`aes192` became `aes192_cbc` and `aes256` became `aes256_cbc`.
|
||||||
|
- Added more OIDs to `algos.EncryptionAlgorithmId`
|
||||||
|
- Added more OIDs to `cms.KeyEncryptionAlgorithmId`
|
||||||
|
- `x509.Name.human_friendly` now properly supports multiple values per
|
||||||
|
`x509.NameTypeAndValue` object
|
||||||
|
- Added `ocsp.OCSPResponse.basic_ocsp_response` and
|
||||||
|
`ocsp.OCSPResponse.response_data` properties
|
||||||
|
- Added `algos.EncryptionAlgorithm.encryption_mode` property
|
||||||
|
- Fixed a bug with parsing times containing timezone offsets in Python 3
|
||||||
|
- The `attributes` field of `csr.CertificationRequestInfo` is now optional,
|
||||||
|
for compatibility with other ASN.1 parsers
|
||||||
|
|
||||||
|
## 0.12.2
|
||||||
|
|
||||||
|
- Correct `core.Sequence.__setitem__()` so set `core.VOID` to an optional
|
||||||
|
field when `None` is set
|
||||||
|
|
||||||
|
## 0.12.1
|
||||||
|
|
||||||
|
- Fixed a `unicode`/`bytes` bug with `x509.URI.dump()` on Python 2
|
||||||
|
|
||||||
|
## 0.12.0
|
||||||
|
|
||||||
|
- Backwards Compatibility Break: `core.NoValue` was renamed to `core.Void` and
|
||||||
|
a singleton was added as `core.VOID`
|
||||||
|
- 20-30% improvement in parsing performance
|
||||||
|
- `core.Void` now implements `__nonzero__`
|
||||||
|
- `core.Asn1Value.copy()` now performs a deep copy
|
||||||
|
- All `core` value classes are now compatible with the `copy` module
|
||||||
|
- `core.SequenceOf` and `core.SetOf` now implement `__contains__`
|
||||||
|
- Added `x509.Name.__len__()`
|
||||||
|
- Fixed a bug where `core.Choice.validate()` would not properly account for
|
||||||
|
explicit tagging
|
||||||
|
- `core.Choice.load()` now properly passes itself as the spec when parsing
|
||||||
|
- `x509.Certificate.crl_distribution_points` no longer throws an exception if
|
||||||
|
the `DistributionPoint` does not have a value for the `distribution_point`
|
||||||
|
field
|
||||||
|
|
||||||
|
## 0.11.1
|
||||||
|
|
||||||
|
- Corrected `core.UTCTime` to interpret year <= 49 as 20xx and >= 50 as 19xx
|
||||||
|
- `keys.PublicKeyInfo.hash_algo` can now handle DSA keys without parameters
|
||||||
|
- Added `crl.CertificateList.sha256` and `crl.CertificateList.sha1`
|
||||||
|
- Fixed `x509.Name.build()` to properly encode `country_name`, `serial_number`
|
||||||
|
and `dn_qualifier` as `core.PrintableString` as specified in RFC 5280,
|
||||||
|
instead of `core.UTF8String`
|
||||||
|
|
||||||
|
## 0.11.0
|
||||||
|
|
||||||
|
- Added Python 2.6 support
|
||||||
|
- Added ability to compare primitive type objects
|
||||||
|
- Implemented proper support for internationalized domains, URLs and email
|
||||||
|
addresses in `x509.Certificate`
|
||||||
|
- Comparing `x509.Name` and `x509.GeneralName` objects adheres to RFC 5280
|
||||||
|
- `x509.Certificate.self_signed` and `x509.Certificate.self_issued` no longer
|
||||||
|
require that certificate is for a CA
|
||||||
|
- Fixed `x509.Certificate.valid_domains` to adhere to RFC 6125
|
||||||
|
- Added `x509.Certificate.is_valid_domain_ip()`
|
||||||
|
- Added `x509.Certificate.sha1` and `x509.Certificate.sha256`
|
||||||
|
- Exposed `util.inet_ntop()` and `util.inet_pton()` for IP address encoding
|
||||||
|
- Improved exception messages for improper types to include type's module name
|
||||||
|
|
||||||
|
## 0.10.1
|
||||||
|
|
||||||
|
- Fixed bug in `core.Sequence` affecting Python 2.7 and pypy
|
||||||
|
|
||||||
|
## 0.10.0
|
||||||
|
|
||||||
|
- Added PEM encoding/decoding functionality
|
||||||
|
- `core.BitString` now uses item access instead of attributes for named bit
|
||||||
|
access
|
||||||
|
- `core.BitString.native` now uses a `set` of unicode strings when `_map` is
|
||||||
|
present
|
||||||
|
- Removed `core.Asn1Value.pprint()` method
|
||||||
|
- Added `core.ParsableOctetString` class
|
||||||
|
- Added `core.ParsableOctetBitString` class
|
||||||
|
- Added `core.Asn1Value.copy()` method
|
||||||
|
- Added `core.Asn1Value.debug()` method
|
||||||
|
- Added `core.SequenceOf.append()` method
|
||||||
|
- Added `core.Sequence.spec()` and `core.SequenceOf.spec()` methods
|
||||||
|
- Added correct IP address parsing to `x509.GeneralName`
|
||||||
|
- `x509.Name` and `x509.GeneralName` are now compared according to rules in
|
||||||
|
RFC 5280
|
||||||
|
- Added convenience attributes to:
|
||||||
|
- `algos.SignedDigestAlgorithm`
|
||||||
|
- `crl.CertificateList`
|
||||||
|
- `crl.RevokedCertificate`
|
||||||
|
- `keys.PublicKeyInfo`
|
||||||
|
- `ocsp.OCSPRequest`
|
||||||
|
- `ocsp.Request`
|
||||||
|
- `ocsp.OCSPResponse`
|
||||||
|
- `ocsp.SingleResponse`
|
||||||
|
- `x509.Certificate`
|
||||||
|
- `x509.Name`
|
||||||
|
- Added `asn1crypto.util` module with the following items:
|
||||||
|
- `int_to_bytes()`
|
||||||
|
- `int_from_bytes()`
|
||||||
|
- `timezone.utc`
|
||||||
|
- Added `setup.py clean` command
|
||||||
|
|
||||||
|
## 0.9.0
|
||||||
|
|
||||||
|
- Initial release
|
@ -0,0 +1,28 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals, division, absolute_import, print_function
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
package_name = "asn1crypto"
|
||||||
|
|
||||||
|
other_packages = [
|
||||||
|
"oscrypto",
|
||||||
|
"certbuilder",
|
||||||
|
"certvalidator",
|
||||||
|
"crlbuilder",
|
||||||
|
"csrbuilder",
|
||||||
|
"ocspbuilder"
|
||||||
|
]
|
||||||
|
|
||||||
|
task_keyword_args = []
|
||||||
|
|
||||||
|
requires_oscrypto = False
|
||||||
|
has_tests_package = True
|
||||||
|
|
||||||
|
package_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
||||||
|
build_root = os.path.abspath(os.path.join(package_root, '..'))
|
||||||
|
|
||||||
|
md_source_map = {}
|
||||||
|
|
||||||
|
definition_replacements = {}
|
@ -0,0 +1,116 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals, division, absolute_import, print_function
|
||||||
|
|
||||||
|
import imp
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
from . import build_root, package_name, package_root
|
||||||
|
|
||||||
|
if sys.version_info < (3,):
|
||||||
|
getcwd = os.getcwdu
|
||||||
|
else:
|
||||||
|
getcwd = os.getcwd
|
||||||
|
|
||||||
|
|
||||||
|
def _import_from(mod, path, mod_dir=None, allow_error=False):
|
||||||
|
"""
|
||||||
|
Imports a module from a specific path
|
||||||
|
|
||||||
|
:param mod:
|
||||||
|
A unicode string of the module name
|
||||||
|
|
||||||
|
:param path:
|
||||||
|
A unicode string to the directory containing the module
|
||||||
|
|
||||||
|
:param mod_dir:
|
||||||
|
If the sub directory of "path" is different than the "mod" name,
|
||||||
|
pass the sub directory as a unicode string
|
||||||
|
|
||||||
|
:param allow_error:
|
||||||
|
If an ImportError should be raised when the module can't be imported
|
||||||
|
|
||||||
|
:return:
|
||||||
|
None if not loaded, otherwise the module
|
||||||
|
"""
|
||||||
|
|
||||||
|
if mod_dir is None:
|
||||||
|
mod_dir = mod.replace('.', os.sep)
|
||||||
|
|
||||||
|
if not os.path.exists(path):
|
||||||
|
return None
|
||||||
|
|
||||||
|
if not os.path.exists(os.path.join(path, mod_dir)) \
|
||||||
|
and not os.path.exists(os.path.join(path, mod_dir + '.py')):
|
||||||
|
return None
|
||||||
|
|
||||||
|
if os.sep in mod_dir:
|
||||||
|
append, mod_dir = mod_dir.rsplit(os.sep, 1)
|
||||||
|
path = os.path.join(path, append)
|
||||||
|
|
||||||
|
try:
|
||||||
|
mod_info = imp.find_module(mod_dir, [path])
|
||||||
|
return imp.load_module(mod, *mod_info)
|
||||||
|
except ImportError:
|
||||||
|
if allow_error:
|
||||||
|
raise
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _preload(require_oscrypto, print_info):
|
||||||
|
"""
|
||||||
|
Preloads asn1crypto and optionally oscrypto from a local source checkout,
|
||||||
|
or from a normal install
|
||||||
|
|
||||||
|
:param require_oscrypto:
|
||||||
|
A bool if oscrypto needs to be preloaded
|
||||||
|
|
||||||
|
:param print_info:
|
||||||
|
A bool if info about asn1crypto and oscrypto should be printed
|
||||||
|
"""
|
||||||
|
|
||||||
|
if print_info:
|
||||||
|
print('Working dir: ' + getcwd())
|
||||||
|
print('Python ' + sys.version.replace('\n', ''))
|
||||||
|
|
||||||
|
asn1crypto = None
|
||||||
|
oscrypto = None
|
||||||
|
|
||||||
|
if require_oscrypto:
|
||||||
|
# Some CI services don't use the package name for the dir
|
||||||
|
if package_name == 'oscrypto':
|
||||||
|
oscrypto_dir = package_root
|
||||||
|
else:
|
||||||
|
oscrypto_dir = os.path.join(build_root, 'oscrypto')
|
||||||
|
oscrypto_tests = None
|
||||||
|
if os.path.exists(oscrypto_dir):
|
||||||
|
oscrypto_tests = _import_from('oscrypto_tests', oscrypto_dir, 'tests')
|
||||||
|
if oscrypto_tests is None:
|
||||||
|
import oscrypto_tests
|
||||||
|
asn1crypto, oscrypto = oscrypto_tests.local_oscrypto()
|
||||||
|
|
||||||
|
else:
|
||||||
|
if package_name == 'asn1crypto':
|
||||||
|
asn1crypto_dir = package_root
|
||||||
|
else:
|
||||||
|
asn1crypto_dir = os.path.join(build_root, 'asn1crypto')
|
||||||
|
if os.path.exists(asn1crypto_dir):
|
||||||
|
asn1crypto = _import_from('asn1crypto', asn1crypto_dir)
|
||||||
|
if asn1crypto is None:
|
||||||
|
import asn1crypto
|
||||||
|
|
||||||
|
if print_info:
|
||||||
|
print(
|
||||||
|
'\nasn1crypto: %s, %s' % (
|
||||||
|
asn1crypto.__version__,
|
||||||
|
os.path.dirname(asn1crypto.__file__)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if require_oscrypto:
|
||||||
|
print(
|
||||||
|
'oscrypto: %s backend, %s, %s' % (
|
||||||
|
oscrypto.backend(),
|
||||||
|
oscrypto.__version__,
|
||||||
|
os.path.dirname(oscrypto.__file__)
|
||||||
|
)
|
||||||
|
)
|
@ -0,0 +1,205 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
"""
|
||||||
|
This file was originally derived from
|
||||||
|
https://github.com/pypa/pip/blob/3e713708088aedb1cde32f3c94333d6e29aaf86e/src/pip/_internal/pep425tags.py
|
||||||
|
|
||||||
|
The following license covers that code:
|
||||||
|
|
||||||
|
Copyright (c) 2008-2018 The pip developers (see AUTHORS.txt file)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import unicode_literals, division, absolute_import, print_function
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import ctypes
|
||||||
|
import re
|
||||||
|
import platform
|
||||||
|
|
||||||
|
if sys.version_info >= (2, 7):
|
||||||
|
import sysconfig
|
||||||
|
|
||||||
|
if sys.version_info < (3,):
|
||||||
|
str_cls = unicode # noqa
|
||||||
|
else:
|
||||||
|
str_cls = str
|
||||||
|
|
||||||
|
|
||||||
|
def _pep425_implementation():
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
A 2 character unicode string of the implementation - 'cp' for cpython
|
||||||
|
or 'pp' for PyPy
|
||||||
|
"""
|
||||||
|
|
||||||
|
return 'pp' if hasattr(sys, 'pypy_version_info') else 'cp'
|
||||||
|
|
||||||
|
|
||||||
|
def _pep425_version():
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
A tuple of integers representing the Python version number
|
||||||
|
"""
|
||||||
|
|
||||||
|
if hasattr(sys, 'pypy_version_info'):
|
||||||
|
return (sys.version_info[0], sys.pypy_version_info.major,
|
||||||
|
sys.pypy_version_info.minor)
|
||||||
|
else:
|
||||||
|
return (sys.version_info[0], sys.version_info[1])
|
||||||
|
|
||||||
|
|
||||||
|
def _pep425_supports_manylinux():
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
A boolean indicating if the machine can use manylinux1 packages
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
import _manylinux
|
||||||
|
return bool(_manylinux.manylinux1_compatible)
|
||||||
|
except (ImportError, AttributeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Check for glibc 2.5
|
||||||
|
try:
|
||||||
|
proc = ctypes.CDLL(None)
|
||||||
|
gnu_get_libc_version = proc.gnu_get_libc_version
|
||||||
|
gnu_get_libc_version.restype = ctypes.c_char_p
|
||||||
|
|
||||||
|
ver = gnu_get_libc_version()
|
||||||
|
if not isinstance(ver, str_cls):
|
||||||
|
ver = ver.decode('ascii')
|
||||||
|
match = re.match(r'(\d+)\.(\d+)', ver)
|
||||||
|
return match and match.group(1) == '2' and int(match.group(2)) >= 5
|
||||||
|
|
||||||
|
except (AttributeError):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _pep425_get_abi():
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
A unicode string of the system abi. Will be something like: "cp27m",
|
||||||
|
"cp33m", etc.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
soabi = sysconfig.get_config_var('SOABI')
|
||||||
|
if soabi:
|
||||||
|
if soabi.startswith('cpython-'):
|
||||||
|
return 'cp%s' % soabi.split('-')[1]
|
||||||
|
return soabi.replace('.', '_').replace('-', '_')
|
||||||
|
except (IOError, NameError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
impl = _pep425_implementation()
|
||||||
|
suffix = ''
|
||||||
|
if impl == 'cp':
|
||||||
|
suffix += 'm'
|
||||||
|
if sys.maxunicode == 0x10ffff and sys.version_info < (3, 3):
|
||||||
|
suffix += 'u'
|
||||||
|
return '%s%s%s' % (impl, ''.join(map(str_cls, _pep425_version())), suffix)
|
||||||
|
|
||||||
|
|
||||||
|
def _pep425tags():
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
A list of 3-element tuples with unicode strings or None:
|
||||||
|
[0] implementation tag - cp33, pp27, cp26, py2, py2.py3
|
||||||
|
[1] abi tag - cp26m, None
|
||||||
|
[2] arch tag - linux_x86_64, macosx_10_10_x85_64, etc
|
||||||
|
"""
|
||||||
|
|
||||||
|
tags = []
|
||||||
|
|
||||||
|
versions = []
|
||||||
|
version_info = _pep425_version()
|
||||||
|
major = version_info[:-1]
|
||||||
|
for minor in range(version_info[-1], -1, -1):
|
||||||
|
versions.append(''.join(map(str, major + (minor,))))
|
||||||
|
|
||||||
|
impl = _pep425_implementation()
|
||||||
|
|
||||||
|
abis = []
|
||||||
|
abi = _pep425_get_abi()
|
||||||
|
if abi:
|
||||||
|
abis.append(abi)
|
||||||
|
abi3 = _pep425_implementation() == 'cp' and sys.version_info >= (3,)
|
||||||
|
if abi3:
|
||||||
|
abis.append('abi3')
|
||||||
|
abis.append('none')
|
||||||
|
|
||||||
|
if sys.platform == 'darwin':
|
||||||
|
plat_ver = platform.mac_ver()
|
||||||
|
ver_parts = plat_ver[0].split('.')
|
||||||
|
minor = int(ver_parts[1])
|
||||||
|
arch = plat_ver[2]
|
||||||
|
if sys.maxsize == 2147483647:
|
||||||
|
arch = 'i386'
|
||||||
|
arches = []
|
||||||
|
while minor > 5:
|
||||||
|
arches.append('macosx_10_%s_%s' % (minor, arch))
|
||||||
|
arches.append('macosx_10_%s_intel' % (minor,))
|
||||||
|
arches.append('macosx_10_%s_universal' % (minor,))
|
||||||
|
minor -= 1
|
||||||
|
else:
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
if 'amd64' in sys.version.lower():
|
||||||
|
arches = ['win_amd64']
|
||||||
|
else:
|
||||||
|
arches = [sys.platform]
|
||||||
|
elif hasattr(os, 'uname'):
|
||||||
|
(plat, _, _, _, machine) = os.uname()
|
||||||
|
plat = plat.lower().replace('/', '')
|
||||||
|
machine.replace(' ', '_').replace('/', '_')
|
||||||
|
if plat == 'linux' and sys.maxsize == 2147483647 and 'arm' not in machine:
|
||||||
|
machine = 'i686'
|
||||||
|
arch = '%s_%s' % (plat, machine)
|
||||||
|
if _pep425_supports_manylinux():
|
||||||
|
arches = [arch.replace('linux', 'manylinux1'), arch]
|
||||||
|
else:
|
||||||
|
arches = [arch]
|
||||||
|
|
||||||
|
for abi in abis:
|
||||||
|
for arch in arches:
|
||||||
|
tags.append(('%s%s' % (impl, versions[0]), abi, arch))
|
||||||
|
|
||||||
|
if abi3:
|
||||||
|
for version in versions[1:]:
|
||||||
|
for arch in arches:
|
||||||
|
tags.append(('%s%s' % (impl, version), 'abi3', arch))
|
||||||
|
|
||||||
|
for arch in arches:
|
||||||
|
tags.append(('py%s' % (versions[0][0]), 'none', arch))
|
||||||
|
|
||||||
|
tags.append(('%s%s' % (impl, versions[0]), 'none', 'any'))
|
||||||
|
tags.append(('%s%s' % (impl, versions[0][0]), 'none', 'any'))
|
||||||
|
|
||||||
|
for i, version in enumerate(versions):
|
||||||
|
tags.append(('py%s' % (version,), 'none', 'any'))
|
||||||
|
if i == 0:
|
||||||
|
tags.append(('py%s' % (version[0]), 'none', 'any'))
|
||||||
|
|
||||||
|
tags.append(('py2.py3', 'none', 'any'))
|
||||||
|
|
||||||
|
return tags
|
@ -0,0 +1,163 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals, division, absolute_import, print_function
|
||||||
|
|
||||||
|
import ast
|
||||||
|
import _ast
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from . import package_root, task_keyword_args
|
||||||
|
from ._import import _import_from
|
||||||
|
|
||||||
|
|
||||||
|
if sys.version_info < (3,):
|
||||||
|
byte_cls = str
|
||||||
|
else:
|
||||||
|
byte_cls = bytes
|
||||||
|
|
||||||
|
|
||||||
|
def _list_tasks():
|
||||||
|
"""
|
||||||
|
Fetches a list of all valid tasks that may be run, and the args they
|
||||||
|
accept. Does not actually import the task module to prevent errors if a
|
||||||
|
user does not have the dependencies installed for every task.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A list of 2-element tuples:
|
||||||
|
0: a unicode string of the task name
|
||||||
|
1: a list of dicts containing the parameter definitions
|
||||||
|
"""
|
||||||
|
|
||||||
|
out = []
|
||||||
|
dev_path = os.path.join(package_root, 'dev')
|
||||||
|
for fname in sorted(os.listdir(dev_path)):
|
||||||
|
if fname.startswith('.') or fname.startswith('_'):
|
||||||
|
continue
|
||||||
|
if not fname.endswith('.py'):
|
||||||
|
continue
|
||||||
|
name = fname[:-3]
|
||||||
|
args = ()
|
||||||
|
|
||||||
|
full_path = os.path.join(package_root, 'dev', fname)
|
||||||
|
with open(full_path, 'rb') as f:
|
||||||
|
full_code = f.read()
|
||||||
|
if sys.version_info >= (3,):
|
||||||
|
full_code = full_code.decode('utf-8')
|
||||||
|
|
||||||
|
task_node = ast.parse(full_code, filename=full_path)
|
||||||
|
for node in ast.iter_child_nodes(task_node):
|
||||||
|
if isinstance(node, _ast.Assign):
|
||||||
|
if len(node.targets) == 1 \
|
||||||
|
and isinstance(node.targets[0], _ast.Name) \
|
||||||
|
and node.targets[0].id == 'run_args':
|
||||||
|
args = ast.literal_eval(node.value)
|
||||||
|
break
|
||||||
|
|
||||||
|
out.append((name, args))
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def show_usage():
|
||||||
|
"""
|
||||||
|
Prints to stderr the valid options for invoking tasks
|
||||||
|
"""
|
||||||
|
|
||||||
|
valid_tasks = []
|
||||||
|
for task in _list_tasks():
|
||||||
|
usage = task[0]
|
||||||
|
for run_arg in task[1]:
|
||||||
|
usage += ' '
|
||||||
|
name = run_arg.get('name', '')
|
||||||
|
if run_arg.get('required', False):
|
||||||
|
usage += '{%s}' % name
|
||||||
|
else:
|
||||||
|
usage += '[%s]' % name
|
||||||
|
valid_tasks.append(usage)
|
||||||
|
|
||||||
|
out = 'Usage: run.py'
|
||||||
|
for karg in task_keyword_args:
|
||||||
|
out += ' [%s=%s]' % (karg['name'], karg['placeholder'])
|
||||||
|
out += ' (%s)' % ' | '.join(valid_tasks)
|
||||||
|
|
||||||
|
print(out, file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_arg(num):
|
||||||
|
"""
|
||||||
|
:return:
|
||||||
|
A unicode string of the requested command line arg
|
||||||
|
"""
|
||||||
|
|
||||||
|
if len(sys.argv) < num + 1:
|
||||||
|
return None
|
||||||
|
arg = sys.argv[num]
|
||||||
|
if isinstance(arg, byte_cls):
|
||||||
|
arg = arg.decode('utf-8')
|
||||||
|
return arg
|
||||||
|
|
||||||
|
|
||||||
|
def run_task():
|
||||||
|
"""
|
||||||
|
Parses the command line args, invoking the requested task
|
||||||
|
"""
|
||||||
|
|
||||||
|
arg_num = 1
|
||||||
|
task = None
|
||||||
|
args = []
|
||||||
|
kwargs = {}
|
||||||
|
|
||||||
|
# We look for the task name, processing any global task keyword args
|
||||||
|
# by setting the appropriate env var
|
||||||
|
while True:
|
||||||
|
val = _get_arg(arg_num)
|
||||||
|
if val is None:
|
||||||
|
break
|
||||||
|
|
||||||
|
next_arg = False
|
||||||
|
for karg in task_keyword_args:
|
||||||
|
if val.startswith(karg['name'] + '='):
|
||||||
|
os.environ[karg['env_var']] = val[len(karg['name']) + 1:]
|
||||||
|
next_arg = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if next_arg:
|
||||||
|
arg_num += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
task = val
|
||||||
|
break
|
||||||
|
|
||||||
|
if task is None:
|
||||||
|
show_usage()
|
||||||
|
|
||||||
|
task_mod = _import_from('dev.%s' % task, package_root, allow_error=True)
|
||||||
|
if task_mod is None:
|
||||||
|
show_usage()
|
||||||
|
|
||||||
|
run_args = task_mod.__dict__.get('run_args', [])
|
||||||
|
max_args = arg_num + 1 + len(run_args)
|
||||||
|
|
||||||
|
if len(sys.argv) > max_args:
|
||||||
|
show_usage()
|
||||||
|
|
||||||
|
for i, run_arg in enumerate(run_args):
|
||||||
|
val = _get_arg(arg_num + 1 + i)
|
||||||
|
if val is None:
|
||||||
|
if run_arg.get('required', False):
|
||||||
|
show_usage()
|
||||||
|
break
|
||||||
|
|
||||||
|
if run_arg.get('cast') == 'int' and val.isdigit():
|
||||||
|
val = int(val)
|
||||||
|
|
||||||
|
kwarg = run_arg.get('kwarg')
|
||||||
|
if kwarg:
|
||||||
|
kwargs[kwarg] = val
|
||||||
|
else:
|
||||||
|
args.append(val)
|
||||||
|
|
||||||
|
run = task_mod.__dict__.get('run')
|
||||||
|
|
||||||
|
result = run(*args, **kwargs)
|
||||||
|
sys.exit(int(not result))
|
@ -0,0 +1,89 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals, division, absolute_import, print_function
|
||||||
|
|
||||||
|
import imp
|
||||||
|
import os
|
||||||
|
import tarfile
|
||||||
|
import zipfile
|
||||||
|
|
||||||
|
import setuptools.sandbox
|
||||||
|
|
||||||
|
from . import package_root, package_name, has_tests_package
|
||||||
|
|
||||||
|
|
||||||
|
def _list_zip(filename):
|
||||||
|
"""
|
||||||
|
Prints all of the files in a .zip file
|
||||||
|
"""
|
||||||
|
|
||||||
|
zf = zipfile.ZipFile(filename, 'r')
|
||||||
|
for name in zf.namelist():
|
||||||
|
print(' %s' % name)
|
||||||
|
|
||||||
|
|
||||||
|
def _list_tgz(filename):
|
||||||
|
"""
|
||||||
|
Prints all of the files in a .tar.gz file
|
||||||
|
"""
|
||||||
|
|
||||||
|
tf = tarfile.open(filename, 'r:gz')
|
||||||
|
for name in tf.getnames():
|
||||||
|
print(' %s' % name)
|
||||||
|
|
||||||
|
|
||||||
|
def run():
|
||||||
|
"""
|
||||||
|
Creates a sdist .tar.gz and a bdist_wheel --univeral .whl
|
||||||
|
|
||||||
|
:return:
|
||||||
|
A bool - if the packaging process was successful
|
||||||
|
"""
|
||||||
|
|
||||||
|
setup = os.path.join(package_root, 'setup.py')
|
||||||
|
tests_root = os.path.join(package_root, 'tests')
|
||||||
|
tests_setup = os.path.join(tests_root, 'setup.py')
|
||||||
|
|
||||||
|
# Trying to call setuptools.sandbox.run_setup(setup, ['--version'])
|
||||||
|
# resulted in a segfault, so we do this instead
|
||||||
|
module_info = imp.find_module('version', [os.path.join(package_root, package_name)])
|
||||||
|
version_mod = imp.load_module('%s.version' % package_name, *module_info)
|
||||||
|
|
||||||
|
pkg_name_info = (package_name, version_mod.__version__)
|
||||||
|
print('Building %s-%s' % pkg_name_info)
|
||||||
|
|
||||||
|
sdist = '%s-%s.tar.gz' % pkg_name_info
|
||||||
|
whl = '%s-%s-py2.py3-none-any.whl' % pkg_name_info
|
||||||
|
setuptools.sandbox.run_setup(setup, ['-q', 'sdist'])
|
||||||
|
print(' - created %s' % sdist)
|
||||||
|
_list_tgz(os.path.join(package_root, 'dist', sdist))
|
||||||
|
setuptools.sandbox.run_setup(setup, ['-q', 'bdist_wheel', '--universal'])
|
||||||
|
print(' - created %s' % whl)
|
||||||
|
_list_zip(os.path.join(package_root, 'dist', whl))
|
||||||
|
setuptools.sandbox.run_setup(setup, ['-q', 'clean'])
|
||||||
|
|
||||||
|
if has_tests_package:
|
||||||
|
print('Building %s_tests-%s' % (package_name, version_mod.__version__))
|
||||||
|
|
||||||
|
tests_sdist = '%s_tests-%s.tar.gz' % pkg_name_info
|
||||||
|
tests_whl = '%s_tests-%s-py2.py3-none-any.whl' % pkg_name_info
|
||||||
|
setuptools.sandbox.run_setup(tests_setup, ['-q', 'sdist'])
|
||||||
|
print(' - created %s' % tests_sdist)
|
||||||
|
_list_tgz(os.path.join(tests_root, 'dist', tests_sdist))
|
||||||
|
setuptools.sandbox.run_setup(tests_setup, ['-q', 'bdist_wheel', '--universal'])
|
||||||
|
print(' - created %s' % tests_whl)
|
||||||
|
_list_zip(os.path.join(tests_root, 'dist', tests_whl))
|
||||||
|
setuptools.sandbox.run_setup(tests_setup, ['-q', 'clean'])
|
||||||
|
|
||||||
|
dist_dir = os.path.join(package_root, 'dist')
|
||||||
|
tests_dist_dir = os.path.join(tests_root, 'dist')
|
||||||
|
os.rename(
|
||||||
|
os.path.join(tests_dist_dir, tests_sdist),
|
||||||
|
os.path.join(dist_dir, tests_sdist)
|
||||||
|
)
|
||||||
|
os.rename(
|
||||||
|
os.path.join(tests_dist_dir, tests_whl),
|
||||||
|
os.path.join(dist_dir, tests_whl)
|
||||||
|
)
|
||||||
|
os.rmdir(tests_dist_dir)
|
||||||
|
|
||||||
|
return True
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user