brent s
0203f8b0d8
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.
257 lines
6.0 KiB
Go
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
|
|
}
|