Skip to content

Firanel/lua-ber

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Lua BER encoding / decoding

BER encoding and decoding in pure Lua.

Compatability

Due to heavy use of binary oparators, this package is only compatible with Lua 5.3 or higher.

Usage

The BerObject:

Property Description Default value
class Tag class: 0-3 (Universal / Application / Context-specific / Private) 0
constructed Is a constructed type false unless only constructed is permitted (native tags only)
type ASN.1 tag 0 (End of content)
length Data length (in Bytes) nil (Calculated from data)
data Data as string nil
children Children for constructed types nil

Encode

Functions:

ber.identifier(options) -- BerObject (without data and length)
ber.length(value) -- number (generates octet for indefinite form if nil)
ber.encode(value) -- BerObject

Generate the identifier octets:

local result = ber.identifier {
  type = ber.Types.INTEGER
}

Generate the length octets:

result = result .. ber.length(2)

Add the data:

result = result .. string.pack(">i2", 4660)

All in a single step:

assert(result == ber.encode {
  type = ber.Types.INTEGER,
  data = string.pack(">i2", 4660)
})

nil, integers, booleans and strings, can be automatically encoded:

assert(result == ber.encode(4660))

Automatic encoding is DER compliant.
Strings are encoded as octet strings.

Use tables with numbered indices to automatically encode sequences:

assert(ber.encode {"hello", 42} == ber.encode {
  type = ber.Types.SEQUENCE,
  data = ber.encode "hello" .. ber.encode(42)
})

Tables with numbered indices may also contain BerObject properties:

assert(ber.encode {
  type = ber.Types.SET,
  1, 2, 3
} == ber.encode {
  type = ber.Types.SET,
  children = {1, 2, 3}
})

If constructed is true and children is set, encode will first encode children and use the result as the data.

ber.encode {
  type = ber.Types.SEQUENCE, -- constructed is implied with sequence type
  children = {"First", 42}
}

Constructed types may set index to true to auto index children:

assert(ber.encode {
  index = true,
  1, 2
} == ber.encode {
  {
    class = ber.Class.ContextSpecific,
    type = 0,
    data = string.byte(1)
  },
  {
    class = ber.Class.ContextSpecific,
    type = 1,
    data = string.byte(2)
  }
})

The metatable index __tober can be used to customize encoding, by providing an encodable value or a function returning an encodable value.

local obj = setmetatable({
  name = "Steve"
}, {
  __tober = function (this) return "Hello "..this.name end
})

assert(ber.encode(obj) == ber.encode "Hello Steve")

Decode

Functions:

ber.decode(
  value,         -- BER data as string
  start,         -- Start index (default: 1)
  recursionDepth -- Max recursion depth for deconstruction (default: math.maxinteger)
)

decode always returns a BerObject:

assert(ber.decode(ber.encode "Hello") == {
  class = 0,
  constructed = false,
  type = 4,
  length = 5,
  data = "Hello",
  children = nil
})