Files
nfs/internal/krb5/der.go
T
petrbalvin a9b8039ef7
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
feat: full NFSv4.2 server and client in pure Go
Assisted-by: GLM 5.3 Flash
2026-09-21 18:51:17 +02:00

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
}