Free5GC源码研究(4) - AUSF研究

news/2024/10/1 18:11:20

本文研究AUthentication Server Function (AUSF) 主要实现的功能

AUSF的概念

在开始研究其源代码之前,我们需要先对AUSF有一些概念上的认识。AUSF的主要技术文档是TS29.509,规定了AUSF提供哪些服务,其对应的API接口有哪些。总的来说,AUSF就是帮助其他NF,比如AMF,来认证一个接入设备。然而整个鉴权过程比较复杂,AUSF在其中起到主导作用,但也会需要其他NF,比如UDM的协助。

我们熟悉的用户鉴权过程都是像提供一个用户密码、提供一个手机验证码,或者一个实物证件这样的,来证明我是我自己。而在通信网络中鉴权过程对我们来说则略显陌生,用的是一种叫AKA(Authentication and Key Agreement)的方式:它要求接入设备计算一个aka-challenge,而只有真正的设备才能准确算出这个aka-challenge,如此一来就完成了鉴权过程。这个过程本质上还是用密钥加密解密的过程。整个鉴权过程包含了而至少两次相互通信,第一次通信是设备向网络提供自己的身份信息(就像输入用户名),网络端在数据库里找到设备的相关数据后返回一个aka-challenge;第二次通信是设备向网络发送自己对aka-challenge解出来的RES,网络端判断这个RES是否正确,如果正确则返回鉴权成功信息,否则返回鉴权失败信息。

通信网络的鉴权过程

能完成以上鉴权过程的具体协议至少有两种,一种是EAP-AKA协议,目前最新版的文档是RFC9048;一种是5G-AKA协议,定义在TS33.401文档里。EAP-AKA是一个较为通用的认证协议,而5G-AKA则是专门为5G网络设计的协议,提供了更强的保护和更优的体验。考虑到了用户和设备的多样性。一些老旧设备可能只支持EAP-AKA,而新设备则可能支持5G-AKA,5G标准要求同时支持这两种协议,以确保网络的顺利迭代、提供不同级别的安全保护、和满足不同用户及设备的需求。TS29.509定义了AUSF应当提供的服务:

img

可以看到,除了基本的设备鉴权服务(ue-authentications)以外,AUSF还应该提供SOR和UPU的保护,也就是提供消息认证和完整性保护机制,这些服务确保了5G网络在漫游和用户面参数更新等关键操作中的安全性和可靠性,对于维护5G网络的稳定运行和保护用户隐私至关重要。然而目前的v3.4.3版本中free5gc/ausf@v
1.2.3
尚未实现这些功能。

// https://github.com/free5gc/ausf/blob/v1.2.3/internal/sbi/api_sorprotection.go
func (s *Server) SupiUeSorPost(c *gin.Context) {c.JSON(http.StatusNotImplemented, gin.H{})
}
// https://github.com/free5gc/ausf/blob/v1.2.3/internal/sbi/api_upuprotection.go
func (s *Server) SupiUeUpuPost(c *gin.Context) {c.JSON(http.StatusNotImplemented, gin.H{})
}

因此,我们只需聚焦于研究设备鉴权服务即可。

AUSF的实现

AUSF的设计文档描绘了为其设计的软件架构(函数调用关系图):

img

整个图里面我们需要重点关注的函数是红框中的三个函数,它们才是实际解决鉴权问题的函数,其他都是简单处理或者套壳调用。但在深入研究这三个函数以前,有必要先瞅一眼internal/context/里面定义的数据结构,因为我们在前文已经知道,Context类型都是NF整个证明周期里最核心的数据存储中心。

// https://github.com/free5gc/ausf/blob/v1.2.3/internal/context/context.gotype AUSFContext struct {// other fields ......suciSupiMap          sync.MapUePool               sync.MapUdmUeauUrl           stringEapAkaSupiImsiPrefix bool
}type AusfUeContext struct {Supi               stringKausf              stringKseaf              stringServingNetworkName stringAuthStatus         models.AuthResultUdmUeauUrl         string// for 5G AKAXresStar string// for EAP-AKA'K_aut    stringXRES     stringRand     stringEapID    uint8Resynced bool
}

context.go里定义了各种类型,其中最值得留意的自然是AUSFContextAusfUeContext,前者存储整个AUSF的重要数据,后者存储一次设备鉴权过程需要用到的数据。每一个设备都和一个AusfUeContext对应,存储在AUSFContext.UePool中。而AusfUeContext中的设备ID都是SUPI(Subscription Permanent Identifier),然而为了保护用户隐私,5G网络允许设备在提供自身身份信息时发送SUCI(Subscription Concealed Identifier)。SUCI是一种隐藏或加密了的用户订阅标识符,网络端在收到SUCI后要还原成SUPI才能进行其他操作,而这SCUI及其还原后的SUPI则存储在AUSFContext.suciSupiMap结构里,方便后续使用。

UeAuthPostRequestProcedure

简单了解过这两个Context类型后,我们开始聚焦UeAuthPostRequestProcedure函数,也就是设备在鉴权过程第一步,访问/ue-authentications时网络端的处理过程。下面是其大幅简化后的代码:

// https://github.com/free5gc/ausf/blob/v1.2.3/internal/sbi/processor/ue_authentication.go#L216
func (p *Processor) UeAuthPostRequestProcedure(c *gin.Context, updateAuthenticationInfo models.AuthenticationInfo) {supiOrSuci := updateAuthenticationInfo.SupiOrSuciresult, err, pd := p.Consumer().GenerateAuthDataApi(udmUrl, supiOrSuci, authInfoReq)authInfoResult := *resultueid := authInfoResult.SupiausfUeContext := ausf_context.NewAusfUeContext(ueid)ausf_context.AddAusfUeContextToPool(ausfUeContext)ausf_context.AddSuciSupiPairToMap(supiOrSuci, ueid)if authInfoResult.AuthType == models.AuthType__5_G_AKA {ausfUeContext.XresStar = authInfoResult.AuthenticationVector.XresStar//  av5gAka := encode5gAka(authInfoResult.AuthenticationVector)responseBody.Var5gAuthData = av5gAka} else if authInfoResult.AuthType == models.AuthType_EAP_AKA_PRIME {	ausfUeContext.XRES = authInfoResult.AuthenticationVector.Xres//  encodedPktAfterMAC := encodePacketArray(authInfoResult.AuthenticationVector)responseBody.Var5gAuthData = base64.StdEncoding.EncodeToString(encodedPktAfterMAC)}responseBody.AuthType = authInfoResult.AuthTypec.JSON(http.StatusCreated, responseBody) // 返回设备一个aka-challenge
}

整个函数所做的事情就是生成一个aka-challenge (responseBody.Var5gAuthData)和对应的Xres/XresStar,把Xres存储在相应的ausfUeContext里,然后把生成的aka-challenge返回给用户设备让它计算出一个解来。这里面生成aka-challenge的主要工作是用函数GenerateAuthDataApi调用UDM来实际完成的。其具体的做法,还要等我们以后研究UDM在看。另外,整个鉴权过程的第一步还用到了很多在free5gc/openapi定义的类型,了解这些类型的细节能进一步提高我们的理解水平。

点击查看更多openapi里的类型
/* https://github.com/free5gc/openapi/blob/v1.0.8/models/model_authentication_info_request.go */
type AuthenticationInfo struct {SupiOrSuci            string                 `json:"supiOrSuci" yaml:"supiOrSuci" bson:"supiOrSuci"`ServingNetworkName    string                 `json:"servingNetworkName" yaml:"servingNetworkName" bson:"servingNetworkName"`ResynchronizationInfo *ResynchronizationInfo `json:"resynchronizationInfo,omitempty" yaml:"resynchronizationInfo" bson:"resynchronizationInfo"`TraceData             *TraceData             `json:"traceData,omitempty" yaml:"traceData" bson:"traceData"`
}/* https://github.com/free5gc/openapi/blob/v1.0.8/models/model_ue_authentication_ctx.go */
type UeAuthenticationCtx struct {AuthType           AuthType                    `json:"authType" yaml:"authType" bson:"authType"`Var5gAuthData      interface{}                 `json:"5gAuthData" yaml:"5gAuthData" bson:"5gAuthData"`Links              map[string]LinksValueSchema `json:"_links" yaml:"_links" bson:"_links"`ServingNetworkName string                      `json:"servingNetworkName,omitempty" yaml:"servingNetworkName" bson:"servingNetworkName"`
}/* https://github.com/free5gc/openapi/blob/v1.0.8/models/model_authentication_info_request.go */
type AuthenticationInfoRequest struct {SupportedFeatures     string                 `json:"supportedFeatures,omitempty" yaml:"supportedFeatures" bson:"supportedFeatures" mapstructure:"SupportedFeatures"`ServingNetworkName    string                 `json:"servingNetworkName" yaml:"servingNetworkName" bson:"servingNetworkName" mapstructure:"ServingNetworkName"`ResynchronizationInfo *ResynchronizationInfo `json:"resynchronizationInfo,omitempty" yaml:"resynchronizationInfo" bson:"resynchronizationInfo" mapstructure:"ResynchronizationInfo"`AusfInstanceId        string                 `json:"ausfInstanceId" yaml:"ausfInstanceId" bson:"ausfInstanceId" mapstructure:"AusfInstanceId"`
}/* https://github.com/free5gc/openapi/blob/v1.0.8/models/model_authentication_info_result.go */
type AuthenticationInfoResult struct {AuthType             AuthType              `json:"authType" yaml:"authType" bson:"authType" mapstructure:"AuthType"`SupportedFeatures    string                `json:"supportedFeatures,omitempty" yaml:"supportedFeatures" bson:"supportedFeatures" mapstructure:"SupportedFeatures"`AuthenticationVector *AuthenticationVector `json:"authenticationVector,omitempty" yaml:"authenticationVector" bson:"authenticationVector" mapstructure:"AuthenticationVector"`Supi                 string                `json:"supi,omitempty" yaml:"supi" bson:"supi" mapstructure:"Supi"`
}/* https://github.com/free5gc/openapi/blob/v1.0.8/models/model_authentication_vector.go */
type AuthenticationVector struct {AvType   AvType `json:"avType" yaml:"avType" bson:"avType" mapstructure:"AvType"`Rand     string `json:"rand" yaml:"rand" bson:"rand" mapstructure:"Rand"`Xres     string `json:"xres" yaml:"xres" bson:"xres" mapstructure:"Xres"`Autn     string `json:"autn" yaml:"autn" bson:"autn" mapstructure:"Autn"`CkPrime  string `json:"ckPrime" yaml:"ckPrime" bson:"ckPrime" mapstructure:"CkPrime"`IkPrime  string `json:"ikPrime" yaml:"ikPrime" bson:"ikPrime" mapstructure:"IkPrime"`XresStar string `json:"xresStar" yaml:"xresStar" bson:"xresStar" mapstructure:"XresStar"`Kausf    string `json:"kausf" yaml:"kausf" bson:"kausf" mapstructure:"Kausf"`
}

Auth5gAkaComfirmRequestProcedure

当用户收到aka-challenge,计算出相应的Res后,就开始第二步向网络端确认自己的计算结果。网络端将对比用户计算的Res和之前生成的XRes,确认是否鉴权成功。如果设备使用的协议是5G-AKA协议,那么相应的处理函数就是Auth5gAkaComfirmRequestProcedure,其简化版代码如下:

// https://github.com/free5gc/ausf/blob/v1.2.3/internal/sbi/processor/ue_authentication.go#L456
func (p *Processor) Auth5gAkaComfirmRequestProcedure(c *gin.Context, updateConfirmationData models.ConfirmationData,ConfirmationDataResponseID string,
) {var confirmDataRsp models.ConfirmationDataResponseausfCurrentContext := ausf_context.GetAusfUeContext(currentSupi)// 对比收到的 RES* 和之前存起来的 XRES*if strings.EqualFold(updateConfirmationData.ResStar, ausfCurrentContext.XresStar) {// 鉴权成功,生成密钥 KSeaf ausfCurrentContext.AuthStatus = models.AuthResult_SUCCESSconfirmDataRsp.AuthResult = models.AuthResult_SUCCESSsuccess = trueconfirmDataRsp.Kseaf = ausfCurrentContext.Kseaf} else {// 鉴权失败ausfCurrentContext.AuthStatus = models.AuthResult_FAILUREconfirmDataRsp.AuthResult = models.AuthResult_FAILUREp.logConfirmFailureAndInformUDM(ConfirmationDataResponseID, models.AuthType__5_G_AKA, servingNetworkName,"5G AKA confirmation failed", ausfCurrentContext.UdmUeauUrl)}p.Consumer().SendAuthResultToUDM(currentSupi, models.AuthType__5_G_AKA, success, servingNetworkName,ausfCurrentContext.UdmUeauUrl)c.JSON(http.StatusOK, confirmDataRsp)
}
点击查看更多openapi里的类型
/* https://github.com/free5gc/openapi/blob/v1.0.8/models/model_confirmation_data.go */
type ConfirmationData struct {ResStar string `json:"resStar" yaml:"resStar" bson:"resStar"`
}/* https://github.com/free5gc/openapi/blob/v1.0.8/models/model_confirmation_data_response.go */
type ConfirmationDataResponse struct {AuthResult AuthResult `json:"authResult" yaml:"authResult" bson:"authResult"`Supi       string     `json:"supi,omitempty" yaml:"supi" bson:"supi"`Kseaf      string     `json:"kseaf,omitempty" yaml:"kseaf" bson:"kseaf"`
}

EapAuthComfirmRequestProcedure

可见这个函数的处理逻辑还是比较简洁的,实际上未经简化的完整函数也只有60行代码。而如果设备使用的是EAP-AKA协议,那么对应的EapAuthComfirmRequestProcedure函数就会相对来说更复杂点,其完整代码有173行,下面是简化版代码。

https://github.com/free5gc/ausf/blob/v1.2.3/internal/sbi/processor/ue_authentication.go#L36
func (p *Processor) EapAuthComfirmRequestProcedure(c *gin.Context, updateEapSession models.EapSession, eapSessionID string) {var eapSession models.EapSessionausfCurrentContext := ausf_context.GetAusfUeContext(currentSupi)eapOK := true// 如果当前认证状态已经是失败,则返回401错误if ausfCurrentContext.AuthStatus == models.AuthResult_FAILURE {eapSession.AuthResult = models.AuthResult_FAILUREc.JSON(http.StatusUnauthorized, eapSession)return}// decodeEapAkaPrimePkt := 对 `updateEapSession.EapPayload` 的各种处理switch decodeEapAkaPrimePkt.Subtype {case ausf_context.AKA_CHALLENGE_SUBTYPE:XMAC := CalculateAtMAC(K_aut, decodeEapAkaPrimePkt.MACInput)MAC := decodeEapAkaPrimePkt.Attributes[ausf_context.AT_MAC_ATTRIBUTE].ValueXRES := ausfCurrentContext.XRESRES := hex.EncodeToString(decodeEapAkaPrimePkt.Attributes[ausf_context.AT_RES_ATTRIBUTE].Value)if !bytes.Equal(MAC, XMAC) {eapOK = false} else if XRES == RES {// 鉴权成功,生成密钥 KSeaf eapSession.KSeaf = ausfCurrentContext.KseafeapSession.Supi = currentSupieapSession.AuthResult = models.AuthResult_SUCCESSp.Consumer().SendAuthResultToUDM(eapSessionID, models.AuthType_EAP_AKA_PRIME,true, servingNetworkName, ausfCurrentContext.UdmUeauUrl)ausfCurrentContext.AuthStatus = models.AuthResult_SUCCESS} else {eapOK = false}default:ausfCurrentContext.AuthStatus = models.AuthResult_FAILURE}}if !eapOK {logger.AuthELog.Warnf("EAP-AKA' failure: %s", eapErrStr)p.Consumer().SendAuthResultToUDM(eapSessionID, models.AuthType_EAP_AKA_PRIME, false, servingNetworkName,ausfCurrentContext.UdmUeauUrl)ausfCurrentContext.AuthStatus = models.AuthResult_FAILUREeapSession.AuthResult = models.AuthResult_ONGOING} else if ausfCurrentContext.AuthStatus == models.AuthResult_FAILURE {p.Consumer().SendAuthResultToUDM(eapSessionID, models.AuthType_EAP_AKA_PRIME, false, servingNetworkName,ausfCurrentContext.UdmUeauUrl)eapSession.AuthResult = models.AuthResult_FAILURE}c.JSON(http.StatusOK, eapSession)
}

可以看到EAP协议中设备发送的请求和网络返回的回复都是eapSession。网络拿到一个eapSession后要首先检查一下ausfCurrentContext.AuthStatus是否已经被设置为failed,如果是的话直接返回鉴权失败,因为这意味着设备尝试对一个aka-challenge进行多次求解。此后,将eapSession.EapPayload中的内容提取出来,分别会很对其MAC与XMAC,RES与XRES,如果都成功,则鉴权通过。

EAP-AKA之所以看起来比5G-AKA复杂,是因为它需要适应更广泛的应用场景和网络环境,同时提供更多的安全和隐私保护选项。而5G-AKA作为专为5G设计的协议,虽然在安全性上进行了增强,但其设计目标更为集中,因此在实现上可能显得更为精简和高效。

点击查看更多与EAP-AKA相关的类型
/* https://github.com/free5gc/openapi/blob/v1.0.8/models/model_eap_session.go */
type EapSession struct {// contains an EAP packetEapPayload string                      `json:"eapPayload" yaml:"eapPayload" bson:"eapPayload"`KSeaf      string                      `json:"kSeaf,omitempty" yaml:"kSeaf" bson:"kSeaf"`Links      map[string]LinksValueSchema `json:"_links,omitempty" yaml:"_links" bson:"_links"`AuthResult AuthResult                  `json:"authResult,omitempty" yaml:"authResult" bson:"authResult"`Supi       string                      `json:"supi,omitempty" yaml:"supi" bson:"supi"`
}// https://github.com/free5gc/ausf/blob/v1.2.3/internal/context/context.go#L57
type EapAkaPrimeAttribute struct {Type   uint8Length uint8Value  []byte
}// https://github.com/free5gc/ausf/blob/v1.2.3/internal/context/context.go#L63
type EapAkaPrimePkt struct {Subtype    uint8Attributes map[uint8]EapAkaPrimeAttributeMACInput   []byte
}// https://github.com/google/gopacket/blob/v1.1.19/layers/eap.go#L36
type EAP struct {BaseLayerCode     EAPCodeId       uint8Length   uint16Type     EAPTypeTypeData []byte
}// https://github.com/google/gopacket/blob/v1.1.19/layers/base.go#L15
type BaseLayer struct {// Contents is the set of bytes that make up this layer.  IE: for an// Ethernet packet, this would be the set of bytes making up the// Ethernet frame.Contents []byte// Payload is the set of bytes contained by (but not part of) this// Layer.  Again, to take Ethernet as an example, this would be the// set of bytes encapsulated by the Ethernet protocol.Payload []byte
}

在本文中我们粗略了解了5G网络中设备的认证鉴权过程,还深入研究了其原地阿玛实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/66856.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

manim边学边做--形状匹配

manim中有几个特殊的用于形状匹配的对象,它们的作用是标记和注释已有的对象,本身一般不单独使用。 形状匹配对象一共有4种:BackgroundRectangle:为已有的对象提供一个矩形的背景 Cross:用交叉线标记已有对象 SurroundingRectangle:用矩形框围住某个对象 Underline:为某个…

高级程序设计语言第2次作业

这个作业属于哪个课程:https://edu.cnblogs.com/campus/fzu/2024C/ 这个作业要求在哪里: https://edu.cnblogs.com/campus/fzu/2024C/homework/13282 学号:102300134 姓名:曾威 练习题目: 第一题: 目标:体现出整数的上溢,浮点数的上溢,浮点数的下溢 思路:命名一个整型…

Day07——数据类型

数据类型 1.强类型语言,要求变量的使用要严格符合规定,所有变量都必须先定义后才能使用; ​ JAVA就是强类型语言 2.弱类型语言。 八大数据类型注意:在表示long类型时,数后面有L 表示float类型时,数后面有F或f 字符单个字,用单引号引用;字符串…

Docker实操过程

Docker实操过程 体会 知乎上搜“docker入门”搜到的笔记总是起一个赚噱头的标题,然后点进去一看,里面大多是一些入门内容的简单罗列,要么对于背景内容长篇大论让读者抓不住重点,要么对于命令API简单罗列,入门者甚至连这些API如何衔接使用,这些命令大概发生了什么可能都没…

谷歌收录批量查询,教你批量查询谷歌收录的方法

批量查询谷歌收录是网站管理员和SEO人员常见的需求,以下是一些实用的方法和步骤来实现这一目标: 一、使用Google Search Console(谷歌搜索控制台) 注册并验证网站:首先,在Google Search Console中注册并验证你的网站。这是获取网站在谷歌搜索结果中详细数据的基础。 下载…

修改Nacos2.4.1源码-适配达梦数据库麒麟ARM系统(国产XC化)

前言:应集团公司现在推广XC化,其中基础环境需要用麒麟ARM操作系统和达梦数据库,而官网的nacos默认适配mysql,需要重新编译源码来适配需要对接的数据库,2.4.2试验了一把,安装启动后 nacos 控制台出现 “创建命名空间失败 / 数据库语法问题”,经分析,问题出在源码的sql语…

怎么查看网站是否被谷歌收录,你会查看网站被谷歌收录的方法吗

查看网站是否被谷歌收录是一个相对简单的过程,以下是几种常用的方法,可以帮助你确定网站或其特定页面是否已被谷歌搜索引擎索引: 一、使用“site:”搜索指令 打开谷歌搜索引擎(Google)。 在搜索框中输入“site:你的网站域名”(注意使用英文冒号,并将“你的网站域名”替换…

操作系统:保护模式(二)内存模型

平坦内存模型 现代操作系统一般不会使用过于复杂的分段机制,而是采用平坦内存模型 + 分页模型来管理内存。 平坦内存模型(Flat Memory Model),这是现代操作系统(如 Linux 和 Windows)常用的内存模型。在这种模型中,所有段的段基址都为 0,段界限为 4GB,使得整个内存空间…