5G_AKA的整体流程

FreeMODBUS源码 GITHUB free5gc 源码分析_FreeMODBUS源码 GITHUB

由上图可以看到5G_AKA的验证流程就是首先UE验证核心网,通过后核心网在对UE进行验证的一个认证注册流程。

核心网UDM产生鉴权向量AV并传输(Authentication Vector)

FreeMODBUS源码 GITHUB free5gc 源码分析_h5_02

这个步骤对应图片中的step1,2其中对应的代码位于 /free5gc/NFs/udm/producer/generate_auth_data.go中 以下为处理UE的鉴权请求的代码部分

func GenerateAuthDataProcedure(authInfoRequest models.AuthenticationInfoRequest, supiOrSuci string) (
        response *models.AuthenticationInfoResult, problemDetails *models.ProblemDetails) {
        logger.UeauLog.Traceln("In GenerateAuthDataProcedure")
        response = &models.AuthenticationInfoResult{}
        rand.Seed(time.Now().UnixNano())
        //利用UE发送鉴权请求时发送的suci解密产生supi, 
        //suci可以理解为supi的一个加密包,其结构图见下方,如果想了解更多可以参考图片下方链接
        supi, err := suci.ToSupi(supiOrSuci, udm_context.UDM_Self().SuciProfiles)
        if err != nil {
                problemDetails = &models.ProblemDetails{
                        Status: http.StatusForbidden,
                        Cause:  authenticationRejected,
                        Detail: err.Error(),
                }

                logger.UeauLog.Errorln("suciToSupi error: ", err.Error())
                return nil, problemDetails
        }

suci的结构如下图所示,如想进一步了解可以参考

FreeMODBUS源码 GITHUB free5gc 源码分析_5g_03

拿到supi的唯一标识符号后,查询用户相关信息,确定认证对象
authSubs, res, err := client.AuthenticationDataDocumentApi.QueryAuthSubsData(context.Background(), supi, nil)

之后利用authSubs取得k,sqn...等值
   if authSubs.PermanentKey != nil {
                  kStr = authSubs.PermanentKey.PermanentKeyValue
                  if len(kStr) == keyStrLen {
                          k, err = hex.DecodeString(kStr)//生产K
  ........
  //获取op和opc信息,对这两个值的意义不明确
   if authSubs.Milenage.Op != nil {
                          opStr = authSubs.Milenage.Op.OpValue
                          if len(opStr) == opStrLen {
                                  op, err = hex.DecodeString(opStr)
  .........
  if authSubs.Opc != nil && authSubs.Opc.OpcValue != "" {
                  opcStr = authSubs.Opc.OpcValue
                  if len(opcStr) == opcStrLen {
                          opc, err = hex.DecodeString(opcStr)
  ........
  if !hasOPC {
          if hasK && hasOP {
                  opc, err = milenage.GenerateOPC(k, op)
                  if err != nil {
                          logger.UeauLog.Errorln("milenage GenerateOPC err ", err)
                  }
          }
  ·········
//获取sqn值
   sqnStr := strictHex(authSubs.SequenceNumber, 12)
        logger.UeauLog.Traceln("sqnStr", sqnStr)
        sqn, err := hex.DecodeString(sqnStr)
·········
//计算产生rand参数
 RAND := make([]byte, 16)
        _, err = cryptoRand.Read(RAND)
·········
   AMF, err := hex.DecodeString("8000")//不理解此处的8000是指什么?端口号吗?
·········
sqn increment部分的自增不理解,同时sqn的定义未明确
·········
//f1函数产生MACA,与MACS MACs(Message Authentication Codes,消息认证码)与MAC地址(Media Access Control Address,媒体访问控制地址)
//二者一起构成MAC吗? 
err = milenage.F1(opc, k, RAND, sqn, AMF, macA, macS)
·········
//计算RES, CK, IK, AK, AKstar
 // Generate RES, CK, IK, AK, AKstar
 // RES == XRES (expected RES) for server
err = milenage.F2345(opc, k, RAND, RES, CK, IK, AK, AKstar) //milenage中函数后期需要了解具体内容

//计算AUTN
SQNxorAK := make([]byte, 6)
        for i := 0; i < len(sqn); i++ {
                SQNxorAK[i] = sqn[i] ^ AK[i]
        }
        // fmt.Printf("SQN xor AK = %x\n", SQNxorAK)
        AUTN := append(append(SQNxorAK, AMF...), macA...)
········
 // 生成 XRES 留待验证UE
                key := append(CK, IK...)
                FC := UeauCommon.FC_FOR_RES_STAR_XRES_STAR_DERIVATION
                P0 := []byte(authInfoRequest.ServingNetworkName)
                P1 := RAND
                P2 := RES

                kdfValForXresStar := UeauCommon.GetKDFValue(
                        key, FC, P0, UeauCommon.KDFLen(P0), P1, UeauCommon.KDFLen(P1), P2, UeauCommon.KDFLen(P2))
                xresStar := kdfValForXresStar[len(kdfValForXresStar)/2:]
                // fmt.Printf("xresStar = %x\n", xresStar)
····
//生产Kausf,不理解用处?猜测是核心网元之间的验证信息??
                FC = UeauCommon.FC_FOR_KAUSF_DERIVATION
                P0 = []byte(authInfoRequest.ServingNetworkName)
                P1 = SQNxorAK
                kdfValForKausf := UeauCommon.GetKDFValue(key, FC, P0, UeauCommon.KDFLen(P0), P1, UeauCommon.KDFLen(P1))
                // fmt.Printf("Kausf = %x\n", kdfValForKausf)
//生成AV信息
                // Fill in rand, xresStar, autn, kausf
                av.Rand = hex.EncodeToString(RAND)
                av.XresStar = hex.EncodeToString(xresStar)
                av.Autn = hex.EncodeToString(AUTN)
                av.Kausf = hex.EncodeToString(kdfValForKausf)

以下是对请求产生AV信息与传输AV信息的抓包

AMF发起鉴权请求,给出suci值

FreeMODBUS源码 GITHUB free5gc 源码分析_FreeMODBUS源码 GITHUB_04

udm发送AV到ausf

FreeMODBUS源码 GITHUB free5gc 源码分析_ci_05

Ausf计算HXRS

FreeMODBUS源码 GITHUB free5gc 源码分析_5g_06

ausf存储 XRES,然后将基于从UDM收到的 AV,计算新的AV并发送给AMF/seaf,具体的计算过程见下方代码

// 根据XRES计算HXRES
                concat := authInfoResult.AuthenticationVector.Rand + authInfoResult.AuthenticationVector.XresStar
                var hxresStarBytes []byte
                if bytes, err := hex.DecodeString(concat); err != nil {
                        logger.Auth5gAkaComfirmLog.Warnf("decode error: %+v", err)
                } else {
                        hxresStarBytes = bytes
                }
                hxresStarAll := sha256.Sum256(hxresStarBytes)
                hxresStar := hex.EncodeToString(hxresStarAll[16:]) // last 128 bits
                logger.Auth5gAkaComfirmLog.Infof("XresStar = %x\n", authInfoResult.AuthenticationVector.XresStar)
···········
//计算kseaf
Kausf := authInfoResult.AuthenticationVector.Kausf
                var KausfDecode []byte
                if ausfDecode, err := hex.DecodeString(Kausf); err != nil {
                        logger.Auth5gAkaComfirmLog.Warnf("AUSF decode failed: %+v", err)
                } else {
                        KausfDecode = ausfDecode
                }
                P0 := []byte(snName)
                Kseaf := UeauCommon.GetKDFValue(KausfDecode, UeauCommon.FC_FOR_KSEAF_DERIVATION, P0, UeauCommon.KDFLen(P0))
                //ausf的认证信息??作用是什么??
                ausfUeContext.XresStar = authInfoResult.AuthenticationVector.XresStar
                ausfUeContext.Kausf = Kausf
                ausfUeContext.Kseaf = hex.EncodeToString(Kseaf)
                ausfUeContext.Rand = authInfoResult.AuthenticationVector.Rand
···········
//更新AV,将XRES转变为HXRES,不在展示代码

ausf向AMF/seaf的数据包

FreeMODBUS源码 GITHUB free5gc 源码分析_5g_07

MAF向UE发起验证请求

FreeMODBUS源码 GITHUB free5gc 源码分析_FreeMODBUS源码 GITHUB_08

在amf/gmm/build.go下有生成ABBA,和其他验证信息的代码
    authenticationRequest.SpareHalfOctetAndNgksi = nasConvert.SpareHalfOctetAndNgksiToNas(ue.NgKsi)
    authenticationRequest.ABBA.SetLen(uint8(len(ue.ABBA)))
    authenticationRequest.ABBA.SetABBAContents(ue.ABBA)
·········
    //此处首先将ausf发来rand解码赋值给rand,再把rand->tmparray->RAND 所以并未改变自AUSF发来的RAND值,AUTN也是相同的操作。
     rand, err := hex.DecodeString(av5gAka.Rand)
    authenticationRequest.AuthenticationParameterRAND =
            nasType.NewAuthenticationParameterRAND(nasMessage.AuthenticationRequestAuthenticationParameterRANDType)
    copy(tmpArray[:], rand[0:16])
    authenticationRequest.AuthenticationParameterRAND.SetRANDValue(tmpArray)

具体的数据包如下

FreeMODBUS源码 GITHUB free5gc 源码分析_5g_09


对比后发现AMF发出的数据与AUSF发来的RAND 和AUTN 是相同的。

UE验证核心网

FreeMODBUS源码 GITHUB free5gc 源码分析_h5_10

根据查阅的资料上看,UE通过预共享的PermanentKey、相同RAND和SQN参数,相同的Milenage函数(f1-f5),计算出的XMAC摘要就应该和MAC相同,即验证通过认为核心网侧合法 详细验证过程见代码 /UERANSIM/src/ue/mm/auth.cpp

auto &rand = msg.authParamRAND->value;
 auto &autn = msg.authParamAUTN->value;
  ·······
 auto milenage = calculateMilenage(m_sqn, rand);//根据这两个参数生产milenage对象同时执行F1,2,3,4,5函数。
 auto &res = milenage.res;
 auto &ck = milenage.ck;
 auto &ik = milenage.ik;
 auto ckIk = OctetString::Concat(ck, ik);
 auto &milenageAk = milenage.ak;
 auto &milenageMac = milenage.mac_a; //计算出MAC
 auto sqnXorAk = OctetString::Xor(m_sqn, milenageAk);
 auto snn = keys::ConstructServingNetworkName(m_base->config->plmn);
//验证
  auto autnCheck = validateAutn(milenageAk, milenageMac, autn)
//回复,若通过则发出自己的res
m_nonCurrentNsCtx = NasSecurityContext{};
        m_nonCurrentNsCtx->tsc = msg.ngKSI.tsc;
        m_nonCurrentNsCtx->ngKsi = msg.ngKSI.ksi;
        m_nonCurrentNsCtx->keys.rand = rand.copy();
        m_nonCurrentNsCtx->keys.resStar = keys::CalculateResStar(ckIk, snn, rand, res);
        m_nonCurrentNsCtx->keys.res = std::move(res);
        m_nonCurrentNsCtx->keys.kAusf = keys::CalculateKAusfFor5gAka(ck, ik, snn, sqnXorAk);
        m_nonCurrentNsCtx->keys.abba = msg.abba.rawData.copy();
        keys::DeriveKeysSeafAmf(*m_base->config, *m_nonCurrentNsCtx);
       ·······
        nas::AuthenticationResponse resp;
        resp.authenticationResponseParameter = nas::IEAuthenticationResponseParameter{};
        resp.authenticationResponseParameter->rawData = m_nonCurrentNsCtx->keys.resStar.copy();
        sendNasMessage(resp);

回复的数据包如下

FreeMODBUS源码 GITHUB free5gc 源码分析_FreeMODBUS源码 GITHUB_11

AMF根据RES计算HXRES并与之前AUSF发来的HXRES比较,向AUSF发出验证请求

FreeMODBUS源码 GITHUB free5gc 源码分析_h5_12

具体代码如下

resStar := authenticationResponse.AuthenticationResponseParameter.GetRES()
  //根据RES计算新的HXRES
  p0, err := hex.DecodeString(av5gAka.Rand)
  if err != nil {
          return err
  }
  p1 := resStar[:]
  concat := append(p0, p1...)
  //res+rand进行sha256并编码得到HRES
  hResStarBytes := sha256.Sum256(concat)
  hResStar := hex.EncodeToString(hResStarBytes[16:])
  //若计算结果与AUSF发来的不相等,尽心错误处理
  if hResStar != av5gAka.HxresStar {
          ue.GmmLog.Errorf("HRES* Validation Failure (received: %s, expected: %s)", hResStar, av5gAka.HxresStar)

          if ue.IdentityTypeUsedForRegistration == nasMessage.MobileIdentity5GSType5gGuti {
                  gmm_message.SendIdentityRequest(ue.RanUe[accessType], nasMessage.MobileIdentity5GSTypeSuci)
                  return nil
          } else {
                  gmm_message.SendAuthenticationReject(ue.RanUe[accessType], "")
                  return GmmFSM.SendEvent(ue.State[accessType], AuthFailEvent, fsm.ArgsType{
                          ArgAmfUe:      ue,
                          ArgAccessType: accessType,
                  })
          }
  }
  //验证成功发送给AUSF验证请求。
  response, problemDetails, err := consumer.SendAuth5gAkaConfirmRequest(ue, hex.EncodeToString(resStar[:]))

AMF(127.0.0.1)向AUSF(127.0.0.9)发送报文如下

FreeMODBUS源码 GITHUB free5gc 源码分析_ci_13

AUSF验证UE通过

比较amf发来的res与最开始计算出的HRES是否相等若相等则为合法用户,成功接入
代码如下

ausfCurrentContext := ausf_context.GetAusfUeContext(currentSupi) //获取验证用户的supi和servname
    servingNetworkName := ausfCurrentContext.ServingNetworkName

    // Compare the received RES* with the stored XRES*
    logger.Auth5gAkaComfirmLog.Infof("res*: %x\nXres*: %x\n", updateConfirmationData.ResStar, ausfCurrentContext.XresStar)
    if strings.Compare(updateConfirmationData.ResStar, ausfCurrentContext.XresStar) == 0 { //比较存储的hres与ausf发来的res
            ausfCurrentContext.AuthStatus = models.AuthResult_SUCCESS
            responseBody.AuthResult = models.AuthResult_SUCCESS
            success = true
            logger.Auth5gAkaComfirmLog.Infoln("5G AKA confirmation succeeded")
            responseBody.Kseaf = ausfCurrentContext.Kseaf
    } else {
            ausfCurrentContext.AuthStatus = models.AuthResult_FAILURE
            responseBody.AuthResult = models.AuthResult_FAILURE
            logConfirmFailureAndInformUDM(ConfirmationDataResponseID, models.AuthType__5_G_AKA, servingNetworkName,
                    "5G AKA confirmation failed", ausfCurrentContext.UdmUeauUrl)
    }

成功后返回的信息如下,可以看到接入成功,返回201

FreeMODBUS源码 GITHUB free5gc 源码分析_ci_14

以上就是5GAKA从总体流程上结合抓包的代码分析,主要还是依据freebuf上的文章重新走了一遍流程,对与很多细节上的问题没有深究,后续需要对源码尽心进一步的研究。