Update module lib/pq to v1.6.0 (#572)
Update module lib/pq to v1.6.0 Reviewed-on: https://kolaente.dev/vikunja/api/pulls/572
This commit is contained in:
174
vendor/github.com/jcmturner/gokrb5/v8/gssapi/MICToken.go
generated
vendored
Normal file
174
vendor/github.com/jcmturner/gokrb5/v8/gssapi/MICToken.go
generated
vendored
Normal file
@ -0,0 +1,174 @@
|
||||
package gssapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/jcmturner/gokrb5/v8/crypto"
|
||||
"github.com/jcmturner/gokrb5/v8/iana/keyusage"
|
||||
"github.com/jcmturner/gokrb5/v8/types"
|
||||
)
|
||||
|
||||
// RFC 4121, section 4.2.6.1
|
||||
|
||||
const (
|
||||
// MICTokenFlagSentByAcceptor - this flag indicates the sender is the context acceptor. When not set, it indicates the sender is the context initiator
|
||||
MICTokenFlagSentByAcceptor = 1 << iota
|
||||
// MICTokenFlagSealed - this flag indicates confidentiality is provided for. It SHALL NOT be set in MIC tokens
|
||||
MICTokenFlagSealed
|
||||
// MICTokenFlagAcceptorSubkey - a subkey asserted by the context acceptor is used to protect the message
|
||||
MICTokenFlagAcceptorSubkey
|
||||
)
|
||||
|
||||
const (
|
||||
micHdrLen = 16 // Length of the MIC Token's header
|
||||
)
|
||||
|
||||
// MICToken represents a GSS API MIC token, as defined in RFC 4121.
|
||||
// It contains the header fields, the payload (this is not transmitted) and
|
||||
// the checksum, and provides the logic for converting to/from bytes plus
|
||||
// computing and verifying checksums
|
||||
type MICToken struct {
|
||||
// const GSS Token ID: 0x0404
|
||||
Flags byte // contains three flags: acceptor, sealed, acceptor subkey
|
||||
// const Filler: 0xFF 0xFF 0xFF 0xFF 0xFF
|
||||
SndSeqNum uint64 // sender's sequence number. big-endian
|
||||
Payload []byte // your data! :)
|
||||
Checksum []byte // checksum of { payload | header }
|
||||
}
|
||||
|
||||
// Return the 2 bytes identifying a GSS API MIC token
|
||||
func getGSSMICTokenID() *[2]byte {
|
||||
return &[2]byte{0x04, 0x04}
|
||||
}
|
||||
|
||||
// Return the filler bytes used in header
|
||||
func fillerBytes() *[5]byte {
|
||||
return &[5]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
|
||||
}
|
||||
|
||||
// Marshal the MICToken into a byte slice.
|
||||
// The payload should have been set and the checksum computed, otherwise an error is returned.
|
||||
func (mt *MICToken) Marshal() ([]byte, error) {
|
||||
if mt.Checksum == nil {
|
||||
return nil, errors.New("checksum has not been set")
|
||||
}
|
||||
|
||||
bytes := make([]byte, micHdrLen+len(mt.Checksum))
|
||||
copy(bytes[0:micHdrLen], mt.getMICChecksumHeader()[:])
|
||||
copy(bytes[micHdrLen:], mt.Checksum)
|
||||
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
// SetChecksum uses the passed encryption key and key usage to compute the checksum over the payload and
|
||||
// the header, and sets the Checksum field of this MICToken.
|
||||
// If the payload has not been set or the checksum has already been set, an error is returned.
|
||||
func (mt *MICToken) SetChecksum(key types.EncryptionKey, keyUsage uint32) error {
|
||||
if mt.Checksum != nil {
|
||||
return errors.New("checksum has already been computed")
|
||||
}
|
||||
checksum, err := mt.checksum(key, keyUsage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mt.Checksum = checksum
|
||||
return nil
|
||||
}
|
||||
|
||||
// Compute and return the checksum of this token, computed using the passed key and key usage.
|
||||
// Note: This will NOT update the struct's Checksum field.
|
||||
func (mt *MICToken) checksum(key types.EncryptionKey, keyUsage uint32) ([]byte, error) {
|
||||
if mt.Payload == nil {
|
||||
return nil, errors.New("cannot compute checksum with uninitialized payload")
|
||||
}
|
||||
d := make([]byte, micHdrLen+len(mt.Payload))
|
||||
copy(d[0:], mt.Payload)
|
||||
copy(d[len(mt.Payload):], mt.getMICChecksumHeader())
|
||||
|
||||
encType, err := crypto.GetEtype(key.KeyType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return encType.GetChecksumHash(key.KeyValue, d, keyUsage)
|
||||
}
|
||||
|
||||
// Build a header suitable for a checksum computation
|
||||
func (mt *MICToken) getMICChecksumHeader() []byte {
|
||||
header := make([]byte, micHdrLen)
|
||||
copy(header[0:2], getGSSMICTokenID()[:])
|
||||
header[2] = mt.Flags
|
||||
copy(header[3:8], fillerBytes()[:])
|
||||
binary.BigEndian.PutUint64(header[8:16], mt.SndSeqNum)
|
||||
return header
|
||||
}
|
||||
|
||||
// Verify computes the token's checksum with the provided key and usage,
|
||||
// and compares it to the checksum present in the token.
|
||||
// In case of any failure, (false, err) is returned, with err an explanatory error.
|
||||
func (mt *MICToken) Verify(key types.EncryptionKey, keyUsage uint32) (bool, error) {
|
||||
computed, err := mt.checksum(key, keyUsage)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !hmac.Equal(computed, mt.Checksum) {
|
||||
return false, fmt.Errorf(
|
||||
"checksum mismatch. Computed: %s, Contained in token: %s",
|
||||
hex.EncodeToString(computed), hex.EncodeToString(mt.Checksum))
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Unmarshal bytes into the corresponding MICToken.
|
||||
// If expectFromAcceptor is true we expect the token to have been emitted by the gss acceptor,
|
||||
// and will check the according flag, returning an error if the token does not match the expectation.
|
||||
func (mt *MICToken) Unmarshal(b []byte, expectFromAcceptor bool) error {
|
||||
if len(b) < micHdrLen {
|
||||
return errors.New("bytes shorter than header length")
|
||||
}
|
||||
if !bytes.Equal(getGSSMICTokenID()[:], b[0:2]) {
|
||||
return fmt.Errorf("wrong Token ID, Expected %s, was %s",
|
||||
hex.EncodeToString(getGSSMICTokenID()[:]),
|
||||
hex.EncodeToString(b[0:2]))
|
||||
}
|
||||
flags := b[2]
|
||||
isFromAcceptor := flags&MICTokenFlagSentByAcceptor != 0
|
||||
if isFromAcceptor && !expectFromAcceptor {
|
||||
return errors.New("unexpected acceptor flag is set: not expecting a token from the acceptor")
|
||||
}
|
||||
if !isFromAcceptor && expectFromAcceptor {
|
||||
return errors.New("unexpected acceptor flag is not set: expecting a token from the acceptor, not in the initiator")
|
||||
}
|
||||
if !bytes.Equal(b[3:8], fillerBytes()[:]) {
|
||||
return fmt.Errorf("unexpected filler bytes: expecting %s, was %s",
|
||||
hex.EncodeToString(fillerBytes()[:]),
|
||||
hex.EncodeToString(b[3:8]))
|
||||
}
|
||||
|
||||
mt.Flags = flags
|
||||
mt.SndSeqNum = binary.BigEndian.Uint64(b[8:16])
|
||||
mt.Checksum = b[micHdrLen:]
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewInitiatorMICToken builds a new initiator token (acceptor flag will be set to 0) and computes the authenticated checksum.
|
||||
// Other flags are set to 0.
|
||||
// Note that in certain circumstances you may need to provide a sequence number that has been defined earlier.
|
||||
// This is currently not supported.
|
||||
func NewInitiatorMICToken(payload []byte, key types.EncryptionKey) (*MICToken, error) {
|
||||
token := MICToken{
|
||||
Flags: 0x00,
|
||||
SndSeqNum: 0,
|
||||
Payload: payload,
|
||||
}
|
||||
|
||||
if err := token.SetChecksum(key, keyusage.GSSAPI_INITIATOR_SIGN); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &token, nil
|
||||
}
|
20
vendor/github.com/jcmturner/gokrb5/v8/gssapi/README.md
generated
vendored
Normal file
20
vendor/github.com/jcmturner/gokrb5/v8/gssapi/README.md
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
# Notes on GSS-API Negotiation Mechanism
|
||||
https://tools.ietf.org/html/rfc4178
|
||||
|
||||
Client sends an initial negotiation message to the server which specifies the list of mechanisms
|
||||
the client can support in order of decreasing preference.
|
||||
This message is generated with the ``NewNegTokenInitKrb5`` method.
|
||||
The message generated by this function specifies only a kerberos v5 mechanism is supported.
|
||||
|
||||
The RFC states that this message can optionally contain the initial mechanism token
|
||||
for the preferred mechanism (KRB5 in this case) of the client. The ``NewNegTokenInitKrb5``
|
||||
includes this in the message.
|
||||
|
||||
The server side responds to this message with a one of four messages:
|
||||
|
||||
| Message Type/State | Description |
|
||||
|--------------------|-------------|
|
||||
| accept-completed | indicates that the initiator-selected mechanism was acceptable to the target, and that the security mechanism token embedded in the first negotiation message was sufficient to complete the authentication |
|
||||
| accept-incomplete | At least one more message is needed from the client to establish security context. |
|
||||
| reject | Negotiation is being terminated. |
|
||||
| request-mic | (this state can only be present in the first reply message from the target) indicates that the MIC token exchange is REQUIRED if per-message integrity services are available |
|
25
vendor/github.com/jcmturner/gokrb5/v8/gssapi/contextFlags.go
generated
vendored
Normal file
25
vendor/github.com/jcmturner/gokrb5/v8/gssapi/contextFlags.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
package gssapi
|
||||
|
||||
import "github.com/jcmturner/gofork/encoding/asn1"
|
||||
|
||||
// GSS-API context flags assigned numbers.
|
||||
const (
|
||||
ContextFlagDeleg = 1
|
||||
ContextFlagMutual = 2
|
||||
ContextFlagReplay = 4
|
||||
ContextFlagSequence = 8
|
||||
ContextFlagConf = 16
|
||||
ContextFlagInteg = 32
|
||||
ContextFlagAnon = 64
|
||||
)
|
||||
|
||||
// ContextFlags flags for GSSAPI
|
||||
type ContextFlags asn1.BitString
|
||||
|
||||
// NewContextFlags creates a new ContextFlags instance.
|
||||
func NewContextFlags() ContextFlags {
|
||||
var c ContextFlags
|
||||
c.BitLength = 32
|
||||
c.Bytes = make([]byte, 4)
|
||||
return c
|
||||
}
|
202
vendor/github.com/jcmturner/gokrb5/v8/gssapi/gssapi.go
generated
vendored
Normal file
202
vendor/github.com/jcmturner/gokrb5/v8/gssapi/gssapi.go
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
// Package gssapi implements Generic Security Services Application Program Interface required for SPNEGO kerberos authentication.
|
||||
package gssapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/jcmturner/gofork/encoding/asn1"
|
||||
)
|
||||
|
||||
// GSS-API OID names
|
||||
const (
|
||||
// GSS-API OID names
|
||||
OIDKRB5 OIDName = "KRB5" // MechType OID for Kerberos 5
|
||||
OIDMSLegacyKRB5 OIDName = "MSLegacyKRB5" // MechType OID for Kerberos 5
|
||||
OIDSPNEGO OIDName = "SPNEGO"
|
||||
OIDGSSIAKerb OIDName = "GSSIAKerb" // Indicates the client cannot get a service ticket and asks the server to serve as an intermediate to the target KDC. http://k5wiki.kerberos.org/wiki/Projects/IAKERB#IAKERB_mech
|
||||
)
|
||||
|
||||
// GSS-API status values
|
||||
const (
|
||||
StatusBadBindings = 1 << iota
|
||||
StatusBadMech
|
||||
StatusBadName
|
||||
StatusBadNameType
|
||||
StatusBadStatus
|
||||
StatusBadSig
|
||||
StatusBadMIC
|
||||
StatusContextExpired
|
||||
StatusCredentialsExpired
|
||||
StatusDefectiveCredential
|
||||
StatusDefectiveToken
|
||||
StatusFailure
|
||||
StatusNoContext
|
||||
StatusNoCred
|
||||
StatusBadQOP
|
||||
StatusUnauthorized
|
||||
StatusUnavailable
|
||||
StatusDuplicateElement
|
||||
StatusNameNotMN
|
||||
StatusComplete
|
||||
StatusContinueNeeded
|
||||
StatusDuplicateToken
|
||||
StatusOldToken
|
||||
StatusUnseqToken
|
||||
StatusGapToken
|
||||
)
|
||||
|
||||
// ContextToken is an interface for a GSS-API context token.
|
||||
type ContextToken interface {
|
||||
Marshal() ([]byte, error)
|
||||
Unmarshal(b []byte) error
|
||||
Verify() (bool, Status)
|
||||
Context() context.Context
|
||||
}
|
||||
|
||||
/*
|
||||
CREDENTIAL MANAGEMENT
|
||||
|
||||
GSS_Acquire_cred acquire credentials for use
|
||||
GSS_Release_cred release credentials after use
|
||||
GSS_Inquire_cred display information about credentials
|
||||
GSS_Add_cred construct credentials incrementally
|
||||
GSS_Inquire_cred_by_mech display per-mechanism credential information
|
||||
|
||||
CONTEXT-LEVEL CALLS
|
||||
|
||||
GSS_Init_sec_context initiate outbound security context
|
||||
GSS_Accept_sec_context accept inbound security context
|
||||
GSS_Delete_sec_context flush context when no longer needed
|
||||
GSS_Process_context_token process received control token on context
|
||||
GSS_Context_time indicate validity time remaining on context
|
||||
GSS_Inquire_context display information about context
|
||||
GSS_Wrap_size_limit determine GSS_Wrap token size limit
|
||||
GSS_Export_sec_context transfer context to other process
|
||||
GSS_Import_sec_context import transferred context
|
||||
|
||||
PER-MESSAGE CALLS
|
||||
|
||||
GSS_GetMIC apply integrity check, receive as token separate from message
|
||||
GSS_VerifyMIC validate integrity check token along with message
|
||||
GSS_Wrap sign, optionally encrypt, encapsulate
|
||||
GSS_Unwrap decapsulate, decrypt if needed, validate integrity check
|
||||
|
||||
SUPPORT CALLS
|
||||
|
||||
GSS_Display_status translate status codes to printable form
|
||||
GSS_Indicate_mechs indicate mech_types supported on local system
|
||||
GSS_Compare_name compare two names for equality
|
||||
GSS_Display_name translate name to printable form
|
||||
GSS_Import_name convert printable name to normalized form
|
||||
GSS_Release_name free storage of normalized-form name
|
||||
GSS_Release_buffer free storage of general GSS-allocated object
|
||||
GSS_Release_OID_set free storage of OID set object
|
||||
GSS_Create_empty_OID_set create empty OID set
|
||||
GSS_Add_OID_set_member add member to OID set
|
||||
GSS_Test_OID_set_member test if OID is member of OID set
|
||||
GSS_Inquire_names_for_mech indicate name types supported by mechanism
|
||||
GSS_Inquire_mechs_for_name indicates mechanisms supporting name type
|
||||
GSS_Canonicalize_name translate name to per-mechanism form
|
||||
GSS_Export_name externalize per-mechanism name
|
||||
GSS_Duplicate_name duplicate name object
|
||||
*/
|
||||
|
||||
// Mechanism is the GSS-API interface for authentication mechanisms.
|
||||
type Mechanism interface {
|
||||
OID() asn1.ObjectIdentifier
|
||||
AcquireCred() error // acquire credentials for use (eg. AS exchange for KRB5)
|
||||
InitSecContext() (ContextToken, error) // initiate outbound security context (eg TGS exchange builds AP_REQ to go into ContextToken to send to service)
|
||||
AcceptSecContext(ct ContextToken) (bool, context.Context, Status) // service verifies the token server side to establish a context
|
||||
MIC() MICToken // apply integrity check, receive as token separate from message
|
||||
VerifyMIC(mt MICToken) (bool, error) // validate integrity check token along with message
|
||||
Wrap(msg []byte) WrapToken // sign, optionally encrypt, encapsulate
|
||||
Unwrap(wt WrapToken) []byte // decapsulate, decrypt if needed, validate integrity check
|
||||
}
|
||||
|
||||
// OIDName is the type for defined GSS-API OIDs.
|
||||
type OIDName string
|
||||
|
||||
// OID returns the OID for the provided OID name.
|
||||
func (o OIDName) OID() asn1.ObjectIdentifier {
|
||||
switch o {
|
||||
case OIDSPNEGO:
|
||||
return asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 2}
|
||||
case OIDKRB5:
|
||||
return asn1.ObjectIdentifier{1, 2, 840, 113554, 1, 2, 2}
|
||||
case OIDMSLegacyKRB5:
|
||||
return asn1.ObjectIdentifier{1, 2, 840, 48018, 1, 2, 2}
|
||||
case OIDGSSIAKerb:
|
||||
return asn1.ObjectIdentifier{1, 3, 6, 1, 5, 2, 5}
|
||||
}
|
||||
return asn1.ObjectIdentifier{}
|
||||
}
|
||||
|
||||
// Status is the GSS-API status and implements the error interface.
|
||||
type Status struct {
|
||||
Code int
|
||||
Message string
|
||||
}
|
||||
|
||||
// Error returns the Status description.
|
||||
func (s Status) Error() string {
|
||||
var str string
|
||||
switch s.Code {
|
||||
case StatusBadBindings:
|
||||
str = "channel binding mismatch"
|
||||
case StatusBadMech:
|
||||
str = "unsupported mechanism requested"
|
||||
case StatusBadName:
|
||||
str = "invalid name provided"
|
||||
case StatusBadNameType:
|
||||
str = "name of unsupported type provided"
|
||||
case StatusBadStatus:
|
||||
str = "invalid input status selector"
|
||||
case StatusBadSig:
|
||||
str = "token had invalid integrity check"
|
||||
case StatusBadMIC:
|
||||
str = "preferred alias for GSS_S_BAD_SIG"
|
||||
case StatusContextExpired:
|
||||
str = "specified security context expired"
|
||||
case StatusCredentialsExpired:
|
||||
str = "expired credentials detected"
|
||||
case StatusDefectiveCredential:
|
||||
str = "defective credential detected"
|
||||
case StatusDefectiveToken:
|
||||
str = "defective token detected"
|
||||
case StatusFailure:
|
||||
str = "failure, unspecified at GSS-API level"
|
||||
case StatusNoContext:
|
||||
str = "no valid security context specified"
|
||||
case StatusNoCred:
|
||||
str = "no valid credentials provided"
|
||||
case StatusBadQOP:
|
||||
str = "unsupported QOP valu"
|
||||
case StatusUnauthorized:
|
||||
str = "operation unauthorized"
|
||||
case StatusUnavailable:
|
||||
str = "operation unavailable"
|
||||
case StatusDuplicateElement:
|
||||
str = "duplicate credential element requested"
|
||||
case StatusNameNotMN:
|
||||
str = "name contains multi-mechanism elements"
|
||||
case StatusComplete:
|
||||
str = "normal completion"
|
||||
case StatusContinueNeeded:
|
||||
str = "continuation call to routine required"
|
||||
case StatusDuplicateToken:
|
||||
str = "duplicate per-message token detected"
|
||||
case StatusOldToken:
|
||||
str = "timed-out per-message token detected"
|
||||
case StatusUnseqToken:
|
||||
str = "reordered (early) per-message token detected"
|
||||
case StatusGapToken:
|
||||
str = "skipped predecessor token(s) detected"
|
||||
default:
|
||||
str = "unknown GSS-API error status"
|
||||
}
|
||||
if s.Message != "" {
|
||||
return fmt.Sprintf("%s: %s", str, s.Message)
|
||||
}
|
||||
return str
|
||||
}
|
193
vendor/github.com/jcmturner/gokrb5/v8/gssapi/wrapToken.go
generated
vendored
Normal file
193
vendor/github.com/jcmturner/gokrb5/v8/gssapi/wrapToken.go
generated
vendored
Normal file
@ -0,0 +1,193 @@
|
||||
package gssapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/jcmturner/gokrb5/v8/crypto"
|
||||
"github.com/jcmturner/gokrb5/v8/iana/keyusage"
|
||||
"github.com/jcmturner/gokrb5/v8/types"
|
||||
)
|
||||
|
||||
// RFC 4121, section 4.2.6.2
|
||||
|
||||
const (
|
||||
HdrLen = 16 // Length of the Wrap Token's header
|
||||
FillerByte byte = 0xFF
|
||||
)
|
||||
|
||||
// WrapToken represents a GSS API Wrap token, as defined in RFC 4121.
|
||||
// It contains the header fields, the payload and the checksum, and provides
|
||||
// the logic for converting to/from bytes plus computing and verifying checksums
|
||||
type WrapToken struct {
|
||||
// const GSS Token ID: 0x0504
|
||||
Flags byte // contains three flags: acceptor, sealed, acceptor subkey
|
||||
// const Filler: 0xFF
|
||||
EC uint16 // checksum length. big-endian
|
||||
RRC uint16 // right rotation count. big-endian
|
||||
SndSeqNum uint64 // sender's sequence number. big-endian
|
||||
Payload []byte // your data! :)
|
||||
CheckSum []byte // authenticated checksum of { payload | header }
|
||||
}
|
||||
|
||||
// Return the 2 bytes identifying a GSS API Wrap token
|
||||
func getGssWrapTokenId() *[2]byte {
|
||||
return &[2]byte{0x05, 0x04}
|
||||
}
|
||||
|
||||
// Marshal the WrapToken into a byte slice.
|
||||
// The payload should have been set and the checksum computed, otherwise an error is returned.
|
||||
func (wt *WrapToken) Marshal() ([]byte, error) {
|
||||
if wt.CheckSum == nil {
|
||||
return nil, errors.New("checksum has not been set")
|
||||
}
|
||||
if wt.Payload == nil {
|
||||
return nil, errors.New("payload has not been set")
|
||||
}
|
||||
|
||||
pldOffset := HdrLen // Offset of the payload in the token
|
||||
chkSOffset := HdrLen + len(wt.Payload) // Offset of the checksum in the token
|
||||
|
||||
bytes := make([]byte, chkSOffset+int(wt.EC))
|
||||
copy(bytes[0:], getGssWrapTokenId()[:])
|
||||
bytes[2] = wt.Flags
|
||||
bytes[3] = FillerByte
|
||||
binary.BigEndian.PutUint16(bytes[4:6], wt.EC)
|
||||
binary.BigEndian.PutUint16(bytes[6:8], wt.RRC)
|
||||
binary.BigEndian.PutUint64(bytes[8:16], wt.SndSeqNum)
|
||||
copy(bytes[pldOffset:], wt.Payload)
|
||||
copy(bytes[chkSOffset:], wt.CheckSum)
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
// SetCheckSum uses the passed encryption key and key usage to compute the checksum over the payload and
|
||||
// the header, and sets the CheckSum field of this WrapToken.
|
||||
// If the payload has not been set or the checksum has already been set, an error is returned.
|
||||
func (wt *WrapToken) SetCheckSum(key types.EncryptionKey, keyUsage uint32) error {
|
||||
if wt.Payload == nil {
|
||||
return errors.New("payload has not been set")
|
||||
}
|
||||
if wt.CheckSum != nil {
|
||||
return errors.New("checksum has already been computed")
|
||||
}
|
||||
chkSum, cErr := wt.computeCheckSum(key, keyUsage)
|
||||
if cErr != nil {
|
||||
return cErr
|
||||
}
|
||||
wt.CheckSum = chkSum
|
||||
return nil
|
||||
}
|
||||
|
||||
// ComputeCheckSum computes and returns the checksum of this token, computed using the passed key and key usage.
|
||||
// Note: This will NOT update the struct's Checksum field.
|
||||
func (wt *WrapToken) computeCheckSum(key types.EncryptionKey, keyUsage uint32) ([]byte, error) {
|
||||
if wt.Payload == nil {
|
||||
return nil, errors.New("cannot compute checksum with uninitialized payload")
|
||||
}
|
||||
// Build a slice containing { payload | header }
|
||||
checksumMe := make([]byte, HdrLen+len(wt.Payload))
|
||||
copy(checksumMe[0:], wt.Payload)
|
||||
copy(checksumMe[len(wt.Payload):], getChecksumHeader(wt.Flags, wt.SndSeqNum))
|
||||
|
||||
encType, err := crypto.GetEtype(key.KeyType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return encType.GetChecksumHash(key.KeyValue, checksumMe, keyUsage)
|
||||
}
|
||||
|
||||
// Build a header suitable for a checksum computation
|
||||
func getChecksumHeader(flags byte, senderSeqNum uint64) []byte {
|
||||
header := make([]byte, 16)
|
||||
copy(header[0:], []byte{0x05, 0x04, flags, 0xFF, 0x00, 0x00, 0x00, 0x00})
|
||||
binary.BigEndian.PutUint64(header[8:], senderSeqNum)
|
||||
return header
|
||||
}
|
||||
|
||||
// Verify computes the token's checksum with the provided key and usage,
|
||||
// and compares it to the checksum present in the token.
|
||||
// In case of any failure, (false, Err) is returned, with Err an explanatory error.
|
||||
func (wt *WrapToken) Verify(key types.EncryptionKey, keyUsage uint32) (bool, error) {
|
||||
computed, cErr := wt.computeCheckSum(key, keyUsage)
|
||||
if cErr != nil {
|
||||
return false, cErr
|
||||
}
|
||||
if !hmac.Equal(computed, wt.CheckSum) {
|
||||
return false, fmt.Errorf(
|
||||
"checksum mismatch. Computed: %s, Contained in token: %s",
|
||||
hex.EncodeToString(computed), hex.EncodeToString(wt.CheckSum))
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Unmarshal bytes into the corresponding WrapToken.
|
||||
// If expectFromAcceptor is true, we expect the token to have been emitted by the gss acceptor,
|
||||
// and will check the according flag, returning an error if the token does not match the expectation.
|
||||
func (wt *WrapToken) Unmarshal(b []byte, expectFromAcceptor bool) error {
|
||||
// Check if we can read a whole header
|
||||
if len(b) < 16 {
|
||||
return errors.New("bytes shorter than header length")
|
||||
}
|
||||
// Is the Token ID correct?
|
||||
if !bytes.Equal(getGssWrapTokenId()[:], b[0:2]) {
|
||||
return fmt.Errorf("wrong Token ID. Expected %s, was %s",
|
||||
hex.EncodeToString(getGssWrapTokenId()[:]),
|
||||
hex.EncodeToString(b[0:2]))
|
||||
}
|
||||
// Check the acceptor flag
|
||||
flags := b[2]
|
||||
isFromAcceptor := flags&0x01 == 1
|
||||
if isFromAcceptor && !expectFromAcceptor {
|
||||
return errors.New("unexpected acceptor flag is set: not expecting a token from the acceptor")
|
||||
}
|
||||
if !isFromAcceptor && expectFromAcceptor {
|
||||
return errors.New("expected acceptor flag is not set: expecting a token from the acceptor, not the initiator")
|
||||
}
|
||||
// Check the filler byte
|
||||
if b[3] != FillerByte {
|
||||
return fmt.Errorf("unexpected filler byte: expecting 0xFF, was %s ", hex.EncodeToString(b[3:4]))
|
||||
}
|
||||
checksumL := binary.BigEndian.Uint16(b[4:6])
|
||||
// Sanity check on the checksum length
|
||||
if int(checksumL) > len(b)-HdrLen {
|
||||
return fmt.Errorf("inconsistent checksum length: %d bytes to parse, checksum length is %d", len(b), checksumL)
|
||||
}
|
||||
|
||||
wt.Flags = flags
|
||||
wt.EC = checksumL
|
||||
wt.RRC = binary.BigEndian.Uint16(b[6:8])
|
||||
wt.SndSeqNum = binary.BigEndian.Uint64(b[8:16])
|
||||
wt.Payload = b[16 : len(b)-int(checksumL)]
|
||||
wt.CheckSum = b[len(b)-int(checksumL):]
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewInitiatorWrapToken builds a new initiator token (acceptor flag will be set to 0) and computes the authenticated checksum.
|
||||
// Other flags are set to 0, and the RRC and sequence number are initialized to 0.
|
||||
// Note that in certain circumstances you may need to provide a sequence number that has been defined earlier.
|
||||
// This is currently not supported.
|
||||
func NewInitiatorWrapToken(payload []byte, key types.EncryptionKey) (*WrapToken, error) {
|
||||
encType, err := crypto.GetEtype(key.KeyType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
token := WrapToken{
|
||||
Flags: 0x00, // all zeroed out (this is a token sent by the initiator)
|
||||
// Checksum size: length of output of the HMAC function, in bytes.
|
||||
EC: uint16(encType.GetHMACBitLength() / 8),
|
||||
RRC: 0,
|
||||
SndSeqNum: 0,
|
||||
Payload: payload,
|
||||
}
|
||||
|
||||
if err := token.SetCheckSum(key, keyusage.GSSAPI_INITIATOR_SEAL); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &token, nil
|
||||
}
|
Reference in New Issue
Block a user