aes128 completely done.

ish. done-ish. it's entirely untested. CTR should work as i modeled it after PoC, and CBC *probably* works as it's straightforward, but I have no idea about the GCM. TODO.
This commit is contained in:
brent s. 2022-04-29 02:49:33 -04:00
parent 9027750325
commit 0203f8b0d8
Signed by: bts
GPG Key ID: 8C004C2F93481F6B
19 changed files with 479 additions and 280 deletions

3
TODO
View File

@ -5,6 +5,9 @@
- 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

-- finish trimming copypasta for aes.
we COULD have a unified AllocateEncrypt and AllocatedDecrypt for AesCipher, but that'd require a func argument for encryption/decryption - which means

provide marshal, unmarshal for keytypes/* keys.
https://golangexample.com/encode-and-decode-binary-message-and-file-formats-in-go/ (?)


View File

@ -734,7 +734,7 @@ pre.rouge {
<h1>OpenSSH Key Structure Guide</h1>
<div class="details">
<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-28 05:40:27 -0400</span>
<span id="revdate">Last updated 2022-04-29 02:49:33 -0400</span>
</div>
<div id="toc" class="toc2">
<div id="toctitle">Table of Contents</div>

View File

@ -1 +0,0 @@
TODO

View File

@ -1,32 +1,26 @@
package cbc

import (
`bytes`
gAes `crypto/aes`
gCipher `crypto/cipher`
`io`
"bytes"
gCipher "crypto/cipher"

`r00t2.io/sshkeys/cipher`
`r00t2.io/sshkeys/cipher/aes`
`r00t2.io/sshkeys/cipher/aes/aes128`
`r00t2.io/sshkeys/internal`
"r00t2.io/sshkeys/internal"
"r00t2.io/sshkeys/internal/ciphers/aesCommon"
)

// Setup populates a Cipher from a key. The key must include the IV suffixed to the actual key.
func (c *Cipher) Setup(key []byte) (err error) {

if key == nil || len(key) < aes128.KdfKeySize {
err = cipher.ErrBadKeyLen
if c == nil {
*c = Cipher{
&aesCommon.AesCipher{},
}
}

if err = c.CipherSetup(key, aesCommon.Aes128Bits); err != nil {
return
}

if c == nil {
c = &Cipher{}
}

c.key = key[0:aes128.KeySize]
c.iv = key[aes128.KeySize:(aes128.KdfKeySize)]

return
}

@ -50,22 +44,6 @@ func (c *Cipher) NameBytes() (name []byte) {
return
}

// BlockSize returns the blocksize of this Cipher.
func (c *Cipher) BlockSize() (size int) {

size = aes.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
}

/*
Encrypt encrypts data (a string, []byte, byte, *bytes.Buffer, or *bytes.Reader) to the *bytes.Reader encrypted.

@ -78,33 +56,18 @@ func (c *Cipher) KdfKeySize() (size int) {
*/
func (c *Cipher) Encrypt(data interface{}) (encrypted *bytes.Reader, err error) {

var b []byte
var padded []byte
var cryptDst []byte
var padded *bytes.Reader
var cryptBlock gCipher.Block
var crypter gCipher.BlockMode

if b, err = internal.SerializeData(data); err != nil {
if cryptDst, padded, cryptBlock, err = c.GetCryptBlock(data); err != nil {
return
}

if padded, err = c.Pad(b); err != nil {
return
}
crypter = gCipher.NewCBCEncrypter(cryptBlock, c.IV)

b = make([]byte, padded.Len())
if b, err = io.ReadAll(padded); err != nil {
return
}

cryptDst = make([]byte, len(b))

if cryptBlock, err = gAes.NewCipher(c.key); err != nil {
return
}
crypter = gCipher.NewCBCEncrypter(cryptBlock, c.iv)

crypter.CryptBlocks(cryptDst, b)
crypter.CryptBlocks(cryptDst, padded)

encrypted = bytes.NewReader(cryptDst)

@ -146,40 +109,6 @@ func (c *Cipher) AllocateEncrypt(data interface{}) (encrypted *bytes.Reader, err
return
}

/*
Pad will pad data (a string, []byte, byte, or *bytes.Buffer) to the Cipher.BlockSize (if necessary).
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()).
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.

NOTE: If data is a *bytes.Reader, ALL bytes WILL be consumed.
*/
func (c *Cipher) Pad(data interface{}) (paddedBuf *bytes.Reader, err error) {

var b []byte
var padNum int
var pad []byte
var buf *bytes.Buffer

if b, err = internal.UnpackBytes(data); err != nil {
return
}
buf = bytes.NewBuffer(b)

for padIdx := 1; (buf.Len() % aes.BlockSize) != 0; padIdx++ {

padNum = padIdx & cipher.PadMod
pad = []byte{byte(uint32(padNum))}

if _, err = buf.Write(pad); err != nil {
return
}
}

return
}

/*
Decrypt takes a raw byte slice, a *bytes.Buffer, or a *bytes.Reader and returns a plain/decrypted *bytes.Reader.

@ -192,25 +121,20 @@ func (c *Cipher) Pad(data interface{}) (paddedBuf *bytes.Reader, err error) {
*/
func (c *Cipher) Decrypt(data interface{}) (decrypted *bytes.Reader, err error) {

var b []byte
var decryptDst []byte
var plain []byte
var padded []byte
var cryptBlock gCipher.Block
var decrypter gCipher.BlockMode

if b, err = internal.SerializeData(data); err != nil {
if plain, padded, cryptBlock, err = c.GetDecryptBlock(data); err != nil {
return
}

decryptDst = make([]byte, len(b))
decrypter = gCipher.NewCBCDecrypter(cryptBlock, c.IV)

if cryptBlock, err = gAes.NewCipher(c.key); err != nil {
return
}
decrypter = gCipher.NewCBCDecrypter(cryptBlock, c.iv)
decrypter.CryptBlocks(plain, padded)

decrypter.CryptBlocks(decryptDst, b)

decrypted = bytes.NewReader(decryptDst)
decrypted = bytes.NewReader(plain)

return
}
@ -239,15 +163,3 @@ func (c *Cipher) AllocatedDecrypt(data interface{}) (decrypted *bytes.Reader, er

return
}

/*
IsPlain indicates if this Cipher is a plain/null encryption (cipher.null.Null).

It will always return false. It is included for interface compatability.
*/
func (c *Cipher) IsPlain() (plain bool) {

plain = false

return
}

View File

@ -1,18 +1,10 @@
package cbc

import (
`crypto/cipher`
"r00t2.io/sshkeys/internal/ciphers/aesCommon"
)

// Cipher is an AES128-CBC cipher.Cipher.
type Cipher struct {
// key contains the encryption key.
key []byte
// iv contains the IV, or initialization vector.
iv []byte
/*
cryptBlock contains the block encryptor.
CBC is a block (all at once) encryption mode.
*/
cryptBlock cipher.Block
*aesCommon.AesCipher
}

View File

@ -1 +0,0 @@
TODO

View File

@ -1,17 +1,25 @@
package ctr

import (
`bytes`
`io`
"bytes"
gCipher "crypto/cipher"

`r00t2.io/sshkeys/cipher/aes`
`r00t2.io/sshkeys/cipher/aes/aes128`
`r00t2.io/sshkeys/internal`
"r00t2.io/sshkeys/internal"
"r00t2.io/sshkeys/internal/ciphers/aesCommon"
)

// Setup populates a Cipher from a key. The key must include the IV suffixed to the actual key.
func (c *Cipher) Setup(key []byte) (err error) {

// TODO
if c == nil {
*c = Cipher{
&aesCommon.AesCipher{},
}
}

if err = c.CipherSetup(key, aesCommon.Aes128Bits); err != nil {
return
}

return
}
@ -36,22 +44,6 @@ func (c *Cipher) NameBytes() (name []byte) {
return
}

// BlockSize returns the blocksize of this Cipher.
func (c *Cipher) BlockSize() (size int) {

size = aes.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
}

/*
Encrypt encrypts data (a string, []byte, byte, *bytes.Buffer, or *bytes.Reader) to the *bytes.Reader encrypted.

@ -64,27 +56,20 @@ func (c *Cipher) KdfKeySize() (size int) {
*/
func (c *Cipher) Encrypt(data interface{}) (encrypted *bytes.Reader, err error) {

var b []byte
var padded []byte
var cryptDst []byte
var padded *bytes.Reader
var cryptBlock gCipher.Block
var crypter gCipher.Stream

if b, err = internal.SerializeData(data); err != nil {
if cryptDst, padded, cryptBlock, err = c.GetCryptBlock(data); err != nil {
return
}

if padded, err = c.Pad(b); err != nil {
return
}
crypter = gCipher.NewCTR(cryptBlock, c.IV)

b = make([]byte, padded.Len())
if b, err = io.ReadAll(padded); err != nil {
return
}
crypter.XORKeyStream(cryptDst, padded)

cryptDst = make([]byte, len(b))

// TODO
_ = cryptDst
encrypted = bytes.NewReader(cryptDst)

return
}
@ -124,22 +109,6 @@ func (c *Cipher) AllocateEncrypt(data interface{}) (encrypted *bytes.Reader, err
return
}

/*
Pad will pad data (a string, []byte, byte, or *bytes.Buffer) to the Cipher.BlockSize (if necessary).
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()).
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.

NOTE: If data is a *bytes.Reader, ALL bytes WILL be consumed.
*/
func (c *Cipher) Pad(data interface{}) (paddedBuf *bytes.Reader, err error) {

// TODO

return
}

/*
Decrypt takes a raw byte slice, a *bytes.Buffer, or a *bytes.Reader and returns a plain/decrypted *bytes.Reader.

@ -152,17 +121,20 @@ func (c *Cipher) Pad(data interface{}) (paddedBuf *bytes.Reader, err error) {
*/
func (c *Cipher) Decrypt(data interface{}) (decrypted *bytes.Reader, err error) {

var b []byte
var decryptDst []byte
var plain []byte
var padded []byte
var cryptBlock gCipher.Block
var decrypter gCipher.Stream

if b, err = internal.SerializeData(data); err != nil {
if plain, padded, cryptBlock, err = c.GetDecryptBlock(data); err != nil {
return
}

decryptDst = make([]byte, len(b))
decrypter = gCipher.NewCTR(cryptBlock, c.IV)

// TODO
_ = decryptDst
decrypter.XORKeyStream(plain, padded)

decrypted = bytes.NewReader(plain)

return
}
@ -191,15 +163,3 @@ func (c *Cipher) AllocatedDecrypt(data interface{}) (decrypted *bytes.Reader, er

return
}

/*
IsPlain indicates if this Cipher is a plain/null encryption (cipher.null.Null).

It will always return false. It is included for interface compatability.
*/
func (c *Cipher) IsPlain() (plain bool) {

plain = false

return
}

View File

@ -0,0 +1,10 @@
package ctr

import (
"r00t2.io/sshkeys/internal/ciphers/aesCommon"
)

// Cipher is an AES128-CTR cipher.Cipher.
type Cipher struct {
*aesCommon.AesCipher
}

View File

@ -1,17 +1,25 @@
package gcm

import (
`bytes`
`io`
"bytes"
gCipher "crypto/cipher"

`r00t2.io/sshkeys/cipher/aes`
`r00t2.io/sshkeys/cipher/aes/aes128`
`r00t2.io/sshkeys/internal`
"r00t2.io/sshkeys/internal"
"r00t2.io/sshkeys/internal/ciphers/aesCommon"
)

// Setup populates a Cipher from a key. The key must include the IV suffixed to the actual key.
func (c *Cipher) Setup(key []byte) (err error) {

// TODO
if c == nil {
*c = Cipher{
&aesCommon.AesCipher{},
}
}

if err = c.CipherSetup(key, aesCommon.Aes128Bits); err != nil {
return
}

return
}
@ -36,22 +44,6 @@ func (c *Cipher) NameBytes() (name []byte) {
return
}

// BlockSize returns the blocksize of this Cipher.
func (c *Cipher) BlockSize() (size int) {

size = aes.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
}

/*
Encrypt encrypts data (a string, []byte, byte, *bytes.Buffer, or *bytes.Reader) to the *bytes.Reader encrypted.

@ -64,27 +56,22 @@ func (c *Cipher) KdfKeySize() (size int) {
*/
func (c *Cipher) Encrypt(data interface{}) (encrypted *bytes.Reader, err error) {

var b []byte
var padded []byte
var cryptDst []byte
var padded *bytes.Reader
var cryptBlock gCipher.Block
var crypter gCipher.AEAD

if b, err = internal.SerializeData(data); err != nil {
if cryptDst, padded, cryptBlock, err = c.GetCryptBlock(data); err != nil {
return
}

if padded, err = c.Pad(b); err != nil {
if crypter, err = gCipher.NewGCM(cryptBlock); err != nil {
return
}

b = make([]byte, padded.Len())
if b, err = io.ReadAll(padded); err != nil {
return
}
cryptDst = crypter.Seal(cryptDst, c.IV, padded, nil)

cryptDst = make([]byte, len(b))

// TODO
_ = cryptDst
encrypted = bytes.NewReader(cryptDst)

return
}
@ -124,22 +111,6 @@ func (c *Cipher) AllocateEncrypt(data interface{}) (encrypted *bytes.Reader, err
return
}

/*
Pad will pad data (a string, []byte, byte, or *bytes.Buffer) to the Cipher.BlockSize (if necessary).
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()).
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.

NOTE: If data is a *bytes.Reader, ALL bytes WILL be consumed.
*/
func (c *Cipher) Pad(data interface{}) (paddedBuf *bytes.Reader, err error) {

// TODO

return
}

/*
Decrypt takes a raw byte slice, a *bytes.Buffer, or a *bytes.Reader and returns a plain/decrypted *bytes.Reader.

@ -152,17 +123,24 @@ func (c *Cipher) Pad(data interface{}) (paddedBuf *bytes.Reader, err error) {
*/
func (c *Cipher) Decrypt(data interface{}) (decrypted *bytes.Reader, err error) {

var b []byte
var decryptDst []byte
var plain []byte
var padded []byte
var cryptBlock gCipher.Block
var decrypter gCipher.AEAD

if b, err = internal.SerializeData(data); err != nil {
if plain, padded, cryptBlock, err = c.GetDecryptBlock(data); err != nil {
return
}

decryptDst = make([]byte, len(b))
if decrypter, err = gCipher.NewGCM(cryptBlock); err != nil {
return
}

// TODO
_ = decryptDst
if plain, err = decrypter.Open(plain, c.IV, padded, nil); err != nil {
return
}

decrypted = bytes.NewReader(plain)

return
}
@ -191,15 +169,3 @@ func (c *Cipher) AllocatedDecrypt(data interface{}) (decrypted *bytes.Reader, er

return
}

/*
IsPlain indicates if this Cipher is a plain/null encryption (cipher.null.Null).

It will always return false. It is included for interface compatability.
*/
func (c *Cipher) IsPlain() (plain bool) {

plain = false

return
}

View File

@ -0,0 +1,10 @@
package gcm

import (
"r00t2.io/sshkeys/internal/ciphers/aesCommon"
)

// Cipher is an AES128-GCM cipher.Cipher.
type Cipher struct {
*aesCommon.AesCipher
}

View File

@ -1,9 +0,0 @@
package cipher

import (
`errors`
)

var (
ErrBadKeyLen error = errors.New("the specified key does not match the Cipher.BlockSize size")
)

13
errs/errors.go Normal file
View File

@ -0,0 +1,13 @@
package errs

import (
"errors"
)

var (
ErrBadData error = errors.New("unable to serialize/cast data into buffer; unknown or invalid data object")
ErrBadIVLen = errors.New("the cipher IV/nonce does not match the expected key size/is of an invalid length")
ErrBadKeyLen = errors.New("the cipher key does not match the expected key size/is of an invalid length")
ErrMissingIV = errors.New("the cipher IV/nonce is empty or missing; Cipher was not setup properly")
ErrMissingKey = errors.New("the cipher key is empty or missing; Cipher was not setup properly")
)

View File

@ -1,9 +0,0 @@
package errs

import (
"errors"
)

var (
ErrBadData = errors.New("unable to serialize/cast data into buffer; unknown or invalid data object")
)

14
go.mod
View File

@ -2,6 +2,16 @@ module r00t2.io/sshkeys

go 1.17

require github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a
require (
github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a
github.com/go-playground/validator/v10 v10.10.1
)

require golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect
require (
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect
golang.org/x/text v0.3.7 // indirect
)

41
go.sum
View File

@ -1,11 +1,52 @@
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a h1:saTgr5tMLFnmy/yg3qDTft4rE5DY2uJ/cCxCe3q0XTU=
github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a/go.mod h1:Bw9BbhOJVNR+t0jCqx2GC6zv0TGBsShs56Y3gfSCvl0=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/validator/v10 v10.10.1 h1:uA0+amWMiglNZKZ9FJRKUAe9U3RX91eVn1JYXMWt7ig=
github.com/go-playground/validator/v10 v10.10.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 h1:siQdpVirKtzPhKl3lZWozZraCFObP8S1v6PRp0bLrtU=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -0,0 +1,7 @@
package aesCommon

const (
Aes128Bits aesBitSize = 128
Aes196Bits aesBitSize = 196
Aes256Bits aesBitSize = 256
)

View File

@ -0,0 +1,9 @@
package aesCommon

// GetByteLen returns the length of *bytes* of an aesBitSize.
func (a *aesBitSize) GetByteLen() (l int) {

l = int(*a) / 8

return
}

View File

@ -0,0 +1,256 @@
package aesCommon

import (
"bytes"
"crypto/aes"
gCipher "crypto/cipher"
"io"

"r00t2.io/sshkeys/cipher"
"r00t2.io/sshkeys/errs"
"r00t2.io/sshkeys/internal"
)

// CipherSetup populates an AesCipher from a key and aesBitSize (see Aes<size>Bits consts). The key must include the IV suffixed to the actual key.
func (a *AesCipher) CipherSetup(key []byte, bits aesBitSize) (err error) {

var kdfKeySize int = bits.GetByteLen() + aes.BlockSize

/*
The assumption here, with ALL these conditions being met, is that the AesCipher is already set up.
Rather than throw an error, we just return.
The specific conditions are, in order:

- a (*AesCipher) is not nil
- a.Key is not nil
- a.Key length is not 0
- a.IV is not nil
- a.IV length is not 0
- a.Bits is not 0 (uninitialized); 0 is an invalid bit size.
- a.KeyLen is not 0 (uninitialized)
*/
if a != nil &&
a.Key != nil &&
len(a.Key) != 0 &&
a.IV != nil &&
len(a.IV) != 0 &&
a.Bits != 0 &&
a.KeyLen != 0 {
return
}

// Check the actual parameters passed to confirm a valid key was passed.
if key == nil || len(key) < kdfKeySize {
err = errs.ErrBadKeyLen
return
}

if a == nil {
*a = AesCipher{}
}

a.Bits = bits
a.KeyLen = a.Bits.GetByteLen()
a.Key = key[0:a.KeyLen]
a.IV = key[a.KeyLen:kdfKeySize]

// And one confirmation check after the above slicing and dicing.
if err = a.keyChk(); err != nil {
return
}

return
}

// BlockSize returns the blocksize of this AesCipher.
func (a *AesCipher) BlockSize() (size int) {

size = aes.BlockSize

return
}

// KdfKeySize returns the target key length from KDF to use with this Cipher.
func (a *AesCipher) KdfKeySize() (size int) {

size = a.Bits.GetByteLen() + aes.BlockSize

return
}

/*
GetCryptBlock returns:
- a pre-sized cryptDst byte slice
- a padded byte slice (of plaintext), and
- a cipher.Block to be used with encrypting

(The actual encryption process using these components varies depending on algorithm.)

data parameter can be one of:

- string
- []byte
- byte
- bytes.Buffer
- *bytes.Buffer
- bytes.Reader
- *bytes.Reader

NOTE: Padding IS applied automatically.

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.Encrypt.

NOTE: If data is a *bytes.Reader, ALL bytes WILL be consumed.
*/
func (a *AesCipher) GetCryptBlock(data interface{}) (cryptDst, paddedPlain []byte, cryptBlock gCipher.Block, err error) {

var b []byte
var padded *bytes.Reader

if err = a.keyChk(); err != nil {
return
}

if b, err = internal.SerializeData(data); err != nil {
return
}

if padded, err = a.Pad(b); err != nil {
return
}

b = make([]byte, padded.Len())
if paddedPlain, err = io.ReadAll(padded); err != nil {
return
}

cryptDst = make([]byte, len(b))

if cryptBlock, err = aes.NewCipher(a.Key); err != nil {
return
}

return
}

/*
GetDecryptBlock returns:
- a pre-sized decryptDst byte slice
- a padded byte slice of the original data (after typeswitching), and
- a cipher.Block to be used with decrypting

(The actual decryption process using these components varies depending on algorithm.)

data parameter can be one of:

- string
- []byte
- byte
- bytes.Buffer
- *bytes.Buffer
- bytes.Reader
- *bytes.Reader

NOTE: The result is still padded; it is expected that the caller is to unpad/strip the padding..

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.Encrypt.

NOTE: If data is a *bytes.Reader, ALL bytes WILL be consumed.
*/
func (a *AesCipher) GetDecryptBlock(data interface{}) (decryptDst, paddedPlain []byte, cryptBlock gCipher.Block, err error) {

if err = a.keyChk(); err != nil {
return
}

if paddedPlain, err = internal.SerializeData(data); err != nil {
return
}

decryptDst = make([]byte, len(paddedPlain))

if cryptBlock, err = aes.NewCipher(a.Key); err != nil {
return
}

return
}

/*
Pad will pad data (a string, []byte, byte, or *bytes.Buffer) to the Cipher.BlockSize (if necessary).
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()).
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.

NOTE: If data is a *bytes.Reader, ALL bytes WILL be consumed.
*/
func (a *AesCipher) Pad(data interface{}) (paddedBuf *bytes.Reader, err error) {

var b []byte
var padNum int
var pad []byte
var buf *bytes.Buffer

if err = a.keyChk(); err != nil {
return
}

if b, err = internal.UnpackBytes(data); err != nil {
return
}
buf = bytes.NewBuffer(b)

for padIdx := 1; (buf.Len() % aes.BlockSize) != 0; padIdx++ {

padNum = padIdx & cipher.PadMod
pad = []byte{byte(uint32(padNum))}

if _, err = buf.Write(pad); err != nil {
return
}
}

return
}

/*
IsPlain indicates if this Cipher is a plain/null encryption (cipher.null.Null).

It will always return false. It is included for interface compatability.
*/
func (a *AesCipher) IsPlain() (plain bool) {

plain = false

return
}

// keyChk checks to make sure the AesCipher.Key and AesCipher.IV exist and are sane before performing cryptographic functions.
func (a *AesCipher) keyChk() (err error) {

if a.Key == nil || len(a.Key) == 0 {
err = errs.ErrMissingKey
}
if len(a.Key) != a.KeyLen {
err = errs.ErrBadKeyLen
return
}
if a.IV == nil || len(a.IV) == 0 {
err = errs.ErrMissingIV
return
}
if len(a.IV) != aes.BlockSize {
err = errs.ErrBadIVLen
return
}

// And just to be *extremely* cautious, we also confirm that the bits (bytes) are correct.
if err = validate.Struct(a); err != nil {
return
}

return
}

View File

@ -0,0 +1,30 @@
package aesCommon

import (
"github.com/go-playground/validator/v10"
)

// AesCipher contains a general AES structure/methods common for all AES types.
type AesCipher struct {
// Key contains the encryption Key.
Key []byte `validate:"skip"`
/*
IV contains the IV, or initialization vector.
It may also/instead refer to the nonce (number-used-once), depending on the algorithm.
*/
IV []byte `validate:"skip"`
/*
KeyLen is the length of key used (in bytes) (int(AesCipher.Bits) / 8).

Must be one of 16 (128-bit), 24 (195-bit), or 32 (256-bit).
*/
KeyLen int `validate:"oneof=16 24 32"`
// Bits is the full bit length of AesCipher.KeyLen (in bits) (AesCipher.KeyLen * 8)
Bits aesBitSize `validate:"skip"`
}

// aesBitSize is not exportable to ensure fixed size selections.
type aesBitSize int

// validate is used to validate the AesCipher.KeyLen.
var validate *validator.Validate = validator.New()