Asn.1 BER and DER encoding library for golang.


WARNING

This repo has been archived!

NO further developement will be made in the foreseen future.


Build Status Go Report Card GoDoc

asn1

-- import "github.com/PromonLogicalis/asn1"

Package asn1 implements encoding and decoding of ASN.1 data structures using both Basic Encoding Rules (BER) or its subset, the Distinguished Encoding Rules (BER).

This package is highly inspired by the Go standard package "encoding/asn1" while supporting additional features such as BER encoding and decoding and ASN.1 CHOICE types.

By default and for convenience the package uses DER for encoding and BER for decoding. However it's possible to use a Context object to set the desired encoding and decoding rules as well other options.

Restrictions:

  • BER allows STRING types, such as OCTET STRING and BIT STRING, to be encoded as constructed types containing inner elements that should be concatenated to form the complete string. The package does not support that, but in the future decoding of constructed strings should be included.

Usage

func Decode

func Decode(data []byte, obj interface{}) (rest []byte, err error)

Decode parses the given BER data into obj. The argument obj should be a reference to the value that will hold the parsed data. Decode uses a default Context and is equivalent to:

rest, err := asn1.NewContext().Decode(data, &obj)

func DecodeWithOptions

func DecodeWithOptions(data []byte, obj interface{}, options string) (rest []byte, err error)

DecodeWithOptions parses the given BER data into obj using the additional options. The argument obj should be a reference to the value that will hold the parsed data. Decode uses a default Context and is equivalent to:

rest, err := asn1.NewContext().DecodeWithOptions(data, &obj, options)

func Encode

func Encode(obj interface{}) (data []byte, err error)

Encode returns the DER encoding of obj. Encode uses a default Context and it's equivalent to:

data, err = asn1.NewContext().Encode(obj)

func EncodeWithOptions

func EncodeWithOptions(obj interface{}, options string) (data []byte, err error)

EncodeWithOptions returns the DER encoding of obj using additional options. EncodeWithOptions uses a default Context and it's equivalent to:

data, err = asn1.NewContext().EncodeWithOptions(obj, options)

type Choice

type Choice struct {
	Type    reflect.Type
	Options string
}

Choice represents one option available for a CHOICE element.

type Context

type Context struct {
}

Context keeps options that affect the ASN.1 encoding and decoding

Use the NewContext() function to create a new Context instance:

ctx := ber.NewContext()
// Set options, ex:
ctx.SetDer(true, true)
// And call decode or encode functions
bytes, err := ctx.EncodeWithOptions(value, "explicit,application,tag:5")
...

func NewContext

func NewContext() *Context

NewContext creates and initializes a new context. The returned Context does not contains any registered choice and it's set to DER encoding and BER decoding.

func (*Context) AddChoice

func (ctx *Context) AddChoice(choice string, entries []Choice) error

AddChoice registers a list of types as options to a given choice.

The string choice refers to a choice name defined into an element via additional options for DecodeWithOptions and EncodeWithOptions of via struct tags.

For example, considering that a field "Value" can be an INTEGER or an OCTET STRING indicating two types of errors, each error with a different tag number, the following can be used:

// Error types
type SimpleError string
type ComplextError string
// The main object
type SomeSequence struct {
	// ...
	Value	interface{}	`asn1:"choice:value"`
	// ...
}
// A Context with the registered choices
ctx := asn1.NewContext()
ctx.AddChoice("value", []asn1.Choice {
	{
		Type: reflect.TypeOf(int(0)),
	},
	{
		Type: reflect.TypeOf(SimpleError("")),
		Options: "tag:1",
	},
	{
		Type: reflect.TypeOf(ComplextError("")),
		Options: "tag:2",
	},
})

Some important notes:

  1. Any choice value must be an interface. During decoding the necessary type will be allocated to keep the parsed value.

  2. The INTEGER type will be encoded using its default class and tag number and so it's not necessary to specify any Options for it.

  3. The two error types in our example are encoded as strings, in order to make possible to differentiate both types during encoding they actually need to be different types. This is solved by defining two alias types: SimpleError and ComplextError.

  4. Since both errors use the same encoding type, ASN.1 says they must have distinguished tags. For that, the appropriate tag is defined for each type.

To encode a choice value, all that is necessary is to set the choice field with the proper object. To decode a choice value, a type switch can be used to determine which type was used.

func (*Context) Decode

func (ctx *Context) Decode(data []byte, obj interface{}) (rest []byte, err error)

Decode parses the given data into obj. The argument obj should be a reference to the value that will hold the parsed data.

See (*Context).DecodeWithOptions() for further details.

func (*Context) DecodeWithOptions

func (ctx *Context) DecodeWithOptions(data []byte, obj interface{}, options string) (rest []byte, err error)

DecodeWithOptions parses the given data into obj using the additional options. The argument obj should be a reference to the value that will hold the parsed data.

It uses the reflect package to inspect obj and because of that only exported struct fields (those that start with a capital letter) are considered.

The Context object defines the decoding rules (BER or DER) and the types available for CHOICE types.

The asn1 package maps Go types to ASN.1 data structures. The package also provides types to specific ASN.1 data structures. The default mapping is shown in the table below:

Go type                | ASN.1 universal tag
-----------------------|--------------------
bool                   | BOOLEAN
All int and uint types | INTEGER
*big.Int               | INTEGER
string                 | OCTET STRING
[]byte                 | OCTET STRING
asn1.Oid               | OBJECT INDETIFIER
asn1.Null              | NULL
Any array or slice     | SEQUENCE OF
Any struct             | SEQUENCE

Arrays and slices are decoded using different rules. A slice is always appended while an array requires an exact number of elements, otherwise a ParseError is returned.

The default mapping can be changed using options provided in the argument options (for the root value) or via struct tags for struct fields. Struct tags use the namei space "asn1".

The available options for encoding and decoding are:

tag

This option requires an numeric argument (ie: "tag:1") and indicates that a element is encoded and decoded as a context specific element with the given tag number. The context specific class can be overridden with the options "application" or "universal".

universal

Sets the tag class to universal. Requires "tag".

application

Sets the tag class to application. Requires "tag".

explicit

Indicates the element is encoded with an enclosing tag. It's usually used in conjunction with "tag".

optional

Indicates that an element can be suppressed.

A missing element that is not marked with "optional" or "default" causes a ParseError to be returned during decoding. A missing element marked as optional is left untouched.

During encoding, a zero value elements is suppressed from output if it's marked as optional.

default

This option is handled similarly to the "optional" option but requires a numeric argument (ie: "default:1").

A missing element that is marked with "default" is set to the given default value during decoding.

A zero value marked with "default" is suppressed from output when encoding is set to DER or is encoded with the given default value when encoding is set to BER.

indefinite

This option is used only during encoding and causes a constructed element to be encoded using the indefinite form.

choice

Indicates that an element can be of one of several types as defined by (*Context).AddChoice()

set

Indicates that a struct, array or slice should be encoded and decoded as a SET instead of a SEQUENCE.

It also affects the way that structs are encoded and decoded in DER. A struct marked with "set" has its fields always encoded in the ascending order of its tags, instead of following the order that the fields are defined in the struct.

Similarly, a struct marked with "set" always enforces that same order when decoding in DER.

func (*Context) Encode

func (ctx *Context) Encode(obj interface{}) (data []byte, err error)

Encode returns the ASN.1 encoding of obj.

See (*Context).EncodeWithOptions() for further details.

func (*Context) EncodeWithOptions

func (ctx *Context) EncodeWithOptions(obj interface{}, options string) (data []byte, err error)

EncodeWithOptions returns the ASN.1 encoding of obj using additional options.

See (*Context).DecodeWithOptions() for further details regarding types and options.

func (*Context) SetDer

func (ctx *Context) SetDer(encoding bool, decoding bool)

SetDer sets DER mode for encofing and decoding.

func (*Context) SetLogger

func (ctx *Context) SetLogger(logger *log.Logger)

SetLogger defines the logger used.

type Null

type Null struct{}

Null is used to encode and decode ASN.1 NULLs.

type Oid

type Oid []uint

Oid is used to encode and decode ASN.1 OBJECT IDENTIFIERs.

func (Oid) Cmp

func (oid Oid) Cmp(other Oid) int

Cmp returns zero if both Oids are the same, a negative value if oid lexicographically precedes other and a positive value otherwise.

func (Oid) String

func (oid Oid) String() string

String returns the dotted representation of oid.

type ParseError

type ParseError struct {
	Msg string
}

ParseError is returned by the package to indicate that the given data is invalid.

func (*ParseError) Error

func (e *ParseError) Error() string

Error returns the error message of a ParseError.

type SyntaxError

type SyntaxError struct {
	Msg string
}

SyntaxError is returned by the package to indicate that the given value or struct is invalid.

func (*SyntaxError) Error

func (e *SyntaxError) Error() string

Error returns the error message of a SyntaxError.

Comments
  • Unable to run application

    Unable to run application

    Hello,

    I am new to the go and apologize in advance if I am asking a silly question. I have downloaded and trying run 'go build' and I seem to be able to successfully build. But I do not see any executable to run. Do we have an application to test this library with takes ber/der files as input?

  • Unsure on adding choice

    Unsure on adding choice

    I have another example I don't know how to add. My format description shows (shortened example)

    UMTSGSMPLMNCallDataRecord ::= SEQUENCE{ callModule CHOICE{ transit [0] IMPLICIT Transit, mSOriginating [1] IMPLICIT MSOriginating } } Transit ::= SET{ tAC [0] IMPLICIT TAC OPTIONAL, callIdentificationNumber [1] IMPLICIT CallIDNumber OPTIONAL }

    I translated this to ....

    ctx.AddChoice("CallModule", []asn1.Choice{ { Type: reflect.TypeOf(Transit), Options: "implicit,tag:0", }, { Type: MSOriginating, Options: "implicit,tag:1", } } type Transit struct { TAC []byte asn1:"implicit,optional,tag:0" // OCTET STRING (SIZE 3..4) CallIDNumber []byte asn1:"implicit,optional,tag:1" // OCTET STRING (SIZE 3) }

    But the compiler doesn't like that arrangement. 'type Transit is not an expression'

    Any pointers please would be helpful.

  • If the top-level element is a CHOICE, no instance should be required

    If the top-level element is a CHOICE, no instance should be required

    Currently, the Decode and DecodeWithOptions methods require an object to be passed where the decoded data will be put in place.

    However, if the top-level element is a CHOICE, the instance should be created by using the context.

    I don't know the implementation well, but if you don't have time to take a look at this, please let me know, I could give it a shot.

    F.e.

    Fulfillment ::= CHOICE {
      preimage PreimageFulfillment ,
      prefix PrefixFulfillment,
      threshold ThresholdFulfillment,
      rsaSha256 RsaSha256Fulfillment,
      ed25519 Ed25519Fulfillment
    }
    
  • Creating Struct Examples

    Creating Struct Examples

    Hi,

    Does anyone have any larger examples of the struct creation from an ASN1 Definition. I am 70% through a very large example and I am 100% sure I have got it wrong.

    If anyone is able to advise please on how they would translate the following (I have trimmed allot to capture the key points)

    ASN.1 Formal Description INIncoming ::= SET{ triggerData0 [5] IMPLICIT TriggerData OPTIONAL }

    TriggerData ::= SEQUENCE{ triggerDetectionPoint [0] IMPLICIT TriggerDetectionPoint, sCPAddress CHOICE{ co-located [2] IMPLICIT NULL, }

    I translated to :

    type TriggerData struct { TriggerDetectionPoint []byte asn1:"implicit,tag:0" //

    SCPAddress			interface{} 	`asn1:"implicit,choice:SCPAddress"` // 
    

    }

    type INIncoming struct { Triggerdata0 TriggerData asn1:"implicit,optional,tag:5" / }

    ctx.AddChoice("SCPAddress", []asn1.Choice{ { Type: CoLocated, Options: "implicit,tag:2" }, { Type: PointCodeAndSubSystemNumber, Options: "implicit,tag:3", }, { Type: globalTitle, Options: "implicit,tag:5", }, { Type: globalTitleAndSubSystemNumber, Options: "implicit,tag:5", }, })

    Kind regards,

  • SEQUENCE or SET of CHOICE types

    SEQUENCE or SET of CHOICE types

    Consider this spec:

    Fulfillment ::= CHOICE {
      preimageSha256   [0] PreimageFulfillment ,
      prefixSha256     [1] PrefixFulfillment,
      thresholdSha256  [2] ThresholdFulfillment,
      rsaSha256        [3] RsaSha256Fulfillment,
      ed25519Sha256    [4] Ed25519Sha512Fulfillment
    }
    
    ThresholdFulfillment ::= SEQUENCE {
      subfulfillments      SET OF Fulfillment,
      subconditions        SET OF Condition
    }
    

    So in order to get a SET, we need the set ASN1 tag on the struct field. But we must also indicate what Go types are possible for the Fulfillment interface.

    However, with the following code, asn1 tries to match the CHOICE type (some struct that implements the Fulfillment interface) against the SubFulfillments field, which of course does not match the []Fulfillment type.

    type FfThresholdSha256 struct {
    	SubFulfillments []Fulfillment `asn1:"tag:0,explicit,set,choice:fulfillment"`
    	SubConditions   []*Condition  `asn1:"tag:1,explicit,set,choice:condition"`
    }
    

    Thus, asn1 should recognize that when a choice it put on a SET or a SEQUENCE, it should match the elements inside the set or the sequence instead of that field itself.

    The specific error you get is

    invalid Go type '[]cryptoconditions.Fulfillment' for choice 'fulfillment'
    
  • Support encoding of pointers as interface types

    Support encoding of pointers as interface types

    This simple case did not work:

    type I interface{}
    type S struct {}
    
    var value I
    value = new(S)
    // value is now an I with underlying type *S
    asn1.Encode(value)
    // > invalid Go type: *S
    
  • Add an ignore tag

    Add an ignore tag "-" like encoding/json

    In the json package a tag json:"-" indicates that the field should be ignored during encoding or decoding. This PR adds a similar tag to asn1: asn1:"-".

  • panic: interface conversion: interface is ed25519.PublicKey, not []uint8

    panic: interface conversion: interface is ed25519.PublicKey, not []uint8

    I'm trying to encode an ed25519.PublicKey (type PublicKey []byte) and I get the following panic.

    For some reason, asn1 seems to expect a []uint8 and does not accept a []byte.

    panic: interface conversion: interface is ed25519.PublicKey, not []uint8
    
    goroutine 1 [running]:
    panic(0x4c0160, 0xc420016140)
    	/usr/lib/go/src/runtime/panic.go:500 +0x1a1
    github.com/PromonLogicalis/asn1.(*Context).encodeOctetString(0xc42000e4e0, 0x4bdb00, 0xc42000e4c0, 0x97, 0x40e518, 0x30, 0x4cb5e0, 0x1, 0xc420010420)
    	/home/steven/gocode/src/github.com/PromonLogicalis/asn1/types.go:188 +0x273
    github.com/PromonLogicalis/asn1.(*Context).(github.com/PromonLogicalis/asn1.encodeOctetString)-fm(0x4bdb00, 0xc42000e4c0, 0x97, 0x4bfaa0, 0x4bdb00, 0x0, 0x0, 0x0)
    	/home/steven/gocode/src/github.com/PromonLogicalis/asn1/encode.go:127 +0x48
    github.com/PromonLogicalis/asn1.(*Context).encodeValue(0xc42000e4e0, 0x4bdb00, 0xc42000e4c0, 0x97, 0xc42000e500, 0x1, 0x1, 0x1)
    	/home/steven/gocode/src/github.com/PromonLogicalis/asn1/encode.go:139 +0x131
    github.com/PromonLogicalis/asn1.(*Context).encode(0xc42000e4e0, 0x4bdb00, 0xc42000e4c0, 0x97, 0xc42000e500, 0x30, 0x0, 0x556c01)
    	/home/steven/gocode/src/github.com/PromonLogicalis/asn1/encode.go:59 +0xd5
    github.com/PromonLogicalis/asn1.(*Context).EncodeWithOptions(0xc42000e4e0, 0x4bdb00, 0xc42000e4c0, 0x0, 0x0, 0xc42000e4c0, 0x18, 0x18, 0x1ce8e2fed22701, 0xc42000e4c0)
    	/home/steven/gocode/src/github.com/PromonLogicalis/asn1/encode.go:28 +0xa4
    github.com/PromonLogicalis/asn1.Encode(0x4bdb00, 0xc42000e4c0, 0xc42000e4c0, 0x4bdb00, 0xc42000e4c0, 0xc420016100, 0x40)
    	/home/steven/gocode/src/github.com/PromonLogicalis/asn1/asn1.go:42 +0x191
    main.main()
    	/home/steven/gocode/src/github.com/me/repo/main.go:18 +0x1cc
    exit status 2
    
Related tags
GED - Global-purpose Encoding / Decoding library

GED - Global-purpose Encoding / Decoding library This library lets you use common encoding/decoding schemes and allows you to define custom ones. Use

Nov 28, 2021
Fast implementation of base58 encoding on golang.

Fast Implementation of Base58 encoding Fast implementation of base58 encoding in Go. Base algorithm is adapted from https://github.com/trezor/trezor-c

Dec 9, 2022
msgpack.org[Go] MessagePack encoding for Golang

MessagePack encoding for Golang ❤️ Uptrace.dev - All-in-one tool to optimize performance and monitor errors & logs Join Discord to ask questions. Docu

Dec 28, 2022
A high-performance 100% compatible drop-in replacement of "encoding/json"
A high-performance 100% compatible drop-in replacement of

A high-performance 100% compatible drop-in replacement of "encoding/json" You can also use thrift like JSON using thrift-iterator Benchmark Source cod

Jan 7, 2023
Faster base64 encoding for Go

base64 Faster base64 encoding for Go, based on Turbo-Base64. Features Drop-in replacement of encoding/base64. except for error messages and ignoring \

Nov 17, 2022
Cap'n Proto library and parser for go. This is go-capnproto-1.0, and does not have rpc. See https://github.com/zombiezen/go-capnproto2 for 2.0 which has rpc and capabilities.

Version 1.0 vs 2.0 Update 2015 Sept 20: Big news! Version 2.0 of the go-bindings, authored by Ross Light, is now released and newly available! It feat

Nov 29, 2022
Go library for decoding generic map values into native Go structures and vice versa.

mapstructure mapstructure is a Go library for decoding generic map values to structures and vice versa, while providing helpful error handling. This l

Jan 1, 2023
csvutil provides fast and idiomatic mapping between CSV and Go (golang) values.
csvutil provides fast and idiomatic mapping between CSV and Go (golang) values.

csvutil Package csvutil provides fast and idiomatic mapping between CSV and Go (golang) values. This package does not provide a CSV parser itself, it

Jan 6, 2023
A go library that facilitates the implementation of decorator pattern.

go-decorator go-decorator is a library that facilitates the implementation of decorator pattern. Installation To install go-decorator, use go get: go

Nov 25, 2021
A library that provides dynamic features of Go language.

go-dynamic go-dynamic is a library that provides dynamic features of Go language. Installation To install go-dynamic, use go get: go get -u github.com

Dec 8, 2021
Encode and decode Go (golang) struct types via protocol buffers.

protostructure protostructure is a Go library for encoding and decoding a struct type over the wire. This library is useful when you want to send arbi

Nov 15, 2022
Encode and decode binary message and file formats in Go

Encode and Decode Binary Formats in Go This module wraps the package encoding/binary of the Go standard library and provides the missing Marshal() and

Dec 22, 2022
auto-generate capnproto schema from your golang source files. Depends on go-capnproto-1.0 at https://github.com/glycerine/go-capnproto

bambam: auto-generate capnproto schema from your golang source files. Adding capnproto serialization to an existing Go project used to mean writing a

Sep 27, 2022
Golang binary decoder for mapping data into the structure

binstruct Golang binary decoder to structure Install go get -u github.com/ghostiam/binstruct Examples ZIP decoder PNG decoder Use For struct From file

Dec 17, 2022
CBOR RFC 7049 (Go/Golang) - safe & fast with standard API + toarray & keyasint, CBOR tags, float64/32/16, fuzz tested.
CBOR RFC 7049 (Go/Golang) - safe & fast with standard API + toarray & keyasint, CBOR tags, float64/32/16, fuzz tested.

CBOR library in Go fxamacker/cbor is a CBOR encoder & decoder in Go. It has a standard API, CBOR tags, options for duplicate map keys, float64→32→16,

Jan 6, 2023
Fixed width file parser (encoder/decoder) in GO (golang)

Fixed width file parser (encoder/decoder) for GO (golang) This library is using to parse fixed-width table data like: Name Address

Sep 27, 2022
golang struct 或其他对象向 []byte 的序列化或反序列化

bytecodec 字节流编解码 这个库实现 struct 或其他对象向 []byte 的序列化或反序列化 可以帮助你在编写 tcp 服务,或者需要操作字节流时,简化数据的组包、解包 这个库的组织逻辑 copy 借鉴了标准库 encoding/json ?? 安装 使用 go get 安装最新版本

Jun 30, 2022
Dynamically Generates Ysoserial's Payload by Golang
Dynamically Generates Ysoserial's Payload by Golang

Gososerial 介绍 ysoserial是java反序列化安全方面著名的工具 无需java环境,无需下载ysoserial.jar文件 输入命令直接获得payload,方便编写安全工具 目前已支持CC1-CC7,K1-K4和CB1链 Introduce Ysoserial is a well-

Jul 10, 2022
A k-mer serialization package for Golang
A k-mer serialization package for Golang

.uniq v5 This package provides k-mer serialization methods for the package kmers, TaxIds of k-mers are optionally saved, while there's no frequency in

Aug 19, 2022