Skip to content

Commit

Permalink
Add support for creation / marshaling / enccryption of AP-REP messages
Browse files Browse the repository at this point in the history
  • Loading branch information
jake-scott committed Mar 23, 2021
1 parent 663478b commit 21e62db
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 0 deletions.
79 changes: 79 additions & 0 deletions v8/messages/APRep.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@ import (
"time"

"github.com/jcmturner/gofork/encoding/asn1"
"github.com/jcmturner/gokrb5/v8/asn1tools"
"github.com/jcmturner/gokrb5/v8/crypto"
"github.com/jcmturner/gokrb5/v8/iana"
"github.com/jcmturner/gokrb5/v8/iana/asnAppTag"
"github.com/jcmturner/gokrb5/v8/iana/keyusage"
"github.com/jcmturner/gokrb5/v8/iana/msgtype"
"github.com/jcmturner/gokrb5/v8/krberror"
"github.com/jcmturner/gokrb5/v8/messages"
"github.com/jcmturner/gokrb5/v8/types"
)

Expand Down Expand Up @@ -47,3 +52,77 @@ func (a *EncAPRepPart) Unmarshal(b []byte) error {
}
return nil
}

// Marshal the AP-REP message to a byte slice
func (a *APRep) Marshal() (b []byte, err error) {
b, err = asn1.Marshal(*a)
if err != nil {
return
}

b = asn1tools.AddASNAppTag(b, asnAppTag.APREP)
return
}

// Decrypt the encrypted part of the APRep message
func (a *APRep) DecryptEncPart(sessionKey types.EncryptionKey) (encpart EncAPRepPart, err error) {
decrypted, err := crypto.DecryptEncPart(a.EncPart, sessionKey, uint32(keyusage.AP_REP_ENCPART))
if err != nil {
err = krberror.Errorf(err, krberror.DecryptingError, "error decrypting AP-REP enc-part")
return
}

err = encpart.Unmarshal(decrypted)
if err != nil {
err = krberror.Errorf(err, krberror.EncodingError, "error unmarshalling decrypted AP-REP enc-part")
return
}

return
}

// Marshal the encrypted part of the APRep message to a byte slice
func (a *EncAPRepPart) Marshal() (b []byte, err error) {
b, err = asn1.Marshal(*a)
if err != nil {
return
}

b = asn1tools.AddASNAppTag(b, asnAppTag.EncAPRepPart)
return
}

// Create a new APRep message with an encrypted enc-part
func NewAPRep(tkt messages.Ticket, sessionKey types.EncryptionKey, encPart EncAPRepPart) (a APRep, err error) {
m, err := encPart.Marshal()
if err != nil {
err = krberror.Errorf(err, krberror.EncodingError, "marshaling error of AP-REP enc-part")
return
}

ed, err := crypto.GetEncryptedData(m, sessionKey, uint32(keyusage.AP_REP_ENCPART), tkt.EncPart.KVNO)
if err != nil {
err = krberror.Errorf(err, krberror.EncryptingError, "error encrypting AP-REP enc-part")
return
}

a = APRep{
PVNO: iana.PVNO,
MsgType: msgtype.KRB_AP_REP,
EncPart: ed,
}
return
}

// Verify a decrypted APRep enc-part against an authenticator. The authenticatror should be
// same as the one embedded in the APReq message that casused this APRep to be generated
func (a *EncAPRepPart) Verify(auth types.Authenticator) error {
// check the response has the same time values as the request
// Note - we can't use time.Equal() as m.clientCTime has a monotomic clock value and
// which causes the equality to fail
if !(a.CTime.Unix() == auth.CTime.Unix() && a.Cusec == auth.Cusec) {
return fmt.Errorf("ap-rep time stamp does not match authenticator")
}

return nil
}
85 changes: 85 additions & 0 deletions v8/messages/APRep_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,47 @@ import (
"github.com/jcmturner/gokrb5/v8/iana"
"github.com/jcmturner/gokrb5/v8/iana/msgtype"
"github.com/jcmturner/gokrb5/v8/test/testdata"
"github.com/jcmturner/gokrb5/v8/types"
"github.com/stretchr/testify/assert"
)

// Sample data from MIT Kerberos v1.19.1

// from src/tests/asn.1/ktest.h
const (
SAMPLE_USEC = 123456
SAMPLE_SEQ_NUMBER = 17
SAMPLE_NONCE = 42
SAMPLE_FLAGS = 0xFEDCBA98
SAMPLE_ERROR = 0x3C
)

func ktest_make_sample_ap_rep_enc_part() *EncAPRepPart {
tm, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME)
return &EncAPRepPart{
CTime: tm,
Cusec: SAMPLE_USEC,
Subkey: *ktest_make_sample_keyblock(),
SequenceNumber: SAMPLE_SEQ_NUMBER,
}
}

func ktest_make_sample_keyblock() *types.EncryptionKey {
kv := []byte("12345678")
return &types.EncryptionKey{
KeyType: 1,
KeyValue: kv,
}
}

func ktest_make_sample_enc_data() *types.EncryptedData {
return &types.EncryptedData{
EType: 0,
KVNO: 5,
Cipher: []byte("krbASN.1 test message"),
}
}

func TestUnmarshalAPRep(t *testing.T) {
t.Parallel()
var a APRep
Expand Down Expand Up @@ -67,3 +105,50 @@ func TestUnmarshalEncAPRepPart_optionalsNULL(t *testing.T) {
assert.Equal(t, tt, a.CTime, "CTime not as expected")
assert.Equal(t, 123456, a.Cusec, "Client microseconds not as expected")
}

// test with all fields populated
func TestAPRepEncPartMarshall(t *testing.T) {
t.Parallel()

want, err := hex.DecodeString(testdata.MarshaledKRB5ap_rep_enc_part)
assert.Nil(t, err, "error not expected decoding test data")

encpart := ktest_make_sample_ap_rep_enc_part()

b, err := encpart.Marshal()
assert.Nil(t, err, "enc part marshal error not expected")
assert.Equal(t, want, b)
}

// test with the optionals not present
func TestAPRepEncPartMarshall_optionalsNULL(t *testing.T) {
t.Parallel()

want, err := hex.DecodeString(testdata.MarshaledKRB5ap_rep_enc_partOptionalsNULL)
assert.Nil(t, err, "error not expected decoding test data")

encpart := ktest_make_sample_ap_rep_enc_part()
encpart.SequenceNumber = 0
encpart.Subkey = types.EncryptionKey{}

b, err := encpart.Marshal()
assert.Nil(t, err, "enc part marshal error not expected")
assert.Equal(t, want, b)
}

func TestAprepMarshal(t *testing.T) {
t.Parallel()

want, err := hex.DecodeString(testdata.MarshaledKRB5ap_rep)
assert.Nil(t, err, "error not expected decoding test data")

aprep := APRep{
PVNO: iana.PVNO,
MsgType: msgtype.KRB_AP_REP,
EncPart: *ktest_make_sample_enc_data(),
}

b, err := aprep.Marshal()
assert.Nil(t, err, "enc part marshal error not expected")
assert.Equal(t, want, b)
}

0 comments on commit 21e62db

Please sign in to comment.