go_sshkeys/internal/ciphers/aesCommon/funcs_aescipher.go
brent s 0203f8b0d8
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.
2022-04-29 02:49:33 -04:00

257 lines
6.0 KiB
Go

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
}