# Implementing RabbitMQ part 2 ## Finding out where to modified the code Take a look back at the previous diagram ![](https://hackmd.io/_uploads/BykD_Ruwn.png) 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 ![](https://hackmd.io/_uploads/SkKw96dw2.png) ![](https://hackmd.io/_uploads/HJnuOadwn.png) ## 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``` ![](https://hackmd.io/_uploads/ByKtspdPn.png) ## 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) ![](https://hackmd.io/_uploads/BJuakRdwn.png) ## 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) :)