From 5ff1efe3aa3bb041272d43ceb46b11146d9ef58f Mon Sep 17 00:00:00 2001 From: Effi Ofer Date: Tue, 27 Jul 2021 12:05:06 +0300 Subject: [PATCH] rbd: add EncryptionLoad api to support the rbd_encryption_load librbd api Signed-off-by: Effi Ofer --- rbd/encryption.go | 37 +++++++++++++++---- rbd/encryption_test.go | 82 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 7 deletions(-) diff --git a/rbd/encryption.go b/rbd/encryption.go index 4b1a6d0..dfcb8b8 100644 --- a/rbd/encryption.go +++ b/rbd/encryption.go @@ -16,8 +16,8 @@ import ( "unsafe" ) -// cData contains the data needed by the encryption functions -type cData struct { +// cEncryptionData contains the data needed by the encryption functions +type cEncryptionData struct { format C.rbd_encryption_format_t opts C.rbd_encryption_options_t optsSize C.size_t @@ -56,12 +56,12 @@ type EncryptionOptionsLUKS2 struct { // EncryptionOptions interface is used to encapsulate the different encryption // formats options and enable converting them from go to C structures. type EncryptionOptions interface { - allocateEncryptionOptions() cData + allocateEncryptionOptions() cEncryptionData } -func (opts EncryptionOptionsLUKS1) allocateEncryptionOptions() cData { +func (opts EncryptionOptionsLUKS1) allocateEncryptionOptions() cEncryptionData { var cOpts C.rbd_encryption_luks1_format_options_t - var retData cData + var retData cEncryptionData cOpts.alg = C.rbd_encryption_algorithm_t(opts.Alg) //CBytes allocates memory which we'll free by calling cOptsFree() cOpts.passphrase = (*C.char)(C.CBytes(opts.Passphrase)) @@ -73,9 +73,9 @@ func (opts EncryptionOptionsLUKS1) allocateEncryptionOptions() cData { return retData } -func (opts EncryptionOptionsLUKS2) allocateEncryptionOptions() cData { +func (opts EncryptionOptionsLUKS2) allocateEncryptionOptions() cEncryptionData { var cOpts C.rbd_encryption_luks2_format_options_t - var retData cData + var retData cEncryptionData cOpts.alg = C.rbd_encryption_algorithm_t(opts.Alg) //CBytes allocates memory which we'll free by calling cOptsFree() cOpts.passphrase = (*C.char)(C.CBytes(opts.Passphrase)) @@ -114,3 +114,26 @@ func (image *Image) EncryptionFormat(opts EncryptionOptions) error { return getError(ret) } + +// EncryptionLoad enables IO on an open encrypted image +// +// Implements: +// int rbd_encryption_load(rbd_image_t image, +// rbd_encryption_format_t format, +// rbd_encryption_options_t opts, +// size_t opts_size); +func (image *Image) EncryptionLoad(opts EncryptionOptions) error { + if image.image == nil { + return ErrImageNotOpen + } + + encryptionOpts := opts.allocateEncryptionOptions() + defer encryptionOpts.free() + + ret := C.rbd_encryption_load( + image.image, + encryptionOpts.format, + encryptionOpts.opts, + encryptionOpts.optsSize) + return getError(ret) +} diff --git a/rbd/encryption_test.go b/rbd/encryption_test.go index 8bad7ce..7a55c25 100644 --- a/rbd/encryption_test.go +++ b/rbd/encryption_test.go @@ -45,3 +45,85 @@ func TestEncryptionFormat(t *testing.T) { conn.DeletePool(poolname) conn.Shutdown() } + +func TestEncryptionLoad(t *testing.T) { + conn := radosConnect(t) + + poolname := GetUUID() + err := conn.MakePool(poolname) + assert.NoError(t, err) + + ioctx, err := conn.OpenIOContext(poolname) + require.NoError(t, err) + + name := GetUUID() + testImageSize := uint64(1 << 23) // format requires more than 4194304 bytes + options := NewRbdImageOptions() + assert.NoError(t, + options.SetUint64(ImageOptionOrder, uint64(testImageOrder))) + err = CreateImage(ioctx, name, testImageSize, options) + assert.NoError(t, err) + + img, err := OpenImage(ioctx, name, NoSnapshot) + assert.NoError(t, err) + + var opts EncryptionOptionsLUKS1 + opts.Alg = EncryptionAlgorithmAES256 + opts.Passphrase = ([]byte)("test-password") + err = img.EncryptionFormat(opts) + assert.NoError(t, err) + + // close the image so we can reopen it and load the encryption info + // then write some encrypted data at the end of the image + err = img.Close() + assert.NoError(t, err) + img, err = OpenImage(ioctx, name, NoSnapshot) + err = img.EncryptionLoad(opts) + assert.NoError(t, err) + + outData := []byte("Hi rbd! Nice to talk through go-ceph :)") + + stats, err := img.Stat() + require.NoError(t, err) + offset := int64(stats.Size) - int64(len(outData)) + + nOut, err := img.WriteAt(outData, offset) + assert.Equal(t, len(outData), nOut) + assert.NoError(t, err) + + err = img.Close() + assert.NoError(t, err) + + // Re-open the image, load the encryption format, and read the encrypted data + img, err = OpenImage(ioctx, name, NoSnapshot) + assert.NoError(t, err) + err = img.EncryptionLoad(opts) + assert.NoError(t, err) + + inData := make([]byte, len(outData)) + nIn, err := img.ReadAt(inData, offset) + assert.Equal(t, nIn, len(inData)) + assert.Equal(t, inData, outData) + assert.NoError(t, err) + + err = img.Close() + assert.NoError(t, err) + + // Re-open the image and attempt to read the encrypted data without loading the encryption + img, err = OpenImage(ioctx, name, NoSnapshot) + assert.NoError(t, err) + + nIn, err = img.ReadAt(inData, offset) + assert.Equal(t, nIn, len(inData)) + assert.NotEqual(t, inData, outData) + assert.NoError(t, err) + + err = img.Close() + assert.NoError(t, err) + err = img.Remove() + assert.NoError(t, err) + + ioctx.Destroy() + conn.DeletePool(poolname) + conn.Shutdown() +}