From 1e3123aaf631a0bb7654adf0d240fa8c6b2b836d Mon Sep 17 00:00:00 2001 From: Mikhail Molotkov Date: Thu, 17 Nov 2022 21:34:45 +0000 Subject: [PATCH 01/11] Trying to get wrapToken sorted for GSSAPI v1 --- v8/gssapi/wrapToken.go | 9 ++ v8/gssapi/wrapTokenV1.go | 256 ++++++++++++++++++++++++++++++++++ v8/gssapi/wrapTokenV1_test.go | 6 + 3 files changed, 271 insertions(+) create mode 100644 v8/gssapi/wrapTokenV1.go create mode 100644 v8/gssapi/wrapTokenV1_test.go diff --git a/v8/gssapi/wrapToken.go b/v8/gssapi/wrapToken.go index ea7d0543..f3d1cd7e 100644 --- a/v8/gssapi/wrapToken.go +++ b/v8/gssapi/wrapToken.go @@ -134,6 +134,15 @@ func (wt *WrapToken) Unmarshal(b []byte, expectFromAcceptor bool) error { if len(b) < 16 { return errors.New("bytes shorter than header length") } + + // Check for 0x60 as the first byte; As per RFC 4121 § 4.4, these Token ID - 0x60 0x00 to 0x60 0xFF + // are reserved to indicate 'Generic GSS-API token framing' that was used by + // GSS-API v1, and are not supported in GSS-API v2.. catch that specific case so + // we can emmit a useful message + if b[0] == 0x60 { + return errors.New(fmt.Sprintf("GSS-API v1 message tokens are not supported: %s", hex.EncodeToString(b[0:2]))) + } + // Is the Token ID correct? if !bytes.Equal(getGssWrapTokenId()[:], b[0:2]) { return fmt.Errorf("wrong Token ID. Expected %s, was %s", diff --git a/v8/gssapi/wrapTokenV1.go b/v8/gssapi/wrapTokenV1.go new file mode 100644 index 00000000..119f5e8b --- /dev/null +++ b/v8/gssapi/wrapTokenV1.go @@ -0,0 +1,256 @@ +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" +) + +const ( + // Length of the Wrap Token's header + TOKEN_NO_CKSUM_SIZE = 16 + + // TOKEN_ID_POS int16 = 0 + // SIGN_ALG_POS int16 = 2 + // SEAL_ALG_POS int16 = 4 +) + +// ===== Almost const 2 bytes values to represent various values from GSS API RFCs +// 2 bytes identifying GSS API Wrap token v1 +var TOK_ID = [2]byte{0x02, 0x01} + +// Filler in WrapToken v1 +var FILLER = [2]byte{0xFF, 0xFF} + +// Use DES MAC MD5 checksum - RFC 1964 +var SGN_ALG_DES_MAC_MD5 = [2]byte{0x00, 0x00} + +// Use DES MAC checksum - RFC 1964 +var SGN_ALG_DES_MAC = [2]byte{0x02, 0x00} + +// Use HMAC SHA1 DES3 KD checksum - RFC 1964 +var SGN_ALG_HMAC_SHA1_DES3_KD = [2]byte{0x04, 0x00} + +// Use HMAC MD5 ARCFOUR checksum - RFC ? +var SGN_ALG_HMAC_MD5_ARCFOUR = [2]byte{0x11, 0x00} + +// Use NONE encryption to seal +var SEAL_ALG_NONE = [2]byte{0xFF, 0xFF} + +// Use DES CBC encryption to seal +var SEAL_ALG_DES = [2]byte{0x00, 0x00} + +// Use DES3 KD encryption to seal +var SEAL_ALG_DES3_KD = [2]byte{0x02, 0x00} + +// Use ARCFOUR HMAC encryption to seal +var SEAL_ALG_ARCFOUR_HMAC = [2]byte{0x10, 0x00} +// ===== + + +// WrapTokenV1 represents a GSS API Wrap token v1, as defined in RFC 1964. +// 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 WrapTokenV1 struct { + // const GSS Token ID: 0x02 0x01 + SGN_ALG uint16 // checksum algorithm indicator. big-endian + SEAL_ALG uint16 // seal algorithm indicator. big-endian + + // const Filler: 0xFF 0xFF + SndSeqNum uint64 // encrypted sender's sequence number. big-endian + CheckSum []byte // authenticated checksum of { payload | header } + Payload []byte // your data! :) +} + +// Marshal the WrapToken into a byte slice. +// The payload should have been set and the checksum computed, otherwise an error is returned. +func (wt *WrapTokenV1) 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") + } + + bytes := make([]byte, 24 + len(wt.Payload)) + copy(bytes[0:], TOK_ID[:]) // Insert TOK_ID + copy(bytes[2:4], SGN_ALG_HMAC_MD5_ARCFOUR[:]) // Insert SGN_ALG + copy(bytes[4:6], SEAL_ALG_NONE[:]) // Insert SEAL_ALG + copy(bytes[6:8], FILLER[:]) // Insert Filler + + binary.BigEndian.PutUint64(bytes[8:16], wt.SndSeqNum) // Insert SND_SEQ + copy(bytes[16:24], wt.CheckSum) // Insert SGN_CKSUM + copy(bytes[24:], wt.Payload) // Insert Data + + 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 *WrapTokenV1) 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 *WrapTokenV1) 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):], getChecksumHeaderV1(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 getChecksumHeaderV1(senderSeqNum uint64) []byte { + header := make([]byte, 16) + copy(header[0:], TOK_ID[:]) + copy(header[2:], SGN_ALG_HMAC_MD5_ARCFOUR[:]) + copy(header[4:], SEAL_ALG_NONE[:][:]) + copy(header[6:], FILLER[:]) + 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 *WrapTokenV1) 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 WrapTokenV1. +func (wt *WrapTokenV1) Unmarshal(b []byte, expectFromAcceptor bool) error { + // This function maps onto GSS_Wrap() from RFC 1964 + // The GSS_Wrap() token has the following format: + // + // Byte no Name Description + // 0..1 TOK_ID Identification field. + // Tokens emitted by GSS_Wrap() contain + // the hex value 02 01 in this field. + // 2..3 SGN_ALG Checksum algorithm indicator. + // 00 00 - DES MAC MD5 + // 02 00 - DES MAC + // 01 00 - MD2.5 + // 11 00 - HMAC MD5 ARCFOUR + // 4..5 SEAL_ALG ff ff - none + // 00 00 - DES + // 6..7 Filler Contains ff ff + // 8..15 SND_SEQ Encrypted sequence number field. + // 16..23 SGN_CKSUM Checksum of plaintext padded data, + // calculated according to algorithm + // specified in SGN_ALG field. + // 24..last Data encrypted or plaintext padded data + start_position := 0 + + // Check if we can read a whole header + if len(b) < 16 { + return errors.New("GSSAPI: bytes shorter than header length") + } + + if b[0] == 0x60 { + start_position = 14 + } + + // Is the Token ID correct? + if !bytes.Equal(TOK_ID[:], b[start_position:start_position+2]) { + return fmt.Errorf("wrong Token ID. Expected %s, was %s", + hex.EncodeToString(TOK_ID_V2[:]), + hex.EncodeToString(b[start_position:start_position+2])) + } + // Check SGN_ALG + switch { + case bytes.Equal(SGN_ALG_DES_MAC[:], b[start_position+2:start_position+4]): + break + case bytes.Equal(SGN_ALG_DES_MAC[:], b[start_position+2:start_position+4]): + break + case bytes.Equal(SGN_ALG_DES_MAC[:], b[start_position+2:start_position+4]): + break + case bytes.Equal(SGN_ALG_DES_MAC[:], b[start_position+2:start_position+4]): + break + default: + return fmt.Errorf("Unsupported SGN_ALG value: %s", hex.EncodeToString(b[start_position+2:start_position+4])) + } + + // Check SEAL_ALG + switch { + case bytes.Equal(SEAL_ALG_NONE[:], b[start_position+4:start_position+6]): + break + case bytes.Equal(SEAL_ALG_DES[:], b[start_position+4:start_position+6]): + break + case bytes.Equal(SEAL_ALG_DES3_KD[:], b[start_position+4:start_position+6]): + break + case bytes.Equal(SEAL_ALG_ARCFOUR_HMAC[:], b[start_position+4:start_position+6]): + break + default: + return fmt.Errorf("Unsupported SEAL_ALG value: %s", hex.EncodeToString(b[start_position+4:start_position+6])) + } + + // Check the filler byte + if !bytes.Equal(FILLER[:], b[start_position+6:start_position+8]) { + return fmt.Errorf("unexpected filler byte: expecting 0xFFFF, was %s", hex.EncodeToString(b[start_position+6:start_position+8])) + } + + wt.SndSeqNum = binary.BigEndian.Uint64(b[start_position+8:start_position+16]) + wt.CheckSum = b[start_position+16:start_position+24] + wt.Payload = b[start_position+24:] + 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 NewInitiatorWrapTokenV1(payload []byte, key types.EncryptionKey) (*WrapTokenV1, error) { + encType, err := crypto.GetEtype(key.KeyType) + if err != nil { + return nil, err + } + + token := WrapTokenV1{ + SndSeqNum: 0, + Payload: payload, + } + + if err := token.SetCheckSum(key, keyusage.GSSAPI_INITIATOR_SEAL); err != nil { + return nil, err + } + + return &token, nil +} diff --git a/v8/gssapi/wrapTokenV1_test.go b/v8/gssapi/wrapTokenV1_test.go new file mode 100644 index 00000000..6e5a8f6a --- /dev/null +++ b/v8/gssapi/wrapTokenV1_test.go @@ -0,0 +1,6 @@ +package gssapi + +const ( + kerberosServerToken = "" + applicationReplyToken = "" +) From f7b977bb17367543026d5e22ddb6648fb19a36e1 Mon Sep 17 00:00:00 2001 From: Mikhail Molotkov <1488maiklm@gmail.com> Date: Thu, 17 Nov 2022 21:44:40 +0000 Subject: [PATCH 02/11] Fix weird indentation typo --- v8/gssapi/wrapTokenV1.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v8/gssapi/wrapTokenV1.go b/v8/gssapi/wrapTokenV1.go index 119f5e8b..fca610fd 100644 --- a/v8/gssapi/wrapTokenV1.go +++ b/v8/gssapi/wrapTokenV1.go @@ -15,7 +15,7 @@ import ( const ( // Length of the Wrap Token's header - TOKEN_NO_CKSUM_SIZE = 16 + TOKEN_NO_CKSUM_SIZE = 16 // TOKEN_ID_POS int16 = 0 // SIGN_ALG_POS int16 = 2 @@ -27,7 +27,7 @@ const ( var TOK_ID = [2]byte{0x02, 0x01} // Filler in WrapToken v1 -var FILLER = [2]byte{0xFF, 0xFF} +var FILLER = [2]byte{0xFF, 0xFF} // Use DES MAC MD5 checksum - RFC 1964 var SGN_ALG_DES_MAC_MD5 = [2]byte{0x00, 0x00} From 6c0148cf0a20b1f50591b59b9f8717f7ec69dd8e Mon Sep 17 00:00:00 2001 From: Mikhail Molotkov <1488maiklm@gmail.com> Date: Thu, 17 Nov 2022 21:59:16 +0000 Subject: [PATCH 03/11] Fix typo in variable name --- v8/gssapi/wrapTokenV1.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v8/gssapi/wrapTokenV1.go b/v8/gssapi/wrapTokenV1.go index fca610fd..89fa196a 100644 --- a/v8/gssapi/wrapTokenV1.go +++ b/v8/gssapi/wrapTokenV1.go @@ -191,7 +191,7 @@ func (wt *WrapTokenV1) Unmarshal(b []byte, expectFromAcceptor bool) error { // Is the Token ID correct? if !bytes.Equal(TOK_ID[:], b[start_position:start_position+2]) { return fmt.Errorf("wrong Token ID. Expected %s, was %s", - hex.EncodeToString(TOK_ID_V2[:]), + hex.EncodeToString(TOK_ID[:]), hex.EncodeToString(b[start_position:start_position+2])) } // Check SGN_ALG From f571e30691616df5a89c37ab17b87efbbcb09e90 Mon Sep 17 00:00:00 2001 From: Mikhail Molotkov <1488maiklm@gmail.com> Date: Fri, 18 Nov 2022 01:02:07 +0000 Subject: [PATCH 04/11] Now we are past Unmarshal, need to figure out chesum checks --- v8/gssapi/wrapTokenV1.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/v8/gssapi/wrapTokenV1.go b/v8/gssapi/wrapTokenV1.go index 89fa196a..f31eafe6 100644 --- a/v8/gssapi/wrapTokenV1.go +++ b/v8/gssapi/wrapTokenV1.go @@ -185,7 +185,7 @@ func (wt *WrapTokenV1) Unmarshal(b []byte, expectFromAcceptor bool) error { } if b[0] == 0x60 { - start_position = 14 + start_position = 13 } // Is the Token ID correct? @@ -194,15 +194,16 @@ func (wt *WrapTokenV1) Unmarshal(b []byte, expectFromAcceptor bool) error { hex.EncodeToString(TOK_ID[:]), hex.EncodeToString(b[start_position:start_position+2])) } + // Check SGN_ALG switch { - case bytes.Equal(SGN_ALG_DES_MAC[:], b[start_position+2:start_position+4]): + case bytes.Equal(SGN_ALG_DES_MAC_MD5[:], b[start_position+2:start_position+4]): break - case bytes.Equal(SGN_ALG_DES_MAC[:], b[start_position+2:start_position+4]): + case bytes.Equal(SGN_ALG_DES_MAC[:], b[start_position+2:start_position+4]): break - case bytes.Equal(SGN_ALG_DES_MAC[:], b[start_position+2:start_position+4]): + case bytes.Equal(SGN_ALG_HMAC_SHA1_DES3_KD[:], b[start_position+2:start_position+4]): break - case bytes.Equal(SGN_ALG_DES_MAC[:], b[start_position+2:start_position+4]): + case bytes.Equal(SGN_ALG_HMAC_MD5_ARCFOUR[:], b[start_position+2:start_position+4]): break default: return fmt.Errorf("Unsupported SGN_ALG value: %s", hex.EncodeToString(b[start_position+2:start_position+4])) @@ -239,6 +240,8 @@ func (wt *WrapTokenV1) Unmarshal(b []byte, expectFromAcceptor bool) error { // This is currently not supported. func NewInitiatorWrapTokenV1(payload []byte, key types.EncryptionKey) (*WrapTokenV1, error) { encType, err := crypto.GetEtype(key.KeyType) + _ = encType + if err != nil { return nil, err } From 3ca18da82bc93e4c752ed6f2056185a90d0a6459 Mon Sep 17 00:00:00 2001 From: Mikhail Molotkov <1488maiklm@gmail.com> Date: Sat, 19 Nov 2022 01:11:34 +0000 Subject: [PATCH 05/11] Trying to figure out checksum calculation still --- v8/gssapi/wrapTokenV1.go | 111 +++++++++++++++++++++++---------------- 1 file changed, 67 insertions(+), 44 deletions(-) diff --git a/v8/gssapi/wrapTokenV1.go b/v8/gssapi/wrapTokenV1.go index f31eafe6..e9b9f581 100644 --- a/v8/gssapi/wrapTokenV1.go +++ b/v8/gssapi/wrapTokenV1.go @@ -14,12 +14,8 @@ import ( ) const ( - // Length of the Wrap Token's header - TOKEN_NO_CKSUM_SIZE = 16 - - // TOKEN_ID_POS int16 = 0 - // SIGN_ALG_POS int16 = 2 - // SEAL_ALG_POS int16 = 4 + // Length of the Wrap Token v1 header + TOKEN_NO_CKSUM_SIZE = 8 ) // ===== Almost const 2 bytes values to represent various values from GSS API RFCs @@ -58,15 +54,17 @@ var SEAL_ALG_ARCFOUR_HMAC = [2]byte{0x10, 0x00} // WrapTokenV1 represents a GSS API Wrap token v1, as defined in RFC 1964. // It contains the header fields, the payload and the checksum, and provides // the logic for converting to/from bytes plus computing and verifying checksums +// This specific Token is for RC4-HMAC Wrap as per https://datatracker.ietf.org/doc/html/rfc4757#section-7.3 type WrapTokenV1 struct { // const GSS Token ID: 0x02 0x01 - SGN_ALG uint16 // checksum algorithm indicator. big-endian - SEAL_ALG uint16 // seal algorithm indicator. big-endian + SGN_ALG uint16 // Checksum algorithm indicator: big-endian + SEAL_ALG uint16 // Seal algorithm indicator: big-endian // const Filler: 0xFF 0xFF - SndSeqNum uint64 // encrypted sender's sequence number. big-endian - CheckSum []byte // authenticated checksum of { payload | header } - Payload []byte // your data! :) + SndSeqNum uint64 // Encrypted sender's sequence number: big-endian + CheckSum []byte // Checksum of plaintext padded data: { payload | header } + Confounder []byte // Random confounder + Payload []byte // Encrypted or plaintext padded data } // Marshal the WrapToken into a byte slice. @@ -102,10 +100,12 @@ func (wt *WrapTokenV1) SetCheckSum(key types.EncryptionKey, keyUsage uint32) err 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 } @@ -116,26 +116,42 @@ func (wt *WrapTokenV1) computeCheckSum(key types.EncryptionKey, keyUsage uint32) 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):], getChecksumHeaderV1(wt.SndSeqNum)) + + if wt.Confounder == nil { + return nil, errors.New("cannot compute checksum with uninitialized confounder") + } + + // Build a slice containing { header | confounder | payload } + header := getChecksumHeaderV1() + checksumMe := make([]byte, len(header) + len(wt.Confounder) + len(wt.Payload)) + copy(checksumMe[0:], header) + copy(checksumMe[len(header):], wt.Confounder) + copy(checksumMe[len(header) + len(wt.Confounder):], wt.Payload) encType, err := crypto.GetEtype(key.KeyType) if err != nil { return nil, err } - return encType.GetChecksumHash(key.KeyValue, checksumMe, keyUsage) + + fmt.Printf("keyType: %d, keyValue: %s, keyUsage: %d, checksumMe: %s\n", key.KeyType, hex.EncodeToString(key.KeyValue), keyUsage, hex.EncodeToString(checksumMe)) + + checksumHash, err := encType.GetChecksumHash(key.KeyValue, checksumMe, keyUsage) + + if err!= nil { + return nil, err + } + + return checksumHash[:8], nil } // Build a header suitable for a checksum computation -func getChecksumHeaderV1(senderSeqNum uint64) []byte { - header := make([]byte, 16) +func getChecksumHeaderV1() []byte { + header := make([]byte, 8) copy(header[0:], TOK_ID[:]) copy(header[2:], SGN_ALG_HMAC_MD5_ARCFOUR[:]) - copy(header[4:], SEAL_ALG_NONE[:][:]) + copy(header[4:], SEAL_ALG_ARCFOUR_HMAC[:]) copy(header[6:], FILLER[:]) - binary.BigEndian.PutUint64(header[8:], senderSeqNum) + return header } @@ -155,28 +171,32 @@ func (wt *WrapTokenV1) Verify(key types.EncryptionKey, keyUsage uint32) (bool, e return true, nil } + // Unmarshal bytes into the corresponding WrapTokenV1. func (wt *WrapTokenV1) Unmarshal(b []byte, expectFromAcceptor bool) error { - // This function maps onto GSS_Wrap() from RFC 1964 - // The GSS_Wrap() token has the following format: - // - // Byte no Name Description - // 0..1 TOK_ID Identification field. - // Tokens emitted by GSS_Wrap() contain - // the hex value 02 01 in this field. - // 2..3 SGN_ALG Checksum algorithm indicator. - // 00 00 - DES MAC MD5 - // 02 00 - DES MAC - // 01 00 - MD2.5 - // 11 00 - HMAC MD5 ARCFOUR - // 4..5 SEAL_ALG ff ff - none - // 00 00 - DES - // 6..7 Filler Contains ff ff - // 8..15 SND_SEQ Encrypted sequence number field. - // 16..23 SGN_CKSUM Checksum of plaintext padded data, - // calculated according to algorithm - // specified in SGN_ALG field. - // 24..last Data encrypted or plaintext padded data + // This function maps onto GSS_Wrap() from RFC 1964 + // The GSS_Wrap() token has the following format: + // + // Byte no Name Description + // 0..1 TOK_ID Identification field. + // Tokens emitted by GSS_Wrap() contain + // the hex value 02 01 in this field. + // 2..3 SGN_ALG Checksum algorithm indicator. + // 00 00 - DES MAC MD5 + // 02 00 - DES MAC + // 01 00 - MD2.5 + // 11 00 - HMAC MD5 ARCFOUR + // 4..5 SEAL_ALG ff ff - none + // 00 00 - DES + // 02 00 - DES3-KD + // 10 00 - ARCFOUR-HMAC + // 6..7 Filler Contains ff ff + // 8..15 SND_SEQ Encrypted sequence number field. + // 16..23 SGN_CKSUM Checksum of plaintext padded data, + // calculated according to algorithm + // specified in SGN_ALG field. + // 24..31 Confounder Random confounder + // 32..last Data encrypted or plaintext padded data start_position := 0 // Check if we can read a whole header @@ -228,9 +248,11 @@ func (wt *WrapTokenV1) Unmarshal(b []byte, expectFromAcceptor bool) error { return fmt.Errorf("unexpected filler byte: expecting 0xFFFF, was %s", hex.EncodeToString(b[start_position+6:start_position+8])) } - wt.SndSeqNum = binary.BigEndian.Uint64(b[start_position+8:start_position+16]) - wt.CheckSum = b[start_position+16:start_position+24] - wt.Payload = b[start_position+24:] + wt.SndSeqNum = binary.BigEndian.Uint64(b[start_position+8:start_position+16]) + wt.CheckSum = b[start_position+16:start_position+24] + wt.Confounder = b[start_position+24:start_position+32] + wt.Payload = b[start_position+32:] + fmt.Printf("Unmarshal! SndSeqNum: %s, CheckSum: %s, Confounder: %s, Payload: %s\n", hex.EncodeToString(b[start_position+8:start_position+16]), hex.EncodeToString(wt.CheckSum), hex.EncodeToString(wt.Confounder), hex.EncodeToString(wt.Payload)) return nil } @@ -248,7 +270,8 @@ func NewInitiatorWrapTokenV1(payload []byte, key types.EncryptionKey) (*WrapToke token := WrapTokenV1{ SndSeqNum: 0, - Payload: payload, + Confounder: payload[:8], + Payload: payload[8:], } if err := token.SetCheckSum(key, keyusage.GSSAPI_INITIATOR_SEAL); err != nil { From 70aefaefef575f5907ced8304c7dd951346d3063 Mon Sep 17 00:00:00 2001 From: Mikhail Molotkov <1488maiklm@gmail.com> Date: Sat, 19 Nov 2022 17:47:43 +0000 Subject: [PATCH 06/11] Checksum is sorted & can connect to Kafka, but Kafka doesn't recognise the token; investigating further --- v8/gssapi/wrapTokenV1.go | 105 +++++++++++++++++++++++---------------- 1 file changed, 61 insertions(+), 44 deletions(-) diff --git a/v8/gssapi/wrapTokenV1.go b/v8/gssapi/wrapTokenV1.go index e9b9f581..3ec74d60 100644 --- a/v8/gssapi/wrapTokenV1.go +++ b/v8/gssapi/wrapTokenV1.go @@ -3,7 +3,8 @@ package gssapi import ( "bytes" "crypto/hmac" - "encoding/binary" + "crypto/rand" + // "encoding/binary" "encoding/hex" "errors" "fmt" @@ -19,6 +20,13 @@ const ( ) // ===== Almost const 2 bytes values to represent various values from GSS API RFCs +// 13 bytes Independent Token Header as per https://www.rfc-editor.org/rfc/rfc2743#page-81 +// 1. 0x60 -- Tag for [APPLICATION 0] SEQUENCE +// 2. 0x30 -- Token length octets (lengths of elements in 3-5 + actual WrapToken v1) +// 3. 0x06 -- Tag for OBJECT IDENTIFIER +// 4. 0x09 -- Object identifier length (lengths of elements in 5) +// 5. 0x2a to 0x02 -- Object identifier octets +var GSS_WRAP_HEADER = [13]byte{0x60, 0x30, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02} // 2 bytes identifying GSS API Wrap token v1 var TOK_ID = [2]byte{0x02, 0x01} @@ -61,7 +69,8 @@ type WrapTokenV1 struct { SEAL_ALG uint16 // Seal algorithm indicator: big-endian // const Filler: 0xFF 0xFF - SndSeqNum uint64 // Encrypted sender's sequence number: big-endian + // SndSeqNum uint64 // Encrypted sender's sequence number: big-endian + SndSeqNum []byte // Encrypted sender's sequence number: big-endian CheckSum []byte // Checksum of plaintext padded data: { payload | header } Confounder []byte // Random confounder Payload []byte // Encrypted or plaintext padded data @@ -77,37 +86,22 @@ func (wt *WrapTokenV1) Marshal() ([]byte, error) { return nil, errors.New("payload has not been set") } - bytes := make([]byte, 24 + len(wt.Payload)) - copy(bytes[0:], TOK_ID[:]) // Insert TOK_ID - copy(bytes[2:4], SGN_ALG_HMAC_MD5_ARCFOUR[:]) // Insert SGN_ALG - copy(bytes[4:6], SEAL_ALG_NONE[:]) // Insert SEAL_ALG - copy(bytes[6:8], FILLER[:]) // Insert Filler + bytes := make([]byte, 13 + 32 + len(wt.Payload)) // { len(GSS_WRAP_HEADER) | len(TOKEN.HEADER) | len (TOKEN.SGN_ALG) | len(TOKEN.SEAL_ALG) | len(FILLER) | len(SND_SEQ) | len(SGN_CHSUM) | len(Confounder) | len (Payload) } + copy(bytes[0:], GSS_WRAP_HEADER[:]) // Seems like the final token needs to have GSS_WRAP_HEADER (as per RFC 2743) + copy(bytes[13:], TOK_ID[:]) // Insert TOK_ID + copy(bytes[15:17], SGN_ALG_HMAC_MD5_ARCFOUR[:]) // Insert SGN_ALG + copy(bytes[17:19], SEAL_ALG_NONE[:]) // Insert SEAL_ALG + copy(bytes[19:21], FILLER[:]) // Insert Filler - binary.BigEndian.PutUint64(bytes[8:16], wt.SndSeqNum) // Insert SND_SEQ - copy(bytes[16:24], wt.CheckSum) // Insert SGN_CKSUM - copy(bytes[24:], wt.Payload) // Insert Data + copy(bytes[21:29], wt.SndSeqNum) // Insert SND_SEQ + copy(bytes[29:37], wt.CheckSum) // Insert SGN_CKSUM + copy(bytes[37:45], wt.Confounder) // Insert Confounder + copy(bytes[45:], wt.Payload) // Insert Data - 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 *WrapTokenV1) 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") - } + fmt.Printf("GSS_WRAP_HEADER: %s, TOK_ID: %s, SGN_ALG: %s, SEAL_ALG: %s, Filler: %s, SND_SEQ: %s, SGN_CKSUM: %s, Confounder: %s, Data: %s\n", hex.EncodeToString(bytes[0:13]), hex.EncodeToString(bytes[13:15]), hex.EncodeToString(bytes[15:17]), hex.EncodeToString(bytes[17:19]), hex.EncodeToString(bytes[19:21]), hex.EncodeToString(bytes[21:29]), hex.EncodeToString(bytes[29:37]), hex.EncodeToString(bytes[37:45]), hex.EncodeToString(bytes[45:])) + fmt.Printf("Final WrapToken v1 is: %s\n", hex.EncodeToString(bytes[:])) - chkSum, cErr := wt.computeCheckSum(key, keyUsage) - if cErr != nil { - return cErr - } - - wt.CheckSum = chkSum - return nil + return bytes, nil } // ComputeCheckSum computes and returns the checksum of this token, computed using the passed key and key usage. @@ -149,7 +143,7 @@ func getChecksumHeaderV1() []byte { header := make([]byte, 8) copy(header[0:], TOK_ID[:]) copy(header[2:], SGN_ALG_HMAC_MD5_ARCFOUR[:]) - copy(header[4:], SEAL_ALG_ARCFOUR_HMAC[:]) + copy(header[4:], SEAL_ALG_NONE[:]) copy(header[6:], FILLER[:]) return header @@ -248,30 +242,33 @@ func (wt *WrapTokenV1) Unmarshal(b []byte, expectFromAcceptor bool) error { return fmt.Errorf("unexpected filler byte: expecting 0xFFFF, was %s", hex.EncodeToString(b[start_position+6:start_position+8])) } - wt.SndSeqNum = binary.BigEndian.Uint64(b[start_position+8:start_position+16]) + // wt.SndSeqNum = binary.BigEndian.Uint64(b[start_position+8:start_position+16]) + wt.SndSeqNum = b[start_position+8:start_position+16] wt.CheckSum = b[start_position+16:start_position+24] wt.Confounder = b[start_position+24:start_position+32] wt.Payload = b[start_position+32:] - fmt.Printf("Unmarshal! SndSeqNum: %s, CheckSum: %s, Confounder: %s, Payload: %s\n", hex.EncodeToString(b[start_position+8:start_position+16]), hex.EncodeToString(wt.CheckSum), hex.EncodeToString(wt.Confounder), hex.EncodeToString(wt.Payload)) + fmt.Printf("Unmarshal! SndSeqNum: %s, CheckSum: %s, Confounder: %s, Payload: %s\n", hex.EncodeToString(wt.SndSeqNum), hex.EncodeToString(wt.CheckSum), hex.EncodeToString(wt.Confounder), hex.EncodeToString(wt.Payload)) 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 NewInitiatorWrapTokenV1(payload []byte, key types.EncryptionKey) (*WrapTokenV1, error) { - encType, err := crypto.GetEtype(key.KeyType) - _ = encType - +// NewInitiatorWrapToken builds a new initiator token +func NewInitiatorWrapTokenV1(payload []byte, seq_num []byte, key types.EncryptionKey) (*WrapTokenV1, error) { + // Create random Confounder + confounder := make([]byte, 8) + _, err := rand.Read(confounder) if err != nil { return nil, err } + // Create new SND_SEQ based on request SND_SEQ + new_seq_num := make([]byte, 8) + copy(new_seq_num[:4], seq_num[4:]) + copy(new_seq_num[4:], []byte{0x00, 0x00, 0x00, 0x00}) + token := WrapTokenV1{ - SndSeqNum: 0, - Confounder: payload[:8], - Payload: payload[8:], + SndSeqNum: new_seq_num[:], + Confounder: confounder[:], + Payload: payload[:], } if err := token.SetCheckSum(key, keyusage.GSSAPI_INITIATOR_SEAL); err != nil { @@ -280,3 +277,23 @@ func NewInitiatorWrapTokenV1(payload []byte, key types.EncryptionKey) (*WrapToke return &token, 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 *WrapTokenV1) 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 +} From 3ecafe79966125136026b207c2f7ed3cda8b54a8 Mon Sep 17 00:00:00 2001 From: Mikhail Molotkov <1488maiklm@gmail.com> Date: Sat, 19 Nov 2022 23:10:59 +0000 Subject: [PATCH 07/11] WrapToken v1 looks good to me, just need to add tests --- v8/gssapi/wrapTokenV1.go | 49 ++++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/v8/gssapi/wrapTokenV1.go b/v8/gssapi/wrapTokenV1.go index 3ec74d60..9b438b2f 100644 --- a/v8/gssapi/wrapTokenV1.go +++ b/v8/gssapi/wrapTokenV1.go @@ -3,8 +3,9 @@ package gssapi import ( "bytes" "crypto/hmac" + "crypto/md5" "crypto/rand" - // "encoding/binary" + "crypto/rc4" "encoding/hex" "errors" "fmt" @@ -27,6 +28,7 @@ const ( // 4. 0x09 -- Object identifier length (lengths of elements in 5) // 5. 0x2a to 0x02 -- Object identifier octets var GSS_WRAP_HEADER = [13]byte{0x60, 0x30, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02} + // 2 bytes identifying GSS API Wrap token v1 var TOK_ID = [2]byte{0x02, 0x01} @@ -65,8 +67,8 @@ var SEAL_ALG_ARCFOUR_HMAC = [2]byte{0x10, 0x00} // This specific Token is for RC4-HMAC Wrap as per https://datatracker.ietf.org/doc/html/rfc4757#section-7.3 type WrapTokenV1 struct { // const GSS Token ID: 0x02 0x01 - SGN_ALG uint16 // Checksum algorithm indicator: big-endian - SEAL_ALG uint16 // Seal algorithm indicator: big-endian + SGN_ALG []byte // Checksum algorithm indicator + SEAL_ALG []byte // Seal algorithm indicator // const Filler: 0xFF 0xFF // SndSeqNum uint64 // Encrypted sender's sequence number: big-endian @@ -77,33 +79,60 @@ type WrapTokenV1 struct { } // Marshal the WrapToken into a byte slice. -// The payload should have been set and the checksum computed, otherwise an error is returned. -func (wt *WrapTokenV1) Marshal() ([]byte, error) { +// The payload & checksum should be present, otherwise an error is returned. +func (wt *WrapTokenV1) Marshal(key types.EncryptionKey) ([]byte, error) { if wt.CheckSum == nil { - return nil, errors.New("checksum has not been set") + return nil, errors.New("Token SGN_CKSUM has not been set") } if wt.Payload == nil { - return nil, errors.New("payload has not been set") + return nil, errors.New("Token Payload has not been set") } - bytes := make([]byte, 13 + 32 + len(wt.Payload)) // { len(GSS_WRAP_HEADER) | len(TOKEN.HEADER) | len (TOKEN.SGN_ALG) | len(TOKEN.SEAL_ALG) | len(FILLER) | len(SND_SEQ) | len(SGN_CHSUM) | len(Confounder) | len (Payload) } + bytes := make([]byte, 13 + 32 + len(wt.Payload)) // { len(GSS_WRAP_HEADER) = 13 | len(TOKEN.HEADER) + len (TOKEN.SGN_ALG) + len(TOKEN.SEAL_ALG) + len(FILLER) + len(SND_SEQ) + len(SGN_CHSUM) + len(Confounder) = 32 | len (Payload) } copy(bytes[0:], GSS_WRAP_HEADER[:]) // Seems like the final token needs to have GSS_WRAP_HEADER (as per RFC 2743) copy(bytes[13:], TOK_ID[:]) // Insert TOK_ID copy(bytes[15:17], SGN_ALG_HMAC_MD5_ARCFOUR[:]) // Insert SGN_ALG copy(bytes[17:19], SEAL_ALG_NONE[:]) // Insert SEAL_ALG copy(bytes[19:21], FILLER[:]) // Insert Filler + fmt.Printf("Original SND_SEQ: %s\n", hex.EncodeToString(wt.SndSeqNum)) + wt.encryptSndSeqNum(key.KeyValue) + copy(bytes[21:29], wt.SndSeqNum) // Insert SND_SEQ copy(bytes[29:37], wt.CheckSum) // Insert SGN_CKSUM copy(bytes[37:45], wt.Confounder) // Insert Confounder copy(bytes[45:], wt.Payload) // Insert Data - fmt.Printf("GSS_WRAP_HEADER: %s, TOK_ID: %s, SGN_ALG: %s, SEAL_ALG: %s, Filler: %s, SND_SEQ: %s, SGN_CKSUM: %s, Confounder: %s, Data: %s\n", hex.EncodeToString(bytes[0:13]), hex.EncodeToString(bytes[13:15]), hex.EncodeToString(bytes[15:17]), hex.EncodeToString(bytes[17:19]), hex.EncodeToString(bytes[19:21]), hex.EncodeToString(bytes[21:29]), hex.EncodeToString(bytes[29:37]), hex.EncodeToString(bytes[37:45]), hex.EncodeToString(bytes[45:])) - fmt.Printf("Final WrapToken v1 is: %s\n", hex.EncodeToString(bytes[:])) + fmt.Printf("Final WrapToken v1 is: GSS_WRAP_HEADER: %s, TOK_ID: %s, SGN_ALG: %s, SEAL_ALG: %s, Filler: %s, SND_SEQ: %s, SGN_CKSUM: %s, Confounder: %s, Data: %s\n", hex.EncodeToString(bytes[0:13]), hex.EncodeToString(bytes[13:15]), hex.EncodeToString(bytes[15:17]), hex.EncodeToString(bytes[17:19]), hex.EncodeToString(bytes[19:21]), hex.EncodeToString(bytes[21:29]), hex.EncodeToString(bytes[29:37]), hex.EncodeToString(bytes[37:45]), hex.EncodeToString(bytes[45:])) + fmt.Printf("Final WrapToken v1 is (as 1 string): %s\n", hex.EncodeToString(bytes[:])) return bytes, nil } +func (wt *WrapTokenV1) encryptSndSeqNum(key []byte) (error) { + if wt.SndSeqNum == nil { + return errors.New("Token SND_SEQ has not been set") + } + + tb := []byte{0x00, 0x00, 0x00, 0x00} + + mac := hmac.New(md5.New, key) + mac.Write(tb) + interimHash := mac.Sum(nil) + + mac = hmac.New(md5.New, interimHash) + mac.Write(wt.SndSeqNum) + encryptHash := mac.Sum(nil) + + rc4Encryption, err := rc4.NewCipher(encryptHash) + if err != nil { + return err + } + + rc4Encryption.XORKeyStream(wt.SndSeqNum, wt.SndSeqNum) + 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 *WrapTokenV1) computeCheckSum(key types.EncryptionKey, keyUsage uint32) ([]byte, error) { From 7432f8e798f83f6292dd40a96405e742b606b641 Mon Sep 17 00:00:00 2001 From: Mikhail Molotkov <1488maiklm@gmail.com> Date: Sun, 20 Nov 2022 15:08:37 +0000 Subject: [PATCH 08/11] Getting better, but still think the token content is not correct (so Kafka rejects connection). Investigating --- CONTRIBUTING.md | 2 +- v8/gssapi/wrapTokenV1.go | 14 ++++--- v8/gssapi/wrapTokenV1_test.go | 79 ++++++++++++++++++++++++++++++++++- 3 files changed, 86 insertions(+), 9 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dec2d63a..25345503 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -45,7 +45,7 @@ This would require a minor (v\_.X.\_) version update. * Ensure tests pass. * Ensure godoc comments are created or updated as required for any new or updated code. * Ensure your contributions are formatted correctly with gofmt. The travis build will test this. -* Do not use external package dependencies. +* Do not use external package dependencies. As gokrb5 is designed to be a core library used in other applications it is best to avoid dependencies the project has no control over, other than the Go standard library, as issues with any dependency could have large knock on effects. * Provide useful commit messages. diff --git a/v8/gssapi/wrapTokenV1.go b/v8/gssapi/wrapTokenV1.go index 9b438b2f..13a678fa 100644 --- a/v8/gssapi/wrapTokenV1.go +++ b/v8/gssapi/wrapTokenV1.go @@ -27,7 +27,7 @@ const ( // 3. 0x06 -- Tag for OBJECT IDENTIFIER // 4. 0x09 -- Object identifier length (lengths of elements in 5) // 5. 0x2a to 0x02 -- Object identifier octets -var GSS_WRAP_HEADER = [13]byte{0x60, 0x30, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02} +var GSS_HEADER = [13]byte{0x60, 0x30, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02} // 2 bytes identifying GSS API Wrap token v1 var TOK_ID = [2]byte{0x02, 0x01} @@ -88,8 +88,8 @@ func (wt *WrapTokenV1) Marshal(key types.EncryptionKey) ([]byte, error) { return nil, errors.New("Token Payload has not been set") } - bytes := make([]byte, 13 + 32 + len(wt.Payload)) // { len(GSS_WRAP_HEADER) = 13 | len(TOKEN.HEADER) + len (TOKEN.SGN_ALG) + len(TOKEN.SEAL_ALG) + len(FILLER) + len(SND_SEQ) + len(SGN_CHSUM) + len(Confounder) = 32 | len (Payload) } - copy(bytes[0:], GSS_WRAP_HEADER[:]) // Seems like the final token needs to have GSS_WRAP_HEADER (as per RFC 2743) + bytes := make([]byte, 13 + 32 + len(wt.Payload)) // { len(GSS_HEADER) = 13 | len(TOKEN.HEADER) + len (TOKEN.SGN_ALG) + len(TOKEN.SEAL_ALG) + len(FILLER) + len(SND_SEQ) + len(SGN_CHSUM) + len(Confounder) = 32 | len (Payload) } + copy(bytes[0:], GSS_HEADER[:]) // Final token needs to have GSS_HEADER (as per RFC 2743) copy(bytes[13:], TOK_ID[:]) // Insert TOK_ID copy(bytes[15:17], SGN_ALG_HMAC_MD5_ARCFOUR[:]) // Insert SGN_ALG copy(bytes[17:19], SEAL_ALG_NONE[:]) // Insert SEAL_ALG @@ -103,8 +103,8 @@ func (wt *WrapTokenV1) Marshal(key types.EncryptionKey) ([]byte, error) { copy(bytes[37:45], wt.Confounder) // Insert Confounder copy(bytes[45:], wt.Payload) // Insert Data - fmt.Printf("Final WrapToken v1 is: GSS_WRAP_HEADER: %s, TOK_ID: %s, SGN_ALG: %s, SEAL_ALG: %s, Filler: %s, SND_SEQ: %s, SGN_CKSUM: %s, Confounder: %s, Data: %s\n", hex.EncodeToString(bytes[0:13]), hex.EncodeToString(bytes[13:15]), hex.EncodeToString(bytes[15:17]), hex.EncodeToString(bytes[17:19]), hex.EncodeToString(bytes[19:21]), hex.EncodeToString(bytes[21:29]), hex.EncodeToString(bytes[29:37]), hex.EncodeToString(bytes[37:45]), hex.EncodeToString(bytes[45:])) - fmt.Printf("Final WrapToken v1 is (as 1 string): %s\n", hex.EncodeToString(bytes[:])) + fmt.Printf("Final WrapToken v1 is (bit by bit): GSS_HEADER: %s, TOK_ID: %s, SGN_ALG: %s, SEAL_ALG: %s, Filler: %s, SND_SEQ: %s, SGN_CKSUM: %s, Confounder: %s, Data: %s\n", hex.EncodeToString(bytes[0:13]), hex.EncodeToString(bytes[13:15]), hex.EncodeToString(bytes[15:17]), hex.EncodeToString(bytes[17:19]), hex.EncodeToString(bytes[19:21]), hex.EncodeToString(bytes[21:29]), hex.EncodeToString(bytes[29:37]), hex.EncodeToString(bytes[37:45]), hex.EncodeToString(bytes[45:])) + fmt.Printf("Final WrapToken v1 is (as string): %s\n", hex.EncodeToString(bytes[:])) return bytes, nil } @@ -300,7 +300,9 @@ func NewInitiatorWrapTokenV1(payload []byte, seq_num []byte, key types.Encryptio Payload: payload[:], } - if err := token.SetCheckSum(key, keyusage.GSSAPI_INITIATOR_SEAL); err != nil { + // keyusage.GSSAPI_ACCEPTOR_SIGN resolves into derivation salt = 13 which is the one we must use for RC4 WrapTokenV1 + // even though https://datatracker.ietf.org/doc/html/rfc4757#section-7.3 suggests to use derivation salt = 15 + if err := token.SetCheckSum(key, keyusage.GSSAPI_ACCEPTOR_SIGN); err != nil { return nil, err } diff --git a/v8/gssapi/wrapTokenV1_test.go b/v8/gssapi/wrapTokenV1_test.go index 6e5a8f6a..55207b07 100644 --- a/v8/gssapi/wrapTokenV1_test.go +++ b/v8/gssapi/wrapTokenV1_test.go @@ -1,6 +1,81 @@ package gssapi +import ( + "encoding/hex" + "testing" + + "github.com/jcmturner/gokrb5/v8/types" + "github.com/stretchr/testify/assert" +) + const ( - kerberosServerToken = "" - applicationReplyToken = "" + kerberosTokenV1 = "603006092a864886f71201020202011100ffffffffec07ef3418bb9d5384c646f1ce946db60c901a9dedf79ea30101000001" + replyToKerberosTokenV1 = "603006092a864886f71201020202011100ffffffff3085bf00a417fcf490f1c33c80eee85253734436d6f1048a0101000001" + + sessionKeyTypeV1 = 23 + sessionKeyV1 = "c5b294ffa21a9cb1050a13213a88cd7b" ) + +func getSessionKeyV1() types.EncryptionKey { + key, _ := hex.DecodeString(sessionKeyV1) + return types.EncryptionKey{ + KeyType: sessionKeyTypeV1, + KeyValue: key, + } +} + +func TestUnmarshal_KerberosTokenV1(t *testing.T) { + kerToken, _ := hex.DecodeString(kerberosTokenV1) + + expectedWrapTokenV1 := WrapTokenV1 {} + expectedWrapTokenV1.SndSeqNum = kerToken[21:29] + expectedWrapTokenV1.CheckSum = kerToken[29:37] + expectedWrapTokenV1.Confounder = kerToken[37:45] + expectedWrapTokenV1.Payload = kerToken[45:] + + var wt WrapTokenV1 + err := wt.Unmarshal(kerToken, false) + + assert.Nil(t, err, "Unexpected error occurred.") + assert.Equal(t, &expectedWrapTokenV1, &wt, "Token not decoded as expected.") +} + +func TestVerify_KerberosTokenV1(t *testing.T) { + kerToken, _ := hex.DecodeString(kerberosTokenV1) + + var wt WrapTokenV1 + err := wt.Unmarshal(kerToken, false) + assert.Nil(t, err, "Unexpected error occurred.") + + success, err := wt.Verify(getSessionKeyV1(), acceptorSign) + assert.Nil(t, err, "Unexpected error occurred.") + assert.Equal(t, true, success, "Token not decoded as expected.") +} + +func TestUnmarshal_ReplyToKerberosTokenV1(t *testing.T) { + kerToken, _ := hex.DecodeString(replyToKerberosTokenV1) + + expectedWrapTokenV1 := WrapTokenV1 {} + expectedWrapTokenV1.SndSeqNum = kerToken[21:29] + expectedWrapTokenV1.CheckSum = kerToken[29:37] + expectedWrapTokenV1.Confounder = kerToken[37:45] + expectedWrapTokenV1.Payload = kerToken[45:] + + var wt WrapTokenV1 + err := wt.Unmarshal(kerToken, false) + + assert.Nil(t, err, "Unexpected error occurred.") + assert.Equal(t, &expectedWrapTokenV1, &wt, "Token not decoded as expected.") +} + +func TestVerify_ReplyToKerberosTokenV1(t *testing.T) { + kerToken, _ := hex.DecodeString(replyToKerberosTokenV1) + + var wt WrapTokenV1 + err := wt.Unmarshal(kerToken, false) + assert.Nil(t, err, "Unexpected error occurred.") + + success, err := wt.Verify(getSessionKeyV1(), acceptorSign) + assert.Nil(t, err, "Unexpected error occurred.") + assert.Equal(t, true, success, "Token not decoded as expected.") +} From 3f1b08e79f34f7773f98de78e0adbcfdc5e9341e Mon Sep 17 00:00:00 2001 From: Mikhail Molotkov <1488maiklm@gmail.com> Date: Mon, 21 Nov 2022 01:44:51 +0000 Subject: [PATCH 09/11] Data needs to be padded & GSS_HEADER needs to be altered to ensure it has correct length byte set --- v8/gssapi/wrapTokenV1.go | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/v8/gssapi/wrapTokenV1.go b/v8/gssapi/wrapTokenV1.go index 13a678fa..4ad1c2f2 100644 --- a/v8/gssapi/wrapTokenV1.go +++ b/v8/gssapi/wrapTokenV1.go @@ -15,11 +15,6 @@ import ( "github.com/jcmturner/gokrb5/v8/types" ) -const ( - // Length of the Wrap Token v1 header - TOKEN_NO_CKSUM_SIZE = 8 -) - // ===== Almost const 2 bytes values to represent various values from GSS API RFCs // 13 bytes Independent Token Header as per https://www.rfc-editor.org/rfc/rfc2743#page-81 // 1. 0x60 -- Tag for [APPLICATION 0] SEQUENCE @@ -103,6 +98,13 @@ func (wt *WrapTokenV1) Marshal(key types.EncryptionKey) ([]byte, error) { copy(bytes[37:45], wt.Confounder) // Insert Confounder copy(bytes[45:], wt.Payload) // Insert Data + // Now we need to calculate the final length of the WrapToken (including GSS_HEADER minus first 2 bytes) + // and alter 2nd byte of GSS_HEADER to set the length + tokenLength := len(bytes) - 2 + tokenLengthByte := byte(tokenLength) + + bytes[1] = tokenLengthByte + fmt.Printf("Final WrapToken v1 is (bit by bit): GSS_HEADER: %s, TOK_ID: %s, SGN_ALG: %s, SEAL_ALG: %s, Filler: %s, SND_SEQ: %s, SGN_CKSUM: %s, Confounder: %s, Data: %s\n", hex.EncodeToString(bytes[0:13]), hex.EncodeToString(bytes[13:15]), hex.EncodeToString(bytes[15:17]), hex.EncodeToString(bytes[17:19]), hex.EncodeToString(bytes[19:21]), hex.EncodeToString(bytes[21:29]), hex.EncodeToString(bytes[29:37]), hex.EncodeToString(bytes[37:45]), hex.EncodeToString(bytes[45:])) fmt.Printf("Final WrapToken v1 is (as string): %s\n", hex.EncodeToString(bytes[:])) @@ -205,12 +207,12 @@ func (wt *WrapTokenV1) Unmarshal(b []byte, expectFromAcceptor bool) error { // Tokens emitted by GSS_Wrap() contain // the hex value 02 01 in this field. // 2..3 SGN_ALG Checksum algorithm indicator. - // 00 00 - DES MAC MD5 - // 02 00 - DES MAC - // 01 00 - MD2.5 + // 00 00 - DES MAC MD5 << please don't use this one as per https://datatracker.ietf.org/doc/html/rfc6649 + // 02 00 - DES MAC << please don't use this one as per https://datatracker.ietf.org/doc/html/rfc6649 + // 01 00 - MD2.5 << please don't use this one as per https://datatracker.ietf.org/doc/html/rfc6649 // 11 00 - HMAC MD5 ARCFOUR // 4..5 SEAL_ALG ff ff - none - // 00 00 - DES + // 00 00 - DES << please don't use this one as per https://datatracker.ietf.org/doc/html/rfc6649 // 02 00 - DES3-KD // 10 00 - ARCFOUR-HMAC // 6..7 Filler Contains ff ff @@ -282,6 +284,15 @@ func (wt *WrapTokenV1) Unmarshal(b []byte, expectFromAcceptor bool) error { // NewInitiatorWrapToken builds a new initiator token func NewInitiatorWrapTokenV1(payload []byte, seq_num []byte, key types.EncryptionKey) (*WrapTokenV1, error) { + // We need to pad the data before we do anything else + // as per https://datatracker.ietf.org/doc/html/rfc1964#section-1.2.2.3 + pad := (8 - (len(payload) % 8)) & 0x7 + + for i := 0; i < pad; i++ { + payload = append(payload, byte(pad)) + } + + // Create random Confounder confounder := make([]byte, 8) _, err := rand.Read(confounder) From af7ed796e7f38efad9d930f3648a6c227fcc29fc Mon Sep 17 00:00:00 2001 From: Mikhail Molotkov <1488maiklm@gmail.com> Date: Wed, 30 Nov 2022 01:08:27 +0000 Subject: [PATCH 10/11] RC4 Tokens are done, now need tests --- v8/gssapi/wrapTokenV1.go | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/v8/gssapi/wrapTokenV1.go b/v8/gssapi/wrapTokenV1.go index 4ad1c2f2..860cf954 100644 --- a/v8/gssapi/wrapTokenV1.go +++ b/v8/gssapi/wrapTokenV1.go @@ -22,7 +22,7 @@ import ( // 3. 0x06 -- Tag for OBJECT IDENTIFIER // 4. 0x09 -- Object identifier length (lengths of elements in 5) // 5. 0x2a to 0x02 -- Object identifier octets -var GSS_HEADER = [13]byte{0x60, 0x30, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02} +var GSS_HEADER = [13]byte{0x60, 0x2b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02} // 2 bytes identifying GSS API Wrap token v1 var TOK_ID = [2]byte{0x02, 0x01} @@ -91,7 +91,7 @@ func (wt *WrapTokenV1) Marshal(key types.EncryptionKey) ([]byte, error) { copy(bytes[19:21], FILLER[:]) // Insert Filler fmt.Printf("Original SND_SEQ: %s\n", hex.EncodeToString(wt.SndSeqNum)) - wt.encryptSndSeqNum(key.KeyValue) + wt.encryptSndSeqNum(key.KeyValue, wt.CheckSum) copy(bytes[21:29], wt.SndSeqNum) // Insert SND_SEQ copy(bytes[29:37], wt.CheckSum) // Insert SGN_CKSUM @@ -111,7 +111,7 @@ func (wt *WrapTokenV1) Marshal(key types.EncryptionKey) ([]byte, error) { return bytes, nil } -func (wt *WrapTokenV1) encryptSndSeqNum(key []byte) (error) { +func (wt *WrapTokenV1) encryptSndSeqNum(key []byte, checksum []byte) (error) { if wt.SndSeqNum == nil { return errors.New("Token SND_SEQ has not been set") } @@ -123,7 +123,7 @@ func (wt *WrapTokenV1) encryptSndSeqNum(key []byte) (error) { interimHash := mac.Sum(nil) mac = hmac.New(md5.New, interimHash) - mac.Write(wt.SndSeqNum) + mac.Write(checksum) encryptHash := mac.Sum(nil) rc4Encryption, err := rc4.NewCipher(encryptHash) @@ -221,11 +221,12 @@ func (wt *WrapTokenV1) Unmarshal(b []byte, expectFromAcceptor bool) error { // calculated according to algorithm // specified in SGN_ALG field. // 24..31 Confounder Random confounder - // 32..last Data encrypted or plaintext padded data + // 32..last Data Encrypted, according to algorithm specified + // in SEAL_ALG field or plaintext padded data start_position := 0 // Check if we can read a whole header - if len(b) < 16 { + if len(b) < 21 { return errors.New("GSSAPI: bytes shorter than header length") } @@ -284,15 +285,6 @@ func (wt *WrapTokenV1) Unmarshal(b []byte, expectFromAcceptor bool) error { // NewInitiatorWrapToken builds a new initiator token func NewInitiatorWrapTokenV1(payload []byte, seq_num []byte, key types.EncryptionKey) (*WrapTokenV1, error) { - // We need to pad the data before we do anything else - // as per https://datatracker.ietf.org/doc/html/rfc1964#section-1.2.2.3 - pad := (8 - (len(payload) % 8)) & 0x7 - - for i := 0; i < pad; i++ { - payload = append(payload, byte(pad)) - } - - // Create random Confounder confounder := make([]byte, 8) _, err := rand.Read(confounder) @@ -300,6 +292,20 @@ func NewInitiatorWrapTokenV1(payload []byte, seq_num []byte, key types.Encryptio return nil, err } + // We need to pad the data (confounder + payload) before we do anything else + // as per https://datatracker.ietf.org/doc/html/rfc1964#section-1.2.2.3 + // TODO: Possibly remove padding code below + // Reasoning: looking through the code an numerous runs, seems like we can skip padding + // because Kafka returns already padded data, so why bother? + // pad := (8 - (len(confounder)+len(payload) % 8)) & 0x7 + // if pad == 0 { + // payload = append(payload, byte(0x00)) + // } else { + // for i := 0; i < pad; i++ { + // payload = append(payload, byte(pad)) + // } + // } + // Create new SND_SEQ based on request SND_SEQ new_seq_num := make([]byte, 8) copy(new_seq_num[:4], seq_num[4:]) @@ -311,8 +317,8 @@ func NewInitiatorWrapTokenV1(payload []byte, seq_num []byte, key types.Encryptio Payload: payload[:], } - // keyusage.GSSAPI_ACCEPTOR_SIGN resolves into derivation salt = 13 which is the one we must use for RC4 WrapTokenV1 - // even though https://datatracker.ietf.org/doc/html/rfc4757#section-7.3 suggests to use derivation salt = 15 + // keyusage.GSSAPI_ACCEPTOR_SIGN (=23) resolves into derivation salt = 13 which is the one we must use for RC4 WrapTokenV1 + // even though https://datatracker.ietf.org/doc/html/rfc4757#section-7.3 suggests to use derivation salt = 15 (which is actually MIC's salt) if err := token.SetCheckSum(key, keyusage.GSSAPI_ACCEPTOR_SIGN); err != nil { return nil, err } From 1ff580080bca551f81e47651c77f11d437264e00 Mon Sep 17 00:00:00 2001 From: Mikhail Molotkov <1488maiklm@gmail.com> Date: Wed, 30 Nov 2022 21:35:39 +0000 Subject: [PATCH 11/11] GSSAPI version 1 is sorted; ready for PR --- v8/gssapi/wrapToken.go | 8 ---- v8/gssapi/wrapTokenV1.go | 77 ++++++++++++----------------------- v8/gssapi/wrapTokenV1_test.go | 34 +++++++++++++--- 3 files changed, 56 insertions(+), 63 deletions(-) diff --git a/v8/gssapi/wrapToken.go b/v8/gssapi/wrapToken.go index f3d1cd7e..09191039 100644 --- a/v8/gssapi/wrapToken.go +++ b/v8/gssapi/wrapToken.go @@ -135,14 +135,6 @@ func (wt *WrapToken) Unmarshal(b []byte, expectFromAcceptor bool) error { return errors.New("bytes shorter than header length") } - // Check for 0x60 as the first byte; As per RFC 4121 § 4.4, these Token ID - 0x60 0x00 to 0x60 0xFF - // are reserved to indicate 'Generic GSS-API token framing' that was used by - // GSS-API v1, and are not supported in GSS-API v2.. catch that specific case so - // we can emmit a useful message - if b[0] == 0x60 { - return errors.New(fmt.Sprintf("GSS-API v1 message tokens are not supported: %s", hex.EncodeToString(b[0:2]))) - } - // Is the Token ID correct? if !bytes.Equal(getGssWrapTokenId()[:], b[0:2]) { return fmt.Errorf("wrong Token ID. Expected %s, was %s", diff --git a/v8/gssapi/wrapTokenV1.go b/v8/gssapi/wrapTokenV1.go index 860cf954..588325a8 100644 --- a/v8/gssapi/wrapTokenV1.go +++ b/v8/gssapi/wrapTokenV1.go @@ -83,14 +83,14 @@ func (wt *WrapTokenV1) Marshal(key types.EncryptionKey) ([]byte, error) { return nil, errors.New("Token Payload has not been set") } - bytes := make([]byte, 13 + 32 + len(wt.Payload)) // { len(GSS_HEADER) = 13 | len(TOKEN.HEADER) + len (TOKEN.SGN_ALG) + len(TOKEN.SEAL_ALG) + len(FILLER) + len(SND_SEQ) + len(SGN_CHSUM) + len(Confounder) = 32 | len (Payload) } - copy(bytes[0:], GSS_HEADER[:]) // Final token needs to have GSS_HEADER (as per RFC 2743) - copy(bytes[13:], TOK_ID[:]) // Insert TOK_ID - copy(bytes[15:17], SGN_ALG_HMAC_MD5_ARCFOUR[:]) // Insert SGN_ALG - copy(bytes[17:19], SEAL_ALG_NONE[:]) // Insert SEAL_ALG - copy(bytes[19:21], FILLER[:]) // Insert Filler - - fmt.Printf("Original SND_SEQ: %s\n", hex.EncodeToString(wt.SndSeqNum)) + // { len(GSS_HEADER) = 13 | len(TOKEN.HEADER) + len (TOKEN.SGN_ALG) + len(TOKEN.SEAL_ALG) + len(FILLER) + len(SND_SEQ) + len(SGN_CHSUM) + len(Confounder) = 32 | len (Payload) } + bytes := make([]byte, 13 + 32 + len(wt.Payload)) + copy(bytes[0:], GSS_HEADER[:]) // Final token needs to have GSS_HEADER (as per RFC 2743) + copy(bytes[13:], TOK_ID[:]) // Insert TOK_ID + copy(bytes[15:17], wt.SGN_ALG) // Insert SGN_ALG + copy(bytes[17:19], wt.SEAL_ALG) // Insert SEAL_ALG + copy(bytes[19:21], FILLER[:]) // Insert Filler + wt.encryptSndSeqNum(key.KeyValue, wt.CheckSum) copy(bytes[21:29], wt.SndSeqNum) // Insert SND_SEQ @@ -102,11 +102,7 @@ func (wt *WrapTokenV1) Marshal(key types.EncryptionKey) ([]byte, error) { // and alter 2nd byte of GSS_HEADER to set the length tokenLength := len(bytes) - 2 tokenLengthByte := byte(tokenLength) - - bytes[1] = tokenLengthByte - - fmt.Printf("Final WrapToken v1 is (bit by bit): GSS_HEADER: %s, TOK_ID: %s, SGN_ALG: %s, SEAL_ALG: %s, Filler: %s, SND_SEQ: %s, SGN_CKSUM: %s, Confounder: %s, Data: %s\n", hex.EncodeToString(bytes[0:13]), hex.EncodeToString(bytes[13:15]), hex.EncodeToString(bytes[15:17]), hex.EncodeToString(bytes[17:19]), hex.EncodeToString(bytes[19:21]), hex.EncodeToString(bytes[21:29]), hex.EncodeToString(bytes[29:37]), hex.EncodeToString(bytes[37:45]), hex.EncodeToString(bytes[45:])) - fmt.Printf("Final WrapToken v1 is (as string): %s\n", hex.EncodeToString(bytes[:])) + bytes[1] = tokenLengthByte return bytes, nil } @@ -146,22 +142,21 @@ func (wt *WrapTokenV1) computeCheckSum(key types.EncryptionKey, keyUsage uint32) return nil, errors.New("cannot compute checksum with uninitialized confounder") } - // Build a slice containing { header | confounder | payload } - header := getChecksumHeaderV1() - checksumMe := make([]byte, len(header) + len(wt.Confounder) + len(wt.Payload)) - copy(checksumMe[0:], header) - copy(checksumMe[len(header):], wt.Confounder) - copy(checksumMe[len(header) + len(wt.Confounder):], wt.Payload) + // Build a slice containing { header=8 | confounder | payload } + checksumMe := make([]byte, 8 + len(wt.Confounder) + len(wt.Payload)) + copy(checksumMe[0:], TOK_ID[:]) + copy(checksumMe[2:], wt.SGN_ALG) + copy(checksumMe[4:], wt.SEAL_ALG) + copy(checksumMe[6:], FILLER[:]) + copy(checksumMe[8:], wt.Confounder) + copy(checksumMe[8 + len(wt.Confounder):], wt.Payload) encType, err := crypto.GetEtype(key.KeyType) if err != nil { return nil, err } - fmt.Printf("keyType: %d, keyValue: %s, keyUsage: %d, checksumMe: %s\n", key.KeyType, hex.EncodeToString(key.KeyValue), keyUsage, hex.EncodeToString(checksumMe)) - checksumHash, err := encType.GetChecksumHash(key.KeyValue, checksumMe, keyUsage) - if err!= nil { return nil, err } @@ -169,17 +164,6 @@ func (wt *WrapTokenV1) computeCheckSum(key types.EncryptionKey, keyUsage uint32) return checksumHash[:8], nil } -// Build a header suitable for a checksum computation -func getChecksumHeaderV1() []byte { - header := make([]byte, 8) - copy(header[0:], TOK_ID[:]) - copy(header[2:], SGN_ALG_HMAC_MD5_ARCFOUR[:]) - copy(header[4:], SEAL_ALG_NONE[:]) - copy(header[6:], FILLER[:]) - - 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. @@ -227,7 +211,7 @@ func (wt *WrapTokenV1) Unmarshal(b []byte, expectFromAcceptor bool) error { // Check if we can read a whole header if len(b) < 21 { - return errors.New("GSSAPI: bytes shorter than header length") + return errors.New("bytes shorter than header length") } if b[0] == 0x60 { @@ -254,6 +238,7 @@ func (wt *WrapTokenV1) Unmarshal(b []byte, expectFromAcceptor bool) error { default: return fmt.Errorf("Unsupported SGN_ALG value: %s", hex.EncodeToString(b[start_position+2:start_position+4])) } + wt.SGN_ALG = b[start_position+2:start_position+4] // Check SEAL_ALG switch { @@ -268,23 +253,23 @@ func (wt *WrapTokenV1) Unmarshal(b []byte, expectFromAcceptor bool) error { default: return fmt.Errorf("Unsupported SEAL_ALG value: %s", hex.EncodeToString(b[start_position+4:start_position+6])) } + wt.SEAL_ALG = b[start_position+4:start_position+6] // Check the filler byte if !bytes.Equal(FILLER[:], b[start_position+6:start_position+8]) { return fmt.Errorf("unexpected filler byte: expecting 0xFFFF, was %s", hex.EncodeToString(b[start_position+6:start_position+8])) } - // wt.SndSeqNum = binary.BigEndian.Uint64(b[start_position+8:start_position+16]) wt.SndSeqNum = b[start_position+8:start_position+16] wt.CheckSum = b[start_position+16:start_position+24] wt.Confounder = b[start_position+24:start_position+32] wt.Payload = b[start_position+32:] - fmt.Printf("Unmarshal! SndSeqNum: %s, CheckSum: %s, Confounder: %s, Payload: %s\n", hex.EncodeToString(wt.SndSeqNum), hex.EncodeToString(wt.CheckSum), hex.EncodeToString(wt.Confounder), hex.EncodeToString(wt.Payload)) + return nil } // NewInitiatorWrapToken builds a new initiator token -func NewInitiatorWrapTokenV1(payload []byte, seq_num []byte, key types.EncryptionKey) (*WrapTokenV1, error) { +func NewInitiatorWrapTokenV1(initial_toke *WrapTokenV1, key types.EncryptionKey) (*WrapTokenV1, error) { // Create random Confounder confounder := make([]byte, 8) _, err := rand.Read(confounder) @@ -294,27 +279,19 @@ func NewInitiatorWrapTokenV1(payload []byte, seq_num []byte, key types.Encryptio // We need to pad the data (confounder + payload) before we do anything else // as per https://datatracker.ietf.org/doc/html/rfc1964#section-1.2.2.3 - // TODO: Possibly remove padding code below - // Reasoning: looking through the code an numerous runs, seems like we can skip padding - // because Kafka returns already padded data, so why bother? - // pad := (8 - (len(confounder)+len(payload) % 8)) & 0x7 - // if pad == 0 { - // payload = append(payload, byte(0x00)) - // } else { - // for i := 0; i < pad; i++ { - // payload = append(payload, byte(pad)) - // } - // } + // However Kafka sends already padded data so we can ignore it // Create new SND_SEQ based on request SND_SEQ new_seq_num := make([]byte, 8) - copy(new_seq_num[:4], seq_num[4:]) + copy(new_seq_num[:4], initial_toke.SndSeqNum[4:]) copy(new_seq_num[4:], []byte{0x00, 0x00, 0x00, 0x00}) token := WrapTokenV1{ + SGN_ALG: initial_toke.SGN_ALG, + SEAL_ALG: initial_toke.SEAL_ALG, SndSeqNum: new_seq_num[:], Confounder: confounder[:], - Payload: payload[:], + Payload: initial_toke.Payload[:], } // keyusage.GSSAPI_ACCEPTOR_SIGN (=23) resolves into derivation salt = 13 which is the one we must use for RC4 WrapTokenV1 diff --git a/v8/gssapi/wrapTokenV1_test.go b/v8/gssapi/wrapTokenV1_test.go index 55207b07..60656bce 100644 --- a/v8/gssapi/wrapTokenV1_test.go +++ b/v8/gssapi/wrapTokenV1_test.go @@ -14,12 +14,15 @@ const ( sessionKeyTypeV1 = 23 sessionKeyV1 = "c5b294ffa21a9cb1050a13213a88cd7b" + + checksum = "7ba2b10519ffdc23" + sndSeqNum = "bbea237800000000" ) -func getSessionKeyV1() types.EncryptionKey { - key, _ := hex.DecodeString(sessionKeyV1) +func getSessionKeyV1(sessionKey string, keyType int32) types.EncryptionKey { + key, _ := hex.DecodeString(sessionKey) return types.EncryptionKey{ - KeyType: sessionKeyTypeV1, + KeyType: keyType, KeyValue: key, } } @@ -28,6 +31,8 @@ func TestUnmarshal_KerberosTokenV1(t *testing.T) { kerToken, _ := hex.DecodeString(kerberosTokenV1) expectedWrapTokenV1 := WrapTokenV1 {} + expectedWrapTokenV1.SGN_ALG = kerToken[15:17] + expectedWrapTokenV1.SEAL_ALG = kerToken[17:19] expectedWrapTokenV1.SndSeqNum = kerToken[21:29] expectedWrapTokenV1.CheckSum = kerToken[29:37] expectedWrapTokenV1.Confounder = kerToken[37:45] @@ -47,15 +52,18 @@ func TestVerify_KerberosTokenV1(t *testing.T) { err := wt.Unmarshal(kerToken, false) assert.Nil(t, err, "Unexpected error occurred.") - success, err := wt.Verify(getSessionKeyV1(), acceptorSign) + success, err := wt.Verify(getSessionKeyV1(sessionKeyV1, sessionKeyTypeV1), acceptorSign) assert.Nil(t, err, "Unexpected error occurred.") assert.Equal(t, true, success, "Token not decoded as expected.") } +// ======================== func TestUnmarshal_ReplyToKerberosTokenV1(t *testing.T) { kerToken, _ := hex.DecodeString(replyToKerberosTokenV1) expectedWrapTokenV1 := WrapTokenV1 {} + expectedWrapTokenV1.SGN_ALG = kerToken[15:17] + expectedWrapTokenV1.SEAL_ALG = kerToken[17:19] expectedWrapTokenV1.SndSeqNum = kerToken[21:29] expectedWrapTokenV1.CheckSum = kerToken[29:37] expectedWrapTokenV1.Confounder = kerToken[37:45] @@ -75,7 +83,23 @@ func TestVerify_ReplyToKerberosTokenV1(t *testing.T) { err := wt.Unmarshal(kerToken, false) assert.Nil(t, err, "Unexpected error occurred.") - success, err := wt.Verify(getSessionKeyV1(), acceptorSign) + success, err := wt.Verify(getSessionKeyV1(sessionKeyV1, sessionKeyTypeV1), acceptorSign) assert.Nil(t, err, "Unexpected error occurred.") assert.Equal(t, true, success, "Token not decoded as expected.") } + +// ======================== +func TestEncodeDecodeSndSeqNum(t *testing.T) { + session, _ := hex.DecodeString(sessionKeyV1) + check, _ := hex.DecodeString(checksum) + ssn, _ := hex.DecodeString(sndSeqNum) + + expectedSndSeqNum := []byte{0x93, 0x4b, 0x9a, 0x21, 0xc8, 0x23, 0xf8, 0x98} + + wrapTokenV1 := WrapTokenV1 {} + wrapTokenV1.SndSeqNum = ssn + + wrapTokenV1.encryptSndSeqNum(session, check) + + assert.Equal(t, expectedSndSeqNum, wrapTokenV1.SndSeqNum, "SndSeqNum is not correctly encrypted") +}