Compare commits

...

3 Commits

Author SHA1 Message Date
brent saner
1de61a888d
use named links for each ident 2025-09-01 13:47:05 -04:00
4b1cfd0c50
releasing key guide under CC 4.0 BY-SA 2023-09-04 01:40:39 -04:00
b38739f960
Sooo the golang ChaCha20-Poly1305 is broken.
By design. It does not allow for the OpenSSH variant (https://github.com/golang/go/issues/36646).

So I'll need to split out that package (and their internal poly1305 package) and maintain an internal variant of it. Ugh.
2022-04-29 16:31:13 -04:00
32 changed files with 536 additions and 373 deletions

3
.gitignore vendored
View File

@ -30,6 +30,9 @@
*.test *.test
!*test.go !*test.go
# Junk for figuring stuff out
/poc/
# Output of the go coverage tool, specifically when used with LiteIDE # Output of the go coverage tool, specifically when used with LiteIDE
*.out *.out

View File

@ -3,3 +3,5 @@
OpenSSH key parsing, generation, etc. library for Golang. OpenSSH key parsing, generation, etc. library for Golang.
Supports newer "proprietary" key type formatting ("OpenSSH v1"). Supports newer "proprietary" key type formatting ("OpenSSH v1").
**WORK IN PROGRESS**

View File

@ -15,17 +15,28 @@ Last updated {localdatetime}
:source-highlighter: rouge :source-highlighter: rouge
:docinfo: shared :docinfo: shared
////
This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
To view a copy of this license, visit
http://creativecommons.org/licenses/by-sa/4.0/.
////
[id="why"]
== Purpose == Purpose
This document attempts to present a much more detailed, thorough, and easily-understood form of the key formats used by OpenSSH. The extent of those formats' canonical documentation is https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key[the OpenSSH source tree's `PROTOCOL.key`^], which is a little lacking. This document attempts to present a much more detailed, thorough, and easily-understood form of the key formats used by OpenSSH. The extent of those formats' canonical documentation is https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key[the OpenSSH source tree's `PROTOCOL.key`^], which is a little lacking.
[id="intro"]
== Basic Introduction == Basic Introduction
=== Legacy
==== Private Keys
[id="intro_legc"]
=== Legacy
[id="intro_legc_priv"]
==== Private Keys
In OpenSSH pre-7.8, private keys are stored in their respective PEM encodingfootnote:[https://datatracker.ietf.org/doc/html/rfc7468] with no modification. These legacy private keys should be entirely usable by OpenSSL/LibreSSL/GnuTLS etc. natively with no conversion necessary. In OpenSSH pre-7.8, private keys are stored in their respective PEM encodingfootnote:[https://datatracker.ietf.org/doc/html/rfc7468] with no modification. These legacy private keys should be entirely usable by OpenSSL/LibreSSL/GnuTLS etc. natively with no conversion necessary.
[id="intro_legc_pub"]
==== Public Keys ==== Public Keys
Each public key *file* (`*.pub`) is written out in the following format: Each public key *file* (`*.pub`) is written out in the following format:
A B C A B C
@ -38,13 +49,14 @@ C:: The key's comment
The structures specified in the breakdowns later in this document describe the _decoded_ version of *B* *_only_*. They are specific to each keytype and format version starting with item `2.0`. The structures specified in the breakdowns later in this document describe the _decoded_ version of *B* *_only_*. They are specific to each keytype and format version starting with item `2.0`.
[id="intro_v1"]
=== New "v1" Format === New "v1" Format
==== Private Keys
[id="intro_v1_priv"]
==== Private Keys
Private key structures have been retooled in the "v1" format. In recent OpenSSH versions, all new keys use the v1 format. They no longer are in straight PEM-compatible format. Private key structures have been retooled in the "v1" format. In recent OpenSSH versions, all new keys use the v1 format. They no longer are in straight PEM-compatible format.
Refer to https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key[`PROTOCOL.key`^] for a (very) general description, or each key's specific breakdown for more detailed information. Refer to https://anongit.mindrot.org/openssh.git/tree/PROTOCOL.key[`PROTOCOL.key`^] (https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key[GitHub mirror^]) for a (very) general description, or each key type's specific breakdown in this document for more detailed information.
The v1 format offers several benefits over the legacy format, including: The v1 format offers several benefits over the legacy format, including:
@ -53,12 +65,26 @@ The v1 format offers several benefits over the legacy format, including:
* embedded public key (no need to derive from the private key) * embedded public key (no need to derive from the private key)
* "checksumming" to confirm proper decryption for encrypted keys * "checksumming" to confirm proper decryption for encrypted keys
[id="intro_v1_pub"]
==== Public Keys ==== Public Keys
All public keys in v1 continue to use the same packed binary format as <<intro_legc_pub, the legacy format>>.
All public keys in v1 continue to use the same packed binary format as <<public_keys, the legacy format>>. [id="bkdn"]
== Keytype-Specific Breakdowns == Keytype-Specific Breakdowns
include::rsa/main.adoc[] include::rsa/main.adoc[]
include::ed25519/main.adoc[] include::ed25519/main.adoc[]
[id="moar"]
== Further Information
++++
<a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">
<img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-sa/4.0/88x31.png" />
</a><br />
<span xmlns:dct="http://purl.org/dc/terms/" href="http://purl.org/dc/dcmitype/Text" property="dct:title" rel="dct:type">OpenSSH Key Structure Guide</span> by <a xmlns:cc="http://creativecommons.org/ns#" href="https://r00t2.io/" property="cc:attributionName" rel="cc:attributionURL">Brent Saner</a> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">Creative Commons Attribution-ShareAlike 4.0 International License</a>.<br />
<br />
++++
You are free to use, distribute, modify, redistribute, use for commercial purposes, etc. with very few restrictions; please see http://creativecommons.org/licenses/by-sa/4.0/[the license summary^] and https://creativecommons.org/licenses/by-sa/4.0/legalcode[full license^] for further details.

View File

@ -4,7 +4,7 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 2.0.17"> <meta name="generator" content="Asciidoctor 2.0.23">
<meta name="author" content="brent saner &lt;bts@square-r00t.net&gt;, https://r00t2.io"> <meta name="author" content="brent saner &lt;bts@square-r00t.net&gt;, https://r00t2.io">
<title>OpenSSH Key Structure Guide</title> <title>OpenSSH Key Structure Guide</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700"> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700">
@ -85,10 +85,10 @@ code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;
ul,ol,dl{line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit} ul,ol,dl{line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}
ul,ol{margin-left:1.5em} ul,ol{margin-left:1.5em}
ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0} ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0}
ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}
ul.square{list-style-type:square}
ul.circle{list-style-type:circle} ul.circle{list-style-type:circle}
ul.disc{list-style-type:disc} ul.disc{list-style-type:disc}
ul.square{list-style-type:square}
ul.circle ul:not([class]),ul.disc ul:not([class]),ul.square ul:not([class]){list-style:inherit}
ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0} ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}
dl dt{margin-bottom:.3125em;font-weight:bold} dl dt{margin-bottom:.3125em;font-weight:bold}
dl dd{margin-bottom:1.25em} dl dd{margin-bottom:1.25em}
@ -141,7 +141,7 @@ p a>code:hover{color:rgba(0,0,0,.9)}
#content::before{content:none} #content::before{content:none}
#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0} #header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}
#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #dddddf} #header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #dddddf}
#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px} #header>h1:only-child{border-bottom:1px solid #dddddf;padding-bottom:8px}
#header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:flex;flex-flow:row wrap} #header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:flex;flex-flow:row wrap}
#header .details span:first-child{margin-left:-.125em} #header .details span:first-child{margin-left:-.125em}
#header .details span.email a{color:rgba(0,0,0,.85)} #header .details span.email a{color:rgba(0,0,0,.85)}
@ -163,6 +163,7 @@ p a>code:hover{color:rgba(0,0,0,.9)}
#toctitle{color:#7a2518;font-size:1.2em} #toctitle{color:#7a2518;font-size:1.2em}
@media screen and (min-width:768px){#toctitle{font-size:1.375em} @media screen and (min-width:768px){#toctitle{font-size:1.375em}
body.toc2{padding-left:15em;padding-right:0} body.toc2{padding-left:15em;padding-right:0}
body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px}
#toc.toc2{margin-top:0!important;background:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto} #toc.toc2{margin-top:0!important;background:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em} #toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}
#toc.toc2>ul{font-size:.9em;margin-bottom:0} #toc.toc2>ul{font-size:.9em;margin-bottom:0}
@ -209,13 +210,10 @@ table.tableblock.fit-content>caption.title{white-space:nowrap;width:0}
.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #dddddf;color:rgba(0,0,0,.6);word-wrap:anywhere} .admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #dddddf;color:rgba(0,0,0,.6);word-wrap:anywhere}
.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0} .admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}
.exampleblock>.content{border:1px solid #e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;border-radius:4px} .exampleblock>.content{border:1px solid #e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;border-radius:4px}
.exampleblock>.content>:first-child{margin-top:0}
.exampleblock>.content>:last-child{margin-bottom:0}
.sidebarblock{border:1px solid #dbdbd6;margin-bottom:1.25em;padding:1.25em;background:#f3f3f2;border-radius:4px} .sidebarblock{border:1px solid #dbdbd6;margin-bottom:1.25em;padding:1.25em;background:#f3f3f2;border-radius:4px}
.sidebarblock>:first-child{margin-top:0}
.sidebarblock>:last-child{margin-bottom:0}
.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center} .sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}
.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0} .exampleblock>.content>:first-child,.sidebarblock>.content>:first-child{margin-top:0}
.exampleblock>.content>:last-child,.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
.literalblock pre,.listingblock>.content>pre{border-radius:4px;overflow-x:auto;padding:1em;font-size:.8125em} .literalblock pre,.listingblock>.content>pre{border-radius:4px;overflow-x:auto;padding:1em;font-size:.8125em}
@media screen and (min-width:768px){.literalblock pre,.listingblock>.content>pre{font-size:.90625em}} @media screen and (min-width:768px){.literalblock pre,.listingblock>.content>pre{font-size:.90625em}}
@media screen and (min-width:1280px){.literalblock pre,.listingblock>.content>pre{font-size:1em}} @media screen and (min-width:1280px){.literalblock pre,.listingblock>.content>pre{font-size:1em}}
@ -331,7 +329,7 @@ a.image{text-decoration:none;display:inline-block}
a.image object{pointer-events:none} a.image object{pointer-events:none}
sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super} sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}
sup.footnote a,sup.footnoteref a{text-decoration:none} sup.footnote a,sup.footnoteref a{text-decoration:none}
sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline} sup.footnote a:active,sup.footnoteref a:active,#footnotes .footnote a:first-of-type:active{text-decoration:underline}
#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em} #footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}
#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em;border-width:1px 0 0} #footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em;border-width:1px 0 0}
#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;margin-bottom:.2em} #footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;margin-bottom:.2em}
@ -394,7 +392,7 @@ b.conum *{color:inherit!important}
dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility} dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}
h1,h2,p,td.content,span.alt,summary{letter-spacing:-.01em} h1,h2,p,td.content,span.alt,summary{letter-spacing:-.01em}
p strong,td.content strong,div.footnote strong{letter-spacing:-.005em} p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}
p,blockquote,dt,td.content,span.alt,summary{font-size:1.0625rem} p,blockquote,dt,td.content,td.hdlist1,span.alt,summary{font-size:1.0625rem}
p{margin-bottom:1.25rem} p{margin-bottom:1.25rem}
.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em} .sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}
.exampleblock>.content{background:#fffef7;border-color:#e0e0dc;box-shadow:0 1px 4px #e0e0dc} .exampleblock>.content{background:#fffef7;border-color:#e0e0dc;box-shadow:0 1px 4px #e0e0dc}
@ -439,217 +437,120 @@ body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-b
<style> <style>
pre.rouge table td { padding: 5px; } pre.rouge table td { padding: 5px; }
pre.rouge table pre { margin: 0; } pre.rouge table pre { margin: 0; }
pre.rouge .cm { pre.rouge, pre.rouge .w {
color: #999988; color: #24292f;
font-style: italic; background-color: #f6f8fa;
} }
pre.rouge .cp { pre.rouge .k, pre.rouge .kd, pre.rouge .kn, pre.rouge .kp, pre.rouge .kr, pre.rouge .kt, pre.rouge .kv {
color: #999999; color: #cf222e;
font-weight: bold;
}
pre.rouge .c1 {
color: #999988;
font-style: italic;
}
pre.rouge .cs {
color: #999999;
font-weight: bold;
font-style: italic;
}
pre.rouge .c, pre.rouge .ch, pre.rouge .cd, pre.rouge .cpf {
color: #999988;
font-style: italic;
}
pre.rouge .err {
color: #a61717;
background-color: #e3d2d2;
}
pre.rouge .gd {
color: #000000;
background-color: #ffdddd;
}
pre.rouge .ge {
color: #000000;
font-style: italic;
} }
pre.rouge .gr { pre.rouge .gr {
color: #aa0000; color: #f6f8fa;
} }
pre.rouge .gh { pre.rouge .gd {
color: #999999; color: #82071e;
background-color: #ffebe9;
}
pre.rouge .nb {
color: #953800;
}
pre.rouge .nc {
color: #953800;
}
pre.rouge .no {
color: #953800;
}
pre.rouge .nn {
color: #953800;
}
pre.rouge .sr {
color: #116329;
}
pre.rouge .na {
color: #116329;
}
pre.rouge .nt {
color: #116329;
} }
pre.rouge .gi { pre.rouge .gi {
color: #000000; color: #116329;
background-color: #ddffdd; background-color: #dafbe1;
} }
pre.rouge .go { pre.rouge .ges {
color: #888888; font-weight: bold;
font-style: italic;
} }
pre.rouge .gp { pre.rouge .kc {
color: #555555; color: #0550ae;
} }
pre.rouge .gs { pre.rouge .l, pre.rouge .ld, pre.rouge .m, pre.rouge .mb, pre.rouge .mf, pre.rouge .mh, pre.rouge .mi, pre.rouge .il, pre.rouge .mo, pre.rouge .mx {
color: #0550ae;
}
pre.rouge .sb {
color: #0550ae;
}
pre.rouge .bp {
color: #0550ae;
}
pre.rouge .ne {
color: #0550ae;
}
pre.rouge .nl {
color: #0550ae;
}
pre.rouge .py {
color: #0550ae;
}
pre.rouge .nv, pre.rouge .vc, pre.rouge .vg, pre.rouge .vi, pre.rouge .vm {
color: #0550ae;
}
pre.rouge .o, pre.rouge .ow {
color: #0550ae;
}
pre.rouge .gh {
color: #0550ae;
font-weight: bold; font-weight: bold;
} }
pre.rouge .gu { pre.rouge .gu {
color: #aaaaaa; color: #0550ae;
}
pre.rouge .gt {
color: #aa0000;
}
pre.rouge .kc {
color: #000000;
font-weight: bold; font-weight: bold;
} }
pre.rouge .kd { pre.rouge .s, pre.rouge .sa, pre.rouge .sc, pre.rouge .dl, pre.rouge .sd, pre.rouge .s2, pre.rouge .se, pre.rouge .sh, pre.rouge .sx, pre.rouge .s1, pre.rouge .ss {
color: #000000; color: #0a3069;
font-weight: bold;
}
pre.rouge .kn {
color: #000000;
font-weight: bold;
}
pre.rouge .kp {
color: #000000;
font-weight: bold;
}
pre.rouge .kr {
color: #000000;
font-weight: bold;
}
pre.rouge .kt {
color: #445588;
font-weight: bold;
}
pre.rouge .k, pre.rouge .kv {
color: #000000;
font-weight: bold;
}
pre.rouge .mf {
color: #009999;
}
pre.rouge .mh {
color: #009999;
}
pre.rouge .il {
color: #009999;
}
pre.rouge .mi {
color: #009999;
}
pre.rouge .mo {
color: #009999;
}
pre.rouge .m, pre.rouge .mb, pre.rouge .mx {
color: #009999;
}
pre.rouge .sa {
color: #000000;
font-weight: bold;
}
pre.rouge .sb {
color: #d14;
}
pre.rouge .sc {
color: #d14;
}
pre.rouge .sd {
color: #d14;
}
pre.rouge .s2 {
color: #d14;
}
pre.rouge .se {
color: #d14;
}
pre.rouge .sh {
color: #d14;
}
pre.rouge .si {
color: #d14;
}
pre.rouge .sx {
color: #d14;
}
pre.rouge .sr {
color: #009926;
}
pre.rouge .s1 {
color: #d14;
}
pre.rouge .ss {
color: #990073;
}
pre.rouge .s, pre.rouge .dl {
color: #d14;
}
pre.rouge .na {
color: #008080;
}
pre.rouge .bp {
color: #999999;
}
pre.rouge .nb {
color: #0086B3;
}
pre.rouge .nc {
color: #445588;
font-weight: bold;
}
pre.rouge .no {
color: #008080;
} }
pre.rouge .nd { pre.rouge .nd {
color: #3c5d5d; color: #8250df;
font-weight: bold;
}
pre.rouge .ni {
color: #800080;
}
pre.rouge .ne {
color: #990000;
font-weight: bold;
} }
pre.rouge .nf, pre.rouge .fm { pre.rouge .nf, pre.rouge .fm {
color: #990000; color: #8250df;
}
pre.rouge .err {
color: #f6f8fa;
background-color: #82071e;
}
pre.rouge .c, pre.rouge .ch, pre.rouge .cd, pre.rouge .cm, pre.rouge .cp, pre.rouge .cpf, pre.rouge .c1, pre.rouge .cs {
color: #6e7781;
}
pre.rouge .gl {
color: #6e7781;
}
pre.rouge .gt {
color: #6e7781;
}
pre.rouge .ni {
color: #24292f;
}
pre.rouge .si {
color: #24292f;
}
pre.rouge .ge {
color: #24292f;
font-style: italic;
}
pre.rouge .gs {
color: #24292f;
font-weight: bold; font-weight: bold;
} }
pre.rouge .nl {
color: #990000;
font-weight: bold;
}
pre.rouge .nn {
color: #555555;
}
pre.rouge .nt {
color: #000080;
}
pre.rouge .vc {
color: #008080;
}
pre.rouge .vg {
color: #008080;
}
pre.rouge .vi {
color: #008080;
}
pre.rouge .nv, pre.rouge .vm {
color: #008080;
}
pre.rouge .ow {
color: #000000;
font-weight: bold;
}
pre.rouge .o {
color: #000000;
font-weight: bold;
}
pre.rouge .w {
color: #bbbbbb;
}
pre.rouge {
background-color: #f8f8f8;
}
</style> </style>
<!-- https://stackoverflow.com/a/34481639 --> <!-- https://stackoverflow.com/a/34481639 -->
<!-- Generate a nice TOC --> <!-- Generate a nice TOC -->
@ -734,89 +635,89 @@ pre.rouge {
<h1>OpenSSH Key Structure Guide</h1> <h1>OpenSSH Key Structure Guide</h1>
<div class="details"> <div class="details">
<span id="author" class="author">brent saner &lt;bts@square-r00t.net&gt;, https://r00t2.io</span><br> <span id="author" class="author">brent saner &lt;bts@square-r00t.net&gt;, https://r00t2.io</span><br>
<span id="revdate">Last updated 2022-04-29 04:09:49 -0400</span> <span id="revdate">Last updated 2025-09-01 13:47:06 -0400</span>
</div> </div>
<div id="toc" class="toc2"> <div id="toc" class="toc2">
<div id="toctitle">Table of Contents</div> <div id="toctitle">Table of Contents</div>
<ul class="sectlevel1"> <ul class="sectlevel1">
<li><a href="#purpose">1. Purpose</a></li> <li><a href="#why">1. Purpose</a></li>
<li><a href="#basic_introduction">2. Basic Introduction</a> <li><a href="#intro">2. Basic Introduction</a>
<ul class="sectlevel2"> <ul class="sectlevel2">
<li><a href="#legacy">2.1. Legacy</a> <li><a href="#intro_legc">2.1. Legacy</a>
<ul class="sectlevel3"> <ul class="sectlevel3">
<li><a href="#private_keys">2.1.1. Private Keys</a></li> <li><a href="#intro_legc_priv">2.1.1. Private Keys</a></li>
<li><a href="#public_keys">2.1.2. Public Keys</a></li> <li><a href="#intro_legc_pub">2.1.2. Public Keys</a></li>
</ul> </ul>
</li> </li>
<li><a href="#new_v1_format">2.2. New "v1" Format</a> <li><a href="#intro_v1">2.2. New "v1" Format</a>
<ul class="sectlevel3"> <ul class="sectlevel3">
<li><a href="#private_keys_2">2.2.1. Private Keys</a></li> <li><a href="#intro_v1_priv">2.2.1. Private Keys</a></li>
<li><a href="#public_keys_2">2.2.2. Public Keys</a></li> <li><a href="#intro_v1_pub">2.2.2. Public Keys</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>
</li> </li>
<li><a href="#keytype_specific_breakdowns">3. Keytype-Specific Breakdowns</a> <li><a href="#bkdn">3. Keytype-Specific Breakdowns</a>
<ul class="sectlevel2"> <ul class="sectlevel2">
<li><a href="#rsa">3.1. RSA</a> <li><a href="#bkdn_rsa">3.1. RSA</a>
<ul class="sectlevel3"> <ul class="sectlevel3">
<li><a href="#public">3.1.1. Public</a> <li><a href="#bkdn_rsa_pub">3.1.1. Public</a>
<ul class="sectlevel4"> <ul class="sectlevel4">
<li><a href="#structure">3.1.1.1. Structure</a></li> <li><a href="#bkdn_rsa_pub_struct">3.1.1.1. Structure</a></li>
<li><a href="#example">3.1.1.2. Example</a></li> <li><a href="#bkdn_rsa_pub_ex">3.1.1.2. Example</a></li>
</ul> </ul>
</li> </li>
<li><a href="#private">3.1.2. Private</a> <li><a href="#bkdn_rsa_priv">3.1.2. Private</a>
<ul class="sectlevel4"> <ul class="sectlevel4">
<li><a href="#legacy_plain">3.1.2.1. Legacy (Plain)</a> <li><a href="#bkdn_rsa_priv_legc_plain">3.1.2.1. Legacy (Plain)</a>
<ul class="sectlevel5"> <ul class="sectlevel5">
<li><a href="#struct_rsa_plain_legacy">3.1.2.1.1. Structure</a></li> <li><a href="#bkdn_rsa_priv_legc_plain_struct">3.1.2.1.1. Structure</a></li>
<li><a href="#bytes_rsa_plain_legacy">3.1.2.1.2. Example</a></li> <li><a href="#bkdn_rsa_priv_legc_plain_ex">3.1.2.1.2. Example</a></li>
</ul> </ul>
</li> </li>
<li><a href="#legacy_encrypted">3.1.2.2. Legacy (Encrypted)</a> <li><a href="#bkdn_rsa_priv_legc_crypt">3.1.2.2. Legacy (Encrypted)</a>
<ul class="sectlevel5"> <ul class="sectlevel5">
<li><a href="#struct_rsa_crypt_legacy">3.1.2.2.1. Structure</a></li> <li><a href="#bkdn_rsa_priv_legc_crypt_struct">3.1.2.2.1. Structure</a></li>
<li><a href="#bytes_rsa_crypt_legacy">3.1.2.2.2. Example</a></li> <li><a href="#bkdn_rsa_priv_legc_crypt_ex">3.1.2.2.2. Example</a></li>
</ul> </ul>
</li> </li>
<li><a href="#v1_plain">3.1.2.3. v1 (Plain)</a> <li><a href="#bkdn_rsa_priv_v1_plain">3.1.2.3. v1 (Plain)</a>
<ul class="sectlevel5"> <ul class="sectlevel5">
<li><a href="#struct_rsa_plain">3.1.2.3.1. Structure</a></li> <li><a href="#bkdn_rsa_priv_v1_plain_struct">3.1.2.3.1. Structure</a></li>
<li><a href="#bytes_rsa_plain">3.1.2.3.2. Example</a></li> <li><a href="#bkdn_rsa_priv_v1_plain_ex">3.1.2.3.2. Example</a></li>
</ul> </ul>
</li> </li>
<li><a href="#v1_encrypted">3.1.2.4. v1 (Encrypted)</a> <li><a href="#bkdn_rsa_priv_v1_crypt">3.1.2.4. v1 (Encrypted)</a>
<ul class="sectlevel5"> <ul class="sectlevel5">
<li><a href="#struct_rsa_crypt">3.1.2.4.1. Structure</a></li> <li><a href="#bkdn_rsa_priv_v1_crypt_struct">3.1.2.4.1. Structure</a></li>
<li><a href="#bytes_rsa_crypt">3.1.2.4.2. Example</a></li> <li><a href="#bkdn_rsa_priv_v1_crypt_ex">3.1.2.4.2. Example</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>
</li> </li>
</ul> </ul>
</li> </li>
<li><a href="#ed25519">3.2. ED25519</a> <li><a href="#bkdn_ed25519">3.2. ED25519</a>
<ul class="sectlevel3"> <ul class="sectlevel3">
<li><a href="#public_2">3.2.1. Public</a> <li><a href="#bkdn_ed25519_pub">3.2.1. Public</a>
<ul class="sectlevel4"> <ul class="sectlevel4">
<li><a href="#structure_2">3.2.1.1. Structure</a></li> <li><a href="#bkdn_ed25519_pub_struct">3.2.1.1. Structure</a></li>
<li><a href="#example_2">3.2.1.2. Example</a></li> <li><a href="#bkdn_ed25519_pub_ex">3.2.1.2. Example</a></li>
</ul> </ul>
</li> </li>
<li><a href="#private_2">3.2.2. Private</a> <li><a href="#bkdn_ed25519_priv">3.2.2. Private</a>
<ul class="sectlevel4"> <ul class="sectlevel4">
<li><a href="#legacy_2">3.2.2.1. Legacy</a></li> <li><a href="#bkdn_ed25519_priv_legc">3.2.2.1. Legacy</a></li>
<li><a href="#v1_plain_2">3.2.2.2. v1 (Plain)</a> <li><a href="#bkdn_ed25519_priv_v1_plain">3.2.2.2. v1 (Plain)</a>
<ul class="sectlevel5"> <ul class="sectlevel5">
<li><a href="#struct_ed25519_plain">3.2.2.2.1. Structure</a></li> <li><a href="#bkdn_ed25519_priv_v1_plain_struct">3.2.2.2.1. Structure</a></li>
<li><a href="#bytes_ed25519_plain">3.2.2.2.2. Example</a></li> <li><a href="#bkdn_ed25519_priv_v1_plain_ex">3.2.2.2.2. Example</a></li>
</ul> </ul>
</li> </li>
<li><a href="#v1_encrypted_2">3.2.2.3. v1 (Encrypted)</a> <li><a href="#bkdn_ed25519_priv_v1_crypt">3.2.2.3. v1 (Encrypted)</a>
<ul class="sectlevel5"> <ul class="sectlevel5">
<li><a href="#struct_ed25519_crypt">3.2.2.3.1. Structure</a></li> <li><a href="#bkdn_ed25519_priv_v1_crypt_struct">3.2.2.3.1. Structure</a></li>
<li><a href="#bytes_ed25519_crypt">3.2.2.3.2. Example</a></li> <li><a href="#bkdn_ed25519_priv_v1_crypt_ex">3.2.2.3.2. Example</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>
@ -825,12 +726,13 @@ pre.rouge {
</li> </li>
</ul> </ul>
</li> </li>
<li><a href="#moar">4. Further Information</a></li>
</ul> </ul>
</div> </div>
</div> </div>
<div id="content"> <div id="content">
<div class="sect1"> <div class="sect1">
<h2 id="purpose"><a class="link" href="#purpose">1. Purpose</a></h2> <h2 id="why"><a class="link" href="#why">1. Purpose</a></h2>
<div class="sectionbody"> <div class="sectionbody">
<div class="paragraph"> <div class="paragraph">
<p>This document attempts to present a much more detailed, thorough, and easily-understood form of the key formats used by OpenSSH. The extent of those formats' canonical documentation is <a href="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key" target="_blank" rel="noopener">the OpenSSH source tree&#8217;s <code>PROTOCOL.key</code></a>, which is a little lacking.</p> <p>This document attempts to present a much more detailed, thorough, and easily-understood form of the key formats used by OpenSSH. The extent of those formats' canonical documentation is <a href="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key" target="_blank" rel="noopener">the OpenSSH source tree&#8217;s <code>PROTOCOL.key</code></a>, which is a little lacking.</p>
@ -838,18 +740,18 @@ pre.rouge {
</div> </div>
</div> </div>
<div class="sect1"> <div class="sect1">
<h2 id="basic_introduction"><a class="link" href="#basic_introduction">2. Basic Introduction</a></h2> <h2 id="intro"><a class="link" href="#intro">2. Basic Introduction</a></h2>
<div class="sectionbody"> <div class="sectionbody">
<div class="sect2"> <div class="sect2">
<h3 id="legacy"><a class="link" href="#legacy">2.1. Legacy</a></h3> <h3 id="intro_legc"><a class="link" href="#intro_legc">2.1. Legacy</a></h3>
<div class="sect3"> <div class="sect3">
<h4 id="private_keys"><a class="link" href="#private_keys">2.1.1. Private Keys</a></h4> <h4 id="intro_legc_priv"><a class="link" href="#intro_legc_priv">2.1.1. Private Keys</a></h4>
<div class="paragraph"> <div class="paragraph">
<p>In OpenSSH pre-7.8, private keys are stored in their respective PEM encoding<sup class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnotedef_1" title="View footnote.">1</a>]</sup> with no modification. These legacy private keys should be entirely usable by OpenSSL/LibreSSL/GnuTLS etc. natively with no conversion necessary.</p> <p>In OpenSSH pre-7.8, private keys are stored in their respective PEM encoding<sup class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnotedef_1" title="View footnote.">1</a>]</sup> with no modification. These legacy private keys should be entirely usable by OpenSSL/LibreSSL/GnuTLS etc. natively with no conversion necessary.</p>
</div> </div>
</div> </div>
<div class="sect3"> <div class="sect3">
<h4 id="public_keys"><a class="link" href="#public_keys">2.1.2. Public Keys</a></h4> <h4 id="intro_legc_pub"><a class="link" href="#intro_legc_pub">2.1.2. Public Keys</a></h4>
<div class="paragraph"> <div class="paragraph">
<p>Each public key <strong>file</strong> (<code>*.pub</code>) is written out in the following format:</p> <p>Each public key <strong>file</strong> (<code>*.pub</code>) is written out in the following format:</p>
</div> </div>
@ -883,14 +785,14 @@ pre.rouge {
</div> </div>
</div> </div>
<div class="sect2"> <div class="sect2">
<h3 id="new_v1_format"><a class="link" href="#new_v1_format">2.2. New "v1" Format</a></h3> <h3 id="intro_v1"><a class="link" href="#intro_v1">2.2. New "v1" Format</a></h3>
<div class="sect3"> <div class="sect3">
<h4 id="private_keys_2"><a class="link" href="#private_keys_2">2.2.1. Private Keys</a></h4> <h4 id="intro_v1_priv"><a class="link" href="#intro_v1_priv">2.2.1. Private Keys</a></h4>
<div class="paragraph"> <div class="paragraph">
<p>Private key structures have been retooled in the "v1" format. In recent OpenSSH versions, all new keys use the v1 format. They no longer are in straight PEM-compatible format.</p> <p>Private key structures have been retooled in the "v1" format. In recent OpenSSH versions, all new keys use the v1 format. They no longer are in straight PEM-compatible format.</p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p>Refer to <a href="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key" target="_blank" rel="noopener"><code>PROTOCOL.key</code></a> for a (very) general description, or each key&#8217;s specific breakdown for more detailed information.</p> <p>Refer to <a href="https://anongit.mindrot.org/openssh.git/tree/PROTOCOL.key" target="_blank" rel="noopener"><code>PROTOCOL.key</code></a> (<a href="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key" target="_blank" rel="noopener">GitHub mirror</a>) for a (very) general description, or each key type&#8217;s specific breakdown in this document for more detailed information.</p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p>The v1 format offers several benefits over the legacy format, including:</p> <p>The v1 format offers several benefits over the legacy format, including:</p>
@ -913,19 +815,19 @@ pre.rouge {
</div> </div>
</div> </div>
<div class="sect3"> <div class="sect3">
<h4 id="public_keys_2"><a class="link" href="#public_keys_2">2.2.2. Public Keys</a></h4> <h4 id="intro_v1_pub"><a class="link" href="#intro_v1_pub">2.2.2. Public Keys</a></h4>
<div class="paragraph"> <div class="paragraph">
<p>All public keys in v1 continue to use the same packed binary format as <a href="#public_keys">the legacy format</a>.</p> <p>All public keys in v1 continue to use the same packed binary format as <a href="#intro_legc_pub">the legacy format</a>.</p>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="sect1"> <div class="sect1">
<h2 id="keytype_specific_breakdowns"><a class="link" href="#keytype_specific_breakdowns">3. Keytype-Specific Breakdowns</a></h2> <h2 id="bkdn"><a class="link" href="#bkdn">3. Keytype-Specific Breakdowns</a></h2>
<div class="sectionbody"> <div class="sectionbody">
<div class="sect2"> <div class="sect2">
<h3 id="rsa"><a class="link" href="#rsa">3.1. RSA</a></h3> <h3 id="bkdn_rsa"><a class="link" href="#bkdn_rsa">3.1. RSA</a></h3>
<div class="paragraph"> <div class="paragraph">
<p>RSA<sup class="footnote">[<a id="_footnoteref_3" class="footnote" href="#_footnotedef_3" title="View footnote.">3</a>]</sup> is a widely-supported PKI system. It is ubiquitous, but it is recommended to use newer systems (e.g. ED25519) for OpenSSH if all clients and destinations support it.</p> <p>RSA<sup class="footnote">[<a id="_footnoteref_3" class="footnote" href="#_footnotedef_3" title="View footnote.">3</a>]</sup> is a widely-supported PKI system. It is ubiquitous, but it is recommended to use newer systems (e.g. ED25519) for OpenSSH if all clients and destinations support it.</p>
</div> </div>
@ -936,9 +838,9 @@ pre.rouge {
<p>It is <strong>highly</strong> recommended to use 4096-bit RSA if using RSA keys.</p> <p>It is <strong>highly</strong> recommended to use 4096-bit RSA if using RSA keys.</p>
</div> </div>
<div class="sect3"> <div class="sect3">
<h4 id="public"><a class="link" href="#public">3.1.1. Public</a></h4> <h4 id="bkdn_rsa_pub"><a class="link" href="#bkdn_rsa_pub">3.1.1. Public</a></h4>
<div class="sect4"> <div class="sect4">
<h5 id="structure"><a class="link" href="#structure">3.1.1.1. Structure</a></h5> <h5 id="bkdn_rsa_pub_struct"><a class="link" href="#bkdn_rsa_pub_struct">3.1.1.1. Structure</a></h5>
<div class="paragraph"> <div class="paragraph">
<p>Public keys are stored in the following structure:</p> <p>Public keys are stored in the following structure:</p>
</div> </div>
@ -962,7 +864,7 @@ pre.rouge {
</div> </div>
</div> </div>
<div class="sect4"> <div class="sect4">
<h5 id="example"><a class="link" href="#example">3.1.1.2. Example</a></h5> <h5 id="bkdn_rsa_pub_ex"><a class="link" href="#bkdn_rsa_pub_ex">3.1.1.2. Example</a></h5>
<div class="listingblock"> <div class="listingblock">
<div class="title"><code>.pub</code> format</div> <div class="title"><code>.pub</code> format</div>
<div class="content"> <div class="content">
@ -1024,17 +926,17 @@ pre.rouge {
</div> </div>
</div> </div>
<div class="sect3"> <div class="sect3">
<h4 id="private"><a class="link" href="#private">3.1.2. Private</a></h4> <h4 id="bkdn_rsa_priv"><a class="link" href="#bkdn_rsa_priv">3.1.2. Private</a></h4>
<div class="sect4"> <div class="sect4">
<h5 id="legacy_plain"><a class="link" href="#legacy_plain">3.1.2.1. Legacy (Plain)</a></h5> <h5 id="bkdn_rsa_priv_legc_plain"><a class="link" href="#bkdn_rsa_priv_legc_plain">3.1.2.1. Legacy (Plain)</a></h5>
<div class="sect5"> <div class="sect5">
<h6 id="struct_rsa_plain_legacy"><a class="link" href="#struct_rsa_plain_legacy">3.1.2.1.1. Structure</a></h6> <h6 id="bkdn_rsa_priv_legc_plain_struct"><a class="link" href="#bkdn_rsa_priv_legc_plain_struct">3.1.2.1.1. Structure</a></h6>
<div class="paragraph"> <div class="paragraph">
<p>Legacy private keys are encoded in standard RSA PEM format (<a href="https://datatracker.ietf.org/doc/html/rfc7468" target="_blank" rel="noopener">RFC 7468</a> § <a href="https://datatracker.ietf.org/doc/html/rfc7468#section-10" target="_blank" rel="noopener">10</a>, <a href="https://datatracker.ietf.org/doc/html/rfc3447#appendix-A" target="_blank" rel="noopener">APPENDIX-A</a>).</p> <p>Legacy private keys are encoded in standard RSA PEM format (<a href="https://datatracker.ietf.org/doc/html/rfc7468" target="_blank" rel="noopener">RFC 7468</a> § <a href="https://datatracker.ietf.org/doc/html/rfc7468#section-10" target="_blank" rel="noopener">10</a>, <a href="https://datatracker.ietf.org/doc/html/rfc3447#appendix-A" target="_blank" rel="noopener">APPENDIX-A</a>).</p>
</div> </div>
</div> </div>
<div class="sect5"> <div class="sect5">
<h6 id="bytes_rsa_plain_legacy"><a class="link" href="#bytes_rsa_plain_legacy">3.1.2.1.2. Example</a></h6> <h6 id="bkdn_rsa_priv_legc_plain_ex"><a class="link" href="#bkdn_rsa_priv_legc_plain_ex">3.1.2.1.2. Example</a></h6>
<div class="listingblock"> <div class="listingblock">
<div class="content"> <div class="content">
<pre class="rouge highlight"><code data-lang="text"><table class="linenotable"><tbody><tr><td class="linenos gl"><pre class="lineno"> 1 <pre class="rouge highlight"><code data-lang="text"><table class="linenotable"><tbody><tr><td class="linenos gl"><pre class="lineno"> 1
@ -1145,9 +1047,9 @@ Zb7jkiz4m88ol7ezdWZyHhVMZqy4bWMCI4moTDcpqJuox6JTQiO2Ajj2pFU=
</div> </div>
</div> </div>
<div class="sect4"> <div class="sect4">
<h5 id="legacy_encrypted"><a class="link" href="#legacy_encrypted">3.1.2.2. Legacy (Encrypted)</a></h5> <h5 id="bkdn_rsa_priv_legc_crypt"><a class="link" href="#bkdn_rsa_priv_legc_crypt">3.1.2.2. Legacy (Encrypted)</a></h5>
<div class="sect5"> <div class="sect5">
<h6 id="struct_rsa_crypt_legacy"><a class="link" href="#struct_rsa_crypt_legacy">3.1.2.2.1. Structure</a></h6> <h6 id="bkdn_rsa_priv_legc_crypt_struct"><a class="link" href="#bkdn_rsa_priv_legc_crypt_struct">3.1.2.2.1. Structure</a></h6>
<div class="paragraph"> <div class="paragraph">
<p>Legacy private keys are encoded in standard RSA PEM format (<a href="https://datatracker.ietf.org/doc/html/rfc7468" target="_blank" rel="noopener">RFC 7468</a> § <a href="https://datatracker.ietf.org/doc/html/rfc7468#section-11" target="_blank" rel="noopener">11</a>, <a href="https://datatracker.ietf.org/doc/html/rfc3447#appendix-A" target="_blank" rel="noopener">APPENDIX-A</a>).</p> <p>Legacy private keys are encoded in standard RSA PEM format (<a href="https://datatracker.ietf.org/doc/html/rfc7468" target="_blank" rel="noopener">RFC 7468</a> § <a href="https://datatracker.ietf.org/doc/html/rfc7468#section-11" target="_blank" rel="noopener">11</a>, <a href="https://datatracker.ietf.org/doc/html/rfc3447#appendix-A" target="_blank" rel="noopener">APPENDIX-A</a>).</p>
</div> </div>
@ -1157,7 +1059,7 @@ The <code>DEK-Info</code> field is defined in <a href="https://datatracker.ietf.
</div> </div>
</div> </div>
<div class="sect5"> <div class="sect5">
<h6 id="bytes_rsa_crypt_legacy"><a class="link" href="#bytes_rsa_crypt_legacy">3.1.2.2.2. Example</a></h6> <h6 id="bkdn_rsa_priv_legc_crypt_ex"><a class="link" href="#bkdn_rsa_priv_legc_crypt_ex">3.1.2.2.2. Example</a></h6>
<div class="paragraph"> <div class="paragraph">
<p>The following example, being encrypted, is protected with a passphrase. The passphrase used in this example key is <strong><code>testpassword</code></strong>.</p> <p>The following example, being encrypted, is protected with a passphrase. The passphrase used in this example key is <strong><code>testpassword</code></strong>.</p>
</div> </div>
@ -1278,12 +1180,12 @@ ftSfkGNUzTzPFbF5iEukTvKm42a7F/I/ExMVgpN/eQxJ7+m5TOgja0KC1h5fCN4L
</div> </div>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p>See the <a href="#bytes_rsa_plain_legacy">plaintext example</a> for the decrypted (non-password-protected) version of this key.</p> <p>See the <a href="#bkdn_rsa_priv_legc_plain_ex">plaintext example</a> for the decrypted (non-password-protected) version of this key.</p>
</div> </div>
</div> </div>
</div> </div>
<div class="sect4"> <div class="sect4">
<h5 id="v1_plain"><a class="link" href="#v1_plain">3.1.2.3. v1 (Plain)</a></h5> <h5 id="bkdn_rsa_priv_v1_plain"><a class="link" href="#bkdn_rsa_priv_v1_plain">3.1.2.3. v1 (Plain)</a></h5>
<div class="admonitionblock tip"> <div class="admonitionblock tip">
<table> <table>
<tr> <tr>
@ -1299,7 +1201,7 @@ ftSfkGNUzTzPFbF5iEukTvKm42a7F/I/ExMVgpN/eQxJ7+m5TOgja0KC1h5fCN4L
</table> </table>
</div> </div>
<div class="sect5"> <div class="sect5">
<h6 id="struct_rsa_plain"><a class="link" href="#struct_rsa_plain">3.1.2.3.1. Structure</a></h6> <h6 id="bkdn_rsa_priv_v1_plain_struct"><a class="link" href="#bkdn_rsa_priv_v1_plain_struct">3.1.2.3.1. Structure</a></h6>
<div class="listingblock"> <div class="listingblock">
<div class="content"> <div class="content">
<pre class="rouge highlight"><code data-lang="text"><table class="linenotable"><tbody><tr><td class="linenos gl"><pre class="lineno"> 1 <pre class="rouge highlight"><code data-lang="text"><table class="linenotable"><tbody><tr><td class="linenos gl"><pre class="lineno"> 1
@ -1381,7 +1283,7 @@ ftSfkGNUzTzPFbF5iEukTvKm42a7F/I/ExMVgpN/eQxJ7+m5TOgja0KC1h5fCN4L
</td> </td>
<td class="content"> <td class="content">
<div class="paragraph"> <div class="paragraph">
<p><strong>Chunk 3.0.0 to 3.0.1:</strong> These blocks are not present in unencrypted keys (see the <a href="#struct_rsa_crypt">encrypted key structure</a> for what these look like). <strong>3.0</strong> reflects this, as it&#8217;s always going to be <code>00000000</code> (0).</p> <p><strong>Chunk 3.0.0 to 3.0.1:</strong> These blocks are not present in unencrypted keys (see the <a href="#bkdn_rsa_priv_v1_crypt_struct">encrypted key structure</a> for what these look like). <strong>3.0</strong> reflects this, as it&#8217;s always going to be <code>00000000</code> (0).</p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p><strong>Chunk 4.0:</strong> This is technically currently unused; upstream hardcodes to 1 (left zero-padded 0x01).</p> <p><strong>Chunk 4.0:</strong> This is technically currently unused; upstream hardcodes to 1 (left zero-padded 0x01).</p>
@ -1398,7 +1300,7 @@ ftSfkGNUzTzPFbF5iEukTvKm42a7F/I/ExMVgpN/eQxJ7+m5TOgja0KC1h5fCN4L
</div> </div>
</div> </div>
<div class="sect5"> <div class="sect5">
<h6 id="bytes_rsa_plain"><a class="link" href="#bytes_rsa_plain">3.1.2.3.2. Example</a></h6> <h6 id="bkdn_rsa_priv_v1_plain_ex"><a class="link" href="#bkdn_rsa_priv_v1_plain_ex">3.1.2.3.2. Example</a></h6>
<div class="paragraph"> <div class="paragraph">
<p>The following example, being encrypted, is protected with a passphrase. The passphrase used in this example key is <strong><code>test</code></strong>.</p> <p>The following example, being encrypted, is protected with a passphrase. The passphrase used in this example key is <strong><code>test</code></strong>.</p>
</div> </div>
@ -1723,7 +1625,7 @@ hau1VzZBnp8AAAAYVGhpcyBpcyBhIGNvbW1lbnQgc3RyaW5nAQID
</div> </div>
</div> </div>
<div class="sect4"> <div class="sect4">
<h5 id="v1_encrypted"><a class="link" href="#v1_encrypted">3.1.2.4. v1 (Encrypted)</a></h5> <h5 id="bkdn_rsa_priv_v1_crypt"><a class="link" href="#bkdn_rsa_priv_v1_crypt">3.1.2.4. v1 (Encrypted)</a></h5>
<div class="admonitionblock tip"> <div class="admonitionblock tip">
<table> <table>
<tr> <tr>
@ -1816,7 +1718,7 @@ Note that <strong>1.0.0</strong> has nothing to do with SSH connections themselv
</table> </table>
</div> </div>
<div class="sect5"> <div class="sect5">
<h6 id="struct_rsa_crypt"><a class="link" href="#struct_rsa_crypt">3.1.2.4.1. Structure</a></h6> <h6 id="bkdn_rsa_priv_v1_crypt_struct"><a class="link" href="#bkdn_rsa_priv_v1_crypt_struct">3.1.2.4.1. Structure</a></h6>
<div class="listingblock"> <div class="listingblock">
<div class="content"> <div class="content">
<pre class="rouge highlight"><code data-lang="text"><table class="linenotable"><tbody><tr><td class="linenos gl"><pre class="lineno"> 1 <pre class="rouge highlight"><code data-lang="text"><table class="linenotable"><tbody><tr><td class="linenos gl"><pre class="lineno"> 1
@ -1873,7 +1775,7 @@ Note that <strong>1.0.0</strong> has nothing to do with SSH connections themselv
<p><strong>Chunk 4.0:</strong> This is technically currently unused; upstream hardcodes to 1 (left zero-padded 0x01).</p> <p><strong>Chunk 4.0:</strong> This is technically currently unused; upstream hardcodes to 1 (left zero-padded 0x01).</p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p><strong>Chunk 4.0.1.0:</strong> When decrypted, this is equivalent to the <a href="#struct_rsa_plain">plaintext</a> <strong>4.0.1.0</strong> to <strong>4.0.1.10</strong>. It uses a padded size appropriate to the encryption cipher used.</p> <p><strong>Chunk 4.0.1.0:</strong> When decrypted, this is equivalent to the <a href="#bkdn_rsa_priv_v1_plain_struct">plaintext</a> <strong>4.0.1.0</strong> to <strong>4.0.1.10</strong>. It uses a padded size appropriate to the encryption cipher used.</p>
</div> </div>
</td> </td>
</tr> </tr>
@ -1881,7 +1783,7 @@ Note that <strong>1.0.0</strong> has nothing to do with SSH connections themselv
</div> </div>
</div> </div>
<div class="sect5"> <div class="sect5">
<h6 id="bytes_rsa_crypt"><a class="link" href="#bytes_rsa_crypt">3.1.2.4.2. Example</a></h6> <h6 id="bkdn_rsa_priv_v1_crypt_ex"><a class="link" href="#bkdn_rsa_priv_v1_crypt_ex">3.1.2.4.2. Example</a></h6>
<div class="paragraph"> <div class="paragraph">
<p>The following example, being encrypted, is protected with a passphrase. The passphrase used in this example key is <strong><code>test</code></strong>.</p> <p>The following example, being encrypted, is protected with a passphrase. The passphrase used in this example key is <strong><code>test</code></strong>.</p>
</div> </div>
@ -2191,7 +2093,7 @@ ZnrXZl+8QIW1MSvaaQFmJFqTs=
</td> </td>
<td class="content"> <td class="content">
<div class="paragraph"> <div class="paragraph">
<p>The decrypted <strong>4.0.1.0</strong> should match the <a href="#struct_rsa_plain">plaintext key&#8217;s structure</a> for <strong>4.0.1.0</strong> through <strong>4.0.1.10</strong>. The padding length WILL change, however, between the two unless using a cipher with an 8-byte block size.</p> <p>The decrypted <strong>4.0.1.0</strong> should match the <a href="#bkdn_rsa_priv_v1_plain_ex">plaintext key&#8217;s structure</a> for <strong>4.0.1.0</strong> through <strong>4.0.1.10</strong>. The padding length WILL change, however, between the two unless using a cipher with an 8-byte block size.</p>
</div> </div>
</td> </td>
</tr> </tr>
@ -2353,14 +2255,14 @@ ZnrXZl+8QIW1MSvaaQFmJFqTs=
</div> </div>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p>See the <a href="#struct_rsa_plain">plaintext structure</a> for details.</p> <p>See the <a href="#bkdn_rsa_priv_v1_plain_struct">plaintext structure</a> for details.</p>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="sect2"> <div class="sect2">
<h3 id="ed25519"><a class="link" href="#ed25519">3.2. ED25519</a></h3> <h3 id="bkdn_ed25519"><a class="link" href="#bkdn_ed25519">3.2. ED25519</a></h3>
<div class="paragraph"> <div class="paragraph">
<p>ED25519<sup class="footnote">[<a id="_footnoteref_4" class="footnote" href="#_footnotedef_4" title="View footnote.">4</a>]</sup> is a relatively somewhat new OpenSSH key algorithm. It has numerous benefits over e.g. RSA, including:</p> <p>ED25519<sup class="footnote">[<a id="_footnoteref_4" class="footnote" href="#_footnotedef_4" title="View footnote.">4</a>]</sup> is a relatively somewhat new OpenSSH key algorithm. It has numerous benefits over e.g. RSA, including:</p>
</div> </div>
@ -2395,9 +2297,9 @@ ZnrXZl+8QIW1MSvaaQFmJFqTs=
<p>I recommend it over all other key types for new SSH keys as long as it&#8217;s supported by clients/servers.</p> <p>I recommend it over all other key types for new SSH keys as long as it&#8217;s supported by clients/servers.</p>
</div> </div>
<div class="sect3"> <div class="sect3">
<h4 id="public_2"><a class="link" href="#public_2">3.2.1. Public</a></h4> <h4 id="bkdn_ed25519_pub"><a class="link" href="#bkdn_ed25519_pub">3.2.1. Public</a></h4>
<div class="sect4"> <div class="sect4">
<h5 id="structure_2"><a class="link" href="#structure_2">3.2.1.1. Structure</a></h5> <h5 id="bkdn_ed25519_pub_struct"><a class="link" href="#bkdn_ed25519_pub_struct">3.2.1.1. Structure</a></h5>
<div class="paragraph"> <div class="paragraph">
<p>Public keys are stored in the following structure:</p> <p>Public keys are stored in the following structure:</p>
</div> </div>
@ -2417,7 +2319,7 @@ ZnrXZl+8QIW1MSvaaQFmJFqTs=
</div> </div>
</div> </div>
<div class="sect4"> <div class="sect4">
<h5 id="example_2"><a class="link" href="#example_2">3.2.1.2. Example</a></h5> <h5 id="bkdn_ed25519_pub_ex"><a class="link" href="#bkdn_ed25519_pub_ex">3.2.1.2. Example</a></h5>
<div class="listingblock"> <div class="listingblock">
<div class="title"><code>id_ed25519.pub</code> Format</div> <div class="title"><code>id_ed25519.pub</code> Format</div>
<div class="content"> <div class="content">
@ -2443,9 +2345,9 @@ ZnrXZl+8QIW1MSvaaQFmJFqTs=
</div> </div>
</div> </div>
<div class="sect3"> <div class="sect3">
<h4 id="private_2"><a class="link" href="#private_2">3.2.2. Private</a></h4> <h4 id="bkdn_ed25519_priv"><a class="link" href="#bkdn_ed25519_priv">3.2.2. Private</a></h4>
<div class="sect4"> <div class="sect4">
<h5 id="legacy_2"><a class="link" href="#legacy_2">3.2.2.1. Legacy</a></h5> <h5 id="bkdn_ed25519_priv_legc"><a class="link" href="#bkdn_ed25519_priv_legc">3.2.2.1. Legacy</a></h5>
<div class="admonitionblock note"> <div class="admonitionblock note">
<table> <table>
<tr> <tr>
@ -2462,7 +2364,7 @@ ZnrXZl+8QIW1MSvaaQFmJFqTs=
</div> </div>
</div> </div>
<div class="sect4"> <div class="sect4">
<h5 id="v1_plain_2"><a class="link" href="#v1_plain_2">3.2.2.2. v1 (Plain)</a></h5> <h5 id="bkdn_ed25519_priv_v1_plain"><a class="link" href="#bkdn_ed25519_priv_v1_plain">3.2.2.2. v1 (Plain)</a></h5>
<div class="admonitionblock tip"> <div class="admonitionblock tip">
<table> <table>
<tr> <tr>
@ -2478,7 +2380,7 @@ ZnrXZl+8QIW1MSvaaQFmJFqTs=
</table> </table>
</div> </div>
<div class="sect5"> <div class="sect5">
<h6 id="struct_ed25519_plain"><a class="link" href="#struct_ed25519_plain">3.2.2.2.1. Structure</a></h6> <h6 id="bkdn_ed25519_priv_v1_plain_struct"><a class="link" href="#bkdn_ed25519_priv_v1_plain_struct">3.2.2.2.1. Structure</a></h6>
<div class="listingblock"> <div class="listingblock">
<div class="content"> <div class="content">
<pre class="rouge highlight"><code data-lang="text"><table class="linenotable"><tbody><tr><td class="linenos gl"><pre class="lineno"> 1 <pre class="rouge highlight"><code data-lang="text"><table class="linenotable"><tbody><tr><td class="linenos gl"><pre class="lineno"> 1
@ -2540,7 +2442,7 @@ ZnrXZl+8QIW1MSvaaQFmJFqTs=
</td> </td>
<td class="content"> <td class="content">
<div class="paragraph"> <div class="paragraph">
<p><strong>Chunk 3.0.0 to 3.0.1:</strong> These blocks are not present in unencrypted keys (see the <a href="#struct_ed25519_crypt">encrypted key structure</a> for what these look like). <strong>3.0</strong> reflects this, as it&#8217;s always going to be <code>00000000</code> (0).</p> <p><strong>Chunk 3.0.0 to 3.0.1:</strong> These blocks are not present in unencrypted keys (see the <a href="#bkdn_ed25519_priv_v1_crypt_struct">encrypted key structure</a> for what these look like). <strong>3.0</strong> reflects this, as it&#8217;s always going to be <code>00000000</code> (0).</p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p><strong>Chunk 4.0:</strong> This is technically currently unused; upstream hardcodes to 1 (left zero-padded <code>0x01</code>).</p> <p><strong>Chunk 4.0:</strong> This is technically currently unused; upstream hardcodes to 1 (left zero-padded <code>0x01</code>).</p>
@ -2557,7 +2459,7 @@ ZnrXZl+8QIW1MSvaaQFmJFqTs=
</div> </div>
</div> </div>
<div class="sect5"> <div class="sect5">
<h6 id="bytes_ed25519_plain"><a class="link" href="#bytes_ed25519_plain">3.2.2.2.2. Example</a></h6> <h6 id="bkdn_ed25519_priv_v1_plain_ex"><a class="link" href="#bkdn_ed25519_priv_v1_plain_ex">3.2.2.2.2. Example</a></h6>
<div class="listingblock"> <div class="listingblock">
<div class="title"><code>id_ed25519</code> Format</div> <div class="title"><code>id_ed25519</code> Format</div>
<div class="content"> <div class="content">
@ -2645,7 +2547,7 @@ g7umsWLE6XzRH3PDnZewAAAAElRoaXMgaXMgYSB0ZXN0IGtleQECAw==
</div> </div>
</div> </div>
<div class="sect4"> <div class="sect4">
<h5 id="v1_encrypted_2"><a class="link" href="#v1_encrypted_2">3.2.2.3. v1 (Encrypted)</a></h5> <h5 id="bkdn_ed25519_priv_v1_crypt"><a class="link" href="#bkdn_ed25519_priv_v1_crypt">3.2.2.3. v1 (Encrypted)</a></h5>
<div class="admonitionblock tip"> <div class="admonitionblock tip">
<table> <table>
<tr> <tr>
@ -2738,7 +2640,7 @@ Note that <strong>1.0.0</strong> has nothing to do with SSH connections themselv
</table> </table>
</div> </div>
<div class="sect5"> <div class="sect5">
<h6 id="struct_ed25519_crypt"><a class="link" href="#struct_ed25519_crypt">3.2.2.3.1. Structure</a></h6> <h6 id="bkdn_ed25519_priv_v1_crypt_struct"><a class="link" href="#bkdn_ed25519_priv_v1_crypt_struct">3.2.2.3.1. Structure</a></h6>
<div class="listingblock"> <div class="listingblock">
<div class="content"> <div class="content">
<pre class="rouge highlight"><code data-lang="text"><table class="linenotable"><tbody><tr><td class="linenos gl"><pre class="lineno"> 1 <pre class="rouge highlight"><code data-lang="text"><table class="linenotable"><tbody><tr><td class="linenos gl"><pre class="lineno"> 1
@ -2789,7 +2691,7 @@ Note that <strong>1.0.0</strong> has nothing to do with SSH connections themselv
<p><strong>Chunk 4.0:</strong> This is technically currently unused; upstream hardcodes to 1 (left zero-padded <code>0x01</code>).</p> <p><strong>Chunk 4.0:</strong> This is technically currently unused; upstream hardcodes to 1 (left zero-padded <code>0x01</code>).</p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p><strong>Chunk 4.0.1.0:</strong> When decrypted, this is equivalent to the <a href="#struct_ed25519_plain">plaintext</a> <strong>4.0.1.0</strong> to <strong>4.0.1.6</strong>. It uses a padded size appropriate to the encryption cipher used.</p> <p><strong>Chunk 4.0.1.0:</strong> When decrypted, this is equivalent to the <a href="#bkdn_ed25519_priv_v1_plain_struct">plaintext</a> <strong>4.0.1.0</strong> to <strong>4.0.1.6</strong>. It uses a padded size appropriate to the encryption cipher used.</p>
</div> </div>
</td> </td>
</tr> </tr>
@ -2797,7 +2699,7 @@ Note that <strong>1.0.0</strong> has nothing to do with SSH connections themselv
</div> </div>
</div> </div>
<div class="sect5"> <div class="sect5">
<h6 id="bytes_ed25519_crypt"><a class="link" href="#bytes_ed25519_crypt">3.2.2.3.2. Example</a></h6> <h6 id="bkdn_ed25519_priv_v1_crypt_ex"><a class="link" href="#bkdn_ed25519_priv_v1_crypt_ex">3.2.2.3.2. Example</a></h6>
<div class="paragraph"> <div class="paragraph">
<p>The following example, being encrypted, is protected with a passphrase. The passphrase used in this example key is <strong><code>test</code></strong>.</p> <p>The following example, being encrypted, is protected with a passphrase. The passphrase used in this example key is <strong><code>test</code></strong>.</p>
</div> </div>
@ -2891,7 +2793,7 @@ dCXGDaRlL924VVCYUytRvu7ilZ+dtc9aCQUFJyDF3iXyxN2H68x7teo9e8vqzGtzLkw5KV
</td> </td>
<td class="content"> <td class="content">
<div class="paragraph"> <div class="paragraph">
<p>The decrypted <strong>4.0.1.0</strong> should match the <a href="#struct_ed25519_plain">plaintext key&#8217;s structure</a> for <strong>4.0.1</strong> through <strong>4.0.1.6</strong>. The padding length WILL change, however, between the two unless using a cipher with an 8-byte block size.</p> <p>The decrypted <strong>4.0.1.0</strong> should match the <a href="#bkdn_ed25519_priv_v1_plain_struct">plaintext key&#8217;s structure</a> for <strong>4.0.1</strong> through <strong>4.0.1.6</strong>. The padding length WILL change, however, between the two unless using a cipher with an 8-byte block size.</p>
</div> </div>
</td> </td>
</tr> </tr>
@ -2937,7 +2839,7 @@ dCXGDaRlL924VVCYUytRvu7ilZ+dtc9aCQUFJyDF3iXyxN2H68x7teo9e8vqzGtzLkw5KV
</div> </div>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p>See the <a href="#struct_ed25519_plain">plaintext structure</a> for details.</p> <p>See the <a href="#bkdn_ed25519_priv_v1_plain_struct">plaintext structure</a> for details.</p>
</div> </div>
</div> </div>
</div> </div>
@ -2945,6 +2847,19 @@ dCXGDaRlL924VVCYUytRvu7ilZ+dtc9aCQUFJyDF3iXyxN2H68x7teo9e8vqzGtzLkw5KV
</div> </div>
</div> </div>
</div> </div>
<div class="sect1">
<h2 id="moar"><a class="link" href="#moar">4. Further Information</a></h2>
<div class="sectionbody">
<a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">
<img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-sa/4.0/88x31.png" />
</a><br />
<span xmlns:dct="http://purl.org/dc/terms/" href="http://purl.org/dc/dcmitype/Text" property="dct:title" rel="dct:type">OpenSSH Key Structure Guide</span> by <a xmlns:cc="http://creativecommons.org/ns#" href="https://r00t2.io/" property="cc:attributionName" rel="cc:attributionURL">Brent Saner</a> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">Creative Commons Attribution-ShareAlike 4.0 International License</a>.<br />
<br />
<div class="paragraph">
<p>You are free to use, distribute, modify, redistribute, use for commercial purposes, etc. with very few restrictions; please see <a href="http://creativecommons.org/licenses/by-sa/4.0/" target="_blank" rel="noopener">the license summary</a> and <a href="https://creativecommons.org/licenses/by-sa/4.0/legalcode" target="_blank" rel="noopener">full license</a> for further details.</p>
</div>
</div>
</div>
</div> </div>
<div id="footnotes"> <div id="footnotes">
<hr> <hr>
@ -2963,7 +2878,7 @@ dCXGDaRlL924VVCYUytRvu7ilZ+dtc9aCQUFJyDF3iXyxN2H68x7teo9e8vqzGtzLkw5KV
</div> </div>
<div id="footer"> <div id="footer">
<div id="footer-text"> <div id="footer-text">
Last updated 2022-03-07 03:36:15 -0500 Last updated 2025-09-01 12:09:44 -0400
</div> </div>
</div> </div>
</body> </body>

View File

@ -1,3 +1,10 @@
////
This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
To view a copy of this license, visit
http://creativecommons.org/licenses/by-sa/4.0/.
////
[id="bkdn_ed25519"]
=== ED25519 === ED25519
ED25519footnote:[https://datatracker.ietf.org/doc/html/rfc8709] is a relatively somewhat new OpenSSH key algorithm. It has numerous benefits over e.g. RSA, including: ED25519footnote:[https://datatracker.ietf.org/doc/html/rfc8709] is a relatively somewhat new OpenSSH key algorithm. It has numerous benefits over e.g. RSA, including:
@ -11,4 +18,5 @@ ED25519footnote:[https://datatracker.ietf.org/doc/html/rfc8709] is a relatively
I recommend it over all other key types for new SSH keys as long as it's supported by clients/servers. I recommend it over all other key types for new SSH keys as long as it's supported by clients/servers.
include::public.adoc[] include::public.adoc[]
include::private/main.adoc[] include::private/main.adoc[]

View File

@ -1,3 +1,10 @@
////
This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
To view a copy of this license, visit
http://creativecommons.org/licenses/by-sa/4.0/.
////
[id="bkdn_ed25519_priv_legc"]
===== Legacy ===== Legacy
[NOTE] [NOTE]

View File

@ -1,3 +1,10 @@
////
This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
To view a copy of this license, visit
http://creativecommons.org/licenses/by-sa/4.0/.
////
[id="bkdn_ed25519_priv"]
==== Private ==== Private
include::legacy/main.adoc[] include::legacy/main.adoc[]

View File

@ -1,3 +1,10 @@
////
This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
To view a copy of this license, visit
http://creativecommons.org/licenses/by-sa/4.0/.
////
[id="bkdn_ed25519_priv_v1_crypt"]
===== v1 (Encrypted) ===== v1 (Encrypted)
[TIP] [TIP]
@ -34,7 +41,7 @@ This is likely going to be:
The author recommends using `aes256-ctr`. It is currently the upstream default. The author recommends using `aes256-ctr`. It is currently the upstream default.
==== ====
[id=struct_ed25519_crypt] [id="bkdn_ed25519_priv_v1_crypt_struct"]
====== Structure ====== Structure
[source,text,linenums] [source,text,linenums]
@ -62,10 +69,10 @@ The author recommends using `aes256-ctr`. It is currently the upstream default.
==== ====
*Chunk 4.0:* This is technically currently unused; upstream hardcodes to 1 (left zero-padded `0x01`). *Chunk 4.0:* This is technically currently unused; upstream hardcodes to 1 (left zero-padded `0x01`).
*Chunk 4.0.1.0:* When decrypted, this is equivalent to the <<struct_ed25519_plain, plaintext>> *4.0.1.0* to *4.0.1.6*. It uses a padded size appropriate to the encryption cipher used. *Chunk 4.0.1.0:* When decrypted, this is equivalent to the <<bkdn_ed25519_priv_v1_plain_struct, plaintext>> *4.0.1.0* to *4.0.1.6*. It uses a padded size appropriate to the encryption cipher used.
==== ====
[id=bytes_ed25519_crypt] [id="bkdn_ed25519_priv_v1_crypt_ex"]
====== Example ====== Example
The following example, being encrypted, is protected with a passphrase. The passphrase used in this example key is *`test`*. The following example, being encrypted, is protected with a passphrase. The passphrase used in this example key is *`test`*.
@ -117,7 +124,7 @@ dCXGDaRlL924VVCYUytRvu7ilZ+dtc9aCQUFJyDF3iXyxN2H68x7teo9e8vqzGtzLkw5KV
[NOTE] [NOTE]
==== ====
The decrypted *4.0.1.0* should match the <<struct_ed25519_plain, plaintext key's structure>> for *4.0.1* through *4.0.1.6*. The padding length WILL change, however, between the two unless using a cipher with an 8-byte block size. The decrypted *4.0.1.0* should match the <<bkdn_ed25519_priv_v1_plain_struct, plaintext key's structure>> for *4.0.1* through *4.0.1.6*. The padding length WILL change, however, between the two unless using a cipher with an 8-byte block size.
==== ====
When *4.0.1.0* is decrypted, it yields: When *4.0.1.0* is decrypted, it yields:
@ -142,4 +149,4 @@ When *4.0.1.0* is decrypted, it yields:
4.0.1.6 0102030405060708090a0b ([1 2 3 4 5 6 7 8 9 10 11], 11 bytes) 4.0.1.6 0102030405060708090a0b ([1 2 3 4 5 6 7 8 9 10 11], 11 bytes)
---- ----
See the <<struct_ed25519_plain, plaintext structure>> for details. See the <<bkdn_ed25519_priv_v1_plain_struct, plaintext structure>> for details.

View File

@ -1,3 +1,9 @@
////
This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
To view a copy of this license, visit
http://creativecommons.org/licenses/by-sa/4.0/.
////
include::plain.adoc[] include::plain.adoc[]
include::encrypted.adoc[] include::encrypted.adoc[]

View File

@ -1,3 +1,10 @@
////
This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
To view a copy of this license, visit
http://creativecommons.org/licenses/by-sa/4.0/.
////
[id="bkdn_ed25519_priv_v1_plain"]
===== v1 (Plain) ===== v1 (Plain)
[TIP] [TIP]
@ -5,7 +12,7 @@
Since plaintext/unencrypted keys do not have a cipher or KDF (as there's no encryption key or algorithm used), they use the string "none" to identify these (and entirely leave out the KDF options). Since plaintext/unencrypted keys do not have a cipher or KDF (as there's no encryption key or algorithm used), they use the string "none" to identify these (and entirely leave out the KDF options).
==== ====
[id=struct_ed25519_plain] [id="bkdn_ed25519_priv_v1_plain_struct"]
====== Structure ====== Structure
[source,text,linenums] [source,text,linenums]
@ -38,7 +45,7 @@ Since plaintext/unencrypted keys do not have a cipher or KDF (as there's no encr
[NOTE] [NOTE]
==== ====
*Chunk 3.0.0 to 3.0.1:* These blocks are not present in unencrypted keys (see the <<struct_ed25519_crypt, encrypted key structure>> for what these look like). *3.0* reflects this, as it's always going to be `00000000` (0). *Chunk 3.0.0 to 3.0.1:* These blocks are not present in unencrypted keys (see the <<bkdn_ed25519_priv_v1_crypt_struct, encrypted key structure>> for what these look like). *3.0* reflects this, as it's always going to be `00000000` (0).
*Chunk 4.0:* This is technically currently unused; upstream hardcodes to 1 (left zero-padded `0x01`). *Chunk 4.0:* This is technically currently unused; upstream hardcodes to 1 (left zero-padded `0x01`).
@ -47,7 +54,7 @@ Since plaintext/unencrypted keys do not have a cipher or KDF (as there's no encr
*Chunk 4.0.1.6:* The padding used aligns the private key (*4.0.1.0* to *4.0.1.5.0*) to the cipher blocksize. For plaintext keys, a blocksize of 8 is used. *Chunk 4.0.1.6:* The padding used aligns the private key (*4.0.1.0* to *4.0.1.5.0*) to the cipher blocksize. For plaintext keys, a blocksize of 8 is used.
==== ====
[id=bytes_ed25519_plain] [id="bkdn_ed25519_priv_v1_plain_ex"]
====== Example ====== Example
.`id_ed25519` Format .`id_ed25519` Format

View File

@ -1,5 +1,13 @@
////
This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
To view a copy of this license, visit
http://creativecommons.org/licenses/by-sa/4.0/.
////
[id="bkdn_ed25519_pub"]
==== Public ==== Public
[id="bkdn_ed25519_pub_struct"]
===== Structure ===== Structure
Public keys are stored in the following structure: Public keys are stored in the following structure:
@ -13,6 +21,7 @@ Public keys are stored in the following structure:
1.0.0 Public key payload (bytes) 1.0.0 Public key payload (bytes)
---- ----
[id="bkdn_ed25519_pub_ex"]
===== Example ===== Example
.`id_ed25519.pub` Format .`id_ed25519.pub` Format

View File

@ -1,5 +1,11 @@
package main package main
/*
* This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
* To view a copy of this license, visit
* http://creativecommons.org/licenses/by-sa/4.0/.
*/
import ( import (
"crypto" "crypto"
"crypto/aes" "crypto/aes"

View File

@ -1,5 +1,11 @@
package main package main
/*
* This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
* To view a copy of this license, visit
* http://creativecommons.org/licenses/by-sa/4.0/.
*/
import ( import (
"crypto/aes" "crypto/aes"
"crypto/cipher" "crypto/cipher"

View File

@ -1,3 +1,10 @@
////
This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
To view a copy of this license, visit
http://creativecommons.org/licenses/by-sa/4.0/.
////
[id="bkdn_rsa"]
=== RSA === RSA
RSAfootnote:[https://datatracker.ietf.org/doc/html/rfc8017] is a widely-supported PKI system. It is ubiquitous, but it is recommended to use newer systems (e.g. ED25519) for OpenSSH if all clients and destinations support it. RSAfootnote:[https://datatracker.ietf.org/doc/html/rfc8017] is a widely-supported PKI system. It is ubiquitous, but it is recommended to use newer systems (e.g. ED25519) for OpenSSH if all clients and destinations support it.

View File

@ -1,6 +1,13 @@
////
This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
To view a copy of this license, visit
http://creativecommons.org/licenses/by-sa/4.0/.
////
[id="bkdn_rsa_priv_legc_crypt"]
===== Legacy (Encrypted) ===== Legacy (Encrypted)
[id=struct_rsa_crypt_legacy] [id="bkdn_rsa_priv_legc_crypt_struct"]
====== Structure ====== Structure
Legacy private keys are encoded in standard RSA PEM format (https://datatracker.ietf.org/doc/html/rfc7468[RFC 7468^] § https://datatracker.ietf.org/doc/html/rfc7468#section-11[11^], https://datatracker.ietf.org/doc/html/rfc3447#appendix-A[APPENDIX-A^]). Legacy private keys are encoded in standard RSA PEM format (https://datatracker.ietf.org/doc/html/rfc7468[RFC 7468^] § https://datatracker.ietf.org/doc/html/rfc7468#section-11[11^], https://datatracker.ietf.org/doc/html/rfc3447#appendix-A[APPENDIX-A^]).
@ -8,7 +15,7 @@ Legacy private keys are encoded in standard RSA PEM format (https://datatracker.
The `Proc-Type` field is defined in https://datatracker.ietf.org/doc/html/rfc1421.html#section-4.6.1.1[RFC 1421 § 4.6.1.1^]. + The `Proc-Type` field is defined in https://datatracker.ietf.org/doc/html/rfc1421.html#section-4.6.1.1[RFC 1421 § 4.6.1.1^]. +
The `DEK-Info` field is defined in https://datatracker.ietf.org/doc/html/rfc1421.html#section-4.6.1.3[RFC 1421 § 4.6.1.3^]. The `DEK-Info` field is defined in https://datatracker.ietf.org/doc/html/rfc1421.html#section-4.6.1.3[RFC 1421 § 4.6.1.3^].
[id=bytes_rsa_crypt_legacy] [id="bkdn_rsa_priv_legc_crypt_ex"]
====== Example ====== Example
The following example, being encrypted, is protected with a passphrase. The passphrase used in this example key is *`testpassword`*. The following example, being encrypted, is protected with a passphrase. The passphrase used in this example key is *`testpassword`*.
@ -73,4 +80,4 @@ ftSfkGNUzTzPFbF5iEukTvKm42a7F/I/ExMVgpN/eQxJ7+m5TOgja0KC1h5fCN4L
-----END RSA PRIVATE KEY----- -----END RSA PRIVATE KEY-----
---- ----
See the <<bytes_rsa_plain_legacy, plaintext example>> for the decrypted (non-password-protected) version of this key. See the <<bkdn_rsa_priv_legc_plain_ex, plaintext example>> for the decrypted (non-password-protected) version of this key.

View File

@ -1,3 +1,9 @@
////
This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
To view a copy of this license, visit
http://creativecommons.org/licenses/by-sa/4.0/.
////
include::plain.adoc[] include::plain.adoc[]
include::encrypted.adoc[] include::encrypted.adoc[]

View File

@ -1,11 +1,18 @@
////
This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
To view a copy of this license, visit
http://creativecommons.org/licenses/by-sa/4.0/.
////
[id="bkdn_rsa_priv_legc_plain"]
===== Legacy (Plain) ===== Legacy (Plain)
[id=struct_rsa_plain_legacy] [id="bkdn_rsa_priv_legc_plain_struct"]
====== Structure ====== Structure
Legacy private keys are encoded in standard RSA PEM format (https://datatracker.ietf.org/doc/html/rfc7468[RFC 7468^] § https://datatracker.ietf.org/doc/html/rfc7468#section-10[10^], https://datatracker.ietf.org/doc/html/rfc3447#appendix-A[APPENDIX-A^]). Legacy private keys are encoded in standard RSA PEM format (https://datatracker.ietf.org/doc/html/rfc7468[RFC 7468^] § https://datatracker.ietf.org/doc/html/rfc7468#section-10[10^], https://datatracker.ietf.org/doc/html/rfc3447#appendix-A[APPENDIX-A^]).
[id=bytes_rsa_plain_legacy] [id="bkdn_rsa_priv_legc_plain_ex"]
====== Example ====== Example
[source,text,linenums] [source,text,linenums]

View File

@ -1,3 +1,10 @@
////
This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
To view a copy of this license, visit
http://creativecommons.org/licenses/by-sa/4.0/.
////
[id="bkdn_rsa_priv"]
==== Private ==== Private
include::legacy/main.adoc[] include::legacy/main.adoc[]

View File

@ -1,3 +1,10 @@
////
This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
To view a copy of this license, visit
http://creativecommons.org/licenses/by-sa/4.0/.
////
[id="bkdn_rsa_priv_v1_crypt"]
===== v1 (Encrypted) ===== v1 (Encrypted)
[TIP] [TIP]
@ -34,7 +41,7 @@ This is likely going to be:
The author recommends using `aes256-ctr`. It is currently the upstream default. The author recommends using `aes256-ctr`. It is currently the upstream default.
==== ====
[id=struct_rsa_crypt] [id="bkdn_rsa_priv_v1_crypt_struct"]
====== Structure ====== Structure
[source,text,linenums] [source,text,linenums]
@ -65,10 +72,10 @@ The author recommends using `aes256-ctr`. It is currently the upstream default.
==== ====
*Chunk 4.0:* This is technically currently unused; upstream hardcodes to 1 (left zero-padded 0x01). *Chunk 4.0:* This is technically currently unused; upstream hardcodes to 1 (left zero-padded 0x01).
*Chunk 4.0.1.0:* When decrypted, this is equivalent to the <<struct_rsa_plain,plaintext>> *4.0.1.0* to *4.0.1.10*. It uses a padded size appropriate to the encryption cipher used. *Chunk 4.0.1.0:* When decrypted, this is equivalent to the <<bkdn_rsa_priv_v1_plain_struct, plaintext>> *4.0.1.0* to *4.0.1.10*. It uses a padded size appropriate to the encryption cipher used.
==== ====
[id=bytes_rsa_crypt] [id="bkdn_rsa_priv_v1_crypt_ex"]
====== Example ====== Example
The following example, being encrypted, is protected with a passphrase. The passphrase used in this example key is *`test`*. The following example, being encrypted, is protected with a passphrase. The passphrase used in this example key is *`test`*.
@ -228,7 +235,7 @@ ZnrXZl+8QIW1MSvaaQFmJFqTs=
[NOTE] [NOTE]
==== ====
The decrypted *4.0.1.0* should match the <<struct_rsa_plain, plaintext key's structure>> for *4.0.1.0* through *4.0.1.10*. The padding length WILL change, however, between the two unless using a cipher with an 8-byte block size. The decrypted *4.0.1.0* should match the <<bkdn_rsa_priv_v1_plain_ex, plaintext key's structure>> for *4.0.1.0* through *4.0.1.10*. The padding length WILL change, however, between the two unless using a cipher with an 8-byte block size.
==== ====
When *4.0.1.0* is decrypted, it yields: When *4.0.1.0* is decrypted, it yields:
@ -311,4 +318,4 @@ When *4.0.1.0* is decrypted, it yields:
4.0.1.10 010203 ([1 2 3], 3 bytes) 4.0.1.10 010203 ([1 2 3], 3 bytes)
---- ----
See the <<struct_rsa_plain, plaintext structure>> for details. See the <<bkdn_rsa_priv_v1_plain_struct, plaintext structure>> for details.

View File

@ -1,3 +1,9 @@
////
This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
To view a copy of this license, visit
http://creativecommons.org/licenses/by-sa/4.0/.
////
include::plain.adoc[] include::plain.adoc[]
include::encrypted.adoc[] include::encrypted.adoc[]

View File

@ -1,3 +1,10 @@
////
This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
To view a copy of this license, visit
http://creativecommons.org/licenses/by-sa/4.0/.
////
[id="bkdn_rsa_priv_v1_plain"]
===== v1 (Plain) ===== v1 (Plain)
[TIP] [TIP]
@ -5,7 +12,7 @@
Since plaintext/unencrypted keys do not have a cipher or KDF (as there's no encryption key or algorithm used), they use the string "none" to identify these (and entirely leave out the KDF options). Since plaintext/unencrypted keys do not have a cipher or KDF (as there's no encryption key or algorithm used), they use the string "none" to identify these (and entirely leave out the KDF options).
==== ====
[id=struct_rsa_plain] [id="bkdn_rsa_priv_v1_plain_struct"]
====== Structure ====== Structure
[source,text,linenums] [source,text,linenums]
@ -48,7 +55,7 @@ Since plaintext/unencrypted keys do not have a cipher or KDF (as there's no encr
[NOTE] [NOTE]
==== ====
*Chunk 3.0.0 to 3.0.1:* These blocks are not present in unencrypted keys (see the <<struct_rsa_crypt, encrypted key structure>> for what these look like). *3.0* reflects this, as it's always going to be `00000000` (0). *Chunk 3.0.0 to 3.0.1:* These blocks are not present in unencrypted keys (see the <<bkdn_rsa_priv_v1_crypt_struct, encrypted key structure>> for what these look like). *3.0* reflects this, as it's always going to be `00000000` (0).
*Chunk 4.0:* This is technically currently unused; upstream hardcodes to 1 (left zero-padded 0x01). *Chunk 4.0:* This is technically currently unused; upstream hardcodes to 1 (left zero-padded 0x01).
@ -57,7 +64,7 @@ Since plaintext/unencrypted keys do not have a cipher or KDF (as there's no encr
*Chunk 4.0.1.10:* The padding used aligns the private key (*4.0.1.0* to *4.0.1.9.0*) to the cipher blocksize. For plaintext keys, a blocksize of 8 is used. *Chunk 4.0.1.10:* The padding used aligns the private key (*4.0.1.0* to *4.0.1.9.0*) to the cipher blocksize. For plaintext keys, a blocksize of 8 is used.
==== ====
[id=bytes_rsa_plain] [id="bkdn_rsa_priv_v1_plain_ex"]
====== Example ====== Example
The following example, being encrypted, is protected with a passphrase. The passphrase used in this example key is *`test`*. The following example, being encrypted, is protected with a passphrase. The passphrase used in this example key is *`test`*.

View File

@ -1,5 +1,13 @@
////
This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
To view a copy of this license, visit
http://creativecommons.org/licenses/by-sa/4.0/.
////
[id="bkdn_rsa_pub"]
==== Public ==== Public
[id="bkdn_rsa_pub_struct"]
===== Structure ===== Structure
Public keys are stored in the following structure: Public keys are stored in the following structure:
@ -15,6 +23,7 @@ Public keys are stored in the following structure:
2.0 modulus ('n') (bytes) 2.0 modulus ('n') (bytes)
---- ----
[id="bkdn_rsa_pub_ex"]
===== Example ===== Example
.`.pub` format .`.pub` format

View File

@ -1,3 +1,13 @@
######################################################################################################
# This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. #
# To view a copy of this license, visit #
# http://creativecommons.org/licenses/by-sa/4.0/. #
######################################################################################################
= NOTES =
This document is largely just a dump of "further reading" resources.
https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
canonical: https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.key?annotate=HEAD canonical: https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.key?annotate=HEAD
https://peterlyons.com/problog/2017/12/openssh-ed25519-private-key-file-format/ https://peterlyons.com/problog/2017/12/openssh-ed25519-private-key-file-format/
@ -48,4 +58,4 @@ https://github.com/openssh/openssh-portable/blob/master/authfile.c
sshkey_save_private_blob (~L56) sshkey_save_private_blob (~L56)
https://github.com/openssh/openssh-portable/blob/master/ssh-keygen.c https://github.com/openssh/openssh-portable/blob/master/ssh-keygen.c
funcs: funcs:
main (~L3145; ~L3673 onwards for key generation) main (~L3145; ~L3673 onwards for key generation)

View File

@ -0,0 +1,3 @@
TODO
I need to look further into the aes128-gcm@openssh.com variant to see if this is valid.

View File

@ -0,0 +1,3 @@
TODO
I need to look further into the aes256-gcm@openssh.com variant to see if this is valid.

View File

@ -1 +1,14 @@
TODO TODO
I need to fork out the chacha20-poly1305 pkg from golang x-stdlib
(
https://pkg.go.dev/golang.org/x/crypto/chacha20poly1305
https://cs.opensource.google/go/x/crypto/+/master:chacha20poly1305/
https://github.com/golang/go/issues/36646
)
because they explicitly do NOT support the chacha20-poly1305 OpenSSH variant (chacha20-poly1305@openssh.com)
(https://github.com/golang/go/issues/36646#issue-552055939
"and there is exactly one widely used (or otherwise) composition:
ChaCha20Poly1305 as implemented by x/crypto/chacha20poly1305 (or by SSH in their weird variant)"
sidenote, this is the same guy that decided it would be a good idea to deprecate golang x-stdlib gpg).

View File

@ -0,0 +1,6 @@
package poly1305
const (
Name string = "chacha20-poly1305@openssh.com"
BlockSize int = 8
)

View File

@ -4,8 +4,7 @@ import (
`bytes` `bytes`
`io` `io`
`r00t2.io/sshkeys/cipher/aes` `r00t2.io/cc20p1305ssh`
`r00t2.io/sshkeys/cipher/aes/aes128`
`r00t2.io/sshkeys/internal` `r00t2.io/sshkeys/internal`
) )
@ -39,7 +38,7 @@ func (c *Cipher) NameBytes() (name []byte) {
// BlockSize returns the blocksize of this Cipher. // BlockSize returns the blocksize of this Cipher.
func (c *Cipher) BlockSize() (size int) { func (c *Cipher) BlockSize() (size int) {
size = aes.BlockSize size = BlockSize
return return
} }
@ -47,7 +46,7 @@ func (c *Cipher) BlockSize() (size int) {
// KdfKeySize returns the target key length from KDF to use with this Cipher. // KdfKeySize returns the target key length from KDF to use with this Cipher.
func (c *Cipher) KdfKeySize() (size int) { func (c *Cipher) KdfKeySize() (size int) {
size = aes128.KeySize size = cc20p1305ssh.KeySize
return return
} }

View File

@ -0,0 +1,22 @@
package poly1305
/*
Cipher is a ChaCha20-Poly1305 (OpenSSH variant) cipher.Cipher.
In the OpenSSH variant (for *key* encryption), only the first
32 bytes is used from the 64-byte key as generated from ChaCha20.
It then proceeds per https://datatracker.ietf.org/doc/html/rfc8439#section-2.8
except:
* The nonce used is a constant of 16 zero bytes
* There is no additional authenticated data
* The Poly1305 authentication tag is generated via a message
that consists *only* of the ciphertext.
In other words, OpenSSH does *not* add padding or
encode message lengths to generate the Poly1305
authentication tag.
*/
type Cipher struct {
Key []byte
}

6
cipher/null/consts.go Normal file
View File

@ -0,0 +1,6 @@
package null
const (
Name string = ""
BlockSize int = 8
)

View File

@ -1,17 +1,26 @@
package null package null
import ( import (
`bytes` "bytes"
`io`
`r00t2.io/sshkeys/cipher/aes` `r00t2.io/sshkeys/cipher`
`r00t2.io/sshkeys/cipher/aes/aes128` "r00t2.io/sshkeys/internal"
`r00t2.io/sshkeys/internal`
) )
/*
Setup populates a Cipher from a key.
This is basically a no-op as null.Cipher does not offer any encryption, so there's no setup necessary.
*/
func (c *Cipher) Setup(key []byte) (err error) { func (c *Cipher) Setup(key []byte) (err error) {
// TODO if c == nil {
*c = Cipher{}
}
if err = c.keyChk(); err != nil {
return
}
return return
} }
@ -39,21 +48,26 @@ func (c *Cipher) NameBytes() (name []byte) {
// BlockSize returns the blocksize of this Cipher. // BlockSize returns the blocksize of this Cipher.
func (c *Cipher) BlockSize() (size int) { func (c *Cipher) BlockSize() (size int) {
size = aes.BlockSize size = BlockSize
return
}
// KdfKeySize returns the target key length from KDF to use with this Cipher.
func (c *Cipher) KdfKeySize() (size int) {
size = aes128.KeySize
return return
} }
/* /*
Encrypt encrypts data (a string, []byte, byte, *bytes.Buffer, or *bytes.Reader) to the *bytes.Reader encrypted. KdfKeySize returns the target key length from KDF to use with this Cipher.
Because this function is only here for interface compat, it always returns 0
(as a Null cipher uses no KDF).
*/
func (c *Cipher) KdfKeySize() (size int) {
size = 0
return
}
/*
Encrypt "encrypts" data (a string, []byte, byte, *bytes.Buffer, or *bytes.Reader) to the *bytes.Reader encrypted.
NOTE: Padding IS applied automatically. NOTE: Padding IS applied automatically.
@ -61,31 +75,15 @@ func (c *Cipher) KdfKeySize() (size int) {
It is up to the caller to consume the buffer as desired beforehand or isolate to a specific sub-buffer beforehand to pass to Cipher.Encrypt. It is up to the caller to consume the buffer as desired beforehand or isolate to a specific sub-buffer beforehand to pass to Cipher.Encrypt.
NOTE: If data is a *bytes.Reader, ALL bytes WILL be consumed. NOTE: If data is a *bytes.Reader, ALL bytes WILL be consumed.
NOTE: No actual encryption takes place, only padding.
*/ */
func (c *Cipher) Encrypt(data interface{}) (encrypted *bytes.Reader, err error) { func (c *Cipher) Encrypt(data interface{}) (encrypted *bytes.Reader, err error) {
var b []byte if encrypted, err = c.Pad(data); err != nil {
var cryptDst []byte
var padded *bytes.Reader
if b, err = internal.SerializeData(data); err != nil {
return return
} }
if padded, err = c.Pad(b); err != nil {
return
}
b = make([]byte, padded.Len())
if b, err = io.ReadAll(padded); err != nil {
return
}
cryptDst = make([]byte, len(b))
// TODO
_ = cryptDst
return return
} }
@ -125,17 +123,38 @@ func (c *Cipher) AllocateEncrypt(data interface{}) (encrypted *bytes.Reader, err
} }
/* /*
Pad will pad data (a string, []byte, byte, or *bytes.Buffer) to the Cipher.BlockSize (if necessary). Pad will pad data (a string, []byte, byte, *bytes.Buffer, *bytes.Reader) to the Cipher.BlockSize. The resulting padded *bytes.Reader is returned.
The resulting padded buffer is returned.
NOTE: If data is a *bytes.Buffer, no bytes will be consumed -- the bytes are taken in entirety without consuming them (Buffer.Bytes()). NOTE: If data is a *bytes.Buffer, no bytes will be consumed -- the bytes are taken in entirety without consuming them (Buffer.Bytes()).
It is up to the caller to consume the buffer as desired beforehand or isolate to a specific sub-buffer beforehand to pass to Cipher.Pad. It is up to the caller to consume the buffer as desired beforehand or isolate to a specific sub-buffer beforehand to pass to Pad.
NOTE: If data is a *bytes.Reader, ALL bytes WILL be consumed. NOTE: If data is a *bytes.Reader, ALL bytes WILL be consumed.
*/ */
func (c *Cipher) Pad(data interface{}) (paddedBuf *bytes.Reader, err error) { func (c *Cipher) Pad(data interface{}) (paddedBuf *bytes.Reader, err error) {
// TODO var b []byte
var padNum int
var pad []byte
var buf *bytes.Buffer
if err = c.keyChk(); err != nil {
return
}
if b, err = internal.UnpackBytes(data); err != nil {
return
}
buf = bytes.NewBuffer(b)
for padIdx := 1; (buf.Len() % BlockSize) != 0; padIdx++ {
padNum = padIdx & cipher.PadMod
pad = []byte{byte(uint32(padNum))}
if _, err = buf.Write(pad); err != nil {
return
}
}
return return
} }
@ -152,17 +171,13 @@ func (c *Cipher) Pad(data interface{}) (paddedBuf *bytes.Reader, err error) {
*/ */
func (c *Cipher) Decrypt(data interface{}) (decrypted *bytes.Reader, err error) { func (c *Cipher) Decrypt(data interface{}) (decrypted *bytes.Reader, err error) {
var b []byte var plain []byte
var decryptDst []byte
if b, err = internal.SerializeData(data); err != nil { if plain, err = internal.SerializeData(data); err != nil {
return return
} }
decryptDst = make([]byte, len(b)) decrypted = bytes.NewReader(plain)
// TODO
_ = decryptDst
return return
} }
@ -195,11 +210,21 @@ func (c *Cipher) AllocatedDecrypt(data interface{}) (decrypted *bytes.Reader, er
/* /*
IsPlain indicates if this Cipher is a plain/null encryption (cipher.null.Null). IsPlain indicates if this Cipher is a plain/null encryption (cipher.null.Null).
It will always return false. It is included for interface compatability. It will always return true. It is included for interface compatability.
*/ */
func (c *Cipher) IsPlain() (plain bool) { func (c *Cipher) IsPlain() (plain bool) {
plain = false plain = true
return
}
// keyChk is more or less a no-op but provides some basic sanity checking.
func (c *Cipher) keyChk() (err error) {
if c == nil {
*c = Cipher{}
}
return return
} }

4
cipher/null/types.go Normal file
View File

@ -0,0 +1,4 @@
package null
// Cipher is a null (plaintext) cipher.Cipher.
type Cipher struct{}