initial public release
0
book/source/_static/.gitkeep
Normal file
74
book/source/_static/epub/css/custom.css
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC0-1.0
|
||||
*/
|
||||
|
||||
/*
|
||||
Admonitions
|
||||
*/
|
||||
div.admonition {
|
||||
background-color: lightgray;
|
||||
}
|
||||
|
||||
div.warning,
|
||||
div.admonition-warning {
|
||||
background-color: palevioletred;
|
||||
}
|
||||
|
||||
/*
|
||||
External links
|
||||
*/
|
||||
a.external.reference {
|
||||
border-bottom: 1px solid darkgreen;
|
||||
color: darkgreen;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/*
|
||||
Internal links
|
||||
*/
|
||||
a.internal {
|
||||
text-decoration: none;
|
||||
border-bottom-style: none;
|
||||
}
|
||||
|
||||
/*
|
||||
Text in section and reference links and footnote links
|
||||
*/
|
||||
span.std-doc,
|
||||
span.std-numref,
|
||||
span.std-ref,
|
||||
a.footnote-reference {
|
||||
border-bottom: 1px dashed #3333ff;
|
||||
color: #3333ff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/*
|
||||
Text in glossary links
|
||||
*/
|
||||
span.std-term {
|
||||
color: black;
|
||||
border-bottom: 1px dotted black;
|
||||
}
|
||||
|
||||
/*
|
||||
Footnote list
|
||||
*/
|
||||
aside.footnote-list {
|
||||
border-top: 1px solid black;
|
||||
margin-top: 2em;
|
||||
padding-top: 2em;
|
||||
}
|
||||
|
||||
/*
|
||||
Cover
|
||||
*/
|
||||
|
||||
div.cover-img {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.cover-img span img {
|
||||
height: 100%;
|
||||
}
|
BIN
book/source/_static/epub/img/cover.png
Normal file
After Width: | Height: | Size: 967 KiB |
57
book/source/_static/html/css/custom.css
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
NOTE: Colors are adapted to match those of the "alabaster" theme
|
||||
*/
|
||||
|
||||
/*
|
||||
External links
|
||||
*/
|
||||
a.external.reference {
|
||||
color: darkgreen;
|
||||
text-decoration: none;
|
||||
border-bottom: 1px solid darkgreen;
|
||||
}
|
||||
|
||||
/*
|
||||
Internal links
|
||||
*/
|
||||
a.internal.reference {
|
||||
text-decoration: none;
|
||||
border-bottom-style: none;
|
||||
}
|
||||
|
||||
/*
|
||||
Text in section and reference links
|
||||
*/
|
||||
span.std-doc,
|
||||
span.std-numref,
|
||||
span.std-ref,
|
||||
a.footnote-reference {
|
||||
border-bottom: 1px dashed #3333ff;
|
||||
color: #3333ff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a.footnote-reference:hover {
|
||||
border-bottom: 1px dashed #3333ff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/*
|
||||
Text in glossary links
|
||||
*/
|
||||
span.std-term {
|
||||
color: rgb(62, 67, 73);
|
||||
border-bottom: 1px dotted rgb(62, 67, 73);
|
||||
}
|
||||
|
||||
/*
|
||||
Footnote list
|
||||
*/
|
||||
aside.footnote-list {
|
||||
border-top: 1px solid black;
|
||||
margin-top: 2em;
|
||||
padding-top: 2em;
|
||||
}
|
BIN
book/source/_static/html/img/favicon.ico
Normal file
After Width: | Height: | Size: 4.2 KiB |
158
book/source/_static/html/img/favicon.svg
Normal file
|
@ -0,0 +1,158 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 178 178"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="favicon.svg"
|
||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
|
||||
width="178"
|
||||
height="178"
|
||||
xml:space="preserve"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"><defs
|
||||
id="defs1"><linearGradient
|
||||
id="swatch251"
|
||||
inkscape:swatch="solid"><stop
|
||||
style="stop-color:#195962;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop251" /></linearGradient><pattern
|
||||
inkscape:collect="always"
|
||||
xlink:href="#shading6"
|
||||
preserveAspectRatio="xMidYMid"
|
||||
id="pattern180"
|
||||
patternTransform="matrix(-0.74999999,0,0,-0.74999999,796.91394,-2553.0795)"
|
||||
x="0"
|
||||
y="0" /><pattern
|
||||
patternUnits="userSpaceOnUse"
|
||||
width="372.6899"
|
||||
height="66.1838"
|
||||
patternTransform="scale(0.2)"
|
||||
preserveAspectRatio="xMidYMid"
|
||||
id="shading6"
|
||||
style="fill:#ee5937"
|
||||
inkscape:label="Schmale Linien (Halbton)"
|
||||
inkscape:collect="always"
|
||||
inkscape:isstock="true"><path
|
||||
id="path34"
|
||||
style="stroke-width:0.487199;stroke-linecap:square;paint-order:markers fill stroke;stop-color:#000000"
|
||||
d="m 371.6961,0 h 0.9938 v 66.1838 h -0.9938 z m -7.9084,0 h 1.1312 v 66.1838 h -1.1312 z m -7.9085,0 h 1.2687 v 66.1838 h -1.2687 z m -7.9084,0 h 1.4062 v 66.1838 h -1.4062 z m -7.9084,0 h 1.5436 v 66.1838 h -1.5436 z m -7.9084,0 h 1.681 v 66.1838 h -1.681 z m -7.9085,0 h 1.8185 v 66.1838 h -1.8185 z m -7.9084,0 h 1.9559 v 66.1838 h -1.9559 z m -7.9084,0 h 2.0934 v 66.1838 h -2.0934 z m -7.9085,0 h 2.2309 v 66.1838 h -2.2309 z m -7.9084,0 h 2.3683 v 66.1838 h -2.3683 z m -7.9084,0 h 2.5057 v 66.1838 h -2.5057 z m -7.9084,0 h 2.6431 v 66.1838 h -2.6431 z m -7.9085,0 h 2.7807 v 66.1838 h -2.7807 z m -7.9084,0 h 2.9181 v 66.1838 h -2.9181 z m -7.9084,0 h 3.0555 v 66.1838 h -3.0555 z m -7.9084,0 h 3.1929 v 66.1838 h -3.1929 z m -7.9085,0 h 3.3304 v 66.1838 h -3.3304 z m -7.9084,0 h 3.4679 v 66.1838 h -3.4679 z m -7.9084,0 h 3.6053 v 66.1838 h -3.6053 z m -7.9085,0 h 3.7428 v 66.1838 h -3.7428 z m -7.9084,0 h 3.8802 v 66.1838 h -3.8802 z m -7.9084,0 h 4.0176 v 66.1838 h -4.0176 z m -7.9084,0 h 4.1551 v 66.1838 h -4.1551 z m -7.9085,0 h 4.2926 v 66.1838 h -4.2926 z m -7.9084,0 h 4.43 v 66.1838 h -4.43 z m -7.9084,0 h 4.5674 v 66.1838 h -4.5674 z m -7.9084,0 h 4.7048 v 66.1838 h -4.7048 z m -7.9085,0 h 4.8424 v 66.1838 h -4.8424 z m -7.9084,0 h 4.9798 v 66.1838 h -4.9798 z m -7.9084,0 h 5.1172 v 66.1838 h -5.1172 z m -7.9084,0 h 5.2546 v 66.1838 h -5.2546 z m -7.9085,0 h 5.3921 v 66.1838 h -5.3921 z m -7.9084,0 h 5.5295 v 66.1838 h -5.5295 z m -7.9084,0 h 5.667 v 66.1838 h -5.667 z m -7.9085,0 h 5.8045 v 66.1838 h -5.8045 z m -7.9084,0 h 5.9419 v 66.1838 h -5.9419 z m -7.9084,0 h 6.0793 v 66.1838 h -6.0793 z m -7.9084,0 h 6.2167 v 66.1838 h -6.2167 z m -7.9085,0 h 6.3543 V 66.1838 H 63.2674 Z M 55.359,0 h 6.4917 V 66.1838 H 55.359 Z m -7.9084,0 h 6.6291 v 66.1838 h -6.6291 z m -7.9084,0 h 6.7665 v 66.1838 h -6.7665 z m -7.9085,0 h 6.904 v 66.1838 h -6.904 z m -7.9084,0 h 7.0415 v 66.1838 h -7.0415 z m -7.9084,0 h 7.1789 V 66.1838 H 15.8169 Z M 7.9084,0 h 7.3164 V 66.1838 H 7.9084 Z M 0,0 H 7.4538 V 66.1838 H 0 Z" /></pattern><inkscape:path-effect
|
||||
effect="fillet_chamfer"
|
||||
id="path-effect32-1-0-4-9"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
|
||||
radius="0"
|
||||
unit="px"
|
||||
method="auto"
|
||||
mode="F"
|
||||
chamfer_steps="1"
|
||||
flexible="false"
|
||||
use_knot_distance="true"
|
||||
apply_no_radius="true"
|
||||
apply_with_radius="true"
|
||||
only_selected="false"
|
||||
hide_knots="false" /></defs><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="true"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:lockguides="false"
|
||||
inkscape:zoom="2.8284271"
|
||||
inkscape:cx="172.35728"
|
||||
inkscape:cy="81.494057"
|
||||
inkscape:window-width="2396"
|
||||
inkscape:window-height="1293"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer31"
|
||||
showgrid="false"
|
||||
inkscape:export-bgcolor="#ffffff00"
|
||||
showguides="true"
|
||||
borderlayer="false"
|
||||
showborder="false"><sodipodi:guide
|
||||
position="1616.9091,-4139.1099"
|
||||
orientation="0,-1"
|
||||
id="guide360"
|
||||
inkscape:locked="false" /><sodipodi:guide
|
||||
position="1460.6512,-3953.7147"
|
||||
orientation="0,659.35662"
|
||||
id="guide361"
|
||||
inkscape:locked="false" /><sodipodi:guide
|
||||
position="2281.0798,283.36511"
|
||||
orientation="943.88005,0"
|
||||
id="guide362"
|
||||
inkscape:locked="false" /><sodipodi:guide
|
||||
position="2120.0079,-4897.5947"
|
||||
orientation="0,-659.35662"
|
||||
id="guide363"
|
||||
inkscape:locked="false" /><sodipodi:guide
|
||||
position="1460.6512,-4897.5947"
|
||||
orientation="-943.88005,0"
|
||||
id="guide364"
|
||||
inkscape:locked="false" /><sodipodi:guide
|
||||
position="1460.6512,-3953.7147"
|
||||
orientation="0,659.35662"
|
||||
id="guide365"
|
||||
inkscape:locked="false" /><sodipodi:guide
|
||||
position="2120.0079,-4897.5947"
|
||||
orientation="0,-659.35662"
|
||||
id="guide367"
|
||||
inkscape:locked="false" /><sodipodi:guide
|
||||
position="1460.6512,-4897.5947"
|
||||
orientation="-943.88005,0"
|
||||
id="guide368"
|
||||
inkscape:locked="false" /><inkscape:page
|
||||
x="6.7782037e-07"
|
||||
y="-1.4691306e-05"
|
||||
width="289.7142"
|
||||
height="162.06558"
|
||||
id="page94"
|
||||
margin="0"
|
||||
bleed="0" /></sodipodi:namedview><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
inkscape:label="background"
|
||||
style="display:inline"
|
||||
transform="translate(-747.93347,2616.9115)"><circle
|
||||
style="fill:#ffffff;stroke-width:1.10046"
|
||||
id="path1"
|
||||
cx="837.15222"
|
||||
cy="-2527.8208"
|
||||
r="88.814758" /></g><g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer31"
|
||||
inkscape:label="OpenPGP signature packet"
|
||||
style="display:inline"
|
||||
transform="translate(-864.87878,2560.6205)"><path
|
||||
id="path2-1-4-5-1-7-1"
|
||||
style="display:inline;mix-blend-mode:multiply;fill:url(#pattern180);fill-opacity:1;stroke:none;stroke-width:5.8655;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:label="Publickey ((Asym))"
|
||||
d="m 998.6159,-2418.8799 c 18.627,0.4549 34.0958,-14.2763 34.5508,-32.9033 0.4549,-18.627 -14.2763,-34.0964 -32.9033,-34.5513 -13.08964,-0.3198 -24.68294,7.9128 -30.53934,18.6737 l -0.02,0.6846 c -5.8504,-0.083 -12.9906,-0.3268 -19.4381,-0.4843 l -6.5249,4.132 -8.0925,-4.489 -8.29796,3.9224 -6.10817,-0.1492 -6.38326,-4.281 -10.01748,3.8804 -9.07911,-4.3467 -12.70174,13.9931 12.00349,14.5971 73.79643,1.8024 -0.01,0.2382 c 5.2187,11.3157 16.4642,18.9558 29.76264,19.2806 z m 16.2144,-25.7678 a 7.1906559,7.1906559 0 0 1 -7.0131,-7.3643 7.1906559,7.1906559 0 0 1 7.3642,-7.013 7.1906559,7.1906559 0 0 1 7.0131,7.3642 7.1906559,7.1906559 0 0 1 -7.3642,7.0131 z" /><path
|
||||
id="path2-1-4-5-8"
|
||||
style="display:inline;mix-blend-mode:multiply;fill:none;fill-opacity:1;stroke:#006961;stroke-width:10.4431;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 156.30312,759.25868 c -9.78054,19.91382 -30.12868,33.02384 -53.81237,33.02384 -33.173715,0 -60.066326,-26.89261 -60.066326,-60.06633 0,-33.17372 26.892611,-60.06633 60.066326,-60.06633 23.31181,0 43.58824,15.15712 53.54415,34.5646 m -9.14624,1.20213 h 4.97796 c 10.89491,0.20296 25.86265,0 38.79397,0 l 11.43353,7.63836 14.59897,-7.63836 14.59896,7.3422 h 10.87817 l 11.54761,-7.3422 17.66083,7.3422 16.34856,-7.34219 22,25.45886 -22,25.45887 h -135.8606 -4.97796 M 89.058073,732.21619 c -4e-6,6.98969 -5.666271,12.65596 -12.655965,12.65596 -6.989695,0 -12.655963,-5.66627 -12.655967,-12.65596 -2e-6,-6.9897 5.666268,-12.65597 12.655967,-12.65597 6.989698,0 12.655967,5.66627 12.655965,12.65597 z"
|
||||
inkscape:label="Publickey ((Asym))"
|
||||
sodipodi:nodetypes="cssscccccccccccccccsssss"
|
||||
transform="matrix(0.56166813,0,0,-0.56166813,852.19581,-2074.1507)"
|
||||
inkscape:path-effect="#path-effect32-1-0-4-9"
|
||||
inkscape:original-d="m 156.30312,759.25868 c -9.78054,19.91382 -30.12868,33.02384 -53.81237,33.02384 -33.173715,0 -60.066326,-26.89261 -60.066326,-60.06633 0,-33.17372 26.892611,-60.06633 60.066326,-60.06633 23.31181,0 43.58824,15.15712 53.54415,34.5646 m -9.14624,1.20213 h 4.97796 c 10.89491,0.20296 25.86265,0 38.79397,0 l 11.43353,7.63836 14.59897,-7.63836 14.59896,7.3422 h 10.87817 l 11.54761,-7.3422 17.66083,7.3422 16.34856,-7.34219 22,25.45886 -22,25.45887 h -135.8606 -4.97796 M 89.058073,732.21619 c -4e-6,6.98969 -5.666271,12.65596 -12.655965,12.65596 -6.989695,0 -12.655963,-5.66627 -12.655967,-12.65596 -2e-6,-6.9897 5.666268,-12.65597 12.655967,-12.65597 6.989698,0 12.655967,5.66627 12.655965,12.65597 z" /></g><metadata
|
||||
id="metadata1"><rdf:RDF><cc:Work
|
||||
rdf:about=""><cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" /></cc:Work><cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/"><cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction" /><cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution" /><cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Notice" /><cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Attribution" /><cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" /><cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#ShareAlike" /></cc:License></rdf:RDF></metadata></svg>
|
After Width: | Height: | Size: 10 KiB |
BIN
book/source/_static/html/img/logo.png
Normal file
After Width: | Height: | Size: 8.4 KiB |
137
book/source/_static/html/img/logo.svg
Normal file
|
@ -0,0 +1,137 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 289.7142 162.06558"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="diag_library_draft.svg"
|
||||
inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
|
||||
width="289.7142"
|
||||
height="162.06558"
|
||||
xml:space="preserve"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs1"><linearGradient
|
||||
id="swatch251"
|
||||
inkscape:swatch="solid"><stop
|
||||
style="stop-color:#195962;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop251" /></linearGradient><pattern
|
||||
inkscape:collect="always"
|
||||
xlink:href="#shading6"
|
||||
preserveAspectRatio="xMidYMid"
|
||||
id="pattern180"
|
||||
patternTransform="matrix(-0.74999999,0,0,-0.74999999,864.87878,-2550.6205)"
|
||||
x="0"
|
||||
y="0" /><pattern
|
||||
patternUnits="userSpaceOnUse"
|
||||
width="372.6899"
|
||||
height="66.1838"
|
||||
patternTransform="scale(0.2)"
|
||||
preserveAspectRatio="xMidYMid"
|
||||
id="shading6"
|
||||
style="fill:#ee5937"
|
||||
inkscape:label="Schmale Linien (Halbton)"
|
||||
inkscape:collect="always"
|
||||
inkscape:isstock="true"><path
|
||||
id="path34"
|
||||
style="stroke-width:0.487199;stroke-linecap:square;paint-order:markers fill stroke;stop-color:#000000"
|
||||
d="m 371.6961,0 h 0.9938 v 66.1838 h -0.9938 z m -7.9084,0 h 1.1312 v 66.1838 h -1.1312 z m -7.9085,0 h 1.2687 v 66.1838 h -1.2687 z m -7.9084,0 h 1.4062 v 66.1838 h -1.4062 z m -7.9084,0 h 1.5436 v 66.1838 h -1.5436 z m -7.9084,0 h 1.681 v 66.1838 h -1.681 z m -7.9085,0 h 1.8185 v 66.1838 h -1.8185 z m -7.9084,0 h 1.9559 v 66.1838 h -1.9559 z m -7.9084,0 h 2.0934 v 66.1838 h -2.0934 z m -7.9085,0 h 2.2309 v 66.1838 h -2.2309 z m -7.9084,0 h 2.3683 v 66.1838 h -2.3683 z m -7.9084,0 h 2.5057 v 66.1838 h -2.5057 z m -7.9084,0 h 2.6431 v 66.1838 h -2.6431 z m -7.9085,0 h 2.7807 v 66.1838 h -2.7807 z m -7.9084,0 h 2.9181 v 66.1838 h -2.9181 z m -7.9084,0 h 3.0555 v 66.1838 h -3.0555 z m -7.9084,0 h 3.1929 v 66.1838 h -3.1929 z m -7.9085,0 h 3.3304 v 66.1838 h -3.3304 z m -7.9084,0 h 3.4679 v 66.1838 h -3.4679 z m -7.9084,0 h 3.6053 v 66.1838 h -3.6053 z m -7.9085,0 h 3.7428 v 66.1838 h -3.7428 z m -7.9084,0 h 3.8802 v 66.1838 h -3.8802 z m -7.9084,0 h 4.0176 v 66.1838 h -4.0176 z m -7.9084,0 h 4.1551 v 66.1838 h -4.1551 z m -7.9085,0 h 4.2926 v 66.1838 h -4.2926 z m -7.9084,0 h 4.43 v 66.1838 h -4.43 z m -7.9084,0 h 4.5674 v 66.1838 h -4.5674 z m -7.9084,0 h 4.7048 v 66.1838 h -4.7048 z m -7.9085,0 h 4.8424 v 66.1838 h -4.8424 z m -7.9084,0 h 4.9798 v 66.1838 h -4.9798 z m -7.9084,0 h 5.1172 v 66.1838 h -5.1172 z m -7.9084,0 h 5.2546 v 66.1838 h -5.2546 z m -7.9085,0 h 5.3921 v 66.1838 h -5.3921 z m -7.9084,0 h 5.5295 v 66.1838 h -5.5295 z m -7.9084,0 h 5.667 v 66.1838 h -5.667 z m -7.9085,0 h 5.8045 v 66.1838 h -5.8045 z m -7.9084,0 h 5.9419 v 66.1838 h -5.9419 z m -7.9084,0 h 6.0793 v 66.1838 h -6.0793 z m -7.9084,0 h 6.2167 v 66.1838 h -6.2167 z m -7.9085,0 h 6.3543 V 66.1838 H 63.2674 Z M 55.359,0 h 6.4917 V 66.1838 H 55.359 Z m -7.9084,0 h 6.6291 v 66.1838 h -6.6291 z m -7.9084,0 h 6.7665 v 66.1838 h -6.7665 z m -7.9085,0 h 6.904 v 66.1838 h -6.904 z m -7.9084,0 h 7.0415 v 66.1838 h -7.0415 z m -7.9084,0 h 7.1789 V 66.1838 H 15.8169 Z M 7.9084,0 h 7.3164 V 66.1838 H 7.9084 Z M 0,0 H 7.4538 V 66.1838 H 0 Z" /></pattern><inkscape:path-effect
|
||||
effect="fillet_chamfer"
|
||||
id="path-effect32-1-0-4-9"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
|
||||
radius="0"
|
||||
unit="px"
|
||||
method="auto"
|
||||
mode="F"
|
||||
chamfer_steps="1"
|
||||
flexible="false"
|
||||
use_knot_distance="true"
|
||||
apply_no_radius="true"
|
||||
apply_with_radius="true"
|
||||
only_selected="false"
|
||||
hide_knots="false" /></defs><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:lockguides="false"
|
||||
inkscape:zoom="2"
|
||||
inkscape:cx="2755.25"
|
||||
inkscape:cy="-1707.25"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1371"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="314"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer31"
|
||||
showgrid="false"
|
||||
inkscape:export-bgcolor="#ffffff00"
|
||||
showguides="true"><sodipodi:guide
|
||||
position="1616.9091,-4157.0443"
|
||||
orientation="0,-1"
|
||||
id="guide360"
|
||||
inkscape:locked="false" /><sodipodi:guide
|
||||
position="1460.6512,-3971.6491"
|
||||
orientation="0,659.35662"
|
||||
id="guide361"
|
||||
inkscape:locked="false" /><sodipodi:guide
|
||||
position="2281.0798,265.43069"
|
||||
orientation="943.88005,0"
|
||||
id="guide362"
|
||||
inkscape:locked="false" /><sodipodi:guide
|
||||
position="2120.0079,-4915.5291"
|
||||
orientation="0,-659.35662"
|
||||
id="guide363"
|
||||
inkscape:locked="false" /><sodipodi:guide
|
||||
position="1460.6512,-4915.5291"
|
||||
orientation="-943.88005,0"
|
||||
id="guide364"
|
||||
inkscape:locked="false" /><sodipodi:guide
|
||||
position="1460.6512,-3971.6491"
|
||||
orientation="0,659.35662"
|
||||
id="guide365"
|
||||
inkscape:locked="false" /><sodipodi:guide
|
||||
position="2120.0079,-4915.5291"
|
||||
orientation="0,-659.35662"
|
||||
id="guide367"
|
||||
inkscape:locked="false" /><sodipodi:guide
|
||||
position="1460.6512,-4915.5291"
|
||||
orientation="-943.88005,0"
|
||||
id="guide368"
|
||||
inkscape:locked="false" /><inkscape:page
|
||||
x="6.7782037e-07"
|
||||
y="-1.4691306e-05"
|
||||
width="289.7142"
|
||||
height="162.06558"
|
||||
id="page94"
|
||||
margin="0"
|
||||
bleed="0" /></sodipodi:namedview><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer18"
|
||||
inkscape:label="Chapter 6"
|
||||
style="display:inline"
|
||||
transform="translate(-864.87878,2550.6205)"><g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer31"
|
||||
inkscape:label="OpenPGP signature packet"><path
|
||||
id="path2-1-4-5-1-7-1"
|
||||
style="display:inline;mix-blend-mode:multiply;fill:url(#pattern180);fill-opacity:1;stroke:none;stroke-width:5.8655;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:label="Publickey ((Asym))"
|
||||
d="m 1066.5807,-2416.4209 c 18.627,0.4549 34.0958,-14.2763 34.5508,-32.9033 0.4549,-18.627 -14.2763,-34.0964 -32.9033,-34.5513 -13.0896,-0.3198 -24.6829,7.9128 -30.5393,18.6737 l -0.02,0.6846 c -5.8504,-0.083 -12.9906,-0.3268 -19.4381,-0.4843 l -6.5249,4.132 -8.0925,-4.489 -8.29796,3.9224 -6.10817,-0.1492 -6.38326,-4.281 -10.01748,3.8804 -9.07911,-4.3467 -12.70174,13.9931 12.00349,14.5971 73.79643,1.8024 -0.01,0.2382 c 5.2187,11.3157 16.4642,18.9558 29.7626,19.2806 z m 16.2144,-25.7678 a 7.1906559,7.1906559 0 0 1 -7.0131,-7.3643 7.1906559,7.1906559 0 0 1 7.3642,-7.013 7.1906559,7.1906559 0 0 1 7.0131,7.3642 7.1906559,7.1906559 0 0 1 -7.3642,7.0131 z" /><path
|
||||
id="path2-1-4-5-8"
|
||||
style="display:inline;mix-blend-mode:multiply;fill:none;fill-opacity:1;stroke:#006961;stroke-width:10.4431;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 156.30312,759.25868 c -9.78054,19.91382 -30.12868,33.02384 -53.81237,33.02384 -33.173715,0 -60.066326,-26.89261 -60.066326,-60.06633 0,-33.17372 26.892611,-60.06633 60.066326,-60.06633 23.31181,0 43.58824,15.15712 53.54415,34.5646 m -9.14624,1.20213 h 4.97796 c 10.89491,0.20296 25.86265,0 38.79397,0 l 11.43353,7.63836 14.59897,-7.63836 14.59896,7.3422 h 10.87817 l 11.54761,-7.3422 17.66083,7.3422 16.34856,-7.34219 22,25.45886 -22,25.45887 h -135.8606 -4.97796 M 89.058073,732.21619 c -4e-6,6.98969 -5.666271,12.65596 -12.655965,12.65596 -6.989695,0 -12.655963,-5.66627 -12.655967,-12.65596 -2e-6,-6.9897 5.666268,-12.65597 12.655967,-12.65597 6.989698,0 12.655967,5.66627 12.655965,12.65597 z"
|
||||
inkscape:label="Publickey ((Asym))"
|
||||
sodipodi:nodetypes="cssscccccccccccccccsssss"
|
||||
transform="matrix(0.56166813,0,0,-0.56166813,920.16065,-2071.6917)"
|
||||
inkscape:path-effect="#path-effect32-1-0-4-9"
|
||||
inkscape:original-d="m 156.30312,759.25868 c -9.78054,19.91382 -30.12868,33.02384 -53.81237,33.02384 -33.173715,0 -60.066326,-26.89261 -60.066326,-60.06633 0,-33.17372 26.892611,-60.06633 60.066326,-60.06633 23.31181,0 43.58824,15.15712 53.54415,34.5646 m -9.14624,1.20213 h 4.97796 c 10.89491,0.20296 25.86265,0 38.79397,0 l 11.43353,7.63836 14.59897,-7.63836 14.59896,7.3422 h 10.87817 l 11.54761,-7.3422 17.66083,7.3422 16.34856,-7.34219 22,25.45886 -22,25.45887 h -135.8606 -4.97796 M 89.058073,732.21619 c -4e-6,6.98969 -5.666271,12.65596 -12.655965,12.65596 -6.989695,0 -12.655963,-5.66627 -12.655967,-12.65596 -2e-6,-6.9897 5.666268,-12.65597 12.655967,-12.65597 6.989698,0 12.655967,5.66627 12.655965,12.65597 z" /></g></g></svg>
|
After Width: | Height: | Size: 9.4 KiB |
22
book/source/_templates/cover.j2
Normal file
|
@ -0,0 +1,22 @@
|
|||
{#
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
#}
|
||||
{%- extends "layout.html" %}
|
||||
{%- block header %}{% endblock %}
|
||||
{%- block rootrellink %}{% endblock %}
|
||||
{%- block relbaritems %}{% endblock %}
|
||||
{%- block sidebarlogo %}{% endblock %}
|
||||
{%- block linktags %}{% endblock %}
|
||||
{%- block relbar1 %}{% endblock %}
|
||||
{%- block sidebar1 %}{% endblock %}
|
||||
{%- block sidebar2 %}{% endblock %}
|
||||
{%- block footer %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="cover-img">
|
||||
<span>
|
||||
<img src="{{ image }}" alt="Cover" />
|
||||
</span>
|
||||
</div>
|
||||
{% endblock %}
|
51
book/source/about.md
Normal file
|
@ -0,0 +1,51 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Introducing OpenPGP
|
||||
|
||||
This documentation project is designed to provide a comprehensive understanding of OpenPGP, highlighting its functionalities and applications for software developers. While this document predominantly references [OpenPGP version 6, as outlined in the latest RFC](https://datatracker.ietf.org/doc/draft-ietf-openpgp-crypto-refresh/), it is important to note that the fundamental principles and functionalities of OpenPGP have remained consistent across its versions since its first release as an open standard in RFC 2440 in 1998.
|
||||
|
||||
This documentation project seeks to introduce all OpenPGP concepts and functionalities to application developers who wish to use it in their projects.
|
||||
|
||||
## What is OpenPGP?
|
||||
|
||||
OpenPGP is an open standard for cryptographic operations. It is a system based on well-understood [cryptographic building blocks](/cryptography). OpenPGP supports the secure delivery of files and messages between a sender and a recipient. It also addresses identities and their verification. OpenPGP is an outgrowth of the ["Pretty Good Privacy (PGP)"](https://en.wikipedia.org/wiki/Pretty_Good_Privacy) encryption program and has many widely used and [interoperable implementations](interoperability).
|
||||
|
||||
With OpenPGP, you can:
|
||||
|
||||
- [Encrypt](/encryption) and [decrypt](/decryption) messages to preserve confidentiality
|
||||
- [Sign](/signing_data) and [verify](/verification) data to ensure {term}`authenticity<Authentication>`
|
||||
- [Issue and validate certifications](/signing_components) about {term}`keys<Key>` and {term}`identities<Identity>`, similar to the role of a {term}`Certificate Authority<Certification Authority>` ({term}`CA<Certification Authority>`) in validating {term}`identities<Identity>`.
|
||||
|
||||
## Who is the audience for this document?
|
||||
|
||||
Three groups of people interact with OpenPGP:
|
||||
|
||||
1. End-users, who use software that contains OpenPGP functionality (e.g., the Thunderbird email software)
|
||||
2. Software developers who build applications that contain OpenPGP functionality
|
||||
3. Implementers of OpenPGP libraries (or other software that directly handles the processing of internal OpenPGP data structures)
|
||||
|
||||
This document is not intended for end-users or implementers of OpenPGP libraries (or other software that directly handles internal OpenPGP data structures).
|
||||
|
||||
Instead, this document is focused on the second group, application developers, who use OpenPGP functionality in their software projects. It describes the properties of the OpenPGP system and its uses. It presupposes solid knowledge of software development concepts and of general cryptographic concepts. Thus, this text describes OpenPGP at the "library-level," teaching concepts that will help software developers get started as a user of any implementation (e.g., [OpenPGP.js](https://openpgpjs.org/), [Sequoia-PGP](https://sequoia-pgp.org/)).
|
||||
|
||||
With the emergence of a new crop of modern, high-quality OpenPGP libraries, and the imminent release of the updated [OpenPGP version 6 specification](https://datatracker.ietf.org/doc/draft-ietf-openpgp-crypto-refresh/), we think that now is a great time to implement OpenPGP functionality in applications or to modernize existing OpenPGP subsystems.
|
||||
|
||||
The goal of this document is to offer an implementation-independent introduction to the OpenPGP technology, assisting software developers in quickly familiarizing themselves and serving as a pathway to relevant information in the RFC.
|
||||
|
||||
## Why not just use the OpenPGP RFC?
|
||||
|
||||
The [OpenPGP RFC](https://datatracker.ietf.org/doc/draft-ietf-openpgp-crypto-refresh/)
|
||||
defines *the message formats used in OpenPGP.* That is, it describes the internal structure of OpenPGP data, which is crucial for OpenPGP library implementers. However, this level of detail is not required for software developers who use OpenPGP via a library.
|
||||
|
||||
This document describes OpenPGP concepts at the "library" level of abstraction, omitting unnecessary detail about the internal encoding of OpenPGP artifacts. Instead, we focus on the properties of these OpenPGP artifacts and how they are used, while adding context that is not elaborated on in the [RFC](https://en.wikipedia.org/wiki/Request_for_Comments).
|
||||
|
||||
## Which version of OpenPGP does this address?
|
||||
|
||||
This documentation encompasses the core aspects of modern OpenPGP practices, applicable across different versions. This respects that, at a foundational level, there is significant overlap, continuity, and consistency from its earliest version to its latest.
|
||||
|
||||
While using version 6 as a reference for current standards, we include insights derived from earlier versions, particularly version 4, which continues to be widely used in ongoing projects.
|
||||
|
||||
Where differences between OpenPGP versions are relevant to application development, we provide focused insights to ensure the content remains as version-agnostic as possible and, thus, broadly applicable for developers working with various iterations of OpenPGP.
|
57
book/source/acknowledgements.md
Normal file
|
@ -0,0 +1,57 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Acknowledgements
|
||||
|
||||
OpenPGP is a manifestation of [the political vision of Phil Zimmermann](https://philzimmermann.com/EN/background/peace.html), reflecting his commitment to human rights and the peace movement. Therefore, we foremost acknowledge Phil, who, alongside early PGP developers, worked with dedication and courage to introduce one of the first sophisticated cryptographic tools to the world, despite substantial personal risk.
|
||||
|
||||
All subsequent projects in the OpenPGP space are indebted to the foundational ideas and principles of those early days[^puzzle], including the pivotal decision to standardize the OpenPGP format through the IETF.
|
||||
|
||||
[^puzzle]: These ideas and principles include some ongoing challenges, such as the puzzle of how to develop a scalable distributed PKI and trust model.
|
||||
|
||||
In the intervening three decades, many have contributed to the OpenPGP space. We recognize the civic-oriented work of those who have helped evolve the OpenPGP ecosystem into a tool for digital empowerment.
|
||||
|
||||
## Team
|
||||
|
||||
The "Notes on OpenPGP" project is a collective endeavor, with contributions from a wide-ranging community.
|
||||
|
||||
The principal authors of this work, "OpenPGP for application developers," are:
|
||||
|
||||
- Tammi Coles (editor)
|
||||
- Sabrina Kurtz (illustrator)
|
||||
- Wiktor Kwapisiewicz
|
||||
- David Runge
|
||||
- Paul Schaub
|
||||
- Heiko Schäfer
|
||||
|
||||
## Expert advisors, readers, supporters
|
||||
|
||||
Andrew Gallagher provided input and discussions, particularly regarding the SKS Keyserver network.
|
||||
|
||||
Daniel Huigens was instrumental in shaping the early direction of this project.
|
||||
|
||||
Daniel Kahn Gillmor provided valuable input and encouragement.
|
||||
|
||||
Dennis Schmolk thoroughly reviewed the initial draft, offering detailed critiques.
|
||||
|
||||
Lance Vick contributed strategic communication advice.
|
||||
|
||||
Lars Wirzenius provided extensive feedback on early drafts.
|
||||
|
||||
Levente Polyak shared insights from the vantage point of an organization extensively using OpenPGP and developing custom OpenPGP applications.
|
||||
|
||||
Ryan Heywood gave early feedback on our binding signature visualizations.
|
||||
|
||||
## Extended hat tips
|
||||
|
||||
Werner Koch for his enduring work on [GnuPG](https://gnupg.org/) and for fostering an interoperable ecosystem.
|
||||
|
||||
[Sequoia-PGP](https://sequoia-pgp.org/), for bringing OpenPGP to the Rust language and developing an essential interoperability test suite.
|
||||
|
||||
ProtonMail for maintaining two crucial modern implementations of OpenPGP: [OpenPGP.js](https://openpgpjs.org/) and [GopenPGP](https://gopenpgp.org/).
|
||||
|
||||
## Funding
|
||||
|
||||
The [Sovereign Tech Fund](https://sovereigntechfund.de/) commissioned the initial development of this project from September to December 2023. We are extremely grateful for their support.
|
381
book/source/adv/certificates.md
Normal file
|
@ -0,0 +1,381 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Advanced material: Certificates
|
||||
|
||||
## When are certificates valid?
|
||||
|
||||
Certificates are composites of components that are linked together using [signatures](../signing_components).
|
||||
|
||||
A certificate can be valid or invalid as a whole. However, even when a certificate is valid, individual components (subkeys or identities) of it can be invalid.
|
||||
|
||||
In this section, we discuss the validity of certificates and their components. This discussion is closely related to [signature validity](/verification), and builds on that concept.
|
||||
|
||||
The validity of the signatures that link a certificate is a necessary precondition. Two concepts are particularly central to the validity of certificates and components:
|
||||
|
||||
- Expiration
|
||||
- Revocation
|
||||
|
||||
### Expiration
|
||||
|
||||
Certificates and components can "expire," which renders them invalid. Each component of a certificate can have an expiration time, or be unlimited in its temporal validity.
|
||||
|
||||
The OpenPGP software of a sender will refuse to encrypt email using an expired certificate, or using an encryption component key that is expired. The sender's software rejects encryption to the key, essentially as a courtesy to the certificate owner, respecting the preferences expressed in their certificate metadata.
|
||||
|
||||
The expiration mechanism in OpenPGP is complemented by a mechanism to extend/renew expiration time.
|
||||
|
||||
Using the expiration mechanism is useful for two reasons:
|
||||
|
||||
- Expiration of a certificate means that it cannot be used anymore. This forces users of that certificate (or their OpenPGP software) to poll for updates for it. For example, from a keyserver.
|
||||
- It is a passive way for certificates to "time out," e.g., if their owner loses control over them, or isn't able to broadcast a revocation, for any reason.
|
||||
|
||||
Component keys use *Key Expiration Time* subpackets for expressing the expiration time. Identity components rely on the [*signature expiration time*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#signature-expiration-subpacket) subpacket of their binding signature. If a binding signature expires, the binding becomes invalid, and the component is considered expired.
|
||||
|
||||
### Revocation
|
||||
|
||||
Since OpenPGP certificates act as ["append only" data structures](append-only), existing components or signatures cannot simply be "removed." Instead, they can be marked as invalid by issuing revocation signatures. These additional revocation signatures are added to the certificate.
|
||||
|
||||
Each component, such as User ID and a subkey, can be revoked without affecting the rest of the certificate.
|
||||
|
||||
The *primary User ID* is an exception: when it is revoked, the entire certificate is considered invalid.
|
||||
|
||||
Revoking the primary key with a [*Key revocation signature*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-key-revocation-signature-ty) (type ID `0x20`) also marks the entire certificate, including all of its components, as invalid and unusable.
|
||||
|
||||
### Semantics of Revocations
|
||||
|
||||
In contrast to expiration, revocation is typically final and not withdrawn[^undo-revocations].
|
||||
|
||||
[^undo-revocations]: While some revocations can be reverted, undoing revocations is an uncommon workflow. Unlike expirations, which are commonly undone by extending the expiration time.
|
||||
|
||||
A revocation indicates that the component should not be used. Revocation signatures over components use a [*Reason for Revocation*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#reason-for-revocation) subpacket to specify further details about the reason why the component or certification was revoked. The OpenPGP format specifies a set of distinct [values for *Reasons for Revocation*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#table-10), and additionally provides space for a human-readable free text field for comments about the revocation.
|
||||
|
||||
Some libraries, such as Sequoia PGP, expose these distinct reasons for users, enabling nuanced machine-readable statements by the revoker. Other implementations focus mainly on the distinction between "hard" and "soft" revocations.
|
||||
|
||||
Of the defined revocation types, *Key is superseded*, *Key is retired* and *User ID is no longer valid* are considered "soft" revocations. Any other reason (including a missing *reason for revocation* subpacket) means that the revocation is "hard."
|
||||
|
||||
The distinction between hard and soft revocations plays a role when evaluating the validity of a component or signature at a specified reference time: Hard revocations have unbounded [temporal validity](temporal-validity), they are in effect even before their creation time and therefore invalidate the revoked component or signature at all points in time.
|
||||
|
||||
By contrast, a soft revocation leaves the revoked component or signature valid before the creation time of the revocation signature. A soft revocation can technically be overridden, for example, with a newer binding signature (the new binding signature and its metadata then shadow the revocation and re-connect and re-validate the component).
|
||||
|
||||
Hard revocations address the following problem: If a private key was compromised, then the attacker can issue signatures using that key. This means, the attacker could issue a signature dated before the revocation, impersonating the owner of the key. A recipient of that signature would mistakenly consider this signature valid if the issuing key has been soft revoked. This is a problem.
|
||||
To counteract this problem, it is reasonable to clearly mark compromised keys as suspect at any point in time. That's what hard revocations do.
|
||||
|
||||
On the other hand, if the subkey was merely retired using a soft revocation, and the certificate holder moved to a different subkey, then the signatures in the past, made by the retired key, are still valid.
|
||||
|
||||
(append-only)=
|
||||
## Certificates are effectively append-only data structures
|
||||
|
||||
OpenPGP certificates act as *append-only data structures*, in practice. Packets that are associated with a certificate cannot be "recalled", once they were published. Third parties (such as other users, or keyservers) may keep and/or distribute copies of those packets.
|
||||
|
||||
While it is not possible to *remove* elements, once they were publicly associated with an OpenPGP certificate, it is possible to invalidate them by adding new metadata to the certificate. This new metadata could set an *expiration time* on a component, or explicitly *revoke* that component. In both cases, no packets are removed from the certificate.
|
||||
|
||||
Invalidation resembles removal of a component in a semantical sense. The component is not a valid element of the certificate anymore, at least starting from some point in time. Implementations that handle the certificate may omit the invalid component in their representation.
|
||||
|
||||
We have to distinguish the "packet level" information about a certificate from an application-level view of that certificate. The two may differ.
|
||||
|
||||
### Reasoning about append-only properties in a distributed system
|
||||
|
||||
OpenPGP is a decentral and distributed system. Users can obtain and transmit certificate information about their own, as well as other users', certificates using a broad range of mechanisms. These mechanisms include keyservers, manual handling, [Web Key Directory](https://datatracker.ietf.org/doc/draft-koch-openpgp-webkey-service/) (WKD) and [Autocrypt](https://en.wikipedia.org/wiki/Autocrypt).
|
||||
|
||||
Different users' OpenPGP software may obtain different views of a particular certificate, over time. Individual users' OpenPGP instances have to reconcile and store a combined version of the possibly disparate elements they obtain from different sources.
|
||||
|
||||
In practice, this means that various OpenPGP users may have differing views of any given certificate. For various reasons, not all users will be in possession of a fully up-to date and complete version of a certificate.
|
||||
|
||||
There are various potential problems associated with this fact: Users may not be aware that a component has been invalidated by the certificate holder. Revocations may not have been propagated to some third party. So for example, they may not be aware that the certificate holder has rotated their encryption subkey to a new one, and doesn't want to receive messages encrypted to the previous encryption subkey.
|
||||
|
||||
One mechanism that addresses a part of this issue is *expiration*: By setting their certificates to expire after an appropriate interval, certificate holders can force their communication partners to refresh their certificate, e.g. from a keyserver[^mgorny].
|
||||
|
||||
[^mgorny]: See, for example, [here](https://blogs.gentoo.org/mgorny/2018/08/13/openpgp-key-expiration-is-not-a-security-measure/): "Expiration times really serve two purposes: naturally eliminating unused keys, and enforcing periodical checks on the primary key."
|
||||
|
||||
Good practices, like setting appropriate expiration times, can mitigate the complexity of the inherently distributed nature of certificates.
|
||||
|
||||
However, such mitigations by definition cannot address all possible cases of outdated certificate information in a decentralized, asynchronous system such as OpenPGP. So a defensive approach is generally appropriate when reasoning about the view of certificates that different actors have.
|
||||
|
||||
When thinking about edge cases, it's useful to "assume the worst." For example:
|
||||
|
||||
- Recipients may not obtain updates to a certificate in a timely manner (this could happen for various reasons, including, but not limited to, interference by malicious actors).
|
||||
- Data associated with a certificate may compound, and a certificate can become too large for convenient handling, even in the course of normal operations (for example, a certificate may receive very many legitimate third-party certifications). If such a problem arises, then by definition, the certificate holder cannot address it: remember that the certificate holder cannot "recall" existing packets.
|
||||
|
||||
### Differing "views" of a certificate exist
|
||||
|
||||
Another way to think about this discussion is that different OpenPGP users may have a different view of any certificate. There is a notional "canonical" version of the certificate, but we cannot assume that every user has exactly this copy. Besides propagation of elements that the certificate holder has linked to a certificate, third-party certifications are by design a distributed mechanism. A third-party certification is issued by a third party, and may or may not be distributed widely by them, or by the certificate holder. Not distributing third-party certifications widely is a workflow that may be entirely appropriate for some use cases[^tpc-privacy].
|
||||
|
||||
[^tpc-privacy]: The two parties to a certification (the issuer and the target of the certification) may prefer not to publish their mutual association. Also see {ref}`social-graph-metadata-leak`.
|
||||
|
||||
As a general tendency, it is desirable for OpenPGP users to have the most complete possible view of all certificates that they interact with.
|
||||
|
||||
However, there are contexts in which it is preferable to only use a subset of the available elements of a certificate. We discuss this in the section {ref}`minimization`.
|
||||
|
||||
(certificate-merging)=
|
||||
## Merging
|
||||
|
||||
As described above, OpenPGP certificates are effectively [append-only](append-only) data structures. As part of the practical realization of this fact, OpenPGP software needs to *merge* different copies of a certificate.
|
||||
|
||||
For example, Bob's OpenPGP software may have a local copy of Alice's certificate, and obtain a different version of Alice's certificate from a keyserver. The goal of the implementation is to add new information about Alice's certificate, if any, to the local copy. Alice may have added a new identity, replaced a subkey with a new subkey, or revoked some components of her certificate. Or, Alice may have revoked her certificate, signaling that she doesn't want communication partners to use that certificate anymore. All of these updates could be crucial for Bob to be aware of.
|
||||
|
||||
Merging two versions of a certificate involves making decisions about which packets should be kept. The versions of the certificate will typically contain some packets that are identical. No duplicates of the exact same packet should be stored in the merged version of the certificate. Additionally, if the newly obtained copy contains packets that are in fact entirely unrelated to the certificate, those should not be retained (a third party may have included unrelated packets, either by mistake, or with malicious intent).
|
||||
|
||||
### Handling unauthenticated information
|
||||
|
||||
For information that *is* related to the certificate, but not bound to it by a self-signature, there is no generally correct approach. The receiving implementation must revolve these cases, possibly in a context-specific manner. Such cases include:
|
||||
|
||||
- Third-party certifications. These could be valuable information, where a third party attests that the association of an identity to a certificate is valid. On the other hand, they could also be a type of spam.
|
||||
- Subpackets in the unhashed area of a signature packet. Again, these could contain information that is useful to the recipient. However, the data could also be either useless, or even misleading/harmful.
|
||||
|
||||
(minimization)=
|
||||
## Certificate minimization
|
||||
|
||||
Certificate minimization is the practice of presenting a partial view of a certificate by filtering out some of its components.
|
||||
|
||||
Filtering out some elements of a certificate can serve various purposes:
|
||||
|
||||
- Omitting unnecessary components for specific use-cases. For example, email clients need encryption, signing and certification component keys, but not authentication subkeys, which are used, e.g., for SSH connections.
|
||||
- Omitting third-party certifications if they are not required for a use-case. ["Certificate flooding,"](https://dkg.fifthhorseman.net/blog/openpgp-certificate-flooding.html) for example, can lead to consumer software rejecting a certificate entirely. Filtering out third-party User ID certifications on import can mitigate this.
|
||||
- Sometimes, a certificate organically grows so big that the user software [has problems handling it](https://www.reddit.com/r/GnuPG/comments/bp23p4/my_key_is_too_large/).
|
||||
|
||||
### Elements that can be omitted as part of a minimization process
|
||||
|
||||
There are different types of elements that can be omitted during minimization:
|
||||
|
||||
- Subkeys (along with signatures on those subkeys)
|
||||
- Identity components (along with both their self-signatures and third-party signatures)
|
||||
- Signatures, by themselves:
|
||||
- Self-signatures that have been superseded by newer self-signatures for the same purpose
|
||||
- Third-party certifications
|
||||
|
||||
### Minimization in applications
|
||||
|
||||
#### Hagrid, which runs keys.openpgp.org
|
||||
|
||||
The [hagrid keyserver software](https://gitlab.com/keys.openpgp.org/hagrid) doesn't publish the identity components in certificates by default. This is a central aspect of the [privacy policy](https://keys.openpgp.org/about/privacy) of the service. Certificates can be uploaded to the service by third parties, which is useful. However, identifying information is only distributed by the service on an explicit opt-in basis.
|
||||
|
||||
Separately, third-party certifications are currently filtered out by the service, to avoid flooding attacks.
|
||||
|
||||
#### GnuPG
|
||||
|
||||
GnuPG offers two explicit methods for certificate minimization, described [in the GnuPG manual](https://www.gnupg.org/documentation/manuals/gnupg-devel/OpenPGP-Key-Management.html) as:
|
||||
|
||||
*clean*
|
||||
: *Compact (by removing all signatures except the selfsig) any user ID that is no longer usable (e.g. revoked, or expired). Then, remove any signatures that are not usable by the trust calculations. Specifically, this removes any signature that does not validate, any signature that is superseded by a later signature, revoked signatures, and signatures issued by keys that are not present on the keyring.*
|
||||
|
||||
*minimize*
|
||||
: *Make the key as small as possible. This removes all signatures from each user ID except for the most recent self-signature.*
|
||||
|
||||
`clean` removes third-party signatures by certificates that are not present in current keyring, as well as other stale data. `minimize` removes superseded signatures that are not needed at the point when the command is executed.
|
||||
|
||||
Independently, GnuPG by default [strips some signatures on key import](https://dev.gnupg.org/T4607#127792)[^gpg-default-strip]. However, a number of Linux distributions change this default behavior, and continue to import signatures without minimization by default. e.g. [Debian](https://dev.gnupg.org/T4628#128513) and Arch Linux: stripping third-party certifications on import, by default, is problematic for users who want to leverage authentication based on the [Web of Trust mechanism](wot).
|
||||
|
||||
[^gpg-default-strip]: GnuPG's changes in the default handling of third-party certifications on imports were prompted by the 2019 [keyserver flooding](keyserver-flooding) event.
|
||||
|
||||
### Limitations that can result from stripping historical self-signatures
|
||||
|
||||
Some implementations, such as Sequoia, prefer to rely on the full historical set of self-signatures to construct a view of the certificate over time. This way, signatures can be verified at different reference times. In this model, removing superseded self-signatures can cause problems with the validation of historical signature.
|
||||
|
||||
An example for the tension between minimization and nuanced verification of the [temporal validity](temporal-validity) of signatures can be seen in the case of rpm-sequoia. See [this discussion](https://github.com/rpm-software-management/rpm-sequoia/issues/50#issuecomment-1689642607) for details:
|
||||
|
||||
Initially, when checking the validity of a data signature for a software package, `rpm-sequoia` used the signature's creation time as the reference time. However, the availability of historical self-signatures in certificates is limited. So sometimes only a more recent self-signature for the primary key is available, and there is no evidence that the primary key was valid at the reference time.
|
||||
|
||||
To deal with this reality, the rpm-sequoia implementation was adjusted to accept data signatures that predate the validity of the current primary key self-signature[^primary-self-sig].
|
||||
|
||||
[^primary-self-sig]: Which in OpenPGP version 4 is often a primary User ID binding signature.
|
||||
|
||||
### Autocrypt
|
||||
|
||||
The [Autocrypt](https://autocrypt.org/) project describes itself as:
|
||||
|
||||
> [..] a set of guidelines for developers to achieve convenient end-to-end-encryption of e-mails. It specifies how e-mail programs negotiate encryption capabilities using regular e-mails.
|
||||
|
||||
The Autocrypt Level 1 specification defines a specific [minimal format for OpenPGP certificates](https://autocrypt.org/level1.html#openpgp-based-key-data) that are distributed by the autocrypt mechanism.
|
||||
|
||||
One goal of the Autocrypt mechanism is to distribute certificates widely. To this end, Autocrypt sends certificates in mail headers, where smaller size is greatly preferable.
|
||||
|
||||
Basic encrypted email functionality requires only a small subset of the recipient's certificate, so small certificate size is feasible.
|
||||
|
||||
### Minimization for email
|
||||
|
||||
Note that minimization of certificates isn't generally "right" or "wrong." The benefit or harm depends on the context.
|
||||
|
||||
For example, we might consider minimizing a certificate for distribution via WKD, with the use-case of email in mind.
|
||||
|
||||
Many certificates can be significantly pruned if the only goal of distributing them is to enable encryption and signature verification. For such cases, many components can be dropped, including invalid subkeys and their binding signatures, authentication subkeys (which are irrelevant to email), shadowed self-signatures, and third-party certifications. With many real-world certificates, the space savings of such a minimization are significant[^space-example].
|
||||
|
||||
Such minimization might be appropriate and convenient to enable encrypted communication with a ProtonMail client, which automatically fetches OpenPGP certificates via WKD while composing a message. The ProtonMail use case requires only component keys, not third-party certifications, and it doesn't require historical component keys or self-signatures.
|
||||
|
||||
However, in a different context, the same certificate might be fetched to verify the authenticity of a signature. In that case, third-party certifications may be crucial for the client. Stripping them could prevent the client from performing Web of Trust calculations and validating the authenticity of the certificate.
|
||||
|
||||
[^space-example]: The following fragment processes an example certificate. It drops any subkey that is not valid at the time of export (because of revocation or expiration), authentication subkeys, and any third-party certifications:
|
||||
|
||||
```sh
|
||||
gpg --export-options export-minimal,export-clean,no-export-attributes \
|
||||
--export-filter keep-uid=mbox=wiktor@metacode.biz \
|
||||
--export-filter 'drop-subkey=expired -t || revoked -t || usage =~ a' \
|
||||
--export wiktor@metacode.biz
|
||||
```
|
||||
|
||||
At the time of writing, the original certificate consists of 152322 bytes of data. The filtered variant consists of only 3771 bytes, which is 40x smaller. In some contexts, there are hard constraints on size, and minimization is unavoidable, e.g., when embedding certificate data in email headers.
|
||||
|
||||
### Pitfalls of minimization
|
||||
|
||||
Disadvantages/risks of minimizing certificates:
|
||||
|
||||
- A minimized certificate does not present a full view of how it (and the validity of its components) evolved over time.
|
||||
- As the OpenPGP subsystem on a user's computer learns about more certificates, third-party certifications that were previously unusable may become usable. Dropping third-party certifications by unknown issuers as a part of minimization prevents this mechanism.
|
||||
- An OpenPGP implementation that minimizes a certificate might remove component keys that it cannot use itself (e.g. because it doesn't support the algorithm of that key), even if the *receiving* implementation supports them.
|
||||
- Refreshing certificates from key servers may inflate the certificate again, since OpenPGP certificates tend to act as [append-only structures](append-only).
|
||||
- Some libraries, such as [anonaddy-sequoia](https://gitlab.com/willbrowning/anonaddy-sequoia/-/blob/master/src/sequoia.rs?ref_type=heads#L125) strip unusable encryption subkeys, but retain at least one subkey, even if all subkeys are expired. Although this may leave only an expired encryption subkey in the certificate, this presents a better UX for the end-user who potentially is still in possession of the private key for decryption.
|
||||
|
||||
## Guidelines
|
||||
|
||||
1. Don't minimize certificates unless you have a good reason to.
|
||||
2. When minimizing a certificate, minimize it in a way that suites your use-case. E.g., when minimizing a certificate for distribution alongside a signed software packet, make sure to include enough historical self-signatures as to not break the verification of the signed packet.
|
||||
3. When presenting a minimized view of a certificate to a consumer, consider when that a new version of that view needs to be generated. Ideally, minimized certificates are freshly generated on demand (e.g., an Autocrypt header is constructed while an email is sent or composed). The receiver is expected to typically merge all data it sees locally.
|
||||
|
||||
## Fingerprints and beyond: "Naming" certificates in user-facing contexts
|
||||
|
||||
Certificates in OpenPGP have traditionally often been "named" using hexadecimal strings of varying length.
|
||||
|
||||
For example, a business card might have shown the hexadecimal fingerprint of a person's OpenPGP certificate to facilitate secure communication. Over time, different formats and lengths for these identifiers have been used.
|
||||
|
||||
This section outlines the various ways in which certificates can be named, and their properties.
|
||||
|
||||
### Fingerprints and Key IDs in Version 4
|
||||
|
||||
With OpenPGP version 4 certificates, it was customary that user-facing software used 20 byte (160 bit) *fingerprints* as an identifier for the certificate. Or alternatively, the 8 byte (64 bit) *Key ID* variant of the fingerprint. Both were represented in hexadecimal format, sometimes with whitespace to group the identifier into blocks for easier readability.
|
||||
|
||||
Workflows such as
|
||||
|
||||
- accepting a certificate for a communication partner, or
|
||||
- issuing a third-party certification for an identity,
|
||||
|
||||
required users to manually compare the 40 character long hexadecimal representation of a fingerprint against a reference source for that fingerprint.
|
||||
|
||||
### Fingerprints in Version 6
|
||||
|
||||
The OpenPGP version 6 standard uses 32 byte (256 bit) fingerprints, but explicitly defines no format for displaying those fingerprints in a human-readable form. The standard [recommends strongly against](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-fingerprint-usability) using version 6 fingerprints as identifiers in user-facing workflows.
|
||||
|
||||
Instead, "mechanical fingerprint transfer and comparison" should be preferred, wherever possible. The reasoning is that humans tend to be bad at comparing high-entropy data[^schuermann] (in addition, many users are probably put off by being asked to compare long hexadecimal strings).
|
||||
|
||||
[^schuermann]: See "An Empirical Study of Textual Key-Fingerprint Representations" <https://www.ibr.cs.tu-bs.de/papers/schuermann-usenix2016.pdf>
|
||||
|
||||
### Use of Fingerprints and Key IDs in APIs
|
||||
|
||||
However, both Fingerprints and Key IDs may (and usually *must*) be used, programmatically, by software that handles OpenPGP data, to address specific certificates. This is equally true for OpenPGP version 6.
|
||||
|
||||
Note that regardless of the OpenPGP version, software that relies on 8-byte Key IDs should not assume that Key IDs are unique. It is trivial to generate collisions for 8-byte Key IDs, so applications must be able to handle Key ID collisions gracefully.
|
||||
|
||||
The historical 4-byte "short Key IDs" format should not be used anywhere, anymore (finding collisions in a 32-bit keyspace has been [trivial for a long time](https://evil32.com/)).
|
||||
|
||||
(certificate-lookup-by-email)=
|
||||
### Looking up certificates by email
|
||||
|
||||
Searching OpenPGP certificates by email is a use case that often arises. For example, when composing an email to a new contact, the sender may want to find the OpenPGP certificate for that contact.
|
||||
|
||||
Different mechanisms allow certificate lookup by email, for example:
|
||||
|
||||
- [Web Key Directory](https://datatracker.ietf.org/doc/draft-koch-openpgp-webkey-service/) (WKD)
|
||||
- The [keys.openpgp.org](https://keys.openpgp.org/) "verifying keyserver" (also known as ["hagrid"](https://gitlab.com/keys.openpgp.org/hagrid), the name of the server software it runs)
|
||||
- SKS-style OpenPGP keyservers (today, most of these run the [Hockeypuck](https://github.com/hockeypuck/hockeypuck) software)
|
||||
|
||||
Their properties differ, for more see [](certificate-distribution).
|
||||
|
||||
(certificate-freshness)=
|
||||
## Certificate freshness: Triggering updates with an expiration time
|
||||
|
||||
For a certificate holder, one problem is that their communication partners may not regularly poll for updates of their certificate.
|
||||
|
||||
A certificate holder usually prefers that everyone else regularly obtains updates for their certificate. This way, a third party will, for example, not mistakenly keep using the certificate indefinitely, after it gets revoked. Setting an expiration time on the certificate, ahead of time, limits the worst case scenario: communication partners will at most use a revoked certificate until its expiration time, even if they never learn of the revocation.
|
||||
|
||||
Once the expiration time is reached, third parties, or ideally their OpenPGP software will have to stop using the certificate, and may attempt to obtain an update for it. For example, from a keyserver, or via WKD. Ideally, certificate updates are obtained automatically, by the user's OpenPGP software, without any need for human intervention.
|
||||
|
||||
After the update, the updated copy of the certificate will usually have a fresh expiration time. The same procedure will repeat once that new expiration time has been reached.
|
||||
|
||||
(social-graph-metadata-leak)=
|
||||
## Metadata leak of Social Graph
|
||||
|
||||
Third-party certifications are signatures over identity components made by other users.
|
||||
|
||||
These certifications form the back-bone of the OpenPGP trust-model called the Web of Trust. The name stems from the fact that the collection of certifications forms a unidirectional graph resembling a web. Each edge of the graph connects the signing certificate to the identity component associated with another certificate.
|
||||
|
||||
OpenPGP software can inspect that graph. Based on the certification data in the graph and a set of trust anchors, it can infer whether a target certificate is legitimate.
|
||||
|
||||
The trust anchor is usually the certificate holder's own key, but a user may designate additional certificates of entities they are connected to as trust anchors.
|
||||
|
||||
Third-party certifications can be published as part of the target certificate to facilitate the process of certificate authentication. Unfortunately, a side effect of this approach is that it's feasible to reconstruct the entire social graph of all people issuing certifications. In addition, the signature creation time of certifications can be used to deduce whether the certificate owner attended a Key Signing Party (and if it was public, where it was held) and whom they interacted with.
|
||||
|
||||
So, there is some tension between the goals of
|
||||
|
||||
- a decentralized system where every participant can access certification information and perform analysis on it locally,
|
||||
- privacy related goals (also see {ref}`certificate-lookup-by-email`, for a comparison of certificate distribution mechanisms, which also touches on this theme).
|
||||
|
||||
(unbound-user-ids)=
|
||||
## Adding unbound, local User IDs to a certificate
|
||||
|
||||
Some OpenPGP software may add User IDs to a certificate, which are not bound to the primary key by the certificate's owner. This can be useful to store local identity information (e.g., Sequoia's public store attaches ["pet-names"][PET] to certificates, in this way).
|
||||
|
||||
[PET]: https://sequoia-pgp.org/blog/2023/04/08/sequoia-sq/#an-address-book-style-trust-model
|
||||
|
||||
Sequoia additionally certifies these "local, third party, User IDs" with a local trust anchor to facilitate local authentication decisions.
|
||||
To prevent accidental publication of these local User IDs (e.g. to public keyservers), Sequoia marks these binding signatures as "local" artifacts using [Exportable Certification](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-exportable-certification) subpackets to mark them as non-exportable.
|
||||
|
||||
(certificate-distribution)=
|
||||
## Certificate distribution mechanisms
|
||||
|
||||
Different mechanisms for discovering certificates, and updating certificate data exist in the OpenPGP space:
|
||||
|
||||
- A *Web Key Directory* service is based on a well-known location on a webserver, serving certificates in a specific format. A WKD server is operated by the entity that controls the DNS domain of an email-based identity of a certificate. This means that WKD is inherently decentralized, and the reliability of OpenPGP certificates may vary depending on the organization that operates a particular WKD instance.
|
||||
- The *keys.openpgp.org* service is a "verifying" keyserver: the keyserver software only publishes identity components (which include email addresses) after sending a verification email to that address, and receiving opt-in consent by the user of the email address. This service makes a different tradeoff: it is centralized, and relying on it to correctly perform the verification step requires trust in the operator. The tradeoff allows the service to only list identity information with the consent of the owner of that identity, and to prevent "enumeration" of the certificates and identities it stores (that is: third parties cannot obtain a list of email addresses in the service's database). By design, this service allows easy publication of revocations without requiring publication of any identity components.
|
||||
- *SKS-style keyservers* act as a distributed synchronizing database, which accepts certificate information without verification. The SKS network handles third-party signatures, additional changes to their handling are pending[^hip1].
|
||||
|
||||
[^hip1]: <https://github.com/hockeypuck/hockeypuck/issues/136>
|
||||
|
||||
One central difference between hockeypuck and hagrid (the software that runs the *keys.openpgp.org* service) is that hockeypuck distributes identity packets and third-party certifications that have indeterminate validity, while hagrid does not.
|
||||
|
||||
(keyserver-flooding)=
|
||||
## Third-party certification flooding
|
||||
|
||||
Traditional OpenPGP keyservers are one mechanism for [collection and distribution](certificate-distribution) of certificate information. Their model revolves around receiving certificate information from sources that don't identify themselves to the keyserver network. Traditionally, these keyservers have accepted both components bound to certificates by self-signatures, and third party identity certifications.
|
||||
|
||||
While a convenience for consumers, indiscriminately accepting and integrating third-party identity certifications comes with significant risks.
|
||||
|
||||
Without any restrictions in place, malicious entities can flood a certificate with excessive certifications. Called "certificate flooding," this form of digital vandalism grossly expands the certificate size, making the certificate cumbersome and impractical for users.
|
||||
|
||||
It also opens the door to potential denial-of-service attacks, rendering the certificate non-functional or significantly impeding its operation.
|
||||
|
||||
The popular [SKS keyserver network experienced certificate flooding firsthand](https://dkg.fifthhorseman.net/blog/openpgp-certificate-flooding.html) in 2019, causing significant changes to its operation.
|
||||
|
||||
```{note}
|
||||
The *keys.openpgp.org* (KOO) service performs a similar function as the SKS-style keyservers.
|
||||
However, there are major differences in its design and tradeoffs.
|
||||
|
||||
The KOO keyserver was designed to:
|
||||
|
||||
1. conform to [GDPR regulations](https://en.wikipedia.org/wiki/General_Data_Protection_Regulation), and
|
||||
2. be resistant to flooding-style vandalism.
|
||||
|
||||
To achieve these goals, KOO does not serve identity components at all, unless an explicit opt-in has been performed, using a confirmation process vial email. Third-party certifications are also not served by default, but only under very specific circumstances, which preclude flooding.
|
||||
```
|
||||
|
||||
### Hockeypuck-based keyservers
|
||||
|
||||
Currently, third-party certification flooding can be worked around by users or administrators requesting the removal/re-adding of a certificate. [See here](https://github.com/hockeypuck/hockeypuck/wiki/HIP-1:-Regaining-control-over-public-key-identity-with-authenticated-key-management).
|
||||
|
||||
Additional mechanisms [are upcoming](support-for-1pa3pc).
|
||||
|
||||
## First-Party attested third-party certifications in OpenPGP (1pa3pc)
|
||||
|
||||
[First-Party attested third-party certifications in OpenPGP](https://datatracker.ietf.org/doc/draft-dkg-openpgp-1pa3pc/) are a "mechanism to allow the owner of a certificate to explicitly approve of specific third-party certifications". 1pa3pc was designed to enable flooding-proof distribution of third-part certifications.
|
||||
|
||||
This mechanism uses the *attested certifications* signature subpacket (type ID `37`), which currently only exists as a proposed feature in [draft-ietf-openpgp-rfc4880bis](https://www.ietf.org/archive/id/draft-ietf-openpgp-rfc4880bis-10.html#table-3)[^ac-draft].
|
||||
|
||||
[^ac-draft]: Introducing the *attested certifications* signature subpacket (type ID `37`) was unfortunately not in scope of the chartered topics for the current "crypto-refresh" work of the OpenPGP working group. However, hopefully the working group can handle this feature in future rechartering.
|
||||
|
||||
(support-for-1pa3pc)=
|
||||
### Support
|
||||
|
||||
- The *keys.openpgp.org* (KOO) keyserver [supports *1pa3pc*](https://gitlab.com/keys.openpgp.org/hagrid/-/commit/39c0e12ac64588220d36bada6497d8396f5915b3).
|
||||
|
||||
- The Hockeypuck keyserver software [plans to add support for *1pa3pc*](https://github.com/hockeypuck/hockeypuck/issues/136#issuecomment-1812466084) in version 2.2.0.
|
||||
|
||||
- The Sequoia `sq` commandline tool [allows adding](https://man.archlinux.org/man/sq-key-attest-certifications.1) attested third-party certifications to a certificate.
|
29
book/source/adv/decryption.md
Normal file
|
@ -0,0 +1,29 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Advanced material: Decryption
|
||||
|
||||
(decryption-seipd-quick-check)=
|
||||
## Verify successful session key decryption
|
||||
|
||||
SEIPDv1 packets might make use of a "quick check" mechanism to quickly verify that the correct session key was used without the need to decrypt the whole SEIPD packet.
|
||||
This check consists of 16 random bytes, followed by a copy of the two last bytes, which are prefixed to the plaintext.
|
||||
During decrypting, these 2 bytes can be compared to the 15th and 16th random byte to detect use of the wrong session key.
|
||||
|
||||
Since the chance to accidentally end up with matching quick check bytes albeit the use of the wrong session key is 1:65536, some implementations validate further contents of the plaintext, such as the packet headers.
|
||||
|
||||
The standard [warns against](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-risks-of-a-quick-check-orac) using the quick check mechanism, as it introduces the risk of a decryption oracle. Instead, the use of SEIPDv2 is recommended, as the AEAD mechanism automatically detects use of the wrong session key early on after the first chunk has been decrypted.
|
||||
|
||||
(decryption-anonymous-recipient)=
|
||||
## Anonymous recipients
|
||||
|
||||
Having all recipients keys listed as part of the PKESK packets presents a metadata leakage. An observer can easily enumerate recipients of a message by comparing the PKESKs with certificates of potential recipients.
|
||||
|
||||
To prevent this issue, the sender can decide to add individual recipients as anonymous recipients using a wildcard key-ID / fingerprint.
|
||||
This is done by creating a normal PKESK packet for the recipient, but setting the recipient key field to `0` (as well as omitting the version number of the key for v6 PKESKs).
|
||||
|
||||
A recipient of such a message that does not find a PKESK addressed specifically to any of their keys, can then try to decrypt any anonymous PKESK packets using any of their encryption subkeys.
|
||||
|
||||
To reduce the number of keys to try, the recipient can skip all secret keys which do not share the public-key algorithm stated in the PKESK packet.
|
32
book/source/adv/encryption.md
Normal file
|
@ -0,0 +1,32 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Advanced material: Encryption
|
||||
|
||||
## Encrypt to multiple/single subkey per certificate?
|
||||
|
||||
A recipient's certificate can contain more than one usable encryption subkey.
|
||||
This raises the question, should the message be encrypted for all of them?
|
||||
|
||||
There is the argument that a powerful attacker might have managed to add an attacker-controlled encryption subkey to the victim's certificate.
|
||||
In this case, only encrypting to the "newest" encryption key would help uncover such an attack. However, a powerful attacker could just MitM any sent messages and just add a PKESK for the victim-controlled encryption keys to hide the fact that the sender used a different key.
|
||||
|
||||
On the other hand, a user might have multiple encryption subkeys on purpose.
|
||||
Picture, for example, a scenario where the same certificate is used on multiple devices, but each device has dedicated encryption subkeys to allow for smoother revocation in case of a lost device.
|
||||
In this scenario, it is important that the sender encrypts the message to all available encryption subkeys.
|
||||
|
||||
## "Negotiating" algorithms based on recipients preference subpackets
|
||||
|
||||
### Prevent "downgrade" -> Policy
|
||||
|
||||
Each implementation should define a "minimum" level of security when it comes to algorithms and key lengths.
|
||||
If the lowest common denominator of symmetric encryption algorithms preferred by a set of recipients provides too little security, the implementation should either use a configured fallback algorithm instead, or fail to produce a message at all.
|
||||
|
||||
## AEAD modes in v2 SEIPD: GCM
|
||||
|
||||
```{note}
|
||||
|
||||
This section is still about to be written.
|
||||
```
|
82
book/source/adv/private_keys.md
Normal file
|
@ -0,0 +1,82 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Advanced material: Private keys
|
||||
|
||||
(key-store-design)=
|
||||
## Private keystores
|
||||
|
||||
This section examines the diverse architectures and operational mechanics of private keystores in OpenPGP. It focuses on the various design choices, their functional implications, and how they contribute to the secure management of private key material.
|
||||
|
||||
### Design variations
|
||||
|
||||
The design of private key subsystems within the OpenPGP framework varies, offering different approaches to cryptographic operations:
|
||||
|
||||
1. **Separate backend operations**: Some designs execute the primitive cryptographic operations in a separate backend, using only the cryptographic key material. This approach is particularly compatible with general purpose hardware cryptographic devices, such as [trusted platform modules (TPMs)](https://en.wikipedia.org/wiki/Trusted_Platform_Module).
|
||||
2. **Component key-based systems**: An OpenPGP private key subsystem may be built around component keys, specifically the content of [Secret-Key packets](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-secret-key-packet-formats). These packets contain metadata that is required for some operations. ECDH operations, in particular, require metadata as KDF parameters.
|
||||
3. **Full transferable secret keys**: Some designs maintain copies of full TSKs in the private key subsystem, leveraging these for private key operations.
|
||||
|
||||
While private keystore operations require component keys, they do not require access to the entire OpenPGP certificate.
|
||||
|
||||
```{note}
|
||||
The third design option, involving the storage of full TSKs in the private key subsystem, can cause "split brain" problems.
|
||||
|
||||
For example, a private keystore might contain a TSK with outdated certificate metadata, marking the certificate as expired, while the updated version in the local public keystore could indicate an extended expiration time.
|
||||
|
||||
This problem was notably present in GnuPG 1.x, which held separate TSK copies in its private store component. Similarly, the current design of Thunderbird's OpenPGP subsystem can lead to users experiencing such issues.
|
||||
```
|
||||
|
||||
### Two-tier architecture
|
||||
|
||||
At its core, an OpenPGP private key subsystem performs operations requiring only the private cryptographic key material, akin to the "separate backend operations" model described above.
|
||||
|
||||
However, the subsystem also supports operations that require additional access to the metadata of the component key. These operations, supplementary to the core keystore operations, do not involve the private key material.
|
||||
|
||||
When implementing a keystore based on hardware cryptographic devices like [OpenPGP card](openpgp-card), its design will consist of two layers:
|
||||
|
||||
- **core layer**: directly handles private key material, and
|
||||
- **supplementary layer**: performs additional cryptographic operations that don't directly use the private key material, such as [AES key wrap](https://www.rfc-editor.org/rfc/rfc3394.html) for ECDH.
|
||||
|
||||
```{note}
|
||||
The decryption process using ECC algorithms, especially ECDH, has multiple steps. The initial step, potentially performed by devices such as OpenPGP cards, directly uses private key material to produce a "shared secret." Following this, operations like ["AES key unwrap"](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-ec-dh-algorithm-ecdh) are conducted in software outside the hardware device.
|
||||
|
||||
Further details on this process can be found in the "Advanced Encryption Standard (AES) Key Wrap Algorithm" [RFC 3394](https://www.rfc-editor.org/rfc/rfc3394.html).
|
||||
```
|
||||
|
||||
### Addressing individual keys
|
||||
|
||||
A critical aspect of private keystore design involves determining how users address individual keys.
|
||||
|
||||
One common method is using the fingerprint of each component key. The availability of these fingerprints, however, depends on the underlying technology of the keystore. For instance, in software-based private keystores or OpenPGP cards, fingerprints of component keys are usually readily available. Keystores relying on generic cryptographic hardware, like TPMs, need to implement their own mechanisms for tracking and managing the fingerprints of each key.
|
||||
|
||||
### Additional keystore duties
|
||||
|
||||
In addition to key management, a keystore often involves various supplementary functions:
|
||||
|
||||
- **Tracking devices**: Keystores may track which devices contain particular component keys.
|
||||
|
||||
- **Handling secrets**: This involves the management of sensitive information such as passphrases for software keys or PINs for OpenPGP cards.
|
||||
|
||||
- **User interaction alerts**: Keystores might also need to prompt users for necessary interactions during certain operations. For example, OpenPGP cards may require user touch confirmation to authorize each cryptographic action.
|
||||
|
||||
## Understanding key overwriting (KO) attacks
|
||||
|
||||
### What they are
|
||||
|
||||
OpenPGP is subject to specific vulnerabilities known as key overwriting (KO) attacks. These attacks exploit weaknesses in how encrypted private keys or their metadata are handled, potentially leading to the leakage of secret data when the key is used. The core issue lies in OpenPGP's handling of Secret-Key packets, where corruption of the non-encrypted fields can cause the unaltered private key material to be used with altered parameters. This mismatch can result in private key leakage.
|
||||
|
||||
Importantly, KO attacks are particularly relevant when an attacker is responsible for storing a user's encrypted private key. By altering the algorithm field in the Secret-Key packet, the attacker may cause the user to perform a cryptographic operation with a different algorithm. E.g., performing a DSA operation with ECC private key material. By observing the output of that attacker-corrupted operation, the attacker can recover the user's unencrypted private key material, even though the attacker had no direct access to it.
|
||||
|
||||
### Mitigation
|
||||
|
||||
Understanding KO attacks is crucial due to their potential to compromise the integrity and confidentiality of encrypted communications, and the risk of complete private key material compromise. KO attacks highlight the necessity for robust key validation procedures and the dangers of storing keys in insecure environments. OpenPGP application developers should consider if this attack class is a concern in their applications.
|
||||
|
||||
Private keys that are protected with [S2K usage mode 253 (AEAD)](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-secret-key-encryption), are not vulnerable to KO attacks. This mode ensures the integrity of the private key by using its unencrypted fields (including the algorithm field) as the *authentication tag* for integrity verification in the decryption process. When an attacker alters the unencrypted part of the packet, then decryption of the private key material will fail, and the user is prevented from e.g. accidentally using the key material with an altered attacker-controlled algorithm.
|
||||
|
||||
Note that while S2K usage mode 253 (AEAD) has been introduced in the OpenPGP version 6 specification, it can also be applied to OpenPGP version 4 key material (also see {ref}`migration-s2k`).
|
||||
|
||||
#### Resources
|
||||
|
||||
For comprehensive information on KO attacks, including background, attack vectors, countermeasures, and technical analyses, visit [KOpenPGP.com](https://www.kopenpgp.com/). It is based on the paper "Victory by KO: Attacking OpenPGP Using Key Overwriting" written by Lara Bruseghini, Daniel Huigens, and Kenneth G. Paterson for the Proceedings of ACM Conference on Computer and Communications Security, Los Angeles, November 2022.
|
55
book/source/adv/signatures.md
Normal file
|
@ -0,0 +1,55 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Advanced material: Signatures
|
||||
|
||||
(notation-signature-subpackets)=
|
||||
## Notation signature subpackets
|
||||
|
||||
[Notation signature subpackets](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#notation-data) can be used to effectively extend the otherwise limited set of {term}`signature subpacket types<OpenPGP Signature Subpacket Type>` in OpenPGP with user-defined {term}`notations<Notation>`. {term}`Issuers<Issuer>` can use these {term}`notations<Notation>` to add name-value pairs to an {term}`OpenPGP signature<OpenPGP Signature Packet>`.
|
||||
|
||||
{term}`Notation` names - strings encoded in UTF-8 - may reside in the "user namespace." This namespace is characterized by a {term}`notation tag`, followed by a DNS domain name, both in UTF-8 format.
|
||||
|
||||
{term}`Notations<Notation>`, as described earlier, allow for user-defined extensions to the {term}`OpenPGP signature subpacket types<OpenPGP Signature Subpacket Type>`. A practical and popular application of this functionality is seen in Keyoxide, a decentralized {term}`identity verification` service. Keyoxide uses {term}`notations<Notation>` in the `ariadne.id` namespace. For the details of this {term}`implementation<OpenPGP Implementation>`, refer to the [Keyoxide documentation](https://docs.keyoxide.org/wiki/ariadne-identity/).
|
||||
|
||||
## Choosing the hash algorithm for a signature
|
||||
|
||||
A central element of signature packets is the hash digest of the input data. Most OpenPGP software supports a set of different hash mechanisms, of which one is chosen for each signature packet (this is one aspect of OpenPGP's *cryptographic agility*), and used to calculate the hash digest.
|
||||
|
||||
Different hash mechanisms offer different trade-offs:
|
||||
|
||||
- *Hash digest size*: Larger hash size tends to correspond with greater strength against cryptanalysis, and hash digests are relatively small: at the time of this writing, typical sizes are 32 to 64 bytes. However, for some use cases - especially where small messages are sent over a bandwidth-limited transport - larger hash sizes may unacceptably increase message size.
|
||||
- *Computational cost*: Different hash algorithms may have different computational costs. Some OpenPGP users may prefer to limit this cost, for example on constrained computing environments.
|
||||
|
||||
The following sections discuss how the hash algorithm is chosen, based on preferences that are associated with the involved OpenPGP certificates.
|
||||
|
||||
### Typically: Local determination
|
||||
|
||||
Often, signature creation isn't targeted at a specific receiver. Many signatures are issued for an indeterminate set of "anyone who receives the signature."
|
||||
|
||||
For example, self-signatures that form a certificate are aimed at everyone who interacts with that certificate. Similarly, when creating a data signature for a software package, this signature is aimed at "anyone who will check the signature," often over a long period of time, easily spanning years.
|
||||
|
||||
In such cases, the issuer of that signature chooses the hash algorithm locally, without following preferences of a third party.
|
||||
|
||||
### With a specific recipient: "Negotiation" based on recipient's preferences
|
||||
|
||||
In contrast, when a message is created for a specific recipient, the sender can - and should - choose the hash algorithm for the signature packet [based on the recipient's hash algorithm preference](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-hash-algorithm-preferences).
|
||||
|
||||
The recipient's hash algorithm preference is defined in metadata of their certificate, see {ref}`preferences-features` for more details.
|
||||
|
||||
In this workflow, the signed hash digest is created with a hash algorithm that follows the recipient's preferences, and its intersection with the sender's capabilities and preferences.
|
||||
|
||||
## Signature versions
|
||||
|
||||
As described in the [RFC](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-signature-packet-type-id-2), the version of a generated signature packet must conform to the version of the key that issues the signature.
|
||||
|
||||
That is:
|
||||
|
||||
- OpenPGP version 6 keys must generate version 6 signature packets
|
||||
- OpenPGP version 4 keys must generate version 4 signature packets
|
||||
|
||||
Note that some historical version 3 signature packets may still be relevant for applications that handle old OpenPGP data[^sig-v3]. These version 3 signature packets will have been generated by version 4 keys.
|
||||
|
||||
[sig-v3]Version 4 signature packets were introduced in [RFC 2440](https://datatracker.ietf.org/doc/html/rfc2440#section-5.2) in 1998, which specifies that applications SHOULD generate v4 signature, however generation of v3 signature packets has remained allowed through [RFC 4880](https://www.rfc-editor.org/rfc/rfc4880.html#section-5.2).
|
145
book/source/adv/signing_components.md
Normal file
|
@ -0,0 +1,145 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Advanced material: Signatures on components
|
||||
|
||||
## Certification recipes
|
||||
|
||||
Different {term}`signatures<OpenPGP Signature Packet>` in OpenPGP serve various specific purposes. This section provides practical guidance on creating these {term}`signatures<OpenPGP Signature Packet>`, illustrating each with concrete examples.
|
||||
|
||||
(recipe-algorithm-preferences)=
|
||||
### Change algorithm preferences
|
||||
|
||||
To modify the preferred {term}`symmetric<Symmetric Cryptography>`, compression, {term}`hash<Hash Function>`, or {term}`AEAD algorithms<Authenticated Encryption With Associated Data>` for a {term}`key<Transferable Secret Key>`, the {term}`key owner<Certificate Holder>` needs to issue a {term}`direct key signature` ({term}`type ID<Signature Type ID>` `0x1F`) on the {term}`primary key<OpenPGP Primary Key>`.
|
||||
|
||||
This {term}`signature<OpenPGP Signature Packet>` should have the following structure:
|
||||
|
||||
| {term}`Subpacket<OpenPGP Signature Subpacket>` | Area | {term}`Critical<Criticality Flag>` | Mandatory | Notes |
|
||||
|---------------------------------------------------------------------------------------------|-----------------------------|------------------------------------------|----------------------|----------------------------------------------------------------------------------------|
|
||||
| {term}`Signature Creation Time<Signature Creation Time Subpacket>` | {term}`Hashed<Hashed Area>` | True | True | Current time |
|
||||
| {term}`Issuer Fingerprint<Issuer Fingerprint Subpacket>` | {term}`Hashed<Hashed Area>` | True or False | Strongly Recommended | The {term}`primary key` is the {term}`issuer` |
|
||||
| {term}`Key Flags<Key Flag>` | {term}`Hashed<Hashed Area>` | True | False | Retain {term}`key flags<Key Flag>` from the previous {term}`self-signature` |
|
||||
| {term}`Features<Features Subpacket>` | {term}`Hashed<Hashed Area>` | True | False | Retain {term}`features<Features Subpacket>` from the previous {term}`self-signature` |
|
||||
| {term}`Key Expiration Time<Key Expiration Time Subpacket>` | {term}`Hashed<Hashed Area>` | True | False | Retain {term}`expiration time` from the previous {term}`self-signature`, if applicable |
|
||||
| {term}`Hash Algorithm Preferences<Preferred Hash Algorithms Subpacket>` | {term}`Hashed<Hashed Area>` | False | False | New {term}`preferences<Algorithm Preferences>` |
|
||||
| {term}`Compression Algorithm Preferences<Preferred Compression Algorithms Subpacket>` | {term}`Hashed<Hashed Area>` | False | False | New {term}`preferences<Algorithm Preferences>` |
|
||||
| {term}`Symmetric Algorithm Preferences<Preferred Symmetric Ciphers for v1 SEIPD Subpacket>` | {term}`Hashed<Hashed Area>` | False | False | New {term}`preferences<Algorithm Preferences>` |
|
||||
| {term}`AEAD Algorithm Preferences<Preferred AEAD Ciphersuites Subpacket>` | {term}`Hashed<Hashed Area>` | False | False | New {term}`preferences<Algorithm Preferences>` |
|
||||
|
||||
### Change expiration time
|
||||
|
||||
To adjust the {term}`expiration time` of an {term}`OpenPGP certificate`, a new *{term}`DirectKey<Direct Key Signature>`* {term}`signature<OpenPGP Signature Packet>` ({term}`type ID<Signature Type ID>` `0x1F`) with a modified {term}`Key Expiration Time subpacket` must be issued. The structure of this {term}`signature<OpenPGP Signature Packet>` is identical to the one outlined in the previous section on changing {term}`algorithm preferences`.
|
||||
|
||||
Additionally, the {term}`expiration time` can be altered for individual {term}`User IDs<User ID>` (detailed below) or separate {term}`subkeys<OpenPGP Subkey>` (see {numref}`bind-subkey`).
|
||||
|
||||
### Add User ID
|
||||
|
||||
To {term}`bind<Binding>` a {term}`User ID` to an {term}`OpenPGP certificate` a {term}`certification signature<Certification>` ({term}`type ID<Signature Type ID>` `0x10`-`0x13`) is used which should have the following structure:
|
||||
|
||||
| {term}`Subpacket<OpenPGP Signature Subpacket>` | Area | {term}`Critical<Criticality Flag>` | Mandatory | Notes |
|
||||
|------------------------------------------------------------------------|-----------------------------|------------------------------------------|----------------------|-------------------------------------------------------------------------------------|
|
||||
| {term}`Signature Creation Time<Signature Creation Time Subpacket>` | {term}`Hashed<Hashed Area>` | True | True | Current time |
|
||||
| {term}`Issuer Fingerprint<Issuer Fingerprint Subpacket>` | {term}`Hashed<Hashed Area>` | True or False | Strongly Recommended | The {term}`primary key<OpenPGP Primary Key>` is the {term}`issuer` |
|
||||
| {term}`Primary User ID<Primary User ID Subpacket>` | {term}`Hashed<Hashed Area>` | True | False | Optional |
|
||||
| {term}`Signature Expiration Time<Signature Expiration Time Subpacket>` | {term}`Hashed<Hashed Area>` | True | False | Optional |
|
||||
|
||||
In addition to these {term}`subpackets<OpenPGP Signature Subpacket>`, {term}`self-certifications<Self-certification>` for {term}`User IDs<User ID>` can include others – such as {term}`key flags<Key Flag>`, {term}`features<Features Subpacket>`, and {term}`algorithm preferences` – as shown in the previous table. This enables the specification of unique capabilities and {term}`preferences<Algorithm Preferences>` for each {term}`identity` associated with the {term}`certificate<OpenPGP Certificate>`.
|
||||
|
||||
### Remove or revoke a User ID
|
||||
|
||||
Since {term}`OpenPGP certificates<OpenPGP certificate>` are often distributed by the means of {term}`key servers<Key Server>`, new {term}`signatures<OpenPGP Signature Packet>` on a {term}`certificate<OpenPGP Certificate>` are often "[merged](certificate-merging)" into existing copies of the {term}`certificate<OpenPGP Certificate>` locally by the recipient.
|
||||
|
||||
This integration process means it is practically impossible to directly remove {term}`signatures<OpenPGP Signature Packet>` or {term}`User IDs<User ID>` from a {term}`certificate<OpenPGP Certificate>`, as there is no way to communicate the intention of {term}`packet<OpenPGP Signature Packet>` deletion to the recipient.
|
||||
|
||||
To effectively mark a {term}`User ID` as invalid, the user can publish a copy of their {term}`certificate<OpenPGP Certificate>` with a {term}`Certification Revocation signature<Certification Revocation Signature Packet>` ({term}`type ID<Signature Type ID>` `0x30`) attached to the invalidated {term}`User ID`. This {term}`signature<OpenPGP Signature Packet>` signals that the specified {term}`User ID` is no longer valid or associated with the {term}`certificate holder`.
|
||||
|
||||
The structure of a {term}`Certification Revocation signature<Certification Revocation Signature Packet>` is as follows:
|
||||
|
||||
| {term}`Subpacket<OpenPGP Signature Subpacket>` | Area | {term}`Critical<Criticality Flag>` | Mandatory | Notes |
|
||||
|--------------------------------------------------------------------|-----------------------------|------------------------------------------|----------------------|-------------------------------------------------------------------------------------|
|
||||
| {term}`Signature Creation Time<Signature Creation Time Subpacket>` | {term}`Hashed<Hashed Area>` | True | True | Current time |
|
||||
| {term}`Issuer Fingerprint<Issuer Fingerprint Subpacket>` | {term}`Hashed<Hashed Area>` | True or False | Strongly Recommended | The {term}`primary key<OpenPGP Primary Key>` is the {term}`issuer` |
|
||||
| {term}`Reason for Revocation<Reason for Revocation Subpacket>` | {term}`Hashed<Hashed Area>` | True | False | Determines {term}`soft<Soft Revocation>` or {term}`hard revocation` |
|
||||
|
||||
For {term}`User ID` {term}`revocations<Revocation>`, the *{term}`Reason for Revocation<Reason for Revocation Subpacket>`* {term}`subpacket<OpenPGP Signature Subpacket>` is crucial. A value of `0` means no specific reason, leading to a {term}`hard revocation`, while `32` indicates the {term}`User ID` is no longer valid, resulting in a {term}`soft revocation`. Omitting the {term}`reason subpacket<Reason For Revocation Subpacket>` is also equivalent to a {term}`hard revocation`.
|
||||
|
||||
It is generally advisable to use reason code `32` for revoking {term}`User IDs<User ID>`.
|
||||
|
||||
(recipe-binding-subkeys)=
|
||||
### Add a subkey
|
||||
|
||||
As part of {term}`life-cycle management`, users may need to add a new {term}`subkey<OpenPGP Subkey>` to their {term}`OpenPGP certificate`, often for reasons such as upgrading to a {term}`subkey<OpenPGP Subkey>` with more advanced cryptographic algorithms. The process involves creating a specific {term}`signature<OpenPGP Signature Packet>` structure:
|
||||
|
||||
| {term}`Subpacket<OpenPGP Signature Subpacket>` | Area | {term}`Critical<Criticality Flag>` | Mandatory | Notes |
|
||||
|---------------------------------------------------------------------------------------------|-----------------------------|------------------------------------------|-------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| {term}`Signature Creation Time<Signature Creation Time Subpacket>` | {term}`Hashed<Hashed Area>` | True | True | Current time |
|
||||
| {term}`Issuer Fingerprint<Issuer Fingerprint Subpacket>` | {term}`Hashed<Hashed Area>` | True or False | Strongly Recommended | The {term}`primary key<OpenPGP Primary Key>` is the {term}`issuer` |
|
||||
| {term}`Key Flags<Key Flag>` | {term}`Hashed<Hashed Area>` | True | Strongly Recommended | Determine the usage of the {term}`key<OpenPGP Subkey>` |
|
||||
| {term}`Key Expiration Time<Key Expiration Time Subpacket>` | {term}`Hashed<Hashed Area>` | True | False | Specifies the {term}`expiration time` of the {term}`subkey<OpenPGP Subkey>` |
|
||||
| {term}`Embedded Signature<Embedded Signature Subpacket>` | {term}`Hashed<Hashed Area>` | True | If {term}`Key Flags<Key Flag>` contains **{term}`S<Signing Key Flag>`** | {term}`Signing subkeys<OpenPGP Signing Subkey>` require embedded *{term}`Primary Key Binding<Primary Key Binding Signature>`* {term}`signature<OpenPGP Signature Packet>` |
|
||||
| {term}`Hash Algorithm Preferences<Preferred Hash Algorithms Subpacket>` | {term}`Hashed<Hashed Area>` | False | False | Per {term}`key<Component Key>` {term}`preferences<Algorithm Preferences>` |
|
||||
| {term}`Compression Algorithm Preferences<Preferred Compression Algorithms Subpacket>` | {term}`Hashed<Hashed Area>` | False | False | Per {term}`key<Component Key>` {term}`preferences<Algorithm Preferences>` |
|
||||
| {term}`Symmetric Algorithm Preferences<Preferred Symmetric Ciphers for v1 SEIPD Subpacket>` | {term}`Hashed<Hashed Area>` | False | False | Per {term}`key<Component Key>` {term}`preferences<Algorithm Preferences>` |
|
||||
| {term}`AEAD Algorithm Preferences<Preferred AEAD Ciphersuites Subpacket>` | {term}`Hashed<Hashed Area>` | False | False | Per {term}`key<Component Key>` {term}`preferences<Algorithm Preferences>` |
|
||||
|
||||
In addition to these {term}`subpackets<OpenPGP Signature Subpacket>`, users can specify {term}`algorithm preferences` for each {term}`subkey<OpenPGP Subkey>`, distinct from those set in the {term}`certificate<OpenPGP Certificate>`'s *{term}`Direct Key<Direct Key Signature>`* {term}`signature<OpenPGP Signature Packet>`.
|
||||
|
||||
### Revoke a subkey
|
||||
|
||||
{term}`Subkeys<OpenPGP subkey>`, like {term}`User IDs<User ID>`, can be individually revoked in OpenPGP.
|
||||
This is done by issuing a {term}`Subkey Revocation signature<Subkey Revocation Signature Packet>` ({term}`type ID<Signature Type ID>` `0x28`) using the {term}`primary key<OpenPGP Primary Key>`.
|
||||
|
||||
The structure of such a {term}`signature<OpenPGP Signature Packet>` is straightforward:
|
||||
|
||||
| {term}`Subpacket<OpenPGP Signature Subpacket>` | Area | {term}`Critical<Criticality Flag>` | Mandatory | Notes |
|
||||
|--------------------------------------------------------------------|-----------------------------|------------------------------------------|----------------------|-------------------------------------------------------------------------------------|
|
||||
| {term}`Signature Creation Time<Signature Creation Time Subpacket>` | {term}`Hashed<Hashed Area>` | True | True | Current time |
|
||||
| {term}`Issuer Fingerprint<Issuer Fingerprint Subpacket>` | {term}`Hashed<Hashed Area>` | True or False | Strongly Recommended | The {term}`primary key<OpenPGP Primary Key>` is the {term}`issuer` |
|
||||
| {term}`Reason for Revocation<Reason For Revocation Subpacket>` | {term}`Hashed<Hashed Area>` | True | False | Determines {term}`soft<Soft Revocation>` or {term}`hard revocation` |
|
||||
|
||||
|
||||
In {term}`Subkey Revocation signatures<Subkey Revocation Signature Packet>`, the [reason for revocation](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-reason-for-revocation) {term}`subpacket<OpenPGP Signature Subpacket>` can only have values in the range of `0-3`. The values `1` ({term}`key<OpenPGP Subkey>` superseded) and `3` ({term}`key<OpenPGP Subkey>` retired and no longer used) indicate {term}`soft revocations<Soft Revocation>`, whereas values `0` (no reason) and `2` ({term}`key<OpenPGP Subkey>` compromised) indicate {term}`hard revocations<Hard Revocation>`.
|
||||
|
||||
Note that a value of `32` is not applicable in these {term}`signatures<OpenPGP Signature Packet>`.
|
||||
|
||||
### Revoke a certificate
|
||||
|
||||
Users may find themselves needing to revoke their entire {term}`OpenPGP certificate`, rendering it unusable. This could be for various reasons, such as migrating to a new {term}`certificate<OpenPGP certificate>` or in response to a compromise of the {term}`certificate<OpenPGP certificate>`'s {term}`secret key material<Private Key Material>`.
|
||||
|
||||
While a {term}`soft-revoked<Soft Revocation>` {term}`certificate<OpenPGP Certificate>` can be re-validated at a later time with a new {term}`certification`, a {term}`hard revocation` is permanent.
|
||||
|
||||
The recommended way to {term}`revoke<Revocation>` a {term}`certificate<OpenPGP Certificate>` is by issuing a {term}`Key Revocation signature<Key Revocation Signature Packet>` ({term}`type ID<Signature Type ID>` `0x20`). Its structure is similar to that of a {term}`Certification Revocation signature<Certification Revocation Signature Packet>`.
|
||||
|
||||
| {term}`Subpacket<OpenPGP Signature Subpacket>` | Area | {term}`Critical<Criticality Flag>` | Mandatory | Notes |
|
||||
|--------------------------------------------------------------------|-----------------------------|------------------------------------------|----------------------|-------------------------------------------------------------------------------------|
|
||||
| {term}`Signature Creation Time<Signature Creation Time Subpacket>` | {term}`Hashed<Hashed Area>` | True | True | Current time |
|
||||
| {term}`Issuer Fingerprint<Issuer Fingerprint Subpacket>` | {term}`Hashed<Hashed Area>` | True or False | Strongly Recommended | The {term}`primary key<OpenPGP Primary Key>` is the {term}`issuer` |
|
||||
| {term}`Reason for Revocation<Reason For Revocation Subpacket>` | {term}`Hashed<Hashed Area>` | True | False | Determines {term}`soft<Soft Revocation>` or {term}`hard revocation` |
|
||||
|
||||
For {term}`Key Revocation signatures<Key Revocation Signature Packet>`, the guidelines regarding the [*Reason for Revocation* subpacket](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-reason-for-revocation) are the same as those for {term}`Subkey Revocation signatures<Subkey Revocation Signature Packet>`.
|
||||
|
||||
### Common subpackets in OpenPGP signatures
|
||||
|
||||
In OpenPGP, certain {term}`subpackets<OpenPGP Signature Subpacket>` are universally expected across all types of {term}`signatures<OpenPGP Signature Packet>`, serving fundamental roles in the {term}`signature<OpenPGP Signature Packet>`'s structure, {term}`verification` and {term}`validation`:
|
||||
|
||||
* **{term}`Signature Creation Time<Signature Creation Time Subpacket>`**: This is a mandatory {term}`subpacket<OpenPGP Signature Subpacket>` in every {term}`OpenPGP signature<OpenPGP Signature Packet>`. It contains the timestamp of when the {term}`signature<OpenPGP Signature Packet>` was created. For security and integrity, this {term}`subpacket<OpenPGP Signature Subpacket>` must be located in the {term}`hashed area` of the {term}`signature<OpenPGP Signature Packet>` and is recommended to be marked as {term}`critical<Criticality Flag>`.
|
||||
|
||||
* **{term}`Issuer Fingerprint<Issuer Fingerprint Subpacket>`**: Essential for {term}`signature<OpenPGP Signature Packet>` {term}`validation`, this {term}`subpacket<OpenPGP Signature Subpacket>` identifies the {term}`key<OpenPGP Primary Key>` (or {term}`subkey<OpenPGP Subkey>`) that was used to create the {term}`signature<OpenPGP Signature Packet>`. OpenPGP v6 {term}`signatures<OpenPGP Signature Packet>` should include the {term}`Issuer Fingerprint subpacket`, containing the 32-byte {term}`fingerprint<OpenPGP Fingerprint>` of the {term}`key<Component Key>`.
|
||||
|
||||
```{note}
|
||||
The {term}`key<Component Key>` used as the {term}`issuer` in the {term}`signature<OpenPGP Signature Packet>` might be a {term}`subkey<OpenPGP Subkey>` of the {term}`certificate<OpenPGP Certificate>`.
|
||||
```
|
||||
|
||||
These {term}`subpackets<OpenPGP Signature Subpacket>` can be placed in either the {term}`hashed<Hashed Area>` or {term}`unhashed area` due to its self-{term}`authenticating<Authentication>` nature. However, we recommend including them in the {term}`signature<OpenPGP Signature Packet>`'s {term}`hashed area`.
|
||||
|
||||
## Managing subpacket conflicts and duplication
|
||||
|
||||
In {term}`OpenPGP signatures<OpenPGP Signature Packet>`, both the {term}`hashed<Hashed Area>` and {term}`unhashed areas<Unhashed Area>` are composed of lists of {term}`subpackets<OpenPGP Signature Subpacket>`. Inherently, this structure permits the duplication of the same {term}`subpacket<OpenPGP Signature Subpacket>`, which could lead to conflicts. To manage these potential conflicts, the following strategies are used:
|
||||
|
||||
- **Precedence of {term}`hashed area`**: {term}`Subpackets<OpenPGP Signature Subpacket>` within the {term}`hashed area` of a {term}`signature<OpenPGP Signature Packet>` take precedence over those in the {term}`unhashed area`. This hierarchy helps resolve conflicts when the same {term}`subpacket<OpenPGP Signature Subpacket>` appears in both areas.
|
||||
|
||||
- **Handling conflicts within the same area**: Conflicts can still arise within the same area, such as when two {term}`subpackets<OpenPGP Signature Subpacket>` have different {term}`expiration times<Expiration Time>`. In such cases, the [OpenPGP specification](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-notes-on-subpackets) advises that {term}`implementations<OpenPGP Implementation>` should favor the last occurrence of a conflicting {term}`subpacket<OpenPGP Signature Subpacket>` in the {term}`hashed area`.
|
||||
|
||||
In certain scenarios, having duplicate {term}`subpackets<OpenPGP Signature Subpacket>` with conflicting content is logical and even necessary. For example, consider a {term}`signature<OpenPGP Signature Packet>` created by a version 4 {term}`issuer` {term}`key<Component Key>`, which was upgraded from an older OpenPGP version (like v3). Since the {term}`key ID` calculation scheme changed from v3 to v4, the identifiers for the same {term}`key<Component Key>` would differ between these versions. Therefore, a v4 signature might contain two {term}`issuer key ID subpackets<Issuer Fingerprint Subpacket>`, each with different, yet correct values for v3 and v4 {term}`keys<Component Key>`, respectively. This allows for backward compatibility and ensures the {term}`signature<OpenPGP Signature Packet>` can be {term}`validated<Validation>` under both {term}`key ID` calculation schemes.
|
52
book/source/adv/signing_data.md
Normal file
|
@ -0,0 +1,52 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Advanced material: Signatures over data
|
||||
|
||||
## Nesting of one-pass signatures
|
||||
|
||||
Signing a message using the one-pass mechanism involves prepending a *One-Pass-Signature* (OPS) packet to the message and appending the corresponding signature, sandwiching the signed content.
|
||||
|
||||
An OpenPGP message can contain multiple signatures added that way.
|
||||
|
||||
```{note}
|
||||
One-Pass-Signatures are nested, meaning the outermost One-Pass-Signature packet corresponds to the outermost signature packet.
|
||||
```
|
||||
|
||||
When a message is signed, the signature is always calculated over the contents of the literal data packet, not the literal data packet itself.
|
||||
This means, that if a message, which is compressed using a compressed data packet is wrapped using a one-pass-signature, the signature is still being calculated over the plaintext inside the literal data packet.
|
||||
|
||||
There is one exception though.
|
||||
```{note}
|
||||
Of course there is.
|
||||
```
|
||||
|
||||
The OPS packet has a "nested" flag[^nested-flag], which can either be `1` or `0`.
|
||||
If this flag is set to `0`, it indicates that further OPSs will follow this packet, which are calculated over the same plaintext data as this OPS is. A value of `1` indicates, that either no further OPS packets will follow (this OPS is the last), or that this OPS is calculated over the the usual plaintext data, but wrapped inside any OPS+Signature combinations that follow this OPS.
|
||||
|
||||
[^nested-flag]: See [description of the nested flag](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#section-5.4-3.8.1).
|
||||
|
||||
This mechanism enables attested signatures, where the signer signs an already one-pass-signed message including the already contained signature.
|
||||
|
||||
As a practical example, consider the following notation:
|
||||
* `LIT("Hello World")` represents a literal data packet with the content `Hello World`.
|
||||
* `COMP(XYZ)` represents a compressed data packet over some other packet `XYZ`.
|
||||
* `OPS₁` represents a one-pass-signature packet with the nested flag set to `1`. Analogous, `OPS₀` has the nested flag set to `0`.
|
||||
* `SIG` represents a signature packet.
|
||||
|
||||
A normal, one-pass-signed message looks like this:
|
||||
`OPS₁ LIT("Hello World") SIG`
|
||||
|
||||
Here, the signature is calculated over the plaintext `Hello World`, as is it in a message that has the following form: `OPS₁ COMP(LIT("Hello World")) SIG`.
|
||||
|
||||
A message, where multiple one-pass-signatures are calculated over the same plaintext looks the following:
|
||||
`OPS₀ OPS₀ OPS₁ LIT("Hello World") SIG SIG SIG`
|
||||
|
||||
All three signatures are calculated over the same plaintext `Hello World`.
|
||||
|
||||
Now, a message, where the signer attests an already signed message has the following format:
|
||||
`OPS₁ OPS₁ LIT("Hello World") SIG SIG`
|
||||
|
||||
While the inner signature is calculated over the usual plaintext `Hello World`, the outer signature is instead calculated over `OPS₁ Hello World SIG`.
|
116
book/source/adv/verification.md
Normal file
|
@ -0,0 +1,116 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Advanced material: Signature verification
|
||||
|
||||
## Attribute shadowing
|
||||
|
||||
When determining the preferences of a key, several signatures may have to be inspected.
|
||||
|
||||
For example, when using a signing subkey to generate a data signature, an implementation might want to check for hash algorithm preferences on the subkey binding signature.
|
||||
However, the RFC [states](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#section-5.2.3.10-2) that signature subpackets in a direct key signature (which may also contain preferences) on the OpenPGP certificate's primary key apply to the entire OpenPGP key, and therefore also to the signing subkey.
|
||||
|
||||
In this case, the implementation uses the preferences from the subkey binding signature, but if no such subpacket is found on the latest binding signature, it falls back to the preferences from the direct key signature.
|
||||
This is called attribute shadowing, since direct key signature subpackets apply to all subkeys, but are shadowed by binding signature subpackets.
|
||||
|
||||
```{figure} ../img/drawio/attribute-shadowing.png
|
||||
:name: fig-signature-verification-attribute-shadowing
|
||||
:alt: Depicts a certificate with to dedicated signing subkeys and a subkey binding signature each. The primary key carries a direct-key signature, which specifies SHA-512 and SHA-256 as hash algorithm preferences. The binding signature of the first signing subkey does not specify preferences, while the binding signature of the second subkey defines SHA-384. Signatures made using the first subkey source the hash algorithm preferences from the direct-key signature, due to the absence of a preference subpacket on the binding signature, while for signature made using the second subkey the direct-key signature's preferences are shadowed by the subkey signatures preferences subpacket.
|
||||
|
||||
Inheritance and Shadowing of Attributes
|
||||
```
|
||||
|
||||
```{admonition} Note
|
||||
:class: note
|
||||
|
||||
Attribute shadowing is relatively straightforward to reason about when used for algorithm preferences. For other subpacket types, shadowing may be confusing, and/or the semantics underspecified (e.g. for key expiration time subpackets).
|
||||
```
|
||||
|
||||
## Signature shadowing
|
||||
|
||||
When inspecting signatures on a component of an OpenPGP certificate, of the signatures that are in effect for each function, only the newest is considered.
|
||||
|
||||
In other words:
|
||||
- If there are three binding signatures `A, B, C` for a subkey,
|
||||
- where:
|
||||
- `A` was created at `t0`,
|
||||
- `B` at `t1`, and
|
||||
- `C` at `t3`, with
|
||||
- `t0 < t1 < t2 < t3`.
|
||||
- Then at `t2`, an implementation only needs to consider signature `B`,
|
||||
- because `C` is not yet in effect, and
|
||||
- `A` is shadowed, because it is older than `B`.
|
||||
|
||||
```{figure} ../img/drawio/cert-validity-subkey.png
|
||||
:name: fig-signature-verification-subkey-validity
|
||||
:alt: Depicts a gantt-style diagram visualizing how the validity of a certificates components changes over time, depending on component signatures.
|
||||
|
||||
An example for how certificate validity can change with time.
|
||||
```
|
||||
|
||||
```{note}
|
||||
|
||||
Signature shadowing should not be confused with attribute shadowing.
|
||||
```
|
||||
|
||||
As attribute and signature shadowing can occur in combination, it is not always obvious which properties a key has at a given time.
|
||||
|
||||
```{figure} ../img/drawio/dk-attributes-and-shadowing.png
|
||||
:name: fig-signature-verification-signature-shadowing
|
||||
:alt: Depicts a certificate with a subkey, whose capabilities change over time, due to signature shadowing another.
|
||||
|
||||
Signatures shadow one another, based on reference time.
|
||||
```
|
||||
|
||||
## Which signatures take precedence?
|
||||
|
||||
Multiple signatures can be attached to an OpenPGP certificate or component. These signatures can contain conflicting information.
|
||||
|
||||
When verifying a signature that is not self-qualifying, an implementation needs to inspect self-qualifying signatures in the issuer's certificate for qualification. The certificate may contain multiple signatures for one component.
|
||||
|
||||
For example, there could be multiple subkey binding signatures for one subkey. This could be the case because the expiration time in the original binding signature has expired, and the certificate holder has issued a new binding signature with an extended expiration time.
|
||||
|
||||
In general, for each category of signatures (categories such as binding signatures for one particular subkey), the signature with the latest creation time takes precedence, and only that signature is considered.
|
||||
|
||||
Alternatively, there can be competing qualifying signatures of different types, e.g., a direct key signature and a self-certification signature on a primary User ID. Both of these contain metadata associated with the entire certificate. By default, the direct key signature is preferred[^conflicting-prefs] in OpenPGP version 6.
|
||||
|
||||
[^conflicting-prefs]: However, the semantics of these cases are not currently fully specified, see [this discussion](https://gitlab.com/openpgp-wg/rfc4880bis/-/issues/103).
|
||||
|
||||
Depending on how a certificate is "located," different metadata from possible candidate signatures "shadow" one another. The RFC [states](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-notes-on-self-signatures) that when a certificate is "located" by the OpenPGP software "via an identity", then the metadata associated with that identity takes precedence over more global metadata, such as that associated with the certificate's primary key, with a direct key signature.
|
||||
|
||||
For example, the latest direct key signature could list "SHA512, SHA384" as hash algorithm preferences, while the latest self-certification of the User ID "Bob" could list only "SHA256."
|
||||
For yet another User ID "Bobby," the self-signature could list no hash algorithm preferences at all.
|
||||
If the user wants to compose a signed message using the associated OpenPGP key they need to figure out which preferences to use.
|
||||
|
||||
The specification recommends that implementations decide which signature takes precedence by the way the certificate is "addressed."
|
||||
|
||||
```{figure} ../img/drawio/narrow-interpretation.png
|
||||
|
||||
Preferences are sourced from signatures on different components, depending on how the key is addressed.
|
||||
```
|
||||
|
||||
If the user wants to write an email as "Bob," it should consider the signature on "Bob," so SHA256 should be used as hash algorithm.
|
||||
If instead the user wants to write as "Bobby," the implementation should inspect the self-certification on "Bobby" instead.
|
||||
However, since this signature does not carry any hash algorithm preferences subpacket, the implementation must fall back to the direct key signature instead.
|
||||
The same is true if the certificate is used without any User ID as sender.
|
||||
|
||||
To complicate things further:
|
||||
Algorithm preferences can also be stated on subkey binding signatures, so if the certificate has a dedicated signing subkey, there is yet another signature which could take precedence.
|
||||
Preferences from the subkey binding signature take precedence over the direct key signature, but not over self-certifications on the User ID.
|
||||
|
||||
There can be more than one signature on a component. As an example, there are 3 direct key signatures (e.g., because the key's expiry has been extended two times).
|
||||
In general, for each component, only the newest self-signature is "in effect," and older signatures are "shadowed."
|
||||
For each certificate, there is at most one "active" direct key signature, for each User ID at most one active self-certification and for each subkey exactly one subkey binding.
|
||||
|
||||
## Complexity of the packet format
|
||||
|
||||
OpenPGP certificates can contain complex preference settings. Additionally, the OpenPGP packet format allows a lot of flexibility when storing certificates in TPK format.
|
||||
|
||||
User ID packets, for example, do not have a fixed position in a TPK. This means an attacker (or an implementation-internal certificate canonicalization procedure) can change the order in which User IDs appear in the certificate's packet sequence.
|
||||
|
||||
As a concrete example, consider a certificate with multiple User IDs, all marked as primary. Or similarly, a certificate with multiple User IDs of which none is marked as primary.
|
||||
Clients might apply different heuristics to figure out which User ID actually qualifies as the primary User ID here.
|
||||
|
||||
Such subtle changes to the representation of a certificate can lead to different preference settings being deduced, by different OpenPGP implementations.
|
95
book/source/armor.md
Normal file
|
@ -0,0 +1,95 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# ASCII armor
|
||||
|
||||
The native format of OpenPGP data is binary.
|
||||
|
||||
However, in many use cases it is customary to use OpenPGP data in a non-binary encoding called "ASCII armor." For example, ASCII armored OpenPGP data is often used in email, for encrypted messages or for signatures.
|
||||
|
||||
## Mechanism
|
||||
|
||||
OpenPGP's ASCII armor mechanism consists of:
|
||||
|
||||
- An [armor header line](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-armor-header-line)
|
||||
- Optional [armor headers](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-armor-headers) that can contain additional metadata
|
||||
- The [base64 encoded](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-base64-conversions) OpenPGP data
|
||||
- An [optional checksum](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-optional-checksum) for this data
|
||||
- An [armor tail line](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-armor-tail-line) (or footer) that matches the header line
|
||||
|
||||
## Example
|
||||
|
||||
In the chapter [](zoom/certificates), we take a look at a very minimal variant of Alice's certificate. In ASCII armored form, the certificate is 388 bytes long, and looks like this:
|
||||
|
||||
```text
|
||||
$ cat alice_minimal.pub
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
xioGZRbqphsAAAAgUyTpQ6+rFfdu1bUSmHlpzRtdEGXr50Liq0f0hrOuZT7CtgYf
|
||||
GwoAAAA9BYJlFuqmBYkFpI+9AwsJBwMVCggCmwECHgEiIQaqoYy7JUaFxYNYMgVj
|
||||
/Te2fzMA+fsOxFc3jNKfECaYswAAAAoJEKqhjLslRoXFZ0cgouNjgeNr0E9W18g4
|
||||
gAIl6FM5SWuQxg12j0S07ExCOI5NPRDCrSnAV85mAXOzeIGeiVLPQ40oEal3CX/L
|
||||
+BXIoY2sIEQrLd4TAEEy0BA8aQZTPEmMdiOCM1QB+V+BQZAO
|
||||
=5nyq
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
```
|
||||
|
||||
In this example, the *armor header line* uses the *header line text* `BEGIN PGP PUBLIC KEY BLOCK` (referring to the certificate data using the term *PGP public key*). Note that the matching footer uses the text `END PGP PUBLIC KEY BLOCK`.
|
||||
|
||||
There are no *armor headers* with additional metadata in this example. The base64 encoded message spans five lines, and is followed by a CRC24 checksum line with the content `=5nyq`.
|
||||
|
||||
The armored format is convenient when transferring OpenPGP data (like this certificate) in email Text. It is a robust format for manual copying and pasting, etc.
|
||||
|
||||
### Contrast with binary OpenPGP data
|
||||
|
||||
Using the `sq` commandline tool, we can compare this with the same OpenPGP data in binary form:
|
||||
|
||||
```text
|
||||
$ sq dearmor alice_minimal.pub > alice_minimal.pub.bin
|
||||
```
|
||||
|
||||
The resulting binary representation of the same data comprises 228 bytes. We can look at its hexdump:
|
||||
|
||||
```text
|
||||
$ hexdump -C alice_minimal.pub.bin
|
||||
00000000 c6 2a 06 65 16 ea a6 1b 00 00 00 20 53 24 e9 43 |.*.e....... S$.C|
|
||||
00000010 af ab 15 f7 6e d5 b5 12 98 79 69 cd 1b 5d 10 65 |....n....yi..].e|
|
||||
00000020 eb e7 42 e2 ab 47 f4 86 b3 ae 65 3e c2 b6 06 1f |..B..G....e>....|
|
||||
00000030 1b 0a 00 00 00 3d 05 82 65 16 ea a6 05 89 05 a4 |.....=..e.......|
|
||||
00000040 8f bd 03 0b 09 07 03 15 0a 08 02 9b 01 02 1e 01 |................|
|
||||
00000050 22 21 06 aa a1 8c bb 25 46 85 c5 83 58 32 05 63 |"!.....%F...X2.c|
|
||||
00000060 fd 37 b6 7f 33 00 f9 fb 0e c4 57 37 8c d2 9f 10 |.7..3.....W7....|
|
||||
00000070 26 98 b3 00 00 00 0a 09 10 aa a1 8c bb 25 46 85 |&............%F.|
|
||||
00000080 c5 67 47 20 a2 e3 63 81 e3 6b d0 4f 56 d7 c8 38 |.gG ..c..k.OV..8|
|
||||
00000090 80 02 25 e8 53 39 49 6b 90 c6 0d 76 8f 44 b4 ec |..%.S9Ik...v.D..|
|
||||
000000a0 4c 42 38 8e 4d 3d 10 c2 ad 29 c0 57 ce 66 01 73 |LB8.M=...).W.f.s|
|
||||
000000b0 b3 78 81 9e 89 52 cf 43 8d 28 11 a9 77 09 7f cb |.x...R.C.(..w...|
|
||||
000000c0 f8 15 c8 a1 8d ac 20 44 2b 2d de 13 00 41 32 d0 |...... D+-...A2.|
|
||||
000000d0 10 3c 69 06 53 3c 49 8c 76 23 82 33 54 01 f9 5f |.<i.S<I.v#.3T.._|
|
||||
000000e0 81 41 90 0e |.A..|
|
||||
000000e4
|
||||
```
|
||||
|
||||
If you read the chapter that discusses the [packet internals of certificate data](/zoom/certificates), you may recognize this data.
|
||||
|
||||
This binary data is, of course, not convenient to copy into the text of an email, or similar. On the other hand, it's a more compact format. In this example, the binary OpenPGP data is around 40% smaller than the ASCII armored representation.
|
||||
|
||||
Both the binary and the ASCII armored format encode exactly the same information. Depending on the context in which the OpenPGP data is used, one or the other is more appropriate.
|
||||
|
||||
## The cleartext signature framework
|
||||
|
||||
One noteworthy mechanism in OpenPGP that uses a specialized variant of ASCII armoring is the [*cleartext signature framework*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#cleartext-signature), which stores an ASCII-armored signature and a cleartext message as a combined text file.
|
||||
|
||||
See our section on the [cleartext signature framework](cleartext-signature) for an example of that format.
|
||||
|
||||
## Advanced topics
|
||||
|
||||
### Optional checksum and its deprecation
|
||||
|
||||
Historically, the ASCII armor mechanism of OpenPGP has specified an (optional) checksum mechanism for the base64 encoded data.
|
||||
|
||||
The specification for OpenPGP version 6 [deprecates this mechanism](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-optional-checksum).
|
||||
|
||||
Existing CRC24 footers should be ignored, and generating these footers is strongly discouraged, except in cases where interoperability requires it.
|
95
book/source/artifacts.md
Normal file
|
@ -0,0 +1,95 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Appendix A: OpenPGP artifacts
|
||||
|
||||
(alice-priv)=
|
||||
## Alice's OpenPGP key
|
||||
|
||||
```text
|
||||
-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||
Comment: AAA1 8CBB 2546 85C5 8358 3205 63FD 37B6 7F33 00F9 FB0E C457 378C D29F 1026 98B3
|
||||
Comment: <alice@example.org>
|
||||
|
||||
xUsGZRbqphsAAAAgUyTpQ6+rFfdu1bUSmHlpzRtdEGXr50Liq0f0hrOuZT4A7+GZ
|
||||
tV8R+6qT6CadO7ItciB9/71C3UvpozaBO6XMz/vCtgYfGwoAAAA9BYJlFuqmBYkF
|
||||
pI+9AwsJBwMVCggCmwECHgEiIQaqoYy7JUaFxYNYMgVj/Te2fzMA+fsOxFc3jNKf
|
||||
ECaYswAAAAoJEKqhjLslRoXFZ0cgouNjgeNr0E9W18g4gAIl6FM5SWuQxg12j0S0
|
||||
7ExCOI5NPRDCrSnAV85mAXOzeIGeiVLPQ40oEal3CX/L+BXIoY2sIEQrLd4TAEEy
|
||||
0BA8aQZTPEmMdiOCM1QB+V+BQZAOzRM8YWxpY2VAZXhhbXBsZS5vcmc+wrkGExsK
|
||||
AAAAQAWCZRbqpgWJBaSPvQMLCQcDFQoIApkBApsBAh4BIiEGqqGMuyVGhcWDWDIF
|
||||
Y/03tn8zAPn7DsRXN4zSnxAmmLMAAAAKCRCqoYy7JUaFxdu4IIotb9pnNbxdBHe0
|
||||
nWeobsXWiFNf4u/5Zgi/wuDbwFYN69QspRkBD7om0IKiz1zreqly2fOyZgeLsro9
|
||||
t4nkdgRuNSQrJymDvpGceGrMtNVpR3YsKdZUv0MZBP9TmMDVC8dLBmUW6qYZAAAA
|
||||
INGuh9fMQq+ZNMXCXMr6t0rIQ/yGNSpGAfPMAPVKCT4/ACh9zdomFjeN6iTHzudw
|
||||
x5vlbwrJd/u9I0FzyVdav3xMwqsGGBsKAAAAMgWCZRbqpgWJBaSPvQKbDCIhBqqh
|
||||
jLslRoXFg1gyBWP9N7Z/MwD5+w7EVzeM0p8QJpizAAAACgkQqqGMuyVGhcUiiSAL
|
||||
DIm1qxXjf+RNuafvcUgUO6smXzR/bUgun3hIWG2a+22y2y+XjsgS/Fd/harRWbyA
|
||||
QAu+LvDhIy2/S3F+0OTANuTSz7KftKhPPiohiXTCM1WvrEE2GytgCfLZGfRBEgvH
|
||||
SwZlFuqmGwAAACAzjNT1GnM5787WDyGNoFiiPD1EqFnpEx8SnG8Z0D1AoAAOy9HJ
|
||||
vIGCqncfqBKmKnSkIMF0dvOGJPuoJaVi3daikcLAhQYYGwoAAADMBYJlFuqmBYkF
|
||||
pI+9ApsCmaAGGRsKAAAAKQWCZRbqpiIhBtB7JOyRoU3SQKwtU+bIqeBUlJpBIi6n
|
||||
OFdu0Zyuo9yZAAAAAFNlIEIDrQzb/LWamKYVJ+QRXvXyoD287Y2UJ0EJ9jxL+Irl
|
||||
r3PhfVQHQD/zKTTC52BWpeFDywi6Zv6LJs7ny6U6RrulyF3kat6uSeE+B7/EnpgU
|
||||
Lz7F9wE+Pk/2GCqsve1SDCIhBqqhjLslRoXFg1gyBWP9N7Z/MwD5+w7EVzeM0p8Q
|
||||
JpizAAAACgkQqqGMuyVGhcWEHCAjPbJJ8wJLCJOvugiJ8OCRD6siJqqzVlcw6pUp
|
||||
BmBvAL5EoZU4qWs6PlHwVQmx4pGpF4b69R4/0ChGPM5uiBQ3Muw9+sYByuWpS7dj
|
||||
lMMNkqvc+iNQcWAxpnPIM1qc2QrHSwZlFuqmGwAAACDwBi/5Muf1wpuajKjKJAxR
|
||||
OVLySBVJj9b62QyLOJg0xwDYFSvx1cQi8FF4tCtpn89N0vMTmG7fQoW0DVOI1u0K
|
||||
tcKrBhgbCgAAADIFgmUW6qYFiQWkj70CmyAiIQaqoYy7JUaFxYNYMgVj/Te2fzMA
|
||||
+fsOxFc3jNKfECaYswAAAAoJEKqhjLslRoXF3Zgg/R2/mYLb/DpRSKA2ykKvFiRM
|
||||
HLb8jRg6WGcr+XPuQbgdalxjFetO9EbZjOuYl3Jok/zObOgK7bCEoVEs9/UHR/K+
|
||||
FcsHp6K860qG87pDlk2saQIwzXPmQcQvLokbeQMB
|
||||
=DfxN
|
||||
-----END PGP PRIVATE KEY BLOCK-----
|
||||
```
|
||||
|
||||
(bob-priv)=
|
||||
## Bob's OpenPGP key
|
||||
|
||||
Bob uses passphrase-protected secret key packets. His passphrase is `password`:
|
||||
|
||||
```text
|
||||
-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||
Comment: BB28 9FB7 A68D BFA8 C384 CCCD E205 8E02 D9C6 CD2F 3C7C 56AE 7FB5 3D97 1170 BA83
|
||||
Comment: Bob Baker <bob@example.org>
|
||||
|
||||
xYYGZSlULBsAAAAgR+fC3FiOy/3ySZBmrqo2ZsqpVS1xiHwlkcN1cx0HYNb+FgkU
|
||||
BDt/Sw6sizliWrTUvWkEE8cBBBUh/7788cWcdZ0f0fgZ5/1HVeNp/y/oUkhmA9M3
|
||||
UnsFy/qx+BP39iCI1vWLxLRRUrpt+Xwa7p/msftj0cpKPzPZLMkmRsK2Bh8bCgAA
|
||||
AD0FgmUpVCwFiQWkj70DCwkHAxUKCAKbAQIeASIhBrson7emjb+ow4TMzeIFjgLZ
|
||||
xs0vPHxWrn+1PZcRcLqDAAAACgkQuyift6aNv6hmsCBGbcyDxHuoq4DTmpzhwxCo
|
||||
Pq37LydspnltmjW6ZlSTLOc7dt+MiSxAUIqH9i0CQVV9cyQfc0Gi7YzjnvQ9RxcZ
|
||||
raou5c0b126fZ6Rt2vzLHICGw3v7dpCAnR0Y2lUvaAXNG0JvYiBCYWtlciA8Ym9i
|
||||
QGV4YW1wbGUub3JnPsK5BhMbCgAAAEAFgmUpVCwFiQWkj70DCwkHAxUKCAKZAQKb
|
||||
AQIeASIhBrson7emjb+ow4TMzeIFjgLZxs0vPHxWrn+1PZcRcLqDAAAACgkQuyif
|
||||
t6aNv6h76CB+0O5ke9ijamCxuAz9FHaMDN+l+mQrTYFTLCpGpkWIta+yHy3YdGog
|
||||
5o5KzDQPrSART32y2dRKQci/49rafLDEqHfPzhEPwwcKWjJxpEpA+AUR+r0WuAh0
|
||||
dRzT5vjPJwLHhgZlKVQsGQAAACAx8dR3SX4a2pudy0Fkzz8IkVhI+iIICfcKe8FC
|
||||
HBUOFP4WCRQEw0VAyFbfmLAChDiHM714gQEEFcXLCpUyt+CPel5FO0wVibtGYRHr
|
||||
pFEH/iCz7VYAup7lgerjiqTWdla37S+cAra9XduruJUZ3XS+L4bhYZTiCe2Jn8Fd
|
||||
wqsGGBsKAAAAMgWCZSlULAWJBaSPvQKbDCIhBrson7emjb+ow4TMzeIFjgLZxs0v
|
||||
PHxWrn+1PZcRcLqDAAAACgkQuyift6aNv6ggfiDu7s2cKnNx1vn17XV99XFo+DVe
|
||||
Z/MQBOIbZ7bQz2ufS4PIjnC62/oybvC+GeNcnD8kOYfwtxPtl6DQdbpHYyqgNO21
|
||||
RMq9oNvei4tKmh7gg6jXGrmWKT6yOIPZyqpQUg/HhgZlKVQsGwAAACAGelHxA+Uz
|
||||
4M73R7YTo7Xjg3KKoLmc/BYWA/QZ3noQNP4WCRQEG4OjK8N9PVQioBCJ848J8QEE
|
||||
FRjFFaC6UN5LB6wyCqvozRo/e069dUiwlnYssBNPINsXQiBPcxmoSbyxVRF7LR9G
|
||||
BZy9/bVQw9ZyPKtbvBQEQKy4m1qvwsCFBhgbCgAAAMwFgmUpVCwFiQWkj70CmwKZ
|
||||
oAYZGwoAAAApBYJlKVQsIiEGLJuY+NkqRYws6tuzQ2Bff3TWsiqA7IzIPzpNxQjJ
|
||||
+pUAAAAA6LUg9nuvXbKHUCoGMAdiVV/ttYcO583925/m/T3nC/CNNShitGiBRNAp
|
||||
HnGyQKVkROyzYznyA9jCF+Ck1jeOCb5nQ7PwxHxRuP4ZG0uRN23pQh4eM2F7V/2F
|
||||
iOkRF9lAM0AEIiEGuyift6aNv6jDhMzN4gWOAtnGzS88fFauf7U9lxFwuoMAAAAK
|
||||
CRC7KJ+3po2/qBhPIIPtGEG7TzgO0gXQjhlx9hNBdKxAzScMwRT7oAT3RZrG5hGH
|
||||
oyvf2n86URceCnfYSwZSOij7CfD0ZJgDmNmvJ5//yx7I7M4YCCheAd5er3/eaF6O
|
||||
VJt3Ui/pv5VuXLTRC8eGBmUpVCwbAAAAIFnzWg9EBmVGFMUClhrtT5DNdCf+A4OQ
|
||||
90WbiTHnseuR/hYJFAT5Ylewq+lINPw46gwA5Z6eAQQViAjElYeZobbZ+D001l/M
|
||||
QvHaiEbEIXadwP3bbjoM43rFoP+p8cNYYECYAL8sx34uIxeixwrL6aOZ8j6Y1zbP
|
||||
C8jTYNrCqwYYGwoAAAAyBYJlKVQsBYkFpI+9ApsgIiEGuyift6aNv6jDhMzN4gWO
|
||||
AtnGzS88fFauf7U9lxFwuoMAAAAKCRC7KJ+3po2/qLHdID+av7QZ75Fq4v9YVHpc
|
||||
wVXtKDX+MOKJM4xz7RvBWErH2xWyqikNZQVuzz/WqOVH/nT+BcqmLWAe3yjrTE4B
|
||||
hSfrR38Nk23E4Bu4HobVrg7rlMU6SKHRWKeX/iSUmr6GDA==
|
||||
=UZBq
|
||||
-----END PGP PRIVATE KEY BLOCK-----
|
||||
```
|
294
book/source/certificates.md
Normal file
|
@ -0,0 +1,294 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Certificates
|
||||
|
||||
OpenPGP fundamentally hinges on the concept of "{term}`OpenPGP certificates<OpenPGP Certificate>`," also known as "{term}`OpenPGP public keys<OpenPGP Public Key>`." These {term}`certificates<OpenPGP Certificate>` are complex data structures essential for {term}`identity verification`, data encryption, and {term}`digital signatures<OpenPGP Signature Packet>`. Understanding their structure and function is pivotal to effectively applying the OpenPGP standard.
|
||||
|
||||
An {term}`OpenPGP certificate`, by definition, does not contain {term}`private key material`.
|
||||
|
||||
Fundamentally, the effective management of {term}`certificates<OpenPGP Certificate>` and a thorough grasp of their {term}`authentication` and {term}`trust models<Trust Model>` are crucial for proficient OpenPGP usage. Although this document offers just a brief overview of these aspects, they form a fundamental part of the broader OpenPGP framework and warrant further study.
|
||||
|
||||
- For an in-depth exploration of OpenPGP's {term}`private key material`, refer to [](/private_keys). This chapter provides essential insights into {term}`private key<Transferable Secret Key>` management and security practices.
|
||||
|
||||
- The bindings that link the {term}`components<Component>` of a {term}`certificate<OpenPGP Certificate>` are comprehensively discussed in [](/signing_components), offering a deeper understanding of {term}`certificate<OpenPGP Certificate>` structure and integrity.
|
||||
|
||||
- Finally, our chapter [](zoom/certificates) discusses the internal structure of {term}`certificates<OpenPGP Certificate>` in detail.
|
||||
|
||||
## Terminology: Understanding "keys"
|
||||
|
||||
The term "{term}`(cryptographic) keys<Cryptographic Key>`" is central to grasping the concept of {term}`OpenPGP certificates<OpenPGP certificate>`. However, it can refer to different entities, making it a potentially confusing term. Let's clarify those differences.
|
||||
|
||||
### Public vs. private keys
|
||||
|
||||
The term "{term}`key`," without additional context, can refer to either public or private {term}`asymmetric<Asymmetric Cryptography>` key material. Additionally, {term}`symmetric<Symmetric Cryptography>` keys may be used in OpenPGP to encrypt {term}`private key material`, adding a layer of security and complexity.
|
||||
|
||||
(layers-of-keys-in-openpgp)=
|
||||
### Layers of keys in OpenPGP
|
||||
|
||||
In OpenPGP, the term "{term}`key`" may refer to three distinct layers, each serving a unique purpose:
|
||||
|
||||
1. A (bare) ["cryptographic key"](asymmetric-key-pair) comprises the private and/or public parameters forming a key. For instance, in the case of an RSA {term}`private key<Transferable Secret Key>`, the key consists of the exponent `d` along with the prime numbers `p` and `q`.
|
||||
2. An OpenPGP *{term}`component key<OpenPGP Component Key>`* includes either an "{term}`OpenPGP primary key`" or an "{term}`OpenPGP subkey`." It is a building block of an {term}`OpenPGP certificate`, consisting of a cryptographic keypair coupled with some invariant {term}`metadata`, such as key {term}`creation time`.
|
||||
3. An "{term}`OpenPGP certificate`" (or "OpenPGP key") consists of several {term}`component keys<OpenPGP Component Key>`, {term}`identity components<Identity Component>`, and other elements. These {term}`certificates<Certificate>` are dynamic, evolving over time as {term}`components<Component>` are added, {term}`expire<Expiration>`, or are marked as {term}`invalid<Validation>`.
|
||||
|
||||
The following section will delve into the OpenPGP-specific layers (2 and 3) to provide a clearer understanding of their roles within {term}`OpenPGP certificates<OpenPGP Certificate>`.
|
||||
|
||||
## Structure of OpenPGP certificates
|
||||
|
||||
An {term}`OpenPGP certificate` (or "{term}`OpenPGP key`") is a collection of an arbitrary number of elements[^packets]:
|
||||
|
||||
[^packets]: In technical terms, the elements of an {term}`OpenPGP certificate` are a collection of "{term}`packets<Packet>`." Each {term}`component key<OpenPGP Component Key>` and {term}`identity component` is internally represented as a {term}`packet`. Another common type of {term}`packet` is the "{term}`signature`" {term}`packet`, which connect the {term}`components<Component>` of a {term}`certificate<OpenPGP Certificate>`.
|
||||
|
||||
- {term}`Component keys<OpenPGP Component Key>`
|
||||
- {term}`Identity components<Identity Component>`
|
||||
- Additional {term}`metadata`, including connections between the {term}`certificate<OpenPGP Certificate>`'s {term}`components<Component>`
|
||||
|
||||
This documentation collectively refers to {term}`component keys<OpenPGP Component Key>` and {term}`identity components<Identity Component>` as "the {term}`components<Component>` of a {term}`certificate<OpenPGP Certificate>`."
|
||||
|
||||
```{figure} plain_svg/Components_of_an_OpenPGP_Certificate.svg
|
||||
:name: fig-openpgp-certificate-components
|
||||
:alt: Depicts a box with white background and the title "OpenPGP certificate". In the box several other boxes and accompanying texts, representing component keys and User IDs, are shown. There are three component keys boxes with a green frame, each with a dotted lower-left section, that shows the text "key creation time" and the green public key symbol in the lower right area. All three have a title, a unique fingerprint below the box and a unique capability keyword, perpendicular to the box on the right side. The top-most component key box has a light-green background, with the title "Component Key (primary)" and capability keyword "certification". The second-to-top component key box has a white background, with the title "Component Key" and capability keyword "encryption". The lowest component key box has a white background, with the title "Component Key" and capability keyword "signing". There are two User ID boxes, each with a black frame, open to top left and lower right corner. Both boxes have a user icon on the top left side, the title "User ID" on the top right side and a User ID string at the bottom. The top box has "Alice Adams <alice@example.org>" and the lower box has "Alice" as User ID string.
|
||||
|
||||
Typical {term}`components<Component>` in an {term}`OpenPGP certificate`
|
||||
```
|
||||
|
||||
Every element in an {term}`OpenPGP certificate` revolves around a central {term}`component`: the *{term}`OpenPGP primary key`*. The primary key acts as a personal *{term}`certification authority`* ({term}`CA<Certification Authority>`) for the {term}`certificate<OpenPGP Certificate>`'s owner, enabling cryptographic statements regarding {term}`subkeys<OpenPGP Subkey>`, {term}`identities<Identity>`, {term}`expiration`, {term}`revocation`, and more.
|
||||
|
||||
```{note}
|
||||
{term}`OpenPGP certificates<OpenPGP Certificate>` tend to have a long lifespan, with the potential for modifications (typically by their owner) over time. {term}`Components<Component>` may be added or {term}`invalidated<Validation>` throughout a {term}`certificate<OpenPGP Certificate>`'s lifetime. However, once published, {term}`components<Component>` [cannot be removed](append-only) from {term}`certificates<OpenPGP Certificate>`.
|
||||
```
|
||||
|
||||
(component-keys)=
|
||||
## Component keys
|
||||
|
||||
An {term}`OpenPGP certificate` usually contains multiple {term}`component keys<OpenPGP Component Key>`. {term}`Component keys<OpenPGP Component Key>` serve in one of two roles: either as an "{term}`OpenPGP primary key`" or as an "{term}`OpenPGP subkey`."
|
||||
|
||||
{term}`OpenPGP component keys<OpenPGP Component Key>` logically consist of an [asymmetric cryptographic keypair](asymmetric-key-pair) and a creation timestamp. Once created, these attributes of a {term}`component key<OpenPGP Component Key>` remain fixed (for ECDH keys, two additional parameters are part of a {term}`component key`'s constitutive data[^ecdh-parameters]).
|
||||
|
||||
[^ecdh-parameters]: For [ECDH](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-algorithm-specific-part-for-ecd) {term}`component keys<OpenPGP Component Key>`, two additional algorithm parameters are integral to the {term}`component key<OpenPGP Component Key>`'s constitutive and immutable properties. Those parameters specify a hash function and a {term}`symmetric<Symmetric Cryptography>` encryption algorithm.
|
||||
|
||||
```{figure} plain_svg/Component_Key.svg
|
||||
:name: fig-component-key
|
||||
:alt: Depicts a box with white background and no title. In the box one other box is shown. The inner box has a green frame, with a dotted lower-left section, that shows the text "key creation time" and the green public key symbol, as well as the red-dotted private key symbol in the lower right area. In the top left of the inner box the text reads "Component Key."
|
||||
|
||||
An {term}`OpenPGP component key`
|
||||
```
|
||||
|
||||
{term}`Component keys<OpenPGP Component Key>` containing {term}`private key material` also include {term}`metadata` specifying the password protection scheme. This is another facet of {term}`metadata`, akin to the aforementioned creation timestamp and additional parameters for certain algorithms. However, this discussion focuses on {term}`OpenPGP certificates<OpenPGP Certificate>`, in which the {term}`component keys<OpenPGP Component Key>` contain only the public part of its cryptographic key data. For information on {term}`private keys<Transferable Secret Key>` in OpenPGP, see [](private_keys).
|
||||
|
||||
(fingerprint)=
|
||||
### Fingerprint
|
||||
|
||||
Each {term}`OpenPGP component key` possesses an *{term}`OpenPGP fingerprint`*. This {term}`fingerprint<OpenPGP Fingerprint>` is derived from the {term}`public key material<OpenPGP Certificate>`, the {term}`creation timestamp<Creation Time>`, and, when relevant, the ECDH parameters.
|
||||
|
||||
```{figure} plain_svg/Fingerprint.svg
|
||||
:name: fig-fingerprint
|
||||
:alt: Depicts a box with white background and the title "Fingerprint of an OpenPGP component key." Inside, another box with a green frame, the title "Component Key", the text "key creation time" on the lower left and a the green public key symbol on the lower right is shown. Below the component key box a fingerprint in a box with a light-yellow background and a yellow dotted line is depicted. The word "Fingerprint" is shown left of the box with the fingerprint and both are connected with a yellow dotted line.
|
||||
|
||||
Every {term}`OpenPGP component key` is identifiable by a {term}`fingerprint<OpenPGP Fingerprint>`.
|
||||
```
|
||||
|
||||
The {term}`fingerprint<OpenPGP Fingerprint>` of our example {term}`OpenPGP component key` is `C0A5 8384 A438 E5A1 4F73 7124 26A4 D45D BAEE F4A3 9E6B 30B0 9D55 13F9 78AC CA94`[^keyid].
|
||||
|
||||
[^keyid]: In OpenPGP version 4, the rightmost 64 bits were sometimes used as a shorter identifier, called "{term}`Key ID`."
|
||||
For example, an OpenPGP version 4 {term}`certificate<OpenPGP Certificate>` with the {term}`fingerprint<OpenPGP Fingerprint>` `B3D2 7B09 FBA4 1235 2B41 8972 C8B8 6AC4 2455 4239` might be referenced by the 64-bit {term}`Key ID` `C8B8 6AC4 2455 4239` or formatted as `0xC8B86AC424554239`.
|
||||
Historically, even shorter 32-bit identifiers were used, like this: `2455 4239`, or `0x24554239`. Such identifiers still appear in very old documents about PGP. However, [32-bit identifiers have been long deemed unfit for purpose](https://evil32.com/). At one point, 32-bit identifiers were called "short {term}`Key ID`," while 64-bit identifiers were referred to as "long Key ID."
|
||||
|
||||
```{note}
|
||||
In practice, the {term}`fingerprint<OpenPGP Fingerprint>` of a {term}`component key<OpenPGP Component Key>`, while not theoretically unique, functions effectively as a unique identifier. The use of a [cryptographic hash algorithm](cryptographic-hash) in generating {term}`fingerprints<OpenPGP Fingerprint>` makes the occurrence of two different {term}`component keys<OpenPGP Component Key>` with the same {term}`fingerprint<OpenPGP Fingerprint>` extremely unlikely[^finger-unique].
|
||||
```
|
||||
|
||||
[^finger-unique]: For both {term}`OpenPGP version 6<RFC>` and version 4, the likelihood of accidental occurrence of duplicate {term}`fingerprints<OpenPGP Fingerprint>` is negligible when {term}`key material` is generated based on an acceptable source of entropy. A separate question is if an attacker can purposely craft a second key with the same {term}`fingerprint<OpenPGP Fingerprint>` as a given pre-existing {term}`component key<OpenPGP Component Key>`. With the current state of the art, this is not possible for OpenPGP version 6 and version 4 keys. However, at the time of this writing, the SHA-1-based {term}`fingerprints<OpenPGP Fingerprint>` of OpenPGP version 4 are considered insufficiently strong at protecting against the generation of pairs of {term}`key material` with the same {term}`fingerprint<OpenPGP Fingerprint>`.
|
||||
|
||||
(primary-key)=
|
||||
### Primary key
|
||||
|
||||
The {term}`OpenPGP primary key` is a {term}`component key<OpenPGP Component Key>` that serves a distinct, central role in an {term}`OpenPGP certificate`:
|
||||
|
||||
- Its {term}`fingerprint<OpenPGP Fingerprint>` acts as an identifier for the entire {term}`OpenPGP certificate`.
|
||||
- It facilitates lifecycle operations, such as adding or {term}`invalidating<Validation>` {term}`subkeys<OpenPGP Subkey>` or {term}`identities<Identity>` within a {term}`certificate<OpenPGP Certificate>`.
|
||||
|
||||
```{admonition} Terminology
|
||||
:class: note
|
||||
|
||||
In the {term}`RFC`, the {term}`OpenPGP primary key` is occasionally referred to as "top-level key." Informally, it has also been termed the "{term}`master key<OpenPGP Primary Key>`."
|
||||
```
|
||||
|
||||
(subkeys)=
|
||||
### Subkeys
|
||||
|
||||
Modern {term}`OpenPGP certificates<OpenPGP Certificate>` typically include several {term}`subkeys<OpenPGP Subkey>` in addition to the {term}`primary key<OpenPGP Primary Key>`, although these {term}`subkeys<OpenPGP Subkey>` are optional.
|
||||
|
||||
While {term}`subkeys<OpenPGP Subkey>` have the same structural attributes as the {term}`primary key<OpenPGP Primary Key>`, they fulfill different roles. {term}`Subkeys<OpenPGP Subkey>` are cryptographically linked with the {term}`primary key<OpenPGP Primary Key>`, a relationship further discussed in {numref}`bind-subkey`.
|
||||
|
||||
```{figure} plain_svg/Binding_Subkeys.svg
|
||||
:name: fig-subkeys
|
||||
:alt: Diagram depicting three component keys. The primary key is positioned at the top, designated for certification. Below it, connected by arrows, are two subkeys labeled as "for encryption" and "for signing," respectively.
|
||||
|
||||
{term}`OpenPGP certificates<OpenPGP Certificate>` can contain multiple {term}`subkeys<OpenPGP Subkey>`.
|
||||
```
|
||||
|
||||
(identity-components)=
|
||||
## Identity components
|
||||
|
||||
{term}`Identity components<Identity Component>` in an {term}`OpenPGP certificate` are used by the {term}`certificate holder` to state that they are known by a certain identifier (like a name, or an email address).
|
||||
|
||||
(user-ids)=
|
||||
### User IDs in OpenPGP certificates
|
||||
|
||||
{term}`OpenPGP certificates<OpenPGP Certificate>` can contain multiple [User IDs](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#uid). Each {term}`User ID` associates the {term}`certificate<OpenPGP Certificate>` with an {term}`identity`.
|
||||
|
||||
```{figure} plain_svg/Binding_a_UserID.svg
|
||||
:name: fig-user-ids
|
||||
:alt: Depicts a diagram with white background and the title "User IDs". Inside, a public primary component key for certification and a User ID is shown. A green arrow points from component key to User ID and is annotated with a signature.
|
||||
|
||||
Relationship of {term}`User ID` to primary {term}`component key` in an {term}`OpenPGP certificate`
|
||||
```
|
||||
|
||||
A typical {term}`User ID` {term}`identity` is a UTF-8-encoded string composed of a name and an email address. By convention, {term}`User IDs<User ID>` align with the format described in [RFC2822](https://www.rfc-editor.org/rfc/rfc2822) as a *name-addr*.
|
||||
|
||||
For further conventions on {term}`User IDs<User ID>`, refer to the document [draft-dkg-openpgp-userid-conventions-00](https://datatracker.ietf.org/doc/draft-dkg-openpgp-userid-conventions/), dated 25 August 2023.
|
||||
|
||||
**Split User IDs**
|
||||
|
||||
One proposed variant for encoding {term}`identities<Identity>` in {term}`User IDs<User ID>` is to use ["split User IDs"](https://dkg.fifthhorseman.net/blog/2021-dkg-openpgp-transition.html#split-user-ids). Although uncommon, there are currently no significant technical barriers to implementing this format[^dkg-split].
|
||||
|
||||
[^dkg-split]: Historically, the OpenPGP ecosystem faced challenges in this context. For further details, refer to Daniel Kahn Gillmor's January 2019 article, ["What were Separated User IDs"](https://dkg.fifthhorseman.net/blog/2019-dkg-openpgp-transition.html#what-were-separated-user-ids).
|
||||
|
||||
The rationale for split {term}`User IDs<User ID>` lies in the distinction between a name and an email address, which represent two separate facets of an individual's {term}`identity`. Separating these elements simplifies the process for third parties tasked with certifying that an {term}`identity` is legitimately connected to a {term}`certificate<OpenPGP Certificate>`.
|
||||
|
||||
Consider this scenario: A third party is confident about the email-based {term}`identity` of an individual (e.g.,`<alice@example.org>`) and is willing to certify it. However, they might not have sufficient knowledge about the person's name-based {term}`identity` (e.g., `Alice Adams`), so are unwilling to extend the same level of {term}`certification`. Split {term}`User IDs<User ID>` address this dichotomy by allowing distinct {term}`certification` processes for each type of {term}`identity`.
|
||||
|
||||
(primary-user-id)=
|
||||
### Implications of the Primary User ID
|
||||
|
||||
Within a {term}`certificate<OpenPGP Certificate>`, a specific {term}`User ID` is designated as the [Primary User ID](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-primary-user-id).
|
||||
|
||||
Each {term}`User ID` carries associated preference settings, such as preferred encryption algorithms, which is detailed in {numref}`zoom-user-id`). When a {term}`certificate<OpenPGP Certificate>` is used in the context of a specific {term}`identity`, then the preferences associated with that {term}`identity component` are used. When a {term}`certificate<OpenPGP Certificate>` is used without reference to a specific {term}`identity`, the preferences associated with the {term}`direct key signature`, or the {term}`primary User ID` take precedence by default.
|
||||
|
||||
The {term}`primary User ID` was historically the main store for preferences that apply to the {term}`certificate<OpenPGP Certificate>` as a whole. For more on this, see {ref}`primary-metadata`.
|
||||
|
||||
(user-attributes)=
|
||||
### User attributes in OpenPGP
|
||||
While
|
||||
[user attributes](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#user-attribute-packet) are similar to {term}`User IDs<User ID>`, they are less commonly used.
|
||||
|
||||
Currently, the OpenPGP standard prescribes only one format to be stored in user attributes: an [image](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-the-image-attribute-subpack) in JPEG format. Typically, this image represents the key owner, although it is not required.
|
||||
|
||||
## Linking the components
|
||||
|
||||
To form an {term}`OpenPGP certificate`, individual {term}`components<Component>` are interconnected by the {term}`certificate holder` using their OpenPGP software. Within OpenPGP, this process is termed "binding", as in "a {term}`subkey<OpenPGP Subkey>` is bound to the {term}`primary key<OpenPGP Primary Key>`." These bindings are realized using cryptographic {term}`signatures<OpenPGP Signature Packet>`. An in-depth discussion of this topic can be found in [](signing_components).
|
||||
|
||||
In very abstract terms, the {term}`primary key<OpenPGP Primary Key>` of a {term}`certificate<OpenPGP Certificate>` acts as a root of trust or "{term}`certification authority<Certification Authority>`." It is responsible for:
|
||||
|
||||
- issuing {term}`signatures<OpenPGP Signature Packet>` that express the {term}`certificate holder`'s intent to use specific {term}`subkeys<OpenPGP Subkey>` or {term}`identity components<Identity Component>`;
|
||||
- conducting other lifecycle operations, including setting {term}`expiration` dates and marking {term}`components<Component>` as {term}`invalidated<Validation>` or "`revoked<Revocation>`."
|
||||
|
||||
By binding {term}`components<Component>` using digital {term}`signatures<OpenPGP Signature Packet>`, recipients of an {term}`OpenPGP certificate` need only {term}`validate<Validation>` the {term}`authenticity<Authentication>` of the {term}`primary key` to use for their communication partner. Traditionally, this is done by manually verifying the *{term}`fingerprint<OpenPGP Fingerprint>`* of the {term}`primary key<OpenPGP Primary Key>`. Once the {term}`validity<Validation>` of the {term}`primary key<OpenPGP Primary Key>` is confirmed, the {term}`validity<Validation>` of the remaining {term}`components<Component>` can be automatically assessed by the user's OpenPGP software. Generally, {term}`components<Component>` are {term}`valid<Validation>` parts of a {term}`certificate<OpenPGP Certificate>` if there is a statement signed by the {term}`certificate<OpenPGP Certificate>`'s {term}`primary key<OpenPGP Primary Key>` endorsing this {term}`validity<Validation>`.
|
||||
|
||||
(metadata-in-certificates)=
|
||||
## Metadata in certificates
|
||||
|
||||
{term}`OpenPGP certificates<OpenPGP Certificate>`, their {term}`component keys<Component Key>`, and {term}`identities<Identity>` possess {term}`metadata` that is not stored within the {term}`components<Component>` it pertains to. Instead, this {term}`metadata` is stored within signature packets, which are integral to the structure of an OpenPGP certificate.
|
||||
|
||||
Key attributes, such as {term}`capabilities<Capability>` (like *signing* or *encryption*) and {term}`expiration times<Expiration Time>`, are examples of {term}`metadata` not stored in the {term}`component key` data. How this {term}`metadata` is stored depends on the {term}`component`:
|
||||
|
||||
- **{term}`Primary key<OpenPGP Primary Key>` {term}`metadata`** is defined either through a {term}`direct key signature` on the {term}`primary key<OpenPGP Primary Key>` (preferred in OpenPGP version 6), or by associating the {term}`metadata` with the [Primary User ID](primary-user-id).
|
||||
|
||||
- **{term}`Subkey<OpenPGP Subkey>` {term}`metadata`** is defined within the [subkey binding signature](recipe-binding-subkeys) that links the {term}`subkey<OpenPGP Subkey>` to the {term}`certificate<OpenPGP Certificate>`.
|
||||
|
||||
- **{term}`Identity component` {term}`metadata`** is associated via the [certifying self-signature](bind-identity) that links the {term}`identity` (usually in the form of a {term}`User ID`) to the {term}`certificate<OpenPGP Certificate>`.
|
||||
|
||||
It is crucial to note that the {term}`components<Component>` of an {term}`OpenPGP certificate` remain static after their creation. The use of {term}`signatures<OpenPGP Signature Packet>` to store {term}`metadata` allows for subsequent modifications without altering the original {term}`component<Component>`. For instance, a {term}`certificate holder` can update the {term}`expiration time` of a {term}`component` by issuing a new, superseding {term}`signature<OpenPGP Signature Packet>`.
|
||||
|
||||
```{figure} plain_svg/Primary_key_metadata.svg
|
||||
:name: fig-primary-metadata
|
||||
:alt: Depicts a direct key signature, associated with a primary component key.
|
||||
|
||||
{term}`Metadata` can be associated with the {term}`primary key<OpenPGP Primary Key>` using a *{term}`direct key signature`*.
|
||||
```
|
||||
|
||||
(key-flags)=
|
||||
### Defining operational capabilities of component keys with key flags
|
||||
|
||||
Each {term}`component key` has a set of ["key flags"](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#key-flags) that delineate the operations a key can perform.
|
||||
|
||||
Commonly used {term}`key flags<Key Flag>` include:
|
||||
|
||||
- **{term}`Certification<Certification Key Flag>`**: enables issuing third-party {term}`certifications<Certification>`
|
||||
- **{term}`Signing<Signing Key Flag>`**: allows the key to sign data
|
||||
- **{term}`Encryption<Encryption Key Flag>`**: allows the key to encrypt data
|
||||
- **{term}`Authentication<Authentication Key Flag>`**: primarily used for SSH authentication[^auth-flag]
|
||||
|
||||
[^auth-flag]: It's important to note that the function of the [authentication](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-authentication-via-digital-) {term}`key flag` is unrelated to the {term}`authentication` process used in certifying OpenPGP {term}`identities<Identity>` and linking them to {term}`certificate<OpenPGP Certificate>`. Rather, this flag indicates a mechanism that uses {term}`cryptographic signatures<OpenPGP Signature Packet>` to confirm control of {term}`private key material` with a remote system.
|
||||
|
||||
```{note}
|
||||
Distinct {term}`component keys<Component Key>` handle specific operations. Only the {term}`primary key<OpenPGP Primary Key>` can be used for {term}`certification`, although it can have additional {term}`capabilities<Capability>`. {term}`Subkeys<OpenPGP Subkey>` can be used for signing, encryption, and authentication but cannot have the {term}`certification` {term}`capability`. A {term}`component key` can technically have multiple {term}`capabilities<Capability>`. It is considered good practice, however, to [use separate keys for each capability](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#section-10.1.5-7).
|
||||
|
||||
Notably, in many algorithms, encryption and signing-related functionalities (i.e., {term}`certification<Certification Key Flag>`, {term}`signing<Signing Key Flag>`, {term}`authentication<Authentication Key Flag>`) are mutually exclusive, because the algorithms only support one of those two families of operations[^key-flag-sharing].
|
||||
```
|
||||
|
||||
[^key-flag-sharing]: With ECC algorithms, it's impossible to combine {term}`encryption<Encryption Key Flag>` functions with those intended for {term}`signing<Signing Key Flag>`. For example, ed25519 is specifically used for {term}`signing<Signing Key Flag>`; cv25519 is designated for {term}`encryption<Encryption Key Flag>`.
|
||||
|
||||
(preferences-features)=
|
||||
### Algorithm preferences and feature signaling
|
||||
|
||||
OpenPGP incorporates significant ["cryptographic agility"](https://en.wikipedia.org/wiki/Cryptographic_agility). It doesn't rely on a single fixed set of algorithms. Instead, it defines a suite of cryptographic primitives from which users (or their applications) can choose.
|
||||
|
||||
This agility facilitates the easy adoption of new cryptographic primitives into the standard, allowing for a seamless transition. Users can gradually migrate to new cryptographic mechanisms without disruption.
|
||||
|
||||
However, this approach requires that OpenPGP software determine the cryptographic mechanisms that a set of communication partners can handle and prefer. OpenPGP employs several mechanisms for this purpose, which allow negotiation between sender and recipient. It's important to note that OpenPGP is not an online scheme; thus, this negotiation is effectively one-way. The active party interprets the preferences expressed in the {term}`certificate<OpenPGP Certificate>` of the passive party.
|
||||
|
||||
Negotiation mechanisms in OpenPGP include:
|
||||
|
||||
- [Preferred hash algorithms](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#preferred-hashes-subpacket)
|
||||
- [Preferred symmetric ciphers for v1 SEIPD](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#preferred-v1-seipd)
|
||||
- [Preferred AEAD ciphersuites](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#preferred-v2-seipd)
|
||||
- [Features subpacket](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#features-subpacket)
|
||||
- [Preferred compression algorithms](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#preferred-compression-subpacket)
|
||||
|
||||
Beyond these explicitly expressed preferences, implementations also deduce {term}`capabilities<Capability>` of communication partners based on the version of the {term}`OpenPGP certificate` they possess.
|
||||
|
||||
#### User ID-specific preferences
|
||||
|
||||
As a starting point, a {term}`certificate<OpenPGP certificate>` has a set of preferences that apply generally. These are defined either in a {term}`direct key signature`, or via the {term}`primary User ID` of the {term}`certificate<OpenPGP certificate>`.
|
||||
|
||||
Additionally, OpenPGP allows modeling {term}`User ID`-specific preferences. The idea is that a user may prefer a different suite of algorithms on their private email account compared to their work email account. Such {term}`identity`-specific preferences can be expressed on the certifying {term}`signatures<OpenPGP Signature Packet>` that bind {term}`User IDs<User ID>` to a {term}`certificate<OpenPGP certificate>`.
|
||||
|
||||
## A typical OpenPGP certificate, revisited
|
||||
|
||||
Following our review of how {term}`keys<Component Key>` and {term}`identity components<Identity Component>` are linked, let's reexamine the {term}`OpenPGP certificate` from {numref}`fig-openpgp-certificate-components`. Our focus now extends to all of its binding signatures and the {term}`direct key signature` that contains {term}`metadata` for the full {term}`certificate<OpenPGP certificate>`:
|
||||
|
||||
```{figure} plain_svg/OpenPGP_Certificate.svg
|
||||
:name: fig-openpgp-certificate
|
||||
:alt: Depicts an OpenPGP certificate, including a set of components, binding signatures, and a direct key signature on the primary key.
|
||||
|
||||
This shows a typical {term}`OpenPGP certificate`, including binding {term}`signatures<OpenPGP Signature Packet>` for all of its {term}`components<Component>`, and a {term}`signature<OpenPGP Signature Packet>` that associates {term}`metadata` with the {term}`primary key<OpenPGP Primary Key>`.
|
||||
```
|
||||
|
||||
(revocations)=
|
||||
## Revocations
|
||||
|
||||
When a {term}`certificate holder` needs to {term}`invalidate<Validation>` certain {term}`components<Component>` of their {term}`certificate<OpenPGP Certificate>`, or even the entire {term}`certificate<OpenPGP Certificate>`, they accomplish this through "{term}`revocation`." {term}`Revoking<Revocation>` the {term}`primary key<OpenPGP Primary Key>` renders the entire {term}`certificate<OpenPGP Certificate>` {term}`invalid<Validation>`.
|
||||
|
||||
Notably, {term}`revocations<Revocation>` are not the only means by which {term}`components<Component>` can become {term}`invalid<Validation>`. Other factors, such as the passing of a {term}`component`'s {term}`expiration time`, can also render {term}`components<Component>` {term}`invalid<Validation>`.
|
||||
|
||||
For more detailed information on {term}`revoking<Revocation>` specific {term}`components<Component>` of a {term}`certificate<OpenPGP Certificate>`, see the section on {ref}`self-revocations`.
|
||||
|
||||
(third-party-identity-certifications)=
|
||||
## Third-party (identity) certifications
|
||||
|
||||
Since its inception, {term}`third-party identity certifications<Third-party Identity Certification>` have been a cornerstone of the OpenPGP ecosystem. The original PGP designers, starting with Phil Zimmermann, advocated for decentralized {term}`trust models<Trust Model>` over reliance on centralized authorities. This decentralized approach in OpenPGP is known as the ["Web of Trust."](wot)
|
||||
|
||||
{term}`Third-party certifications<Third-party Identity Certification>` are statements by OpenPGP users confirming that a user with a specific {term}`identity` is the owner of a particular {term}`OpenPGP certificate`.
|
||||
|
||||
For example, Bob's OpenPGP software may issue a {term}`certification` that Bob has checked that the {term}`User ID` `Alice Adams <alice@example.org>` and the {term}`certificate<OpenPGP Certificate>` with the {term}`fingerprint<OpenPGP Fingerprint>` `AAA1 8CBB 2546 85C5 8358 3205 63FD 37B6 7F33 00F9 FB0E C457 378C D29F 1026 98B3` are legitimately linked.
|
||||
|
||||
Take, for instance, a scenario where Bob's OpenPGP software issues a {term}`certification` confirming as legitimate the link between the {term}`User ID` `Alice Adams <alice@example.org>` and the {term}`certificate<OpenPGP Certificate>` bearing the {term}`fingerprint<OpenPGP Fingerprint>` `AAA1 8CBB 2546 85C5 8358 3205 63FD 37B6 7F33 00F9 FB0E C457 378C D29F 1026 98B3`.
|
||||
|
||||
This process assumes that Bob knows the person known as `Alice Adams` and is confident that `alice@example.org` is indeed Alice's email address. Bob also verifies that the {term}`certificate<OpenPGP Certificate>` his OpenPGP software associates with Alice matches the one Alice uses. In essence, both users must have a {term}`certificate<OpenPGP Certificate>` for Alice with an identical {term}`fingerprint<OpenPGP Fingerprint>`. In OpenPGP version 6, manual {term}`fingerprint<OpenPGP Fingerprint>` comparison by end-users is discouraged, with a replacement {term}`verification` mechanism still under development. The {term}`verification` process must occur over a sufficiently secure channel, such as an end-to-end encrypted video call or a face-to-face meeting.
|
||||
|
||||
For more on third-party {term}`certifications<Certification>`, see {ref}`third-party-certifications`.
|
25
book/source/compression.md
Normal file
|
@ -0,0 +1,25 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Compression
|
||||
|
||||
Optional compression of data is one element of OpenPGP's composable functionality. Compression within OpenPGP can be convenient to applications.
|
||||
|
||||
In one use case, this functionality is particularly helpful: When encrypting a message, the encrypted output is by definition high-entropy, and cannot be compressed anymore - even if the plaintext message was low-entropy, and could have been compressed well (like, for example, a text-file).
|
||||
|
||||
|
||||
This means that to use whatever potential for compression exists, the message must be compressed *before* encryption. OpenPGP offers an integrated compression mechanism to make this convenient (otherwise, messages would need to be compressed and decompressed before and after encryption, to achieve the same space-efficiency).
|
||||
|
||||
## Decompression yields a 'wrapped' OpenPGP packet stream
|
||||
|
||||
Compression in OpenPGP is a simple mechanism: A [Compressed Data packet](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-compressed-data-packet-type) acts as a compressed container for a series of OpenPGP packets.
|
||||
|
||||
The compressed data packet consists of the specification of which compression algorithm is used, followed by a compressed representation of the contained data.
|
||||
|
||||
The series of OpenPGP packets inside the Compressed Data packet can be handled like any stream of OpenPGP packets.
|
||||
|
||||
## Typical usage
|
||||
|
||||
Compressed data packets are often used inside [encrypted data packets](/encryption), or wrapping the data of an [inline-signed message](inline-signature).
|
110
book/source/conf.py
Normal file
|
@ -0,0 +1,110 @@
|
|||
# SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
#
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# For the full list of built-in configuration values, see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
from datetime import date
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
||||
|
||||
project = 'OpenPGP for application developers'
|
||||
author = 'The "Notes on OpenPGP" project'
|
||||
upstream_url = "https://codeberg.org/openpgp/notes"
|
||||
license_url = "https://creativecommons.org/licenses/by-sa/4.0/"
|
||||
copyright = f'{date.today().year}, <a href="{upstream_url}">{author}</a>, <a href="{license_url}">CC-BY-SA-4.0</a>'
|
||||
version = "0.1"
|
||||
suppress_warnings = [
|
||||
'epub.unknown_project_files',
|
||||
]
|
||||
description = 'The essential OpenPGP guide for application developers. Learn the OpenPGP standard for cryptographic operations and how to implement it in your projects. Gain insights into digital signatures, key management, certificates, and more. Version agnostic.'
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||
|
||||
extensions = [
|
||||
'myst_parser',
|
||||
'sphinxext.opengraph',
|
||||
]
|
||||
source_suffix = ['.md', '.rst']
|
||||
|
||||
templates_path = ['_templates']
|
||||
exclude_patterns = []
|
||||
|
||||
# number code-blocks, figures and tables, if they have a caption
|
||||
numfig = True
|
||||
# number figures in flat hierarchy
|
||||
numfig_secnum_depth = 0
|
||||
|
||||
# format today as YYYY-MM-DD instead of language specific
|
||||
today_fmt = "%Y-%m-%d"
|
||||
|
||||
# -- Options for MyST-parser -------------------------------------------------
|
||||
# https://myst-parser.readthedocs.io/en/latest/configuration.html
|
||||
|
||||
# we want to circumvent obscure warnings about footnotes following a heading:
|
||||
# https://github.com/executablebooks/MyST-Parser/issues/352
|
||||
myst_footnote_transition = False
|
||||
|
||||
# Enable definition lists
|
||||
# https://myst-parser.readthedocs.io/en/latest/syntax/optional.html#syntax-definition-lists
|
||||
myst_enable_extensions = [
|
||||
"deflist",
|
||||
]
|
||||
|
||||
# https://myst-parser.readthedocs.io/en/latest/configuration.html#setting-html-metadata
|
||||
myst_html_meta = {
|
||||
"description lang=en": description,
|
||||
"keywords": "openpgp, cryptographic operations, data encryption, digital signatures, key management, identity verification, secure communication, pgp, cryptographic protocols, public key encryption, openpgp v4, openpgp v6, openpgp implementation",
|
||||
"property=og:locale": "en_US"
|
||||
}
|
||||
|
||||
# -- Options for EPUB output -------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-epub-output
|
||||
|
||||
epub_copyright = f'{date.today().year}, {author} ({upstream_url}), CC-BY-SA-4.0'
|
||||
epub_cover = ('_static/epub/img/cover.png', 'cover.j2')
|
||||
epub_description = description
|
||||
epub_css_files = [
|
||||
'epub/css/custom.css'
|
||||
]
|
||||
epub_basename = project.replace(" ", "_")
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
||||
|
||||
html_theme = 'alabaster'
|
||||
html_static_path = ['_static']
|
||||
html_css_files = [
|
||||
('html/css/custom.css', {'priority': 1000})
|
||||
]
|
||||
|
||||
html_favicon = '_static/html/img/favicon.ico'
|
||||
html_logo = '_static/html/img/logo.svg'
|
||||
html_show_sphinx = False
|
||||
html_show_sourcelink = False
|
||||
html_title = project
|
||||
|
||||
# https://github.com/sphinx-doc/alabaster/blob/0.x/alabaster/theme.conf
|
||||
html_theme_options = {
|
||||
'code_font_size': '9pt',
|
||||
'extra_nav_links': {
|
||||
'Sources on Codeberg': upstream_url,
|
||||
'Download EPUB': f'https://openpgp.dev/book/{epub_basename}.epub',
|
||||
},
|
||||
'show_relbars': 'yes',
|
||||
'show_powered_by': False,
|
||||
}
|
||||
|
||||
# https://github.com/wpilibsuite/sphinxext-opengraph#simple-config
|
||||
ogp_site_url = 'https://openpgp.dev/book/'
|
||||
ogp_image = '_static/html/img/logo.png'
|
||||
ogp_type = 'book'
|
||||
|
||||
ogp_custom_meta_tags = [
|
||||
f'<meta property="og:description" content="{description}" />',
|
||||
]
|
||||
|
144
book/source/cryptography.md
Normal file
|
@ -0,0 +1,144 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Cryptographic concepts and terms
|
||||
|
||||
(cryptographic-hash)=
|
||||
## Cryptographic hash functions
|
||||
|
||||
[Cryptographic hash functions](https://en.wikipedia.org/wiki/Cryptographic_hash_function) take data strings of any length (like a text message or file) and output a fixed-size code, a "hash digest," which is often abbreviated as either "digest" or "hash." A hash digest is also sometimes called a "(cryptographic) checksum." A hash digest acts like a unique identifier for the original data.
|
||||
|
||||
Cryptographic hash functions have two important properties:
|
||||
|
||||
- [**Pre-image resistance**](https://en.wikipedia.org/wiki/Preimage_attack): Given a hash digest, it should be very difficult to determine any data that matches this hash digest (including, but not limited to, the original data the hash represents). This property embodies the concept of a [one-way function](https://en.wikipedia.org/wiki/One-way_function) – a calculation that is easy to perform, but very hard to reverse.
|
||||
- [**Collision resistance**](https://en.wikipedia.org/wiki/Collision_resistance): It should be very difficult to find two distinct pieces of data that map to the same hash digest.
|
||||
|
||||
(message-authentication-code)=
|
||||
## Message authentication codes
|
||||
|
||||
A [message authentication code](https://en.wikipedia.org/wiki/Message_authentication_code) (MAC), also known as an {term}`authentication tag`, is a small piece of information used to verify the integrity and authenticity of a message.
|
||||
|
||||
It is derived from the original message using a {term}`(symmetric) secret key<Symmetric Secret Key>`. The recipient of a message containing a MAC, who is also in possession of the secret key, can verify that the message has not been altered.
|
||||
|
||||
[HMAC](https://en.wikipedia.org/wiki/HMAC) is a type of MAC that relies on a hash function. It is used in the OpenPGP protocol.
|
||||
|
||||
### Key derivation functions
|
||||
|
||||
A hash function can also be used to create a [key derivation function](https://en.wikipedia.org/wiki/Key_derivation_function) (KDF).
|
||||
One application of KDFs is to generate symmetric key material from a password by iteratively passing it through a hash function.
|
||||
|
||||
A notable KDF for the OpenPGP specification is the [HKDF](https://en.wikipedia.org/wiki/HKDF), which is a key derivation function based on the HMAC.
|
||||
|
||||
For detailed information on KDFs and their role in the OpenPGP protocol, see the [encrypted secrets](protected-private-keys) chapter and the [SEIPDv2](seipd-v2) section of the encryption chapter.
|
||||
|
||||
(symmetric-key-cryptography)=
|
||||
## Symmetric-key cryptography
|
||||
|
||||
[Symmetric-key cryptography](https://en.wikipedia.org/wiki/Symmetric-key_algorithm) uses the same cryptographic key for both encryption and decryption, unlike asymmetric cryptography where a pair of keys is used: a public key for encryption and a corresponding private key for decryption. Symmetric-key cryptographic systems support *encryption/decryption* operations.
|
||||
|
||||
Participants in symmetric-key operations need to exchange the shared secret over a secure channel.
|
||||
|
||||
```{figure} plain_svg/symmetric_key.svg
|
||||
:name: fig-symmetric-key
|
||||
:alt: Depicts a box with a white background and the title "Symmetric key". In the box a single key symbol, rendered with full yellow line, is shown pointing to the right hand side.
|
||||
|
||||
A symmetric cryptographic key (which acts as a shared secret)
|
||||
```
|
||||
|
||||
### Benefits and downsides
|
||||
|
||||
Symmetric-key cryptography has major benefits: It is much faster than public-key cryptography (see below). Also, most current symmetric-key cryptographic mechanisms are believed to be resilient against possible advances in quantum computing[^postquantum].
|
||||
|
||||
[^postquantum]: Daniel J. Bernstein (2009). ["Introduction to post-quantum cryptography" (PDF)](http://www.pqcrypto.org/www.springer.com/cda/content/document/cda_downloaddocument/9783540887010-c1.pdf) states that: "many important classes of cryptographic systems", including secret-key cryptographic mechanisms like AES "[..] are believed to resist classical computers and quantum computers." (pages 1, 2).
|
||||
|
||||
However, exchanging the required shared secret is a problem that needs to be solved separately.
|
||||
|
||||
[Hybrid cryptosystems](hybrid-cryptosystems) combine the advantages of symmetric-key cryptography with a separate mechanism for managing the shared secret, using public-key cryptography.
|
||||
|
||||
### Symmetric-key cryptography in OpenPGP
|
||||
|
||||
Symmetric-key cryptography is used in OpenPGP in three contexts:
|
||||
|
||||
- most prominently, as part of a hybrid cryptosystem to encrypt and decrypt data,
|
||||
- to encrypt [password-protected private key material](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-secret-key-encryption), and
|
||||
- for [password-protected data encryption](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-symmetric-key-encrypted-ses), a less commonly used feature of the standard.
|
||||
|
||||
Where symmetric keys are used in OpenPGP for data encryption, they are called either "message keys" or "session keys[^sessionkey]."
|
||||
|
||||
[^sessionkey]: In OpenPGP version 6, the ["Version 2 Symmetrically Encrypted Integrity Protected Data Packet Format"](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-version-2-symmetrically-enc) requires that a "message key" is derived from a "session key." In contrast, up to OpenPGP version 4, and in version 6 when using ["Version 1 Symmetrically Encrypted Integrity Protected Data Packet Format"](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-version-1-symmetrically-enc), the "session key" was used directly as a symmetric encryption key.
|
||||
|
||||
### Authenticated encryption with associated data (AEAD)
|
||||
|
||||
[Authenticated encryption](https://en.wikipedia.org/wiki/Authenticated_encryption) offers more than just confidentiality; it ensures data integrity too.
|
||||
|
||||
In OpenPGP version 6, AEAD replaced the MDC[^MDC] mechanism to address malleability. In earlier OpenPGP versions, malicious alterations to ciphertext might go unnoticed. AEAD guards against such undetected changes.
|
||||
|
||||
[^MDC]: OpenPGP version 4 introduced a mechanism called MDC (Modification Detection Code), which fulfills a comparable purpose as AEAD in safeguarding message integrity. MDC is a non-standard mechanism, but no known attacks have compromised this scheme as of this document's last update.
|
||||
|
||||
By addressing the malleability problem, AEAD also counters a variation of the EFAIL[^efail] attack.
|
||||
|
||||
[^efail]: A variation of the [EFAIL](https://en.wikipedia.org/wiki/EFAIL) attack can be prevented by both the MDC and AEAD mechanisms. Also see ["No, PGP is not broken, not even with the Efail vulnerabilities,"](https://proton.me/blog/pgp-vulnerability-efail) especially the section "Malleability Gadget Exfiltration Channel Attack."
|
||||
|
||||
(public-key-cryptography)=
|
||||
## Public-key (asymmetric) cryptography
|
||||
|
||||
[Public-key cryptography](https://en.wikipedia.org/wiki/Public-key_cryptography) uses asymmetric pairs of related keys. Each pair consists of a public key and a private key. These systems support encryption, decryption, and digital signature operations.
|
||||
|
||||
Unlike symmetric cryptography, participants are not required to pre-arrange a shared secret. In public-key cryptography, the public key material is shared openly for certain cryptographic operations, such as encryption and signature verification, while the private key, kept confidential, is used for operations like decryption and signature creation.
|
||||
|
||||
(asymmetric-key-pair)=
|
||||
### Asymmetric cryptographic key pairs
|
||||
|
||||
Throughout this document, we will frequently reference asymmetric cryptographic key pairs:
|
||||
|
||||
```{figure} plain_svg/asymmetric_keypair.svg
|
||||
:name: fig-asymmetric-keypair
|
||||
:alt: Depicts a box with white background and the title "Asymmetric keypair". In the box two key symbols with text next to them are shown. The top key symbol is rendered using full green lines, points to the right hand side and has the accompanying text "Public key". The lower key symbol is rendered using dotted red lines, points to the left hand side and has the accompanying text "Private key".
|
||||
|
||||
An asymmetric cryptographic key pair
|
||||
```
|
||||
|
||||
Each key pair comprises two parts: the {term}`public key<OpenPGP Certificate>` and the {term}`private key<Transferable Secret Key>`. For ease of identification in this documentation, the {term}`public key<OpenPGP Certificate>` will be shown in green and the private key in red. Additionally, {term}`public keys<OpenPGP Certificate>` are depicted with a solid border and pointing to the right, while {term}`private keys<Transferable Secret Key>` are shown with a dotted border and pointing to the left.
|
||||
|
||||
It's important to note that in many scenarios, only the {term}`public key<OpenPGP Certificate>` is exposed or used. These situations will be elaborated upon in subsequent sections of this document.
|
||||
|
||||
```{figure} plain_svg/public_key.svg
|
||||
:name: fig-public-key
|
||||
:alt: Depicts a box with white background and the title "Public part of an asymmetric keypair". In the box one key symbol with text next to it is shown. The key symbol is rendered using full green lines, points to the right hand side and has the accompanying text "Public key".
|
||||
|
||||
The public part of an asymmetric key pair
|
||||
```
|
||||
|
||||
### Usage and terminology in OpenPGP
|
||||
|
||||
OpenPGP extensively uses {term}`public-key cryptography<Public Key Algorithm>` for encryption and digital signing operations.
|
||||
|
||||
```{admonition} Terminology
|
||||
:class: note
|
||||
|
||||
OpenPGP documentation, including the foundational RFC, opts for the term "secret key" over the more widely accepted "private key." As a result, in the RFC, you'll encounter the "public/secret key" pairing more frequently than "public/private key." This terminology reflects historical developments in the OpenPGP community, not a difference in technology.
|
||||
|
||||
While "secret key" (as used in the OpenPGP RFC) and "private key" serve the same purpose in cryptographic operations, this document will use the more common "public/private" terminology for clarity and consistency with broader cryptographic discussions.
|
||||
```
|
||||
|
||||
### Cryptographic digital signatures
|
||||
|
||||
[Digital signatures](https://en.wikipedia.org/wiki/Digital_signature) are a fundamental mechanism of {term}`asymmetric cryptography`, providing secure, mathematical means to validate the {term}`authenticity<Authentication>`, integrity, and origin of digital messages and documents.
|
||||
|
||||
In OpenPGP, digital signatures have diverse applications, extending beyond mere validation of a message's origin. They can signify various intents, including {term}`certification`, consent, acknowledgment, or even revocation by the signer. The multifaceted nature of "statements" conveyed through {term}`digital signatures<OpenPGP Signature Packet>` in cryptographic protocols is wide-ranging but crucial, allowing third parties to inspect/evaluate these statements for {term}`authenticity<Authentication>` and intended purpose.
|
||||
|
||||
{term}`Digital signatures<OpenPGP Signature Packet>` in OpenPGP are used in two primary contexts:
|
||||
|
||||
- [Data signatures](signing_data)
|
||||
- [Signatures on components](/signing_components)
|
||||
|
||||
(hybrid-cryptosystems)=
|
||||
## Hybrid cryptosystems
|
||||
|
||||
[Hybrid cryptosystems](https://en.wikipedia.org/wiki/Hybrid_cryptosystem) combine the use of {term}`symmetric<Symmetric Cryptography>` and {term}`asymmetric (public-key)<Asymmetric Cryptography>` cryptography to capitalize on the strengths of each, namely {term}`symmetric cryptography`'s speed and efficiency and {term}`public-key cryptography<Asymmetric Cryptography>`'s mechanism for secure key exchange.
|
||||
|
||||
### Usage and terminology in OpenPGP
|
||||
|
||||
OpenPGP uses a {term}`hybrid cryptosystem` for encryption. This approach involves generating unique shared secrets, known as "session keys," for each session. For detailed information on this topic, please refer to the chapters [](encryption) and [](decryption).
|
184
book/source/decryption.md
Normal file
|
@ -0,0 +1,184 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Decryption
|
||||
|
||||
Message decryption is the process of taking an encrypted message and recovering its plaintext.
|
||||
This involves multiple steps.
|
||||
|
||||
Implementations typically first process the PKESK and SKESK packets leading the SEIPD packet to identify \*ESK packets suitable for decryption.
|
||||
A PKESK packet is suitable if it contains a recipient-Key ID matching a decryption (sub-) key of the user's certificate.
|
||||
Typically, all \*ESK packets leading a SEIPD packet contain the same *session key* once decrypted.
|
||||
|
||||
```{note}
|
||||
|
||||
Anonymous-recipient PKESK packets contain a recipient-Key ID of `0`, so if no suitable non-anonymous PKESK was found, any anonymous PKESKs are tried with any available decryption (sub-) keys (see [](decryption-anonymous-recipient)).
|
||||
```
|
||||
|
||||
If no suitable PKESK packets were found, SKESK packets are tried next, meaning the user is typically prompted to enter a decryption passphrase.
|
||||
|
||||
Once any of these methods succeeded, the resulting *session key* is used to decrypt the SEIPD packet.
|
||||
|
||||
## Passphrase-protected session key (SKESK)
|
||||
|
||||
Decrypting a SKESK packet to recover the *session key* is done by performing the encryption steps in reverse, based on a user-provided passphrase.
|
||||
|
||||
In both version 4 and version 6 of the SKESK packet, the user is prompted to enter a passphrase, which is passed through the S2K function described by the SKESK packet.
|
||||
However, the subsequent steps of the procedure are different, as described in the following sections.
|
||||
|
||||
### SKESK v4
|
||||
|
||||
Here, the result of the S2K function is a symmetric key, which is either used to decrypt the encrypted session key contained in the SKESK packet, or - less commonly - used as session key directly (see [](decryption-skesk4-direct-method)).
|
||||
|
||||
```{note}
|
||||
|
||||
The "direct method" where the result of the S2K function is directly used as session key is only applicable if only one SKESK packet is present.
|
||||
```
|
||||
|
||||
```{figure} plain_svg/SKESKv4-decryption.svg
|
||||
:name: fig-skeskv4-decryption
|
||||
:alt: Diagram depicting how the S2K function is used to derive key symmetric key from the user-provided passphrase. This key is then either used directly as session key, or used to decrypt the encrypted session key.
|
||||
|
||||
Decrypting the session key from a version 4 SKESK packet.
|
||||
```
|
||||
|
||||
With version 4 SKESK packets, which are only used with version 1 SEIPD packets, the *session key* is used as *message key* without an intermediate derivation.
|
||||
|
||||
(decryption-skesk4-direct-method)=
|
||||
#### Direct-Method
|
||||
|
||||
In version 4 of the SKESK packet, the encrypted session key is optional. A missing encrypted session key signals the use of the "direct-method," which means the result of passing the passphrase through the S2K function is directly used as the session key/message key.
|
||||
|
||||
When the direct method is used, the symmetric cipher algorithm ID of the SKESK packet dictates the cipher algorithm used to decrypt the plaintext from the SEIPD packet.
|
||||
|
||||
Otherwise, the cipher algorithm ID to decrypt the SEIPD packet was prefixed to the decrypted session key.
|
||||
|
||||
Sanitizing this algorithm ID of the decrypted session key acts as a very early quick check to verify that the used passphrase was correct. For further validation of the session key, see [](decryption-seipd-quick-check).
|
||||
|
||||
|
||||
### SKESK v6
|
||||
|
||||
With version 6 SKESK packets, the result of the passing the passphrase through the S2K function is used as *initial keying material* (IKM) to derive a symmetric *key encryption key* using HKDF as a key derivation function. The HKDF function doesn't use any salt in this step, and the *info* parameter is assembled from parameters of the SKESK packet.
|
||||
|
||||
In the next step, this symmetric key is used to decrypt the *session key* using AEAD.
|
||||
The AEAD function uses information from the associated SEIPD v2 packet as *additional data*.
|
||||
The function is also salted using the SEIPD v2's salt.
|
||||
The *AEAD Auth Tag* of the SKESK packet is used as authentication tag.
|
||||
|
||||
The result is the *session key*.
|
||||
|
||||
```{figure} plain_svg/SKESKv6-decryption.svg
|
||||
:name: fig-skeskv6-decryption
|
||||
:alt: Diagram depicting the complicated process of deriving the session key from a SKESK version 6 packet.
|
||||
|
||||
Decrypting the session key from a version 6 SKESK packet.
|
||||
```
|
||||
|
||||
## Key-protected session key (PKESK)
|
||||
|
||||
More common than SKESK packets are PKESK packets which are used to protect the session key using an encryption key of the recipient.
|
||||
|
||||
### PKESK v3
|
||||
|
||||
With version 3 PKESKs, the recipient's secret encryption (sub-) key is directly used to decrypt the encrypted *session key*.
|
||||
The Key ID of the subkey to be used is recorded in the PKESKs key-id field. A value of `0` indicates an anonymous recipient (see [](decryption-anonymous-recipient)).
|
||||
|
||||
To detect, which symmetric cipher is used to decrypt the SEIPD v1 packet later on, each public key algorithm uses a slightly different encoding to unpack the symmetric algorithm tag from the decrypted session key. See the respective sections[^rsa-spec] [^elgamal-spec] [^ecdh-spec] [^x25519-spec] [^x448-spec] of the standard. Typically, the cipher algorithm ID is prefixed to the actual session key.
|
||||
|
||||
[^rsa-spec]: [Algorithm-Specific Fields for RSA encryption](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-algorithm-specific-fields-f)
|
||||
[^elgamal-spec]: [Algorithm-Specific Fields for Elgamal encryption](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-algorithm-specific-fields-fo)
|
||||
[^ecdh-spec]: [Algorithm-Specific Fields for ECDH encryption](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-algorithm-specific-fields-for)
|
||||
[^x25519-spec]: [Algorithm-Specific Fields for X25519 encryption](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-algorithm-specific-fields-for-)
|
||||
[^x448-spec]: [Algorithm-Specific Fields for X448 encryption](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-algorithm-specific-fields-for-x)
|
||||
|
||||
```{figure} plain_svg/PKESKv3-decryption.svg
|
||||
:name: fig-decryption-pkesk3
|
||||
:alt: Depicts, how the secret-key component of the users encryption subkey is directly used to decrypt the encrypted session key.
|
||||
|
||||
Decrypting the session key from a version 3 PKESK packet.
|
||||
```
|
||||
|
||||
### PKESK v6
|
||||
|
||||
The decryption of version 6 PKESK packets works quite similarly to version 3.
|
||||
|
||||
```{figure} plain_svg/PKESKv6-decryption.svg
|
||||
:name: fig-decryption-pkesk6
|
||||
:alt: Depicts, how the secret-key component of the users encryption subkey is directly used to decrypt the encrypted session key.
|
||||
|
||||
Decrypting the session key from a version 6 PKESK packet.
|
||||
```
|
||||
|
||||
Contrary to the version 3 PKESK, the encrypted session key within the version 6 PKESK does not contain the symmetric cipher algorithm used to decrypt the SEIPD packet.
|
||||
Instead, this cipher algorithm ID is encoded inside the SEIPD v2 packet directly.
|
||||
|
||||
## SEIPD (v1)
|
||||
|
||||
Version 1 SEIPD packets MUST only be used with version 3 PKESK packets and/or version 4 SKESK packets.
|
||||
Any other combinations are not allowed and MUST result in a broken message.
|
||||
|
||||
```{note}
|
||||
Since SEIPD version 1 is susceptible to downgrade attacks under certain scenarios, it is recommended to use SEIPD version 2 wherever possible.
|
||||
```
|
||||
|
||||
To decrypt the contents of a version 1 SEIPD packet, the session key obtained in the previous step is used.
|
||||
The cipher algorithm is either extracted from the decrypted session key (the algorithm ID is typically prefixed to the decrypted session key), or - in case of a SKESK packet using the direct-method - taken from the SKESKs cipher algorithm field.
|
||||
|
||||
Once the cipher is initialized, the whole encrypted data from the SEIPD packet is decrypted.
|
||||
|
||||
### Verifying the quick-check bytes
|
||||
|
||||
To quickly verify that the correct session-key was used during decryption, bytes with index 14 and 15 are compared to those with index 16 and 17 (zero-indexed).
|
||||
A mismatch of those pairs of bytes indicates that the wrong session-key was used and decryption is aborted.
|
||||
|
||||
### Verifying the modification detection code (mdc)
|
||||
|
||||
The contents of a SEIPDv1 packet are protected against unnoticed modification via the addition of a modification detection code.
|
||||
This is done by calculating the SHA1 checksum of the entire decrypted plaintext, but excluding the last 20 bytes, which are the actual checksum computed by the sender.
|
||||
Compare figure {numref}`fig-encryption-mdc`.
|
||||
|
||||
The result is then compared to those last 20 bytes to detect modifications of the ciphertext.
|
||||
|
||||
```{figure} plain_svg/SEIPDv1-decryption.svg
|
||||
:name: fig-decryption-seipd1
|
||||
:alt: Depicts how the session key is used directly to decrypt the contents of the SEIPD packet.
|
||||
|
||||
The contents of the SEIPD packet are decrypted using the session key as message key.
|
||||
```
|
||||
|
||||
## SEIPD w/ AEAD (v2)
|
||||
|
||||
Preferred mode.
|
||||
Version 2 SEIPD packets MUST only be used with version 6 PKESK packets and/or version 6 SKESK packets.
|
||||
Any other combinations are not allowed and MUST result in a broken message.
|
||||
|
||||
Once the session key was obtained from a PKESK or SKESK, it is used to derive a *message key* and an IV. This is done by passing the session key through a salted HKDF function, where the salt is unique per message and obtained from the SEIPD packet.
|
||||
|
||||
The result is split into the message key and first half of the IV.
|
||||
|
||||
```{figure} plain_svg/SEIPDv2-decryption-mk-derivation.svg
|
||||
:name: fig-decryption-seipd2-mk-derivation
|
||||
:alt: Depicts how the session key is fed into a salted HKDF to derive both the message key and the first half of an IV.
|
||||
|
||||
In a first step, a message key and half of an IV is derived from the session key.
|
||||
```
|
||||
|
||||
Then, the contents of the SEIPDs encrypted data are split into chunks, which are processed sequentially. Each chunk is decrypted using AEAD with parameters from the SEIPD packet as *additional data*.
|
||||
For each chunk, the chunk index starting at `0` is passed into the function as second half of the IV.
|
||||
|
||||
All decrypted plaintext blocks are appended to form the result of the decryption process.
|
||||
|
||||
After all blocks have been processed, in a final AEAD step, the total number of plaintext octets gets appended to the *additional data* and the final AEAD auth tag from the SEIPD packet is processed.
|
||||
|
||||
```{figure} plain_svg/SEIPDv2-decryption-chunks.svg
|
||||
:name: fig-decryption-seipd2-chunks
|
||||
:alt: Depicts, how the message key and index-postfixed IV are used to decrypt each individual chunk of plaintext.
|
||||
|
||||
Each chunk is decrypted using AEAD using the message key and an IV with appended chunk index.
|
||||
```
|
||||
|
||||
## SED
|
||||
|
||||
Legacy mode: may be decrypted, but not produced.
|
161
book/source/encryption.md
Normal file
|
@ -0,0 +1,161 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Encryption
|
||||
|
||||
[Encryption](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-confidentiality-via-encrypt) is one of the core facilities of OpenPGP. It provides confidentiality.
|
||||
|
||||
For an in-depth, packet-level view of encrypted data in OpenPGP, see [](/zoom/encryption).
|
||||
|
||||
## Terminology
|
||||
|
||||
| Term | Description |
|
||||
|--------------|------------------------------------------------------------------------------------------------------------|
|
||||
| SEIPD Packet | *Symmetrically Encrypted, Integrity Protected Data* packet; contains the encrypted message payload |
|
||||
| SKESK Packet | *Symmetric-Key Encrypted Session Key* packet; contains or provides a passphrase-encrypted session key |
|
||||
| PKESK Packet | *Public-Key Encrypted Session Key* packet; contains a session key encrypted using an asymmetric public key |
|
||||
| Session Key | Symmetric encryption key, which is either used directly as - or to derive - the message key |
|
||||
| Message Key | Symmetric encryption key used to encrypt the contents of the SEIPD packet |
|
||||
|
||||
## High-Level overview of the message encryption process
|
||||
|
||||
Encryption in OpenPGP is performed in two distinct steps:
|
||||
|
||||
1. **Symmetric encryption**: The plaintext is encrypted based on a (secret) symmetric key, the [*session key*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-confidentiality-via-encrypt). The (potentially large) ciphertext only needs to be stored once, even if it is sent to multiple recipients. All recipients get access to the same shared *session key* to decrypt the message.
|
||||
2. **Session key transmission**: For each recipient of the message, a packet that contains a protected copy of the session key is generated.
|
||||
- Usually, the *session key* is encrypted to a public encryption component key of the recipient.
|
||||
- Alternatively - or additionally - the *session key* may also be encrypted using a passphrase. This is a specialized and less commonly used mode of operation that doesn't require OpenPGP certificates.
|
||||
|
||||
```{note}
|
||||
Above, "plaintext" means one of:
|
||||
- *Literal Data* packet,
|
||||
- *Compressed Data* packet or a
|
||||
- *signed message*.
|
||||
|
||||
A *signed message*, in turn, is a packet sequence that either
|
||||
- resembles an *inline-signed message* (a *Literal Data* packet sandwhiched between one or more *One-Pass-Signature* and their respective *Signature* packets), or a
|
||||
- *prefixed-signed* message (one or more *Signature* packets followed by a single *Literal Data* packet).
|
||||
```
|
||||
|
||||
## Encryption mechanism versions
|
||||
|
||||
OpenPGP's encryption mechanisms have evolved over time. The RFC shows an [overview of encryption mechanisms](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#section-10.3.2.1), and how they may be combined.
|
||||
|
||||
Two generations of encryption mechanisms are currently relevant in OpenPGP, and will co-exist for the foreseeable future.
|
||||
|
||||
The main difference between these lies in the symmetric part of the encryption mechanism, represented by versions 1 and 2 of the *Symmetrically Encrypted and Integrity Protected Data* packets (abbreviated as "SEIPD"). The two versions use different mechanisms to provide non-malleability. More on these below.
|
||||
|
||||
Older, legacy encryption mechanisms exist in OpenPGP. However, those must not be used for encryption anymore. Messages encrypted using these legacy mechanisms may still be decrypted, although with caution. For more information, see the [decryption](/decryption) chapter.
|
||||
|
||||
SEIPD packets are used in combination with two mechanisms that store *session keys*:
|
||||
|
||||
- [Public-Key Encrypted Session Key](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-public-key-encrypted-sessio) (PKESK) packets and
|
||||
- [Symmetric-Key Encrypted Session Key](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#skesk) (SKESK) packets.
|
||||
|
||||
The typical combination of mechanisms for encryption in OpenPGP is a [hybrid cryptosystem](hybrid-cryptosystems), consisting of one or more [Public-Key Encrypted Session Key](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-public-key-encrypted-sessio) packets (PKESK), followed by a [Symmetrically Encrypted Integrity Protected Data](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-symmetrically-encrypted-int) (SEIPD) packet.
|
||||
|
||||
In this combination, an asymmetric cryptographic mechanism is used to protect a *session key* inside PKESK packets. The *session key*, in turn, is used to protect the plaintext using symmetric-key encryption in a SEIPD packet.
|
||||
|
||||
## Encrypted session keys: PKESK, SKESK
|
||||
|
||||
Encrypted session key (ESK) packets are a family of two mechanisms for securing symmetric key material:
|
||||
|
||||
- [PKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-public-key-encrypted-sessio): Uses asymmetric OpenPGP key material to protect a session key, and
|
||||
- [SKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-symmetric-key-encrypted-ses): Uses passphrases to protect the symmetric key material, instead of OpenPGP asymmetric key material (this is less commonly used).
|
||||
|
||||
An arbitrary number of PKESKs and SKESKs can be used in the same message. It is also possible to mix the two, resulting in a message which can be decrypted using either one of the designated OpenPGP keys or any of the passphrases used to encrypt the message. This is useful to make a message available to a number of known recipients, with the option to provide the passphrase to future recipients.
|
||||
|
||||
### PKESK: Session key encrypted to an asymmetric OpenPGP key
|
||||
|
||||
To encrypt an OpenPGP message for a recipient, the session key is encrypted to the recipient's public key. The resulting encrypted session key is packed into a PKESK packet, which holds essential metadata, like an identifier of the recipients encryption (sub)-key.
|
||||
|
||||
This procedure is repeated for each recipient of the message, and all resulting PKESK packets are prepended to the SEIPD packet (see below) containing the actual message.
|
||||
|
||||
Typically, the sender would also include themselves as a recipient, to be able to decrypt the message with their own key at a later point in time.
|
||||
|
||||
### SKESK: Session key encrypted to a passphrase
|
||||
|
||||
As an alternative (or augmentation) to PKESK packets, a message can also be encrypted to a symmetric passphrase. This is done using a SKESK packet, which uses an S2K mechanism to derive a symmetric key from a passphrase. This key is either used directly as the session key, or more commonly, used as a key-encapsulation-key (KEK) to encrypt the session key.
|
||||
|
||||
Also see https://flowcrypt.com/docs/guide/send-and-receive/send-password-protected-emails.html
|
||||
|
||||
As for protection of secret key material, it is important to choose appropriate S2K parameters when generating an SKESK packet.
|
||||
The specification currently recommends to use either *Iterated and Salted S2K* or *Argon2*.
|
||||
|
||||
## Symmetric encryption of data, SEIPD
|
||||
|
||||
*Symmetrically Encrypted Integrity Protected Data* (SEIPD) packets represent the symmetric aspect of OpenPGP's encryption mechanism. The function of these packets is entirely independent of (asymmetric) OpenPGP keys.
|
||||
|
||||
A SEIPD packet contains the actual payload: the ciphertext of the encrypted message. For a large encrypted message, the SEIPD packet will also be large.
|
||||
|
||||
```{note}
|
||||
SEIPD packets are the successor to the [Symmetrically Encrypted Data](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-symmetrically-encrypted-dat) packet, which is obsolete.
|
||||
```
|
||||
|
||||
Two versions of the SEIPD packet (differentiated by the version number) have been specified. Version 1, introduced in RFC4880, is used in OpenPGP v4 while SEIPD version 2 was introduced with OpenPGP v6. Both versions can be used with either OpenPGP v4 or v6 keys, although OpenPGP v4 keys need to announce support for SEIPD version 2 via the *Feature* signature subpacket.
|
||||
|
||||
When decrypted, the data contained in a SEIPD packet forms an [OpenPGP message](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-openpgp-messages). That is, the decrypted data consists of a series of OpenPGP packets.
|
||||
|
||||
In both versions of SEIPD, the decryptor must have obtained a *session key* in a previous step, before processing the SEIPD packet. Using this session key, the decryptor can decrypt the SEIPD packet and process the plaintext data that it contains.
|
||||
|
||||
### v1 SEIPD, based on MDC
|
||||
|
||||
The [version 1 SEIPD](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#version-one-seipd) mechanism is supported by all modern OpenPGP version 4 implementations. It was introduced in [RFC 4880](https://www.rfc-editor.org/rfc/rfc4880.html#section-5.13) as a replacement for the *SED* (Symmetrically Encrypted Data) packet. SEIPDv1 provides integrity protection of the ciphertext using a SHA-1 checksum of the plaintext as modification detection code.
|
||||
|
||||
Version 1 SEIPD can only be combined with [version 3 PKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#v3-pkesk) and/or [version 4 SKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#v4-skesk) packets.
|
||||
|
||||
In this version of the SEIPD packet, the session key is used directly as message key, meaning the payload is encrypted symmetrically using the session key.
|
||||
|
||||
When communicating with a mix of recipients, some of whose OpenPGP software only supports OpenPGP version 4, then this mechanism must be used.
|
||||
|
||||
```{figure} plain_svg/SEIPDv1-PKESK.svg
|
||||
:name: fig-encryption-seipdv1-pkesk
|
||||
:alt: Depicts a dotted hexagon labeled "Plaintext", from which a curved arrow passes another dotted hexagon "Session Key" and finally points to a "SEIPDv1" packet. Two more curved arrows originate from the session key and pass Alice' and Bob's encryption key, ending in two PKESK packets.
|
||||
|
||||
With SEIPDv1, the session key is directly used as message key to encrypt the payload
|
||||
```
|
||||
|
||||
(quick-check-and-mdc)=
|
||||
#### Preparing the plaintext with quick check and modification detection code
|
||||
|
||||
Before encrypting the plaintext, the data is modified by adding both a prepended "quick check", as well as an appended modification detection code.
|
||||
|
||||
The quick check comprises of 16 randomly chosen bytes plus 2 bytes which are the last two of the 16 random bytes repeated.
|
||||
This mechanism is useful to quickly check, whether the correct session key was used when decrypting the message.
|
||||
These quick-check bytes are prepended to the plaintext.
|
||||
|
||||
The modification detection code on the other hand is added to allow detection of unwanted modification of the ciphertext.
|
||||
First, the two marker bytes `0xD3` and `0x14` are appended to the plaintext. Then, the SHA1 checksum of the entire plaintext including quick check and marker bytes is calculated and appended to the plaintext.
|
||||
|
||||
```{figure} plain_svg/mdc.svg
|
||||
:name: fig-encryption-mdc
|
||||
:alt: Depicts, how the prior to encryption, the plaintext bytes are prepended with 18 quick check bytes and appended with 22 bytes of modification detection code. The quick check comprises of 16 random bytes plus 2 repeated bytes. The modification detection code starts with the marker bytes 0xD314, followed by the SHA1 checksum of the entire plaintext including quick check and marker bytes.
|
||||
|
||||
The plaintext inside of a SEIPDv1 packet contains quick check bytes, the actual plaintext and modification detection code
|
||||
```
|
||||
|
||||
Lastly, the whole prepared plaintext is encrypted symmetrically.
|
||||
|
||||
(seipd-v2)=
|
||||
### v2 SEIPD, based on AEAD
|
||||
|
||||
The [version 2 SEIPD](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#version-two-seipd) mechanism was introduced in OpenPGP version 6. Consequently, it can only be used for encryption when all recipients explicitly announce support for it using a *Feature* signature subpacket.
|
||||
It provides integrity protection of the ciphertext using *AEAD* (authenticated encryption with additional data).
|
||||
v2 SEIPD can only be combined with either [version 6 PKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#v6-pkesk) and/or [version 6 SKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#v6-skesk) packets.
|
||||
|
||||
In version 2 SEIPD, the *session key* is transformed into a *message key*, based on a per-message salt value stored separately in the v2 SEIPD packet. The message key is then used in an AEAD scheme to encrypt the message payload.
|
||||
|
||||
```{note}
|
||||
The session key can use a different symmetric algorithm than the message key.
|
||||
```
|
||||
|
||||
```{figure} plain_svg/SEIPDv2-PKESK.svg
|
||||
:name: fig-encryption-seipdv2-pkesk
|
||||
:alt: TODO
|
||||
|
||||
With SEIPDv2, the message key is derived from the session key in an extra step.
|
||||
```
|
||||
|
||||
This additional step introduces key-separation into the protocol, which protects against certain attacks, such as an [OpenPGP SEIP downgrade attack](https://www.metzdowd.com/pipermail/cryptography/2015-October/026685.html).
|
9
book/source/examples/ascii_armored_encrypted_message.asc
Normal file
|
@ -0,0 +1,9 @@
|
|||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
wV0GIQbApYOEpDjloU9zcSQmpNRduu70o55rMLCdVRP5eKzKlBkxYaQzuusD78oj
|
||||
AdiGwQ8MI8sSAXAV4AEMKIcbINqhIBgSm5EV9h+Yl/XV3fEZ1JOaBnrtso2ZAS7S
|
||||
cgIHAQaWc/Ip4Thq0EZDZwlpRUk/TUL+TWEpsGdQs8ifDyFAk7t3+3XvvLr5dUg3
|
||||
+Ot+sESkCSjhrZk50HIjwjBVZ6Y159yfaOqttMT6cXqWaxIishPaJ+OR1q2bZS1N
|
||||
2jFbaROOcbASK6AVzqCWneqkIA==
|
||||
=WFpq
|
||||
-----END PGP MESSAGE-----
|
691
book/source/glossary.md
Normal file
|
@ -0,0 +1,691 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Glossary
|
||||
|
||||
```{glossary}
|
||||
:sorted:
|
||||
|
||||
AEAD
|
||||
See {term}`Authenticated Encryption With Associated Data`.
|
||||
|
||||
AEAD Algorithm
|
||||
See {term}`Authenticated Encryption With Associated Data`.
|
||||
|
||||
Algorithm Preferences
|
||||
The preferences for {term}`hash algorithms<Hash Function>`, {term}`compression algorithms<Data Compression>`, {term}`symmetric algorithms<Symmetric Cryptography>` and {term}`AEAD algorithms<Authenticated Encryption With Associated Data>` are set using {term}`direct key signatures<Direct Key Signature>` or {term}`primary User ID` {term}`binding signatures<Binding Signature>`.
|
||||
|
||||
See [](recipe-algorithm-preferences).
|
||||
|
||||
Asymmetric Cryptography
|
||||
Asymmetric cryptography is used in OpenPGP. For a more detailed discussion see [](public-key-cryptography).
|
||||
|
||||
Authenticated Encryption With Associated Data
|
||||
Short AEAD, refers to an encryption scheme that ensures confidentiality of a message. Additionally, additional data, which is not confidential, may be associated with the message.
|
||||
|
||||
See Wikipedia on [Authenticated Encryption](https://en.wikipedia.org/wiki/Authenticated_encryption).
|
||||
|
||||
Authentication
|
||||
The process of {term}`validiting<Validation>` an {term}`identity claim`.
|
||||
The term "authentication" here is semantically different from the one used in {term}`Authentication Key Flag`.
|
||||
|
||||
Authentication Key Flag
|
||||
A {term}`Key Flag`, which indicates that a {term}`Component Key` can be used to confirm control over {term}`private key material` against a remote system. The term "authentication" here is semantically different from {term}`Authentication`. See [](key-flags).
|
||||
|
||||
Authentication Tag
|
||||
See {term}`Message Authentication Code`.
|
||||
|
||||
Authenticity
|
||||
See {term}`Authentication`.
|
||||
|
||||
Back Signature
|
||||
See {term}`Primary Key Binding Signature`.
|
||||
|
||||
Binary Signature
|
||||
A {term}`Data Signature` with the {term}`Signature Type ID` `0x00`, which is used for binary data.
|
||||
|
||||
Binding
|
||||
The process of creating a {term}`Binding Signature` for a {term}`Component`, or the resulting {term}`Binding Signature`.
|
||||
|
||||
See {ref}`binding-signatures` for more.
|
||||
|
||||
Binding Signature
|
||||
A {term}`self-signature` on a {term}`component` which associates that {term}`component` to the issuing {term}`component key` in a {term}`certificate<OpenPGP Certificate>`.
|
||||
|
||||
See {ref}`binding-signatures` for more.
|
||||
|
||||
CA
|
||||
See {term}`Certification Authority`.
|
||||
|
||||
Capability
|
||||
The operations an {term}`OpenPGP Component Key` can perform. See [](key-flags).
|
||||
|
||||
Certificate
|
||||
See {term}`OpenPGP Certificate`
|
||||
|
||||
Certificate Authority
|
||||
See {term}`Certification Authority`
|
||||
|
||||
Certificate Holder
|
||||
A person or other entity, that holds an {term}`Transferable Secret Key` and thus is able to modify the accompanying {term}`OpenPGP Certificate`.
|
||||
|
||||
Certification
|
||||
A certification, in OpenPGP, is a signature that makes a statement about an {term}`identity` in a {term}`certificate<OpenPGP Certificate>`, or an entire {term}`certificate<OpenPGP Certificate>`.
|
||||
|
||||
Most commonly, the term is applied to "[third-party certifications](third-party-certifications)," in which an external actor indicates that they have {term}`validated<Validation>` the link between an {term}`identity` and a {term}`certificate<OpenPGP Certificate>`. However, the term is also used for [self-signatures that bind identity components](bind-identity) to a {term}`certificate<OpenPGP Certificate>`.
|
||||
|
||||
Certification Authority
|
||||
Also known as [Certificate authority](https://en.wikipedia.org/wiki/Certificate_authority), this is an entity that handles digital certificates, especially by signing or issuing them.
|
||||
|
||||
Certification Key Flag
|
||||
A {term}`Key Flag`, indicating that a {term}`Component Key` can be used for issuing third-party {term}`certifications<Certification>`. See [](key-flags).
|
||||
|
||||
Certification Revocation Signature Packet
|
||||
An {term}`OpenPGP Signature Packet` to {term}`revoke<Revocation>` an earlier {term}`self-certification` of a {term}`User ID`.
|
||||
|
||||
[RFC 5.2.1.13](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-certification-revocation-si)
|
||||
|
||||
Certification Signature
|
||||
See {term}`Certification`.
|
||||
|
||||
Certifying Self-signature
|
||||
An {term}`OpenPGP Signature Packet` by the {term}`Certificate Holder` on an {term}`Identity Component` of their own {term}`Certificate`.
|
||||
|
||||
Certifying Signature
|
||||
See {term}`Certification`.
|
||||
|
||||
Cipher Type Byte
|
||||
This historical term was defined in [RFC 1991](https://datatracker.ietf.org/doc/html/rfc1991#section-4.1) and was subsequently superseded by {term}`Packet Tag` in [RFC 2440](https://datatracker.ietf.org/doc/html/rfc2440#section-4.2), which is in turn superseded by {term}`Packet Type ID` in the new [RFC](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-packet-headers).
|
||||
|
||||
Cleartext Signature
|
||||
A {term}`Data Signature` which exists in a combined text format, encapsulating the (readable) text input it was created for. See [](cleartext-signature).
|
||||
|
||||
Cleartext Signature Framework
|
||||
A framework for creating {term}`cleartext signatures<Cleartext Signature>`.
|
||||
See [RFC 7](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#cleartext-signature).
|
||||
|
||||
Component
|
||||
An element in an {term}`OpenPGP Certificate`, that represents a {term}`component key` or {term}`identity component`.
|
||||
|
||||
Component Key
|
||||
See {term}`OpenPGP Component Key`.
|
||||
|
||||
Compression
|
||||
See {term}`Data Compression`.
|
||||
|
||||
Creation Time
|
||||
The point in time at which e.g. an {term}`OpenPGP Certificate`, or one of its {term}`component<Component>` is created.
|
||||
|
||||
Creator
|
||||
See {term}`Issuer`.
|
||||
|
||||
Criticality Flag
|
||||
A flag on {term}`Subpacket`s, that defines their criticality, which is used for validation. See [](criticality-of-subpackets).
|
||||
|
||||
Cryptographic Key
|
||||
A {term}`symmetric<Symmetric Cryptography>` or {term}`asymmetric<Asymmetric Cryptography>` cryptographic key is used for signing and encryption operations. See [](cryptography).
|
||||
|
||||
Cryptographic Signature
|
||||
A raw cryptographic signature is a sequence of bytes created by a {term}`Cryptographic Key`.
|
||||
|
||||
CTB
|
||||
See {term}`Cipher Type Byte`.
|
||||
|
||||
Data Compression
|
||||
The process of encoding information using fewer bits than the original representation.
|
||||
In OpenPGP data compression is used to reduce the size required for encrypted messages.
|
||||
|
||||
See Wikipedia on [Data Compression](https://en.wikipedia.org/wiki/Data_compression).
|
||||
|
||||
Data Signature
|
||||
{term}`Cryptographic signature` over binary documents or canonical text documents. See [](/signing_data).
|
||||
|
||||
Data Signature Packet
|
||||
An {term}`OpenPGP Signature Packet` which describes a {term}`Data Signature`. See [](/signing_data).
|
||||
|
||||
Delegation
|
||||
OpenPGP users can [delegate authentication decisions](delegation) to third parties, and thus rely on {term}`certifications<Certification>` they issue. The remote party is then called a "{term}`trusted introducer`".
|
||||
|
||||
This kind of delegation involves {term}`certifications<Certification>` that include the {term}`trust signature` subpacket.
|
||||
|
||||
Detached Signature
|
||||
A {term}`Data Signature` which exists as a separate file to the file it was created for. See [](forms-of-data-signatures).
|
||||
|
||||
Direct Key Signature
|
||||
A {term}`Signature` that sets preferences and advertises {term}`features<Features Subpacket>` applicable to an entire {term}`Certificate`. See [](direct-key-signature).
|
||||
|
||||
Embedded Signature Subpacket
|
||||
An {term}`OpenPGP Signature Subpacket` which contains a complete {term}`OpenPGP Signature Packet`.
|
||||
|
||||
See [RFC 5.2.3.34](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-embedded-signature)
|
||||
|
||||
Encryption Key Flag
|
||||
A {term}`Key Flag`, indicating that a {term}`Component Key` can be used for encrypting data. See [](key-flags).
|
||||
|
||||
Expiration
|
||||
A mechanism by which a {term}`Component` is invalidated due to the {term}`Expiration Time` of its {term}`binding signature` being older than the {term}`Reference Time` by which it is validated.
|
||||
|
||||
Expiration Time
|
||||
The time of expiry of an {term}`OpenPGP Signature Packet`.
|
||||
|
||||
Features Subpacket
|
||||
A {term}`OpenPGP Signature Subpacket`, which denotes advanced OpenPGP features an {term}`implementation<OpenPGP Implementation>` supports.
|
||||
|
||||
For an in-depth view on these {term}`subpackets<OpenPGP Signature Subpacket>` see [](zoom-dks).
|
||||
|
||||
See [RFC 5.2.3.32](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-features)
|
||||
|
||||
Fingerprint
|
||||
See {term}`OpenPGP Fingerprint`.
|
||||
|
||||
Hard Revocation
|
||||
A {term}`Revocation Signature Packet` for a {term}`Certification` or a {term}`Component Key`, which either includes a {term}`Reason For Revocation Subpacket` with a {term}`Revocation Code`, that signifies the target being compromised (e.g., `0` or `2`), or has no {term}`Reason For Revocation Subpacket` at all.
|
||||
|
||||
See [](hard-vs-soft-revocations).
|
||||
|
||||
See [RFC 5.2.3.31](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-reason-for-revocation).
|
||||
|
||||
Hash Algorithm
|
||||
See {term}`Hash Function`.
|
||||
|
||||
Hash Digest
|
||||
Output of a cryptographic hash function for a string of data of any length. See [](cryptographic-hash).
|
||||
|
||||
Hash Function
|
||||
A function used to map data of arbitrary size to fixed-size values (see {term}`Hash Digest`).
|
||||
|
||||
Hashed Area
|
||||
An area in an {term}`OpenPGP Signature Packet` containing {term}`OpenPGP Signature Subpacket`s, that is covered by the {term}`Hash Digest` a {term}`Cryptographic Signature` is created for. See [](subpacket-areas).
|
||||
|
||||
Hashed Subpacket
|
||||
An {term}`OpenPGP Signature Subpacket` residing in the {term}`Hashed Area` of an {term}`OpenPGP Signature Packet`.
|
||||
|
||||
Hybrid Cryptosystem
|
||||
A cryptographic system that employs both {term}`Asymmetric Cryptography` and {term}`Symmetric Cryptography`. See [](hybrid-cryptosystems).
|
||||
|
||||
Identity
|
||||
An identity of a {term}`Certificate Holder`. It is represented by an {term}`Identity Component`, which may be certified using {term}`third-party identity certifications<Third-party Identity Certification>`, or by a {term}`Notation`.
|
||||
|
||||
Identity Certification
|
||||
An {term}`OpenPGP Signature Packet` on an {term}`Identity Component` which {term}`certifies<Certification>` its {term}`authenticity<Authentication>`.
|
||||
|
||||
Identity Claim
|
||||
A {term}`Certificate Holder` may use {term}`Identity Components<Identity Component>` or {term}`Notations<Notation>` to state a claim about their {term}`Identity`.
|
||||
|
||||
Identity Component
|
||||
Part of an {term}`OpenPGP Certificate`, that is used to associate data about the {term}`Certificate Holder` with it. See [](identity-components) for further details.
|
||||
|
||||
Identity Verification
|
||||
A process by which the {term}`Identity Claim` of a {term}`Certificate Holder` is verified. See also {term}`Signature Verification`.
|
||||
|
||||
Initial Introducer
|
||||
An {term}`OpenPGP Certificate` explicitly {term}`delegated<Delegation>` to from a {term}`Trust Anchor`.
|
||||
|
||||
Inline Signature
|
||||
A {term}`Data Signature` which exists encapsulated alongside the data it was created for in an OpenPGP container. See [](forms-of-data-signatures).
|
||||
|
||||
Issuer
|
||||
An entity, that created an {term}`OpenPGP Signature Packet` using an {term}`Transferable Secret Key`.
|
||||
|
||||
Issuer Fingerprint Subpacket
|
||||
A {term}`Subpacket` specifying the {term}`Fingerprint` of an {term}`Issuer Key`.
|
||||
|
||||
See [RFC 5.2.3.35](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-issuer-fingerprint)
|
||||
|
||||
Issuer Key
|
||||
The {term}`OpenPGP Component Key` of an {term}`Issuer`, used to create an {term}`OpenPGP Signature Packet`.
|
||||
|
||||
Key
|
||||
In OpenPGP, and cryptography more generally, the term "key" holds different meanings.
|
||||
|
||||
First, it can apply to different [cryptographic primitives](/cryptography):
|
||||
|
||||
- asymmetric public key
|
||||
- asymmetric private key
|
||||
- {term}`Symmetric Secret Key`
|
||||
|
||||
Additionally, in OpenPGP, asymmetric cryptographic keys are used on [three different layers](layers-of-keys-in-openpgp) of abstraction:
|
||||
|
||||
- cryptographic key
|
||||
- OpenPGP component key
|
||||
- {term}`OpenPGP key` (which in turn refers to either an {term}`OpenPGP Certificate` or a {term}`Transferable Secret Key`
|
||||
|
||||
Key Expiration Time Subpacket
|
||||
An {term}`OpenPGP Signature Subpacket Type` which defines the {term}`Expiration Time` for an {term}`OpenPGP Signature Packet` on a {term}`key<Component Key>`.
|
||||
|
||||
See [RFC 5.2.3.13](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-key-expiration-time)
|
||||
|
||||
Key Flag
|
||||
A preference encoded in an {term}`OpenPGP Signature Subpacket`, that defines the {term}`Capability` a {term}`OpenPGP Component Key` has. See [](signature-subpackets).
|
||||
|
||||
Key Holder
|
||||
See {term}`Certificate Holder`.
|
||||
|
||||
Key ID
|
||||
The high-order (leftmost) 64 bits of an {term}`OpenPGP Fingerprint`.
|
||||
Historically, this term refers to the low-order (rightmost) 64 bits of an {term}`OpenPGP Fingerprint`.
|
||||
|
||||
Key Material
|
||||
May refer to {term}`Public Key Material` or {term}`Private Key Material`.
|
||||
|
||||
Key Owner
|
||||
See {term}`Certificate Holder`.
|
||||
|
||||
Key Revocation Signature Packet
|
||||
A {term}`Revocation Self-signature` for an entire {term}`OpenPGP Certificate`.
|
||||
|
||||
Key Server
|
||||
A piece of software available over the network, which provides access to {term}`OpenPGP Certificates<OpenPGP Certificate>` e.g., by searching for an {term}`OpenPGP Fingerprint` or {term}`User ID`, via the `HKP` and/ or `HKPS` protocols.
|
||||
Several implementations such as [hagrid](https://gitlab.com/keys.openpgp.org/hagrid/), or [hockeypuck](https://github.com/hockeypuck/hockeypuck) exist.
|
||||
|
||||
Life-cycle Management
|
||||
In OpenPGP several actions are necessary for the prolonged use of an {term}`OpenPGP Certificate` or adapting its {term}`components<Component>` to the requirements of the {term}`Certificate Holder`.
|
||||
These are for example changes to {term}`binding signatures<Binding Signature>` (adding or {term}`revocation` of {term}`component keys<Component Key>` or {term}`direct key signature<Direct Key Signature>`), modification of {term}`expiration time` or other {term}`metadata` for {term}`components<Component>`.
|
||||
See [](self-signatures).
|
||||
|
||||
Literal Data Packet
|
||||
A {term}`packet<OpenPGP Signature Packet>` in a {term}`Data Signature` which contains data, that has been signed using a {term}`cryptographic signature`. See [RFC 5.9](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#lit) for more details.
|
||||
|
||||
MAC
|
||||
See {term}`Message Authentication Code`.
|
||||
|
||||
Master Key
|
||||
See {term}`OpenPGP Primary Key`.
|
||||
|
||||
Message Authentication Code
|
||||
A piece of information used for integrity and {term}`authenticity<Authentication>` verification of a message. See [](message-authentication-code).
|
||||
|
||||
Meta-Introducer
|
||||
An {term}`OpenPGP Certificate` with a {term}`Trust Depth` greater than one.
|
||||
|
||||
Metadata
|
||||
Data related to preferences of an {term}`OpenPGP Certificate` or its {term}`Certificate Holder`, that can be found in {term}`signature` {term}`packets<Packet>`. See [](metadata-in-certificates).
|
||||
|
||||
Notation
|
||||
A mechanism for a {term}`Certificate Holder` to provide user-defined data using a {term}`Notation Signature Subpacket`.
|
||||
|
||||
Notation Signature Subpacket
|
||||
An {term}`OpenPGP Signature Subpacket` which is used to add user-defined data to a {term}`Certificate`. See [](notation-signature-subpackets).
|
||||
|
||||
Notation Tag
|
||||
Part of a {term}`Notation` name.
|
||||
|
||||
One-pass Signature Packet
|
||||
One or more {term}`packets<OpenPGP Signature Packet>` before the actual data in a {term}`Data Signature` which contain information to allow a receiving {term}`implementation<OpenPGP Implementation>` to create {term}`hashes<Hash Digest>` required for signature verification. See [RFC 5.4](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#one-pass-sig) for more details.
|
||||
|
||||
OpenPGP Certificate
|
||||
An OpenPGP certificate contains public key material, identity claims and third party certifications (but no private key material)
|
||||
|
||||
OpenPGP Component Key
|
||||
An {term}`OpenPGP Primary Key` or {term}`OpenPGP Subkey`. For an in-depth discussion see [](component-keys).
|
||||
|
||||
OpenPGP Fingerprint
|
||||
An OpenPGP Fingerprint is a shorthand representation of an {term}`OpenPGP Component Key`. Fingerprints effectively act as unique identifiers. See [](fingerprint).
|
||||
|
||||
The Fingerprint of the {term}`primary component key<OpenPGP Primary Key>` is used as an identifier for the full {term}`OpenPGP Certificate`.
|
||||
|
||||
OpenPGP Implementation
|
||||
A piece of software implementing the OpenPGP protocol (to some extend).
|
||||
|
||||
OpenPGP Key
|
||||
Used either for an {term}`OpenPGP Certificate` (containing public key material and metadata), or for an {term}`OpenPGP Private Key`. See [](/certificates) for an in-depth discussion.
|
||||
|
||||
OpenPGP Message
|
||||
A data structure, which contains OpenPGP components such as {term}`OpenPGP Certificate` or {term}`OpenPGP Signature Packet` and plaintext or encrypted data.
|
||||
|
||||
OpenPGP Public Key
|
||||
See {term}`OpenPGP Certificate`.
|
||||
|
||||
OpenPGP Private Key
|
||||
See {term}`Transferable Secret Key`.
|
||||
|
||||
OpenPGP Primary Key
|
||||
An {term}`OpenPGP Component Key` that is used in the primary key role of an {term}`OpenPGP Certificate`. For a more detailed discussion, see [](primary-key).
|
||||
|
||||
OpenPGP Signature
|
||||
See {term}`OpenPGP Signature Packet`.
|
||||
|
||||
OpenPGP Signature Packet
|
||||
A {term}`packet` that contains a raw {term}`cryptographic signature`, a {term}`Signature Type ID` and additional {term}`metadata`. See [](/signatures). Basic concepts are introduced in [](/signatures) and more detailed use-cases are explained in [](/signing_data) and [](/signing_components).
|
||||
|
||||
OpenPGP Signature Subpacket
|
||||
A data structure in a {term}`Signature Packet`, that describes {term}`metadata` and preferences. See [](signature-subpackets).
|
||||
|
||||
OpenPGP Signature Subpacket Type
|
||||
An {term}`OpenPGP Signature Subpacket` type.
|
||||
|
||||
OpenPGP Signature Type
|
||||
The type of an {term}`OpenPGP Signature Packet` is defined by its {term}`Signature Type ID`. See [](signature-types).
|
||||
|
||||
OpenPGP Signing Subkey
|
||||
An {term}`OpenPGP Subkey` with the {term}`Signing Key Flag`.
|
||||
|
||||
OpenPGP Subkey
|
||||
An {term}`OpenPGP Component Key` that is used in the subkey role, in an {term}`OpenPGP Certificate`. For a more detailed discussion, see [](subkeys).
|
||||
|
||||
Owner
|
||||
See {term}`Certificate Holder`.
|
||||
|
||||
Packet
|
||||
An element in an {term}`OpenPGP Certificate`, which represents {term}`components<Component>` or {term}`signatures<OpenPGP Signature Packet>`.
|
||||
|
||||
Packet Header
|
||||
A section of variable length at the beginning of a {term}`Packet`, which encodes for example the {term}`Packet Type ID`. See the relevant [section in the RFC](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-packet-headers), which explains this section in more detail.
|
||||
|
||||
Packet Tag
|
||||
This historical term was defined in [RFC 2440](https://datatracker.ietf.org/doc/html/rfc2440#section-4.2) and is superseded by {term}`Packet Type ID` in the new [RFC](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-packet-headers).
|
||||
|
||||
Packet Type ID
|
||||
A numerical value encoded in the first octet of a {term}`Packet Header`, defining a {term}`Packet`'s type.
|
||||
|
||||
Positive Certification
|
||||
An {term}`OpenPGP Signature Type` with the {term}`Signature Type ID` `0x13`, which is used in {term}`binding signatures<Binding Signature>` for {term}`User IDs<User ID>`. This {term}`OpenPGP Signature Type` implies that the {term}`issuer` has done substantial {term}`verification` of the {term}`Identity Claim`.
|
||||
|
||||
See [](bind-identity).
|
||||
|
||||
Preferred Compression Algorithms Subpacket
|
||||
An {term}`OpenPGP Signature Subpacket Type` which defines the preferred {term}`compression algorithms<Data Compression>` for an {term}`OpenPGP Signature Packet`. This defines which {term}`algorithms<Data Compression>` the {term}`key holder<Certificate Holder>` prefers to use.
|
||||
|
||||
See [RFC 5.2.3.17](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-preferred-compression-algor).
|
||||
|
||||
Preferred Hash Algorithms Subpacket
|
||||
An {term}`OpenPGP Signature Subpacket Type` which defines the preferred {term}`hash algorithm<Hash Function>` for an {term}`OpenPGP Signature Packet`. This defines which algorithms the {term}`key holder<Certificate Holder>` prefers to receive.
|
||||
|
||||
See [RFC 5.2.3.16](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-preferred-hash-algorithms).
|
||||
|
||||
Preferred Symmetric Ciphers for v1 SEIPD Subpacket
|
||||
An {term}`OpenPGP Signature Subpacket Type` which defines the preferred version 1 {term}`SEIPD<Symmetrically Encrypted Integrity Protected Data>` algorithms for an {term}`OpenPGP Signature Packet`. This defines which algorithms the {term}`key holder<Certificate Holder>` prefers to receive and implicitly signifies the supported algorithms of the {term}`key holder<Certificate Holder>`'s {term}`implementation<OpenPGP Implementation>`.
|
||||
|
||||
See [RFC 5.2.3.14](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-preferred-symmetric-ciphers).
|
||||
|
||||
Preferred AEAD Ciphersuites Subpacket
|
||||
An {term}`OpenPGP Signature Subpacket Type` which defines the preferred version 2 {term}`SEIPD<Symmetrically Encrypted Integrity Protected Data>` algorithms for an {term}`OpenPGP Signature Packet`. This defines which algorithms the {term}`key holder<Certificate Holder>` prefers to receive and implicitly signifies the supported algorithms of the {term}`key holder<Certificate Holder>`'s {term}`implementation<OpenPGP Implementation>`.
|
||||
|
||||
See [RFC 5.2.3.15](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-preferred-aead-ciphersuites)
|
||||
|
||||
|
||||
Primary Component Key
|
||||
See {term}`OpenPGP Primary Key`.
|
||||
|
||||
Primary Introducer
|
||||
See {term}`Initial Introducer`.
|
||||
|
||||
Primary Key
|
||||
See {term}`OpenPGP Primary Key`.
|
||||
|
||||
Primary Key Binding Signature
|
||||
A {term}`Binding Signature`, which is created by a {term}`OpenPGP Signing Subkey` on the {term}`OpenPGP Primary Key` of an {term}`OpenPGP Certificate` and stored in an {term}`Embedded Signature Subpacket` in the {term}`Binding Signature` for the {term}`OpenPGP Signing Subkey`.
|
||||
|
||||
This special case is explained in more detail in [](bind-signing-subkey).
|
||||
|
||||
Primary User ID
|
||||
A {term}`User ID` which carries the default preferences for {term}`identity components<Identity Component>` without preferences.
|
||||
|
||||
See [](primary-user-id).
|
||||
|
||||
Primary User ID Subpacket
|
||||
An {term}`OpenPGP Signature Subpacket` used in {term}`User ID self-signatures<User ID Binding Signature>` which allows to signify whether the {term}`User ID` in question is considered a {term}`Primary User ID`.
|
||||
|
||||
See [RFC 5.2.3.27](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#primary-user-id-subpacket)
|
||||
|
||||
Primary User ID Binding Signature
|
||||
A {term}`Binding Signature`, which is created by an {term}`OpenPGP Primary Key` to bind a {term}`User ID` to its {term}`OpenPGP Certificate` and marking it as the {term}`Primary User ID`.
|
||||
|
||||
This {term}`Binding Signature` may carry {term}`metadata` specific to the {term}`User ID` at hand as well as some applicable to the entire {term}`OpenPGP Certificate`.
|
||||
|
||||
See [](primary-user-id-binding).
|
||||
|
||||
Private Key
|
||||
See {term}`Transferable Secret Key`.
|
||||
|
||||
Private Key Material
|
||||
A raw cryptographic private key.
|
||||
|
||||
Public Key
|
||||
See {term}`OpenPGP Public Key`.
|
||||
|
||||
Public Key Algorithm
|
||||
An {term}`asymmetric cryptographic<Asymmetric Cryptography>` algorithm. See [](public-key-cryptography).
|
||||
|
||||
Public Key Cryptography
|
||||
See {term}`Asymmetric Cryptography`.
|
||||
|
||||
Public Key Material
|
||||
See {term}`OpenPGP Certificate`.
|
||||
|
||||
Reason For Revocation Subpacket
|
||||
An {term}`OpenPGP Signature Subpacket`, which is used in {term}`Certification Revocation Signature Packet` and {term}`key revocation signature packets<Key Revocation Signature Packet>` to describe a reason for the {term}`revocation`.
|
||||
|
||||
See [RFC 5.2.3.31](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-reason-for-revocation)
|
||||
|
||||
Reference Time
|
||||
A point in time at which an {term}`OpenPGP Certificate` is evaluated.
|
||||
|
||||
Regular Expression Subpacket
|
||||
An {term}`OpenPGP Signature Subpacket` which allows for limiting {term}`delegations<Delegation>` to {term}`identities<Identity>` matching a regular expression.
|
||||
|
||||
Revocation
|
||||
Mechanism to invalidate a {term}`component` or an entire {term}`OpenPGP Certificate` using a {term}`Revocation Self-signature`. See [](revocations).
|
||||
|
||||
Revocation Certificate
|
||||
A {term}`Revocation Self-signature` for an {term}`OpenPGP Primary Key` distributed alongside the plain {term}`OpenPGP Primary Key`.
|
||||
|
||||
See [RFC 10.1.2](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-openpgp-v6-revocation-certi)
|
||||
|
||||
Note that in [OpenPGP v4 this term is typically used](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#section-10.1.3-6) for a bare {term}`Revocation Self-signature` {term}`packet<OpenPGP Signature Packet>`.
|
||||
|
||||
Revocation Code
|
||||
A number in a {term}`Reason For Revocation Subpacket` which represents the reason for a {term}`Revocation`.
|
||||
|
||||
Revocation Self-signature
|
||||
A class of {term}`self-signatures<Self-signature>` to {term}`revoke<Revocation>` {term}`primary keys<OpenPGP Primary Key>`, {term}`User IDs<User ID>` or {term}`User Attributes<User Attribute>` and invalidate {term}`subkey binding signatures<Subkey Binding Signature>`.
|
||||
|
||||
See [](self-revocations).
|
||||
|
||||
Revocation Signature
|
||||
See {term}`Revocation Signature Packet`.
|
||||
|
||||
Revocation Signature Packet
|
||||
An {term}`OpenPGP Signature Packet` used for the {term}`revocation` of a {term}`certification` or {term}`binding`.
|
||||
|
||||
Revocation signatures are often {term}`self-signatures<Self-signature>`, more specifically {term}`revocation self-signatures<Revocation Self-signature>`.
|
||||
However, *{term}`certification revocations<Certification Revocation Signature Packet>`* can be both {term}`self-signatures<Self-signature>` or {term}`third-party signatures<Third-party Signature>`.
|
||||
Additionally, with the deprecated *Revocation Key* mechanism, {term}`third-party<Third-party Signature>` *Key-* and *Subkey revocations* also exist.
|
||||
|
||||
RFC
|
||||
This document, unless noted otherwise, refers to the [OpenPGP version 6 specification](https://datatracker.ietf.org/doc/draft-ietf-openpgp-crypto-refresh/) when referring to *RFC*.
|
||||
|
||||
SEIPD
|
||||
See {term}`Symmetrically Encrypted Integrity Protected Data`.
|
||||
|
||||
Self-certification
|
||||
A {term}`certification` on a {term}`component` of an {term}`OpenPGP Certificate` issued by a {term}`component key` of the same {term}`OpenPGP certificate`.
|
||||
|
||||
Secret Key Material
|
||||
See {term}`Private Key Material`.
|
||||
|
||||
Self-signature
|
||||
An {term}`OpenPGP Signature Packet` by the {term}`Certificate Holder` on a {term}`Component` of their own {term}`Certificate`.
|
||||
|
||||
Session Key
|
||||
A unique shared secret used in encryption in a {term}`Hybrid Cryptosystem`. See [](encryption) and [](decryption).
|
||||
|
||||
Soft Revocation
|
||||
A {term}`Revocation Signature Packet` for a {term}`Certification` or a {term}`Component Key`, which includes a {term}`Reason For Revocation Subpacket` with a {term}`Revocation Code`, that does not signify the target being compromised (e.g., `0` or `2`).
|
||||
|
||||
See [](hard-vs-soft-revocations).
|
||||
|
||||
See [RFC 5.2.3.31](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-reason-for-revocation).
|
||||
|
||||
Signature
|
||||
See {term}`OpenPGP Signature Packet`.
|
||||
|
||||
Signature Creation Time Subpacket
|
||||
An {term}`OpenPGP Signature Subpacket Type` which defines the {term}`Creation Time` for an {term}`OpenPGP Signature Packet`.
|
||||
|
||||
See [RFC 5.2.3.11](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-signature-creation-time)
|
||||
|
||||
Signature Expiration Time Subpacket
|
||||
An {term}`OpenPGP Signature Subpacket Type` which defines the {term}`Expiration Time` for an {term}`OpenPGP Signature Packet`.
|
||||
|
||||
See [RFC 5.2.3.18](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-signature-expiration-time)
|
||||
|
||||
Signature On Component
|
||||
{term}`Cryptographic signature` associated with {term}`Component Keys<Component Key>` or {term}`Identity Components<Identity Component>`. See [](/signing_components).
|
||||
|
||||
Signature Over Data
|
||||
See {term}`Data Signature`.
|
||||
|
||||
Signature Packet
|
||||
See {term}`OpenPGP Signature Packet`.
|
||||
|
||||
Signature Subpacket
|
||||
See {term}`OpenPGP Signature Subpacket`.
|
||||
|
||||
Signature Subpacket Type
|
||||
See {term}`OpenPGP Signature Subpacket Type`.
|
||||
|
||||
Signature Type
|
||||
See {term}`OpenPGP Signature Type`.
|
||||
|
||||
Signature Type ID
|
||||
A numerical identifier for a {term}`Signature Type`.
|
||||
|
||||
Signature Verification
|
||||
In cryptography the mechanism of verification relates to a process in which a claim (i.e., a {term}`signature`) is tested (i.e., using the relevant {term}`components<Component>` of a {term}`certificate`).
|
||||
|
||||
Signer
|
||||
A {term}`Certificate Holder`, that is able to create {term}`self-signatures<Self-signature>` and {term}`third-party signatures<Third-party Signature>`.
|
||||
|
||||
Signing-capable
|
||||
See {term}`Signing Key Flag`.
|
||||
|
||||
Signing Key Flag
|
||||
A {term}`Key Flag`, indicating that a {term}`Component Key` can be used for signing data. See [](key-flags).
|
||||
|
||||
Signing Subkey
|
||||
See {term}`OpenPGP Signing Subkey`.
|
||||
|
||||
Strong Authentication
|
||||
"Strong Authentication" in this text refers to having ascertained that a {term}`certificate<OpenPGP Certificate>` and an {term}`identity claim` on it are legitimately linked. That is, that the person who controls the {term}`certificate<OpenPGP Certificate>` is correctly represented by the {term}`identity component`.
|
||||
|
||||
Strong authentication in OpenPGP is typically encoded with a {term}`certification signature`.
|
||||
|
||||
Ascertaining strong authentication requires an out-of-band check: Either via a manual {term}`verification` process, or an automated system that can {term}`certify<Certification>` that a user has identified to the system that issues the {term}`identity` in question (e.g. an email provider can {term}`certify<Certification>` email-based {term}`identities<Identity>` that it issues to the user).
|
||||
|
||||
Also see {term}`Authentication`.
|
||||
|
||||
Subkey
|
||||
See {term}`OpenPGP Subkey`.
|
||||
|
||||
Subkey Binding Signature
|
||||
A {term}`Self-signature` to associate an {term}`OpenPGP Subkey` with an {term}`OpenPGP Primary Key`. See [](bind-subkey).
|
||||
|
||||
Subkey Revocation Signature Packet
|
||||
A {term}`Self-signature` to {term}`revoke<Revocation>` an {term}`OpenPGP Subkey` in an {term}`OpenPGP Certificate`.
|
||||
|
||||
See [RFC 5.2.1.12](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-subkey-revocation-signature)
|
||||
|
||||
Subpacket
|
||||
See {term}`OpenPGP Signature Subpacket`.
|
||||
|
||||
Subpacket Type
|
||||
See {term}`OpenPGP Signature Subpacket Type`.
|
||||
|
||||
Symmetric Cryptography
|
||||
Symmetric cryptography is used in OpenPGP. For a more detailed discussion see [](symmetric-key-cryptography).
|
||||
|
||||
Symmetrically Encrypted Integrity Protected Data
|
||||
Short *SEIPD*, this refers to {term}`Symmetric Cryptography` based encrypted data, which is used in a Symmetrically Encrypted Integrity Protected Data Packet.
|
||||
|
||||
See [RFC 5.13](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-symmetrically-encrypted-int).
|
||||
|
||||
Symmetric Secret Key
|
||||
The {term}`Private Key Material` used in {term}`Symmetric Cryptography`.
|
||||
|
||||
Text Signature
|
||||
A {term}`signature packet<OpenPGP signature packet>` with the {term}`Signature Type ID` `0x01`, which is used for textual data.
|
||||
|
||||
Third-party Identity Certification
|
||||
{term}`Certification` by third-parties to confirm ownership of an {term}`OpenPGP Certificate` by a {term}`Certificate Holder`. See [](third-party-identity-certifications).
|
||||
|
||||
Third-party Signature
|
||||
A {term}`Signature` by a third-party on a {term}`Component` of a {term}`Certificate`.
|
||||
|
||||
Transferable Secret Key
|
||||
A Transferable Secret Key (TSK) is the combination of an {term}`OpenPGP Certificate` and the associated {term}`private key material`. Also often referred to as an "OpenPGP private key". It is discussed in detail in [](/private_keys).
|
||||
|
||||
Trust Amount
|
||||
A numerical value between `0` and `255`, stored in {term}`trust signatures<Trust Signature>` used for indicating the degree of reliance on the {term}`delegation`.
|
||||
Values less than `120` indicate partial trust, values equal to or greater than `120` indicate complete trust.
|
||||
|
||||
See [](trust-amount).
|
||||
See [RFC 5.2.3.21](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-trust-signature)
|
||||
|
||||
Trust Anchor
|
||||
An entity in a {term}`Trust Model` for which trust is assumed and not derived.
|
||||
|
||||
Trust Depth
|
||||
This numerical value is part of a {term}`Trust Signature` and describes the extent of trustworthiness of a {term}`Certification`, that the {term}`signer` assigns to it.
|
||||
|
||||
See [](trust-level).
|
||||
|
||||
Trust Level
|
||||
See {term}`Trust Depth`.
|
||||
|
||||
Trust Model
|
||||
A model by which trust between {term}`identities<Identity>` associated with different {term}`OpenPGP Certificates<OpenPGP Certificate>` is created. See [](third-party-identity-certifications).
|
||||
|
||||
Trust Root
|
||||
See {term}`Trust Anchor`.
|
||||
|
||||
Trust Signature
|
||||
The *trust signature* {term}`subpacket<OpenPGP Signature Subpacket>` on a {term}`certifying signature<Certification>` is used for {term}`delegation` of {term}`authentication` decisions. With this feature, an OpenPGP user can designate a {term}`certificate<OpenPGP Certificate>` as a "{term}`trusted introducer`" and opt to rely on {term}`certifications<Certification>` they issue.
|
||||
|
||||
See [RFC 5.2.3.21](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-trust-signature)
|
||||
|
||||
Trusted introducer
|
||||
OpenPGP users can choose to rely on {term}`certifications<Certification>` issued by a third party. The remote party of such a {term}`delegation` is called a "trusted introducer".
|
||||
|
||||
See {ref}`delegation` for more details.
|
||||
|
||||
TSK
|
||||
See {term}`Transferable Secret Key`.
|
||||
|
||||
tsig
|
||||
See {term}`Trust signature`
|
||||
|
||||
Type ID
|
||||
See {term}`Signature Type ID`.
|
||||
|
||||
Unhashed Area
|
||||
An area in a {term}`Signature Packet` containing {term}`Signature Subpacket`s, that is *not* covered by the {term}`Hash Digest` a {term}`Cryptographic Signature` is created for. See [](subpacket-areas).
|
||||
|
||||
Unhashed Subpacket
|
||||
A {term}`Signature Subpacket` residing in the {term}`Unhashed Area` of a {term}`Signature Packet`.
|
||||
|
||||
User Attribute
|
||||
An {term}`Identity Component`, which may hold a single JPEG image. See [](user-attributes).
|
||||
|
||||
User ID
|
||||
An {term}`Identity Component`, which describes an {term}`Identity` of a {term}`Certificate Holder`. See [](user-ids).
|
||||
|
||||
User ID Binding Signature
|
||||
A {term}`Binding Signature`, which is created by an {term}`OpenPGP Primary Key` to bind a {term}`User ID` to an {term}`OpenPGP Certificate`.
|
||||
|
||||
Validation
|
||||
A mechanism by which the [operational needs of a use-case are met](https://en.wikipedia.org/wiki/Verification_and_validation#Validation).
|
||||
In OpenPGP terminology this may refer to processes such as ensuring, that an {term}`OpenPGP Signature Packet` has been created after a {term}`Transferable Secret Key`'s {term}`Creation Time`, but before its {term}`Expiration Time`.
|
||||
|
||||
Validity
|
||||
See {term}`Validation`.
|
||||
|
||||
Verification
|
||||
A mechanism by which the [compliance with design specifications are met](https://en.wikipedia.org/wiki/Verification_and_validation#Verification).
|
||||
In OpenPGP terminology this may refer to e.g. {term}`Signature Verification` or {term}`Identity Verification`.
|
||||
|
||||
Web Of Trust
|
||||
A {term}`trust model` which is based on a network of {term}`certifications<Certification>` and {term}`delegations<Delegation>`, that can be used to discern the reliability of {term}`certificates<Certificate>` and their associated {term}`identities<Identity>`. See [](wot).
|
||||
```
|
1
book/source/img/README
Normal file
|
@ -0,0 +1 @@
|
|||
Generated image data, based on source material in book/assets/
|
BIN
book/source/img/drawio/attribute-shadowing.png
Normal file
After Width: | Height: | Size: 57 KiB |
BIN
book/source/img/drawio/cert-validity-key-expiration.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
book/source/img/drawio/cert-validity-simple.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
book/source/img/drawio/cert-validity-subkey.png
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
book/source/img/drawio/dk-attributes-and-shadowing.png
Normal file
After Width: | Height: | Size: 57 KiB |
BIN
book/source/img/drawio/narrow-interpretation.png
Normal file
After Width: | Height: | Size: 68 KiB |
BIN
book/source/img/mermaid/09-sigtree.png
Normal file
After Width: | Height: | Size: 108 KiB |
BIN
book/source/img/mermaid/sig-types.png
Normal file
After Width: | Height: | Size: 85 KiB |
51
book/source/index.md
Normal file
|
@ -0,0 +1,51 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# OpenPGP for application developers
|
||||
|
||||
**{sub-ref}`today`**
|
||||
|
||||
```{toctree}
|
||||
:numbered:
|
||||
:maxdepth: 2
|
||||
:glob:
|
||||
|
||||
about.md
|
||||
openpgp.md
|
||||
cryptography.md
|
||||
certificates.md
|
||||
private_keys.md
|
||||
signatures.md
|
||||
signing_data.md
|
||||
signing_components.md
|
||||
verification.md
|
||||
encryption.md
|
||||
decryption.md
|
||||
compression.md
|
||||
armor.md
|
||||
pitfalls.md
|
||||
policy.md
|
||||
versions.md
|
||||
migration.md
|
||||
|
||||
adv/certificates.md
|
||||
adv/private_keys.md
|
||||
adv/signatures.md
|
||||
adv/signing_data.md
|
||||
adv/signing_components.md
|
||||
adv/verification.md
|
||||
adv/encryption.md
|
||||
adv/decryption.md
|
||||
|
||||
zoom/certificates.md
|
||||
zoom/private_keys.md
|
||||
zoom/signatures.md
|
||||
zoom/encryption.md
|
||||
|
||||
glossary.md
|
||||
acknowledgements.md
|
||||
|
||||
artifacts.md
|
||||
```
|
114
book/source/migration.md
Normal file
|
@ -0,0 +1,114 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Migration from OpenPGP v4 to v6
|
||||
|
||||
The OpenPGP protocol has developed over time, and will continue to do so, adapting to new challenges and expectations.
|
||||
|
||||
Some of these changes might be subtle, like the addition of a new hash algorithm, while others are more invasive, like a new OpenPGP key format.
|
||||
|
||||
This makes it necessary to migrate both implementations and existing user keys and certificates.
|
||||
|
||||
In this chapter, we want to explore possible steps to migrate from OpenPGP v4 as defined by RFC4880 to v6 (crypto-refresh).
|
||||
|
||||
## Adoption of new features
|
||||
|
||||
The new standard introduced a number of new features, which improve security aspects of the protocol.
|
||||
Some of these features can only be used with new OpenPGP version 6 keys, and require users to migrate to fresh keys.
|
||||
|
||||
Other features can be used with existing OpenPGP version 4 keys, as soon as implementations support the features, and users' certificates reflect that the features are supported by the user's software.
|
||||
|
||||
### SEIPD v2
|
||||
|
||||
A perfect example for a newly introduced feature that can be applied to existing v4 keys are the new SEIPD v2 packets.
|
||||
|
||||
Existing OpenPGP v4 keys can simply announce support for SEIPD v2 via a *Feature* subpacket in their certificate. Publishing such an updated *Feature* set via their OpenPGP certificate signals that the user's OpenPGP software is capable of handling SEIPD v2.
|
||||
|
||||
Senders who can produce this new encryption mode can then opt to use it when encrypting to this recipient.
|
||||
|
||||
(migration-s2k)=
|
||||
### S2K usage mode AEAD
|
||||
|
||||
Another good example is the S2K mechanism for secret-key encryption.
|
||||
|
||||
This feature concerns local copies of OpenPGP private keys on each user's machine. There is, by definition, no interoperability concern around this feature: Passphrase-protection of the private key material is a local implementation detail on each user's machine.
|
||||
|
||||
The RFC [states](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-avoiding-ciphertext-malleab) that: "Users are RECOMMENDED to migrate to AEAD."
|
||||
|
||||
In the context of this chapter, this means that encrypted private keys should be upgraded by the user's OpenPGP software to use [S2K usage mode 253](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-secret-key-encryption-s2k-u) (AEAD) to encrypt the user's private key material.
|
||||
|
||||
Note that S2K usage mode 253 (AEAD) can be applied to both version 6 and version 4 private keys, with sufficiently up-to-date OpenPGP software. This S2K usage mode is strongly recommended for private keys of all versions.
|
||||
|
||||
#### S2K method Argon2
|
||||
|
||||
Independently, the RFC recommends the use of the Argon2 S2K method to hash passphrases, when it is available. This mechanism also concerns the local passphrase-protection of private key material.
|
||||
|
||||
Use of Argon2 is only allowed in combination with AEAD.
|
||||
|
||||
Users can and should migrate the protection of their private keys to Argon2 (combined with the AEAD usage mode).
|
||||
|
||||
### OpenPGP v6 signatures
|
||||
|
||||
Version 6 signatures can't be generated with OpenPGP v4 keys. Only OpenPGP v6 keys can issue v6 signatures.
|
||||
|
||||
On the receiving/verifying side, v6 signatures can be checked by anyone whose OpenPGP software supports v6 certificates and v6 signature verification. This includes OpenPGP users who currently use a v4 key.
|
||||
|
||||
### Software migration
|
||||
|
||||
Over time, steadily more OpenPGP libraries and tools will add support for OpenPGP v6 features. This migration might take a while, while implementers catch up.
|
||||
|
||||
The OpenPGP v6 standard gives guidance for library authors to extend an OpenPGP implementation to support version 6 in [Appendix B. Upgrade Guidance](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-upgrade-guidance-adapting-i).
|
||||
|
||||
## Key migration
|
||||
|
||||
Some OpenPGP v6 features are only available for use with keys in the v6 format.
|
||||
|
||||
For example, only an [OpenPGP v6 key can issue a v6 signature](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-packet-versions-in-signatur).
|
||||
|
||||
On the other hand, an OpenPGP v6 key can *only* issue v6 signatures, so if you require compatibility with v4 verifiers, you shouldn't yet migrate to a v6 key/certificate.
|
||||
|
||||
When migrating to a v6 key, generating a fresh v6 key is the recommended approach.
|
||||
|
||||
It is not possible to adopt v4 subkeys into a v6 key, since every subkey to a v6 primary key must itself be a v6 subkey, see in [OpenPGP v6 certificate structure](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#section-10.1.1-5).
|
||||
|
||||
### Converting v4 component keys into v6 component keys
|
||||
|
||||
That is, taking the existing key material from a v4 component key and re-framing it as a v6 component key, for use with an OpenPGP version 6 certificate.
|
||||
|
||||
```{admonition} TL;DR
|
||||
:class: info
|
||||
|
||||
don't.
|
||||
```
|
||||
|
||||
#### Motivation to convert
|
||||
|
||||
It might be tempting to consider migrating existing key material to the v6 format. Such a step should be considered very carefully though.
|
||||
|
||||
Unfortunately, keys cannot simply be converted into the new format, and used seamlessly. For one thing, the Fingerprint of component keys changes for the same key material between version 4 and version 6 (and with it, the Key ID that is a shortened version of the Fingerprint).
|
||||
|
||||
An OpenPGP v4 Fingerprint is calculated as the SHA-1 checksum of the normalized public key packet, which results in a 20 byte fingerprint (often represented as a 40 character hexadecimal string). The v4 Key ID consists of the *last* 64 bits of the fingerprint.
|
||||
|
||||
On the other hand, a v6 fingerprint is calculated as the SHA-256 checksum of the normalized public key packet, so it comprises 32 bytes. The v6 Key ID consists of the *first* 64 bit of the fingerprint.
|
||||
|
||||
As a consequence, component key identifiers in OpenPGP artifacts, such as issuer subpackets in signatures, or recipient Key IDs in PKESK packets issued by a v4 key do not match the component key identifiers of same key material converted to v6.
|
||||
|
||||
Further, v6 keys can only issue v6 signatures, and v6 certificates can only be used to verify v6 signatures. Otherwise, a downgrade vector could exist, by which verifiers could be tricked into verifying specially crafted v4 signatures against OpenPGP v6 certificates. If a vulnerability arose in OpenPGP v4 at some point, which allows an attacker to craft valid v4 signatures, this could affect OpenPGP v6 certificates.
|
||||
|
||||
#### Retaining decryption access to old messages
|
||||
|
||||
Another motivation for converting old key material might be the desire to stay able to decrypt messages encrypted for the old key.
|
||||
This won't be possible out of the box, as the Key ID in the respective PKESK packet no longer matches that of the converted key. So at the bare minimum, the user's implementation would need to be able to map Key IDs. This is not a feature prevalent in the ecosystem though.
|
||||
|
||||
An alternative approach - that doesn't require special handling in the user's OpenPGP software - is to replace the PKESK headers of the messages. The session key for each message can be easily obtained by decrypting the message using the old key, so the session key can be re-encrypted for either the converted v6 key, or a freshly generated v6 key. This new PKESK packet can be added to, or replaced in, the message.
|
||||
|
||||
#### Conclusion
|
||||
|
||||
In conclusion, converting v4 key material to v6 to verify old signatures is not a strong argument.
|
||||
Being able to read old messages using a converted key is also not really viable, since it is equally simple to just re-create the PKESK headers for a fresh v6 key.
|
||||
|
||||
```{note}
|
||||
Also see <https://wiki.gnupg.org/OpenPGPEmailSummit202305Notes>
|
||||
```
|
112
book/source/openpgp.md
Normal file
|
@ -0,0 +1,112 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# A high-level view
|
||||
|
||||
## Why OpenPGP?
|
||||
|
||||
OpenPGP is a widely recognized, IETF-standardized set of cryptographic operations. It is broadly used in securing communications, like encrypted messages and email, and ensuring the integrity of software packages in most Linux distributions. It enjoys a vast ecosystem of libraries, tools, and community support forums. Moreover, its robustness and versatility have made OpenPGP a security choice for other use cases in which encryption and integrity are important. These include file transfer applications, password managers, secure data storage, and signing source code in git repositories.
|
||||
|
||||
There are other compelling reasons for why you might consider using OpenPGP in your project:
|
||||
|
||||
1. **Decentralized trust model**: OpenPGP's decentralization defines mechanisms for {term}`authentication` that allow individuals and entities to create and manage their own cryptographic {term}`identities<Identity>`. Unlike centralized {term}`trust models<Trust Model>`, decentralized {term}`trust models<Trust Model>` empower individuals and entities to manage their own {term}`identities<Identity>`, fostering a community-driven web of trust instead of relying on a centralized authority, thus reducing single points of failure.
|
||||
|
||||
2. **End-to-end encryption**: OpenPGP provides a robust framework for implementing end-to-end encryption. Content remains confidential, verifiable, {term}`authenticated<Authentication>`, and protected against unauthorized access, even when the communication channel itself might be otherwise compromised. Encryption is crucial in a myriad of scenarios, particularly when transmitting sensitive information such as financial data, personally identifiable information (PII), or proprietary business data.
|
||||
|
||||
3. **Anonymity and pseudonymity**: In sensitive and volatile situations where identity protection is crucial, OpenPGP can be used to provide a level of anonymity or pseudonymity that helps protect user identities. For example, OpenPGP has been used alongside other privacy tools, such as [Tor](https://en.wikipedia.org/wiki/The_Tor_Project) and [VPN](https://en.wikipedia.org/wiki/Virtual_private_network)s, to provide secure and anonymous communication for whistleblowers, human rights lawyers, activists in repressive regimes, and journalists, reducing their risks for retaliation and state violence.
|
||||
|
||||
4. **Interoperability**: OpenPGP is a well-structured and standardized protocol, widely adopted by various public and private entities but not tied to any particular vendor's technology. It supports all major operating systems, such as Windows, macOS, GNU/Linux, Android, and iOS. Because of standardization, wide adoption, cross-platform compatibility, and adaptability, OpenPGP's interoperability significantly contributes to reducing development time, costs, and technical hurdles.
|
||||
|
||||
## A very brief history
|
||||
|
||||
The OpenPGP standard has evolved over time, and remains under active development.
|
||||
|
||||
(Also see [https://www.openpgp.org/about/history/](https://www.openpgp.org/about/history/))
|
||||
|
||||
### Pretty Good Privacy (PGP)
|
||||
|
||||
The origins of OpenPGP can be traced back to *Pretty Good Privacy (PGP)*, a software program written by [Phil Zimmermann](https://en.wikipedia.org/wiki/Phil_Zimmermann) and first released in 1991.
|
||||
|
||||
The original PGP software played a role in the political struggles sometimes referred to as the ["Crypto Wars"](https://en.wikipedia.org/wiki/Crypto_Wars) (also see ["Crypto: How the Code Rebels Beat the Government Saving Privacy in the Digital" (2002)](https://en.wikipedia.org/wiki/Crypto_(book)), which includes some of PGP's history).
|
||||
|
||||
The original PGP software was never under a Free Software license, despite its source code being widely published by its author. [PGP's ownership has changed over the years](https://en.wikipedia.org/wiki/Pretty_Good_Privacy#PGP_Corporation_and_Symantec), and [PGP's scope and suite of products have expanded](https://en.wikipedia.org/wiki/Pretty_Good_Privacy#PGP_Corporation_encryption_applications).
|
||||
|
||||
### Standardizing OpenPGP
|
||||
|
||||
While PGP was first developed as commercial software, the owner at the time, PGP Inc., started a standardization effort with the IETF, first publishing [RFC 1991 "PGP Message Exchange Formats"](https://datatracker.ietf.org/doc/html/rfc1991) in August 1996.
|
||||
|
||||
In July 1997, a process to produce an open standard under the then new name [OpenPGP](https://en.wikipedia.org/wiki/Pretty_Good_Privacy#OpenPGP) was started, resulting in [RFC 2440 "OpenPGP Message Format"](https://datatracker.ietf.org/doc/html/rfc2440), published in November 1998.
|
||||
|
||||
The name OpenPGP can be used freely by implementations, unlike the name PGP, which is a [registered trademark](https://uspto.report/TM/74685229).
|
||||
|
||||
### GnuPG, an early Free Software implementation
|
||||
|
||||
[First released 1997-12-20](https://gnupg.org/download/release_notes.html#sec-2-70) by Werner Koch, a German computer programmer, GNU Privacy Guard (GnuPG) is a free and open-source implementation of the OpenPGP standard.
|
||||
|
||||
GnuPG was a major early implementation of OpenPGP. Over the years, the importance of GnuPG has grown significantly as it became a foundational tool for email security, software signing, and more. It played an important (and successful) role in the [release of NSA documents](https://theintercept.com/2014/10/28/smuggling-snowden-secrets/) by [Edward Snowden](https://en.wikipedia.org/wiki/Edward_Snowden).
|
||||
|
||||
Because the GnuPG program binary is called "gpg," "GnuPG" and "gpg" are often used interchangeably.
|
||||
|
||||
## The RFC 4880 era
|
||||
|
||||
### OpenPGP version 4
|
||||
|
||||
In 2007, the IETF published [RFC 4880](https://datatracker.ietf.org/doc/html/rfc4880), which defines version 4 OpenPGP artifacts. As of late 2023, version 4 is the most commonly used version.
|
||||
|
||||
An extension for Elliptic Curve Cryptography was defined in [RFC 6637](https://www.rfc-editor.org/rfc/rfc6637), specifying the use of three NIST prime field curves.
|
||||
|
||||
Some implementations explored other non-standardized extensions. Notably, algorithms based on Curve 25519 were tentatively defined in the [rfc4880bis](https://www.ietf.org/archive/id/draft-ietf-openpgp-rfc4880bis-10.html#name-elliptic-curve-cryptography) document. These algorithms are widely used, even though rfc4880bis has never been finalized as a new version of the standard.
|
||||
|
||||
(major-implementations)=
|
||||
### Major implementations of OpenPGP
|
||||
|
||||
Today, multiple implementations of OpenPGP play important roles:
|
||||
|
||||
- The Mozilla Thunderbird email software uses [RNP](https://www.rnpgp.org/), a C++ implementation of OpenPGP.
|
||||
- [GNU Privacy Guard (GnuPG)](https://gnupg.org/), a key implementation of the OpenPGP standard, is integral to numerous critical infrastructures, most prominently in ensuring package integrity verification for Linux distributions.
|
||||
- Proton Mail, which provides email encryption services for a large number of users, uses and maintains [OpenPGP.js](https://openpgpjs.org/) as well as [GopenPGP](https://gopenpgp.org/), an OpenPGP wrapper library written in golang.
|
||||
- The RPM Package Manager software includes an OpenPGP backend based on [Sequoia PGP](https://sequoia-pgp.org/), a modern OpenPGP implementation written in Rust. The Fedora Linux operating system [uses Sequoia PGP in rpm](https://sequoia-pgp.org/blog/2023/04/27/rpm-sequoia/) since version 38.
|
||||
|
||||
(interoperability)=
|
||||
### Interoperability
|
||||
|
||||
OpenPGP was standardized in 1997 to encourage development of interoperable implementations. This has already been a success early on, but in recent years, there has been [much development of new implementations](major-implementations).
|
||||
|
||||
Historically, interoperability has only been tested in an adhoc manner. Since 2019, the Sequoia project is maintaining and operating the ["OpenPGP interoperability test suite"](https://tests.sequoia-pgp.org/), for more rigorous and systematic testing. The test suite has identified numerous [issues](https://gitlab.com/sequoia-pgp/openpgp-interoperability-test-suite#hall-of-fame).
|
||||
|
||||
## The road ahead
|
||||
|
||||
```{note}
|
||||
Software and protocol development sometimes skips version numbers due to reasons like internal testing, significant changes, avoiding confusion, marketing decisions, or technical issues. The official successor to OpenPGP version 4 is OpenPGP version 6, detailed below.
|
||||
```
|
||||
|
||||
### OpenPGP version 6
|
||||
|
||||
As of this writing (in 2023), [version 6 of OpenPGP](https://datatracker.ietf.org/doc/draft-ietf-openpgp-crypto-refresh/) is approaching publication as an RFC.
|
||||
The [IETF OpenPGP working group](https://datatracker.ietf.org/wg/openpgp/about/#autoid-1) is focused on updating the cryptographic mechanisms, adding new algorithms, and the deprecation of obsolete algorithms.
|
||||
|
||||
This document describes OpenPGP version 6, while pointing out differences to previous versions that are relevant to application developers.
|
||||
|
||||
Significant support for OpenPGP version 6 has already been achieved for multiple implementations, including:
|
||||
|
||||
- [Bouncy Castle Java](https://github.com/bcgit/bc-java/issues/1421),
|
||||
- [GopenPGP](https://github.com/ProtonMail/gopenpgp/tree/v3),
|
||||
- [OpenPGP.js](https://github.com/openpgpjs/openpgpjs/releases/tag/v6.0.0-alpha.0),
|
||||
- [PGPy](https://github.com/dkg/PGPy/tree/dkg/crypto-refresh),
|
||||
- [Sequoia PGP](https://gitlab.com/sequoia-pgp/sequoia/-/tree/crypto-refresh).
|
||||
|
||||
Initial efforts to incorporate support for OpenPGP version 6 have been undertaken in the PGPainless and RNP implementations.
|
||||
|
||||
### Post-quantum cryptography in OpenPGP
|
||||
|
||||
There is [ongoing work](https://datatracker.ietf.org/doc/draft-wussler-openpgp-pqc/) to standardize and add support for post-quantum {term}`public-key algorithms<Public Key Algorithm>` in OpenPGP. This project is funded by the [german "BSI"](https://en.wikipedia.org/wiki/Federal_Office_for_Information_Security). Goals include adding support for post-quantum cryptography to Thunderbird and GnuPG. A [presentation](https://datatracker.ietf.org/meeting/113/materials/slides-113-openpgp-a-post-quantum-approach-for-openpgp-00) was given at [IETF 113](https://datatracker.ietf.org/meeting/113/session/openpgp/).
|
||||
|
||||
## Zooming in: Internal structure of OpenPGP data
|
||||
|
||||
OpenPGP data is internally structured as "packets." We'll look into examples of this internal structure in a series of chapters at the end of this document.
|
||||
|
||||
Getting familiar with the internal format of OpenPGP data provides practical insight into the [RFC](https://datatracker.ietf.org/doc/draft-ietf-openpgp-crypto-refresh/), which describes the internal structure of OpenPGP {term}`packets<Packet>` in full detail, and may also come in handy for debugging issues.
|
||||
|
||||
(Most of the time, however, we will look at OpenPGP artifacts at a higher level of abstraction.)
|
16
book/source/pitfalls.md
Normal file
|
@ -0,0 +1,16 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Pitfalls / Things to keep in mind
|
||||
|
||||
## Key IDs are really not guaranteed to be unique
|
||||
|
||||
Use fingerprints, or expect duplicates
|
||||
|
||||
## Signature Subpackets can have duplicates
|
||||
|
||||
## Packet Nesting can be unreasonable
|
||||
|
||||
- EBNF of allowed packet sequences is complex -> Recommend [stricter](https://mailarchive.ietf.org/arch/msg/openpgp/uepOF6XpSegMO4c59tt9e5H1i4g/) best-practices?
|
11
book/source/policy.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Algorithms and Policy
|
||||
|
||||
```{note}
|
||||
|
||||
This section is still about to be written.
|
||||
```
|
133
book/source/private_keys.md
Normal file
|
@ -0,0 +1,133 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Managing private key material in OpenPGP
|
||||
|
||||
## Overview of private keys
|
||||
|
||||
This chapter discusses the handling of private key material within OpenPGP.
|
||||
|
||||
Private key material is associated with component keys, which are integral parts of [OpenPGP certificates](/certificates). For a discussion of packet structure internals, see the chapter [](zoom/private_keys).
|
||||
|
||||
## Terminology: "certificates" and "private keys"
|
||||
|
||||
Recall that in this document, the term *OpenPGP certificate* refers to what are commonly known as "OpenPGP public keys." OpenPGP certificates are the combination of component public keys, identity components, binding self-signatures, and third-party certifications,
|
||||
as discussed in the previous chapter ([](/certificates)).
|
||||
|
||||
This chapter focuses on the corresponding counterpart to the elements of certificates: the *private key material* of component keys.
|
||||
|
||||
In this documentation, we treat the private key material as logically separate from the OpenPGP certificate. Operations that use private key material are typically managed by a separate subsystem. It is useful to view OpenPGP certificates and the associated private key material as related but distinct elements[^pkcs11]:
|
||||
|
||||
```{figure} plain_svg/OpenPGPCert_with_privatekeystore.svg
|
||||
:name: fig-openpgp-certificate-with-private-key-store
|
||||
:alt: A diagram on a white background showing an OpenPGP certificate and a private keystore. Gray dotted lines connect the green public key symbols of the OpenPGP certificate to red dotted private key symbols in the private keystore.
|
||||
|
||||
An OpenPGP certificate, with the associated private key material handled in a separate subsystem.
|
||||
```
|
||||
|
||||
[^pkcs11]: The distinction between certificates (which combine public key material and identity information) and private key material is similarly made in the data model of [PKCS #11](https://en.wikipedia.org/wiki/PKCS_11) cryptographic systems.
|
||||
|
||||
In certain cases, an exception arises where the cryptographic private key material is integrated into the same OpenPGP framing format as the certificate. This is specifically done in the context of transferable secret keys (TSKs).
|
||||
|
||||
## Transferable secret key format
|
||||
|
||||
Sometimes it is useful to handle OpenPGP certificates combined with private key material in the form of a [*transferable secret key (TSK)*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-transferable-secret-keys). A TSK is a serialized format that combines OpenPGP certificate data with its connected private key material, stored in a single file.
|
||||
|
||||
```{figure} plain_svg/TSK.svg
|
||||
:name: fig-transferable-secret-key
|
||||
:alt: A box on a white background titled "transferable secret key." It resembles the figure depicting an OpenPGP certificate, except that in each component key box, below the green public key symbol, the red-dotted private key symbol is also shown.
|
||||
|
||||
OpenPGP certificate with integrated private key material, as a TSK
|
||||
```
|
||||
|
||||
The TSK format is particularly useful for backups of OpenPGP key material or transferring a key to a different computer[^gpg-tsk]. For insights into the packet structure of a TSK, see the chapter [](zoom/private_keys).
|
||||
|
||||
[^gpg-tsk]: For example, in GnuPG, an OpenPGP key can be exported in (armored) TSK format using the following command: `gpg --export-secret-key --armor <fingerprint>`.
|
||||
|
||||
```{admonition} Terminology
|
||||
:class: note
|
||||
|
||||
Transferable secret keys are sometimes colloquially referred to as "OpenPGP private keys."
|
||||
```
|
||||
|
||||
Historically, the concept of TSKs, which combine all elements of an OpenPGP certificate with the associated private key material, has sometimes been conflated with OpenPGP private key operations. However, TSKs are primarily a format for storage and transport; it is generally considered inappropriate as a data structure for use in a private keystore. For further details, see {ref}`key-store-design`.
|
||||
|
||||
(protected-private-keys)=
|
||||
## Protecting keys with passphrases
|
||||
|
||||
In the OpenPGP format, private key material can be optionally protected with a [passphrase](https://en.wikipedia.org/wiki/Passphrase).
|
||||
|
||||
This method proves effective in scenarios where an unauthorized party obtains the OpenPGP key data but does not know the passphrase. Such a safeguard renders the key unusable to the attacker, effectively protecting it against unauthorized access or use.
|
||||
|
||||
### Transforming passphrases into symmetric keys
|
||||
|
||||
When protecting private key material in OpenPGP, a symmetric key is derived from the user's passphrase. This derived key is then used to protect the OpenPGP private key data.
|
||||
|
||||
To facilitate this, the OpenPGP standard defines a set of mechanisms known as [string-to-key (S2K)](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-string-to-key-s2k-specifier). S2K mechanisms are used to generate high-entropy symmetric encryption keys from lower-entropy passphrases, using a [key derivation function (KDF)](https://en.wikipedia.org/wiki/Key_derivation_function).
|
||||
|
||||
```{figure} plain_svg/passphrase_using_S2K.svg
|
||||
:name: fig-passphrase-using-s2k
|
||||
:alt: A diagram on a white background titled "Converting a passphrase into a symmetric key." On the left is a light-yellow box with dotted-yellow borders framing the phrase "correct horse battery staple." A dotted yellow line falls below the box to the term "passphrase." To the right of the box is a light-green arrow with green-dotted borders and the text "S2K mechanism (string-to-key). The arrow points to its right, where a yellow symmetric key symbol is shown.
|
||||
|
||||
Deriving a symmetric key from a passphrase
|
||||
```
|
||||
|
||||
This symmetric key is used to protect the private key material it is in a passive state, for example, when stored on disk. To use a passphrase-protected OpenPGP private key, it is first decrypted using the symmetric key and then used for private key operations, remaining temporarily unlocked in memory.
|
||||
|
||||
#### Mechanisms for symmetric key generation
|
||||
|
||||
Over time, OpenPGP has evolved to include various [S2K mechanisms for generating symmetric keys](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-string-to-key-s2k-types-reg), in line with advancements in cryptographic practices. Currently, two mechanisms are universally recommended:
|
||||
|
||||
- [**Argon2**](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-argon2): Introduced in OpenPGP version 6, Argon2 is a memory-hard mechanism designed to reduce the efficiency of brute-force attacks using specialized hardware.
|
||||
- [**Iterated and Salted S2K**](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-iterated-and-salted-s2k): This mechanism is a staple with OpenPGP version 4 implementations.
|
||||
|
||||
A third mechanism is conditionally allowed for key generation. Decryption of private keys that use obsolete mechanisms is also allowed.
|
||||
|
||||
The RFC uses the terms "String-to-Key (S2K) specifier" or "String-to-Key (S2K) specifier type" for mechanisms used to *generate* a symmetric key from a passphrase.
|
||||
|
||||
### Using symmetric keys for encryption
|
||||
|
||||
The generation of a symmetric key from a passphrase leads to its subsequent use in encrypting or decrypting OpenPGP private key material.
|
||||
|
||||
The RFC uses the term "String-to-Key Usage (S2K usage)" for the mechanism used to *apply* the symmetric key.
|
||||
|
||||
Different mechanisms are specified [for encryption of OpenPGP private key material](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-secret-key-encryption).
|
||||
|
||||
### Component-based passphrase protection
|
||||
|
||||
The OpenPGP mechanism for protecting private key material applies individually to each component key:
|
||||
|
||||
- Private key material for individual component keys within a single certificate can be protected with different mechanisms or passphrases.
|
||||
- Individual component keys may be stored in unprotected form, while others are secured.
|
||||
|
||||
Commonly, when creating a certificate, the user's software will use the same encryption mechanism and passphrase for all component keys. This might give the erroneous impression that all component private key material is encrypted in one, monolithic operation using a single passphrase.
|
||||
|
||||
However, variations are possible, such as when adding new subkeys to an existing certificate. In such cases, a user might choose a different passphrase, or the software might select a different encryption mechanism, for instance, for updated best practices.
|
||||
|
||||
(openpgp-card)=
|
||||
## OpenPGP cards for private keys
|
||||
|
||||
[OpenPGP cards](https://en.wikipedia.org/wiki/OpenPGP_card) represent a category of hardware security devices specifically designed to handle OpenPGP private key material. These cards offer an alternative to directly managing private key material on the user's computer.
|
||||
|
||||
Hardware security devices, such as OpenPGP cards, are designed to prevent the user's computer from direct access to the private key material. The goal is to make it impossible to exfiltrate the key material, even when a remote attacker has fully compromised the user's system.
|
||||
|
||||
OpenPGP cards adhere to an open specification detailed in the [Functional Specification of the OpenPGP application on ISO Smart Card Operating Systems, Version 3.4.1](https://gnupg.org/ftp/specs/OpenPGP-smart-card-application-3.4.1.pdf). This specification has been implemented by multiple vendors across various devices, with several Free Software versions available, some of which are compatible with open hardware designs.
|
||||
|
||||
Effectively, the OpenPGP card specification outlines one model for a private keystore subsystem. OpenPGP cards do not store a full OpenPGP certificate. Instead, they have three distinct "key slots" designated for *signing*, *decryption*, and *authentication*. Each key slot stores the data of one component key[^missing-ecdh], including its cryptographic private key material. Additionally, OpenPGP cards explicitly store the fingerprint of each component key within the corresponding key slot.
|
||||
|
||||
[^missing-ecdh]: In the case of ECDH keys, the KDF parameters (hash function ID and a symmetric encryption algorithm ID) are not stored on the OpenPGP card. This is considered a flaw in the OpenPGP card specification. These missing parameters can be handled in two ways by OpenPGP software on the host computer: either by consulting a copy of the component key (e.g., by inspecting a copy of the certificate) or by deducing the missing KDF parameters from the stored OpenPGP fingerprint on the card.
|
||||
|
||||
Notably, the practice of explicitly storing fingerprints on OpenPGP cards contrasts with the general OpenPGP format, where fingerprints of component keys are not stored but are instead dynamically calculated from the key data.
|
||||
|
||||
## Private key operations
|
||||
|
||||
Although OpenPGP encompasses a broad range of cryptographic mechanisms, the set of operations performed within the core of a private keystore are simple and very limited.
|
||||
|
||||
Specifically, an OpenPGP private keystore implements two primitives:
|
||||
|
||||
1. Given private key material whose algorithm supports decryption, it can decrypt a *session key*.
|
||||
2. Given private key material whose algorithm supports signing, it can calculate a *cryptographic signature* for a hash digest.
|
||||
|
||||
These essential operations require access only to the component keys and their associated private key material, specifically [Secret-Key packets](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-secret-key-packet-formats). Additional packets, such as binding signatures, are not required.
|
158
book/source/signatures.md
Normal file
|
@ -0,0 +1,158 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# OpenPGP Signatures
|
||||
|
||||
{term}`Signatures<OpenPGP Signature Packet>` are a fundamental mechanism within OpenPGP. They provide the syntax for forming and interpreting comprehensive statements about {term}`certificates<OpenPGP Certificate>` and their {term}`components<Component>`, as well as for ensuring the integrity and {term}`authenticity<Authentication>` of data.
|
||||
|
||||
Without {term}`signatures<OpenPGP Signature Packet>`, {term}`keys<Transferable Secret Key>` would remain unassociated with any {term}`certificate<OpenPGP Certificate>` or {term}`owner<Certificate Holder>`. {term}`Signatures<OpenPGP Signature Packet>` are crucial for binding {term}`component keys<Component Key>` and {term}`identity components<Identity Component>` into hierarchical {term}`certificates<OpenPGP Certificate>` and for establishing the {term}`authenticity<Authentication>` of messages.
|
||||
|
||||
## Terminology: "cryptographic signatures" and "signature packets"
|
||||
|
||||
Within OpenPGP, the term *{term}`signature<OpenPGP Signature Packet>`* can have two different meanings:
|
||||
|
||||
- **{term}`Cryptographic signature`**: a sequence of bytes created by {term}`cryptographic keys<Cryptographic Key>`, calculated according to a {term}`signature` scheme.
|
||||
|
||||
```{figure} plain_svg/cryptographic_signature.svg
|
||||
:name: fig-cryptographic-signature
|
||||
:alt: Depicts a box on white background. In the box, a green seal symbol with the word "sig" is shown on the left side, connected to the text "Cryptographic signature" by a black dotted line.
|
||||
|
||||
A {term}`cryptographic signature`
|
||||
```
|
||||
|
||||
- **{term}`OpenPGP signature packets<OpenPGP signature packet>`**: Defined in the [OpenPGP standard](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-signature-packet-type-id-2), these {term}`packets<Packet>` combine a raw {term}`cryptographic signature` along with a *{term}`type<OpenPGP Signature Type>`* designation and additional {term}`metadata`.
|
||||
|
||||
```{figure} plain_svg/OpenPGP_Signature_packet.svg
|
||||
:name: fig-signature-packet-0
|
||||
:alt: Depicts a box on white background. In the top, the text OpenPGP signature packet is connected to a dotted box. Inside a yellow box is shown. It has the title "signature metadata" and two lines of content, reading "signature type" and "additional metadata". The yellow box is labeled with the green cryptographic signature symbol. The green symbol is labeled with a dotted line and the text "Cyptographic signature" to its right. On the left side of the box, connected with a dotted line, a small cion-sized representation of the yellow signature packet and its green cryptographic signature are shown. This introducedthe equivalence of the two representations.
|
||||
|
||||
An "{term}`OpenPGP Signature Packet`"
|
||||
```
|
||||
|
||||
In this document, "{term}`signature<OpenPGP Signature Packet>`" will refer to {term}`OpenPGP signature packets<OpenPGP signature packet>`.
|
||||
|
||||
(signature-types)=
|
||||
## Signature types in OpenPGP
|
||||
|
||||
The OpenPGP standard defines a set of [Signature types](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-signature-types), each identified by a numerical *{term}`signature type ID`*. {term}`Signature types<OpenPGP Signature Type>` define the purpose of a {term}`signature packet<OpenPGP Signature Packet>` and how it should be interpreted.
|
||||
|
||||
{term}`Signature types<OpenPGP Signature Type>` can be predominantly classified in two ways:
|
||||
|
||||
- **{term}`Signatures over data<Data Signature>`**: These signatures are denoted by {term}`type IDs<Type ID>` `0x00` for binary documents and `0x01` for canonical text documents. The {term}`signer` uses these {term}`signatures<OpenPGP Signature Packet>` to claim ownership, assert creation, or certify the immutability of the document.
|
||||
- **{term}`Signatures on components<Signature On Component>`**: These are {term}`signatures<OpenPGP Signature Packet>` that are associated with {term}`component keys<Component Key>` or {term}`identity components<Identity Component>` of a {term}`certificate<OpenPGP Certificate>`.
|
||||
|
||||
{term}`Signatures on components<Signature On Component>` are a complex topic, and we discuss them in depth in [](/signing_components). They are grouped based on two criteria:
|
||||
|
||||
- the origin of the {term}`signature<OpenPGP Signature Packet>`, distinguishing between a {term}`self-signature` and a {term}`third-party signature`
|
||||
- the nature of the statement made by the {term}`signature<OpenPGP Signature Packet>`, such as certifying an {term}`identity` or binding {term}`component keys<Component Key>` into a {term}`certificate<OpenPGP Certificate>`
|
||||
|
||||
```{figure} img/mermaid/sig-types.png
|
||||
:name: fig-signature-types
|
||||
:alt: Depicts a diagram, describing different types of OpenPGP signatures. On the right hand side a long yellow box with the title "Signature Types and Targets" is shown, which contains signature type IDs and their names (in gray boxes) and further yellow boxes, grouping other types of signature type IDs. At the top the signature type ID "0x02 Standalone" is shown. Below, another yellow box groups the "Signature Packet"s "0x50 Third-Party Confirmation" and "0x40 Timestamp". Another box groups types of signatures, that apply to "Data" packets "0x00 Binary Data" and "0x01 Canonical Text". Below, a box groups types of signatures, that apply to "Primary Key + User ID/ Attr. Packet"s. The type IDs "0x10 Generic Certification", "0x11 Persona Certification", "0x12 Casual Certification" and "0x13 Positive Certification" are shown together in one gray box and "0x30 Certification Revocation" in another. Another yellow box groups types of signatures, that apply to "Primary Key" packets "0x1F Direct-Key Signature" and "0x20 Key Revocation". The last box groups types of signatures, that apply to "Primary + Subkey" packets. "0x18 Subkey Binding" and "0x19 Primary Key Binding" are shown together in one gray box, "0x28 Subkey Revocation" in another. On the left hand side of the diagram shows gray boxes identifying different types of signatures, with the most basic being "OpenPGP Signature" on the far left. With arrows it points to further signature types ("Signature on Data", "Signature on Component") and several signature type IDs ("0x02", "0x50" and "0x40"). The signature type "Signature on Data" points to "0x00" and "0x01". The signature type "Signature on Component" points to two more specific signature types, namely "Third-Party" and "Self-Signature". "Third-Party" points at the group of "0x10", "0x11", "0x12" and "0x13", as well as "0x30" and "0x1F". "Self-Signature" points at the group of "0x10", "0x11", "0x12" and "0x13", as well as "0x30", "0x1F", the group of "0x18" and "0x19" and finally "0x28".
|
||||
|
||||
An overview of {term}`signature types<OpenPGP Signature Type>` in OpenPGP
|
||||
```
|
||||
|
||||
This chapter will cover the overarching principles applicable to all {term}`OpenPGP signature types<OpenPGP Signature Type>`.
|
||||
|
||||
For more detail about specific {term}`types of signatures<OpenPGP Signature Type>`, see the chapters on [](/signing_data) and [](/signing_components), respectively.
|
||||
|
||||
## Structure of an OpenPGP signature packet
|
||||
|
||||
As outlined above, an {term}`OpenPGP signature<OpenPGP Signature Packet>` is a composite data structure, which combines:
|
||||
|
||||
- **{term}`Signature type ID`**: specifies the {term}`signature<OpenPGP Signature Packet>`'s intended meaning, as detailed above
|
||||
- **{term}`Metadata`**: varies based, in part, on the {term}`signature type ID`; mostly encoded as "{term}`subpackets<OpenPGP Signature Subpacket>`" (see {ref}`signature-subpackets`)
|
||||
- **Raw {term}`cryptographic signature`**
|
||||
|
||||
```{figure} plain_svg/OpenPGP_Signature_packet_2.svg
|
||||
:name: fig-signature-packet
|
||||
:alt: Depicts a diagram with the title "OpenPGP signature packet". A box with green dotted frame and white background provides the title "Signature", while inside the box the text reads "Signature over; Input data, Signature metadata". The words "Signature metadata" serve as title for a yellow box at the lower half of the signature type box. The yellow box also bears the green cryptographic signature symbol.
|
||||
|
||||
Structure and context of an {term}`OpenPGP signature packet`
|
||||
```
|
||||
|
||||
The input data packets differ between specific signature types. Also see {numref}`fig-signature-types`. For example:
|
||||
|
||||
- for a [*binary data signature*](data-signature-types), the input data packet is a *literal data packet*, while
|
||||
- for a [*subkey binding signature*](bind-subkey), the input data packets consist of a primary and a subkey packet.
|
||||
|
||||
### Creating an OpenPGP signature packet
|
||||
|
||||
Creating an {term}`OpenPGP signature packet` involves encoding a statement about a specific set of data within the {term}`packet`.
|
||||
|
||||
The input data of a {term}`signature packet<OpenPGP Signature Packet>` includes:
|
||||
|
||||
- **{term}`Packets<Packet>` being signed**: Typically one or more {term}`packets<Packet>`, though sometimes none, depending on the context. These are the {term}`packets<Packet>` to which the signature statement pertains.
|
||||
- **Data within the {term}`signature packet`**: This includes information that specifies the intent of the {term}`signature<OpenPGP Signature Packet>`.
|
||||
|
||||
The input data is determined by the {term}`signature type<OpenPGP Signature Type>` and consists of the exact content that the signature statement addresses.
|
||||
|
||||
The {term}`signature packet<OpenPGP Signature Packet>` consists of two parts:
|
||||
|
||||
1. **Statement definition**: This part of the {term}`packet` defines the meaning or intent of the {term}`signature<OpenPGP Signature Packet>`.
|
||||
2. **{term}`Cryptographic signature`**: This is the formal endorsement by the {term}`signer`, created as follows:
|
||||
- A {term}`hash digest` is calculated from the input data.
|
||||
- The {term}`cryptographic signature` is then calculated for this {term}`hash digest`.
|
||||
|
||||
```{figure} plain_svg/Signature_Creation.svg
|
||||
:name: fig-signature-creation
|
||||
:alt: Depicts a complex diagram with white background and the title "Signature creation". On the top left side a box with black frame and white background reads "Input Data packets, One or more packets". Below it the symbol of a signature packet is shown (however, instead of the green signature symbol, only a circle with white background and dotted frame is shown). Both are connected (via green dotted arrows) to a green, right pointing arrow symbol with green dotted frame and the title "Hash mechanism". Text above the green arrow symbol reads "A hash digest is calculated from the input data packets and the signature metadata". The "Hash mechanism" arrow points at a box with white background and green frame, which reads "hash digest". At the top right corner of the diagram the symbol for a component key with both public and private key and the title "Signer private key" is shown. Both hash digest and component key symbol point to a large green arrow symbol, with green dotted frame, at the lower right corner of the diagram, using green dotted arrow lines. The large arrow symbol has the title "Signing mechanism" and text overlaid across it reads "A cryptographic signature is calculated over the hash digest, using the private key material of the signer.". It points at a cryptographic signature symbol at the bottom of the diagram. The cryptographic signature symbol is connected (via a green dotted arrow line) to the circle with white background and dotted green frame in the signature packet symbol.
|
||||
|
||||
Creating a {term}`signature<OpenPGP Signature Packet>` in OpenPGP
|
||||
```
|
||||
|
||||
(signature-verify)=
|
||||
### Verifying an OpenPGP signature packet
|
||||
|
||||
Verifying an {term}`OpenPGP signature packet` is similar to its creation, with some crucial differences that facilitate the {term}`verification` by entities other than the {term}`signer`.
|
||||
|
||||
The main differences:
|
||||
|
||||
- **Access to {term}`public key<OpenPGP Certificate>`**: Unlike the creation process, which is exclusive to the {term}`signer`, {term}`verification` can be performed by anyone who has access to the {term}`public key<OpenPGP Certificate>` of the {term}`signer`.
|
||||
- **Use of {term}`signature verification` mechanism**:
|
||||
After calculating the {term}`hash digest` from the input data, a {term}`signature verification` mechanism is employed. This mechanism uses the {term}`hash digest`, the {term}`cryptographic signature` from the {term}`signature packet<OpenPGP Signature Packet>`, and the {term}`public key<OpenPGP Certificate>` of the {term}`signer`. Its purpose is to ascertain the cryptographic {term}`validity<Validation>` of the {term}`signature<OpenPGP Signature Packet>`.
|
||||
|
||||
```{figure} plain_svg/Signature_Verification.svg
|
||||
:name: fig-signature-verification
|
||||
:alt: Depicts a complex diagram with white background and the title "Signature verification". On the top left side a box with black frame and white background reads "Input Data packets, One or more packets". Below it the symbol of a signature packet is shown. Both are connected (via green dotted arrows) to a green, right pointing arrow symbol with green dotted frame and the title "Hash mechanism". Text above the green arrow symbol reads "A hash digest is calculated from the input data packates and the signature metadata". The "Hash mechanism" arrow points at a box with white background and green frame, which reads "hash digest". At the top right corner of the diagram the symbol for a component key with only public key and the title "Signer public key" is shown. Hash digest, component key symbol and the cryptographic signature symbol in the signature packet point to a large green arrow symbol, with green dotted frame, at the lower right corner of the diagram, using green dotted arrow lines. The large arrow symbol has the title "Signature verification mechanism" and text overlaid across it reads "A cryptographic signature is verified against the hash digest, using the public key of the signer.". It points at a success and fail symbol at the bottom of the diagram.
|
||||
|
||||
Verifying a {term}`signature<OpenPGP Signature Packet>` in OpenPGP
|
||||
```
|
||||
|
||||
(signature-subpackets)=
|
||||
## Signature subpackets
|
||||
|
||||
In the OpenPGP protocol, {term}`signature subpackets<OpenPGP Signature Subpacket>` enhance the expressiveness of a {term}`signature<OpenPGP Signature Packet>` beyond what is conveyed by just the bare {term}`cryptographic signature` and the {term}`signature type ID`. These {term}`subpackets<OpenPGP Signature Subpacket>`, introduced in [RFC 2440](https://datatracker.ietf.org/doc/html/rfc2440), are essential for embedding additional {term}`metadata` within {term}`signature packets<OpenPGP Signature Packet>`.
|
||||
|
||||
{term}`Signature subpackets<OpenPGP Signature subpacket>` serve as sub-elements within {term}`signature packets<OpenPGP Signature Packet>`, providing extra context and meaning to a {term}`signature<OpenPGP Signature Packet>`.
|
||||
They are formatted as key-value pairs, where the keys are defined as [subpacket type IDs](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-signature-subpacket-types-r) by the {term}`RFC`. The {term}`RFC` also provides the format and interpretation of the values.
|
||||
|
||||
### Examples of signature subpackets
|
||||
- The [*issuer fingerprint*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#issuer-fingerprint-subpacket) {term}`subpacket<OpenPGP Signature Subpacket>` encodes the {term}`fingerprint<OpenPGP Fingerprint>` of the {term}`component key` that issued the {term}`signature<OpenPGP Signature Packet>`.
|
||||
- The [*key flags*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-key-flags) {term}`subpacket<OpenPGP Signature Subpacket>` defines the {term}`capabilities<capability>` that are assigned to a {term}`component key` within a {term}`certificate<OpenPGP Certificate>`.
|
||||
|
||||
(subpacket-areas)=
|
||||
### Hashed and unhashed signature subpackets
|
||||
|
||||
{term}`Signature subpackets<OpenPGP Signature Subpacket>` within OpenPGP can reside in one of two distinct areas of a {term}`signature packet<OpenPGP Signature Packet>`, each serving a different purpose.
|
||||
|
||||
- **{term}`Hashed area`**: {term}`Hashed subpackets<Hashed Subpacket>` are included in the {term}`hash digest` of the {term}`signature<OpenPGP Signature Packet>` and are thus covered by its {term}`cryptographic signature`. They reliably express the {term}`signer`'s intent.
|
||||
- **{term}`Unhashed area`**: {term}`Unhashed subpackets<Unhashed Subpacket>`, conversely, are not included in the {term}`hash digest` for the {term}`signature<OpenPGP Signature Packet>`. They are thus not protected against tampering and can be used to retroactively add, change, or remove metadata in a {term}`signature packet<OpenPGP Signature Packet>` without affecting its {term}`validity<Validation>`. They are primarily used for advisory purposes or in scenarios where the integrity of the {term}`subpacket` content can be self-authenticated. An example is the {term}`issuer fingerprint subpacket`, which can be {term}`validated<Validation>` through successful {term}`signature verification` using the referenced {term}`issuer key`.
|
||||
|
||||
The majority of {term}`signature subpackets<OpenPGP Signature Subpacket>` are stored in the {term}`hashed area`.
|
||||
|
||||
For detailed information and specifications, refer to [Hashed vs. Unhashed Subpackets](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-hashed-vs-unhashed-subpacke) in the OpenPGP {term}`RFC`.
|
||||
|
||||
(criticality-of-subpackets)=
|
||||
### Criticality of subpackets
|
||||
|
||||
In the OpenPGP protocol, each {term}`signature subpacket<OpenPGP Signature Subpacket>` can be marked with a *{term}`criticality flag`*. This flag plays a pivotal role in the interpretation and {term}`validation` of the {term}`signature<OpenPGP Signature Packet>`. When set, it instructs any receiving {term}`implementation<OpenPGP Implementation>` encountering an unrecognized {term}`subpacket type<OpenPGP Signature Subpacket Type>` to treat this as a significant error and to {term}`invalidate<Validation>` the {term}`signature<OpenPGP Signature Packet>`.
|
||||
|
||||
This mechanism accounts for different {term}`OpenPGP implementations<OpenPGP Implementation>` that may support only certain subsets of the standard. Moreover, it anticipates the evolution of the standard, including the addition of new {term}`subpacket types<OpenPGP Signature Subpacket Type>`.
|
||||
|
||||
Consider a scenario where an {term}`implementation<OpenPGP Implementation>` does not recognize a {term}`subpacket<OpenPGP Signature Subpacket>` indicating {term}`signature<OpenPGP Signature Packet>` {term}`expiration`. Without understanding this concept, the {term}`implementation<OpenPGP Implementation>` might erroneously accept an already {term}`expired<Expiration>` {term}`signature<OpenPGP Signature Packet>`. By marking the {term}`signature expiration time subpacket` as {term}`critical<Criticality Flag>`, the creator of the {term}`signature<OpenPGP Signature Packet>` ensures that any recipient who cannot process this {term}`subpacket<OpenPGP Signature Subpacket>` will reject the {term}`signature<OpenPGP Signature Packet>` as {term}`invalid<Validation>`.
|
||||
|
||||
For specific guidelines on which {term}`subpackets<OpenPGP Signature Subpacket>` should be marked as {term}`critical<Criticality Flag>`, refer to the {term}`RFC` sections [5.2.3.11](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-signature-creation-time) to [5.2.3.36](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-intended-recipient-fingerpr).
|
277
book/source/signing_components.md
Normal file
|
@ -0,0 +1,277 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Signatures on components
|
||||
|
||||
This chapter examines {term}`OpenPGP signatures<OpenPGP Signature Packet>` associated with {term}`certificate components<Component>`, applying to:
|
||||
|
||||
- {term}`component keys<Component Key>`, encompassing {term}`primary keys<OpenPGP Primary Key>` and {term}`subkeys<OpenPGP Subkey>`
|
||||
- {term}`identity components<Identity Component>`, namely {term}`User IDs<User ID>` and {term}`User attributes<User attribute>`
|
||||
|
||||
{term}`Signatures on components<Signature On Component>` are used to construct and maintain {term}`certificates<OpenPGP Certificate>`, and to model the {term}`authentication` of {term}`identities<Identity>`.
|
||||
|
||||
This chapter expands on topics introduced in the [](certificates) chapter.
|
||||
|
||||
## Self-signatures vs third-party signatures
|
||||
|
||||
{term}`Component signatures<Signature On Component>` in OpenPGP are categorized into two distinct types:
|
||||
|
||||
- **{term}`self-signatures<Self-Signature>`**, which are issued by the {term}`certificate holder` using the {term}`certificate<OpenPGP Certificate>`'s {term}`primary key<OpenPGP Primary Key>`
|
||||
- **{term}`third-party signatures<Third-party Signature>`**, which are issued by an external entity, not the {term}`certificate holder`
|
||||
|
||||
(self-signatures)=
|
||||
### Self-signatures
|
||||
|
||||
{term}`Self-signatures<Self-signature>` are fundamental in creating and managing {term}`OpenPGP certificates<OpenPGP Certificate>`. They bind the various {term}`components<Component>` of a {term}`certificate<OpenPGP Certificate>` into one combined data structure and facilitate the {term}`certificate<OpenPGP Certificate>`'s {term}`life-cycle management`.
|
||||
|
||||
{term}`Life-cycle management` operations include:
|
||||
|
||||
- {term}`binding<Binding Signature>` additional {term}`components<Component>` to a {term}`certificate<OpenPGP Certificate>`
|
||||
- modifying {term}`expiration time` or other {term}`metadata` of `components<Component>`
|
||||
- revoking, and thus invalidating, {term}`components<Component>` or existing {term}`self-signatures<Self-signature>`
|
||||
|
||||
{term}`Self-signatures<Self-signature>` are issued by the {term}`certificate's owner<Certificate Holder>` using the {term}`certificate<OpenPGP Certificate>`'s {term}`primary key<OpenPGP Primary Key>`.
|
||||
|
||||
```{note}
|
||||
No [key flag](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-key-flags) is required to issue {term}`self-signatures<Self-signature>`. An {term}`OpenPGP primary key` can issue {term}`self-signatures<Self-signature>` by default.
|
||||
```
|
||||
|
||||
### Third-party signatures
|
||||
|
||||
{term}`Third-party signatures<Third-party Signature>` are pivotal in OpenPGP for decentralized {term}`authentication`, forming the basis of the {term}`Web of Trust`. They encode {term}`authentication`-related statements about {term}`certificates<OpenPGP Certificate>` and linked {term}`identities<Identity>`, establishing trustworthiness of {term}`identity claims<Identity Claim>`.
|
||||
|
||||
Third-party signatures are used to make specific statements:
|
||||
|
||||
- {term}`certifying<Certification>` {term}`identity claims<Identity Claim>`
|
||||
- {term}`delegating<Delegation>` {term}`authentication` decisions
|
||||
- {term}`revoking<Revocation>`, and thus {term}`invalidating<Validation>`, prior {term}`third-party signature` statements
|
||||
|
||||
```{note}
|
||||
The **{term}`certify others<Certification Key Flag>`** [key flag](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-key-flags) (`0x01`) is required to issue {term}`third-party signatures<Third-party Signature>`. By convention[^primary-certification], only the {term}`certificate<OpenPGP Certificate>`'s {term}`primary key<OpenPGP Primary Key>` can hold this {term}`key flag`.
|
||||
```
|
||||
|
||||
[^primary-certification]: Most current {term}`implementations<OpenPGP Implementation>` assume that only the {term}`primary key<OpenPGP Primary Key>` may hold the *{term}`certify others<Certification Key Flag>`* {term}`key flag`, although this is not specified in the {term}`RFC`.
|
||||
|
||||
### Distinct functions of self-signatures and third-party signatures
|
||||
|
||||
The meaning of an {term}`OpenPGP signature<OpenPGP Signature Packet>` depends significantly on its {term}`issuer`. {term}`Self-signatures<Self-signature>` and {term}`third-party signatures<Third-party Signature>`, even when of the same [signature type](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-signature-types), serve distinct functions. For example:
|
||||
|
||||
- {term}`Certifying self-signatures<Certifying Self-signature>` ({term}`type IDs<Signature Type ID>` `0x10` - `0x13`) bind a {term}`User ID` to a {term}`certificate<OpenPGP Certificate>`.
|
||||
- {term}`Third-party signatures<Third-party Signature>` of the same {term}`type IDs<Signature Type ID>` endorse the {term}`authenticity<Authentication>` of a {term}`User ID` on another user's {term}`certificate<OpenPGP Certificate>`.
|
||||
|
||||
In another instance:
|
||||
|
||||
- *When issued as a {term}`self-signature`*, a [direct key signature](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-direct-key-signature-type-i) sets {term}`preferences<Algorithm Preferences>` and advertises {term}`features<Features Subpacket>` applicable to the entire {term}`certificate<OpenPGP Certificate>`.
|
||||
- *When issued by a {term}`third party<Third-party Signature>`*, especially when it carries a [trust signature](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-trust-signature) {term}`subpacket<OpenPGP Signature Subpacket>`, a similar {term}`direct key signature` {term}`delegates<Delegation>` trust to the signed {term}`certificate<OpenPGP Certificate>`. This may designate the signed {term}`certificate<OpenPGP Certificate>` as a {term}`trust anchor` within the {term}`issuer`'s {term}`Web of Trust`.
|
||||
|
||||
(binding-signatures)=
|
||||
## Self-signatures in certificate formation and management
|
||||
|
||||
{term}`Self-signatures<Self-signature>` play a crucial role in forming and managing the structure of {term}`OpenPGP certificates<OpenPGP certificate>`. These act as *{term}`binding signatures<Binding Signature>`*, joining {term}`components<Component>` and embedding {term}`metadata`.
|
||||
|
||||
Internally, an {term}`OpenPGP certificate` is essentially a series of {term}`packets<Packet>` strung sequentially. When a {term}`certificate<OpenPGP Certificate>` is stored in a file format known as a [transferable public key](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-transferable-public-keys), {term}`packets<Packet>` can be easily added or removed.
|
||||
|
||||
To safeguard against unauthorized additions or alterations of {term}`components<Component>`, OpenPGP uses {term}`cryptographic signatures<Cryptographic Signature>`. These validate that all {term}`components<Component>`, such as {term}`subkeys<OpenPGP Subkey>` or [identity components](identity-components), were linked to the {term}`OpenPGP certificate` by its {term}`owner<Certificate Holder>`, using the {term}`primary key<OpenPGP Primary Key>`. While anyone can still store unrelated elements to a {term}`certificate<OpenPGP Certificate>` dataset, {term}`OpenPGP implementations<OpenPGP implementation>` will reject them if they lack a {term}`valid<Validation>` cryptographic connection with the {term}`certificate<OpenPGP Certificate>`.
|
||||
|
||||
```{note}
|
||||
Conversely, omissions of {term}`packets<Packet>` by third parties can easily occur when handling an {term}`OpenPGP certificate` dataset. This could pose a challenge, for example, when an attacker deliberately omits {term}`revocation` {term}`packets<Packet>`. Without access to an alternative, complete {term}`certificate<OpenPGP Certificate>` source, recipients might not detect these omissions.
|
||||
```
|
||||
|
||||
However, there are legitimate instances in which third parties add "unbound" {term}`packets<Packet>` (i.e., not signed by the {term}`certificate<OpenPGP Certificate>`'s {term}`owner<Certificate Holder>`) to a {term}`certificate<OpenPGP Certificate>`:
|
||||
|
||||
- [Third-party certifications](third-party-certifications) are often stored within the {term}`packet` data of the {term}`certificate<OpenPGP Certificate>` to which they are related. This is a standard practice that provides convenience for users by allowing easy access to all relevant {term}`certifications<Certification>`. (See [](keyserver-flooding) for discussion of a related pitfall.)
|
||||
- {term}`OpenPGP software<OpenPGP Implementation>` may locally add [unbound identity data](unbound-user-ids) to a {term}`certificate<OpenPGP Certificate>`.
|
||||
|
||||
(bind-subkey)=
|
||||
### Binding subkeys to a certificate
|
||||
|
||||
{term}`Subkeys<OpenPGP Subkey>` are linked to {term}`OpenPGP certificates<OpenPGP certificate>` via a [subkey binding signature](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#sigtype-subkey-binding) ({term}`type ID<Signature Type ID>` `0x18`). This {term}`signature type<OpenPGP Signature Type>` indicates the association of the {term}`primary key<OpenPGP Primary Key>` with the {term}`subkey<OpenPGP Subkey>`.
|
||||
|
||||
A {term}`subkey binding signature` binds a {term}`subkey<OpenPGP Subkey>` to a {term}`primary key<OpenPGP Primary Key>`, and it embeds {term}`metadata` into the {term}`signature packet<OpenPGP Signature Packet>`. Once generated, the {term}`subkey binding signature` {term}`packet` is stored in the {term}`certificate<OpenPGP Certificate>` directly after the {term}`subkey<OpenPGP Subkey>` it binds.
|
||||
|
||||
{term}`Subkeys<OpenPGP Subkey>` designated for signing purposes, identified by the *{term}`signing<Signing Key Flag>`* [key flag](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-key-flags), represent a unique category and are handled differently. See {numref}`bind-signing-subkey`.
|
||||
|
||||
```{figure} plain_svg/subkey_binding_signature.svg
|
||||
:name: fig-subkey-binding-signature
|
||||
:alt: Depicts a diagram on white background with the title "Subkey binding signature". At the top left the symbol of a primary component key with certification capability is shown. At the bottom left the symbol of a component key with encryption capability is shown. The primary component key points at the lower component key with a full green arrow line. In the middle of the connection the small symbol of a signature packet is shown. On the right side of the diagram a detailed version of the signature packet can be found in a box with the title "Subkey binding signature". The text reads "Signature over Primary key, Subkey" and the box with "Signature metadata" contains the list "signature creation time", "key expiration time", "key flags" and "issuer fingerprint". The primary component key points at the detailed signature packet with a dotted green arrow line and the text "Primary key creates a subkey binding signature to bind the subkey to the primary key".
|
||||
|
||||
Linking an {term}`OpenPGP subkey` to the {term}`primary key<OpenPGP Primary Key>` with a {term}`binding signature`
|
||||
```
|
||||
|
||||
{term}`Metadata` for the {term}`subkey`, such as the [*key expiration time*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#key-expiration-subpacket) and {term}`capabilities<Capability>` set by [*key flags*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#key-flags), are included in {term}`subpackets<OpenPGP Signature Subpacket>` within the {term}`subkey binding signature` {term}`packet`.
|
||||
|
||||
```{note}
|
||||
The {term}`validity<Validation>` of a {term}`subkey<OpenPGP Subkey>` is intrinsically linked to that of the {term}`primary key<OpenPGP Primary Key>`. An {term}`expired<Expiration>` {term}`primary key<OpenPGP Primary Key>` renders any associated {term}`subkey<OpenPGP Subkey>` {term}`invalid<Validation>`, regardless of the {term}`subkey<OpenPGP Subkey>`'s own {term}`expiration` setting.
|
||||
|
||||
Legally, a {term}`subkey<OpenPGP Subkey>` may not have a specified {term}`expiration time`. In such cases, its {term}`expiration` aligns implicitly with that of the {term}`primary key<OpenPGP Primary Key>`. Additionally, the {term}`creation time` of a {term}`subkey<OpenPGP Subkey>` must always be more recent than that of the {term}`primary key<OpenPGP Primary Key>`.
|
||||
```
|
||||
|
||||
(bind-signing-subkey)=
|
||||
### Special case: Binding signing subkeys
|
||||
|
||||
{term}`Binding` {term}`subkeys<OpenPGP Subkey>` that possess the *{term}`signing<Signing Key Flag>`* {term}`key flag` to a {term}`certificate<OpenPGP Certificate>` represents a unique scenario. While similar to the {term}`binding process<Binding>` of other {term}`subkeys<OpenPGP Subkey>`, there is an additional, critical requirement: mutual association.
|
||||
|
||||
That is, to bind a {term}`signing-capable<Signing Key Flag>` {term}`subkey<OpenPGP Subkey>` to a {term}`primary key<OpenPGP Primary Key>`, it is insufficient that the "{term}`primary key<OpenPGP Primary Key>` wants to be associated with the {term}`subkey<OpenPGP Subkey>`." The {term}`subkey<OpenPGP Subkey>` must explicitly signal that it "wants to be associated with the {term}`primary key<OpenPGP Primary Key>`."
|
||||
|
||||
This mutual binding is crucial for security. Without it, an individual (e.g., Alice) could falsely claim a connection to another person's (e.g., Bob's) {term}`signing subkey<OpenPGP Signing Subkey>`.
|
||||
Alice could thus claim to have issued {term}`signatures<OpenPGP Signature Packet>` which were actually issued by Bob.
|
||||
To prevent such scenarios, where an attacker might wrongfully "adopt" a victim's {term}`signing subkey<OpenPGP Signing Subkey>`, a dual-layer of {term}`signatures<OpenPGP Signature Packet>` is used:
|
||||
|
||||
- the [subkey binding signature](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#sigtype-subkey-binding) ({term}`type ID<Signature Type ID>` `0x18`), which is issued by the {term}`certificate<OpenPGP Certificate>`'s {term}`primary key<OpenPGP Primary Key>`
|
||||
- the [primary key binding signature](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#sigtype-primary-binding) ({term}`type ID<Signature Type ID>` `0x19`), created by the {term}`subkey<OpenPGP Subkey>` itself. This is informally known as an embedded "{term}`back signature<Primary Key Binding Signature>`," because the {term}`subkey<OpenPGP Subkey>`'s {term}`signature<OpenPGP Signature Packet>` points back to the {term}`primary key<OpenPGP Primary Key>`.
|
||||
|
||||
```{figure} plain_svg/subkey_binding_signatur_for_signing_sk.svg
|
||||
:name: fig-subkey-binding-signature-for-signing-subkeys
|
||||
:alt: Depicts a diagram on white background with the title "Subkey binding signature for signing subkeys". At the top left the symbol of a primary component key with certification capability is shown. At the bottom left the symbol of a component key with signing capability is shown. The primary component key points at the lower component key with a full green arrow line. In the middle of the connection the small symbol of a signature packet is shown. On the right side of the diagram a detailed version of the signature packet can be found in a box with the title "Subkey binding signature". The text reads "Signature over Primary key, Subkey" and the box with "Signature metadata" in it contains the list "signature creation time", "key expiration time", "key flags" and "issuer fingerprint". Within the signature metadata a box with a green dotted frame extends the list with an inlined signature packet with the title "Embedded Signature; Primary key binding". Its inner text reads "Signature over Primary Key, Signing Subkey". The signature metadata area of this embedded signature holds the list "signature creation time" and "issuer fingerprint". The cryptographic signature symbol overlaps both metadata and general section of the embedded signature. From the signing component key a green dotted arrow line points to the embedded signature in the subkey binding signature with the text "Signing key creates a primary binding signature to associate itself with the primary key" ("primary binding signature" in bold). At the top of the diagram, the primary component key points at the detailed signature packet with a dotted green arrow line and the text "Primary key creates a subkey binding signature to bind the subkey to the primary key".
|
||||
|
||||
Linking an {term}`OpenPGP signing subkey` to the {term}`primary key<OpenPGP Primary Key>` with a {term}`binding signature`, and an embedded {term}`primary key binding signature`
|
||||
```
|
||||
|
||||
The {term}`back signature<Primary Key Binding Signature>` signifies the mutuality of the {term}`subkey<OpenPGP Subkey>`'s association with the {term}`primary key<OpenPGP Primary Key>` and is embedded as {term}`subpacket<OpenPGP Signature Subpacket>` data within the {term}`subkey binding signature`, reinforcing the {term}`authenticity<Authentication>` of the {term}`binding`.
|
||||
|
||||
(bind-identity)=
|
||||
### Binding identities to a certificate
|
||||
|
||||
{term}`Self-signatures<Self-signature>` also play a vital role in {term}`binding` {term}`identity components<Identity Component>`, such as {term}`User IDs<User ID>` or {term}`User Attributes<User Attribute>`, to an {term}`OpenPGP certificate`.
|
||||
|
||||
To bind the {term}`User ID` `Alice Adams <alice@example.org>` to her {term}`OpenPGP certificate` (`AAA1 8CBB 2546 85C5 8358 3205 63FD 37B6 7F33 00F9 FB0E C457 378C D29F 1026 98B3`), Alice would use a {term}`certification signature<Certifying Self-signature>`.
|
||||
|
||||
There are four types of *{term}`certifying self-signature`*. The most commonly used {term}`type<Signature Type ID>` for {term}`binding` {term}`User IDs<User ID>` is the [positive certification](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#sigtype-positive-cert) ({term}`type ID<Signature Type ID>` `0x13`). Alternatively, {term}`type<Signature Type ID>` `0x10`, `0x11`, or `0x12` might be used. This {term}`binding signature` must be issued by the {term}`primary key<OpenPGP Primary Key>`.
|
||||
|
||||
The {term}`certifying self-signature` {term}`packet` – calculated over the {term}`primary key<OpenPGP Primary Key>`, {term}`User ID`, and {term}`metadata` of the {term}`signature packet<OpenPGP Signature Packet>` – is added to the {term}`certificate<OpenPGP Certificate>`, directly following the {term}`User ID` {term}`packet`.
|
||||
|
||||
```{figure} plain_svg/user_id_certification.svg
|
||||
:name: fig-user-id-certification
|
||||
:alt: Depicts a diagram on white background with the title "User ID binding signature". At the top left the symbol of a primary component key with certification capability is shown. At the bottom left the symbol of a User ID reads "Alice Adams <alice@example.org>". The primary component key points at the User ID with a full green arrow line. In the middle of the connection the small symbol of a signature packet is shown. On the right side of the diagram a detailed version of the signature packet can be found in a box with the title "User ID binding signature". The text reads "Signature over Primary key, User ID" and the box with "Signature metadata" in it contains the list "signature creation time", "key expiration time", "primary User ID flag", "algorithm preferences", "key expiration time (primary key)" and "key flags (primary key)". At the top of the diagram, the primary component key points at the detailed signature packet with a dotted green arrow line and the text "Primary key creates a User ID binding signature to associate the User ID with the primary key".
|
||||
|
||||
Linking a {term}`User ID` to an {term}`OpenPGP certificate`
|
||||
```
|
||||
|
||||
(primary-metadata)=
|
||||
### Adding global metadata to a certificate
|
||||
|
||||
The {term}`signatures<OpenPGP Signature Packet>` that {term}`bind<Binding>` {term}`subkeys<OpenPGP Subkey>` and {term}`identity components<Identity Component>` to a {term}`certificate<OpenPGP Certificate>` serve dual purposes: linking {term}`components<Component>` to the {term}`certificate<OpenPGP Certificate>` and adding {term}`metadata` to {term}`components<Component>`.
|
||||
|
||||
While it is essential to add {term}`metadata` that pertains to the entire {term}`certificate<OpenPGP Certificate>`, this does not require {term}`binding` any {term}`component` to the {term}`certificate<OpenPGP Certificate>`. In this case, the {term}`signature<OpenPGP Signature Packet>` mechanism is used just to associate {term}`metadata` with the {term}`certificate<OpenPGP Certificate>` globally.
|
||||
|
||||
Two {term}`signature types<OpenPGP Signature Type>` can perform this function:
|
||||
|
||||
- {term}`direct key signature` on the {term}`primary key<OpenPGP Primary Key>`
|
||||
- {term}`primary User ID binding signature`
|
||||
|
||||
The types of {term}`metadata` typically associated with the {term}`certificate<OpenPGP Certificate>` through these methods include:
|
||||
|
||||
- {term}`key` {term}`expiration`
|
||||
- {term}`key flags<Key Flag>` ({term}`capabilities<Capability>`)
|
||||
- {term}`features<Features Subpacket>`
|
||||
- {term}`algorithm preferences` signaling
|
||||
|
||||
(direct-key-signature)=
|
||||
#### Direct key signature
|
||||
|
||||
A [*direct key signature*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-direct-key-signature-type-i) serves as the [preferred mechanism](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#section-5.2.3.10-9) in OpenPGP v6 for defining {term}`metadata` for the entire {term}`certificate<OpenPGP Certificate>`, by associating it with the {term}`primary key<OpenPGP Primary Key>`.
|
||||
|
||||
(primary-user-id-binding)=
|
||||
#### Self-signature binding to primary User ID
|
||||
|
||||
In OpenPGP v4, another mechanism was often used for {term}`metadata` management: integrating global {term}`certificate<OpenPGP Certificate>` {term}`metadata` within a {term}`User ID binding signature`. This is specifically evident in the {term}`binding signature` of the [*primary* User ID](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-primary-user-id) of the {term}`OpenPGP certificate`.
|
||||
|
||||
This method results in the {term}`primary User ID binding signature` containing a mix of {term}`metadata`: some specific to that {term}`User ID` and some applicable to the {term}`certificate` globally.
|
||||
|
||||
Given the widespread adoption of this mechanism in existing {term}`OpenPGP certificates<OpenPGP certificate>`, it is crucial that {term}`OpenPGP applications<OpenPGP Implementation>` recognize and manage it.
|
||||
|
||||
(self-revocations)=
|
||||
### Revocation self-signatures: Invalidating certificate components
|
||||
|
||||
{term}`Revocation self-signatures<Revocation Self-signature>` represent an important class of {term}`self-signatures<Self-signature>`, used primarily to invalidate {term}`components<Component>` or retract prior {term}`signature<OpenPGP Signature Packet>` statements.
|
||||
|
||||
There are several types of {term}`revocation signatures<Revocation Self-signature>`, each serving a specific purpose:
|
||||
|
||||
- A [**key revocation signature**](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-key-revocation-signature-ty) ({term}`type ID<Signature Type ID>` `0x20`) marks a {term}`primary key<OpenPGP Primary Key>` as {term}`revoked<Revocation>`.
|
||||
- A [**subkey revocation signature**](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-subkey-revocation-signature) ({term}`type ID<Signature Type ID>` `0x28`) {term}`invalidates<Validation>` the {term}`binding of a subkey<Subkey Binding Signature>`.
|
||||
- A [**certification revocation**](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-certification-revocation-si) ({term}`type ID<Signature Type ID>` `0x30`) {term}`invalidates<Validation>` the {term}`binding` of a {term}`User ID` or {term}`User Attribute`.
|
||||
|
||||
Common scenarios for using {term}`revocations<Revocation>` include marking {term}`certificates<OpenPGP Certificate>` or individual {term}`subkeys<OpenPGP Subkey>` as unusable (e.g., when the {term}`private key<Transferable Secret Key>` has been compromised or replaced) or declaring {term}`User IDs<User ID>` as no longer {term}`valid<Validation>`.
|
||||
|
||||
```{note}
|
||||
{term}`OpenPGP certificates<OpenPGP Certificate>` act as append-only data structures in practice. Once elements of a {term}`certificate<OpenPGP Certificate>` are published, they cannot be removed from {term}`key servers<Key Server>` or third-party OpenPGP systems. {term}`Implementations<OpenPGP Implementation>` usually merge all available {term}`components<Component>` and {term}`signatures<OpenPGP Signature Packet>`.
|
||||
|
||||
{term}`Revocations<Revocation>` are used to mark {term}`components<Component>` or {term}`signatures<OpenPGP Signature Packet>` as {term}`invalid<Validation>`.
|
||||
```
|
||||
|
||||
Note: {term}`certification signatures<Certification>` [can be made irrevocable](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-revocable).
|
||||
|
||||
(hard-vs-soft-revocations)=
|
||||
#### Hard vs soft revocations
|
||||
|
||||
{term}`Revocation signatures<Revocation Self-signature>` often include a [*Reason for Revocation* subpacket](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-reason-for-revocation), with a code specifying *why* the {term}`revocation<Revocation Self-signature>` was issued. This code determines whether the {term}`revocation` is considered *soft* or *hard*.
|
||||
|
||||
- **{term}`Soft revocation`**: This is typically used for graceful or planned {term}`invalidation<Validation>` of {term}`components<Component>`, such as retiring or updating {term}`components<Component>`. It {term}`invalidates<Validation>` the {term}`component` from the {term}`revocation signature<Revocation Self-signature>`'s {term}`creation time`, but earlier uses remain {term}`valid<Validation>`. Soft revocations can be reversed with a new {term}`self-signature`.
|
||||
- **{term}`Hard revocation`**: This irrevocably invalidates the {term}`component`, affecting all past and future uses. It is typically used to signal compromise of {term}`secret key material<Private Key Material>`.
|
||||
|
||||
```{note}
|
||||
A {term}`revocation signature packet` lacking a {term}`Reason for Revocation subpacket` is interpreted as a {term}`hard revocation`.
|
||||
```
|
||||
|
||||
(third-party-certifications)=
|
||||
## Authentication and delegation in third-party signatures
|
||||
|
||||
{term}`Third-party signatures<Third-party Signature>` in OpenPGP primarily encode {term}`authentication` statements for {term}`identities<Identity>` and {term}`delegate<Delegation>` trust decisions. These {term}`signatures<OpenPGP Signature Packet>` can be manually inspected or processed as machine-readable artifacts by {term}`OpenPGP software<OpenPGP Implementation>`, which evaluates the {term}`authenticity<Authentication>` of {term}`certificates<OpenPGP Certificate>` based on user-specified {term}`trust anchors<Trust Anchor>`.
|
||||
|
||||
### Certifying identity components
|
||||
|
||||
When a {term}`signer` issues a {term}`certifying signature<Certification>` on an {term}`identity`, it indicates a verified link between the {term}`identity` and the {term}`certificate<OpenPGP Certificate>`. That is, the {term}`signer` vouches for the {term}`identity claim`.
|
||||
|
||||
For example, Alice can vouch that Bob's {term}`User ID` `Bob Baker <bob@example.com>` is legitimately linked with his {term}`certificate<OpenPGP Certificate>` `BB28 9FB7 A68D BFA8 C384 CCCD E205 8E02 D9C6 CD2F 3C7C 56AE 7FB5 3D97 1170 BA83`, by creating a {term}`certification signature<Certification>`. Bob can then distribute Alice's `certifying signature<Certification>` as part of his {term}`certificate<OpenPGP Certificate>`.
|
||||
|
||||
Other users may or may not decide to rely on Alice's statement to determine the {term}`authenticity<Authentication>` of Bob's {term}`certificate<OpenPGP Certificate>`.
|
||||
|
||||
(delegation)=
|
||||
### Trust signatures: delegating authentication
|
||||
|
||||
OpenPGP uses [*trust signature*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#trust-signature-subpacket) {term}`subpackets<OpenPGP Signature Subpacket>` to {term}`delegate<Delegation>` {term}`authentication` decisions, designating the recipient {term}`certificate<OpenPGP Certificate>` as a "{term}`trusted introducer`" (or a {term}`trust anchor`) for the user. This includes specifying {term}`trust depth` (or level) for transitive {term}`delegations<Delegation>` and quantifying trust with numerical values, indicating the extent of reliance on the {term}`introducer<Trusted Introducer>`'s {term}`certifications<Certification>`.
|
||||
|
||||
{term}`Trust signature` {term}`subpackets<OpenPGP Signature Subpacket>` are applicable in {term}`third-party signatures<Third-party Signature>`, more specifically:
|
||||
|
||||
- {term}`identity certification signatures<Identity Certification>` ({term}`type ID<Signature Type ID>` `0x10` - `0x13`)
|
||||
- [direct key signatures](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-direct-key-signature-type-i) ({term}`type ID<Signature Type ID>` `0x1F`)
|
||||
|
||||
(trust-level)=
|
||||
#### Trust depth/level
|
||||
|
||||
The "{term}`trust depth`" (or {term}`level<Trust Depth>`) in OpenPGP signifies the extent of transitive {term}`delegation` within the {term}`authentication` process. It determines how far a {term}`delegation` can be extended from the original {term}`trusted introducer` to subsequent intermediaries. Essentially, a {term}`certificate<OpenPGP Certificate>` with a {term}`trust depth` of more than one acts as a "{term}`meta-introducer`," facilitating {term}`authentication` decisions across multiple levels in the network.
|
||||
|
||||
A {term}`trust depth` of 1 means relying on {term}`certifications<Certification>` made directly by the {term}`trusted introducer`. The user's OpenPGP software will accept {term}`certifications<Certification>` made directly by the {term}`introducer<Trusted Introducer>` for {term}`authenticating<Authentication>` identities.
|
||||
|
||||
However, when the {term}`trust depth` is set higher, it implies a chain of {term}`delegation` may extend beyond the {term}`initial introducer`. The user's software will recognize and accept {term}`certifications<Certification>` made not only by the {term}`primary introducer<Initial Introducer>` but also by other intermediaries whom the {term}`primary introducer<Initial Introducer>` designated as {term}`trusted introducers<Trusted Introducer>`.
|
||||
|
||||
This allows for a more extensive network of trusted {term}`certifications<Certification>`, enabling a broader and more interconnected {term}`Web of Trust`.
|
||||
|
||||
(trust-amount)=
|
||||
#### Trust amounts
|
||||
|
||||
The "{term}`trust amount`," with a numerical value ranging from `0` to `255`, quantifies the degree of reliance on a {term}`delegation`.
|
||||
|
||||
A higher value indicates greater degree of reliance. This quantification aids {term}`OpenPGP software<OpenPGP Implementation>` in determining an aggregate amount of reliance, based on combined {term}`certifications<Certification>` from multiple {term}`trusted introducers<Trusted Introducer>`.
|
||||
|
||||
(trust-scope)=
|
||||
#### Limiting delegation scope
|
||||
|
||||
When using *{term}`trust signature`* {term}`subpackets<OpenPGP Signature Subpacket>`, a {term}`delegation` can be limited to {term}`identities<Identity>` that match a [*regular expression*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#regex-subpacket).
|
||||
|
||||
With this mechanism, for example, it is possible to {term}`delegate<Delegation>` {term}`authentication` decisions only for {term}`User IDs<User ID>` that match the email domain of an organization.
|
||||
|
||||
(wot)=
|
||||
### Web of Trust: Decentralized trust decisions
|
||||
|
||||
The {term}`Web of Trust` in OpenPGP is a {term}`trust model` that facilitates {term}`authentication` decisions through a network of {term}`certifications<Certification>` and {term}`delegations<Delegation>`. It is characterized by a so-called [strong set](https://en.wikipedia.org/wiki/Web_of_trust#Strong_set), which refers to a group of {term}`certificates<OpenPGP Certificate>` that are robustly interconnected via `third-party certifications<Third-party Identity Certification>`.
|
||||
|
||||
In this model, users independently {term}`delegate<Delegation>` {term}`authentication` decisions, choosing whose {term}`certification` to rely on. This {term}`delegation` is based on the {term}`certificates<OpenPGP Certificate>` and {term}`third-party signatures<Third-party Signature>` available to them, with their {term}`OpenPGP software<OpenPGP Implementation>` applying the {term}`Web of Trust` mechanism to discern the reliability of each {term}`certificate<OpenPGP Certificate>` for an {term}`identity`.
|
||||
|
||||
The {term}`OpenPGP RFC<RFC>` doesn't specify exactly how {term}`Web of Trust` calculations are performed. It only defines the data formats on which these calculations can be performed.
|
||||
|
||||
### Revoking third-party signatures
|
||||
|
||||
To reverse a previously issued {term}`third-party signature`, the {term}`issuer` can generate a [*certification revocation signature*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-certification-revocation-si) ({term}`type ID<Signature Type ID>` `0x30`). The {term}`revocation` must be issued by the same {term}`key<Component Key>` that created the original {term}`signature<OpenPGP Signature Packet>` or, in deprecated practice, by a designated [Revocation Key](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-revocation-key).
|
138
book/source/signing_data.md
Normal file
|
@ -0,0 +1,138 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Signatures over data
|
||||
|
||||
In OpenPGP, a *{term}`data signature`* guarantees the {term}`authenticity<Authentication>` and, implicitly, the integrity of certain data. Typical use cases of {term}`data signatures<Data Signature>` include the {term}`authentication` of software packages and emails.
|
||||
|
||||
"{term}`Authenticity<Authentication>`" in this context means that the {term}`data signature` was issued by {term}`the entity controlling the signing key material<Certificate Holder>`. However,
|
||||
it does not automatically signal if the expected party indeed controls the {term}`signer` {term}`certificate<OpenPGP Certificate>`. OpenPGP does offer mechanisms for *strong {term}`authentication`*, connecting {term}`certificates<OpenPGP Certificate>` to specific {term}`identities<Identity>`. This verifies that the intended communication partner is indeed associated with the cryptographic {term}`identity` behind the {term}`signature<OpenPGP Signature Packet>`[^sign-auth].
|
||||
|
||||
[^sign-auth]: Other signing solutions, like [signify](https://flak.tedunangst.com/post/signify), focus on pure signing without strong {term}`authentication` of the {term}`signer`'s {term}`identity`.
|
||||
|
||||
{term}`Data signatures<Data Signature>` can only be issued by {term}`component keys<Component Key>` with the *{term}`signing<Signing Key Flag>`* [key flag](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-key-flags).
|
||||
|
||||
Note that {term}`data signatures<Data Signature>` are distinct from [](/signing_components), which are used to form and maintain {term}`certificates<OpenPGP Certificate>`, as well as to {term}`certify<Certification>` {term}`identities<Identity>` on {term}`certificates<OpenPGP Certificate>`.
|
||||
|
||||
(data-signature-types)=
|
||||
## Signature types
|
||||
|
||||
{term}`OpenPGP data signatures<Data Signature>` use one of two [signature types](signature-types):
|
||||
|
||||
- [**Binary signature**](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#sigtype-binary) ({term}`type ID<Signature Type ID>` `0x00`): This is the standard {term}`signature type` for binary data and is typically used for files or data streams. {term}`Binary signatures<Binary Signature>` are calculated over the data without any modifications or transformations.
|
||||
- [**Text signature**](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-signature-of-a-canonical-te) ({term}`type ID<Signature Type ID>` `0x01`): Used for textual data, such as email bodies. When calculating a {term}`text signature`, the data is first normalized by converting line endings into a canonical form (`<CR><LF>`). This approach mitigates issues caused by platform-specific text encodings. This is especially important for detached and {term}`cleartext signatures<Cleartext Signature>`, where the message file might undergo re-encoding between the creation and {term}`verification` of the {term}`signature<OpenPGP Signature Packet>`.
|
||||
|
||||
{term}`Data signatures<Data Signature>` are generated by {term}`hashing<Hash Digest>` the message content along with the {term}`metadata` in the {term}`OpenPGP signature packet`, and calculating a {term}`cryptographic signature` over that {term}`hash<Hash Digest>`. The resulting {term}`cryptographic signature` is stored in the {term}`signature packet<OpenPGP Signature Packet>`.
|
||||
|
||||
{term}`Data signature packets<Data Signature Packet>` manifest in three distinct forms, which will be detailed in the subsequent section.
|
||||
|
||||
(forms-of-data-signatures)=
|
||||
## Forms of OpenPGP data signatures
|
||||
|
||||
{term}`OpenPGP data signatures<Data Signature>` can be applied in three distinct forms[^sign-modes-gpg]:
|
||||
|
||||
- **{term}`Detached<Detached Signature>`**: The OpenPGP signature exists as a separate entity, independent of the signed data.
|
||||
- **{term}`Inline<Inline Signature>`**: Both the original data and its corresponding {term}`OpenPGP signature<OpenPGP Signature Packet>` are encapsulated within an {term}`OpenPGP message`.
|
||||
- **{term}`Cleartext signature`**: A plaintext message and its {term}`OpenPGP signature<OpenPGP Signature Packet>` coexist in a combined text format, preserving the readability of the original message.
|
||||
|
||||
[^sign-modes-gpg]: These three forms of {term}`signature<OpenPGP Signature Packet>` application align with GnuPG's `--detach-sign`, `--sign`, and `--clearsign` command options.
|
||||
|
||||
### Detached signatures
|
||||
|
||||
A {term}`detached signature` is produced by calculating an {term}`OpenPGP signature<OpenPGP Signature Packet>` over the data intended for signing. The original data remains unchanged, and the {term}`OpenPGP signature<OpenPGP Signature Packet>` is stored as a standalone file. A {term}`detached signature` file can be distributed alongside or independent of the original data. The {term}`authenticity<Authentication>` and integrity of the original data file can be {term}`verified<Verification>` by using the {term}`detached signature` file.
|
||||
|
||||
This {term}`signature<OpenPGP Signature Packet>` format is especially useful for signing software releases and other files where it is imperative that the content remains unaltered during the signing process.
|
||||
|
||||
(inline-signature)=
|
||||
### Inline signatures
|
||||
|
||||
An {term}`inline signature` joins the signed data and its corresponding {term}`data signature` into a single {term}`OpenPGP message`.
|
||||
|
||||
This method is commonly used for signing or encrypting emails. Most email software capable of handling OpenPGP communications typically uses {term}`inline signatures<Inline Signature>`.
|
||||
|
||||
#### Structure
|
||||
|
||||
An {term}`inline-signed<Inline Signature>` {term}`OpenPGP message` consists of three segments:
|
||||
|
||||
1. [**One-pass signature packets**](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#one-pass-sig): These one or more {term}`packets<Packet>` precede the signed data and enable {term}`signature<OpenPGP Signature Packet>` computation in one pass.
|
||||
|
||||
2. [**Literal data packet**](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#lit): This contains the original data (e.g., the body of a message), without additional interpretation or conversion.
|
||||
|
||||
3. **{term}`Data signature packets<OpenPGP Signature Packet>`**: These contain the {term}`cryptographic signature` corresponding to the original data.
|
||||
|
||||
#### Creation
|
||||
|
||||
To produce an {term}`inline signature`, the {term}`signer` processes the entirety of the data by reading from an input file and writing into an output {term}`OpenPGP message` file. As the data is processed, the {term}`signer` simultaneously calculates a {term}`cryptographic signature`. This procedure results in the appending of a {term}`data signature packet` to the output {term}`OpenPGP message` file, where it can be efficiently stored.
|
||||
|
||||
For efficient {term}`verification`, an application must understand how to handle the {term}`literal data<Literal Data Packet>` prior to its reading. This requirement is addressed by the {term}`one-pass signature packets<One-pass Signature Packet>` located at the beginning of {term}`inline-signed<Inline Signature>` messages. These {term}`packets<Packet>` include essential information such as the {term}`fingerprint<OpenPGP Fingerprint>` of the {term}`signing key<OpenPGP Component Key>` and the {term}`hash<Hash Digest>` algorithm used for computing the {term}`signature<OpenPGP Signature Packet>`'s {term}`hash digest`. This setup enables the verifier to process the data correctly and efficiently.
|
||||
|
||||
Strictly speaking, knowing just the hash algorithm would be sufficient to begin the verification process. However, having efficient access to the signer's fingerprint or key ID upfront allows OpenPGP software to fetch the signer's certificates before processing the entirety of the - potentially large - signed data, and .
|
||||
|
||||
#### Verification
|
||||
|
||||
{term}`Inline-signed<Inline Signature>` messages enable efficient {term}`verification` in *one pass*, structured as follows:
|
||||
|
||||
1. **Initiation with {term}`one-pass signature packets<One-pass Signature Packet>`**: These {term}`packets<Packet>` begin the {term}`verification` process. They include the {term}`signer`'s {term}`key ID`/{term}`fingerprint<OpenPGP Fingerprint>`, essential for identifying the appropriate {term}`public key<OpenPGP Certificate>` for signature {term}`validation`.
|
||||
|
||||
2. **Processing the {term}`literal data packet`**: This step involves {term}`hashing<Hash Digest>` the literal data, preparing it for {term}`signature<OpenPGP Signature Packet>` {term}`verification`.
|
||||
|
||||
3. **{term}`Verifying<Verification>` {term}`signature packets<OpenPGP Signature Packet>`**: Located at the end of the message, these {term}`packets<Packet>` are checked against the previously calculated {term}`hash digest`.
|
||||
|
||||
Important to note, the {term}`signer`'s {term}`public key<OpenPGP Certificate>`, critical for the final {term}`verification` step, is not embedded in the message. Verifiers must acquire this {term}`key` externally (e.g., from a {term}`key server`) to authenticate the {term}`signature<OpenPGP Signature Packet>` successfully.
|
||||
|
||||
(cleartext-signature)=
|
||||
### Cleartext signatures
|
||||
|
||||
The *{term}`Cleartext Signature Framework`* (CSF) in OpenPGP accomplishes two primary objectives:
|
||||
|
||||
- maintaining the message in a human-readable cleartext format, accessible without OpenPGP-specific software
|
||||
- incorporating an {term}`OpenPGP signature<OpenPGP Signature Packet>` for {term}`authentication` by users with OpenPGP-compatible software
|
||||
|
||||
#### Example
|
||||
|
||||
The following is a detailed example of a {numref}`cleartext` signature:
|
||||
|
||||
```text
|
||||
-----BEGIN PGP SIGNED MESSAGE-----
|
||||
Hash: SHA512
|
||||
|
||||
hello world
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
wpgGARsKAAAAKQWCZT0vBCIhBtB7JOyRoU3SQKwtU+bIqeBUlJpBIi6nOFdu0Zyu
|
||||
o9yZAAAAANqgIHAzoRTzu/7Zuxc8Izf4r3/qSCmBfDqWzTXqmVtsSBSHACka3qbN
|
||||
eehqu8H6S0UK8V7yHbpVhExu9Hu72jWEzU/B0h9MR5gDhJPoWurx8YfyXBDsRS4y
|
||||
r13/eqMN8kfCDw==
|
||||
=Ks9w
|
||||
-----END PGP SIGNATURE-----
|
||||
```
|
||||
|
||||
This {term}`signature<Cleartext Signature>` consists of two parts: a message ("hello world") and an ASCII-armored {term}`OpenPGP signature<OpenPGP Signature Packet>`. The message is immediately comprehensible to a human reader, while the {term}`signature<OpenPGP Signature Packet>` block allows for the message's {term}`authenticity<Authentication>` {term}`verification` via OpenPGP software.
|
||||
|
||||
#### Use case
|
||||
|
||||
{term}`Cleartext signatures<Cleartext Signature>` combine the advantages of both {term}`detached<Detached Signature>` and {term}`inline signatures<Inline Signature>`:
|
||||
|
||||
- **Self-contained format**: {term}`Cleartext signatures<Cleartext signature>` enable the message and its {term}`signature<OpenPGP Signature Packet>` to be stored as a single file.
|
||||
|
||||
- **Human readability**: The message within a {term}`cleartext signature` remains accessible in a plain text format. This eliminates the need for specialized software to read the message content.
|
||||
|
||||
These features are particularly beneficial in scenarios where signed messages are managed semi-manually and where existing system infrastructure offers limited or no native support for OpenPGP in the workflow[^arch-certifications].
|
||||
|
||||
[^arch-certifications]: An illustrative example is the workflow adopted by Arch Linux to {term}`certify<Certification>` {term}`User IDs<User ID>` of new packagers. This process relies on [cleartext signed statements from existing packagers](https://gitlab.archlinux.org/archlinux/archlinux-keyring/-/blob/master/.gitlab/issue_templates/New%20Packager%20Key.md?ref_type=heads&plain=1#L33-46). These signed statements are stored as attachments in an issue tracking system for later inspection. The advantage of this approach lies in the convenience of having the message and signature in a single file, which simplifies manual handling. Based on the vouches in these {term}`cleartext signed<Cleartext Signature>` messages and an [email confirmation from the new packager](https://gitlab.archlinux.org/archlinux/archlinux-keyring/-/wikis/workflows/verify-a-packager-key), the main key operators can issue {term}`OpenPGP third-party certifications<Third-party Identity Certification>`.
|
||||
|
||||
#### Text transformations for cleartext signatures
|
||||
|
||||
The {term}`cleartext signature framework` includes specific text normalization procedures to ensure the integrity and clarity of the message:
|
||||
|
||||
- **Escaping dashes**: The framework implements a method of [dash-escaped text](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-dash-escaped-text) within the message. Dash-escaping ensures that the parser correctly distinguishes between the armor headers, which are part of the {term}`signature<OpenPGP Signature Packet>`'s structure, and any lines in the message that happen to start with a dash.
|
||||
|
||||
- **Normalization of line endings**: Consistent with the approach for any other [text signature](data-signature-types), a {term}`cleartext signature` is calculated on the text with normalized line endings (`<CR><LF>`). This ensures that the {term}`signature<OpenPGP Signature Packet>` remains valid regardless of the text format of the receiving {term}`implementation<OpenPGP Implementation>`.
|
||||
|
||||
#### Pitfalls
|
||||
|
||||
Despite their widespread adoption, {term}`cleartext signatures<Cleartext Signature>` have their limitations and are sometimes viewed as a "legacy method"[^csf-gnupg]. The {term}`RFC` details the [pitfalls of cleartext signatures](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-issues-with-the-cleartext-s), such as incompatibility with semantically meaningful whitespace, challenges with large messages, and security vulnerabilities related to misleading Hash header manipulations. Given these issues, safer alternatives like {term}`inline<Inline Signature>` and {term}`detached signature` forms are advised.
|
||||
|
||||
[^csf-gnupg]: https://lists.gnupg.org/pipermail/gnupg-devel/2023-November/035428.html
|
117
book/source/verification.md
Normal file
|
@ -0,0 +1,117 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Signature verification
|
||||
|
||||
Signature verification in the OpenPGP protocol is a complex process.
|
||||
Many factors influence the validity of a signature.
|
||||
|
||||
Firstly, its expiration time: A signature can be valid at one point in time and expired a second later.
|
||||
|
||||
Signatures can be invalid due to the absence or presence of other signatures (e.g., revocations).
|
||||
Some signatures can be verified standalone, while others require the verification of a chain-like structure of signatures, mostly within the issuer's certificate.
|
||||
|
||||
## When are signatures valid?
|
||||
|
||||
As a necessary condition, a valid signature must be [cryptographically correct](signature-verify). This means that both the signature and its signed input data must be intact.
|
||||
|
||||
However, there is a difference between signature *correctness* and *validity*:
|
||||
|
||||
A signature may be cryptographically correct, but still not qualify as a *valid* signature.
|
||||
Put mathematically, the set of valid signatures is a subset of the set of correct signatures.
|
||||
|
||||
The validity of a correct signature is additionally constrained by a number of conditions:
|
||||
|
||||
* **Well-formedness**: Signature packets need to be well-formed. This means that they must contain suitable signature metadata (this includes: the required signature subpackets must be present in the proper subpacket area). The signature metadata must not contain unknown critical subpackets or unknown critical notations[^unknown-critical]. Some implementations additionally apply a policy that constrains accepted hash algorithms, cryptographic algorithms, and key strengths.
|
||||
* **Temporal validity**: Most signatures have a limited validity period, constrained by the signature creation- and expiration time.
|
||||
* **Qualification**: Furthermore, some signatures need to be *qualified* by other valid signatures in order to be considered valid. This is especially the case with signatures created by dedicated signing subkeys, where, in addition to the signature itself, the subkeys binding signature(s) must be verified.
|
||||
* **Revocation**: Lastly, signatures can be invalidated by revocations.
|
||||
|
||||
[^unknown-critical]: Note that this implies that a signature might be considered valid by one implementation and be rejected by another, based on the set of subpackets and notations each implementation is aware of.
|
||||
|
||||
## Well-formedness of signatures
|
||||
|
||||
There are a number of criteria that a signature must fulfill to be considered well-formed:
|
||||
|
||||
- Each signature MUST have a signature creation time subpacket in its hashed subpacket area. A signature with only an unhashed creation time - or none at all - is not well-formed.
|
||||
- The signature cannot be older than the component key that issued it.
|
||||
- Analogously, a signature with a creation time in the future needs to be rejected as well.
|
||||
- A well-formed signature needs to carry an Issuer Fingerprint subpacket, or an Issuer KeyID subpacket. It is generally recommended to place Issuer subpackets in the hashed area of the signature, but a receiving implementation may also accept signatures which only contain unhashed copies of these subpackets.
|
||||
- A signature disqualifies as well-formed if it contains subpackets which are marked as critical, but unknown to the receiving implementation. Unknown subpackets which are not marked as critical do not have an effect on whether the signature is well-formed.
|
||||
- The same applies to notations. Unknown notations that are marked as critical render the signature malformed.
|
||||
|
||||
(temporal-validity)=
|
||||
## Temporal validity
|
||||
|
||||
A signature is valid only for a constrained period of time:
|
||||
|
||||
- The creation time of the signature acts as a lower bound for the validity. A signature only becomes valid at its creation time. Hard revocation signatures are an exception: They are by definition valid at any point in time, and have no lower temporal bound.
|
||||
- If present, the signature's expiration time acts as a natural upper bound for its validity.
|
||||
|
||||
When checking a signature for validity, a reference time is used. The validity of the signature is evaluated at that reference time.
|
||||
|
||||
The reference time can be:
|
||||
|
||||
- the current time during validation, or
|
||||
- another point in time that is significant to the signature that is validated. For example, when checking the signature of an email, the reference time might be the signature creation time, or the time of receipt of the email.
|
||||
|
||||
For the signature to qualify as valid, it needs to be in effect. In other words, the reference time must fall into the period between signature creation and signature expiration.
|
||||
|
||||
The same reference time must be used when verifying required qualifying signatures, if any.
|
||||
|
||||
## Self-qualifying and non-self-qualifying signatures
|
||||
|
||||
Some signatures can be verified on their own, while others require the verification of additional signatures on the issuer certificate. We will call the former category *self-qualifying* signatures.
|
||||
|
||||
Typically, self-qualifying signatures are self-signatures, meaning signatures issued by an OpenPGP primary key for the components in its certificate.
|
||||
|
||||
Examples for self-qualifying signatures are:
|
||||
|
||||
- direct key self-signatures (`0x1F`),
|
||||
- User ID self-certifications (`0x10`-`0x13`),
|
||||
- key-revocation self-signatures (`0x20`),
|
||||
- certification revocation self-signatures (`0x30`) or
|
||||
- self-signatures used to bind or revoke subkeys (`0x18`, `0x19`, `0x28`).
|
||||
|
||||
Examples for signatures which are not self-qualifying are:
|
||||
|
||||
- data signatures (`0x00`, `0x01`) and
|
||||
- signatures issued over third-party certificates, such as:
|
||||
- third-party direct key signatures (`0x1F`),
|
||||
- third-party key-revocations (`0x20`),
|
||||
- third-party certification (`0x10`-`0x13`), or
|
||||
- third-party certification revocation signatures (`0x30`).
|
||||
|
||||
## Signature qualification
|
||||
|
||||
To verify non-self-qualifying signatures, it is necessary to look at more than just the signature itself.
|
||||
|
||||
This is required because the issuing component key needs to be qualified to create such a signature (e.g., because a specific capability key flag is required). The qualification typically emerges via a self-signature on the key itself.
|
||||
|
||||
In short, a chain of valid signatures from the signature itself to the primary key of the issuer certificate needs to be established.
|
||||
|
||||
For example, a subkey may issue a data signature over an email body only if that subkey is validly bound to the issuer's certificate via a subkey binding signature. That binding signature needs to contain a *key flags* subpacket that marks the subkey as *signing* capable.
|
||||
Similarly, certification signatures over third-party certificates require the issuer key to carry a valid self-signature with the *certification* key flag.
|
||||
|
||||
Self-qualifying signatures have no such limitations.
|
||||
|
||||
For example, a certificate consisting only of a primary key and a single key-revocation self-signature contains everything needed to verify the revocation, as key-revocation self-signatures are self-qualifying.
|
||||
This construct is referred to as a [revocation certificate](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-openpgp-v6-revocation-certi).
|
||||
|
||||
On the other hand, to verify a data signature over a text document, an implementation needs to verify not only the data signature itself, but also the binding signature (and back-signature) of the signing subkey which qualifies the signing subkey.
|
||||
|
||||
```{figure} img/mermaid/09-sigtree.png
|
||||
:name: fig-signature-verification-signature-tree
|
||||
:alt: Depicts a diagrammatic representation of a certificate and a data signature. Arrows between the primary key and other components of the certificate show, how signatures bind the certificate together. In this example, they form a tree of signatures, which all need to be verified in order for the data signature to be valid.
|
||||
|
||||
Tree of signatures that qualify a data signature
|
||||
```
|
||||
|
||||
## Revocations
|
||||
|
||||
A signature can be *disqualified* by the presence of a revocation signature.
|
||||
|
||||
Revocations can be limited in scope, e.g., a subkey-revocation signature only revokes a single subkey.
|
||||
Moreover, revocations can also be constrained to a certain validity period by including a soft revocation reason and expiration time in the revocation signature.
|
13
book/source/versions.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# OpenPGP versions
|
||||
|
||||
## Differences between OpenPGP versions
|
||||
|
||||
```{note}
|
||||
|
||||
This section is still about to be written.
|
||||
```
|
785
book/source/zoom/certificates.md
Normal file
|
@ -0,0 +1,785 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Zooming in: Packet structure of certificates
|
||||
|
||||
Now that we've established the concepts and components that make up OpenPGP certificates, let's look at the internal details of an example certificate.
|
||||
|
||||
## A very minimal OpenPGP certificate
|
||||
|
||||
In this section, we will examine a very minimal version of a "public key" variant of [Alice's OpenPGP key](alice-priv), specifically an OpenPGP certificate that excludes private key material.
|
||||
|
||||
To achieve this, we will use the Sequoia-PGP tool `sq` to handle and transform our example OpenPGP key, as well as to inspect internal OpenPGP packet data.
|
||||
|
||||
Starting from [Alice's OpenPGP private key](alice-priv), we first produce the corresponding public key/certificate using the following command:
|
||||
|
||||
```text
|
||||
$ sq key extract-cert alice.priv > alice.pub
|
||||
```
|
||||
|
||||
(zoom-split-alice)=
|
||||
### Splitting the OpenPGP certificate into packets
|
||||
|
||||
To create a very minimal version of Alice's certificate, we will split the data in `alice.pub` into its component packets and reassemble only the relevant ones back into a new variant.
|
||||
|
||||
Execute the following command to achieve this:
|
||||
|
||||
```text
|
||||
$ sq packet split alice.pub
|
||||
```
|
||||
|
||||
With this command, `sq` generates a set of files, each containing an individual OpenPGP packet extracted from the original full certificate in `alice.pub`:
|
||||
|
||||
```text
|
||||
alice.pub-0--PublicKey
|
||||
alice.pub-1--Signature
|
||||
alice.pub-2--UserID
|
||||
alice.pub-3--Signature
|
||||
alice.pub-4--PublicSubkey
|
||||
alice.pub-5--Signature
|
||||
alice.pub-6--PublicSubkey
|
||||
alice.pub-7--Signature
|
||||
alice.pub-8--PublicSubkey
|
||||
alice.pub-9--Signature
|
||||
```
|
||||
|
||||
|
||||
```{figure} ../plain_svg/certificate_packet_list.svg
|
||||
:name: fig-certificate-packet-list
|
||||
:alt: Depicts a box with white background and the title "Certificate packet list". Inside, a list of several boxes on white background and varying frame colors represent a list of OpenPGP packets from top to bottom. The first box, with green frame, represents the "Public-Key packet", and includes the green public key symbol. The second box, with yellow frame, represents a "Signature packet" ("Direct Key Signature") and includes the green cryptographic signature symbol. The third box, with black frame, represents a "User ID packet", and includes the black User ID symbol. The fourth box, with yellow frame, represents a "Signature packet" ("Certifying self-signature for User ID"), and includes the green cryptographic signature symbol. The fifth box, with green frame, represents a "Public-Subkey packet" and includes the green public key symbol. The sixth box, with yellow frame, represents a "Signature packet" ("Subkey binding signature") and includes the green cryptographic signature symbol. The seventh box, with green frame, represents a "Public-Subkey packet" and includes the green public key symbol. The eighth box, with yellow frame, represents a "Signature packet" ("Subkey binding signature") and includes the green cryptographic signature symbol. The ninth box, with green frame, represents a "Public-Subkey packet" and includes the green public key symbol. The tenth box, with yellow frame, represents a "Signature packet" ("Subkey binding signature") and includes the green cryptographic signature symbol.
|
||||
|
||||
Overview of the packets in Alice's OpenPGP certificate
|
||||
```
|
||||
|
||||
This process allows us to focus on the specific packets within Alice's OpenPGP certificate.
|
||||
|
||||
### Assembling packets into an OpenPGP certificate
|
||||
|
||||
In this step, we'll merge the first two packets of Alice's certificate to create a very minimal certificate:
|
||||
|
||||
Execute the following:
|
||||
|
||||
```text
|
||||
$ sq packet join alice.pub-0--PublicKey alice.pub-1--Signature --output alice_minimal.pub
|
||||
```
|
||||
|
||||
This command combines the contents of `alice.pub-0--PublicKey` and `alice.pub-1--Signature` into a single file named `alice_minimal.pub`.
|
||||
|
||||
### Inspecting this certificate
|
||||
|
||||
This version of Alice's certificate contains just two packets:
|
||||
|
||||
- the [*Public-Key packet*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-public-key-packet-formats) for the primary key, and
|
||||
- a [*Direct Key Signature*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#sigtype-direct-key), which is a self-signature that binds metadata to the primary key.
|
||||
|
||||
This is the shape of the packets we'll explore in the subsequent sections:
|
||||
|
||||
```{figure} ../plain_svg/Minimal_OpenPGP_certificate.svg
|
||||
:name: fig-public-certificate-minimal
|
||||
:alt: TODO
|
||||
|
||||
A minimal OpenPGP certificate, visualized
|
||||
```
|
||||
|
||||
In real-world scenarios, OpenPGP certificates are typically far more complex than this minimal example. However, this is indeed a valid OpenPGP certificate. In the following sections, we will introduce more components to this certificate, increasing its complexity and exploring their details.
|
||||
|
||||
In ASCII-armored representation, this very minimal key appears as follows:
|
||||
|
||||
```text
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
xioGZRbqphsAAAAgUyTpQ6+rFfdu1bUSmHlpzRtdEGXr50Liq0f0hrOuZT7CtgYf
|
||||
GwoAAAA9BYJlFuqmBYkFpI+9AwsJBwMVCggCmwECHgEiIQaqoYy7JUaFxYNYMgVj
|
||||
/Te2fzMA+fsOxFc3jNKfECaYswAAAAoJEKqhjLslRoXFZ0cgouNjgeNr0E9W18g4
|
||||
gAIl6FM5SWuQxg12j0S07ExCOI5NPRDCrSnAV85mAXOzeIGeiVLPQ40oEal3CX/L
|
||||
+BXIoY2sIEQrLd4TAEEy0BA8aQZTPEmMdiOCM1QB+V+BQZAO
|
||||
=5nyq
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
```
|
||||
|
||||
The output of `sq` is presented as a block of text. We will now decode this OpenPGP data and inspect the two packets it contains.
|
||||
|
||||
To achieve this, we will use the Sequoia-PGP tool `sq` and run the `packet dump` subcommand:
|
||||
|
||||
```text
|
||||
$ sq packet dump --hex alice_minimal.pub
|
||||
```
|
||||
|
||||
This will allow us to gain a detailed understanding of the packet contents.
|
||||
|
||||
(zoom-public-key)=
|
||||
### Public-Key packet
|
||||
|
||||
The output begins with a (primary) [Public-Key packet](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-public-key-packet-formats):
|
||||
|
||||
```text
|
||||
Public-Key Packet, new CTB, 2 header bytes + 42 bytes
|
||||
Version: 6
|
||||
Creation time: 2023-09-29 15:17:58 UTC
|
||||
Pk algo: Ed25519
|
||||
Pk size: 256 bits
|
||||
Fingerprint: AAA18CBB254685C58358320563FD37B67F3300F9FB0EC457378CD29F102698B3
|
||||
KeyID: AAA18CBB254685C5
|
||||
|
||||
00000000 c6 CTB
|
||||
00000001 2a length
|
||||
00000002 06 version
|
||||
00000003 65 16 ea a6 creation_time
|
||||
00000007 1b pk_algo
|
||||
00000008 00 00 00 20 public_len
|
||||
0000000c 53 24 e9 43 ed25519_public
|
||||
00000010 af ab 15 f7 6e d5 b5 12 98 79 69 cd 1b 5d 10 65
|
||||
00000020 eb e7 42 e2 ab 47 f4 86 b3 ae 65 3e
|
||||
```
|
||||
|
||||
The Public-Key packet consists primarily of the cryptographic key data. Let's look at the packet field by field:
|
||||
|
||||
**OpenPGP packet syntax**
|
||||
|
||||
The first fields of a packet are governed by the general [Packet Syntax](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-packet-syntax):
|
||||
|
||||
- `CTB: 0xc6`[^CTB]: This is the [packet type ID](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-packet-headers) for this packet. The binary representation of the value `0xc6` is `11000110`. The first two bits show that the packet is in *OpenPGP packet format* (as opposed to in *Legacy packet format*) and the remaining 6 bits encode the type ID value, which is "6." This type ID value corresponds to a Public-Key packet, as listed in the [packet type IDs](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-packet-types).
|
||||
|
||||
- `length: 0x2a`: This indicates the remaining length of this packet.
|
||||
|
||||
**Public-Key packet syntax**
|
||||
|
||||
The packet type ID ("6") defines the semantics of the following data within the packet. In this case, it is a Public-Key packet, which is a kind of [Key Material Packet](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-key-material-packets).
|
||||
|
||||
- `version: 0x06`: The key material is in version 6 format. This means that the next part of the packet adheres to the structure of [Version 6 Public Keys](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-version-6-public-keys).
|
||||
|
||||
- `creation_time: 0x6516eaa6`: This field represents the key's creation time. (See also [Time Fields](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-time-fields)).
|
||||
|
||||
- `pk_algo: 0x1b`: This corresponds to the key's public-key algorithm ID, which has a decimal value of 27. Refer to the list of [Public-Key Algorithms](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-public-key-algorithms)) for more details.
|
||||
|
||||
- `public_len: 0x00000020`: This field specifies the octet count for the subsequent public key material. In this case, it represents the length of the following `ed25519_public` field.
|
||||
|
||||
- `ed25519_public`: This is the [algorithm-specific representation](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-algorithm-specific-part-for-ed2) of the public key material. The format is based on the value of `pk_algo`, which, in this case, is 32 bytes of Ed25519 public key data.
|
||||
|
||||
[^CTB]: Sequoia uses the term CTB ({term}`Cipher Type Byte`) to refer to the [packet type ID](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-packet-headers).
|
||||
|
||||
Note that the *Public-Key packet* contains only the public part of the key.
|
||||
|
||||
```{figure} ../plain_svg/public-key_packet.svg
|
||||
:name: fig-public-key-packet
|
||||
:alt: Depicts a box with white background and title "Public-Key packet". In the center a box with white background and green frame is shown. Inside it several items are listed, separated by green dotted horizontal lines. The first three are "Version", "Creation Time", "Public-Key Algorithm" written in black. The last one is written in green and reads "Public Key Material" and has the green public key symbol at its right side.
|
||||
|
||||
Structure of a Public-Key packet.
|
||||
```
|
||||
|
||||
(zoom-dks)=
|
||||
### Direct Key Signature
|
||||
|
||||
The next packet in the certificate is a [*Direct Key Signature*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#sigtype-direct-key), which plays a crucial role in binding specific information to the primary key. This signature is contained within the file `alice.pub-1--Signature`.
|
||||
|
||||
This packet binds the data within the signature subpackets with the primary key. Each entry under "Signature Packet -> Hashed area" is one signature subpacket, providing essential information such as algorithm preferences, including *symmetric algorithm preference* and *hash algorithm preferences*.
|
||||
|
||||
```text
|
||||
Signature Packet, new CTB, 2 header bytes + 182 bytes
|
||||
Version: 6
|
||||
Type: DirectKey
|
||||
Pk algo: Ed25519
|
||||
Hash algo: SHA512
|
||||
Hashed area:
|
||||
Signature creation time: 2023-09-29 15:17:58 UTC (critical)
|
||||
Key expiration time: P1095DT62781S (critical)
|
||||
Symmetric algo preferences: AES256, AES128
|
||||
Hash preferences: SHA512, SHA256
|
||||
Key flags: C (critical)
|
||||
Features: MDC
|
||||
Issuer Fingerprint: AAA18CBB254685C58358320563FD37B67F3300F9FB0EC457378CD29F102698B3
|
||||
Unhashed area:
|
||||
Issuer: AAA18CBB254685C5
|
||||
Digest prefix: 6747
|
||||
Level: 0 (signature over data)
|
||||
|
||||
00000000 c2 CTB
|
||||
00000001 b6 length
|
||||
00000002 06 version
|
||||
00000003 1f type
|
||||
00000004 1b pk_algo
|
||||
00000005 0a hash_algo
|
||||
00000006 00 00 00 3d hashed_area_len
|
||||
0000000a 05 subpacket length
|
||||
0000000b 82 subpacket tag
|
||||
0000000c 65 16 ea a6 sig creation time
|
||||
00000010 05 subpacket length
|
||||
00000011 89 subpacket tag
|
||||
00000012 05 a4 8f bd key expiry time
|
||||
00000016 03 subpacket length
|
||||
00000017 0b subpacket tag
|
||||
00000018 09 07 pref sym algos
|
||||
0000001a 03 subpacket length
|
||||
0000001b 15 subpacket tag
|
||||
0000001c 0a 08 pref hash algos
|
||||
0000001e 02 subpacket length
|
||||
0000001f 9b subpacket tag
|
||||
00000020 01 key flags
|
||||
00000021 02 subpacket length
|
||||
00000022 1e subpacket tag
|
||||
00000023 01 features
|
||||
00000024 22 subpacket length
|
||||
00000025 21 subpacket tag
|
||||
00000026 06 version
|
||||
00000027 aa a1 8c bb 25 46 85 c5 83 issuer fp
|
||||
00000030 58 32 05 63 fd 37 b6 7f 33 00 f9 fb 0e c4 57 37
|
||||
00000040 8c d2 9f 10 26 98 b3
|
||||
00000047 00 00 00 0a unhashed_area_len
|
||||
0000004b 09 subpacket length
|
||||
0000004c 10 subpacket tag
|
||||
0000004d aa a1 8c issuer
|
||||
00000050 bb 25 46 85 c5
|
||||
00000055 67 digest_prefix1
|
||||
00000056 47 digest_prefix2
|
||||
00000057 20 salt_len
|
||||
00000058 a2 e3 63 81 e3 6b d0 4f salt
|
||||
00000060 56 d7 c8 38 80 02 25 e8 53 39 49 6b 90 c6 0d 76
|
||||
00000070 8f 44 b4 ec 4c 42 38 8e
|
||||
00000078 4d 3d 10 c2 ad 29 c0 57 ed25519_sig
|
||||
00000080 ce 66 01 73 b3 78 81 9e 89 52 cf 43 8d 28 11 a9
|
||||
00000090 77 09 7f cb f8 15 c8 a1 8d ac 20 44 2b 2d de 13
|
||||
000000a0 00 41 32 d0 10 3c 69 06 53 3c 49 8c 76 23 82 33
|
||||
000000b0 54 01 f9 5f 81 41 90 0e
|
||||
```
|
||||
|
||||
Below is a field-by-field examination of the packet:
|
||||
|
||||
**OpenPGP packet syntax**
|
||||
|
||||
The first fields of a packet are governed by the general [Packet Syntax](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-packet-syntax):
|
||||
|
||||
- `CTB: 0xc2`: This field indicates the Packet type ID for this packet. Bits 7 and 6 show that the packet is in “OpenPGP packet format.” The remaining 6 bits encode the type ID’s value, which is “2” for a Signature packet.
|
||||
|
||||
- `length: 0xb6`: This field shows the remaining length of this packet.
|
||||
|
||||
**Signature packet syntax**
|
||||
|
||||
The packet type ID (“2”) defines the semantics of the remaining data in the packet. In this case, as it indicates a [Signature packet](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#signature-packet), the following data is specific to this packet type:
|
||||
|
||||
- `version: 0x06`: This is a version 6 signature.
|
||||
|
||||
- `type: 0x1f`: This indicates the [Signature Type](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-signature-types).
|
||||
|
||||
- `pk_algo: 0x1b`: This specifies the Public-Key algorithm ID, with decimal 27 corresponding to [Ed25519](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-public-key-algorithms)).
|
||||
|
||||
- `hash_algo: 0x0a`: This specifies the hash algorithm ID, with decimal 10 corresponding to [SHA2-512](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-hash-algorithms)).
|
||||
|
||||
- `hashed_area_len: 0x0000003d`: This specifies the length of the following hashed subpacket data.
|
||||
|
||||
The next segment of this packet contains the hashed subpacket data.
|
||||
|
||||
In OpenPGP Signatures, there are two sets of subpacket data: hashed and unhashed. Hashed subpackets are protected by the digital signature of the packet, while unhashed subpackets are not.
|
||||
|
||||
A subpacket data set in an OpenPGP Signature contains a list of zero or more Signature subpackets.
|
||||
|
||||
The following subpacket data consists of sets of "subpacket length, subpacket type ID, data." Each subpacket is displayed as one line, starting with the [subpacket type description](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-signature-subpacket-specifi) (based on the subpacket type ID). Note that bit 7 of the subpacket type ID signals if that subpacket is ["critical."](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#section-5.2.3.7-7)
|
||||
|
||||
```{note}
|
||||
Critical here means that the receiver must interpret the subpacket and is expected to fail, otherwise. Non-critical subpackets may be ignored by the receiver.
|
||||
```
|
||||
|
||||
The subpacket details are as follows:
|
||||
|
||||
- [**Signature Creation Time**](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#signature-creation-subpacket)
|
||||
- Type: `2`
|
||||
- Critical: `Yes`
|
||||
- Value: `0x6516eaa6`
|
||||
- Notes: See also [Time Fields](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-time-fields).
|
||||
|
||||
- [**Key Expiration Time**](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#key-expiration-subpacket)
|
||||
- Type: `9`
|
||||
- Critical: `Yes`
|
||||
- Value: `0x05a48fbd`
|
||||
- Notes: Defined as number of seconds after the key creation time
|
||||
|
||||
- [**Preferred Symmetric Ciphers for v1 SEIPD**](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#preferred-v1-seipd)
|
||||
- Type: `11`
|
||||
- Critical: `No`
|
||||
- Value: `0x09 0x07`
|
||||
- Notes: Values correspond to *AES with 256-bit key* and *AES with 128-bit key*
|
||||
|
||||
- [**Preferred Hash Algorithms**](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#preferred-hashes-subpacket)
|
||||
- Type: `21`
|
||||
- Critical: `No`
|
||||
- Value: `0x0a 0x08`
|
||||
- Notes: Values correspond to *SHA2-512* and *SHA2-256*.
|
||||
|
||||
- [**Key Flags**](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#key-flags)
|
||||
- Type: `27`
|
||||
- Critical: `Yes`
|
||||
- Value: `0x01`
|
||||
- Notes: Value corresponds to the *certifications* key flag.
|
||||
|
||||
- [**Features**](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#features-subpacket)
|
||||
- Type: `30`
|
||||
- Critical: `No`
|
||||
- Value: `0x01`
|
||||
- Notes: Value corresponds to *Symmetrically Encrypted Integrity Protected Data packet version 1*
|
||||
|
||||
- [**Issuer Fingerprint**](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#issuer-fingerprint-subpacket)
|
||||
- Type: `33`
|
||||
- Critical: `No`
|
||||
- Value: `aaa18cbb254685c58358320563fd37b67f3300f9fb0ec457378cd29f102698b3`
|
||||
- Notes: The fingerprint identifies the component key that issued the signature in this packet. In this instance, the value is the primary key fingerprint of the certificate we're looking at.
|
||||
|
||||
The next part of this packet contains unhashed subpacket data:
|
||||
|
||||
- `unhashed_area_len: 0x0000000a`: length of the following unhashed subpacket data (value: 10 bytes).
|
||||
|
||||
As above, the following subpacket data consists of sets of subpacket length, subpacket type id, and data. In this case, only one subpacket follows:
|
||||
|
||||
- [**Issuer Key ID**](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#issuer-keyid-subpacket)
|
||||
- Type: `16`
|
||||
- Critical: `No`
|
||||
- Value: `aaa18cbb254685c5`
|
||||
- Notes: This is the shortened version 6 *Key ID* of the fingerprint of this certificate's primary key.
|
||||
|
||||
This concludes the unhashed subpacket data.
|
||||
|
||||
This next section shows the remaining fields of this signature packet, which relate to the cryptographic digital signature:
|
||||
|
||||
- `digest_prefix: 0x6747`: the left 16 bits of the signed hash digest
|
||||
|
||||
- `salt_len, salt`: a random [salt value](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-advantages-of-salted-signat) with size [matching the hash algorithm](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#hash-algorithms-registry))
|
||||
|
||||
- `ed25519_sig`: [algorithm-specific](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-algorithm-specific-fields-for-ed2) representation of the signature (here: 64 bytes of Ed25519 signature)
|
||||
|
||||
The hash digest is calculated from the following data (see [Computing Signatures](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-computing-signatures) in the RFC):
|
||||
|
||||
- the signature's salt
|
||||
- the serialized primary key's public data
|
||||
- the serialized direct key signature packet (excluding the unhashed area)
|
||||
|
||||
The signature is calculated from this hash digest.
|
||||
|
||||
```{figure} ../plain_svg/direct_key_signature_packet.svg
|
||||
:name: fig-direct-key-signature-packet
|
||||
:alt: Depicts a box with white background, title "Signature packet" and subtitle "Direct Key Signature (type ID 0x1F)". In the center a box with white background and yellow frame is shown. Inside it several items are listed, separated by yellow dotted horizontal lines. The first three are "Version", "Public-Key Algorithm" and "Hash Algorithm". The fourth item is called "Hashed area" and confines further sub-items by a light-yellow frame on the top and left side. The sub-items are "Signature Creation Time", "Key Expiration Time", "Preferred Symmetric Ciphers for v1 SEIPD", "Preferred Hash Algorithms", "Key Flags", "Features" and "Issuer Fingerprint". The fifth item is named "Unhashed area" and again introduces an area for sub-items, this time using a light-gray border on the top and left side. The unhashed area has no sub-items though. The last item is called "Cryptographic Signature", with the subtitle "by the primary key over primary key, subkey and signature metadata" and includes the green cryptographic signature symbol on the right side.
|
||||
|
||||
Structure of a direct key signature packet.
|
||||
```
|
||||
|
||||
(zoom-subkey-enc)=
|
||||
## Encryption subkey
|
||||
|
||||
Let's now look at a subkey in Alice's OpenPGP certificate. A subkey, when linked to an OpenPGP certificate via its primary key, consists of two elements:
|
||||
|
||||
- a key packet that contains the component key itself, and
|
||||
- a signature packet that links this component key to the primary key and, implicitly, to the full OpenPGP certificate.
|
||||
|
||||
We will use the files containing individual packets of Alice's certificate, which we separated above. In this split representation, the encryption subkey is stored in `alice.pub-4--PublicSubkey`, while the associated binding self-signature is stored in `alice.pub-5--Signature`.
|
||||
|
||||
````{note}
|
||||
It's common to look at a packet dump for a full OpenPGP certificate as shown below:
|
||||
|
||||
```text
|
||||
$ sq packet dump --hex alice.pub
|
||||
```
|
||||
|
||||
This command shows the details for the full series of packets in an OpenPGP certificate (refer to the list of [packets of Alice's certificate](zoom-split-alice)). Finding a particular packet in that list can take a bit of focus and practice though.
|
||||
|
||||
In the following sections,we make it easier for ourselves by directly examining individual packets from the files we created with `sq packet split` above.
|
||||
````
|
||||
|
||||
### Public-Subkey packet
|
||||
|
||||
We'll now look at the *Public-Subkey packet* that contains the component key data of this subkey:
|
||||
|
||||
```text
|
||||
$ sq packet dump --hex alice.pub-4--PublicSubkey
|
||||
Public-Subkey Packet, new CTB, 2 header bytes + 42 bytes
|
||||
Version: 6
|
||||
Creation time: 2023-09-29 15:17:58 UTC
|
||||
Pk algo: X25519
|
||||
Pk size: 256 bits
|
||||
Fingerprint: C0A58384A438E5A14F73712426A4D45DBAEEF4A39E6B30B09D5513F978ACCA94
|
||||
KeyID: C0A58384A438E5A1
|
||||
|
||||
00000000 ce CTB
|
||||
00000001 2a length
|
||||
00000002 06 version
|
||||
00000003 65 16 ea a6 creation_time
|
||||
00000007 19 pk_algo
|
||||
00000008 00 00 00 20 public_len
|
||||
0000000c d1 ae 87 d7 x25519_public
|
||||
00000010 cc 42 af 99 34 c5 c2 5c ca fa b7 4a c8 43 fc 86
|
||||
00000020 35 2a 46 01 f3 cc 00 f5 4a 09 3e 3f
|
||||
```
|
||||
|
||||
Notice that the structure of this *Public-Subkey packet* mirrors the primary key's [*Public-Key packet*](zoom-public-key) above. However, there are notable differences between the two packets:
|
||||
|
||||
- The packet type ID (`CTB`) in this packet shows type 14 ([*Public-Subkey packet*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-public-subkey-packet-type-i)).
|
||||
|
||||
- The `pk_algo` value is set to `0x19` (decimal 25), which [corresponds to X25519](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-public-key-algorithms). Notably, though both the primary key and this subkey use a cryptographic mechanism based on Curve25519, the encryption key uses Curve 25519 in a different way: namely, X25519 is a Diffie–Hellman function constructed from Curve25519.
|
||||
- Accordingly, the public part of the cryptographic key pair is labeled `x25519_public`, as implied by the value (`0x19`) of `pk_algo`. However, the actual data is just 32 bytes of cryptographic key material, without any type information.
|
||||
|
||||
### Subkey binding signature
|
||||
|
||||
The aforementioned subkey packet is disconnected from the OpenPGP certificate to which it belongs. The link between the subkey and the complete OpenPGP certificate is made with a cryptographic signature, generated by primary key of the OpenPGP certificate.
|
||||
|
||||
The type of signature is called a *subkey binding signature*, because it "binds" or connects the subkey to the rest of the key.
|
||||
|
||||
The signature does more than just bind the subkey; it also carries additional metadata about the subkey. This metadata is in the binding signature, and not in the subkey packet, because it may change over time, while the subkey packet itself remains unchanged. This evolving metadata is stored in self-signatures: if the key holder wants to modify the metadata (for example, to change the key's expiration time), a newer version of the same signature type can be issued. The recipient OpenPGP software will recognize that the newer self-signature supersedes the older one, and that the metadata in the newer signature reflects the most current intent of the key holder.
|
||||
|
||||
Note that this subkey binding signature packet is quite similar to the Direct Key Signature discussed above. Both signatures serve a similar purpose in adding metadata to a component key, particularly as the hashed subpacket data contains much of the same metadata elements.
|
||||
|
||||
```text
|
||||
$ sq packet dump --hex alice.pub-5--Signature
|
||||
Signature Packet, new CTB, 2 header bytes + 171 bytes
|
||||
Version: 6
|
||||
Type: SubkeyBinding
|
||||
Pk algo: Ed25519
|
||||
Hash algo: SHA512
|
||||
Hashed area:
|
||||
Signature creation time: 2023-09-29 15:17:58 UTC (critical)
|
||||
Key expiration time: P1095DT62781S (critical)
|
||||
Key flags: EtEr (critical)
|
||||
Issuer Fingerprint: AAA18CBB254685C58358320563FD37B67F3300F9FB0EC457378CD29F102698B3
|
||||
Unhashed area:
|
||||
Issuer: AAA18CBB254685C5
|
||||
Digest prefix: 2289
|
||||
Level: 0 (signature over data)
|
||||
|
||||
00000000 c2 CTB
|
||||
00000001 ab length
|
||||
00000002 06 version
|
||||
00000003 18 type
|
||||
00000004 1b pk_algo
|
||||
00000005 0a hash_algo
|
||||
00000006 00 00 00 32 hashed_area_len
|
||||
0000000a 05 subpacket length
|
||||
0000000b 82 subpacket tag
|
||||
0000000c 65 16 ea a6 sig creation time
|
||||
00000010 05 subpacket length
|
||||
00000011 89 subpacket tag
|
||||
00000012 05 a4 8f bd key expiry time
|
||||
00000016 02 subpacket length
|
||||
00000017 9b subpacket tag
|
||||
00000018 0c key flags
|
||||
00000019 22 subpacket length
|
||||
0000001a 21 subpacket tag
|
||||
0000001b 06 version
|
||||
0000001c aa a1 8c bb issuer fp
|
||||
00000020 25 46 85 c5 83 58 32 05 63 fd 37 b6 7f 33 00 f9
|
||||
00000030 fb 0e c4 57 37 8c d2 9f 10 26 98 b3
|
||||
0000003c 00 00 00 0a unhashed_area_len
|
||||
00000040 09 subpacket length
|
||||
00000041 10 subpacket tag
|
||||
00000042 aa a1 8c bb 25 46 85 c5 issuer
|
||||
0000004a 22 digest_prefix1
|
||||
0000004b 89 digest_prefix2
|
||||
0000004c 20 salt_len
|
||||
0000004d 0b 0c 89 salt
|
||||
00000050 b5 ab 15 e3 7f e4 4d b9 a7 ef 71 48 14 3b ab 26
|
||||
00000060 5f 34 7f 6d 48 2e 9f 78 48 58 6d 9a fb
|
||||
0000006d 6d b2 db ed25519_sig
|
||||
00000070 2f 97 8e c8 12 fc 57 7f 85 aa d1 59 bc 80 40 0b
|
||||
00000080 be 2e f0 e1 23 2d bf 4b 71 7e d0 e4 c0 36 e4 d2
|
||||
00000090 cf b2 9f b4 a8 4f 3e 2a 21 89 74 c2 33 55 af ac
|
||||
000000a0 41 36 1b 2b 60 09 f2 d9 19 f4 41 12 0b
|
||||
```
|
||||
|
||||
The analysis of this packet dump will be less extensive, given that its structure mirrors the *Direct Key Signature* explored above.
|
||||
|
||||
One notable difference is the `type` field, showing that this signature is of type `0x18` ([Subkey Binding Signature](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-subkey-binding-signature-ty)).
|
||||
|
||||
The `pk_algo` value of this signature derives from the algorithm of the primary key (`0x1b`, corresponding to Ed25519). This signature is issued by the primary key, thus using the signing algorithm of the primary key. (The algorithm used to produce the cryptographic signature in this packet is entirely independent of the `pk_algo` of the key material of this subkey itself, which uses the X25519 mechanism.)
|
||||
|
||||
As shown in the text at the top of this packet dump, the hashed subpacket data contains four pieces of information:
|
||||
|
||||
- signature creation time: `2023-09-29 15:17:58 UTC` (**critical**)
|
||||
- key expiration time: `P1095DT62781S` (**critical**)
|
||||
- key flags: `EtEr` (**critical**) (encryption for communication, encryption for storage)
|
||||
- issuer fingerprint: `AAA18CBB254685C58358320563FD37B67F3300F9FB0EC457378CD29F102698B3`
|
||||
|
||||
The rest of the packet mirrors the *Direct Key Signature* discussed above:
|
||||
- a 16-bit digest prefix
|
||||
- a salt value
|
||||
- the cryptographic signature itself
|
||||
|
||||
The signature is calculated over a hash digest. In this case, the hash digest is derived from the following data:
|
||||
|
||||
- the signature's salt
|
||||
- the serialized primary key's public data
|
||||
- the serialized subkey's public data
|
||||
- the serialized subkey binding signature packet (excluding the unhashed area)
|
||||
|
||||
Refer to [Computing Signatures](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-computing-signatures) in the RFC for details.
|
||||
|
||||
## Signing subkey
|
||||
|
||||
```{note}
|
||||
|
||||
This section is still about to be written.
|
||||
```
|
||||
|
||||
```text
|
||||
$ sq packet dump --hex alice.pub-6--PublicSubkey
|
||||
Public-Subkey Packet, new CTB, 2 header bytes + 42 bytes
|
||||
Version: 6
|
||||
Creation time: 2023-09-29 15:17:58 UTC
|
||||
Pk algo: Ed25519
|
||||
Pk size: 256 bits
|
||||
Fingerprint: D07B24EC91A14DD240AC2D53E6C8A9E054949A41222EA738576ED19CAEA3DC99
|
||||
KeyID: D07B24EC91A14DD2
|
||||
|
||||
00000000 ce CTB
|
||||
00000001 2a length
|
||||
00000002 06 version
|
||||
00000003 65 16 ea a6 creation_time
|
||||
00000007 1b pk_algo
|
||||
00000008 00 00 00 20 public_len
|
||||
0000000c 33 8c d4 f5 ed25519_public
|
||||
00000010 1a 73 39 ef ce d6 0f 21 8d a0 58 a2 3c 3d 44 a8
|
||||
00000020 59 e9 13 1f 12 9c 6f 19 d0 3d 40 a0
|
||||
```
|
||||
|
||||
```text
|
||||
$ sq packet dump --hex alice.pub-7--Signature
|
||||
Signature Packet, new CTB, 3 header bytes + 325 bytes
|
||||
Version: 6
|
||||
Type: SubkeyBinding
|
||||
Pk algo: Ed25519
|
||||
Hash algo: SHA512
|
||||
Hashed area:
|
||||
Signature creation time: 2023-09-29 15:17:58 UTC (critical)
|
||||
Key expiration time: P1095DT62781S (critical)
|
||||
Key flags: S (critical)
|
||||
Embedded signature: (critical)
|
||||
Signature Packet
|
||||
Version: 6
|
||||
Type: PrimaryKeyBinding
|
||||
Pk algo: Ed25519
|
||||
Hash algo: SHA512
|
||||
Hashed area:
|
||||
Signature creation time: 2023-09-29 15:17:58 UTC (critical)
|
||||
Issuer Fingerprint: D07B24EC91A14DD240AC2D53E6C8A9E054949A41222EA738576ED19CAEA3DC99
|
||||
Digest prefix: 5365
|
||||
Level: 0 (signature over data)
|
||||
|
||||
Issuer Fingerprint: AAA18CBB254685C58358320563FD37B67F3300F9FB0EC457378CD29F102698B3
|
||||
Unhashed area:
|
||||
Issuer: AAA18CBB254685C5
|
||||
Digest prefix: 841C
|
||||
Level: 0 (signature over data)
|
||||
|
||||
00000000 c2 CTB
|
||||
00000001 c0 85 length
|
||||
00000003 06 version
|
||||
00000004 18 type
|
||||
00000005 1b pk_algo
|
||||
00000006 0a hash_algo
|
||||
00000007 00 00 00 cc hashed_area_len
|
||||
0000000b 05 subpacket length
|
||||
0000000c 82 subpacket tag
|
||||
0000000d 65 16 ea sig creation time
|
||||
00000010 a6
|
||||
00000011 05 subpacket length
|
||||
00000012 89 subpacket tag
|
||||
00000013 05 a4 8f bd key expiry time
|
||||
00000017 02 subpacket length
|
||||
00000018 9b subpacket tag
|
||||
00000019 02 key flags
|
||||
0000001a 99 subpacket length
|
||||
0000001b a0 subpacket tag
|
||||
0000001c 06 19 1b 0a embedded sig
|
||||
00000020 00 00 00 29 05 82 65 16 ea a6 22 21 06 d0 7b 24
|
||||
00000030 ec 91 a1 4d d2 40 ac 2d 53 e6 c8 a9 e0 54 94 9a
|
||||
00000040 41 22 2e a7 38 57 6e d1 9c ae a3 dc 99 00 00 00
|
||||
00000050 00 53 65 20 42 03 ad 0c db fc b5 9a 98 a6 15 27
|
||||
00000060 e4 11 5e f5 f2 a0 3d bc ed 8d 94 27 41 09 f6 3c
|
||||
00000070 4b f8 8a e5 af 73 e1 7d 54 07 40 3f f3 29 34 c2
|
||||
00000080 e7 60 56 a5 e1 43 cb 08 ba 66 fe 8b 26 ce e7 cb
|
||||
00000090 a5 3a 46 bb a5 c8 5d e4 6a de ae 49 e1 3e 07 bf
|
||||
000000a0 c4 9e 98 14 2f 3e c5 f7 01 3e 3e 4f f6 18 2a ac
|
||||
000000b0 bd ed 52 0c
|
||||
000000b4 22 subpacket length
|
||||
000000b5 21 subpacket tag
|
||||
000000b6 06 version
|
||||
000000b7 aa a1 8c bb 25 46 85 c5 83 issuer fp
|
||||
000000c0 58 32 05 63 fd 37 b6 7f 33 00 f9 fb 0e c4 57 37
|
||||
000000d0 8c d2 9f 10 26 98 b3
|
||||
000000d7 00 00 00 0a unhashed_area_len
|
||||
000000db 09 subpacket length
|
||||
000000dc 10 subpacket tag
|
||||
000000dd aa a1 8c issuer
|
||||
000000e0 bb 25 46 85 c5
|
||||
000000e5 84 digest_prefix1
|
||||
000000e6 1c digest_prefix2
|
||||
000000e7 20 salt_len
|
||||
000000e8 23 3d b2 49 f3 02 4b 08 salt
|
||||
000000f0 93 af ba 08 89 f0 e0 91 0f ab 22 26 aa b3 56 57
|
||||
00000100 30 ea 95 29 06 60 6f 00
|
||||
00000108 be 44 a1 95 38 a9 6b 3a ed25519_sig
|
||||
00000110 3e 51 f0 55 09 b1 e2 91 a9 17 86 fa f5 1e 3f d0
|
||||
00000120 28 46 3c ce 6e 88 14 37 32 ec 3d fa c6 01 ca e5
|
||||
00000130 a9 4b b7 63 94 c3 0d 92 ab dc fa 23 50 71 60 31
|
||||
00000140 a6 73 c8 33 5a 9c d9 0a
|
||||
```
|
||||
|
||||
(zoom-user-id)=
|
||||
## Adding an identity component
|
||||
|
||||
In this section, we'll look at an identity associated with Alice's certificate.
|
||||
|
||||
User IDs are a mechanism for connecting [identities](identity-components) with an OpenPGP certificate. Typically, a User ID is a string combining a name and an email address.
|
||||
|
||||
To understand the internal packet structure of this identity and its connection to the OpenPGP certificate, we'll examine two packets that constitute the identity component. One is the [User ID packet](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#uid), located in the file `alice.pub-2--UserID`, which contains identity information. The other is a certifying self-signature, specifically a [Positive certification of a User ID and Public-Key packet](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-positive-certification-of-a) located in the file `alice.pub-3--Signature`. This certification, issued after substantial verification of the identity claim, validates the association between the User ID and the certificate's public key. These packets are snippets from Alice's full OpenPGP certificate.
|
||||
|
||||
### User ID packet
|
||||
|
||||
First, let's look at the User ID packet, which encodes an identity that is associated with an OpenPGP certificate:
|
||||
|
||||
```text
|
||||
$ sq packet dump --hex alice.pub-2--UserID
|
||||
User ID Packet, new CTB, 2 header bytes + 19 bytes
|
||||
Value: <alice@example.org>
|
||||
|
||||
00000000 cd CTB
|
||||
00000001 13 length
|
||||
00000002 3c 61 6c 69 63 65 40 65 78 61 6d 70 6c 65 value
|
||||
00000010 2e 6f 72 67 3e
|
||||
```
|
||||
|
||||
- `CTB: 0xcd`: This is the packet type ID for this packet. Bits 7 and 6 show that the packet is in “OpenPGP packet format” (not “Legacy packet format”). The remaining 6 bits encode the type ID’s value: “13,” which is the value for a [User ID packet](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#uid).
|
||||
|
||||
- `length: 0x13`: This field shows the remaining length of the packet (here: 19 bytes).
|
||||
|
||||
- `value`: This comprises 19 bytes of data that contain UTF-8 encoded text. The value corresponds to the string `<alice@example.org>`. With this identity component, Alice asserts usage and control over the specified email address. Note that the email address is enclosed in `<` and `>` characters, in line with the conventions of [RFC 2822](https://www.rfc-editor.org/rfc/rfc2822).
|
||||
|
||||
Essentially, a User ID packet is just a string marked as a User ID by the packet type ID.
|
||||
|
||||
### Linking the User ID with a certification self-signature
|
||||
|
||||
Similar to [linking a subkey](zoom-subkey-enc) to the OpenPGP certificate, a self-signature is used to connect this new component to the certificate.
|
||||
|
||||
To bind identities to a certificate with a self-signature, signature types `0x10` - `0x13` can be used. Here, the signature type `0x13` (*positive certification*) is used.
|
||||
|
||||
```text
|
||||
$ sq packet dump --hex alice.pub-3--Signature
|
||||
Signature Packet, new CTB, 2 header bytes + 185 bytes
|
||||
Version: 6
|
||||
Type: PositiveCertification
|
||||
Pk algo: Ed25519
|
||||
Hash algo: SHA512
|
||||
Hashed area:
|
||||
Signature creation time: 2023-09-29 15:17:58 UTC (critical)
|
||||
Key expiration time: P1095DT62781S (critical)
|
||||
Symmetric algo preferences: AES256, AES128
|
||||
Hash preferences: SHA512, SHA256
|
||||
Primary User ID: true (critical)
|
||||
Key flags: C (critical)
|
||||
Features: MDC
|
||||
Issuer Fingerprint: AAA18CBB254685C58358320563FD37B67F3300F9FB0EC457378CD29F102698B3
|
||||
Unhashed area:
|
||||
Issuer: AAA18CBB254685C5
|
||||
Digest prefix: DBB8
|
||||
Level: 0 (signature over data)
|
||||
|
||||
00000000 c2 CTB
|
||||
00000001 b9 length
|
||||
00000002 06 version
|
||||
00000003 13 type
|
||||
00000004 1b pk_algo
|
||||
00000005 0a hash_algo
|
||||
00000006 00 00 00 40 hashed_area_len
|
||||
0000000a 05 subpacket length
|
||||
0000000b 82 subpacket tag
|
||||
0000000c 65 16 ea a6 sig creation time
|
||||
00000010 05 subpacket length
|
||||
00000011 89 subpacket tag
|
||||
00000012 05 a4 8f bd key expiry time
|
||||
00000016 03 subpacket length
|
||||
00000017 0b subpacket tag
|
||||
00000018 09 07 pref sym algos
|
||||
0000001a 03 subpacket length
|
||||
0000001b 15 subpacket tag
|
||||
0000001c 0a 08 pref hash algos
|
||||
0000001e 02 subpacket length
|
||||
0000001f 99 subpacket tag
|
||||
00000020 01 primary user id
|
||||
00000021 02 subpacket length
|
||||
00000022 9b subpacket tag
|
||||
00000023 01 key flags
|
||||
00000024 02 subpacket length
|
||||
00000025 1e subpacket tag
|
||||
00000026 01 features
|
||||
00000027 22 subpacket length
|
||||
00000028 21 subpacket tag
|
||||
00000029 06 version
|
||||
0000002a aa a1 8c bb 25 46 issuer fp
|
||||
00000030 85 c5 83 58 32 05 63 fd 37 b6 7f 33 00 f9 fb 0e
|
||||
00000040 c4 57 37 8c d2 9f 10 26 98 b3
|
||||
0000004a 00 00 00 0a unhashed_area_len
|
||||
0000004e 09 subpacket length
|
||||
0000004f 10 subpacket tag
|
||||
00000050 aa a1 8c bb 25 46 85 c5 issuer
|
||||
00000058 db digest_prefix1
|
||||
00000059 b8 digest_prefix2
|
||||
0000005a 20 salt_len
|
||||
0000005b 8a 2d 6f da 67 salt
|
||||
00000060 35 bc 5d 04 77 b4 9d 67 a8 6e c5 d6 88 53 5f e2
|
||||
00000070 ef f9 66 08 bf c2 e0 db c0 56 0d
|
||||
0000007b eb d4 2c a5 19 ed25519_sig
|
||||
00000080 01 0f ba 26 d0 82 a2 cf 5c eb 7a a9 72 d9 f3 b2
|
||||
00000090 66 07 8b b2 ba 3d b7 89 e4 76 04 6e 35 24 2b 27
|
||||
000000a0 29 83 be 91 9c 78 6a cc b4 d5 69 47 76 2c 29 d6
|
||||
000000b0 54 bf 43 19 04 ff 53 98 c0 d5 0b
|
||||
```
|
||||
|
||||
|
||||
Because this packet structure closely mirrors the [Direct Key Signature](zoom-dks) discussed above, we will cover this succinctly.
|
||||
|
||||
We're again looking at a Signature packet. Its `type` is `0x13` ([corresponding to a *positive certification* signature](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-signature-types)).
|
||||
|
||||
The designated public key algorithm and hash function for this signature are Ed25519 and SHA512, respectively.
|
||||
|
||||
As shown in the text atop this packet dump, the hashed subpacket data contains the following metadata:
|
||||
|
||||
- Signature creation time: `2023-09-29 15:17:58 UTC` (**critical**)
|
||||
- Key expiration time: `P1095DT62781S` (**critical**)
|
||||
- Symmetric algo preferences: `AES256, AES128`
|
||||
- Hash preferences: `SHA512, SHA256`
|
||||
- Primary User ID: `true` (**critical**)
|
||||
- Key flags: `C` (**critical**)
|
||||
- Features: `MDC`
|
||||
- Issuer fingerprint: `AAA18CBB254685C58358320563FD37B67F3300F9FB0EC457378CD29F102698B3`
|
||||
|
||||
This is a combination of metadata about the User ID itself (designating this User ID as the *primary User ID* of this certificate), algorithm preferences for this identity, and settings that apply to the primary key.
|
||||
|
||||
````{note}
|
||||
Historically, the self-signature that binds the primary User ID to the certificate also contains subpackets relevant not to the User ID, but to the primary key itself.
|
||||
|
||||
Setting key expiration time and key flags on the primary User ID self-signature is one mechanism to configure the primary key.
|
||||
|
||||
The interaction between metadata on direct key signatures and User ID binding self-signatures [is subtle](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-notes-on-self-signatures), with changes between version 6 and version 4.
|
||||
````
|
||||
|
||||
This section is followed, again, by the (informational) unhashed subpacket area.
|
||||
|
||||
Subsequently, we see a salt value for the signature and the signature itself.
|
||||
|
||||
The signature is calculated over a hash. The hash, in this case, is derived from the following data:
|
||||
|
||||
- the signature's salt
|
||||
- the serialized primary key's public data
|
||||
- the serialized User ID
|
||||
This section specifies- the serialized self-signature packet (excluding the unhashed area)
|
||||
|
||||
Refer to [Computing Signatures](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-computing-signatures) in the RFC for details.
|
||||
|
||||
## Certifications (Third Party Signatures)
|
||||
|
||||
```{note}
|
||||
|
||||
This section is still about to be written.
|
||||
```
|
||||
|
||||
## Revocations
|
||||
|
||||
```{note}
|
||||
|
||||
This section is still about to be written.
|
||||
```
|
113
book/source/zoom/encryption.md
Normal file
|
@ -0,0 +1,113 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Zooming in: Packet structure of encrypted data
|
||||
|
||||
## SEIPD v2
|
||||
|
||||
### Encrypt
|
||||
|
||||
We encrypt a short message to Alice, using a public certificate version of {ref}`alice-priv`:
|
||||
|
||||
```text
|
||||
$ echo "hello world" | sq encrypt --recipient-file alice.pub
|
||||
```
|
||||
|
||||
This produces an ASCII armored encrypted message:
|
||||
|
||||
```{literalinclude} ../examples/ascii_armored_encrypted_message.asc
|
||||
:language: text
|
||||
```
|
||||
|
||||
### Inspect the packet dump of the encrypted message
|
||||
|
||||
Inspecting the packets of this message, we see:
|
||||
|
||||
```text
|
||||
$ sq packet dump --hex enc.pgp
|
||||
Public-Key Encrypted Session Key Packet, new CTB, 2 header bytes + 93 bytes
|
||||
Version: 6
|
||||
Recipient: C0A58384A438E5A14F73712426A4D45DBAEEF4A39E6B30B09D5513F978ACCA94
|
||||
Pk algo: X25519
|
||||
|
||||
00000000 c1 CTB
|
||||
00000001 5d length
|
||||
00000002 06 version
|
||||
00000003 21 recipient_len
|
||||
00000004 06 recipient_version
|
||||
00000005 c0 a5 83 84 a4 38 e5 a1 4f 73 71 recipient
|
||||
00000010 24 26 a4 d4 5d ba ee f4 a3 9e 6b 30 b0 9d 55 13
|
||||
00000020 f9 78 ac ca 94
|
||||
00000025 19 pk_algo
|
||||
00000026 31 61 a4 33 ba eb 03 ef ca 23 x25519_e
|
||||
00000030 01 d8 86 c1 0f 0c 23 cb 12 01 70 15 e0 01 0c 28
|
||||
00000040 87 1b 20 da a1 20
|
||||
00000046 18 x25519_esk_len
|
||||
00000047 12 9b 91 15 f6 1f 98 97 f5 x25519_esk
|
||||
00000050 d5 dd f1 19 d4 93 9a 06 7a ed b2 8d 99 01 2e
|
||||
|
||||
Sym. Encrypted and Integrity Protected Data Packet, new CTB, 2 header bytes + 114 bytes
|
||||
Version: 2
|
||||
Symmetric algo: AES-128
|
||||
AEAD algo: EAX
|
||||
Chunk size: 4096
|
||||
Salt: 9673F229E1386AD0464367096945493F4D42FE4D6129B06750B3C89F0F214093
|
||||
No session key supplied
|
||||
|
||||
00000000 d2 CTB
|
||||
00000001 72 length
|
||||
00000002 02 version
|
||||
00000003 07 sym_algo
|
||||
00000004 01 aead_algo
|
||||
00000005 06 chunk_size
|
||||
00000006 96 73 f2 29 e1 38 6a d0 46 43 salt
|
||||
00000010 67 09 69 45 49 3f 4d 42 fe 4d 61 29 b0 67 50 b3
|
||||
00000020 c8 9f 0f 21 40 93
|
||||
00000026 bb 77 fb 75 ef bc ba f9 75 48 .w.u....uH
|
||||
00000030 37 f8 eb 7e b0 44 a4 09 28 e1 ad 99 39 d0 72 23 7..~.D..(...9.r#
|
||||
00000040 c2 30 55 67 a6 35 e7 dc 9f 68 ea ad b4 c4 fa 71 .0Ug.5...h.....q
|
||||
00000050 7a 96 6b 12 22 b2 13 da 27 e3 91 d6 ad 9b 65 2d z.k."...'.....e-
|
||||
00000060 4d da 31 5b 69 13 8e 71 b0 12 2b a0 15 ce a0 96 M.1[i..q..+.....
|
||||
00000070 9d ea a4 20 ...
|
||||
```
|
||||
|
||||
### Decrypt
|
||||
|
||||
```text
|
||||
$ sq decrypt --dump-session-key --recipient-file alice.sec enc.pgp
|
||||
Session key: 8DDA27B9B000BD84D0A39DFF66780111
|
||||
Encrypted using AES-128
|
||||
Compressed using ZIP
|
||||
hello world
|
||||
```
|
||||
|
||||
Inspecting the packets inside the SEIPD container:
|
||||
|
||||
```text
|
||||
$ sq decrypt --dump --recipient-file alice.sec enc.pgp
|
||||
Public-Key Encrypted Session Key Packet, new CTB, 93 bytes
|
||||
Version: 6
|
||||
Recipient: C0A58384A438E5A14F73712426A4D45DBAEEF4A39E6B30B09D5513F978ACCA94
|
||||
Pk algo: X25519
|
||||
|
||||
Encrypted using AES-128
|
||||
Compressed using ZIP
|
||||
hello world
|
||||
Sym. Encrypted and Integrity Protected Data Packet, new CTB, 114 bytes
|
||||
│ Version: 2
|
||||
│ Symmetric algo: AES-128
|
||||
│ AEAD algo: EAX
|
||||
│ Chunk size: 4096
|
||||
│ Salt: 9673F229E1386AD0464367096945493F4D42FE4D6129B06750B3C89F0F214093
|
||||
│
|
||||
└── Compressed Data Packet, new CTB, 44 bytes
|
||||
│ Algorithm: ZIP
|
||||
│
|
||||
├── Literal Data Packet, new CTB, 18 bytes
|
||||
│ Format: Binary data
|
||||
│
|
||||
└── Padding Packet, new CTB, 14 bytes
|
||||
Unknown variant
|
||||
```
|
192
book/source/zoom/private_keys.md
Normal file
|
@ -0,0 +1,192 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Zooming in: Packet structure of private key material
|
||||
|
||||
## A look at Alice's (unencrypted) private key packets
|
||||
|
||||
Let's take a look at the key material packets of [Alice's key](alice-priv).
|
||||
|
||||
To inspect the internal structure of Alice's key, we run the Sequoia-PGP tool `sq` (using the `packet dump` subcommand). The output of `sq` is one big block of text. To discuss the relevant content, we'll only show the output for the packets that contain key data, here:
|
||||
|
||||
```text
|
||||
$ sq packet dump --hex alice.priv
|
||||
```
|
||||
|
||||
### Primary Secret-Key packet
|
||||
|
||||
The output starts with the (primary) [Secret-Key packet](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-secret-key-packet-formats).
|
||||
|
||||
This is the structure of the Secret-Key packet we will now look at.
|
||||
|
||||
```{figure} ../plain_svg/secret-key_packet.svg
|
||||
:name: fig-secret-key-packet
|
||||
:alt: Depicts a box with white background and title "Secret-Key packet". In the center a box with white background and red frame is shown. Inside it several items are listed, separated by red dotted horizontal lines. The first three are "Version", "Creation Time", "Public-Key Algorithm" written in black. The fourth one is written in green and reads "Public Key Material" and has the green public key symbol at its right side. The fifth one is again written in black and reads "S2K Usage (Secret Key Encryption)". The sixth item reads "Secret Key Material", written in red and has the red private key symbol at its right side.
|
||||
|
||||
Structure of a Secret-Key packet.
|
||||
```
|
||||
|
||||
The output of Sequoia's `sq packet dump` for this packet:
|
||||
|
||||
```text
|
||||
Secret-Key Packet, new CTB, 2 header bytes + 75 bytes
|
||||
Version: 6
|
||||
Creation time: 2023-09-29 15:17:58 UTC
|
||||
Pk algo: Ed25519
|
||||
Pk size: 256 bits
|
||||
Fingerprint: AAA18CBB254685C58358320563FD37B67F3300F9FB0EC457378CD29F102698B3
|
||||
KeyID: AAA18CBB254685C5
|
||||
|
||||
Secret Key:
|
||||
|
||||
Unencrypted
|
||||
|
||||
00000000 c5 CTB
|
||||
00000001 4b length
|
||||
00000002 06 version
|
||||
00000003 65 16 ea a6 creation_time
|
||||
00000007 1b pk_algo
|
||||
00000008 00 00 00 20 public_len
|
||||
0000000c 53 24 e9 43 ed25519_public
|
||||
00000010 af ab 15 f7 6e d5 b5 12 98 79 69 cd 1b 5d 10 65
|
||||
00000020 eb e7 42 e2 ab 47 f4 86 b3 ae 65 3e
|
||||
0000002c 00 s2k_usage
|
||||
0000002d ef e1 99 ed25519_secret
|
||||
00000030 b5 5f 11 fb aa 93 e8 26 9d 3b b2 2d 72 20 7d ff
|
||||
00000040 bd 42 dd 4b e9 a3 36 81 3b a5 cc cf fb
|
||||
```
|
||||
|
||||
The Secret-Key packet consists in large part of the actual cryptographic key data. Notice that its content is almost entirely the same as the Public-Key packet [seen in the previous chapter](zoom-public-key). Let's look at the packet field by field:
|
||||
|
||||
- `CTB: 0xc5`[^CTB]: The [packet type ID](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-packet-headers) for this packet. The binary representation of the value `0xc5` is `11000101`. Bits 7 and 6 show that the packet is in *OpenPGP packet format* (as opposed to in *Legacy packet format*). The remaining 6 bits encode the type ID's value: "5". This is the value for a Secret-Key packet, as shown in the list of [packet type IDs](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-packet-types).
|
||||
- `length: 0x4b`: The remaining length of this packet.
|
||||
|
||||
[^CTB]: Sequoia uses the term CTB ({term}`Cipher Type Byte`) to refer to the [packet type ID](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-packet-headers).
|
||||
|
||||
The packet type id defines the semantics of the remaining data in the packet. We're looking at a Secret-Key packet, which is a kind of [Key Material Packet](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-key-material-packets).
|
||||
|
||||
- `version: 0x06`: The key material is in version 6 format
|
||||
|
||||
This means that the next part of the packet follows the structure of [Version 6 Public Keys](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-version-6-public-keys)
|
||||
|
||||
- `creation_time: 0x6516eaa6`: "The time that the key was created" (also see [Time Fields](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-time-fields))
|
||||
- `pk_algo: 0x1b`: "The public-key algorithm ID of this key" (decimal value 27, see the list of [Public-Key Algorithms](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-public-key-algorithms))
|
||||
- `public_len: 0x00000020`: "Octet count for the following public key material" (in this case, the length of the following `ed25519_public` field)
|
||||
- `ed25519_public`: [Algorithm-specific representation](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-algorithm-specific-part-for-ed2) of the public key material (the format is based on the value of `pk_algo`), in this case 32 bytes of Ed25519 public key
|
||||
|
||||
This concludes the Public Key section of the packet. The remaining data follows the [Secret-Key packet format](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-secret-key-packet-formats):
|
||||
|
||||
- `s2k_usage: 0x00`: The [*S2K usage* value](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-secret-key-encryption-s2k-u) of `0x00` specifies that the secret-key data is not encrypted
|
||||
- `ed25519_secret`: [Algorithm-specific representation](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-algorithm-specific-part-for-ed2) of the secret key data (the format is based on the value of `pk_algo`). Because the private key material in this packet is not encrypted, this field
|
||||
|
||||
```{tip}
|
||||
|
||||
The overall structure of OpenPGP packets is described in the [Packet Syntax](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-packet-syntax) chapter of the RFC.
|
||||
```
|
||||
|
||||
Note that the *Secret-Key packet* contains both the private and the public part of the key.
|
||||
|
||||
### Secret-Subkey packet
|
||||
|
||||
Further down in the "packet dump" of Alice's key, we see the encryption subkey, which we already inspected in its Public-Subkey packet format, [above](zoom-subkey-enc):
|
||||
|
||||
```text
|
||||
Secret-Subkey Packet, new CTB, 2 header bytes + 75 bytes
|
||||
Version: 6
|
||||
Creation time: 2023-09-29 15:17:58 UTC
|
||||
Pk algo: X25519
|
||||
Pk size: 256 bits
|
||||
Fingerprint: C0A58384A438E5A14F73712426A4D45DBAEEF4A39E6B30B09D5513F978ACCA94
|
||||
KeyID: C0A58384A438E5A1
|
||||
|
||||
Secret Key:
|
||||
|
||||
Unencrypted
|
||||
|
||||
00000000 c7 CTB
|
||||
00000001 4b length
|
||||
00000002 06 version
|
||||
00000003 65 16 ea a6 creation_time
|
||||
00000007 19 pk_algo
|
||||
00000008 00 00 00 20 public_len
|
||||
0000000c d1 ae 87 d7 x25519_public
|
||||
00000010 cc 42 af 99 34 c5 c2 5c ca fa b7 4a c8 43 fc 86
|
||||
00000020 35 2a 46 01 f3 cc 00 f5 4a 09 3e 3f
|
||||
0000002c 00 s2k_usage
|
||||
0000002d 28 7d cd x25519_secret
|
||||
00000030 da 26 16 37 8d ea 24 c7 ce e7 70 c7 9b e5 6f 0a
|
||||
00000040 c9 77 fb bd 23 41 73 c9 57 5a bf 7c 4c
|
||||
```
|
||||
|
||||
Again, this packet consists of the same content as its Public-Subkey equivalent, followed by two additional fields:
|
||||
|
||||
- The "S2K usage" field, which indicated whether the private key material is encrypted. Like Alice's primary key (above), this subkey is not encrypted.
|
||||
- The private key material: in this case, the algorithm-specific private key data consists of 32 bytes of `x25519_secret` data.
|
||||
|
||||
As with the public key material, the difference between the format of this subkey packet and the private key packet is minimal: Only the packet type ID differs.
|
||||
|
||||
## Bob's (encrypted) private key material
|
||||
|
||||
Now we look at the primary key material packet of [Bob's key](bob-priv), which uses passphrase protection.
|
||||
|
||||
```text
|
||||
Secret-Key Packet, new CTB, 2 header bytes + 134 bytes
|
||||
Version: 6
|
||||
Creation time: 2023-10-13 14:29:00 UTC
|
||||
Pk algo: Ed25519
|
||||
Pk size: 256 bits
|
||||
Fingerprint: BB289FB7A68DBFA8C384CCCDE2058E02D9C6CD2F3C7C56AE7FB53D971170BA83
|
||||
KeyID: BB289FB7A68DBFA8
|
||||
|
||||
Secret Key:
|
||||
|
||||
Encrypted
|
||||
S2K: Argon2id with t: 1, p: 4, m: 2^21, salt: 3B7F4B0EAC8B39625AB4D4BD690413C7 Sym. algo: AES-256
|
||||
|
||||
00000000 c5 CTB
|
||||
00000001 86 length
|
||||
00000002 06 version
|
||||
00000003 65 29 54 2c creation_time
|
||||
00000007 1b pk_algo
|
||||
00000008 00 00 00 20 public_len
|
||||
0000000c 47 e7 c2 dc ed25519_public
|
||||
00000010 58 8e cb fd f2 49 90 66 ae aa 36 66 ca a9 55 2d
|
||||
00000020 71 88 7c 25 91 c3 75 73 1d 07 60 d6
|
||||
0000002c fe s2k_usage
|
||||
0000002d 16 parameters_len
|
||||
0000002e 09 sym_algo
|
||||
0000002f 14 s2k_len
|
||||
00000030 04 s2k_type
|
||||
00000031 3b 7f 4b 0e ac 8b 39 62 5a b4 d4 bd 69 04 13 argon2_salt
|
||||
00000040 c7
|
||||
00000041 01 argon2_t
|
||||
00000042 04 argon2_p
|
||||
00000043 15 argon2_m
|
||||
00000044 21 ff be fc f1 c5 9c 75 9d 1f d1 f8 encrypted_mpis
|
||||
00000050 19 e7 fd 47 55 e3 69 ff 2f e8 52 48 66 03 d3 37
|
||||
00000060 52 7b 05 cb fa b1 f8 13 f7 f6 20 88 d6 f5 8b c4
|
||||
00000070 b4 51 52 ba 6d f9 7c 1a ee 9f e6 b1 fb 63 d1 ca
|
||||
00000080 4a 3f 33 d9 2c c9 26 46
|
||||
```
|
||||
|
||||
The first portion of Bob's Secret-Key packet has the same structure as Alice's, but beginning at the `s2k_usage`, we see different data. The format of this data is described in [Secret-Key Packet Formats](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-secret-key-packet-formats).
|
||||
|
||||
- `s2k_usage: 0xfe`: [S2K usage](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-secret-key-encryption-s2k-u) is set to `AEAD`, here (decimal value 253).
|
||||
- `parameters_len: 0x16` (decimal value: 22): "Cumulative length of all the following conditionally included string-to-key parameter fields."
|
||||
- `sym_algo: 0x9`: [Symmetric-Key Algorithm](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-symmetric-key-algorithms) specifies that AES 256 is used as the AEAD algorithm
|
||||
- `s2k_len: 0x14` (decimal value 20): "[..] count of the size of the one field following this octet"
|
||||
|
||||
The next set of data is the "string-to-key (S2K) specifier." Its format depends on the type.
|
||||
|
||||
- `s2k_type: 0x04` [String-to-Key (S2K) Specifier Type](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-string-to-key-s2k-specifier-), set to *Argon2* here.
|
||||
|
||||
The next fields are [specific to Argon2](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-argon2):
|
||||
|
||||
- `argon2_salt`: "16-octet salt value"
|
||||
- `argon2_t`: "number of passes t"
|
||||
- `argon2_p`: "degree of parallelism p"
|
||||
- `argon2_m`: "the exponent of the memory size"
|
||||
|
||||
"Plain or encrypted multiprecision integers comprising the secret key data. This is algorithm-specific and described in Section 5.5.5. If the string-to-key usage octet is 253 (AEAD), then an AEAD authentication tag is at the end of that data."
|
231
book/source/zoom/signatures.md
Normal file
|
@ -0,0 +1,231 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Zooming in: Packet structure of data signatures
|
||||
|
||||
In this chapter, we'll create signatures using [Alice's private key](alice-priv) material, and inspect the packet structure of those signatures.
|
||||
|
||||
In some examples, we'll use a test-message that contains the string `hello world` followed by one line feed (`0x0a`) character:
|
||||
|
||||
```text
|
||||
$ echo "hello world" > message.txt
|
||||
```
|
||||
|
||||
## Detached signature
|
||||
|
||||
We can produce a detached signature for this "hello world" message, using Alice's private signing key material:
|
||||
|
||||
```text
|
||||
$ sq sign --detached --signer-file alice.pgp message.txt
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
wpgGABsKAAAAKQWCZT0tDyIhBtB7JOyRoU3SQKwtU+bIqeBUlJpBIi6nOFdu0Zyu
|
||||
o9yZAAAAANueIJCkVJ5aC1Zw485o7Y72uHPnk7ktkZyhKH2MuHjCdIHQU0qe/8bR
|
||||
0B3ywHNzLwUoqj0efYWhj6XeXa08haxUH7i50MEDjfFrPc281B0C5fiiGN4PYc76
|
||||
B8tA2/ZjsSgHCw==
|
||||
=n8EV
|
||||
-----END PGP SIGNATURE-----
|
||||
```
|
||||
|
||||
And inspect the packet structure of this signature:
|
||||
|
||||
```text
|
||||
$ sq packet dump --hex detached-sig.txt
|
||||
Signature Packet, new CTB, 2 header bytes + 152 bytes
|
||||
Version: 6
|
||||
Type: Binary
|
||||
Pk algo: Ed25519
|
||||
Hash algo: SHA512
|
||||
Hashed area:
|
||||
Signature creation time: 2023-10-28 15:47:27 UTC (critical)
|
||||
Issuer Fingerprint: D07B24EC91A14DD240AC2D53E6C8A9E054949A41222EA738576ED19CAEA3DC99
|
||||
Digest prefix: DB9E
|
||||
Level: 0 (signature over data)
|
||||
|
||||
00000000 c2 CTB
|
||||
00000001 98 length
|
||||
00000002 06 version
|
||||
00000003 00 type
|
||||
00000004 1b pk_algo
|
||||
00000005 0a hash_algo
|
||||
00000006 00 00 00 29 hashed_area_len
|
||||
0000000a 05 subpacket length
|
||||
0000000b 82 subpacket tag
|
||||
0000000c 65 3d 2d 0f sig creation time
|
||||
00000010 22 subpacket length
|
||||
00000011 21 subpacket tag
|
||||
00000012 06 version
|
||||
00000013 d0 7b 24 ec 91 a1 4d d2 40 ac 2d 53 e6 issuer fp
|
||||
00000020 c8 a9 e0 54 94 9a 41 22 2e a7 38 57 6e d1 9c ae
|
||||
00000030 a3 dc 99
|
||||
00000033 00 00 00 00 unhashed_area_len
|
||||
00000037 db digest_prefix1
|
||||
00000038 9e digest_prefix2
|
||||
00000039 20 salt_len
|
||||
0000003a 90 a4 54 9e 5a 0b salt
|
||||
00000040 56 70 e3 ce 68 ed 8e f6 b8 73 e7 93 b9 2d 91 9c
|
||||
00000050 a1 28 7d 8c b8 78 c2 74 81 d0
|
||||
0000005a 53 4a 9e ff c6 d1 ed25519_sig
|
||||
00000060 d0 1d f2 c0 73 73 2f 05 28 aa 3d 1e 7d 85 a1 8f
|
||||
00000070 a5 de 5d ad 3c 85 ac 54 1f b8 b9 d0 c1 03 8d f1
|
||||
00000080 6b 3d cd bc d4 1d 02 e5 f8 a2 18 de 0f 61 ce fa
|
||||
00000090 07 cb 40 db f6 63 b1 28 07 0b
|
||||
```
|
||||
|
||||
## Inline signature
|
||||
|
||||
```text
|
||||
$ sq sign --signer-file alice.pgp message.txt
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
xEYGAAobIK+vlFDAK62+055LpOCoOGecp66NiyRz6M+emCLp5Nbg0Hsk7JGhTdJA
|
||||
rC1T5sip4FSUmkEiLqc4V27RnK6j3JkByxJiAAAAAABoZWxsbyB3b3JsZArCmAYA
|
||||
GwoAAAApBYJlPXuNIiEG0Hsk7JGhTdJArC1T5sip4FSUmkEiLqc4V27RnK6j3JkA
|
||||
AAAAhrggr6+UUMArrb7Tnkuk4Kg4Z5ynro2LJHPoz56YIunk1uApSiAe9CYGgqrs
|
||||
p6Ud6ARDVcOWWFhxTJK2rNULlZ9k4HPFvUT4PTrjpb4kjRAb6MDgSSclPaj14FjL
|
||||
rpr/eqQF
|
||||
=r993
|
||||
-----END PGP MESSAGE-----
|
||||
```
|
||||
|
||||
```text
|
||||
$ sq packet dump --hex inline-sig.txt
|
||||
One-Pass Signature Packet, new CTB, 2 header bytes + 70 bytes
|
||||
Version: 6
|
||||
Type: Binary
|
||||
Pk algo: Ed25519
|
||||
Hash algo: SHA512
|
||||
Issuer: D07B24EC91A14DD240AC2D53E6C8A9E054949A41222EA738576ED19CAEA3DC99
|
||||
Last: true
|
||||
|
||||
00000000 c4 CTB
|
||||
00000001 46 length
|
||||
00000002 06 version
|
||||
00000003 00 type
|
||||
00000004 0a hash_algo
|
||||
00000005 1b pk_algo
|
||||
00000006 20 salt_len
|
||||
00000007 af af 94 50 c0 2b ad be d3 salt
|
||||
00000010 9e 4b a4 e0 a8 38 67 9c a7 ae 8d 8b 24 73 e8 cf
|
||||
00000020 9e 98 22 e9 e4 d6 e0
|
||||
00000027 d0 7b 24 ec 91 a1 4d d2 40 issuer
|
||||
00000030 ac 2d 53 e6 c8 a9 e0 54 94 9a 41 22 2e a7 38 57
|
||||
00000040 6e d1 9c ae a3 dc 99
|
||||
00000047 01 last
|
||||
|
||||
Literal Data Packet, new CTB, 2 header bytes + 18 bytes
|
||||
Format: Binary data
|
||||
Content: "hello world\n"
|
||||
|
||||
00000000 cb CTB
|
||||
00000001 12 length
|
||||
00000002 62 format
|
||||
00000003 00 filename_len
|
||||
00000004 00 00 00 00 date
|
||||
00000008 68 65 6c 6c 6f 20 77 6f hello wo
|
||||
00000010 72 6c 64 0a rld.
|
||||
|
||||
Signature Packet, new CTB, 2 header bytes + 152 bytes
|
||||
Version: 6
|
||||
Type: Binary
|
||||
Pk algo: Ed25519
|
||||
Hash algo: SHA512
|
||||
Hashed area:
|
||||
Signature creation time: 2023-10-28 21:22:21 UTC (critical)
|
||||
Issuer Fingerprint: D07B24EC91A14DD240AC2D53E6C8A9E054949A41222EA738576ED19CAEA3DC99
|
||||
Digest prefix: 86B8
|
||||
Level: 0 (signature over data)
|
||||
|
||||
00000000 c2 CTB
|
||||
00000001 98 length
|
||||
00000002 06 version
|
||||
00000003 00 type
|
||||
00000004 1b pk_algo
|
||||
00000005 0a hash_algo
|
||||
00000006 00 00 00 29 hashed_area_len
|
||||
0000000a 05 subpacket length
|
||||
0000000b 82 subpacket tag
|
||||
0000000c 65 3d 7b 8d sig creation time
|
||||
00000010 22 subpacket length
|
||||
00000011 21 subpacket tag
|
||||
00000012 06 version
|
||||
00000013 d0 7b 24 ec 91 a1 4d d2 40 ac 2d 53 e6 issuer fp
|
||||
00000020 c8 a9 e0 54 94 9a 41 22 2e a7 38 57 6e d1 9c ae
|
||||
00000030 a3 dc 99
|
||||
00000033 00 00 00 00 unhashed_area_len
|
||||
00000037 86 digest_prefix1
|
||||
00000038 b8 digest_prefix2
|
||||
00000039 20 salt_len
|
||||
0000003a af af 94 50 c0 2b salt
|
||||
00000040 ad be d3 9e 4b a4 e0 a8 38 67 9c a7 ae 8d 8b 24
|
||||
00000050 73 e8 cf 9e 98 22 e9 e4 d6 e0
|
||||
0000005a 29 4a 20 1e f4 26 ed25519_sig
|
||||
00000060 06 82 aa ec a7 a5 1d e8 04 43 55 c3 96 58 58 71
|
||||
00000070 4c 92 b6 ac d5 0b 95 9f 64 e0 73 c5 bd 44 f8 3d
|
||||
00000080 3a e3 a5 be 24 8d 10 1b e8 c0 e0 49 27 25 3d a8
|
||||
00000090 f5 e0 58 cb ae 9a ff 7a a4 05
|
||||
```
|
||||
|
||||
(cleartext)=
|
||||
## Cleartext signature
|
||||
|
||||
```text
|
||||
$ sq sign --cleartext-signature --signer-file alice.pgp message.txt
|
||||
-----BEGIN PGP SIGNED MESSAGE-----
|
||||
Hash: SHA512
|
||||
|
||||
hello world
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
wpgGARsKAAAAKQWCZT0vBCIhBtB7JOyRoU3SQKwtU+bIqeBUlJpBIi6nOFdu0Zyu
|
||||
o9yZAAAAANqgIHAzoRTzu/7Zuxc8Izf4r3/qSCmBfDqWzTXqmVtsSBSHACka3qbN
|
||||
eehqu8H6S0UK8V7yHbpVhExu9Hu72jWEzU/B0h9MR5gDhJPoWurx8YfyXBDsRS4y
|
||||
r13/eqMN8kfCDw==
|
||||
=Ks9w
|
||||
-----END PGP SIGNATURE-----
|
||||
```
|
||||
|
||||
```text
|
||||
$ sq packet dump --hex cleartext-sig.txt
|
||||
Signature Packet, new CTB, 2 header bytes + 152 bytes
|
||||
Version: 6
|
||||
Type: Text
|
||||
Pk algo: Ed25519
|
||||
Hash algo: SHA512
|
||||
Hashed area:
|
||||
Signature creation time: 2023-10-28 15:55:48 UTC (critical)
|
||||
Issuer Fingerprint: D07B24EC91A14DD240AC2D53E6C8A9E054949A41222EA738576ED19CAEA3DC99
|
||||
Digest prefix: DAA0
|
||||
Level: 0 (signature over data)
|
||||
|
||||
00000000 c2 CTB
|
||||
00000001 98 length
|
||||
00000002 06 version
|
||||
00000003 01 type
|
||||
00000004 1b pk_algo
|
||||
00000005 0a hash_algo
|
||||
00000006 00 00 00 29 hashed_area_len
|
||||
0000000a 05 subpacket length
|
||||
0000000b 82 subpacket tag
|
||||
0000000c 65 3d 2f 04 sig creation time
|
||||
00000010 22 subpacket length
|
||||
00000011 21 subpacket tag
|
||||
00000012 06 version
|
||||
00000013 d0 7b 24 ec 91 a1 4d d2 40 ac 2d 53 e6 issuer fp
|
||||
00000020 c8 a9 e0 54 94 9a 41 22 2e a7 38 57 6e d1 9c ae
|
||||
00000030 a3 dc 99
|
||||
00000033 00 00 00 00 unhashed_area_len
|
||||
00000037 da digest_prefix1
|
||||
00000038 a0 digest_prefix2
|
||||
00000039 20 salt_len
|
||||
0000003a 70 33 a1 14 f3 bb salt
|
||||
00000040 fe d9 bb 17 3c 23 37 f8 af 7f ea 48 29 81 7c 3a
|
||||
00000050 96 cd 35 ea 99 5b 6c 48 14 87
|
||||
0000005a 00 29 1a de a6 cd ed25519_sig
|
||||
00000060 79 e8 6a bb c1 fa 4b 45 0a f1 5e f2 1d ba 55 84
|
||||
00000070 4c 6e f4 7b bb da 35 84 cd 4f c1 d2 1f 4c 47 98
|
||||
00000080 03 84 93 e8 5a ea f1 f1 87 f2 5c 10 ec 45 2e 32
|
||||
00000090 af 5d ff 7a a3 0d f2 47 c2 0f
|
||||
```
|