ncasn

package module
v0.0.0-...-f89b3ad Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 26, 2026 License: GPL-3.0 Imports: 17 Imported by: 0

README

An ASN.1 Unaligned Packed Encoding Rules encoding for Namecoin data

Usage

An ncasn.Zone object represents a single name in the d/ namespace, containing a slice of ncasn.Records as well as WHOIS data. Zones can then be serialized into ASN.1 UPER through ncasn.MarshalRecords(). For encoding efficiency, some records make some reasonable assumptions (mainly involving restricting certain insecure record data), which are documented through comments.

Efficiency

Storage usage was benchmarked against proposed Tor CAA and IETF CBOR DNS standards as well as the JSON-based Namecoin format, based on a sample of private DNS zones and (in much larger numbers) the Namecoin blockchain, the blockchain dumping and benchmarking code can be found in the benchmark module.

Caveats

Some (hopefully reasonable) assumptions had to be made in order to achieve good coverage of the data, specifically, some record types had to be encoded in unspecified ways in some formats. For example, IPNS records are encoded as the raw key bytes in CBOR, and textual representations were used in the Tor format. Some record types were still excluded entirely (e.g., Namecoin imports), but did not meaningfully affect the results (see below).

Results

These numbers are subject to change (hopefully improve!) as the format is updated and the benchmark may have to be tweaked, but the following comparisons to different formats can currently be made (as a ratio with our storage usage as the denominator):

Format: Size ratio | Record coverage | Record count
JSON: 4.28 | 1.00 | 233598
Tor: 1.77 | 0.89 | 207121
CBOR: 1.20 | 0.98 | 228794

Documentation

Index

Constants

This section is empty.

Variables

View Source
var DS_DIGEST_TYPES = []uint8{
	2, 4, 7, 8,
}
View Source
var DS_KEY_ALGOS = []uint8{
	15, 16, 18, 19,
}
View Source
var HYPHA_ENCODING = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~-").WithPadding(base64.NoPadding)
View Source
var I2P_ENCODING = base32.StdEncoding.WithPadding(base32.NoPadding)
View Source
var UNKNOWN_IPNS_HEADER = []byte{0x08, 0x01, 0x12, 0x20}

Functions

func GetChoice

func GetChoice(ref reflect.Value) uint8

func GetKeyAlgorithmIndex

func GetKeyAlgorithmIndex(id uint8) int8

func I2pRecordFromDomain

func I2pRecordFromDomain(domain string) (*I2PB32, *I2PEB32, error)

func MarshalRecords

func MarshalRecords(zone Zone) ([]byte, error)

func PostProcessIpv6

func PostProcessIpv6(records []Record)

func PreProcessIpv6

func PreProcessIpv6(records []Record)

Types

type A

type A struct {
	Target []byte `asn1:"size:4"`
}

type AAAA

type AAAA struct {
	ZeroOffset *uint8 `asn1:"optional,size:0..13"`
	Bytes      []byte `asn1:"size:0..16"`
}

Use Bytes, the offset must be exported for go-asn to handle it but it is useless after processing.

type DS

type DS struct {
	KeyTag uint16 `asn1:"size:0..65535"`
	// Use GetKeyAlgorithm().
	AlgorithmIndex uint8 `asn1:"size:0..3"`
	Digest         DsDigestUnion
}

func (*DS) GetKeyAlgorithm

func (record *DS) GetKeyAlgorithm() uint8

type DsDigestUnion

type DsDigestUnion struct {
	Sha256      *[]byte `asn1:"choice:0,size:32"`
	Sha384      *[]byte `asn1:"choice:1,size:48"`
	Unassigned7 *[]byte `asn1:"choice:2,size:32..64"`
	Unassigned8 *[]byte `asn1:"choice:3,size:32..64"`
}

func (*DsDigestUnion) GetType

func (digest *DsDigestUnion) GetType() uint8

type Generic

type Generic struct {
	Type   uint16 `asn1:"size:0..65535"`
	Target string `asn1:"ia5string,size:0..255"`
}

type HyphanetUSK

type HyphanetUSK struct {
	KeyHash []byte `asn1:"size:32"`
	Key     []byte `asn1:"size:32"`
	Extra   []byte `asn1:"size:5"`
	Name    string `asn1:"utf8string,size:1..512"`
	Edition int64  `asn1:"size:-9223372036854775808..9223372036854775807"`
}

func USKRecordFromKey

func USKRecordFromKey(key string) (*HyphanetUSK, error)

func (*HyphanetUSK) ToKey

func (record *HyphanetUSK) ToKey() string

type I2PB32

type I2PB32 struct {
	Bytes []byte `asn1:"size:32"`
}

func (*I2PB32) ToDomain

func (record *I2PB32) ToDomain() string

type I2PEB32

type I2PEB32 struct {
	PublicSigType  uint8 `asn1:"size:0..31"` // "Don't expect 2-byte sigtypes to ever happen, we're only up to 13. No need to implement now."
	BlindedSigType uint8 `asn1:"size:0..31"`
	Secret         bool
	ClientAuth     bool
	Key            []byte `asn1:"size:32"` // More may be required for some SigTypes?
}

https://i2p.net/en/docs/overview/naming/#extended-base32-names

func (*I2PEB32) ToDomain

func (record *I2PEB32) ToDomain() string

type IPNS

type IPNS struct {
	// Assumed to be Ed25519.
	Key []byte `asn1:"size:32"`
}

func IpnsToRecord

func IpnsToRecord(key string) (*IPNS, error)

func (*IPNS) ToString

func (record *IPNS) ToString() string

type Import

type Import struct {
	Name      string  `asn1:"ia5string,size:3..63"`
	Subdomain *string `asn1:"optional,ia5string,size:0..249"`
}

type LOC

type LOC struct {
	Lat  uint32 `asn1:"size:0..4294967185"`
	Long uint32 `asn1:"size:0..4294967185"`

	// Centimeters, 0 refers to -100000m.
	Altitude uint32 `asn1:"size:0..4294967185"`

	Size *LOCExponent `asn1:"optional"`
	// Must be nil if Size is nil
	HorizontalPrecision *LOCExponent `asn1:"optional"`
	// Must be nil if HorizontalPrecision is nil
	VerticalPrecision *LOCExponent `asn1:"optional"`
}

Lat and Long are expressed as milliarcseconds, this matches the DNS wire format and should be more efficient than encoding degrees/minutes/seconds individually when seconds are included.

func StringToLoc

func StringToLoc(value string) (*LOC, error)

Formatted as in a zone file

func (*LOC) String

func (record *LOC) String() string

Formatted as in a zone file

type LOCExponent

type LOCExponent struct {
	Mantissa uint8 `asn1:"size:0..9"`
	Exp      uint8 `asn1:"size:0..9"`
}

type MX

type MX struct {
	Priority uint16 `asn1:"size:0..65535"`
	Target   string `asn1:"ia5string,size:0..255"`
}

type OnionV3

type OnionV3 struct {
	// The version byte is omitted as upgrading may require a schema change anyway, append 0x03 before base32 encoding. The checksum bytes are also omitted.
	Bytes []byte `asn1:"size:32"`
}

func OnionRecordFromDomain

func OnionRecordFromDomain(domain string) (*OnionV3, error)

func (*OnionV3) ToDomain

func (record *OnionV3) ToDomain() string

type ParsingPlaceholder

type ParsingPlaceholder struct {
	Info *Whois `asn1:"optional"`
}

This is used in order to avoid manually handling data before Zone.Records, Zone cannot be (un)marshalled directly due to relying on consuming all data to determine the length of Zone.Records, which go-asn cannot do.

type Record

type Record struct {
	// Relative to the base domain, 249 = 255 - 6 (.x.bit).
	// Always non-nil after being unmarshalled, the base domain is represented as an empty string. During (un)marshalling, nils are used to refer to the previous entry.
	Name       *string `asn1:"optional,ia5string,size:0..249"`
	RecordData RecordUnion
}

type RecordUnion

type RecordUnion struct {
	A     *A     `asn1:"choice:0"`
	AAAA  *AAAA  `asn1:"choice:1"`
	Srv   *SRV   `asn1:"choice:2"`
	Ds    *DS    `asn1:"choice:3"`
	Txt   *TXT   `asn1:"choice:4"`
	Tlsa  *TLSA  `asn1:"choice:5"`
	Loc   *LOC   `asn1:"choice:6"`
	Mx    *MX    `asn1:"choice:7"`
	Sshfp *SSHFP `asn1:"choice:8"`
	// This is analogous to a DNS ALIAS record, Namecoin's aliases are analogous to DNS CNAME records, which can be specified in Generic instead.
	Alias    *string      `asn1:"choice:9,ia5string,size:0..255"`
	Onion    *OnionV3     `asn1:"choice:10"`
	I2p      *I2PB32      `asn1:"choice:11"`
	I2pLs2   *I2PEB32     `asn1:"choice:12"`
	Generic  *Generic     `asn1:"choice:13"`
	Import   *Import      `asn1:"choice:14"`
	Ipns     *IPNS        `asn1:"choice:15"`
	Hyphanet *HyphanetUSK `asn1:"choice:16"`
}

type SRV

type SRV struct {
	Priority uint16  `asn1:"size:0..65535"`
	Weight   *uint16 `asn1:"optional,size:0..65535"`
	Port     uint16  `asn1:"size:0..65535"`
	Target   string  `asn1:"ia5string,size:0..255"`
}

type SSHFP

type SSHFP struct {
	KeyAlgoIndex uint8 `asn1:"size:0..2"`
	// Assumed to be SHA-256.
	Fingerprint []byte `asn1:"size:32"`
}

func (*SSHFP) GetKeyAlgo

func (record *SSHFP) GetKeyAlgo() uint8

type TLSA

type TLSA struct {
	Selector        uint8 `asn1:"size:0..1"`
	AssociationData TlsaUnion
}

Always assumed to be DANE-TA

type TXT

type TXT struct {
	// May contain quoted strings up to 255 characters each.
	Content string `asn1:"ia5string,size:0..4096"`
}

type TlsaUnion

type TlsaUnion struct {
	Sha256      *[]byte `asn1:"choice:0,size:32"`
	Sha512      *[]byte `asn1:"choice:1,size:64"`
	Unassigned0 *[]byte `asn1:"choice:2,size:32..64"`
	Unassigned1 *[]byte `asn1:"choice:3,size:32..64"`
}

func (*TlsaUnion) GetMatchingType

func (union *TlsaUnion) GetMatchingType() uint8

type Whois

type Whois struct {
	Fields *WhoisFields `asn1:"choice:0"`
	Entity *string      `asn1:"choice:1,ia5string,size:0..255"`
}

type WhoisFields

type WhoisFields struct {
	Registrant  *string `asn1:"ia5string,size:0..255"`
	Registrar   *string `asn1:"ia5string,size:0..255"`
	AdmContact  *string `asn1:"ia5string,size:0..255"`
	TechContact *string `asn1:"ia5string,size:0..255"`
}

type Zone

type Zone struct {
	Info    *Whois
	Records []Record
}

func UnmarshalRecords

func UnmarshalRecords(data []byte) (*Zone, error)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL