diff --git a/src/Tahoe/SDMF/Internal/Keys.hs b/src/Tahoe/SDMF/Internal/Keys.hs
index d4f6eaaa008f6c21950f59d2c0fc7758035a76cf..4ba23fe5483f3fb0313c20c20de49df62d592afe 100644
--- a/src/Tahoe/SDMF/Internal/Keys.hs
+++ b/src/Tahoe/SDMF/Internal/Keys.hs
@@ -20,7 +20,6 @@ import qualified Data.ByteString.Lazy as LB
 import qualified Data.Text as T
 import Data.X509 (PrivKey (PrivKeyRSA), PubKey (PubKeyRSA))
 import Tahoe.CHK.Crypto (taggedHash, taggedPairHash)
-import Tahoe.CHK.Server (StorageServerID)
 
 newtype KeyPair = KeyPair {toPrivateKey :: RSA.PrivateKey} deriving newtype (Show)
 
@@ -40,7 +39,7 @@ newtype StorageIndex = StorageIndex {unStorageIndex :: B.ByteString}
 
 newtype WriteEnablerMaster = WriteEnablerMaster ByteArray.ScrubbedBytes
 
-data WriteEnabler = WriteEnabler StorageServerID ByteArray.ScrubbedBytes
+newtype WriteEnabler = WriteEnabler ByteArray.ScrubbedBytes
 
 data Data = Data {unData :: AES128, dataKeyBytes :: ByteArray.ScrubbedBytes}
 
@@ -95,6 +94,7 @@ deriveDataKey :: SDMF_IV -> Read -> Maybe Data
 deriveDataKey (SDMF_IV iv) r =
     Data <$> key <*> pure (ByteArray.convert sbs)
   where
+    -- XXX taggedPairHash has a bug where it doesn't ever truncate.
     sbs = B.take keyLength . taggedPairHash keyLength mutableDataKeyTag (B.pack . ByteArray.unpack $ iv) . ByteArray.convert . readKeyBytes $ r
     key = maybeCryptoError . cipherInit $ sbs
 
@@ -123,6 +123,19 @@ deriveWriteEnablerMaster w = WriteEnablerMaster bs
 mutableWriteEnablerMasterTag :: B.ByteString
 mutableWriteEnablerMasterTag = "allmydata_mutable_writekey_to_write_enabler_master_v1"
 
+{- | Derive the "write enabler" secret for a given peer and "write enabler
+ master" for an SDMF share.
+-}
+deriveWriteEnabler :: B.ByteString -> WriteEnablerMaster -> WriteEnabler
+deriveWriteEnabler peerid (WriteEnablerMaster master) = WriteEnabler bs
+  where
+    -- This one shouldn't be truncated.  Set the length to the size of sha256d
+    -- output.
+    bs = ByteArray.convert . taggedPairHash 32 mutableWriteEnablerTag (ByteArray.convert master) $ peerid
+
+mutableWriteEnablerTag :: B.ByteString
+mutableWriteEnablerTag = "allmydata_mutable_write_enabler_master_and_nodeid_to_write_enabler_v1"
+
 {- | Encode a public key to the Tahoe-LAFS canonical bytes representation -
  X.509 SubjectPublicKeyInfo of the ASN.1 DER serialization of an RSA
  PublicKey.
diff --git a/src/Tahoe/SDMF/Keys.hs b/src/Tahoe/SDMF/Keys.hs
index 7b724d19626def89695df1230f243bc2dc0c9b00..165915612de3689305539139b0c0e53807b323da 100644
--- a/src/Tahoe/SDMF/Keys.hs
+++ b/src/Tahoe/SDMF/Keys.hs
@@ -8,10 +8,12 @@ import Tahoe.SDMF.Internal.Keys (
     Signature (..),
     StorageIndex (..),
     Write (..),
+    WriteEnabler (..),
     WriteEnablerMaster (..),
     deriveDataKey,
     deriveReadKey,
     deriveStorageIndex,
+    deriveWriteEnabler,
     deriveWriteEnablerMaster,
     deriveWriteKey,
     toPublicKey,
diff --git a/test/Spec.hs b/test/Spec.hs
index e0061e9f6d194c61dfd7b73b21eeb737cfd755d5..e185dc49dcb1d803fa8f0b21ec09d97bc8fe549d 100644
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -86,15 +86,20 @@ tests =
                     expectedDataKey = ("bbj67exlrkfcaqutwlgwvukbfe" :: T.Text)
                     expectedStorageIndex = ("cmkuloz2t6fhsh7npxxteba6sq" :: T.Text)
                     expectedWriteEnablerMaster = ("qgptod5dsanfep2kbimvxl2yixndnoks7ndoeamczj7g33gokcvq" :: T.Text)
+                    expectedWriteEnabler = ("bg4ldrgfyiffufltcuttr3cnrmrjfpoxc65qdoqa6d5izkzofl5q" :: T.Text)
 
-                    -- Derive all the keys.
+                    -- Constants involved in the derivation.  These agree with
+                    -- those used to generate the above expected values.
                     (Just iv) = Keys.SDMF_IV <$> makeIV (B.replicate 16 0x42)
+                    peerid = B.replicate 20 0x42
+
+                    -- Derive all the keys.
                     (Just w@(Keys.Write _ derivedWriteKey)) = Keys.deriveWriteKey sigKey
                     (Just r@(Keys.Read _ derivedReadKey)) = Keys.deriveReadKey w
                     (Just (Keys.Data _ derivedDataKey)) = Keys.deriveDataKey iv r
                     (Keys.StorageIndex derivedStorageIndex) = Keys.deriveStorageIndex r
-                    (Keys.WriteEnablerMaster derivedWriteEnablerMaster) = Keys.deriveWriteEnablerMaster w
-
+                    wem@(Keys.WriteEnablerMaster derivedWriteEnablerMaster) = Keys.deriveWriteEnablerMaster w
+                    (Keys.WriteEnabler derivedWriteEnabler) = Keys.deriveWriteEnabler peerid wem
                     -- A helper to format a key as text for convenient
                     -- comparison to expected value.
                     fmtKey = T.toLower . encodeBase32Unpadded . ByteArray.convert
@@ -124,6 +129,10 @@ tests =
                             "write enabler master: expected /= derived"
                             expectedWriteEnablerMaster
                             (fmtKey derivedWriteEnablerMaster)
+                        assertEqual
+                            "write enabler: expected /= derived"
+                            expectedWriteEnabler
+                            (fmtKey derivedWriteEnabler)
         , testProperty "Share round-trips through bytes" $
             property $ do
                 share <- forAll shares