on d1663a6 help sign in
skrepka/client/Crypto.fs
64 lines 3.1 kB history raw
1namespace Skrepka
3open System
4open System.Text
5open System.Security.Cryptography
6open Sodium
8module Crypto =
10 let toHex (bytes: byte[]) : string =
11 Convert.ToHexString(bytes).ToLowerInvariant()
13 let fromHex (hex: string) : byte[] =
14 Convert.FromHexString(hex)
16 let generateIdentity () : byte[] * byte[] =
17 let kp = PublicKeyAuth.GenerateKeyPair()
18 (kp.PrivateKey, kp.PublicKey)
20 let signChallenge (privKey: byte[]) (challenge: string) : string =
21 let message = Encoding.UTF8.GetBytes(challenge)
22 PublicKeyAuth.SignDetached(message, privKey) |> toHex
24 let encrypt (senderPrivKey: byte[]) (senderPubKey: byte[]) (recipientEd25519Pub: byte[]) (plaintext: string) : byte[] =
25 let ephKp = PublicKeyBox.GenerateKeyPair()
26 let recipientX25519Pub =
27 PublicKeyAuth.ConvertEd25519PublicKeyToCurve25519PublicKey(recipientEd25519Pub)
28 let rawSecret = ScalarMult.Mult(ephKp.PrivateKey, recipientX25519Pub)
29 let salt = Array.append ephKp.PublicKey recipientX25519Pub
30 let info = Encoding.UTF8.GetBytes("skrepka-v1")
31 let key = HKDF.DeriveKey(HashAlgorithmName.SHA256, rawSecret, 32, salt, info)
32 let nonce = SodiumCore.GetRandomBytes(24)
33 let ptBytes = Encoding.UTF8.GetBytes(plaintext)
34 let ciphertext = SecretAeadXChaCha20Poly1305.Encrypt(ptBytes, nonce, key)
35 let signature = PublicKeyAuth.SignDetached(ciphertext, senderPrivKey)
36 Array.concat [| ephKp.PublicKey; nonce; ciphertext; senderPubKey; signature |]
38 let decrypt (recipientPrivKey: byte[]) (blob: byte[]) : (string * string) option =
39 if blob.Length < 168 then
40 None
41 else
42 try
43 let ephPub = blob.[0..31]
44 let nonce = blob.[32..55]
45 let senderPub = blob.[blob.Length - 96 .. blob.Length - 65]
46 let signature = blob.[blob.Length - 64 .. blob.Length - 1]
47 let ciphertext = blob.[56 .. blob.Length - 97]
49 if not (PublicKeyAuth.VerifyDetached(signature, ciphertext, senderPub)) then
50 None
51 else
52 let recipientX25519Priv =
53 PublicKeyAuth.ConvertEd25519SecretKeyToCurve25519SecretKey(recipientPrivKey)
54 let rawSecret = ScalarMult.Mult(recipientX25519Priv, ephPub)
55 let recipientEd25519Pub = recipientPrivKey.[32..63]
56 let recipientX25519Pub =
57 PublicKeyAuth.ConvertEd25519PublicKeyToCurve25519PublicKey(recipientEd25519Pub)
58 let salt = Array.append ephPub recipientX25519Pub
59 let info = Encoding.UTF8.GetBytes("skrepka-v1")
60 let key = HKDF.DeriveKey(HashAlgorithmName.SHA256, rawSecret, 32, salt, info)
61 let plaintext = SecretAeadXChaCha20Poly1305.Decrypt(ciphertext, nonce, key)
62 Some(Encoding.UTF8.GetString(plaintext), toHex senderPub)
63 with _ ->
64 None