# Implementing RabbitMQ part 2
## Finding out where to modified the code
Take a look back at the previous diagram

Number 2 is in [```https://github.com/free5gc/amf/blob/main/internal/sbi/consumer/ue_authentication.go```](https://github.com/free5gc/amf/blob/main/internal/sbi/consumer/ue_authentication.go)
:::info
```go=
package consumer
import (
"context"
"encoding/base64"
"fmt"
"net/url"
"strconv"
"github.com/antihax/optional"
amf_context "github.com/free5gc/amf/internal/context"
"github.com/free5gc/amf/internal/logger"
"github.com/free5gc/nas/nasType"
"github.com/free5gc/openapi"
"github.com/free5gc/openapi/Nausf_UEAuthentication"
"github.com/free5gc/openapi/models"
)
func SendUEAuthenticationAuthenticateRequest(ue *amf_context.AmfUe,
resynchronizationInfo *models.ResynchronizationInfo,
) (*models.UeAuthenticationCtx, *models.ProblemDetails, error) {
configuration := Nausf_UEAuthentication.NewConfiguration()
configuration.SetBasePath(ue.AusfUri)
client := Nausf_UEAuthentication.NewAPIClient(configuration)
amfSelf := amf_context.GetSelf()
servedGuami := amfSelf.ServedGuamiList[0]
var authInfo models.AuthenticationInfo
authInfo.SupiOrSuci = ue.Suci
if mnc, err := strconv.Atoi(servedGuami.PlmnId.Mnc); err != nil {
return nil, nil, err
} else {
authInfo.ServingNetworkName = fmt.Sprintf("5G:mnc%03d.mcc%s.3gppnetwork.org", mnc, servedGuami.PlmnId.Mcc)
}
if resynchronizationInfo != nil {
authInfo.ResynchronizationInfo = resynchronizationInfo
}
ueAuthenticationCtx, httpResponse, err := client.DefaultApi.UeAuthenticationsPost(context.Background(), authInfo)
defer func() {
if httpResponse != nil {
if rspCloseErr := httpResponse.Body.Close(); rspCloseErr != nil {
logger.ConsumerLog.Errorf("UeAuthenticationsPost response body cannot close: %+v",
rspCloseErr)
}
}
}()
if err == nil {
return &ueAuthenticationCtx, nil, nil
} else if httpResponse != nil {
if httpResponse.Status != err.Error() {
return nil, nil, err
}
problem := err.(openapi.GenericOpenAPIError).Model().(models.ProblemDetails)
return nil, &problem, nil
} else {
return nil, nil, openapi.ReportError("server no response")
}
}
```
:::
Number 3 is in [```https://github.com/free5gc/ausf/blob/main/internal/sbi/producer/ue_authentication.go```](https://github.com/free5gc/ausf/blob/main/internal/sbi/producer/ue_authentication.go)
:::info
```go=
func UeAuthPostRequestProcedure(updateAuthenticationInfo models.AuthenticationInfo) (*models.UeAuthenticationCtx,
string, *models.ProblemDetails,
) {
var responseBody models.UeAuthenticationCtx
var authInfoReq models.AuthenticationInfoRequest
supiOrSuci := updateAuthenticationInfo.SupiOrSuci
snName := updateAuthenticationInfo.ServingNetworkName
servingNetworkAuthorized := ausf_context.IsServingNetworkAuthorized(snName)
if !servingNetworkAuthorized {
var problemDetails models.ProblemDetails
problemDetails.Cause = "SERVING_NETWORK_NOT_AUTHORIZED"
problemDetails.Status = http.StatusForbidden
logger.UeAuthLog.Infoln("403 forbidden: serving network NOT AUTHORIZED")
return nil, "", &problemDetails
}
logger.UeAuthLog.Infoln("Serving network authorized")
responseBody.ServingNetworkName = snName
authInfoReq.ServingNetworkName = snName
self := ausf_context.GetSelf()
authInfoReq.AusfInstanceId = self.GetSelfID()
var lastEapID uint8
if updateAuthenticationInfo.ResynchronizationInfo != nil {
logger.UeAuthLog.Warningln("Auts: ", updateAuthenticationInfo.ResynchronizationInfo.Auts)
ausfCurrentSupi := ausf_context.GetSupiFromSuciSupiMap(supiOrSuci)
logger.UeAuthLog.Warningln(ausfCurrentSupi)
ausfCurrentContext := ausf_context.GetAusfUeContext(ausfCurrentSupi)
logger.UeAuthLog.Warningln(ausfCurrentContext.Rand)
if updateAuthenticationInfo.ResynchronizationInfo.Rand == "" {
updateAuthenticationInfo.ResynchronizationInfo.Rand = ausfCurrentContext.Rand
}
logger.UeAuthLog.Warningln("Rand: ", updateAuthenticationInfo.ResynchronizationInfo.Rand)
authInfoReq.ResynchronizationInfo = updateAuthenticationInfo.ResynchronizationInfo
lastEapID = ausfCurrentContext.EapID
}
udmUrl := getUdmUrl(self.NrfUri)
client := createClientToUdmUeau(udmUrl)
authInfoResult, rsp, err := client.GenerateAuthDataApi.GenerateAuthData(context.Background(), supiOrSuci, authInfoReq)
if err != nil {
logger.UeAuthLog.Infoln(err.Error())
var problemDetails models.ProblemDetails
if authInfoResult.AuthenticationVector == nil {
problemDetails.Cause = "AV_GENERATION_PROBLEM"
} else {
problemDetails.Cause = "UPSTREAM_SERVER_ERROR"
}
problemDetails.Status = int32(rsp.StatusCode)
return nil, "", &problemDetails
}
defer func() {
if rspCloseErr := rsp.Body.Close(); rspCloseErr != nil {
logger.UeAuthLog.Errorf("GenerateAuthDataApi response body cannot close: %+v", rspCloseErr)
}
}()
ueid := authInfoResult.Supi
ausfUeContext := ausf_context.NewAusfUeContext(ueid)
ausfUeContext.ServingNetworkName = snName
ausfUeContext.AuthStatus = models.AuthResult_ONGOING
ausfUeContext.UdmUeauUrl = udmUrl
ausf_context.AddAusfUeContextToPool(ausfUeContext)
logger.UeAuthLog.Infof("Add SuciSupiPair (%s, %s) to map.\n", supiOrSuci, ueid)
ausf_context.AddSuciSupiPairToMap(supiOrSuci, ueid)
locationURI := self.Url + factory.AusfAuthResUriPrefix + "/ue-authentications/" + supiOrSuci
putLink := locationURI
if authInfoResult.AuthType == models.AuthType__5_G_AKA {
logger.UeAuthLog.Infoln("Use 5G AKA auth method")
putLink += "/5g-aka-confirmation"
// Derive HXRES* from XRES*
concat := authInfoResult.AuthenticationVector.Rand + authInfoResult.AuthenticationVector.XresStar
var hxresStarBytes []byte
if bytes, err := hex.DecodeString(concat); err != nil {
logger.Auth5gAkaLog.Errorf("decode concat error: %+v", err)
return nil, "",
&models.ProblemDetails{
Title: "Concat Decode Problem",
Cause: "CONCAT_DECODE_PROBLEM",
Detail: err.Error(),
Status: http.StatusInternalServerError,
}
} else {
hxresStarBytes = bytes
}
hxresStarAll := sha256.Sum256(hxresStarBytes)
hxresStar := hex.EncodeToString(hxresStarAll[16:]) // last 128 bits
logger.Auth5gAkaLog.Infof("XresStar = %x\n", authInfoResult.AuthenticationVector.XresStar)
// Derive Kseaf from Kausf
Kausf := authInfoResult.AuthenticationVector.Kausf
var KausfDecode []byte
if ausfDecode, err := hex.DecodeString(Kausf); err != nil {
logger.Auth5gAkaLog.Errorf("decode Kausf failed: %+v", err)
return nil, "",
&models.ProblemDetails{
Title: "Kausf Decode Problem",
Cause: "KAUSF_DECODE_PROBLEM",
Detail: err.Error(),
Status: http.StatusInternalServerError,
}
} else {
KausfDecode = ausfDecode
}
P0 := []byte(snName)
Kseaf, err := ueauth.GetKDFValue(KausfDecode, ueauth.FC_FOR_KSEAF_DERIVATION, P0, ueauth.KDFLen(P0))
if err != nil {
logger.Auth5gAkaLog.Errorf("GetKDFValue failed: %+v", err)
return nil, "",
&models.ProblemDetails{
Title: "Kseaf Derivation Problem",
Cause: "KSEAF_DERIVATION_PROBLEM",
Detail: err.Error(),
Status: http.StatusInternalServerError,
}
}
ausfUeContext.XresStar = authInfoResult.AuthenticationVector.XresStar
ausfUeContext.Kausf = Kausf
ausfUeContext.Kseaf = hex.EncodeToString(Kseaf)
ausfUeContext.Rand = authInfoResult.AuthenticationVector.Rand
var av5gAka models.Av5gAka
av5gAka.Rand = authInfoResult.AuthenticationVector.Rand
av5gAka.Autn = authInfoResult.AuthenticationVector.Autn
av5gAka.HxresStar = hxresStar
responseBody.Var5gAuthData = av5gAka
linksValue := models.LinksValueSchema{Href: putLink}
responseBody.Links = make(map[string]models.LinksValueSchema)
responseBody.Links["5g-aka"] = linksValue
} else if authInfoResult.AuthType == models.AuthType_EAP_AKA_PRIME {
logger.UeAuthLog.Infoln("Use EAP-AKA' auth method")
putLink += "/eap-session"
var identity string
// TODO support more SUPI type
if ueid[:4] == "imsi" {
if !self.EapAkaSupiImsiPrefix {
// 33.501 v15.9.0 or later
identity = ueid[5:]
} else {
// 33.501 v15.8.0 or earlier
identity = ueid
}
}
ikPrime := authInfoResult.AuthenticationVector.IkPrime
ckPrime := authInfoResult.AuthenticationVector.CkPrime
RAND := authInfoResult.AuthenticationVector.Rand
AUTN := authInfoResult.AuthenticationVector.Autn
XRES := authInfoResult.AuthenticationVector.Xres
ausfUeContext.XRES = XRES
ausfUeContext.Rand = authInfoResult.AuthenticationVector.Rand
_, K_aut, _, _, EMSK := eapAkaPrimePrf(ikPrime, ckPrime, identity)
logger.AuthELog.Tracef("K_aut: %x", K_aut)
ausfUeContext.K_aut = hex.EncodeToString(K_aut)
Kausf := EMSK[0:32]
ausfUeContext.Kausf = hex.EncodeToString(Kausf)
P0 := []byte(snName)
Kseaf, err := ueauth.GetKDFValue(Kausf, ueauth.FC_FOR_KSEAF_DERIVATION, P0, ueauth.KDFLen(P0))
if err != nil {
logger.AuthELog.Errorf("GetKDFValue failed: %+v", err)
}
ausfUeContext.Kseaf = hex.EncodeToString(Kseaf)
var eapPkt radius.EapPacket
eapPkt.Code = radius.EapCode(1)
if updateAuthenticationInfo.ResynchronizationInfo == nil {
rand.Seed(time.Now().Unix())
randIdentifier := rand.Intn(256)
ausfUeContext.EapID = uint8(randIdentifier)
} else {
ausfUeContext.EapID = lastEapID + 1
}
eapPkt.Identifier = ausfUeContext.EapID
eapPkt.Type = radius.EapType(50) // according to RFC5448 6.1
var eapAKAHdr, atRand, atAutn, atKdf, atKdfInput, atMAC string
eapAKAHdrBytes := make([]byte, 3) // RFC4187 8.1
eapAKAHdrBytes[0] = ausf_context.AKA_CHALLENGE_SUBTYPE
eapAKAHdr = string(eapAKAHdrBytes)
if atRandTmp, err := EapEncodeAttribute("AT_RAND", RAND); err != nil {
logger.AuthELog.Errorf("EAP encode RAND failed: %+v", err)
} else {
atRand = atRandTmp
}
if atAutnTmp, err := EapEncodeAttribute("AT_AUTN", AUTN); err != nil {
logger.AuthELog.Errorf("EAP encode AUTN failed: %+v", err)
} else {
atAutn = atAutnTmp
}
if atKdfTmp, err := EapEncodeAttribute("AT_KDF", snName); err != nil {
logger.AuthELog.Errorf("EAP encode KDF failed: %+v", err)
} else {
atKdf = atKdfTmp
}
if atKdfInputTmp, err := EapEncodeAttribute("AT_KDF_INPUT", snName); err != nil {
logger.AuthELog.Errorf("EAP encode KDF failed: %+v", err)
} else {
atKdfInput = atKdfInputTmp
}
if atMACTmp, err := EapEncodeAttribute("AT_MAC", ""); err != nil {
logger.AuthELog.Errorf("EAP encode MAC failed: %+v", err)
} else {
atMAC = atMACTmp
}
dataArrayBeforeMAC := eapAKAHdr + atRand + atAutn + atKdf + atKdfInput + atMAC
eapPkt.Data = []byte(dataArrayBeforeMAC)
encodedPktBeforeMAC := eapPkt.Encode()
MacValue := CalculateAtMAC(K_aut, encodedPktBeforeMAC)
atMAC = atMAC[:4] + string(MacValue)
dataArrayAfterMAC := eapAKAHdr + atRand + atAutn + atKdf + atKdfInput + atMAC
eapPkt.Data = []byte(dataArrayAfterMAC)
encodedPktAfterMAC := eapPkt.Encode()
responseBody.Var5gAuthData = base64.StdEncoding.EncodeToString(encodedPktAfterMAC)
linksValue := models.LinksValueSchema{Href: putLink}
responseBody.Links = make(map[string]models.LinksValueSchema)
responseBody.Links["eap-session"] = linksValue
}
responseBody.AuthType = authInfoResult.AuthType
return &responseBody, locationURI, nil
}
```
:::
Number 4 is in [```https://github.com/free5gc/udm/blob/main/internal/sbi/producer/generate_auth_data.go```](https://github.com/free5gc/udm/blob/main/internal/sbi/producer/generate_auth_data.go)
:::info
```go=
func HandleGenerateAuthDataRequest(request *httpwrapper.Request) *httpwrapper.Response {
// step 1: log
logger.UeauLog.Infoln("Handle GenerateAuthDataRequest")
// step 2: retrieve request
authInfoRequest := request.Body.(models.AuthenticationInfoRequest)
supiOrSuci := request.Params["supiOrSuci"]
// step 3: handle the message
response, problemDetails := GenerateAuthDataProcedure(authInfoRequest, supiOrSuci)
// step 4: process the return value from step 3
if response != nil {
// status code is based on SPEC, and option headers
return httpwrapper.NewResponse(http.StatusOK, nil, response)
} else if problemDetails != nil {
return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails)
}
problemDetails = &models.ProblemDetails{
Status: http.StatusForbidden,
Cause: "UNSPECIFIED",
}
return httpwrapper.NewResponse(http.StatusForbidden, nil, problemDetails)
}
```
:::
## Installing Docker


## Running RabbitMQ image
Run the docker container and map port 15672 for the management web app and port 5672 for the message broker
```sudo docker run --rm -it -p 15672:15672 -p 5672:5672 rabbitmq:3-management```

## Check out the management web app
Check out the management web app at http://localhost:15672 and log in with the default username (guest) and password (guest)

## What's Next to Implement
1. In AMF, AUSF, and UDM initialization, initiate connection to Rabbit MQ and declare queue
2. We are using AMQP and wants to mimic HTTP. Probably we'll making looks like this
```go=
// function to generate a random string of length n
func randString(n int) string {
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
rand.Seed(time.Now().UnixNano())
b := make([]rune, n)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b)
}
// function to send a request to a queue and wait for a reply
func sendRequest(queue string, body []byte) ([]byte, error) {
// generate a random string for temporary queue name
tempQueue := randString(10)
// declare a temporary queue with auto-delete enabled
q, err := rabbitCh.QueueDeclare(
tempQueue, // name
false, // durable
true, // delete when unused
false, // exclusive
false, // no-wait
nil, // arguments
)
if err != nil {
return nil, err
}
// consume messages from temporary queue
msgs, err := rabbitCh.Consume(
q.Name, // queue name
"", // consumer tag
true, // auto-ack
false, // exclusive
false, // no-local
false, // no-wait
nil, // arguments
)
if err != nil {
return nil, err
}
corrId := randString(32)
err = rabbitCh.Publish(
"", // exchange name
queue, // routing key
false, // mandatory
false, // immediate
amqp.Publishing {
ContentType: "application/json",
CorrelationId: corrId,
ReplyTo: q.Name,
Body: body,
})
if err != nil {
return nil, err
}
for d := range msgs {
if corrId == d.CorrelationId {
return d.Body, nil
}
}
return nil, fmt.Errorf("no reply received")
}
```
3. Implement to AMF, AUSF, and UDM. Done (most likely) :)