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:
7
internal/ciphers/aesCommon/consts.go
Normal file
7
internal/ciphers/aesCommon/consts.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package aesCommon
|
||||
|
||||
const (
|
||||
Aes128Bits aesBitSize = 128
|
||||
Aes196Bits aesBitSize = 196
|
||||
Aes256Bits aesBitSize = 256
|
||||
)
|
||||
9
internal/ciphers/aesCommon/funcs_aesbitsize.go
Normal file
9
internal/ciphers/aesCommon/funcs_aesbitsize.go
Normal 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
|
||||
}
|
||||
256
internal/ciphers/aesCommon/funcs_aescipher.go
Normal file
256
internal/ciphers/aesCommon/funcs_aescipher.go
Normal 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
|
||||
}
|
||||
30
internal/ciphers/aesCommon/types.go
Normal file
30
internal/ciphers/aesCommon/types.go
Normal 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()
|
||||
Reference in New Issue
Block a user