Ich bin derzeit Bindungen zu einer Krypto-Bibliothek zu schreiben, die eine Funktion zum Erzeugen von Schlüsselpaaren aussetzt:Wie erstellt man zwei ByteStrings, die diese externe Bibliothek API aufrufen?
const size_t PUBLICKEYBYTES = 32;
const size_t SECRETKEYBYTES = 32;
int random_keypair(unsigned char pk[PUBLICKEYBYTES],
unsigned char sk[SECRETKEYBYTES]);
Diese Funktion generiert zufällig einen geheimen Schlüssel, berechnet die entsprechenden öffentlichen Schlüssel und stellt die Ergebnisse in pk
und sk
.
Wenn ich gerade eine ByteString
zurückgegeben habe, habe ich festgestellt, dass der einfachste Weg ist, create :: Int -> (Ptr Word8 -> IO()) -> IO ByteString
von Data.ByteString.Internal
zu verwenden. Diese Funktion kann jedoch nicht zwei ByteStrings
gleichzeitig erstellen.
Mein erster Ansatz war zu schreiben, so etwas wie:
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)
Allerdings scheint dies nicht mit GHC 7.10.2 zu arbeiten. Beim Ausführen der Testsuite stelle ich fest, dass ich scheinbar die ByteString
s zwischen Funktionsaufrufen teile, was dazu führt, dass die Verschlüsselung/Entschlüsselung fehlschlägt und falsche Ergebnisse liefert.
Ich habe es geschafft, durch Definieren meine eigene Funktion, um das Problem zu umgehen:
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)
und dessen Verwendung mag:
randomKeypair = fmap (PublicKey *** SecretKey) $
createWithResult publicKeyBytes $ \ppk ->
B.create secretKeyBytes $ \psk ->
void $ c_random_keypair ppk psk
Dies scheint zu funktionieren, alle Tests bestanden.
Meine Frage ist, was genau ist die Semantik, wenn es um die gemeinsame und referentielle Transparenz geht, wenn es um die IO
Monade geht?
Meine Intuition sagte mir (falsch), dass ich das Problem auf die erste Weise lösen könnte, aber anscheinend konnte ich nicht. Was ich glaube, war, dass der Optimierer sah, dass die let
-Statements in Top-Level-Definitionen fließen konnten, und das war der Grund, warum ich diese Probleme bekam.
Also, wenn ByteString eine 'replicateIO :: Int -> Word8 -> IO ByteString' Funktion, die 'unsafePerformIO' nicht aufgerufen hat, hätte es funktioniert? – dnaq