我目前正在编写与加密库的绑定,该库公开用于生成密钥对的函数:
const size_t PUBLICKEYBYTES = 32;
const size_t SECRETKEYBYTES = 32;
int random_keypair(unsigned char pk[PUBLICKEYBYTES],
unsigned char sk[SECRETKEYBYTES]);
该函数随机生成一个密钥,计算相应的公钥并将结果放入pk
and sk
.
刚归还的时候ByteString
我发现最简单的方法是使用create :: Int -> (Ptr Word8 -> IO ()) -> IO ByteString
from Data.ByteString.Internal
。但是,该函数无法创建两个ByteStrings
同时。
我的第一个方法是写一些类似的东西:
newtype PublicKey = PublicKey ByteString
newtype SecretKey = SecretKey ByteString
randomKeypair :: IO (PublicKey, SecretKey)
randomKeypair = do
let pk = B.replicate 0 publicKeyBytes
sk = B.replicate 0 secretKeyBytes
B.unsafeUseAsCString pk $ \ppk ->
B.unsafeUseAsCString sk $ \psk ->
c_random_keypair ppk psk
return (PublicKey pk, SecretKey sk)
然而,这似乎不适用于 GHC 7.10.2。运行测试套件时,我发现我似乎共享了ByteString
s 在函数调用之间,导致加密/解密失败并给出不正确的结果。
我已经通过定义自己的函数来解决这个问题:
createWithResult :: Int -> (Ptr Word8 -> IO a) -> IO (ByteString, a)
createWithResult i f = do
fp <- B.mallocByteString i
r <- withForeignPtr fp f
return (B.fromForeignPtr fp 0 i, r)
并像这样使用它:
randomKeypair = fmap (PublicKey *** SecretKey) $
createWithResult publicKeyBytes $ \ppk ->
B.create secretKeyBytes $ \psk ->
void $ c_random_keypair ppk psk
这似乎有效,所有测试都通过了。
我的问题是,当涉及到共享和引用透明度时,语义到底是什么?IO
monad?
我的直觉告诉我(错误地)我可以用第一种方法解决问题,但显然我不能。我相信正在发生的事情是优化器看到let
- 语句可以上升到顶级定义中,这就是我遇到这些问题的原因。