feat: full NFSv4.2 server and client in pure Go
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
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
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
// 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
|
||||
}
|
||||
Reference in New Issue
Block a user