5G_AKA的整体流程
由上图可以看到5G_AKA的验证流程就是首先UE验证核心网,通过后核心网在对UE进行验证的一个认证注册流程。
核心网UDM产生鉴权向量AV并传输(Authentication Vector)
这个步骤对应图片中的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的结构如下图所示,如想进一步了解可以参考
拿到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值
udm发送AV到ausf
Ausf计算HXRS
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的数据包
MAF向UE发起验证请求
在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)
具体的数据包如下
对比后发现AMF发出的数据与AUSF发来的RAND 和AUTN 是相同的。
UE验证核心网
根据查阅的资料上看,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);
回复的数据包如下
AMF根据RES计算HXRES并与之前AUSF发来的HXRES比较,向AUSF发出验证请求
具体代码如下
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)发送报文如下
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
以上就是5GAKA从总体流程上结合抓包的代码分析,主要还是依据freebuf上的文章重新走了一遍流程,对与很多细节上的问题没有深究,后续需要对源码尽心进一步的研究。