an atproto pds written in F# (.NET 9) 馃
pds fsharp giraffe dotnet atproto
at main 3.2 kB view raw
1namespace PDSharp.Core 2 3open System 4open System.Text 5open Org.BouncyCastle.Crypto.Digests 6open Org.BouncyCastle.Crypto.Signers 7open Org.BouncyCastle.Crypto.Parameters 8open Org.BouncyCastle.Math 9open Org.BouncyCastle.Asn1.X9 10open Org.BouncyCastle.Crypto.Generators 11open Org.BouncyCastle.Security 12open Org.BouncyCastle.Asn1.Sec 13 14module Crypto = 15 let sha256 (data : byte[]) : byte[] = 16 let digest = Sha256Digest() 17 digest.BlockUpdate(data, 0, data.Length) 18 let size = digest.GetDigestSize() 19 let result = Array.zeroCreate<byte> size 20 digest.DoFinal(result, 0) |> ignore 21 result 22 23 let sha256Str (input : string) : byte[] = sha256 (Encoding.UTF8.GetBytes(input)) 24 25 type Curve = 26 | P256 27 | K256 28 29 let getCurveParams (curve : Curve) = 30 match curve with 31 | P256 -> ECNamedCurveTable.GetByName("secp256r1") 32 | K256 -> ECNamedCurveTable.GetByName("secp256k1") 33 34 let getDomainParams (curve : Curve) = 35 let ecP = getCurveParams curve 36 ECDomainParameters(ecP.Curve, ecP.G, ecP.N, ecP.H, ecP.GetSeed()) 37 38 type EcKeyPair = { 39 PrivateKey : ECPrivateKeyParameters option 40 PublicKey : ECPublicKeyParameters 41 Curve : Curve 42 } 43 44 let generateKey (curve : Curve) : EcKeyPair = 45 let domainParams = getDomainParams curve 46 let genParam = ECKeyGenerationParameters(domainParams, SecureRandom()) 47 let generator = ECKeyPairGenerator() 48 generator.Init(genParam) 49 let pair = generator.GenerateKeyPair() 50 51 { 52 PrivateKey = Some(pair.Private :?> ECPrivateKeyParameters) 53 PublicKey = (pair.Public :?> ECPublicKeyParameters) 54 Curve = curve 55 } 56 57 let enforceLowS (s : BigInteger) (n : BigInteger) : BigInteger = 58 let halfN = n.ShiftRight(1) 59 if s.CompareTo(halfN) > 0 then n.Subtract(s) else s 60 61 let sign (key : EcKeyPair) (digest : byte[]) : byte[] = 62 match key.PrivateKey with 63 | None -> failwith "Private key required for signing" 64 | Some privParams -> 65 let signer = ECDsaSigner() 66 signer.Init(true, privParams) 67 let inputs = digest 68 let signature = signer.GenerateSignature(inputs) 69 let r = signature.[0] 70 let s = signature.[1] 71 72 let n = privParams.Parameters.N 73 let canonicalS = enforceLowS s n 74 75 let to32Bytes (bi : BigInteger) = 76 let bytes = bi.ToByteArrayUnsigned() 77 78 if bytes.Length > 32 then 79 failwith "Signature component too large" 80 81 let padded = Array.zeroCreate<byte> 32 82 Array.Copy(bytes, 0, padded, 32 - bytes.Length, bytes.Length) 83 padded 84 85 let rBytes = to32Bytes r 86 let sBytes = to32Bytes canonicalS 87 Array.append rBytes sBytes 88 89 let verify (key : EcKeyPair) (digest : byte[]) (signature : byte[]) : bool = 90 if signature.Length <> 64 then 91 false 92 else 93 let rBytes = Array.sub signature 0 32 94 let sBytes = Array.sub signature 32 32 95 96 let r = BigInteger(1, rBytes) 97 let s = BigInteger(1, sBytes) 98 99 let domainParams = key.PublicKey.Parameters 100 let n = domainParams.N 101 let halfN = n.ShiftRight(1) 102 103 if s.CompareTo(halfN) > 0 then 104 false 105 else 106 let signer = ECDsaSigner() 107 signer.Init(false, key.PublicKey) 108 signer.VerifySignature(digest, r, s)