Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
M
Mirai
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
MyCard
Mirai
Commits
e85014e7
Commit
e85014e7
authored
Aug 09, 2019
by
liujiahua123123
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'origin/master'
parents
eeed2859
43595db0
Changes
7
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
612 additions
and
50 deletions
+612
-50
mirai-core/src/main/java/net/mamoe/mirai/network/Protocol.java
...-core/src/main/java/net/mamoe/mirai/network/Protocol.java
+61
-0
mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/ClientHeartbeatPacket.java
...oe/mirai/network/packet/client/ClientHeartbeatPacket.java
+14
-2
mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/ClientLoginPacket.java
.../mamoe/mirai/network/packet/client/ClientLoginPacket.java
+32
-2
mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/ClientPacket.java
...a/net/mamoe/mirai/network/packet/client/ClientPacket.java
+28
-44
mirai-core/src/main/java/net/mamoe/mirai/util/TEAEncryption.java
...ore/src/main/java/net/mamoe/mirai/util/TEAEncryption.java
+12
-2
mirai-core/src/main/java/net/mamoe/mirai/util/_TEAEncryption.java
...re/src/main/java/net/mamoe/mirai/util/_TEAEncryption.java
+456
-0
mirai-native/src/main/java/net/mamoe/mirai/util/TeaEncryption.java
...ive/src/main/java/net/mamoe/mirai/util/TeaEncryption.java
+9
-0
No files found.
mirai-core/src/main/java/net/mamoe/mirai/network/Protocol.java
0 → 100644
View file @
e85014e7
package
net.mamoe.mirai.network
;
import
java.net.InetAddress
;
import
java.net.UnknownHostException
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.List
;
import
java.util.stream.Collectors
;
/**
* @author Him188moe @ Mirai Project
*/
public
interface
Protocol
{
List
<
String
>
SERVER_IP
=
new
ArrayList
<>()
{{
add
(
"183.60.56.29"
);
List
.
of
(
"sz2.tencent.com"
,
"sz3.tencent.com"
,
"sz4.tencent.com"
,
"sz5.tencent.com"
,
"sz6.tencent.com"
,
"sz8.tencent.com"
,
"sz9.tencent.com"
).
forEach
(
s
->
{
try
{
SERVER_IP
.
add
(
InetAddress
.
getByName
(
s
).
getHostAddress
());
}
catch
(
UnknownHostException
ignored
)
{
}
});
}};
String
head
=
"02"
;
String
ver
=
"37 13 "
;
String
fixVer
=
"03 00 00 00 01 2E 01 00 00 68 52 00 00 00 00 "
;
String
tail
=
" 03"
;
String
_fixVer
=
"02 00 00 00 01 01 01 00 00 68 20 "
;
String
_0825data0
=
"00 18 00 16 00 01 "
;
String
_0825data2
=
"00 00 04 53 00 00 00 01 00 00 15 85 "
;
String
_0825key
=
"A4 F1 91 88 C9 82 14 99 0C 9E 56 55 91 23 C8 3D"
;
String
redirectionKey
=
"A8 F2 14 5F 58 12 60 AF 07 63 97 D6 76 B2 1A 3B"
;
String
publicKey
=
"02 6D 28 41 D2 A5 6F D2 FC 3E 2A 1F 03 75 DE 6E 28 8F A8 19 3E 5F 16 49 D3"
;
String
shareKey
=
"1A E9 7F 7D C9 73 75 98 AC 02 E0 80 5F A9 C6 AF"
;
String
_0836fix
=
"06 A9 12 97 B7 F8 76 25 AF AF D3 EA B4 C8 BC E7 "
;
String
_00BaKey
=
"C1 9C B8 C8 7B 8C 81 BA 9E 9E 7A 89 E1 7A EC 94"
;
String
_00BaFixKey
=
"69 20 D1 14 74 F5 B3 93 E4 D5 02 B3 71 1A CD 2A"
;
String
encryptKey
=
"“BA 42 FF 01 CF B4 FF D2 12 F0 6E A7 1B 7C B3 08”"
;
static
byte
[]
hexToBytes
(
String
hex
)
{
var
list
=
Arrays
.
stream
(
hex
.
split
(
" "
)).
map
(
String:
:
trim
).
map
(
s
->
Byte
.
valueOf
(
s
,
16
)).
collect
(
Collectors
.
toList
());
var
buff
=
new
byte
[
list
.
size
()];
for
(
int
i
=
0
;
i
<
list
.
size
();
i
++)
{
buff
[
i
]
=
list
.
get
(
i
);
}
return
buff
;
}
}
mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/ClientHeartbeatPacket.java
View file @
e85014e7
package
net.mamoe.mirai.network.packet.client
;
import
lombok.Data
;
import
lombok.EqualsAndHashCode
;
import
net.mamoe.mirai.network.Protocol
;
import
net.mamoe.mirai.network.packet.PacketId
;
import
net.mamoe.mirai.util.TEAEncryption
;
import
java.io.IOException
;
/**
* @author Him188moe @ Mirai Project
*/
@PacketId
(
0x0058
)
@EqualsAndHashCode
(
callSuper
=
true
)
@Data
@PacketId
(
0x00_58
)
public
class
ClientHeartbeatPacket
extends
ClientPacket
{
public
long
qq
;
public
byte
[]
sessionKey
;
//登录后获得
@Override
public
void
encode
()
throws
IOException
{
this
.
writeRandom
(
2
);
this
.
writeQQ
(
qq
);
this
.
writeHex
(
Protocol
.
fixVer
);
this
.
write
(
TEAEncryption
.
encrypt
(
new
byte
[]{
0x00
,
0x01
,
0x00
,
0x01
},
sessionKey
));
}
}
mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/ClientLoginPacket.java
View file @
e85014e7
package
net.mamoe.mirai.network.packet.client
;
import
lombok.Data
;
import
lombok.EqualsAndHashCode
;
import
net.mamoe.mirai.network.Protocol
;
import
net.mamoe.mirai.network.packet.PacketId
;
import
net.mamoe.mirai.util.TEAEncryption
;
import
java.io.IOException
;
/**
* @author Him188moe @ Mirai Project
*/
@PacketId
(
0x08_25_31_01
)
// TODO: 2019/8/8
@EqualsAndHashCode
(
callSuper
=
true
)
@Data
@PacketId
(
0x08_25_31_01
)
public
class
ClientLoginPacket
extends
ClientPacket
{
public
long
qq
;
@Override
public
void
encode
()
throws
IOException
{
this
.
writeQQ
(
qq
);
this
.
writeHex
(
Protocol
.
fixVer
);
this
.
writeHex
(
Protocol
.
_0825key
);
//TEA 加密
var
data
=
new
ClientPacket
()
{
@Override
public
void
encode
()
{
public
void
encode
()
throws
IOException
{
this
.
writeHex
(
Protocol
.
_0825data0
);
this
.
writeHex
(
Protocol
.
_0825data2
);
this
.
writeQQ
(
qq
);
this
.
writeHex
(
"00 00 00 00 03 09 00 08 00 01"
);
this
.
writeIp
(
Protocol
.
SERVER_IP
.
get
(
2
));
this
.
writeHex
(
"00 02 00 36 00 12 00 02 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 14 00 1D 01 02 00 19"
);
this
.
writeHex
(
Protocol
.
publicKey
);
}
};
data
.
encode
();
this
.
write
(
TEAEncryption
.
encrypt
(
data
.
toByteArray
(),
Protocol
.
hexToBytes
(
Protocol
.
_0825key
)));
}
}
mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/ClientPacket.java
View file @
e85014e7
package
net.mamoe.mirai.network.packet.client
;
import
lombok.Getter
;
import
net.mamoe.mirai.network.Protocol
;
import
net.mamoe.mirai.network.packet.Packet
;
import
net.mamoe.mirai.network.packet.PacketId
;
import
java.io.ByteArrayOutputStream
;
import
java.io.DataOutputStream
;
import
java.io.IOException
;
import
java.util.Arrays
;
/**
* @author Him188moe @ Mirai Project
...
...
@@ -25,60 +25,38 @@ public abstract class ClientPacket extends DataOutputStream implements Packet {
packageId
=
annotation
.
value
();
try
{
writeHead
(
);
writeVersion
(
);
this
.
writeHex
(
Protocol
.
head
);
this
.
writeHex
(
Protocol
.
ver
);
writePacketId
();
}
catch
(
IOException
e
)
{
throw
new
RuntimeException
(
e
);
}
}
public
static
void
main
(
String
[]
args
)
throws
IOException
{
var
pk
=
new
ClientPacket
()
{
@Override
public
void
encode
()
throws
IOException
{
writeHead
();
protected
void
writeIp
(
String
ip
)
throws
IOException
{
for
(
String
s
:
ip
.
split
(
"\\."
))
{
this
.
writeInt
(
Integer
.
parseInt
(
s
));
}
};
pk
.
encode
();
System
.
out
.
println
(
Arrays
.
toString
(((
ByteArrayOutputStream
)
pk
.
out
).
toByteArray
()));
}
protected
void
write
Hea
d
()
throws
IOException
{
this
.
write
Byte
(
0x02
);
protected
void
write
PacketI
d
()
throws
IOException
{
this
.
write
Int
(
this
.
packageId
);
}
protected
void
writeVersion
()
throws
IOException
{
this
.
writeByte
(
0x37_13
);
protected
void
writeHex
(
String
hex
)
throws
IOException
{
for
(
String
s
:
hex
.
split
(
" "
))
{
s
=
s
.
trim
();
if
(
s
.
isEmpty
())
{
continue
;
}
protected
void
writePacketId
()
throws
IOException
{
this
.
writeByte
(
this
.
packageId
);
this
.
writeByte
(
Byte
.
valueOf
(
s
,
16
));
}
protected
void
writeFixVer
()
throws
IOException
{
this
.
writeByte
(
0x03
);
this
.
writeByte
(
0x00
);
this
.
writeByte
(
0x00
);
this
.
writeByte
(
0x00
);
this
.
writeByte
(
0x01
);
this
.
writeByte
(
0x2E
);
this
.
writeByte
(
0x01
);
this
.
writeByte
(
0x00
);
this
.
writeByte
(
0x00
);
this
.
writeByte
(
0x68
);
this
.
writeByte
(
0x52
);
this
.
writeByte
(
0x00
);
this
.
writeByte
(
0x00
);
this
.
writeByte
(
0x00
);
this
.
writeByte
(
0x00
);
}
protected
void
write0825Key
()
throws
IOException
{
this
.
writeLong
(
0xA4_F1_91_88
);
this
.
writeLong
(
0xC9_82_14_99
);
this
.
writeLong
(
0x0C_9E_56_55
);
this
.
writeLong
(
0x91_23_C8_3D
);
protected
void
writeRandom
(
int
length
)
throws
IOException
{
for
(
int
i
=
0
;
i
<
length
;
i
++)
{
this
.
writeByte
((
byte
)
(
int
)
(
Math
.
random
()
*
255
));
}
}
protected
void
writeQQ
(
long
qq
)
throws
IOException
{
...
...
@@ -87,7 +65,13 @@ public abstract class ClientPacket extends DataOutputStream implements Packet {
/**
* Encode this packet
*/
* Encode this packet.
* <p>
* Before sending the packet, an {@linkplain Protocol#tail tail} will be added.
*/
// TODO: 2019/8/9 添加 tail
public
abstract
void
encode
()
throws
IOException
;
public
byte
[]
toByteArray
()
{
return
((
ByteArrayOutputStream
)
this
.
out
).
toByteArray
();
}
}
mirai-core/src/main/java/net/mamoe/mirai/util/TEAEncryption.java
View file @
e85014e7
package
net.mamoe.mirai.util
;
/**
* TEA encryption
*
* @author Him188moe @ Mirai Project
*/
public
final
class
TEAEncryption
{
public
static
byte
[]
encrypt
(
byte
[]
source
,
byte
[]
key
)
{
return
new
_TEAEncryption
().
encrypt
(
source
,
key
);
}
public
byte
[]
decrypt
(
byte
[]
source
,
byte
[]
key
)
{
return
new
_TEAEncryption
().
decrypt
(
source
,
key
);
}
public
byte
[]
decrypt
(
byte
[]
source
,
int
offset
,
int
length
,
byte
[]
key
)
{
return
new
_TEAEncryption
().
decrypt
(
source
,
offset
,
length
,
key
);
}
}
mirai-core/src/main/java/net/mamoe/mirai/util/_TEAEncryption.java
0 → 100644
View file @
e85014e7
package
net.mamoe.mirai.util
;
import
java.io.ByteArrayOutputStream
;
import
java.util.Random
;
/**
* 加密解密QQ消息的工具类. QQ消息的加密算法是一个16次的迭代过程,并且是反馈的,每一个加密单元是8字节,输出也是8字节,密钥是16字节
* 我们以prePlain表示前一个明文块,plain表示当前明文块,crypt表示当前明文块加密得到的密文块,preCrypt表示前一个密文块
* f表示加密算法,d表示解密算法 那么从plain得到crypt的过程是: crypt = f(plain ˆ preCrypt) ˆ
* prePlain 所以,从crypt得到plain的过程自然是 plain = d(crypt ˆ prePlain) ˆ
* preCrypt 此外,算法有它的填充机制,其会在明文前和明文后分别填充一定的字节数,以保证明文长度是8字节的倍数
* 填充的字节数与原始明文长度有关,填充的方法是:
*
* <pre>
* <code>
*
* ------- 消息填充算法 -----------
* a = (明文长度 + 10) mod 8
* if(a 不等于 0) a = 8 - a;
* b = 随机数 & 0xF8 | a; 这个的作用是把a的值保存了下来
* plain[0] = b; 然后把b做为明文的第0个字节,这样第0个字节就保存了a的信息,这个信息在解密时就要用来找到真正明文的起始位置
* plain[1 至 a+2] = 随机数 & 0xFF; 这里用随机数填充明文的第1到第a+2个字节
* plain[a+3 至 a+3+明文长度-1] = 明文; 从a+3字节开始才是真正的明文
* plain[a+3+明文长度, 最后] = 0; 在最后,填充0,填充到总长度为8的整数为止。到此为止,结束了,这就是最后得到的要加密的明文内容
* ------- 消息填充算法 ------------
*
* </code>
* </pre>
*
* @author https://juejin.im/post/5c5bdf8cf265da2dc706d532
* @author luma
* @author notXX
*/
final
class
_TEAEncryption
{
// 指向当前的明文块
private
byte
[]
plain
;
// 这指向前面一个明文块
private
byte
[]
prePlain
;
// 输出的密文或者明文
private
byte
[]
out
;
// 当前加密的密文位置和上一次加密的密文块位置,他们相差8
private
int
crypt
,
preCrypt
;
// 当前处理的加密解密块的位置
private
int
pos
;
// 填充数
private
int
padding
;
// 密钥
private
byte
[]
key
;
// 用于加密时,表示当前是否是第一个8字节块,因为加密算法是反馈的
// 但是最开始的8个字节没有反馈可用,所有需要标明这种情况
private
boolean
header
=
true
;
// 这个表示当前解密开始的位置,之所以要这么一个变量是为了避免当解密到最后时
// 后面已经没有数据,这时候就会出错,这个变量就是用来判断这种情况免得出错
private
int
contextStart
;
// 随机数对象
private
static
Random
random
=
new
Random
();
// 字节输出流
private
ByteArrayOutputStream
baos
;
/**
* 构造函数
*/
public
_TEAEncryption
()
{
baos
=
new
ByteArrayOutputStream
(
8
);
}
/**
* 把字节数组从offset开始的len个字节转换成一个unsigned int, 因为java里面没有unsigned,所以unsigned
* int使用long表示的, 如果len大于8,则认为len等于8。如果len小于8,则高位填0 <br>
* (edited by notxx) 改变了算法, 性能稍微好一点. 在我的机器上测试10000次, 原始算法花费18s, 这个算法花费12s.
*
* @param in 字节数组.
* @param offset 从哪里开始转换.
* @param len 转换长度, 如果len超过8则忽略后面的
* @return
*/
private
static
long
getUnsignedInt
(
byte
[]
in
,
int
offset
,
int
len
)
{
long
ret
=
0
;
int
end
=
0
;
if
(
len
>
8
)
end
=
offset
+
8
;
else
end
=
offset
+
len
;
for
(
int
i
=
offset
;
i
<
end
;
i
++)
{
ret
<<=
8
;
ret
|=
in
[
i
]
&
0xff
;
}
return
(
ret
&
0xffffffff
L
)
|
(
ret
>>>
32
);
}
/**
* 解密
*
* @param in 密文
* @param offset 密文开始的位置
* @param len 密文长度
* @param k 密钥
* @return 明文
*/
public
byte
[]
decrypt
(
byte
[]
in
,
int
offset
,
int
len
,
byte
[]
k
)
{
// 检查密钥
if
(
k
==
null
)
return
null
;
crypt
=
preCrypt
=
0
;
this
.
key
=
k
;
int
count
;
byte
[]
m
=
new
byte
[
offset
+
8
];
// 因为QQ消息加密之后至少是16字节,并且肯定是8的倍数,这里检查这种情况
if
((
len
%
8
!=
0
)
||
(
len
<
16
))
return
null
;
// 得到消息的头部,关键是得到真正明文开始的位置,这个信息存在第一个字节里面,所以其用解密得到的第一个字节与7做与
prePlain
=
decipher
(
in
,
offset
);
pos
=
prePlain
[
0
]
&
0x7
;
// 得到真正明文的长度
count
=
len
-
pos
-
10
;
// 如果明文长度小于0,那肯定是出错了,比如传输错误之类的,返回
if
(
count
<
0
)
return
null
;
// 这个是临时的preCrypt,和加密时第一个8字节块没有prePlain一样,解密时
// 第一个8字节块也没有preCrypt,所有这里建一个全0的
for
(
int
i
=
offset
;
i
<
m
.
length
;
i
++)
m
[
i
]
=
0
;
// 通过了上面的代码,密文应该是没有问题了,我们分配输出缓冲区
out
=
new
byte
[
count
];
// 设置preCrypt的位置等于0,注意目前的preCrypt位置是指向m的,因为java没有指针,所以我们在后面要控制当前密文buf的引用
preCrypt
=
0
;
// 当前的密文位置,为什么是8不是0呢?注意前面我们已经解密了头部信息了,现在当然该8了
crypt
=
8
;
// 自然这个也是8
contextStart
=
8
;
// 加1,和加密算法是对应的
pos
++;
// 开始跳过头部,如果在这个过程中满了8字节,则解密下一块
// 因为是解密下一块,所以我们有一个语句 m = in,下一块当然有preCrypt了,我们不再用m了
// 但是如果不满8,这说明了什么?说明了头8个字节的密文是包含了明文信息的,当然还是要用m把明文弄出来
// 所以,很显然,满了8的话,说明了头8个字节的密文除了一个长度信息有用之外,其他都是无用的填充
padding
=
1
;
while
(
padding
<=
2
)
{
if
(
pos
<
8
)
{
pos
++;
padding
++;
}
if
(
pos
==
8
)
{
m
=
in
;
if
(!
decrypt8Bytes
(
in
,
offset
,
len
))
return
null
;
}
}
// 这里是解密的重要阶段,这个时候头部的填充都已经跳过了,开始解密
// 注意如果上面一个while没有满8,这里第一个if里面用的就是原始的m,否则这个m就是in了
int
i
=
0
;
while
(
count
!=
0
)
{
if
(
pos
<
8
)
{
out
[
i
]
=
(
byte
)
(
m
[
offset
+
preCrypt
+
pos
]
^
prePlain
[
pos
]);
i
++;
count
--;
pos
++;
}
if
(
pos
==
8
)
{
m
=
in
;
preCrypt
=
crypt
-
8
;
if
(!
decrypt8Bytes
(
in
,
offset
,
len
))
return
null
;
}
}
// 最后的解密部分,上面一个while已经把明文都解出来了,就剩下尾部的填充了,应该全是0
// 所以这里有检查是否解密了之后是不是0,如果不是的话那肯定出错了,返回null
for
(
padding
=
1
;
padding
<
8
;
padding
++)
{
if
(
pos
<
8
)
{
if
((
m
[
offset
+
preCrypt
+
pos
]
^
prePlain
[
pos
])
!=
0
)
return
null
;
pos
++;
}
if
(
pos
==
8
)
{
m
=
in
;
preCrypt
=
crypt
;
if
(!
decrypt8Bytes
(
in
,
offset
,
len
))
return
null
;
}
}
return
out
;
}
/**
* @param in 需要被解密的密文
* @param k 密钥
* @return Message 已解密的消息
* @paraminLen 密文长度
*/
public
byte
[]
decrypt
(
byte
[]
in
,
byte
[]
k
)
{
return
decrypt
(
in
,
0
,
in
.
length
,
k
);
}
/**
* 加密
*
* @param in 明文字节数组
* @param offset 开始加密的偏移
* @param len 加密长度
* @param k 密钥
* @return 密文字节数组
*/
public
byte
[]
encrypt
(
byte
[]
in
,
int
offset
,
int
len
,
byte
[]
k
)
{
// 检查密钥
if
(
k
==
null
)
return
in
;
plain
=
new
byte
[
8
];
prePlain
=
new
byte
[
8
];
pos
=
1
;
padding
=
0
;
crypt
=
preCrypt
=
0
;
this
.
key
=
k
;
header
=
true
;
// 计算头部填充字节数
pos
=
(
len
+
0x0A
)
%
8
;
if
(
pos
!=
0
)
pos
=
8
-
pos
;
// 计算输出的密文长度
out
=
new
byte
[
len
+
pos
+
10
];
// 这里的操作把pos存到了plain的第一个字节里面
// 0xF8后面三位是空的,正好留给pos,因为pos是0到7的值,表示文本开始的字节位置
plain
[
0
]
=
(
byte
)
((
rand
()
&
0xF8
)
|
pos
);
// 这里用随机产生的数填充plain[1]到plain[pos]之间的内容
for
(
int
i
=
1
;
i
<=
pos
;
i
++)
plain
[
i
]
=
(
byte
)
(
rand
()
&
0xFF
);
pos
++;
// 这个就是prePlain,第一个8字节块当然没有prePlain,所以我们做一个全0的给第一个8字节块
for
(
int
i
=
0
;
i
<
8
;
i
++)
prePlain
[
i
]
=
0x0
;
// 继续填充2个字节的随机数,这个过程中如果满了8字节就加密之
padding
=
1
;
while
(
padding
<=
2
)
{
if
(
pos
<
8
)
{
plain
[
pos
++]
=
(
byte
)
(
rand
()
&
0xFF
);
padding
++;
}
if
(
pos
==
8
)
encrypt8Bytes
();
}
// 头部填充完了,这里开始填真正的明文了,也是满了8字节就加密,一直到明文读完
int
i
=
offset
;
while
(
len
>
0
)
{
if
(
pos
<
8
)
{
plain
[
pos
++]
=
in
[
i
++];
len
--;
}
if
(
pos
==
8
)
encrypt8Bytes
();
}
// 最后填上0,以保证是8字节的倍数
padding
=
1
;
while
(
padding
<=
7
)
{
if
(
pos
<
8
)
{
plain
[
pos
++]
=
0x0
;
padding
++;
}
if
(
pos
==
8
)
encrypt8Bytes
();
}
return
out
;
}
/**
* @param in 需要加密的明文
* @param k 密钥
* @return Message 密文
* @paraminLen 明文长度
*/
public
byte
[]
encrypt
(
byte
[]
in
,
byte
[]
k
)
{
return
encrypt
(
in
,
0
,
in
.
length
,
k
);
}
/**
* 加密一个8字节块
*
* @param in 明文字节数组
* @return 密文字节数组
*/
private
byte
[]
encipher
(
byte
[]
in
)
{
// 迭代次数,16次
int
loop
=
0x10
;
// 得到明文和密钥的各个部分,注意java没有无符号类型,所以为了表示一个无符号的整数
// 我们用了long,这个long的前32位是全0的,我们通过这种方式模拟无符号整数,后面用到的long也都是一样的
// 而且为了保证前32位为0,需要和0xFFFFFFFF做一下位与
long
y
=
getUnsignedInt
(
in
,
0
,
4
);
long
z
=
getUnsignedInt
(
in
,
4
,
4
);
long
a
=
getUnsignedInt
(
key
,
0
,
4
);
long
b
=
getUnsignedInt
(
key
,
4
,
4
);
long
c
=
getUnsignedInt
(
key
,
8
,
4
);
long
d
=
getUnsignedInt
(
key
,
12
,
4
);
// 这是算法的一些控制变量,为什么delta是0x9E3779B9呢?
// 这个数是TEA算法的delta,实际是就是(sqr(5) - 1) * 2^31 (根号5,减1,再乘2的31次方)
long
sum
=
0
;
long
delta
=
0x9E3779B9
;
delta
&=
0xFFFFFFFF
L
;
// 开始迭代了,乱七八糟的,我也看不懂,反正和DES之类的差不多,都是这样倒来倒去
while
(
loop
--
>
0
)
{
sum
+=
delta
;
sum
&=
0xFFFFFFFF
L
;
y
+=
((
z
<<
4
)
+
a
)
^
(
z
+
sum
)
^
((
z
>>>
5
)
+
b
);
y
&=
0xFFFFFFFF
L
;
z
+=
((
y
<<
4
)
+
c
)
^
(
y
+
sum
)
^
((
y
>>>
5
)
+
d
);
z
&=
0xFFFFFFFF
L
;
}
// 最后,我们输出密文,因为我用的long,所以需要强制转换一下变成int
baos
.
reset
();
writeInt
((
int
)
y
);
writeInt
((
int
)
z
);
return
baos
.
toByteArray
();
}
/**
* 解密从offset开始的8字节密文
*
* @param in 密文字节数组
* @param offset 密文开始位置
* @return 明文
*/
private
byte
[]
decipher
(
byte
[]
in
,
int
offset
)
{
// 迭代次数,16次
int
loop
=
0x10
;
// 得到密文和密钥的各个部分,注意java没有无符号类型,所以为了表示一个无符号的整数
// 我们用了long,这个long的前32位是全0的,我们通过这种方式模拟无符号整数,后面用到的long也都是一样的
// 而且为了保证前32位为0,需要和0xFFFFFFFF做一下位与
long
y
=
getUnsignedInt
(
in
,
offset
,
4
);
long
z
=
getUnsignedInt
(
in
,
offset
+
4
,
4
);
long
a
=
getUnsignedInt
(
key
,
0
,
4
);
long
b
=
getUnsignedInt
(
key
,
4
,
4
);
long
c
=
getUnsignedInt
(
key
,
8
,
4
);
long
d
=
getUnsignedInt
(
key
,
12
,
4
);
// 算法的一些控制变量,sum在这里也有数了,这个sum和迭代次数有关系
// 因为delta是这么多,所以sum如果是这么多的话,迭代的时候减减减,减16次,最后
// 得到0。反正这就是为了得到和加密时相反顺序的控制变量,这样才能解密呀~~
long
sum
=
0xE3779B90
;
sum
&=
0xFFFFFFFF
L
;
long
delta
=
0x9E3779B9
;
delta
&=
0xFFFFFFFF
L
;
// 迭代开始了, @_@
while
(
loop
--
>
0
)
{
z
-=
((
y
<<
4
)
+
c
)
^
(
y
+
sum
)
^
((
y
>>>
5
)
+
d
);
z
&=
0xFFFFFFFF
L
;
y
-=
((
z
<<
4
)
+
a
)
^
(
z
+
sum
)
^
((
z
>>>
5
)
+
b
);
y
&=
0xFFFFFFFF
L
;
sum
-=
delta
;
sum
&=
0xFFFFFFFF
L
;
}
baos
.
reset
();
writeInt
((
int
)
y
);
writeInt
((
int
)
z
);
return
baos
.
toByteArray
();
}
/**
* 写入一个整型到输出流,高字节优先
*
* @param t
*/
private
void
writeInt
(
int
t
)
{
baos
.
write
(
t
>>>
24
);
baos
.
write
(
t
>>>
16
);
baos
.
write
(
t
>>>
8
);
baos
.
write
(
t
);
}
/**
* 解密
*
* @param in 密文
* @return 明文
*/
private
byte
[]
decipher
(
byte
[]
in
)
{
return
decipher
(
in
,
0
);
}
/**
* 加密8字节
*/
private
void
encrypt8Bytes
()
{
// 这部分完成我上面所说的 plain ^ preCrypt,注意这里判断了是不是第一个8字节块,如果是的话,那个prePlain就当作preCrypt用
for
(
pos
=
0
;
pos
<
8
;
pos
++)
{
if
(
header
)
plain
[
pos
]
^=
prePlain
[
pos
];
else
plain
[
pos
]
^=
out
[
preCrypt
+
pos
];
}
// 这个完成我上面说的 f(plain ^ preCrypt)
byte
[]
crypted
=
encipher
(
plain
);
// 这个没什么,就是拷贝一下,java不像c,所以我只好这么干,c就不用这一步了
System
.
arraycopy
(
crypted
,
0
,
out
,
crypt
,
8
);
// 这个完成了 f(plain ^ preCrypt) ^ prePlain,ok,下面拷贝一下就行了
for
(
pos
=
0
;
pos
<
8
;
pos
++)
out
[
crypt
+
pos
]
^=
prePlain
[
pos
];
System
.
arraycopy
(
plain
,
0
,
prePlain
,
0
,
8
);
// 完成了加密,现在是调整crypt,preCrypt等等东西的时候了
preCrypt
=
crypt
;
crypt
+=
8
;
pos
=
0
;
header
=
false
;
}
/**
* 解密8个字节
*
* @param in 密文字节数组
* @param offset 从何处开始解密
* @param len 密文的长度
* @return true表示解密成功
*/
private
boolean
decrypt8Bytes
(
byte
[]
in
,
int
offset
,
int
len
)
{
// 这里第一步就是判断后面还有没有数据,没有就返回,如果有,就执行 crypt ^ prePlain
for
(
pos
=
0
;
pos
<
8
;
pos
++)
{
if
(
contextStart
+
pos
>=
len
)
return
true
;
prePlain
[
pos
]
^=
in
[
offset
+
crypt
+
pos
];
}
// 好,这里执行到了 d(crypt ^ prePlain)
prePlain
=
decipher
(
prePlain
);
if
(
prePlain
==
null
)
return
false
;
// 解密完成,最后一步好像没做?
// 这里最后一步放到decrypt里面去做了,因为解密的步骤有点不太一样
// 调整这些变量的值先
contextStart
+=
8
;
crypt
+=
8
;
pos
=
0
;
return
true
;
}
/**
* 这是个随机因子产生器,用来填充头部的,如果为了调试,可以用一个固定值
* 随机因子可以使相同的明文每次加密出来的密文都不一样
*
* @return 随机因子
*/
private
int
rand
()
{
return
random
.
nextInt
();
}
}
mirai-native/src/main/java/net/mamoe/mirai/util/TeaEncryption.java
0 → 100644
View file @
e85014e7
package
net.mamoe.mirai.util
;
/**
* @author Him188moe @ Mirai Project
*/
public
final
class
TeaEncryption
{
public
static
native
int
Decrypt
();
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment