Haskell 绑定到 libsodium 的外部函数接口 - 错误签名
Haskell Foreign function interface binding to libsodium - bad signature
我正在对 libsodium 进行绑定,因此我可以在 Haskell 中签署字节串,但签名与 C 为相同消息和密钥创建的签名不匹配。
我在 libsodium 中绑定到这个函数:
int crypto_sign_detached(unsigned char *sig, unsigned long long *siglen,
const unsigned char *m, unsigned long long mlen,
const unsigned char *sk);
文档说:
The crypto_sign_detached() function signs the message m whose length is mlen bytes, using the secret key sk, and puts the signature into sig, which can be up to crypto_sign_BYTES long.
The actual length of the signature is put into siglen if siglen is not NULL.
It is safe to ignore siglen and always consider a signature as crypto_sign_BYTES bytes long: shorter signatures will be transparently padded with zeros if necessary.
crypto_sign_BYTES 是 64.
这里是 Haskell 代码(抱歉有点长,但越短越好):
{-# LANGUAGE OverloadedStrings #-}
module Main where
import qualified Data.ByteString as B
import qualified Data.ByteString.Base16 as B16
import qualified Data.ByteString.Internal as Bi
import qualified Foreign as F
import qualified Foreign.C.Types as T
import System.IO.Unsafe (unsafePerformIO)
secretkey :: B.ByteString
secretkey = B.pack [0x34, 0xEB, 0xA3, 0x9F, 0xC5, 0xA1, 0xB4, 0x1D,
0x64, 0x12, 0xCE, 0xC3, 0xD2, 0x0A, 0x7F, 0xA8,
0x24, 0x24, 0x2A, 0xDC, 0x1E, 0x6C, 0x04, 0x48,
0xCE, 0x91, 0xB3, 0xC4, 0x84, 0xCC, 0x7A, 0xC6]
main :: IO ()
main = do
status <- c_sodium_init
case status of
0 ->
case sign "test" secretkey of
Right sig -> do
print $ B16.encode sig
Left err -> print err
_ -> putStrLn "Could not initialize."
crypto_sign_BYTES :: Int
crypto_sign_BYTES = 64
packCString :: Int -> F.Ptr F.Word8 -> IO B.ByteString
packCString len cstr = create len $ \p -> Bi.memcpy p cstr len
create :: Int -> (F.Ptr F.Word8 -> IO ()) -> IO B.ByteString
create l f = do
fp <- Bi.mallocByteString l
F.withForeignPtr fp f
return $! Bi.PS fp 0 l
foreign import ccall unsafe "sodium.h crypto_sign_detached"
c_crypto_sign_detached
:: CUString
-> F.Ptr T.CULLong
-> CUString
-> T.CULLong
-> CUString
-> IO T.CInt
sign :: B.ByteString -> B.ByteString -> Either Int B.ByteString
sign message secretKey = unsafePerformIO $
useAsCString message $ \messagePtr ->
useAsCString secretKey $ \secretKeyPtr ->
F.allocaBytes crypto_sign_BYTES $ \sig -> do
status <- c_crypto_sign_detached
sig
F.nullPtr
messagePtr
(fromIntegral $ B.length message)
secretKeyPtr
case status of
0 -> do
packed <- packCString crypto_sign_BYTES $ F.castPtr sig
return $ Right packed
errCode -> return $ Left $ fromIntegral errCode
foreign import ccall unsafe "sodium.h sodium_init"
c_sodium_init :: IO T.CInt
type CUString = F.Ptr T.CUChar
useAsCString :: B.ByteString -> (CUString -> IO a) -> IO a
useAsCString (Bi.PS fp o l) action =
F.allocaBytes (l+1) $ \buf ->
F.withForeignPtr fp $ \p -> do
Bi.memcpy buf (p `F.plusPtr` o) (fromIntegral l)
F.pokeByteOff buf l (0::F.Word8)
action (F.castPtr buf)
C代码如下:
#include <sodium.h>
#include <stdio.h>
int main (void)
{
if (sodium_init() < 0) {
printf("could not initialize");
}
unsigned char message[4] = "test";
unsigned char sk[32] = {0x34, 0xEB, 0xA3, 0x9F, 0xC5, 0xA1, 0xB4,
0x1D, 0x64, 0x12, 0xCE, 0xC3, 0xD2, 0x0A,
0x7F, 0xA8, 0x24, 0x24, 0x2A, 0xDC, 0x1E,
0x6C, 0x04, 0x48, 0xCE, 0x91, 0xB3, 0xC4,
0x84, 0xCC, 0x7A, 0xC6};
unsigned char sig[crypto_sign_BYTES];
crypto_sign_detached(sig, NULL, message, 4, sk);
int i;
for (i=0; i<64; i++)
{
printf("%02X", sig[i]);
}
printf("\n");
}
Haskell 输出为:
"17aa8f20476a8e70f860707b8fd7f8f78ffd778690a755ddf3a432a81786ea3dea9efa2028d8896efaf1efe428781b3af83ab0450fc94a2ae6ec2c1a34ca830e"
C输出为:
17AA8F20476A8E70F860707B8FD7F8F78FFD778690A755DDF3A432A81786EA3D4B54ECDA53E7774C43BE7098A96C39A59664953E40AFFB54ACB84E09CBFF4E07
为什么这些签名只匹配 64 字节的前 32 个字节?如果有帮助,我已将代码放在 Github.
crypto_sign_SECRETKEYBYTES
是 64,而不是 32。将你的密钥设置这么长,C 和 Haskell 代码将相互一致。
我正在对 libsodium 进行绑定,因此我可以在 Haskell 中签署字节串,但签名与 C 为相同消息和密钥创建的签名不匹配。
我在 libsodium 中绑定到这个函数:
int crypto_sign_detached(unsigned char *sig, unsigned long long *siglen,
const unsigned char *m, unsigned long long mlen,
const unsigned char *sk);
文档说:
The crypto_sign_detached() function signs the message m whose length is mlen bytes, using the secret key sk, and puts the signature into sig, which can be up to crypto_sign_BYTES long.
The actual length of the signature is put into siglen if siglen is not NULL.
It is safe to ignore siglen and always consider a signature as crypto_sign_BYTES bytes long: shorter signatures will be transparently padded with zeros if necessary.
crypto_sign_BYTES 是 64.
这里是 Haskell 代码(抱歉有点长,但越短越好):
{-# LANGUAGE OverloadedStrings #-}
module Main where
import qualified Data.ByteString as B
import qualified Data.ByteString.Base16 as B16
import qualified Data.ByteString.Internal as Bi
import qualified Foreign as F
import qualified Foreign.C.Types as T
import System.IO.Unsafe (unsafePerformIO)
secretkey :: B.ByteString
secretkey = B.pack [0x34, 0xEB, 0xA3, 0x9F, 0xC5, 0xA1, 0xB4, 0x1D,
0x64, 0x12, 0xCE, 0xC3, 0xD2, 0x0A, 0x7F, 0xA8,
0x24, 0x24, 0x2A, 0xDC, 0x1E, 0x6C, 0x04, 0x48,
0xCE, 0x91, 0xB3, 0xC4, 0x84, 0xCC, 0x7A, 0xC6]
main :: IO ()
main = do
status <- c_sodium_init
case status of
0 ->
case sign "test" secretkey of
Right sig -> do
print $ B16.encode sig
Left err -> print err
_ -> putStrLn "Could not initialize."
crypto_sign_BYTES :: Int
crypto_sign_BYTES = 64
packCString :: Int -> F.Ptr F.Word8 -> IO B.ByteString
packCString len cstr = create len $ \p -> Bi.memcpy p cstr len
create :: Int -> (F.Ptr F.Word8 -> IO ()) -> IO B.ByteString
create l f = do
fp <- Bi.mallocByteString l
F.withForeignPtr fp f
return $! Bi.PS fp 0 l
foreign import ccall unsafe "sodium.h crypto_sign_detached"
c_crypto_sign_detached
:: CUString
-> F.Ptr T.CULLong
-> CUString
-> T.CULLong
-> CUString
-> IO T.CInt
sign :: B.ByteString -> B.ByteString -> Either Int B.ByteString
sign message secretKey = unsafePerformIO $
useAsCString message $ \messagePtr ->
useAsCString secretKey $ \secretKeyPtr ->
F.allocaBytes crypto_sign_BYTES $ \sig -> do
status <- c_crypto_sign_detached
sig
F.nullPtr
messagePtr
(fromIntegral $ B.length message)
secretKeyPtr
case status of
0 -> do
packed <- packCString crypto_sign_BYTES $ F.castPtr sig
return $ Right packed
errCode -> return $ Left $ fromIntegral errCode
foreign import ccall unsafe "sodium.h sodium_init"
c_sodium_init :: IO T.CInt
type CUString = F.Ptr T.CUChar
useAsCString :: B.ByteString -> (CUString -> IO a) -> IO a
useAsCString (Bi.PS fp o l) action =
F.allocaBytes (l+1) $ \buf ->
F.withForeignPtr fp $ \p -> do
Bi.memcpy buf (p `F.plusPtr` o) (fromIntegral l)
F.pokeByteOff buf l (0::F.Word8)
action (F.castPtr buf)
C代码如下:
#include <sodium.h>
#include <stdio.h>
int main (void)
{
if (sodium_init() < 0) {
printf("could not initialize");
}
unsigned char message[4] = "test";
unsigned char sk[32] = {0x34, 0xEB, 0xA3, 0x9F, 0xC5, 0xA1, 0xB4,
0x1D, 0x64, 0x12, 0xCE, 0xC3, 0xD2, 0x0A,
0x7F, 0xA8, 0x24, 0x24, 0x2A, 0xDC, 0x1E,
0x6C, 0x04, 0x48, 0xCE, 0x91, 0xB3, 0xC4,
0x84, 0xCC, 0x7A, 0xC6};
unsigned char sig[crypto_sign_BYTES];
crypto_sign_detached(sig, NULL, message, 4, sk);
int i;
for (i=0; i<64; i++)
{
printf("%02X", sig[i]);
}
printf("\n");
}
Haskell 输出为:
"17aa8f20476a8e70f860707b8fd7f8f78ffd778690a755ddf3a432a81786ea3dea9efa2028d8896efaf1efe428781b3af83ab0450fc94a2ae6ec2c1a34ca830e"
C输出为:
17AA8F20476A8E70F860707B8FD7F8F78FFD778690A755DDF3A432A81786EA3D4B54ECDA53E7774C43BE7098A96C39A59664953E40AFFB54ACB84E09CBFF4E07
为什么这些签名只匹配 64 字节的前 32 个字节?如果有帮助,我已将代码放在 Github.
crypto_sign_SECRETKEYBYTES
是 64,而不是 32。将你的密钥设置这么长,C 和 Haskell 代码将相互一致。