wireform-asn1
wireform-asn1 implements ASN.1 Basic Encoding Rules (BER) and Distinguished
Encoding Rules (DER) per ITU-T X.690.
ASN.1 underpins X.509 certificates, LDAP, SNMP, Kerberos, and smart-card
protocols. DER is the canonical subset required for cryptographic uses. Use this
package when you need standards-compliant DER output, ASN.1 module parsing, or
typed encoding of certificate and telecom structures.
Key features
Section titled “Key features”- Typeclass API via
ToASN1andFromASN1withencodeASN1/decodeASN1 - ITU-T X.690 DER encoder producing canonical byte sequences
- ASN.1 module definition parser for
.asn1schema files - Schema AST with tagging modes (Automatic, Implicit, Explicit) and constraints
- Codegen and QuasiQuoter for inline
[asn1| ... |]modules - Template Haskell deriver with
asn1ImplicitTagandasn1ExplicitTagmodifiers
Basic usage
Section titled “Basic usage”Derive instances for your record types, then encode to DER and decode back:
{-# LANGUAGE DerivingStrategies #-}{-# LANGUAGE TemplateHaskell #-}
import ASN1.Derive ( ToASN1, FromASN1 , encodeASN1, decodeASN1 , deriveASN1 , asn1ImplicitTag )import Data.Text (Text)import GHC.Generics (Generic)
data Person = Person { personId :: !Int , personName :: !Text , personAdmin :: !Bool } deriving stock (Show, Eq, Generic)
{-# ANN personAdmin (asn1ImplicitTag 0) #-}
$(deriveASN1 ''Person)
carol :: Personcarol = Person 1 "Carol" True
encodePerson :: Person -> ByteStringencodePerson = encodeASN1
decodePerson :: ByteString -> Either String PersondecodePerson = decodeASN1
roundTrip :: Either String PersonroundTrip = decodePerson (encodePerson carol)For certificate-shaped structures, work directly with the dynamic Value ADT
when you need fine-grained control over tagging:
import qualified Data.Vector as Vimport qualified ASN1.Value as AVimport qualified ASN1.Encode as AEimport qualified ASN1.Decode as AD
tbsPrefix :: AV.ValuetbsPrefix = AV.Sequence $ V.fromList [ AV.Tagged AV.ContextSpecific 0 (AV.Integer 2) -- X.509 version v3 , AV.Integer 12345 -- serial number ]
derBytes :: ByteStringderBytes = AE.encode tbsPrefix
parseDer :: Either String AV.ValueparseDer = AD.decode derBytesGenerate types from ASN.1 modules:
{-# LANGUAGE TemplateHaskell #-}import ASN1.QQ (asn1)
[asn1| Person DEFINITIONS ::= BEGIN Person ::= SEQUENCE { id INTEGER, name UTF8String, admin BOOLEAN } END|]wireform-gen asn1 -i module.asn1 -o src/Gen/Performance
Section titled “Performance”DER encode/decode
Section titled “DER encode/decode”| Operation | encode | decode | ratio |
|---|---|---|---|
| single Subject | 140 ns | 115 ns | 0.82x |
| [Subject] x 100 | 17260 ns | 12699 ns | 0.74x |
Last run 2026-06-27 11:35:54 UTC. ghc-9.8.4 on darwin-aarch64, criterion 1.6.5.
Sub-microsecond per-record encode and decode. The DER codec is allocation-lean with unboxed field codecs.
The chart and table above are regenerated by wireform-stats from wireform-asn1/bench-results/summary/asn1-encode-decode.json — the same source the README chart is built from.
Notable modules
Section titled “Notable modules”| Module | Purpose |
|---|---|
ASN1.Derive | ToASN1 / FromASN1, encodeASN1 / decodeASN1, deriveASN1 |
ASN1.Encode / ASN1.Decode | BER/DER wire encoder and decoder |
ASN1.Value | Dynamic untyped Value ADT (Sequence, Tagged, Integer, etc.) |
ASN1.Schema / ASN1.Parser | Schema AST and module definition parser |
ASN1.CodeGen / ASN1.QQ | Haskell codegen and quasiquoter |