Signed-off-by: Vladimir Serbinenko <phco...@gmail.com>
---
 grub-core/fs/zfs/zfs.c      | 502 ++++++++++++++++++++++++++++++++----
 grub-core/fs/zfs/zfscrypt.c | 335 +++++++++++++++++++++---
 include/grub/zfs/zfs.h      |  54 +++-
 3 files changed, 793 insertions(+), 98 deletions(-)

diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c
index b88a2b032..da1661680 100644
--- a/grub-core/fs/zfs/zfs.c
+++ b/grub-core/fs/zfs/zfs.c
@@ -227,6 +227,10 @@ struct subvolume
     grub_uint64_t txg;
     grub_uint64_t algo;
   } *keyring;
+
+  struct grub_zfs_datto_key key_datto;
+
+  int is_datto_encrypted;
 };
 
 struct grub_zfs_data
@@ -264,16 +268,19 @@ struct grub_zfs_dir_ctx
   struct grub_zfs_data *data;
 };
 
-grub_err_t (*grub_zfs_decrypt) (grub_crypto_cipher_handle_t cipher,
-                               grub_uint64_t algo,
-                               const void *nonce,
-                               char *buf, grub_size_t size,
-                               const grub_uint32_t *expected_mac,
-                               grub_zfs_endian_t endian) = NULL;
-grub_crypto_cipher_handle_t (*grub_zfs_load_key) (const struct grub_zfs_key 
*key,
-                                                 grub_size_t keysize,
-                                                 grub_uint64_t salt,
-                                                 grub_uint64_t algo) = NULL;
+struct grub_zfs_decryptor *grub_zfs_decrypt = NULL;
+
+struct grub_zfs_crypt_datto_data
+{
+  grub_uint8_t *iv;
+  grub_size_t ivlen;
+  grub_uint8_t *mac;
+  grub_size_t maclen;
+  grub_uint8_t *master;
+  grub_size_t masterlen;
+  grub_uint8_t *hmac;
+  grub_size_t hmaclen;
+};
 /*
  * List of pool features that the grub implementation of ZFS supports for
  * read. Note that features that are only required for write do not need
@@ -289,6 +296,7 @@ static const char *spa_feature_names[] = {
   "com.klarasystems:vdev_zaps_v2",
   "com.delphix:head_errlog",
   "org.freebsd:zstd_compress",
+  "com.datto:encryption",
   NULL
 };
 
@@ -400,6 +408,47 @@ static decomp_entry_t decomp_table[ZIO_COMPRESS_FUNCTIONS] 
= {
 static grub_err_t zio_read_data (const blkptr_t * bp,
                                 void *buf, struct grub_zfs_data *data);
 
+static int
+fill_crypt_datto_data (const void *name,
+                   grub_size_t namelen __attribute__ ((unused)),
+                   const void *val_in,
+                   grub_size_t nelem,
+                   grub_size_t elemsize,
+                   void *data_in)
+{
+  struct grub_zfs_crypt_datto_data *data = data_in;
+  if (grub_strcmp(name, "DSL_CRYPTO_IV") == 0 && elemsize == 1)
+    {
+      data->ivlen = nelem;
+      data->iv = grub_malloc(nelem);
+      if (data->iv)
+       grub_memcpy(data->iv, val_in, nelem);
+    }
+  else if (grub_strcmp(name, "DSL_CRYPTO_MAC") == 0 && elemsize == 1)
+    {
+      data->maclen = nelem;
+      data->mac = grub_malloc(nelem);
+      if (data->mac)
+       grub_memcpy(data->mac, val_in, nelem);
+    }
+  else if (grub_strcmp(name, "DSL_CRYPTO_MASTER_KEY_1") == 0 && elemsize == 1)
+    {
+      data->masterlen = nelem;
+      data->master = grub_malloc(nelem);
+      if (data->master)
+       grub_memcpy(data->master, val_in, nelem);
+    }
+  else if (grub_strcmp(name, "DSL_CRYPTO_HMAC_KEY_1") == 0 && elemsize == 1)
+    {
+      data->hmaclen = nelem;
+      data->hmac = grub_malloc(nelem);
+      if (data->hmac)
+       grub_memcpy(data->hmac, val_in, nelem);
+    }
+
+  return 0;
+}
+
 /*
  * Our own version of log2().  Same thing as highbit()-1.
  */
@@ -690,7 +739,7 @@ grub_zfs_byteswap_type(void *buf, grub_size_t len, 
grub_uint8_t type)
 static grub_err_t
 zio_checksum_verify (zio_cksum_t zc, grub_uint32_t checksum,
                     grub_zfs_endian_t endian,
-                    char *buf, grub_size_t size)
+                    char *buf, grub_size_t size, int datto_crypt)
 {
   zio_eck_t *zec = (zio_eck_t *) (buf + size) - 1;
   zio_checksum_info_t *ci = &zio_checksum_table[checksum];
@@ -715,6 +764,12 @@ zio_checksum_verify (zio_cksum_t zc, grub_uint32_t 
checksum,
     ci->ci_func (buf, size, endian, &actual_cksum);
 
   int cksumlen = checksum != ZIO_CHECKSUM_SHA256_MAC ? 32 : 20;
+  if (datto_crypt)
+    {
+      actual_cksum.zc_word[0] ^= actual_cksum.zc_word[2];
+      actual_cksum.zc_word[1] ^= actual_cksum.zc_word[3];
+      cksumlen = 16;
+    }
 
   if (ci->ci_eck && !GRUB_ZFS_IS_NATIVE_BYTEORDER(endian))
     grub_zfs_byteswap_checksum(&actual_cksum);
@@ -796,7 +851,7 @@ uberblock_verify (uberblock_phys_t * ub, grub_uint64_t 
offset,
 
   zc.zc_word[0] = grub_cpu_to_zfs64 (offset, endian);
   err = zio_checksum_verify (zc, ZIO_CHECKSUM_LABEL, endian,
-                            (char *) ub, s);
+                            (char *) ub, s, 0);
 
   return err;
 }
@@ -1269,7 +1324,7 @@ check_pool_label (struct grub_zfs_data *data,
   /* Now check the integrity of the vdev_phys_t structure though checksum.  */
   ZIO_SET_CHECKSUM(&emptycksum, grub_cpu_to_zfs64(diskdesc->vdev_phys_sector 
<< 9, endian), 0, 0, 0);
   err = zio_checksum_verify (emptycksum, ZIO_CHECKSUM_LABEL, endian,
-                            nvlist, VDEV_PHYS_SIZE);
+                            nvlist, VDEV_PHYS_SIZE, 0);
   if (err) {
     grub_free (nvlist);
     return err;
@@ -2019,7 +2074,7 @@ zio_read_gang (const blkptr_t * bp, const dva_t * dva, 
void *buf,
   ZIO_SET_CHECKSUM (&zc, DVA_GET_VDEV (dva),
                    DVA_GET_OFFSET (dva), bp->blk_birth, 0);
   err = zio_checksum_verify (zc, ZIO_CHECKSUM_GANG_HEADER, 
BP_GET_BYTEORDER(bp),
-                            (char *) zio_gb, SPA_GANGBLOCKSIZE);
+                            (char *) zio_gb, SPA_GANGBLOCKSIZE, 0);
   if (err)
     {
       grub_free (zio_gb);
@@ -2113,6 +2168,57 @@ decode_embedded_bp_compressed(const blkptr_t *bp, void 
*buf)
   return GRUB_ERR_NONE;
 }
 
+static int
+datto_is_encrypted_type(grub_uint8_t dn_type)
+{
+  /* New structred types.  */
+  if (dn_type & 0x80)
+    return !!(dn_type & 0x20);
+  switch(dn_type) {
+  case DMU_OT_NONE ... DMU_OT_SPACE_MAP:
+  case DMU_OT_OBJSET ... DMU_OT_ZNODE:
+  case DMU_OT_MASTER_NODE:
+  case DMU_OT_ZVOL_PROP:
+  case DMU_OT_ZAP_OTHER ... DMU_OT_DSL_PERMS:
+  case DMU_OT_FUID_SIZE ... DMU_OT_SCRUB_QUEUE:
+  case DMU_OT_USERREFS ... DMU_OT_DDT_STATS:
+    return 0;
+  default:
+    return 1;
+  }
+}
+
+static void
+add_blkptr_to_aad (char *aad, grub_size_t *aad_offset, blkptr_t bp, 
grub_zfs_endian_t endian, int preswapped)
+{
+  if (!preswapped && !GRUB_ZFS_IS_NATIVE_BYTEORDER(endian))
+    grub_zfs_byteswap_blkptr(&bp);
+  
+  grub_uint64_t blk_prop = BP_IS_HOLE(&bp) ? 0 : bp.blk_prop;
+
+  if (BP_GET_LEVEL(&bp) != 0) {
+    blk_prop &= ~0x8000007fffff0000ULL;
+  }
+
+  blk_prop &= ~0x4000ff0000000000ULL;
+
+  grub_set_unaligned64(&aad[*aad_offset], grub_cpu_to_le64(blk_prop));
+  *aad_offset += 8;
+
+  if (!GRUB_ZFS_IS_NATIVE_BYTEORDER(endian))
+    {
+      grub_set_unaligned64(&aad[*aad_offset], 
grub_swap_bytes64(bp.blk_cksum.zc_word[2]));
+      grub_set_unaligned64(&aad[*aad_offset+8], 
grub_swap_bytes64(bp.blk_cksum.zc_word[3]));
+    }
+  else
+      grub_memcpy(&aad[*aad_offset], &bp.blk_cksum.zc_word[2], 16);
+      
+  *aad_offset += 16;
+
+  grub_memset(&aad[*aad_offset], 0, 8);
+  *aad_offset += 8;
+}
+
 /*
  * Read in a block of data, verify its checksum, decompress if needed,
  * and put the uncompressed data in buf.
@@ -2122,17 +2228,44 @@ zio_read (const blkptr_t *bp, void **buf,
          grub_size_t *size, struct grub_zfs_data *data)
 {
   grub_size_t lsize, psize;
-  unsigned int comp, encrypted;
+  unsigned int comp;
   char *compbuf = NULL;
   grub_err_t err;
   zio_cksum_t zc = bp->blk_cksum;
   grub_uint32_t checksum;
+  int datto_encrypted = 0, datto_authenticated = 0, datto_dnode_encryption = 
0, oracle_encrypted = 0;
 
   *buf = NULL;
 
   checksum = BP_GET_CHECKSUM(bp);
   comp = BP_GET_COMPRESS(bp);
-  encrypted = BP_GET_PROP_BIT_61(bp);
+  if (BP_GET_PROP_BIT_61(bp))
+    {
+      if (data->subvol.is_datto_encrypted)
+       {
+         grub_uint8_t type = BP_GET_TYPE(bp);
+         if (BP_GET_LEVEL(bp) > 0)
+           datto_authenticated = 1;
+         else if (type == DMU_OT_DNODE)
+           {
+             datto_encrypted = 1;
+             datto_authenticated = 0;
+             datto_dnode_encryption = 1;
+           }
+         else if (type == DMU_OT_OBJSET)
+           {
+             /* Objset uses inner hmacs that we don't suport yet.
+                Normal checksum is unaffected . */
+           }
+         else
+           {
+             datto_encrypted = datto_is_encrypted_type(type);
+             datto_authenticated = !datto_encrypted;
+           }
+       }
+      else
+         oracle_encrypted = 1;
+    }
   if (BP_IS_EMBEDDED(bp))
     {
       if (BPE_GET_ETYPE(bp) != BP_EMBEDDED_TYPE_DATA)
@@ -2186,10 +2319,11 @@ zio_read (const blkptr_t *bp, void **buf,
       return err;
     }
 
-  if (!BP_IS_EMBEDDED(bp))
+  if (!BP_IS_EMBEDDED(bp) && !datto_encrypted)
     {
       err = zio_checksum_verify (zc, checksum, BP_GET_BYTEORDER(bp),
-                                compbuf, psize);
+                                compbuf, psize,
+                                datto_authenticated);
       if (err)
         {
           grub_dprintf ("zfs", "incorrect checksum\n");
@@ -2199,48 +2333,190 @@ zio_read (const blkptr_t *bp, void **buf,
         }
     }
 
-  if (encrypted)
+  if (!BP_IS_EMBEDDED(bp) && datto_authenticated && 
data->subvol.key_datto.hmac_key && BP_GET_LEVEL(bp) == 0)
     {
-      if (!grub_zfs_decrypt)
-       err = grub_error (GRUB_ERR_BAD_FS,
-                         N_("module `%s' isn't loaded"),
-                         "zfscrypt");
-      else
+      grub_uint64_t hmac[8];
+      gcry_error_t err_gcry;
+      err_gcry = grub_crypto_hmac_buffer (GRUB_MD_SHA512,
+                                         data->subvol.key_datto.hmac_key, 64,
+                                         compbuf, psize,
+                                         hmac);
+      if (err_gcry)
        {
-         unsigned i, besti = 0;
-         grub_uint64_t bestval = 0;
-         for (i = 0; i < data->subvol.nkeys; i++)
-           if (data->subvol.keyring[i].txg <= bp->blk_birth
-               && data->subvol.keyring[i].txg > bestval)
-             {
-               besti = i;
-               bestval = data->subvol.keyring[i].txg;
-             }
-         if (bestval == 0)
+         grub_free (compbuf);
+          *buf = NULL;
+         return grub_crypto_gcry_error (err_gcry);
+       }
+
+      if (!GRUB_ZFS_IS_NATIVE_BYTEORDER(BP_GET_BYTEORDER(bp)))
+       {
+         grub_zfs_byteswap_u64(&hmac[0]);
+         grub_zfs_byteswap_u64(&hmac[1]);
+       }      
+
+      if (grub_crypto_memcmp(&zc.zc_word[2], hmac, 8) != 0)
+       {
+         grub_free (compbuf);
+          *buf = NULL;
+         grub_dprintf ("zfs", "actual hmac "
+                       "%016llx %016llx %016llx %016llx %016llx %016llx 
%016llx %016llx \n",
+                       (unsigned long long) hmac[0],
+                       (unsigned long long) hmac[1],
+                       (unsigned long long) hmac[2],
+                       (unsigned long long) hmac[3],
+                       (unsigned long long) hmac[4],
+                       (unsigned long long) hmac[5],
+                       (unsigned long long) hmac[6],
+                       (unsigned long long) hmac[7]);
+         grub_dprintf ("zfs", "expected hmac %016llx %016llx %016llx 
%016llx\n",
+                       (unsigned long long) zc.zc_word[0],
+                       (unsigned long long) zc.zc_word[1],
+                       (unsigned long long) zc.zc_word[2],
+                       (unsigned long long) zc.zc_word[3]);
+         return grub_error (GRUB_ERR_BAD_FS, N_("HMAC verification failed"));
+       }
+    }
+
+  if (datto_dnode_encryption && (!grub_zfs_decrypt || 
!data->subvol.key_datto.master_key))
+    {
+      grub_dprintf("zfs", "Skipping decrypt of bonus because of missing 
zfscrypt module or key\n");
+      datto_encrypted = 0;
+      datto_dnode_encryption = 0;
+    }
+  if ((oracle_encrypted || datto_encrypted) && !grub_zfs_decrypt)
+    err = grub_error (GRUB_ERR_BAD_FS,
+                     N_("module `%s' isn't loaded"),
+                     "zfscrypt");
+  else if (datto_encrypted)
+    {
+      grub_uint32_t iv[3];
+
+      if (!data->subvol.key_datto.master_key)
+       {
+         grub_free (compbuf);
+         *buf = NULL;
+         grub_dprintf ("zfs", "no key for txg %" PRIxGRUB_UINT64_T "\n",
+                       bp->blk_birth);
+         return grub_error (GRUB_ERR_BAD_FS, "no key found in keychain");
+       }
+
+      grub_set_unaligned64(iv, grub_cpu_to_zfs64((bp)->blk_dva[2].dva_word[1], 
BP_GET_BYTEORDER(bp)));
+      iv[2] = grub_cpu_to_zfs32((bp)->blk_fill >> 32, BP_GET_BYTEORDER(bp));
+
+      if (datto_dnode_encryption)
+       {
+         grub_size_t offset = 0, crypt_offset = 0, aad_offset = 0;
+         char *crypt = grub_malloc(psize), *aad = grub_malloc(psize);
+         grub_zfs_endian_t endian = BP_GET_BYTEORDER(bp);
+         if (!crypt || !aad)
            {
              grub_free (compbuf);
+             grub_free (crypt);
+             grub_free (aad);
              *buf = NULL;
-             grub_dprintf ("zfs", "no key for txg %" PRIxGRUB_UINT64_T "\n",
-                           bp->blk_birth);
-             return grub_error (GRUB_ERR_BAD_FS, "no key found in keychain");
+             return grub_errno;
            }
-         grub_dprintf ("zfs", "using key %u (%" PRIxGRUB_UINT64_T
-                       ", %p) for txg %" PRIxGRUB_UINT64_T "\n",
-                       besti, data->subvol.keyring[besti].txg,
-                       data->subvol.keyring[besti].cipher,
-                       bp->blk_birth);
-         err = grub_zfs_decrypt (data->subvol.keyring[besti].cipher,
-                                 data->subvol.keyring[besti].algo,
-                                 &(bp)->blk_dva[2],
-                                 compbuf, psize, zc.zc_mac,
-                                 BP_GET_BYTEORDER(bp));
+         for (offset = 0; offset + sizeof (dnode_phys_t) <= psize; offset += 
sizeof(dnode_phys_t))
+           {
+             dnode_phys_t *dnp = (dnode_phys_t *) (void *) (compbuf + offset);
+             dnode_phys_t *dno = (dnode_phys_t *) (aad + aad_offset);
+             grub_memcpy(aad + aad_offset, dnp, 64);
+             dno->dn_used = 0;
+             dno->dn_flags &= DNODE_FLAG_SPILL_BLKPTR;
+             int has_spill = dnp->dn_flags & DNODE_FLAG_SPILL_BLKPTR;
+             unsigned i;
+
+             aad_offset += 64;
+
+             for (i = 0; i < dnp->dn_nblkptr; i++) {
+               add_blkptr_to_aad (aad, &aad_offset, dnp->dn_blkptr[i], endian, 
0);
+             }
+
+             if (has_spill)
+               add_blkptr_to_aad (aad, &aad_offset, dnp->dn_spill, endian, 0);
+
+             char *bonus = DN_BONUS(dnp);
+             char *bonusmaxptr = (char *) (dnp + 1);
+             if (has_spill)
+               bonusmaxptr -= sizeof(blkptr_t);
+             grub_size_t bonusmaxlen = bonusmaxptr - bonus;
+             if (datto_is_encrypted_type(dnp->dn_bonustype))
+               {
+                 grub_memcpy(crypt + crypt_offset, bonus, bonusmaxlen);
+                 crypt_offset += bonusmaxlen;
+               }
+             else
+               {
+                 grub_memcpy(aad + aad_offset, bonus, bonusmaxlen);
+                 grub_zfs_byteswap_type((aad + aad_offset), bonusmaxlen, 
dnp->dn_bonustype);
+                 aad_offset += bonusmaxlen;
+               }
+           }
+         err = grub_zfs_decrypt->decrypt_datto (&data->subvol.key_datto,
+                                                iv, 
(bp)->blk_dva[2].dva_word[0],
+                                                crypt, crypt_offset, aad, 
aad_offset,
+                                                &zc.zc_word[2],
+                                                endian);
+         grub_size_t out_offset = 0;
+         for (offset = 0; offset + sizeof (dnode_phys_t) <= psize; offset += 
sizeof(dnode_phys_t))
+           {
+             dnode_phys_t *dnp = (dnode_phys_t *) (void *) (compbuf + offset);
+             int has_spill = dnp->dn_flags & DNODE_FLAG_SPILL_BLKPTR;
+             if (!datto_is_encrypted_type(dnp->dn_bonustype))
+               continue;
+             char * bonus = DN_BONUS(dnp);
+             char *bonusmaxptr = (char *) (dnp + 1);
+             if (has_spill)
+               bonusmaxptr -= sizeof(blkptr_t);
+             grub_size_t bonusmaxlen = bonusmaxptr - bonus;
+             grub_memcpy(bonus, crypt + out_offset, bonusmaxlen);
+             out_offset += bonusmaxlen;
+           }
+
        }
-      if (err)
+      else
+       err = grub_zfs_decrypt->decrypt_datto (&data->subvol.key_datto,
+                                              iv, (bp)->blk_dva[2].dva_word[0],
+                                              compbuf, psize, NULL, 0,
+                                              &zc.zc_word[2],
+                                              BP_GET_BYTEORDER(bp));
+    }
+  else if (oracle_encrypted)
+    {
+      unsigned i, besti = 0;
+      grub_uint64_t bestval = 0;
+      grub_dprintf("zfs", "Subvol has %d keys\n", (int) data->subvol.nkeys);
+      for (i = 0; i < data->subvol.nkeys; i++)
+       if (data->subvol.keyring[i].txg <= bp->blk_birth
+           && data->subvol.keyring[i].txg > bestval)
+         {
+           besti = i;
+           bestval = data->subvol.keyring[i].txg;
+         }
+      if (bestval == 0)
        {
          grub_free (compbuf);
          *buf = NULL;
-         return err;
+         grub_dprintf ("zfs", "no key for txg %" PRIxGRUB_UINT64_T "\n",
+                       bp->blk_birth);
+         return grub_error (GRUB_ERR_BAD_FS, "no key found in keychain");
        }
+      grub_dprintf ("zfs", "using key %u (%" PRIxGRUB_UINT64_T
+                   ", %p) for txg %" PRIxGRUB_UINT64_T "\n",
+                   besti, data->subvol.keyring[besti].txg,
+                   data->subvol.keyring[besti].cipher,
+                   bp->blk_birth);
+      err = grub_zfs_decrypt->decrypt_oracle 
(data->subvol.keyring[besti].cipher,
+                                             data->subvol.keyring[besti].algo,
+                                             &(bp)->blk_dva[2],
+                                             compbuf, psize, zc.zc_mac,
+                                             BP_GET_BYTEORDER(bp));
+    }
+  if (err)
+    {
+      grub_free (compbuf);
+      *buf = NULL;
+      return err;
     }
 
   if (comp != ZIO_COMPRESS_OFF)
@@ -2254,6 +2530,7 @@ zio_read (const blkptr_t *bp, void **buf,
 
       err = decomp_table[comp].decomp_func (compbuf, *buf, psize, lsize);
       grub_free (compbuf);
+      compbuf = NULL;
       if (err)
        {
          grub_free (*buf);
@@ -2273,6 +2550,51 @@ zio_read (const blkptr_t *bp, void **buf,
        grub_zfs_byteswap_type(*buf, lsize, BP_GET_TYPE(bp));
     }
 
+  if (!BP_IS_EMBEDDED(bp) && datto_authenticated && BP_GET_LEVEL(bp) > 0)
+    {
+      grub_uint64_t hash[8];
+      char *aad = grub_malloc(lsize);
+      grub_size_t aad_offset = 0;
+      if (!aad)
+       {
+         grub_free (*buf);
+         *buf = NULL;
+         return grub_errno;
+       }
+      for (unsigned i = 0; i < lsize / sizeof(blkptr_t); i++)
+       add_blkptr_to_aad (aad, &aad_offset, ((blkptr_t *)*buf)[i], 
BP_GET_BYTEORDER(bp), 1);
+      grub_crypto_hash(GRUB_MD_SHA512, hash, aad, aad_offset);
+      grub_free(aad);
+
+      if (!GRUB_ZFS_IS_NATIVE_BYTEORDER(BP_GET_BYTEORDER(bp)))
+       {
+         grub_zfs_byteswap_u64(&hash[0]);
+         grub_zfs_byteswap_u64(&hash[1]);
+       }      
+
+      if (grub_crypto_memcmp(&zc.zc_word[2], hash, 8) != 0)
+       {
+         grub_free (*buf);
+          *buf = NULL;
+         grub_dprintf ("zfs", "actual hash "
+                       "%016llx %016llx %016llx %016llx %016llx %016llx 
%016llx %016llx \n",
+                       (unsigned long long) hash[0],
+                       (unsigned long long) hash[1],
+                       (unsigned long long) hash[2],
+                       (unsigned long long) hash[3],
+                       (unsigned long long) hash[4],
+                       (unsigned long long) hash[5],
+                       (unsigned long long) hash[6],
+                       (unsigned long long) hash[7]);
+         grub_dprintf ("zfs", "expected hash %016llx %016llx %016llx 
%016llx\n",
+                       (unsigned long long) zc.zc_word[0],
+                       (unsigned long long) zc.zc_word[1],
+                       (unsigned long long) zc.zc_word[2],
+                       (unsigned long long) zc.zc_word[3]);
+         return grub_error (GRUB_ERR_BAD_FS, N_("hash verification failed"));
+       }
+    }
+
   return GRUB_ERR_NONE;
 }
 
@@ -3519,8 +3841,8 @@ load_zap_key (const void *name, grub_size_t namelen, 
const void *val_in,
   ctx->subvol->keyring[ctx->keyn].algo =
     grub_le_to_cpu64 (*(grub_uint64_t *) val_in);
   ctx->subvol->keyring[ctx->keyn].cipher =
-    grub_zfs_load_key (val_in, nelem, ctx->salt,
-                      ctx->subvol->keyring[ctx->keyn].algo);
+    grub_zfs_decrypt->load_key_oracle (val_in, nelem, ctx->salt,
+                                      ctx->subvol->keyring[ctx->keyn].algo);
   ctx->keyn++;
   return 0;
 }
@@ -3587,6 +3909,82 @@ dnode_get_fullpath (const char *fullpath, struct 
subvolume *subvol,
 
   grub_dprintf ("zfs", "alive\n");
 
+  grub_uint64_t crypt_obj;
+
+  err = zap_lookup (dn, "com.datto:crypto_key_obj", &crypt_obj, data, 0);
+  if (err)
+    {
+      grub_errno = GRUB_ERR_NONE;
+      crypt_obj = 0;
+    }
+
+  grub_dprintf("zfs", "crypt obj = %lld\n", (long long) crypt_obj);
+  subvol->is_datto_encrypted = crypt_obj != 0;
+
+  if (grub_zfs_decrypt && crypt_obj)
+    {
+      dnode_phys_t crypt_dn;
+      err = dnode_get (&(data->mos), crypt_obj, 0xc4,
+                      &crypt_dn, data);
+      if (err)
+       {
+         grub_free (fsname);
+         grub_free (snapname);
+         return err;
+       }
+
+      err = dnode_get (&(data->mos), crypt_obj, 0 /* ?? */,
+                      &crypt_dn, data);
+      struct grub_zfs_crypt_datto_data crypt_datto_data = { 0 };
+      grub_uint64_t pbkdf2iters = 0, pbkdf2salt = 0, guid = 0, algo = 0, 
version = 0;
+      zap_iterate (&crypt_dn, 1, fill_crypt_datto_data, &crypt_datto_data, 
data);
+
+      err = zap_lookup (&crypt_dn, "pbkdf2iters", &pbkdf2iters, data, 0);
+      if (err)
+       {
+         grub_errno = GRUB_ERR_NONE;
+         pbkdf2iters = 0;
+       }
+
+      err = zap_lookup (&crypt_dn, "DSL_CRYPTO_GUID", &guid, data, 0);
+      if (err)
+       {
+         grub_errno = GRUB_ERR_NONE;
+         guid = 0;
+       }
+
+      err = zap_lookup (&crypt_dn, "DSL_CRYPTO_SUITE", &algo, data, 0);
+      if (err)
+       {
+         grub_errno = GRUB_ERR_NONE;
+         algo = 0;
+       }
+
+      err = zap_lookup (&crypt_dn, "DSL_CRYPTO_VERSION", &version, data, 0);
+      if (err)
+       {
+         grub_errno = GRUB_ERR_NONE;
+         version = 0;
+       }
+
+      err = zap_lookup (&crypt_dn, "pbkdf2salt", &pbkdf2salt, data, 0);
+      if (err)
+       {
+         grub_errno = GRUB_ERR_NONE;
+         pbkdf2salt = 0;
+       }
+
+      pbkdf2salt = grub_cpu_to_le64(pbkdf2salt);
+
+      subvol->key_datto = 
grub_zfs_decrypt->load_key_datto(crypt_datto_data.iv, crypt_datto_data.ivlen,
+                                                          
crypt_datto_data.mac, crypt_datto_data.maclen,
+                                                          
crypt_datto_data.master, crypt_datto_data.masterlen,
+                                                          
crypt_datto_data.hmac, crypt_datto_data.hmaclen,
+                                                          (const grub_uint8_t 
*) &pbkdf2salt, sizeof (pbkdf2salt),
+                                                          pbkdf2iters, guid, 
algo, version);
+    }
+
+
   headobj = ((dsl_dir_phys_t *) DN_BONUS (dn))->dd_head_dataset_obj;
 
   err = dnode_get (&(data->mos), headobj, 0, &subvol->mdn, data);
@@ -3600,7 +3998,7 @@ dnode_get_fullpath (const char *fullpath, struct 
subvolume *subvol,
 
   grub_dprintf("zfs", "keychain obj = %lld\n", (long long) keychainobj);
 
-  if (grub_zfs_decrypt && keychainobj)
+  if (grub_zfs_decrypt && !subvol->is_datto_encrypted && keychainobj)
     {
       struct dnode_get_fullpath_ctx ctx = {
        .subvol = subvol,
diff --git a/grub-core/fs/zfs/zfscrypt.c b/grub-core/fs/zfs/zfscrypt.c
index 535fd4065..e202f4265 100644
--- a/grub-core/fs/zfs/zfscrypt.c
+++ b/grub-core/fs/zfs/zfscrypt.c
@@ -46,8 +46,9 @@
 GRUB_MOD_LICENSE ("GPLv3+");
 
 /*
-  Mostly based on following article:
+  Oracle part is mostly based on following article:
   https://blogs.oracle.com/darren/entry/zfs_encryption_what_is_on
+  Datto part is based on comments in OpenZFS. No actal code has been used
  */
 
 enum grub_zfs_algo
@@ -56,7 +57,26 @@ enum grub_zfs_algo
     GRUB_ZFS_ALGO_GCM,
   };
 
-struct grub_zfs_key
+struct datto_algo {
+  grub_size_t wrapkeylen;
+  grub_size_t cryptkeylen;
+  grub_size_t masterkeylen;
+  enum grub_zfs_algo used_algo;
+} datto_algos[] = {
+  { 0 },
+  { 0 },
+  { 0 },
+  { 32, 16, 16, GRUB_ZFS_ALGO_CCM },
+  { 32, 24, 24, GRUB_ZFS_ALGO_CCM },
+  { 32, 32, 32, GRUB_ZFS_ALGO_CCM },
+  { 32, 16, 16, GRUB_ZFS_ALGO_GCM },
+  { 32, 24, 24, GRUB_ZFS_ALGO_GCM },
+  { 32, 32, 32, GRUB_ZFS_ALGO_GCM },
+};
+#define MAX_WRAPCIPHERTEXTLEN 96
+#define MAX_MACLEN 16
+
+struct grub_zfs_key_oracle
 {
   grub_uint64_t algo;
   grub_uint8_t enc_nonce[13];
@@ -102,26 +122,81 @@ grub_zfs_add_key (grub_uint8_t *key_in,
 
 static gcry_err_code_t
 grub_ccm_decrypt (grub_crypto_cipher_handle_t cipher,
-                 grub_uint8_t *out, const grub_uint8_t *in,
-                 grub_size_t psize,
+                 grub_uint8_t *out,
+                 const grub_uint8_t *in, grub_size_t psize,
+                 const grub_uint8_t *aad, grub_size_t aadsize,
                  void *mac_out, const void *nonce,
-                 unsigned l, unsigned m)
+                 unsigned noncelen, unsigned m)
 {
   grub_uint8_t iv[16];
   grub_uint8_t mul[16];
   grub_uint32_t mac[4];
-  unsigned i, j;
+  unsigned i, j, l = 15 - noncelen, aprefixlen = 0;
   gcry_err_code_t err;
+  grub_uint8_t aprefix[16] = { 0 };
 
-  grub_memcpy (iv + 1, nonce, 15 - l);
+  grub_memcpy (iv + 1, nonce, noncelen);
 
-  iv[0] = (l - 1) | (((m-2) / 2) << 3);
+  iv[0] = (l - 1) | (((m-2) / 2) << 3) | ((aadsize != 0) << 6);
   for (j = 0; j < l; j++)
     iv[15 - j] = psize >> (8 * j);
   err = grub_crypto_ecb_encrypt (cipher, mac, iv, 16);
   if (err)
     return err;
 
+  if (aadsize == 0)
+    aprefixlen = 0;
+  else if (aadsize <= 0xFEFF)
+    {
+      aprefixlen = 2;
+      aprefix[0] = aadsize >> 8;
+      aprefix[1] = aadsize;
+    }
+#if GRUB_CPU_SIZEOF_VOID_P == 8
+  else if ((aadsize >> 32) == 0)
+#endif
+    {
+      aprefixlen = 6;
+      aprefix[0] = 0xff;
+      aprefix[1] = 0xfe;
+      grub_set_unaligned32(aprefix + 2, grub_cpu_to_be32(aadsize));
+    }
+#if GRUB_CPU_SIZEOF_VOID_P == 8
+  else
+    {
+      aprefixlen = 10;
+      aprefix[0] = 0xff;
+      aprefix[1] = 0xff;
+      grub_set_unaligned64(aprefix + 2, grub_cpu_to_be64(aadsize));
+    }
+#endif
+
+  if (aadsize != 0)
+    {
+      grub_size_t ablocks = (aadsize + aprefixlen + 15) / 16;
+      grub_size_t first_block_datalen = 16 - aprefixlen;
+      if (first_block_datalen > aadsize)
+       first_block_datalen = aadsize;
+      grub_memcpy (aprefix + aprefixlen, aad, first_block_datalen);
+
+      grub_crypto_xor (mac, mac, aprefix, 16);
+      err = grub_crypto_ecb_encrypt (cipher, mac, mac, 16);
+      if (err)
+       return err;
+
+      for (i = 1; i < ablocks; i++)
+       {
+         grub_size_t csize, instart = (i - 1) * 16 + first_block_datalen;
+         csize = 16;
+         if (csize > aadsize - instart)
+           csize = aadsize - instart;
+         grub_crypto_xor (mac, mac, aad + instart, csize);
+         err = grub_crypto_ecb_encrypt (cipher, mac, mac, 16);
+         if (err)
+           return err;
+       }
+    }
+
   iv[0] = l - 1;
 
   for (i = 0; i < (psize + 15) / 16; i++)
@@ -187,6 +262,8 @@ static gcry_err_code_t
 grub_gcm_decrypt (grub_crypto_cipher_handle_t cipher,
                  grub_uint8_t *out, const grub_uint8_t *in,
                  grub_size_t psize,
+                 const grub_uint8_t *aad,
+                 grub_size_t aadsize,
                  void *mac_out, const void *nonce,
                  unsigned nonce_len, unsigned m)
 {
@@ -213,7 +290,7 @@ grub_gcm_decrypt (grub_crypto_cipher_handle_t cipher,
   else
     {
       grub_memset (iv, 0, sizeof (iv));
-      grub_memcpy (iv, nonce, nonce_len);
+      grub_memcpy (iv, nonce, nonce_len > sizeof (iv) ? sizeof (iv) : 
nonce_len);
       grub_gcm_mul (iv, h);
       iv[15] ^= nonce_len * 8;
       grub_gcm_mul (iv, h);
@@ -223,6 +300,16 @@ grub_gcm_decrypt (grub_crypto_cipher_handle_t cipher,
   if (err)
     return err;
 
+  for (i = 0; i < (aadsize + 15) / 16; i++)
+    {
+      grub_size_t csize;
+      csize = 16;
+      if (csize > aadsize - 16 * i)
+       csize = aadsize - 16 * i;
+      grub_crypto_xor (mac, mac, aad + 16 * i, csize);
+      grub_gcm_mul (mac, h);
+    }
+
   for (i = 0; i < (psize + 15) / 16; i++)
     {
       grub_size_t csize;
@@ -244,6 +331,8 @@ grub_gcm_decrypt (grub_crypto_cipher_handle_t cipher,
     }
   for (j = 0; j < 8; j++)
     mac[15 - j] ^= ((((grub_uint64_t) psize) * 8) >> (8 * j));
+  for (j = 0; j < 8; j++)
+    mac[7 - j] ^= ((((grub_uint64_t) aadsize) * 8) >> (8 * j));
   grub_gcm_mul (mac, h);
 
   if (mac_out)
@@ -257,30 +346,33 @@ static gcry_err_code_t
 algo_decrypt (grub_crypto_cipher_handle_t cipher, grub_uint64_t algo,
              grub_uint8_t *out, const grub_uint8_t *in,
              grub_size_t psize,
+             const grub_uint8_t *aad,
+             grub_size_t aadsize,
              void *mac_out, const void *nonce,
              unsigned l, unsigned m)
 {
   switch (algo)
     {
-    case 0:
+    case GRUB_ZFS_ALGO_CCM:
       return grub_ccm_decrypt (cipher, out, in, psize,
-                              mac_out, nonce, l, m);
-    case 1:
+                              aad, aadsize, mac_out, nonce,
+                              l <= 15 ? l : 0, m);
+    case GRUB_ZFS_ALGO_GCM:
       return grub_gcm_decrypt (cipher, out, in, psize,
-                              mac_out, nonce,
-                              15 - l, m);
+                              aad, aadsize, mac_out, nonce,
+                              l <= 15 ? l : 0, m);
     default:
       return GPG_ERR_CIPHER_ALGO;
     }
 }
 
 static grub_err_t
-grub_zfs_decrypt_real (grub_crypto_cipher_handle_t cipher,
-                      grub_uint64_t algo,
-                      const void *nonce,
-                      char *buf, grub_size_t size,
-                      const grub_uint32_t *expected_mac,
-                      grub_zfs_endian_t endian)
+grub_zfs_decrypt_oracle (grub_crypto_cipher_handle_t cipher,
+                        grub_uint64_t algo,
+                        const void *nonce,
+                        char *buf, grub_size_t size,
+                        const grub_uint32_t *expected_mac,
+                        grub_zfs_endian_t endian)
 {
   grub_uint32_t mac[4];
   unsigned i;
@@ -298,8 +390,8 @@ grub_zfs_decrypt_real (grub_crypto_cipher_handle_t cipher,
   err = algo_decrypt (cipher, algo,
                      (grub_uint8_t *) buf,
                      (grub_uint8_t *) buf,
-                     size, mac,
-                     sw + 1, 3, 12);
+                     size, NULL, 0, mac,
+                     sw, 12, 12);
   if (err)
     return grub_crypto_gcry_error (err);
 
@@ -310,11 +402,73 @@ grub_zfs_decrypt_real (grub_crypto_cipher_handle_t cipher,
   return GRUB_ERR_NONE;
 }
 
+static grub_err_t
+grub_zfs_decrypt_datto (const struct grub_zfs_datto_key *key,
+                       const grub_uint32_t *nonce, grub_uint64_t salt,
+                       char *buf, grub_size_t size,
+                       const char *aadbuf, grub_size_t aadsize,
+                       const grub_uint64_t *expected_mac,
+                       grub_zfs_endian_t endian)
+{
+  grub_uint64_t mac[2];
+  unsigned i;
+  grub_uint8_t extractkey[64];
+  grub_uint8_t expandkey[64];
+  grub_uint8_t t[9];
+  gcry_err_code_t err;
+  grub_crypto_cipher_handle_t cipher;
+
+  if (!key)
+    return grub_error (GRUB_ERR_ACCESS_DENIED,
+                      N_("no decryption key available"));
+
+  /* A special case of HKDF with 512 > keylen.  */
+  err = grub_crypto_hmac_buffer (GRUB_MD_SHA512,
+                                "", 0,
+                                key->master_key, key->master_keylen,
+                                extractkey);
+  if (err)
+    return grub_crypto_gcry_error (err);
+
+  grub_set_unaligned64(t, grub_cpu_to_zfs64(salt, endian));
+  t[8] = 1;
+  err = grub_crypto_hmac_buffer (GRUB_MD_SHA512,
+                                extractkey, 64,
+                                t, 9, expandkey);
+  if (err)
+    return grub_crypto_gcry_error (err);
+
+  cipher = grub_crypto_cipher_open (GRUB_CIPHER_AES);
+  if (!cipher)
+    return grub_crypto_gcry_error (err);
+  err = grub_crypto_cipher_set_key (cipher, expandkey, 
datto_algos[key->algo].cryptkeylen);
+  if (err)
+    {
+      grub_crypto_cipher_close (cipher);
+      return grub_crypto_gcry_error (err);
+    }
+
+  err = algo_decrypt (cipher, datto_algos[key->algo].used_algo,
+                     (grub_uint8_t *) buf,
+                     (grub_uint8_t *) buf,
+                     size, (grub_uint8_t *) aadbuf, aadsize, mac,
+                     nonce, 12, 16);
+  grub_crypto_cipher_close (cipher);
+  if (err)
+    return grub_crypto_gcry_error (err);
+
+  for (i = 0; i < 2; i++)
+    if (grub_zfs_to_cpu64 (expected_mac[i], endian)
+       != grub_le_to_cpu64 (mac[i]))
+      grub_dprintf("zfs", N_("MAC verification failed"));
+  return GRUB_ERR_NONE;
+}
+
 static grub_crypto_cipher_handle_t
-grub_zfs_load_key_real (const struct grub_zfs_key *key,
-                       grub_size_t keysize,
-                       grub_uint64_t salt,
-                       grub_uint64_t algo)
+grub_zfs_load_key_oracle (const struct grub_zfs_key_oracle *key,
+                         grub_size_t keysize,
+                         grub_uint64_t salt,
+                         grub_uint64_t algo)
 {
   unsigned keylen;
   struct grub_zfs_wrap_key *wrap_key;
@@ -373,7 +527,7 @@ grub_zfs_load_key_real (const struct grub_zfs_key *key,
        }
 
       err = algo_decrypt (cipher, algo, decrypted, key->unknown_purpose_key, 
32,
-                         mac, key->unknown_purpose_nonce, 2, 16);
+                         NULL, 0, mac, key->unknown_purpose_nonce, 13, 16);
       if (err || (grub_crypto_memcmp (mac, key->unknown_purpose_key + 32, 16)
                  != 0))
        {
@@ -383,8 +537,8 @@ grub_zfs_load_key_real (const struct grub_zfs_key *key,
          continue;
        }
 
-      err = algo_decrypt (cipher, algo, decrypted, key->enc_key, keylen, mac,
-                         key->enc_nonce, 2, 16);
+      err = algo_decrypt (cipher, algo, decrypted, key->enc_key, keylen, NULL, 
0,
+                         mac, key->enc_nonce, 13, 16);
       if (err || grub_crypto_memcmp (mac, key->enc_key + keylen, 16) != 0)
        {
          grub_dprintf ("zfs", "key loading failed\n");
@@ -413,6 +567,119 @@ grub_zfs_load_key_real (const struct grub_zfs_key *key,
   return NULL;
 }
 
+static struct grub_zfs_datto_key
+grub_zfs_load_key_datto (const grub_uint8_t *iv, grub_size_t ivlen,
+                        const grub_uint8_t *mac_in, grub_size_t mac_inlen,
+                        const grub_uint8_t *master, grub_size_t masterlen,
+                        const grub_uint8_t *hmac, grub_size_t hmaclen,
+                        const grub_uint8_t *pbkdf2salt, grub_size_t 
pbkdf2saltlen,
+                        grub_uint64_t pbkdf2iters, grub_uint64_t guid, 
grub_uint64_t algo, grub_uint64_t version)
+{
+  struct grub_zfs_wrap_key *wrap_key;
+  struct grub_zfs_datto_key ret = { 0 };
+
+  if (algo <= 2 || algo >= ARRAY_SIZE(datto_algos))
+    {
+      grub_error(GRUB_ERR_BAD_FS, "unsupported crypto algo");
+      return ret;
+    }
+
+  /* Note: if last two ever become variable they need to be checkecked as 
matching algo. Especially mac_inlen.  */
+  if (masterlen < datto_algos[algo].masterkeylen || hmaclen != 64 || mac_inlen 
!= 16)
+    {
+      grub_error(GRUB_ERR_BAD_FS, "crypto keys are invalid");
+      return ret;
+    }
+
+  masterlen = datto_algos[algo].masterkeylen;
+
+  grub_uint8_t ciphertext[MAX_WRAPCIPHERTEXTLEN];
+  grub_uint8_t plaintext[MAX_WRAPCIPHERTEXTLEN];
+  grub_uint8_t mac_computed[MAX_MACLEN];
+  struct {
+    grub_uint64_t guid;
+    grub_uint64_t algo;
+    grub_uint64_t version;
+  } aad = {
+    grub_cpu_to_le64(guid),
+    grub_cpu_to_le64(algo),
+    grub_cpu_to_le64(version)
+  };
+
+  grub_memcpy(ciphertext, master, masterlen);
+  grub_memcpy(ciphertext + masterlen, hmac, hmaclen);
+
+  for (wrap_key = zfs_wrap_keys; wrap_key; wrap_key = wrap_key->next)
+    {
+      grub_crypto_cipher_handle_t cipher;
+      grub_uint8_t wrap_key_real[32] = { 0 };
+      gcry_err_code_t err = 0;
+
+      cipher = grub_crypto_cipher_open (GRUB_CIPHER_AES);
+      if (!cipher)
+       {
+         grub_errno = GRUB_ERR_NONE;
+         return ret;
+       }
+      grub_memset (wrap_key_real, 0, sizeof (wrap_key_real));
+
+      if (!wrap_key->is_passphrase)
+       grub_memcpy(wrap_key_real, wrap_key->key,
+                   wrap_key->keylen < datto_algos[algo].wrapkeylen ? 
wrap_key->keylen : datto_algos[algo].wrapkeylen);
+      else
+       // TODO: PBKDF2
+       err = grub_crypto_pbkdf2 (GRUB_MD_SHA1,
+                                 (const grub_uint8_t *) wrap_key->key,
+                                 wrap_key->keylen,
+                                 pbkdf2salt, pbkdf2saltlen,
+                                 pbkdf2iters, wrap_key_real, 
datto_algos[algo].wrapkeylen);
+
+      if (err)
+       {
+         grub_errno = GRUB_ERR_NONE;
+         grub_crypto_cipher_close (cipher);
+         continue;
+       }
+
+      err = grub_crypto_cipher_set_key (cipher, wrap_key_real, 
datto_algos[algo].wrapkeylen);
+      if (err)
+       {
+         grub_errno = GRUB_ERR_NONE;
+         grub_crypto_cipher_close (cipher);
+         continue;
+       }
+
+      err = algo_decrypt (cipher, datto_algos[algo].used_algo,
+                         plaintext, ciphertext, masterlen + hmaclen,
+                         (grub_uint8_t *) &aad, sizeof(aad),
+                         mac_computed, iv, ivlen, mac_inlen);
+      if (err || (grub_crypto_memcmp (mac_computed, mac_in, mac_inlen) != 0))
+       {
+         grub_dprintf ("zfs", "key loading failed\n");
+         grub_errno = GRUB_ERR_NONE;
+         grub_crypto_cipher_close (cipher);
+         continue;
+       }
+
+      ret.master_keylen = masterlen;
+      ret.master_key = grub_malloc(ret.master_keylen);
+      if (!ret.master_key)
+       return ret;
+      grub_memcpy(ret.master_key, plaintext, masterlen);
+      ret.hmac_key = grub_malloc(hmaclen);
+      if (!ret.hmac_key)
+       {
+         grub_free(ret.master_key);
+         ret.master_key = NULL;
+         return ret;
+       }
+      ret.algo = algo;
+      grub_memcpy(ret.hmac_key, plaintext + masterlen, hmaclen);
+      return ret;
+    }
+  return ret;
+}
+
 static const struct grub_arg_option options[] =
   {
     {"raw", 'r', 0, N_("Assume input is raw."), 0, 0},
@@ -471,12 +738,17 @@ grub_cmd_zfs_key (grub_extcmd_context_t ctxt, int argc, 
char **args)
                               && !ctxt->state[1].set));
 }
 
+struct grub_zfs_decryptor grub_zfs_decrypt_real = {
+  .decrypt_oracle = grub_zfs_decrypt_oracle,
+  .decrypt_datto = grub_zfs_decrypt_datto,
+  .load_key_oracle = grub_zfs_load_key_oracle,
+  .load_key_datto = grub_zfs_load_key_datto,
+};
 static grub_extcmd_t cmd_key;
 
 GRUB_MOD_INIT(zfscrypt)
 {
-  grub_zfs_decrypt = grub_zfs_decrypt_real;
-  grub_zfs_load_key = grub_zfs_load_key_real;
+  grub_zfs_decrypt = &grub_zfs_decrypt_real;
   cmd_key = grub_register_extcmd ("zfskey", grub_cmd_zfs_key, 0,
                                  N_("[-h|-p|-r] [FILE]"),
                                  N_("Import ZFS wrapping key stored in FILE."),
@@ -486,6 +758,5 @@ GRUB_MOD_INIT(zfscrypt)
 GRUB_MOD_FINI(zfscrypt)
 {
   grub_zfs_decrypt = 0;
-  grub_zfs_load_key = 0;
   grub_unregister_extcmd (cmd_key);
 }
diff --git a/include/grub/zfs/zfs.h b/include/grub/zfs/zfs.h
index 0c5c40a73..fbac368b0 100644
--- a/include/grub/zfs/zfs.h
+++ b/include/grub/zfs/zfs.h
@@ -147,20 +147,46 @@ grub_zfs_add_key (grub_uint8_t *key_in,
                  grub_size_t keylen,
                  int passphrase);
 
-extern grub_err_t (*grub_zfs_decrypt) (grub_crypto_cipher_handle_t cipher,
-                                      grub_uint64_t algo,
-                                      const void *nonce,
-                                      char *buf, grub_size_t size,
-                                      const grub_uint32_t *expected_mac,
-                                      grub_zfs_endian_t endian);
-
-struct grub_zfs_key;
-
-extern grub_crypto_cipher_handle_t (*grub_zfs_load_key) (const struct 
grub_zfs_key *key,
-                                                        grub_size_t keysize,
-                                                        grub_uint64_t salt,
-                                                        grub_uint64_t algo);
-
+struct grub_zfs_key_oracle;
+
+struct grub_zfs_datto_key
+{
+  grub_uint8_t *master_key;
+  grub_size_t master_keylen;
+  grub_uint8_t *hmac_key;
+  grub_uint64_t algo;
+};
+
+struct grub_zfs_decryptor
+{
+  grub_err_t (*decrypt_oracle) (grub_crypto_cipher_handle_t cipher,
+                               grub_uint64_t algo,
+                               const void *nonce,
+                               char *buf, grub_size_t size,
+                               const grub_uint32_t *expected_mac,
+                               grub_zfs_endian_t endian);
+
+  grub_crypto_cipher_handle_t (*load_key_oracle) (const struct 
grub_zfs_key_oracle *key,
+                                                 grub_size_t keysize,
+                                                 grub_uint64_t salt,
+                                                 grub_uint64_t algo);
+
+  grub_err_t (*decrypt_datto) (const struct grub_zfs_datto_key *key,
+                              const grub_uint32_t *nonce, grub_uint64_t salt,
+                              char *buf, grub_size_t size,
+                              const char *aadbuf, grub_size_t aadsize,
+                              const grub_uint64_t *expected_mac,
+                              grub_zfs_endian_t endian);
+
+  struct grub_zfs_datto_key (*load_key_datto) (const grub_uint8_t *iv, 
grub_size_t ivlen,
+                                              const grub_uint8_t *mac, 
grub_size_t maclen,
+                                              const grub_uint8_t *master, 
grub_size_t masterlen,
+                                              const grub_uint8_t *hmac, 
grub_size_t hmaclen,
+                                              const grub_uint8_t *pbkdf2salt, 
grub_size_t pbkdf2saltlen,
+                                              grub_uint64_t pbkdf2iters, 
grub_uint64_t guid, grub_uint64_t algo, grub_uint64_t version);
+};
+
+extern struct grub_zfs_decryptor *grub_zfs_decrypt;
 
 
 #endif /* ! GRUB_ZFS_HEADER */
-- 
2.49.0


_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to