Attualmente sto scrivendo attacchi a una libreria crittografica che espone una funzione per coppie di chiavi che generano:Come creare due ByteString chiamando questa API della libreria esterna?
const size_t PUBLICKEYBYTES = 32;
const size_t SECRETKEYBYTES = 32;
int random_keypair(unsigned char pk[PUBLICKEYBYTES],
unsigned char sk[SECRETKEYBYTES]);
Questa funzione genera in modo casuale una chiave segreta, calcola la corrispondente chiave pubblica e mette il risultato in pk
e sk
.
Appena restituito uno ByteString
ho trovato che il modo più semplice è utilizzare create :: Int -> (Ptr Word8 -> IO()) -> IO ByteString
da Data.ByteString.Internal
. Tuttavia, quella funzione non può creare due ByteStrings
allo stesso tempo.
Il mio primo approccio è stato quello di scrivere qualcosa di simile:
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)
Tuttavia, questo non sembra funzionare con GHC 7.10.2. Quando eseguo la suite di test, trovo che sembra che abbia condiviso le chiamate di funzioni tra ByteString
s, causando errori di codifica/decrittografia e risultati errati.
sono riuscito a risolvere il problema definendo la mia propria funzione:
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)
e di utilizzarlo come:
randomKeypair = fmap (PublicKey *** SecretKey) $
createWithResult publicKeyBytes $ \ppk ->
B.create secretKeyBytes $ \psk ->
void $ c_random_keypair ppk psk
Questo sembra funzionare, tutti i test passano.
La mia domanda è, quali sono esattamente la semantica quando si tratta di condivisione e trasparenza referenziale quando si parla della monade IO
?
La mia intuizione mi ha detto (erroneamente) che potevo risolvere il problema nel primo modo, ma a quanto pare non potevo. Quello che credo stia accadendo è che l'ottimizzatore ha visto che gli statement let
potevano essere spostati in alto nelle definizioni di livello superiore, e questa era la ragione per cui avevo questi problemi.
Quindi se ByteString avesse esposto una funzione 'replicateIO :: Int -> Word8 -> IO ByteString' che non chiamava 'unsafePerformIO', avrebbe funzionato? – dnaq