Test / test (push) Successful in 2m4s
Release / gates (push) Successful in 2m5s
Release / build (amd64, freebsd) (push) Successful in 1m27s
Release / build (amd64, linux) (push) Successful in 1m22s
Release / build (amd64, netbsd) (push) Successful in 1m19s
Release / build (amd64, openbsd) (push) Successful in 1m20s
Release / build (arm64, darwin) (push) Successful in 1m21s
Release / build (arm64, freebsd) (push) Successful in 1m26s
Release / build (arm64, linux) (push) Successful in 1m25s
Release / build (arm64, netbsd) (push) Successful in 1m31s
Release / build (arm64, openbsd) (push) Successful in 1m27s
Release / build (loong64, linux) (push) Successful in 1m37s
Release / build (riscv64, linux) (push) Successful in 1m21s
Release / release (push) Successful in 40s
Assisted-by: GLM 5.3 Flash
137 lines
3.1 KiB
Go
137 lines
3.1 KiB
Go
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
// A minimal DER encoder and decoder for the Kerberos protocol messages
|
|
// of RFC 4120: just the constructs the AP-REQ and AP-REP exchanges of
|
|
// the GSS context establishment need.
|
|
|
|
package krb5
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
)
|
|
|
|
// DER tag octets used below.
|
|
const (
|
|
tagInteger = 0x02
|
|
tagBitString = 0x03
|
|
tagOctet = 0x04
|
|
tagNull = 0x05
|
|
tagOID = 0x06
|
|
tagSequence = 0x30
|
|
tagGeneral = 0x80 // the high bits of a context or application tag
|
|
)
|
|
|
|
// ErrDER marks a malformed DER input.
|
|
var ErrDER = errors.New("krb5: malformed DER")
|
|
|
|
// derLen encodes a DER length octet string.
|
|
func derLen(n int) []byte {
|
|
if n < 0x80 {
|
|
return []byte{byte(n)}
|
|
}
|
|
var buf [8]byte
|
|
i := len(buf)
|
|
for n > 0 {
|
|
i--
|
|
buf[i] = byte(n)
|
|
n >>= 8
|
|
}
|
|
return append([]byte{0x80 | byte(len(buf)-i)}, buf[i:]...)
|
|
}
|
|
|
|
// derTLV wraps a payload in one tag-length-value record.
|
|
func derTLV(tag byte, payload []byte) []byte {
|
|
return append(append([]byte{tag}, derLen(len(payload))...), payload...)
|
|
}
|
|
|
|
// derInt encodes a DER integer.
|
|
func derInt(v uint64) []byte {
|
|
var raw [8]byte
|
|
binary.BigEndian.PutUint64(raw[:], v)
|
|
i := 0
|
|
for i < 7 && raw[i] == 0 && raw[i+1]&0x80 == 0 {
|
|
i++
|
|
}
|
|
for i < 8 && raw[i] == 0xff && i+1 < 8 && raw[i+1]&0x80 != 0 {
|
|
i++
|
|
}
|
|
return derTLV(tagInteger, raw[i:])
|
|
}
|
|
|
|
// derGeneralString encodes a Kerberos string.
|
|
func derGeneralString(s string) []byte {
|
|
return derTLV(0x1b, []byte(s))
|
|
}
|
|
|
|
// derOID encodes the krb5 mechanism object identifier.
|
|
var derOID = []byte{0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02}
|
|
|
|
// derReader walks a DER structure.
|
|
type derReader struct {
|
|
b []byte
|
|
off int
|
|
}
|
|
|
|
func newDERReader(b []byte) *derReader { return &derReader{b: b} }
|
|
|
|
// next reads one TLV and answers the tag and payload.
|
|
func (r *derReader) next() (byte, []byte, error) {
|
|
if r.off+2 > len(r.b) {
|
|
return 0, nil, ErrDER
|
|
}
|
|
tag := r.b[r.off]
|
|
length := int(r.b[r.off+1])
|
|
start := r.off + 2
|
|
if length&0x80 != 0 {
|
|
n := length & 0x7f
|
|
if n == 0 || n > 4 || start+n > len(r.b) {
|
|
return 0, nil, ErrDER
|
|
}
|
|
length = 0
|
|
for i := range n {
|
|
length = length<<8 | int(r.b[start+i])
|
|
}
|
|
start += n
|
|
}
|
|
if start+length > len(r.b) {
|
|
return 0, nil, ErrDER
|
|
}
|
|
r.off = start + length
|
|
return tag, r.b[start : start+length], nil
|
|
}
|
|
|
|
// expect reads one TLV and requires the tag.
|
|
func (r *derReader) expect(tag byte) ([]byte, error) {
|
|
got, payload, err := r.next()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if got != tag {
|
|
return nil, ErrDER
|
|
}
|
|
return payload, nil
|
|
}
|
|
|
|
// derUint decodes a DER integer payload.
|
|
func derUint(payload []byte) (uint64, error) {
|
|
if len(payload) == 0 || len(payload) > 8 {
|
|
return 0, ErrDER
|
|
}
|
|
var v uint64
|
|
for _, b := range payload {
|
|
v = v<<8 | uint64(b)
|
|
}
|
|
return v, nil
|
|
}
|
|
|
|
// derBitString decodes a DER bit string payload: the leading unused bit
|
|
// count octet followed by the bits.
|
|
func derBitString(payload []byte) ([]byte, error) {
|
|
if len(payload) == 0 {
|
|
return nil, ErrDER
|
|
}
|
|
return payload[1:], nil
|
|
}
|