bcrypt and null kdf done, work on ciphers next (then keytypes)
This commit is contained in:
		
							parent
							
								
									91d5e99404
								
							
						
					
					
						commit
						ff3f8243d1
					
				
							
								
								
									
										2
									
								
								TODO
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								TODO
									
									
									
									
									
								
							| @ -4,5 +4,3 @@ | ||||
| --- ssh-rsa (sha1), rsa-sha2-256, rsa-sha2-512 (new default) | ||||
| - ciphers: | ||||
| -- 3des-cbc, aes128-cbc, aes192-cbc, aes256-cbc, aes128-ctr, aes192-ctr, aes256-ctr, aes128-gcm@openssh.com, aes256-gcm@openssh.com, chacha20-poly1305@openssh.com | ||||
| - kdf | ||||
| -- bcrypt_pbkdf | ||||
|  | ||||
| @ -651,13 +651,90 @@ pre.rouge { | ||||
|   background-color: #f8f8f8; | ||||
| } | ||||
| </style> | ||||
| <!-- https://stackoverflow.com/a/34481639 --> | ||||
| <!-- Generate a nice TOC --> | ||||
| <script src="https://code.jquery.com/jquery-1.11.3.min.js"></script> | ||||
| <script src="https://code.jquery.com/ui/1.11.4/jquery-ui.min.js"></script> | ||||
| <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.tocify/1.9.0/javascripts/jquery.tocify.min.js"></script> | ||||
| <!-- We do not need the tocify CSS because the asciidoc CSS already provides most of what we neeed --> | ||||
| 
 | ||||
| <style> | ||||
| .tocify-header { | ||||
|     font-style: italic; | ||||
| } | ||||
| 
 | ||||
| .tocify-subheader { | ||||
|     font-style: normal; | ||||
|     font-size: 90%; | ||||
| } | ||||
| 
 | ||||
| .tocify ul { | ||||
|     margin: 0; | ||||
|  } | ||||
| 
 | ||||
| .tocify-focus { | ||||
|     color: #7a2518; | ||||
|     background-color: rgba(0, 0, 0, 0.1); | ||||
| } | ||||
| 
 | ||||
| .tocify-focus > a { | ||||
|     color: #7a2518; | ||||
| } | ||||
| </style> | ||||
| 
 | ||||
| <script type="text/javascript"> | ||||
|     $(function () { | ||||
|         // Add a new container for the tocify toc into the existing toc so we can re-use its | ||||
|         // styling | ||||
|         $("#toc").append("<div id='generated-toc'></div>"); | ||||
|         $("#generated-toc").tocify({ | ||||
|             extendPage: true, | ||||
|             context: "#content", | ||||
|             highlightOnScroll: true, | ||||
|             hideEffect: "slideUp", | ||||
|             // Use the IDs that asciidoc already provides so that TOC links and intra-document | ||||
|             // links are the same. Anything else might confuse users when they create bookmarks. | ||||
|             hashGenerator: function(text, element) { | ||||
|                 return $(element).attr("id"); | ||||
|             }, | ||||
|             // Smooth scrolling doesn't work properly if we use the asciidoc IDs | ||||
|             smoothScroll: false, | ||||
|             // Set to 'none' to use the tocify classes | ||||
|             theme: "none", | ||||
|             // Handle book (may contain h1) and article (only h2 deeper) | ||||
|             selectors: $( "#content" ).has( "h1" ).size() > 0 ? "h1,h2,h3,h4,h5" : "h2,h3,h4,h5", | ||||
|             ignoreSelector: ".discrete" | ||||
|         }); | ||||
| 
 | ||||
|         // Switch between static asciidoc toc and dynamic tocify toc based on browser size | ||||
|         // This is set to match the media selectors in the asciidoc CSS | ||||
|         // Without this, we keep the dynamic toc even if it is moved from the side to preamble | ||||
|         // position which will cause odd scrolling behavior | ||||
|         var handleTocOnResize = function() { | ||||
|             if ($(document).width() < 768) { | ||||
|                 $("#generated-toc").hide(); | ||||
|                 $(".sectlevel0").show(); | ||||
|                 $(".sectlevel1").show(); | ||||
|             } | ||||
|             else { | ||||
|                 $("#generated-toc").show(); | ||||
|                 $(".sectlevel0").hide(); | ||||
|                 $(".sectlevel1").hide(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         $(window).resize(handleTocOnResize); | ||||
|         handleTocOnResize(); | ||||
|     }); | ||||
| </script> | ||||
| 
 | ||||
| </head> | ||||
| <body class="book toc2 toc-left"> | ||||
| <div id="header"> | ||||
| <h1>OpenSSH Key Structure Guide</h1> | ||||
| <div class="details"> | ||||
| <span id="author" class="author">brent saner <bts@square-r00t.net>, https://r00t2.io</span><br> | ||||
| <span id="revdate">Last updated 2022-03-10 00:18:45 -0500</span> | ||||
| <span id="revdate">Last updated 2022-04-25 04:27:24 -0400</span> | ||||
| </div> | ||||
| <div id="toc" class="toc2"> | ||||
| <div id="toctitle">Table of Contents</div> | ||||
|  | ||||
							
								
								
									
										77
									
								
								_ref/docinfo.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								_ref/docinfo.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,77 @@ | ||||
| <!-- https://stackoverflow.com/a/34481639 --> | ||||
| <!-- Generate a nice TOC --> | ||||
| <script src="https://code.jquery.com/jquery-1.11.3.min.js"></script> | ||||
| <script src="https://code.jquery.com/ui/1.11.4/jquery-ui.min.js"></script> | ||||
| <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.tocify/1.9.0/javascripts/jquery.tocify.min.js"></script> | ||||
| <!-- We do not need the tocify CSS because the asciidoc CSS already provides most of what we neeed --> | ||||
| 
 | ||||
| <style> | ||||
| .tocify-header { | ||||
|     font-style: italic; | ||||
| } | ||||
| 
 | ||||
| .tocify-subheader { | ||||
|     font-style: normal; | ||||
|     font-size: 90%; | ||||
| } | ||||
| 
 | ||||
| .tocify ul { | ||||
|     margin: 0; | ||||
|  } | ||||
| 
 | ||||
| .tocify-focus { | ||||
|     color: #7a2518;  | ||||
|     background-color: rgba(0, 0, 0, 0.1); | ||||
| } | ||||
| 
 | ||||
| .tocify-focus > a { | ||||
|     color: #7a2518;  | ||||
| } | ||||
| </style> | ||||
| 
 | ||||
| <script type="text/javascript"> | ||||
|     $(function () { | ||||
|         // Add a new container for the tocify toc into the existing toc so we can re-use its | ||||
|         // styling | ||||
|         $("#toc").append("<div id='generated-toc'></div>"); | ||||
|         $("#generated-toc").tocify({ | ||||
|             extendPage: true, | ||||
|             context: "#content", | ||||
|             highlightOnScroll: true, | ||||
|             hideEffect: "slideUp", | ||||
|             // Use the IDs that asciidoc already provides so that TOC links and intra-document | ||||
|             // links are the same. Anything else might confuse users when they create bookmarks. | ||||
|             hashGenerator: function(text, element) { | ||||
|                 return $(element).attr("id"); | ||||
|             }, | ||||
|             // Smooth scrolling doesn't work properly if we use the asciidoc IDs | ||||
|             smoothScroll: false, | ||||
|             // Set to 'none' to use the tocify classes | ||||
|             theme: "none", | ||||
|             // Handle book (may contain h1) and article (only h2 deeper) | ||||
|             selectors: $( "#content" ).has( "h1" ).size() > 0 ? "h1,h2,h3,h4,h5" : "h2,h3,h4,h5", | ||||
|             ignoreSelector: ".discrete" | ||||
|         }); | ||||
| 
 | ||||
|         // Switch between static asciidoc toc and dynamic tocify toc based on browser size | ||||
|         // This is set to match the media selectors in the asciidoc CSS | ||||
|         // Without this, we keep the dynamic toc even if it is moved from the side to preamble | ||||
|         // position which will cause odd scrolling behavior | ||||
|         var handleTocOnResize = function() { | ||||
|             if ($(document).width() < 768) { | ||||
|                 $("#generated-toc").hide(); | ||||
|                 $(".sectlevel0").show(); | ||||
|                 $(".sectlevel1").show(); | ||||
|             } | ||||
|             else { | ||||
|                 $("#generated-toc").show(); | ||||
|                 $(".sectlevel0").hide(); | ||||
|                 $(".sectlevel1").hide(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         $(window).resize(handleTocOnResize); | ||||
|         handleTocOnResize(); | ||||
|     }); | ||||
| </script> | ||||
| 
 | ||||
							
								
								
									
										1
									
								
								cipher/aes/aes128/cbc/TODO
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								cipher/aes/aes128/cbc/TODO
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| TODO | ||||
							
								
								
									
										5
									
								
								cipher/aes/aes128/cbc/consts.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								cipher/aes/aes128/cbc/consts.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| package cbc | ||||
| 
 | ||||
| const ( | ||||
| 	Name string = "aes128-cbc" | ||||
| ) | ||||
							
								
								
									
										10
									
								
								cipher/aes/aes128/consts.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								cipher/aes/aes128/consts.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| package aes128 | ||||
| 
 | ||||
| import ( | ||||
| 	`r00t2.io/sshkeys/cipher/aes` | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	KeySize    int = 16 // in bytes; AES128 is so named for its 128-bit key, thus: 128 / 8 = 16 | ||||
| 	KdfKeySize int = KeySize + aes.IvSize | ||||
| ) | ||||
							
								
								
									
										1
									
								
								cipher/aes/aes128/ctr/TODO
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								cipher/aes/aes128/ctr/TODO
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| TODO | ||||
							
								
								
									
										5
									
								
								cipher/aes/aes128/ctr/consts.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								cipher/aes/aes128/ctr/consts.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| package ctr | ||||
| 
 | ||||
| const ( | ||||
| 	Name string = "aes128-ctr" | ||||
| ) | ||||
							
								
								
									
										1
									
								
								cipher/aes/aes128/gcm/TODO
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								cipher/aes/aes128/gcm/TODO
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| TODO | ||||
							
								
								
									
										5
									
								
								cipher/aes/aes128/gcm/consts.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								cipher/aes/aes128/gcm/consts.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| package gcm | ||||
| 
 | ||||
| const ( | ||||
| 	Name string = "aes128-gcm@openssh.com" | ||||
| ) | ||||
							
								
								
									
										1
									
								
								cipher/aes/aes192/cbc/TODO
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								cipher/aes/aes192/cbc/TODO
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| TODO | ||||
							
								
								
									
										5
									
								
								cipher/aes/aes192/cbc/consts.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								cipher/aes/aes192/cbc/consts.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| package cbc | ||||
| 
 | ||||
| const ( | ||||
| 	Name string = "aes192-cbc" | ||||
| ) | ||||
							
								
								
									
										10
									
								
								cipher/aes/aes192/consts.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								cipher/aes/aes192/consts.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| package aes192 | ||||
| 
 | ||||
| import ( | ||||
| 	`r00t2.io/sshkeys/cipher/aes` | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	KeySize    int = 24 // in bytes; AES182 is so named for its 192-bit key, thus: 192 / 8 = 24 | ||||
| 	KdfKeySize int = KeySize + aes.IvSize | ||||
| ) | ||||
							
								
								
									
										1
									
								
								cipher/aes/aes192/ctr/TODO
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								cipher/aes/aes192/ctr/TODO
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| TODO | ||||
							
								
								
									
										5
									
								
								cipher/aes/aes192/ctr/consts.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								cipher/aes/aes192/ctr/consts.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| package ctr | ||||
| 
 | ||||
| const ( | ||||
| 	Name string = "aes192-ctr" | ||||
| ) | ||||
							
								
								
									
										1
									
								
								cipher/aes/aes192/gcm/TODO
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								cipher/aes/aes192/gcm/TODO
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| TODO | ||||
							
								
								
									
										5
									
								
								cipher/aes/aes192/gcm/consts.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								cipher/aes/aes192/gcm/consts.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| package gcm | ||||
| 
 | ||||
| const ( | ||||
| 	Name string = "aes192-gcm@openssh.com" | ||||
| ) | ||||
							
								
								
									
										1
									
								
								cipher/aes/aes256/cbc/TODO
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								cipher/aes/aes256/cbc/TODO
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| TODO | ||||
							
								
								
									
										5
									
								
								cipher/aes/aes256/cbc/consts.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								cipher/aes/aes256/cbc/consts.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| package cbc | ||||
| 
 | ||||
| const ( | ||||
| 	Name string = "aes256-cbc" | ||||
| ) | ||||
							
								
								
									
										10
									
								
								cipher/aes/aes256/consts.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								cipher/aes/aes256/consts.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| package aes256 | ||||
| 
 | ||||
| import ( | ||||
| 	`r00t2.io/sshkeys/cipher/aes` | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	KeySize    int = 32 // in bytes; AES256 is so named for its 256-bit key, thus: 256 / 8 = 32 | ||||
| 	KdfKeySize int = KeySize + aes.IvSize | ||||
| ) | ||||
							
								
								
									
										1
									
								
								cipher/aes/aes256/ctr/TODO
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								cipher/aes/aes256/ctr/TODO
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| TODO | ||||
							
								
								
									
										5
									
								
								cipher/aes/aes256/ctr/consts.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								cipher/aes/aes256/ctr/consts.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| package ctr | ||||
| 
 | ||||
| const ( | ||||
| 	Name string = "aes256-ctr" | ||||
| ) | ||||
							
								
								
									
										1
									
								
								cipher/aes/aes256/gcm/TODO
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								cipher/aes/aes256/gcm/TODO
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| TODO | ||||
							
								
								
									
										5
									
								
								cipher/aes/aes256/gcm/consts.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								cipher/aes/aes256/gcm/consts.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| package gcm | ||||
| 
 | ||||
| const ( | ||||
| 	Name string = "aes256-gcm@openssh.com" | ||||
| ) | ||||
							
								
								
									
										10
									
								
								cipher/aes/consts.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								cipher/aes/consts.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| package aes | ||||
| 
 | ||||
| import ( | ||||
| 	`crypto/aes` | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	BlockSize int = aes.BlockSize | ||||
| 	IvSize    int = aes.BlockSize | ||||
| ) | ||||
							
								
								
									
										1
									
								
								cipher/chacha20/poly1305/TODO
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								cipher/chacha20/poly1305/TODO
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| TODO | ||||
							
								
								
									
										1
									
								
								cipher/null/TODO
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								cipher/null/TODO
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| TODO | ||||
							
								
								
									
										1
									
								
								cipher/tripledes/cbc/TODO
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								cipher/tripledes/cbc/TODO
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| TODO | ||||
							
								
								
									
										50
									
								
								cipher/types.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								cipher/types.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | ||||
| package cipher | ||||
| 
 | ||||
| import ( | ||||
| 	`bytes` | ||||
| ) | ||||
| 
 | ||||
| type Cipher interface { | ||||
| 	// Name returns the string form of the cipher name. | ||||
| 	Name() (name string) | ||||
| 	// NameBytes returns the Name result but in bytes, with a leading uint32 bytecount packed in. | ||||
| 	NameBytes() (name []byte) | ||||
| 	// BlockSize returns the blocksize of the cipher.Cipher. This is used for externally padding data for Cipher.Encrypt and Cipher.AllocateEncrypt. | ||||
| 	BlockSize() (size int) | ||||
| 	// KdfKeySize returns the desired/needed key size for use with kdf.KDF. | ||||
| 	KdfKeySize() (size int) | ||||
| 	// Setup initializes the Cipher with a given key. | ||||
| 	Setup(key []byte) (err error) | ||||
| 	/* | ||||
| 		Encrypt takes plain data, either a: | ||||
| 			- string | ||||
| 			- raw byte slice ([]byte or []uint8) | ||||
| 			- single byte (byte or uint8) | ||||
| 			- *bytes.Buffer | ||||
| 		and returns an encrypted *bytes.Buffer of data. | ||||
| 	*/ | ||||
| 	Encrypt(data interface{}) (encrypted *bytes.Reader, err error) | ||||
| 	// AllocateEncrypt is exactly like cipher.Cipher.Encrypt except that it includes a (NON-encrypted) uint32 prefix of byte allocation. | ||||
| 	AllocateEncrypt(data interface{}) (encrypted *bytes.Reader, err error) | ||||
| 	/* | ||||
| 		Decrypt takes encrypted data, either a: | ||||
| 			- raw byte slice ([]byte or []uint8) | ||||
| 			- *bytes.Buffer | ||||
| 		and returns a plain/decrypted *bytes.Buffer of data. | ||||
| 	*/ | ||||
| 	Decrypt(data interface{}) (decrypted *bytes.Reader, err error) | ||||
| 	// AllocatedDecrypt is exactly like cipher.Cipher.Decrypt except that it assumes that data includes a (NON-encrypted) uint32 prefix of byte allocation. | ||||
| 	AllocatedDecrypt(data interface{}) (decrypted *bytes.Reader, err error) | ||||
| 	// IsPlain returns true if this is a "null" cipher; i.e. no encryption is actually performed. | ||||
| 	IsPlain() (plain bool) | ||||
| 	/* | ||||
| 		Pad returns padded bytes in a *bytes.Buffer according to the cipher's padding specification. | ||||
| 		data can be one of either: | ||||
| 			- string | ||||
| 			- raw byte slice ([]byte or []uint8) | ||||
| 			- single byte (byte or uint8) | ||||
| 			- *bytes.Buffer | ||||
| 		This is a prerequisite in some ciphers, and must be performed BEFORE encrypting. | ||||
| 	*/ | ||||
| 	Pad(data interface{}) (paddedBuf *bytes.Reader, err error) | ||||
| } | ||||
							
								
								
									
										11
									
								
								kdf/bcrypt/consts.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								kdf/bcrypt/consts.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | ||||
| package bcrypt | ||||
| 
 | ||||
| const ( | ||||
| 	Name string = "bcrypt" | ||||
| 	// DefaultRounds is the default per OpenSSH, not per the bcrypt_pbkdf spec itself. It is recommended to use e.g. 100 rounds. | ||||
| 	DefaultRounds uint32 = 16 | ||||
| 	// DefaultSaltLen is the default per OpenSSH, not per the bcrypt_pbkdf spec itself. | ||||
| 	DefaultSaltLen int = 16 | ||||
| 	// DefaultKeyLen is suitable for AES256-CTR but may not be for others. TODO: revisit this and find something more flexible? | ||||
| 	DefaultKeyLen uint32 = 48 | ||||
| ) | ||||
							
								
								
									
										246
									
								
								kdf/bcrypt/funcs.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										246
									
								
								kdf/bcrypt/funcs.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,246 @@ | ||||
| package bcrypt | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"crypto/rand" | ||||
| 	"encoding/binary" | ||||
| 
 | ||||
| 	"github.com/dchest/bcrypt_pbkdf" | ||||
| 	"r00t2.io/sshkeys/internal" | ||||
| 	`r00t2.io/sshkeys/kdf/errs` | ||||
| ) | ||||
| 
 | ||||
| /* | ||||
| 	Setup must be called before DeriveKey. It configures a . | ||||
| 
 | ||||
| 	If secret is nil or an empty byte slice, ErrNoSecret will be returned. | ||||
| 
 | ||||
| 	If salt is nil or an empty byte slice, one will be randomly generated of DefaultSaltLen length. | ||||
| 
 | ||||
| 	If rounds is 0, DefaultRounds will be used. | ||||
| 
 | ||||
| 	If keyLen is 0, DefaultKeyLen will be used. | ||||
| */ | ||||
| func (k *KDF) Setup(secret, salt []byte, rounds, keyLen uint32) (err error) { | ||||
| 
 | ||||
| 	if secret == nil || len(secret) == 0 { | ||||
| 		err = errs.ErrNoSecret | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if salt == nil || len(salt) == 0 { | ||||
| 		salt = make([]byte, DefaultSaltLen) | ||||
| 		if _, err = rand.Read(salt); err != nil { | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if rounds == 0 { | ||||
| 		rounds = DefaultRounds | ||||
| 	} | ||||
| 
 | ||||
| 	if keyLen == 0 { | ||||
| 		keyLen = DefaultKeyLen | ||||
| 	} | ||||
| 
 | ||||
| 	k.secret = secret | ||||
| 	k.salt = salt | ||||
| 	k.rounds = rounds | ||||
| 	k.keyLen = keyLen | ||||
| 	k.hasSalt = true | ||||
| 	k.hasRounds = true | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| 	SetupAuto is used to provide out-of-band configuration if the KDF options were found via kdf.UnpackKDF. | ||||
| 
 | ||||
| 	You can test this by running KDF.AutoOK. | ||||
| */ | ||||
| func (k *KDF) SetupAuto(secret []byte, keyLen uint32) (err error) { | ||||
| 
 | ||||
| 	if !k.AutoOK() { | ||||
| 		err = errs.ErrUnknownKdf | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if keyLen == 0 { | ||||
| 		keyLen = DefaultKeyLen | ||||
| 	} | ||||
| 
 | ||||
| 	k.secret = secret | ||||
| 	k.keyLen = keyLen | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| 	DeriveKey returns the derived key from a configured bcrypt.KDF. | ||||
| 
 | ||||
| 	It must be called *after* Setup. | ||||
| */ | ||||
| func (k *KDF) DeriveKey() (key []byte, err error) { | ||||
| 
 | ||||
| 	if k.secret == nil { | ||||
| 		err = errs.ErrNoSecret | ||||
| 		return | ||||
| 	} | ||||
| 	if k.salt == nil { | ||||
| 		err = errs.ErrNoSalt | ||||
| 		return | ||||
| 	} | ||||
| 	if k.rounds == 0 { | ||||
| 		err = errs.ErrNoRounds | ||||
| 		return | ||||
| 	} | ||||
| 	if k.keyLen == 0 { | ||||
| 		err = errs.ErrNoKeyLen | ||||
| 	} | ||||
| 
 | ||||
| 	if k.key, err = bcrypt_pbkdf.Key(k.secret, k.salt, int(k.rounds), int(k.keyLen)); err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	key = k.key | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // Name returns bcrypt.Name. | ||||
| func (k *KDF) Name() (name string) { | ||||
| 
 | ||||
| 	name = Name | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // NameBytes returns the byte form of bcrypt.Name with leading bytecount allocator. | ||||
| func (k *KDF) NameBytes() (name []byte) { | ||||
| 
 | ||||
| 	var nb []byte | ||||
| 	var s = k.Name() | ||||
| 
 | ||||
| 	nb = []byte(s) | ||||
| 
 | ||||
| 	name = make([]byte, 4) | ||||
| 	binary.BigEndian.PutUint32(name, uint32(len(nb))) | ||||
| 	name = append(name, nb...) | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // PackedBytes returns block 3.0 and recursed. | ||||
| func (k *KDF) PackedBytes() (buf *bytes.Reader, err error) { | ||||
| 
 | ||||
| 	var rounds = make([]byte, 4) | ||||
| 	var packer *bytes.Reader | ||||
| 	var w = new(bytes.Buffer) | ||||
| 
 | ||||
| 	// block 3.0.0.0 and block 3.0.0.0.0 | ||||
| 	if packer, err = internal.ReadSizeBytes(k.salt, true); err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 	if _, err = packer.WriteTo(w); err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// block 3.0.0.1 | ||||
| 	binary.BigEndian.PutUint32(rounds, k.rounds) | ||||
| 	if _, err = w.Write(rounds); err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// block 3.0 | ||||
| 	if buf, err = internal.ReadSizeBytes(w, true); err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // Rounds returns the number of rounds used in derivation. | ||||
| func (k *KDF) Rounds() (rounds uint32) { | ||||
| 
 | ||||
| 	rounds = k.rounds | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // Salt returns the salt bytes. | ||||
| func (k *KDF) Salt() (salt []byte) { | ||||
| 
 | ||||
| 	salt = k.salt | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| 	AutoOK returns true if a kdf.UnpackKDF call was able to fetch the bcrypt.KDF options successfully, in which case the caller may use bcrypt.KDF.SetupAuto. | ||||
| 
 | ||||
| 	If false, it will need to be manually configured via bcrypt.KDF.Setup. | ||||
| */ | ||||
| func (k *KDF) AutoOK() (ok bool) { | ||||
| 
 | ||||
| 	ok = true | ||||
| 
 | ||||
| 	if k.salt == nil || len(k.salt) == 0 { | ||||
| 		ok = false | ||||
| 	} | ||||
| 
 | ||||
| 	if k.rounds == 0 { | ||||
| 		ok = false | ||||
| 	} | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // IsPlain indicates if this kdf.KDF actually does derivation (false) or not (true). | ||||
| func (k *KDF) IsPlain() (plain bool) { | ||||
| 
 | ||||
| 	plain = false | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| 	AddSalt adds a salt as parsed from kdf.UnpackKDF. | ||||
| 
 | ||||
| 	If a salt has already been set, a no-op will be performed. | ||||
| 	If you want to use a different salt, you will need to create a new bcrypt.KDF. | ||||
| */ | ||||
| func (k *KDF) AddSalt(salt []byte) (err error) { | ||||
| 
 | ||||
| 	if salt == nil || len(salt) == 0 { | ||||
| 		err = errs.ErrNoSalt | ||||
| 		return | ||||
| 	} | ||||
| 	if k.hasSalt { | ||||
| 		return | ||||
| 	} | ||||
| 	k.salt = salt | ||||
| 	k.hasSalt = true | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| 	AddRounds adds the rounds as parsed from kdf.UnpackKDF. | ||||
| 
 | ||||
| 	If the number of rounds has already been set, a no-op will be performed. | ||||
| 	If you want to use a different number of rounds, you will need to create a new bcrypt.KDF. | ||||
| */ | ||||
| func (k *KDF) AddRounds(rounds uint32) (err error) { | ||||
| 
 | ||||
| 	if rounds == 0 { | ||||
| 		err = errs.ErrNoRounds | ||||
| 		return | ||||
| 	} | ||||
| 	if k.hasRounds { | ||||
| 		return | ||||
| 	} | ||||
| 	k.rounds = rounds | ||||
| 	k.hasRounds = true | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										25
									
								
								kdf/bcrypt/types.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								kdf/bcrypt/types.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | ||||
| package bcrypt | ||||
| 
 | ||||
| /* | ||||
| 	BcryptPbkdf combines bcrypt hashing algorithm with PBKDF2 key derivation. | ||||
| 
 | ||||
| 	(bcrypt) https://www.usenix.org/legacy/events/usenix99/provos/provos_html/node1.html | ||||
| 	(PBKDF2) https://datatracker.ietf.org/doc/html/rfc2898 | ||||
| 	http://www.tedunangst.com/flak/post/bcrypt-pbkdf | ||||
| */ | ||||
| type KDF struct { | ||||
| 	// salt is used to salt the hash for each round in rounds. | ||||
| 	salt []byte | ||||
| 	// rounds controls how many iterations of salting/hashing is done. | ||||
| 	rounds uint32 | ||||
| 	// keyLen is how long the derived key should be in bytes. | ||||
| 	keyLen uint32 | ||||
| 	// secret is the "passphrase" used to seed the key creation. | ||||
| 	secret []byte | ||||
| 	// key is used to store the derived key. | ||||
| 	key []byte | ||||
| 	// hasSalt is true if a salt has been set. | ||||
| 	hasSalt bool | ||||
| 	// hasRounds is true if a number of rounds have been set. | ||||
| 	hasRounds bool | ||||
| } | ||||
| @ -1,11 +0,0 @@ | ||||
| package kdf | ||||
| 
 | ||||
| const ( | ||||
| 	BcryptPbkdfName string = "bcrypt" | ||||
| 	// BcryptPbkdfDefaultRounds is the default per OpenSSH, not per the bcrypt_pbkdf spec itself. It is recommended to use e.g. 100 rounds. | ||||
| 	BcryptPbkdfDefaultRounds uint32 = 16 | ||||
| 	// BcryptPbkdfDefaultSaltLen is the default per OpenSSH, not per the bcrypt_pbkdf spec itself. | ||||
| 	BcryptPbkdfDefaultSaltLen int = 16 | ||||
| 	// BcryptPbkdfDefaultKeyLen is suitable for AES256-CTR but may not be for others. TODO: revisit this and find something more flexible. | ||||
| 	BcryptPbkdfDefaultKeyLen uint32 = 48 | ||||
| ) | ||||
| @ -1,5 +0,0 @@ | ||||
| package kdf | ||||
| 
 | ||||
| const ( | ||||
| 	NullName string = "none" | ||||
| ) | ||||
| @ -1,4 +1,4 @@ | ||||
| package kdf | ||||
| package errs | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
							
								
								
									
										23
									
								
								kdf/funcs.go
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								kdf/funcs.go
									
									
									
									
									
								
							| @ -2,6 +2,10 @@ package kdf | ||||
| 
 | ||||
| import ( | ||||
| 	"strings" | ||||
| 
 | ||||
| 	`r00t2.io/sshkeys/kdf/bcrypt` | ||||
| 	`r00t2.io/sshkeys/kdf/errs` | ||||
| 	`r00t2.io/sshkeys/kdf/null` | ||||
| ) | ||||
| 
 | ||||
| /* | ||||
| @ -12,12 +16,12 @@ import ( | ||||
| func GetKDF(name string) (k KDF, err error) { | ||||
| 
 | ||||
| 	switch strings.ToLower(name) { | ||||
| 	case BcryptPbkdfName: | ||||
| 		k = &BcryptPbkdf{} | ||||
| 	case NullName, "": | ||||
| 		k = &Null{} | ||||
| 	case bcrypt.Name: | ||||
| 		k = &bcrypt.KDF{} | ||||
| 	case null.Name, "": | ||||
| 		k = &null.KDF{} | ||||
| 	default: | ||||
| 		err = ErrUnknownKdf | ||||
| 		err = errs.ErrUnknownKdf | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| @ -25,18 +29,21 @@ func GetKDF(name string) (k KDF, err error) { | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| 	UnpackKDF returns a KDF from raw data as packed in a private key's bytes. | ||||
| 	UnpackKDF returns a kdf.KDF from raw data as packed in a private key's bytes. | ||||
| 
 | ||||
| 	KDF.Setup must be called on the resulting KDF. | ||||
| 	kdf.KDF.Setup must be called on the resulting kdf.KDF. | ||||
| 
 | ||||
| 	data can be: | ||||
| 
 | ||||
| 		- a []byte, which can be: | ||||
| 			- the full private key bytes | ||||
| 			- KDF section (e.g. _ref/format.ed25519 2.0 + (3.0 to 3.0.0.1) or 2.0.0 + (3.0 to 3.0.0.1)) | ||||
| 			- KDF section (e.g. ED25519 private key block 2.0 + (block 3.0 to block 3.0.0.1) OR | ||||
| 			  block 2.0.0 + (block 3.0 to block 3.0.0.1)) | ||||
| 		- a *bytes.Buffer, which supports the same as above | ||||
| */ | ||||
| func UnpackKDF(data interface{}) (k KDF, err error) { | ||||
| 
 | ||||
| 	// TODO | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
|  | ||||
| @ -1,226 +0,0 @@ | ||||
| package kdf | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"crypto/rand" | ||||
| 	"encoding/binary" | ||||
| 
 | ||||
| 	bcryptPbkdf "github.com/dchest/bcrypt_pbkdf" | ||||
| 	"r00t2.io/sshkeys/internal" | ||||
| ) | ||||
| 
 | ||||
| /* | ||||
| 	Setup must be called before DeriveKey. It configures a BcryptPbkdf. | ||||
| 
 | ||||
| 	If secret is nil or an empty byte slice, ErrNoSecret will be returned. | ||||
| 
 | ||||
| 	If salt is nil or an empty byte slice, one will be randomly generated of BcryptPbkdfDefaultSaltLen length. | ||||
| 
 | ||||
| 	If rounds is 0, BcryptPbkdfDefaultRounds will be used. | ||||
| 
 | ||||
| 	If keyLen is 0, BcryptPbkdfDefaultKeyLen will be used. | ||||
| */ | ||||
| func (b *BcryptPbkdf) Setup(secret, salt []byte, rounds, keyLen uint32) (err error) { | ||||
| 
 | ||||
| 	if secret == nil || len(secret) == 0 { | ||||
| 		err = ErrNoSecret | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if salt == nil || len(salt) == 0 { | ||||
| 		salt = make([]byte, BcryptPbkdfDefaultSaltLen) | ||||
| 		if _, err = rand.Read(salt); err != nil { | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if rounds == 0 { | ||||
| 		rounds = BcryptPbkdfDefaultRounds | ||||
| 	} | ||||
| 
 | ||||
| 	if keyLen == 0 { | ||||
| 		keyLen = BcryptPbkdfDefaultKeyLen | ||||
| 	} | ||||
| 
 | ||||
| 	b.secret = secret | ||||
| 	b.salt = salt | ||||
| 	b.rounds = rounds | ||||
| 	b.keyLen = keyLen | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| 	SetupAuto is used to provide out-of-band configuration if the KDF options were found via GetKdfFromBytes. | ||||
| 
 | ||||
| 	You can test this by running KDF.AutoOK. | ||||
| */ | ||||
| func (b *BcryptPbkdf) SetupAuto(secret []byte, keyLen uint32) (err error) { | ||||
| 
 | ||||
| 	if !b.AutoOK() { | ||||
| 		err = ErrUnknownKdf | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if keyLen == 0 { | ||||
| 		keyLen = BcryptPbkdfDefaultKeyLen | ||||
| 	} | ||||
| 
 | ||||
| 	b.secret = secret | ||||
| 	b.keyLen = keyLen | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| 	DeriveKey returns the derived key from a BcryptPbkdf. | ||||
| 
 | ||||
| 	It must be called *after* Setup. | ||||
| */ | ||||
| func (b *BcryptPbkdf) DeriveKey() (key []byte, err error) { | ||||
| 
 | ||||
| 	if b.secret == nil { | ||||
| 		err = ErrNoSecret | ||||
| 		return | ||||
| 	} | ||||
| 	if b.salt == nil { | ||||
| 		err = ErrNoSalt | ||||
| 		return | ||||
| 	} | ||||
| 	if b.rounds == 0 { | ||||
| 		err = ErrNoRounds | ||||
| 		return | ||||
| 	} | ||||
| 	if b.keyLen == 0 { | ||||
| 		err = ErrNoKeyLen | ||||
| 	} | ||||
| 
 | ||||
| 	if b.key, err = bcryptPbkdf.Key(b.secret, b.salt, int(b.rounds), int(b.keyLen)); err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	key = b.key | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // Name returns BcryptPbkdfName. | ||||
| func (b *BcryptPbkdf) Name() (name string) { | ||||
| 
 | ||||
| 	name = BcryptPbkdfName | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // NameBytes returns the byte form of BcryptPbkdf.Name with leading bytecount allocator. | ||||
| func (b *BcryptPbkdf) NameBytes() (name []byte) { | ||||
| 
 | ||||
| 	var nb []byte | ||||
| 	var s = b.Name() | ||||
| 
 | ||||
| 	nb = []byte(s) | ||||
| 
 | ||||
| 	name = make([]byte, 4) | ||||
| 	binary.BigEndian.PutUint32(name, uint32(len(nb))) | ||||
| 	name = append(name, nb...) | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // PackedBytes returns 3.0 and recursed. | ||||
| func (b *BcryptPbkdf) PackedBytes() (buf *bytes.Reader, err error) { | ||||
| 
 | ||||
| 	var rounds = make([]byte, 4) | ||||
| 	var packer *bytes.Reader | ||||
| 	var w = new(bytes.Buffer) | ||||
| 
 | ||||
| 	// 3.0.0.0 and 3.0.0.0.0 | ||||
| 	if packer, err = internal.ReadSizeBytes(b.salt, true); err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 	if _, err = packer.WriteTo(w); err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// 3.0.0.1 | ||||
| 	binary.BigEndian.PutUint32(rounds, b.rounds) | ||||
| 	if _, err = w.Write(rounds); err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// 3.0 | ||||
| 	if buf, err = internal.ReadSizeBytes(w, true); err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // Rounds returns the number of rounds used in derivation. | ||||
| func (b *BcryptPbkdf) Rounds() (rounds uint32) { | ||||
| 
 | ||||
| 	rounds = b.rounds | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // Salt returns the salt bytes. | ||||
| func (b *BcryptPbkdf) Salt() (salt []byte) { | ||||
| 
 | ||||
| 	salt = b.salt | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| 	AutoOK returns true if a GetKdfFromBytes call was able to fetch the KDF options successfully, in which case the caller may use KDF.SetupAuto. | ||||
| 
 | ||||
| 	If false, it will need to be manually configured via KDF.Setup. | ||||
| */ | ||||
| func (b *BcryptPbkdf) AutoOK() (ok bool) { | ||||
| 
 | ||||
| 	ok = true | ||||
| 
 | ||||
| 	if b.salt == nil || len(b.salt) == 0 { | ||||
| 		ok = false | ||||
| 	} | ||||
| 
 | ||||
| 	if b.rounds == 0 { | ||||
| 		ok = false | ||||
| 	} | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // IsPlain indicates if this KDF actually does derivation (false) or not (true). | ||||
| func (b *BcryptPbkdf) IsPlain() (plain bool) { | ||||
| 
 | ||||
| 	plain = false | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // addSalt adds a salt as parsed from GetKdfFromBytes. | ||||
| func (b *BcryptPbkdf) addSalt(salt []byte) (err error) { | ||||
| 
 | ||||
| 	if salt == nil || len(salt) == 0 { | ||||
| 		err = ErrNoSalt | ||||
| 		return | ||||
| 	} | ||||
| 	b.salt = salt | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // addRounds adds the rounds as parsed from GetKdfFromBytes | ||||
| func (b *BcryptPbkdf) addRounds(rounds uint32) (err error) { | ||||
| 
 | ||||
| 	if rounds == 0 { | ||||
| 		err = ErrNoRounds | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	b.rounds = rounds | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| @ -1,137 +0,0 @@ | ||||
| package kdf | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/binary" | ||||
| ) | ||||
| 
 | ||||
| /* | ||||
| 	Setup must be called before DeriveKey. It configures a Null. | ||||
| 
 | ||||
| 	Note that this doesn't actually do anything, it's here for interface compat. | ||||
| 	It is recommended to use nil/zero values. | ||||
| */ | ||||
| func (n *Null) Setup(secret, salt []byte, rounds, keyLen uint32) (err error) { | ||||
| 
 | ||||
| 	_, _, _, _ = secret, salt, rounds, keyLen | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| 	SetupAuto is used to provide out-of-band configuration if the KDF options were found via GetKdfFromBytes. | ||||
| 
 | ||||
| 	Note that this doesn't actually do anything, it's here for interface compat. | ||||
| 	It is recommended to use nil/zero values. | ||||
| */ | ||||
| func (n *Null) SetupAuto(secret []byte, keyLen uint32) (err error) { | ||||
| 
 | ||||
| 	_, _ = secret, keyLen | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| 	DeriveKey returns the derived key from a Null. | ||||
| 
 | ||||
| 	Note that this doesn't actually do anything, it's here for interface compat. | ||||
| 	key will ALWAYS be a nil byte slice. | ||||
| */ | ||||
| func (n *Null) DeriveKey() (key []byte, err error) { | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // Name returns NullName. | ||||
| func (n *Null) Name() (name string) { | ||||
| 
 | ||||
| 	name = NullName | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // NameBytes returns the byte form of Null.Name with leading bytecount allocator. | ||||
| func (n *Null) NameBytes() (name []byte) { | ||||
| 
 | ||||
| 	var b []byte | ||||
| 	var s = n.Name() | ||||
| 
 | ||||
| 	b = []byte(s) | ||||
| 
 | ||||
| 	name = make([]byte, 4) | ||||
| 	binary.BigEndian.PutUint32(name, uint32(len(b))) | ||||
| 	name = append(name, b...) | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // PackedBytes returns 3.0 and recursed. | ||||
| func (n *Null) PackedBytes() (buf *bytes.Reader, err error) { | ||||
| 
 | ||||
| 	// This is static. | ||||
| 	buf = bytes.NewReader([]byte{0x0, 0x0, 0x0, 0x0}) | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| 	Rounds returns the number of rounds used in derivation. | ||||
| 
 | ||||
| 	Note that this will always return 0; it's here for interface compat. | ||||
| */ | ||||
| func (n *Null) Rounds() (rounds uint32) { | ||||
| 
 | ||||
| 	rounds = 0 | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| 	Salt returns the salt bytes. | ||||
| 
 | ||||
| 	Note that this will always return nil; it's here for interface compat. | ||||
| */ | ||||
| func (n *Null) Salt() (salt []byte) { | ||||
| 
 | ||||
| 	salt = nil | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| 	AutoOK returns true if a GetKdfFromBytes call was able to fetch the KDF options successfully, in which case the caller may use KDF.SetupAuto. | ||||
| 
 | ||||
| 	If false, it will need to be manually configured via KDF.Setup. | ||||
| 
 | ||||
| 	Note that this won't actually do anything and ok will always return as true. | ||||
| */ | ||||
| func (n *Null) AutoOK() (ok bool) { | ||||
| 
 | ||||
| 	ok = true | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // IsPlain indicates if this KDF actually does derivation (false) or not (true). | ||||
| func (n *Null) IsPlain() (plain bool) { | ||||
| 
 | ||||
| 	plain = true | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // addSalt is a no-op, just here for interface compat. | ||||
| func (n *Null) addSalt(salt []byte) (err error) { | ||||
| 
 | ||||
| 	_ = salt | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // addRounds is a no-op; just here for interface compat. | ||||
| func (n *Null) addRounds(rounds uint32) (err error) { | ||||
| 
 | ||||
| 	_ = rounds | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										5
									
								
								kdf/null/consts.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								kdf/null/consts.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| package null | ||||
| 
 | ||||
| const ( | ||||
| 	Name string = "none" | ||||
| ) | ||||
							
								
								
									
										137
									
								
								kdf/null/funcs.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								kdf/null/funcs.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,137 @@ | ||||
| package null | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/binary" | ||||
| ) | ||||
| 
 | ||||
| /* | ||||
| 	Setup must be called before DeriveKey. It configures a null.KDF. | ||||
| 
 | ||||
| 	Note that this doesn't actually do anything, it's here for interface compat. | ||||
| 	It is recommended to use nil/zero values. | ||||
| */ | ||||
| func (k *KDF) Setup(secret, salt []byte, rounds, keyLen uint32) (err error) { | ||||
| 
 | ||||
| 	_, _, _, _ = secret, salt, rounds, keyLen | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| 	SetupAuto is used to provide out-of-band configuration if the null.KDF options were found via kdf.UnpackKDF. | ||||
| 
 | ||||
| 	Note that this doesn't actually do anything, it's here for interface compat. | ||||
| 	It is recommended to use nil/zero values. | ||||
| */ | ||||
| func (k *KDF) SetupAuto(secret []byte, keyLen uint32) (err error) { | ||||
| 
 | ||||
| 	_, _ = secret, keyLen | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| 	DeriveKey returns the derived key from a null.KDF. | ||||
| 
 | ||||
| 	Note that this doesn't actually do anything, it's here for interface compat. | ||||
| 	key will ALWAYS be a nil byte slice. | ||||
| */ | ||||
| func (k *KDF) DeriveKey() (key []byte, err error) { | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // Name returns null.Name. | ||||
| func (k *KDF) Name() (name string) { | ||||
| 
 | ||||
| 	name = Name | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // NameBytes returns the byte form of null.Name with leading bytecount allocator. | ||||
| func (k *KDF) NameBytes() (name []byte) { | ||||
| 
 | ||||
| 	var b []byte | ||||
| 	var s = k.Name() | ||||
| 
 | ||||
| 	b = []byte(s) | ||||
| 
 | ||||
| 	name = make([]byte, 4) | ||||
| 	binary.BigEndian.PutUint32(name, uint32(len(b))) | ||||
| 	name = append(name, b...) | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // PackedBytes returns block 3.0 and recursed. | ||||
| func (k *KDF) PackedBytes() (buf *bytes.Reader, err error) { | ||||
| 
 | ||||
| 	// This is static. | ||||
| 	buf = bytes.NewReader([]byte{0x0, 0x0, 0x0, 0x0}) | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| 	Rounds returns the number of rounds used in derivation. | ||||
| 
 | ||||
| 	Note that this will always return 0; it's here for interface compat. | ||||
| */ | ||||
| func (k *KDF) Rounds() (rounds uint32) { | ||||
| 
 | ||||
| 	rounds = 0 | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| 	Salt returns the salt bytes. | ||||
| 
 | ||||
| 	Note that this will always return nil; it's here for interface compat. | ||||
| */ | ||||
| func (k *KDF) Salt() (salt []byte) { | ||||
| 
 | ||||
| 	salt = nil | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| 	AutoOK returns true if a kdf.UnpackKDF call was able to fetch the null.KDF options successfully, in which case the caller may use null.KDF.SetupAuto. | ||||
| 
 | ||||
| 	If false, it will need to be manually configured via null.KDF.Setup. | ||||
| 
 | ||||
| 	Note that this won't actually do anything and ok will always return as true. | ||||
| */ | ||||
| func (k *KDF) AutoOK() (ok bool) { | ||||
| 
 | ||||
| 	ok = true | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // IsPlain indicates if this kdf.KDF actually does derivation (false) or not (true). | ||||
| func (k *KDF) IsPlain() (plain bool) { | ||||
| 
 | ||||
| 	plain = true | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // AddSalt is a no-op, just here for interface compat. | ||||
| func (k *KDF) AddSalt(salt []byte) (err error) { | ||||
| 
 | ||||
| 	_ = salt | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // AddRounds is a no-op; just here for interface compat. | ||||
| func (k *KDF) AddRounds(rounds uint32) (err error) { | ||||
| 
 | ||||
| 	_ = rounds | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										4
									
								
								kdf/null/types.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								kdf/null/types.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| package null | ||||
| 
 | ||||
| // KDF is a dummy kdf.KDF that is used for unencrypted/plain SSH private keys. It literally does nothing. | ||||
| type KDF struct{} | ||||
							
								
								
									
										31
									
								
								kdf/types.go
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								kdf/types.go
									
									
									
									
									
								
							| @ -26,31 +26,8 @@ type KDF interface { | ||||
| 	IsPlain() (plain bool) | ||||
| 	// PackedBytes returns the bytes suitable for serializing into a key file. | ||||
| 	PackedBytes() (buf *bytes.Reader, err error) | ||||
| 	// addSalt adds the salt as parsed from the private key. | ||||
| 	addSalt(salt []byte) (err error) | ||||
| 	// addRounds adds the rounds as parsed from the private key. | ||||
| 	addRounds(rounds uint32) (err error) | ||||
| 	// AddSalt adds the salt as parsed from the private key. | ||||
| 	AddSalt(salt []byte) (err error) | ||||
| 	// AddRounds adds the rounds as parsed from the private key. | ||||
| 	AddRounds(rounds uint32) (err error) | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| 	BcryptPbkdf combines bcrypt hashing algorithm with PBKDF2 key derivation. | ||||
| 
 | ||||
| 	(bcrypt) https://www.usenix.org/legacy/events/usenix99/provos/provos_html/node1.html | ||||
| 	(PBKDF2) https://datatracker.ietf.org/doc/html/rfc2898 | ||||
| 	http://www.tedunangst.com/flak/post/bcrypt-pbkdf | ||||
| */ | ||||
| type BcryptPbkdf struct { | ||||
| 	// salt is used to salt the hash for each round in rounds. | ||||
| 	salt []byte | ||||
| 	// rounds controls how many iterations of salting/hashing is done. | ||||
| 	rounds uint32 | ||||
| 	// keyLen is how long the derived key should be in bytes. | ||||
| 	keyLen uint32 | ||||
| 	// secret is the "passphrase" used to seed the key creation. | ||||
| 	secret []byte | ||||
| 	// key is used to store the derived key. | ||||
| 	key []byte | ||||
| } | ||||
| 
 | ||||
| // Null is a dummy KDF that is used for unencrypted/plain SSH private keys. It literally does nothing. | ||||
| type Null struct{} | ||||
|  | ||||
| @ -6,14 +6,14 @@ import ( | ||||
| 	`r00t2.io/sshkeys/kdf` | ||||
| ) | ||||
| 
 | ||||
| // KeyEd25519 is an ed25519 OpenSSH key. | ||||
| type KeyEd25519 struct { | ||||
| 	KeyPairs []*KeypairEd25519 `xml:"keypairs" json:"keypairs" yaml:"Keypairs"` | ||||
| 	Cipher   string            // TODO: (sshkeys/cipher).Cipher | ||||
| // Key is an ed25519 OpenSSH key. | ||||
| type Key struct { | ||||
| 	KeyPairs []*Keypair `xml:"keypairs" json:"keypairs" yaml:"Keypairs"` | ||||
| 	Cipher   string     // TODO: (sshkeys/cipher).Cipher | ||||
| 	KDF      kdf.KDF | ||||
| } | ||||
| 
 | ||||
| type KeypairEd25519 struct { | ||||
| type Keypair struct { | ||||
| 	Private ed25519.PrivateKey `xml:"private" json:"private_key" yaml:"Private Key"` | ||||
| 	Public  ed25519.PublicKey  `xml:"public" json:"public_key" yaml:"Public Key"` | ||||
| 	Comment string             `xml:"comment,attr" json:"comment" yaml:"comment"` | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user