This commit is contained in:
parent
324d8b3b54
commit
b5111370a3
@ -22,13 +22,15 @@
|
|||||||
+ [ ] /api
|
+ [ ] /api
|
||||||
- [ ] 进阶 API
|
- [ ] 进阶 API
|
||||||
- [ ] 皮肤上传和安全检查
|
- [ ] 皮肤上传和安全检查
|
||||||
+ [ ] 皮肤数据的RSA签名
|
+ [x] 皮肤数据的RSA签名
|
||||||
|
+ [ ] 皮肤上传
|
||||||
|
+ [x] 安全检查
|
||||||
- [ ] 兼容S3后端
|
- [ ] 兼容S3后端
|
||||||
- [x] 服务器状态接口
|
- [x] 服务器状态接口
|
||||||
- [x] authlib-injector 元数据接口
|
- [x] authlib-injector 元数据接口
|
||||||
|
- [ ] TGbot前端
|
||||||
|
|
||||||
#### Release 1.0
|
#### Release 1.0
|
||||||
- [ ] TGbot前端
|
|
||||||
- [ ] 单元测试
|
- [ ] 单元测试
|
||||||
+ [ ] API
|
+ [ ] API
|
||||||
- [x] /authserver
|
- [x] /authserver
|
||||||
|
@ -16,7 +16,8 @@
|
|||||||
"fastify": "^3.29.0",
|
"fastify": "^3.29.0",
|
||||||
"hex-to-uuid": "^1.1.1",
|
"hex-to-uuid": "^1.1.1",
|
||||||
"mongoose": "^6.3.1",
|
"mongoose": "^6.3.1",
|
||||||
"pino-pretty": "^7.6.1"
|
"pino-pretty": "^7.6.1",
|
||||||
|
"pngjs": "^6.0.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "nodemon --watch src/index.js",
|
"dev": "nodemon --watch src/index.js",
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import mongoose from 'mongoose'
|
import mongoose from 'mongoose'
|
||||||
const { Schema } = mongoose
|
const { Schema } = mongoose
|
||||||
import { uuidToNoSymboUUID } from '../generator.js'
|
import { uuidToNoSymboUUID } from '../generator.js'
|
||||||
|
import { ImageSecurity } from '../secure.js'
|
||||||
|
|
||||||
export const PlayerSchema = new Schema({
|
export const PlayerSchema = new Schema({
|
||||||
username: String, // 有符号 UUID
|
username: String, // 有符号 UUID
|
||||||
@ -89,13 +90,16 @@ export function getPlayerSerialization(player) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const val = Buffer.from(JSON.stringify(textures)).toString('base64')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
uuid: uuidToNoSymboUUID(player.uuid),
|
uuid: uuidToNoSymboUUID(player.uuid),
|
||||||
name: player.username,
|
name: player.username,
|
||||||
properties: [
|
properties: [
|
||||||
{
|
{
|
||||||
name: "texturs",
|
name: "texturs",
|
||||||
value: Buffer.from(JSON.stringify(textures)).toString('base64')
|
value: val,
|
||||||
|
signature: ImageSecurity.sign(val),
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,89 @@
|
|||||||
export {
|
import { PNG } from 'pngjs'
|
||||||
|
import fs from 'fs'
|
||||||
|
import { createHash, createPrivateKey, createSign } from 'crypto'
|
||||||
|
import { config } from './config.js'
|
||||||
|
|
||||||
|
export const ImageSecurity = {
|
||||||
|
createImageHash: async (path, isCape) => {
|
||||||
|
const png = await new Promise((resolve, reject) => {
|
||||||
|
fs.createReadStream(path).pipe(new PNG()).on('metadata', function(metadata) {
|
||||||
|
if(metadata.width * metadata.height > 40960) {
|
||||||
|
reject('Image size exceeds 40960 pixels')
|
||||||
|
}
|
||||||
|
}).on('parsed', function(buff) {
|
||||||
|
resolve(this)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
if (isCape) {
|
||||||
|
if(png.width % 64 === 0 && png.height % 32 === 0) {
|
||||||
|
return createHashInternal(png)
|
||||||
|
} else if (png.width % 22 === 0 && png.height % 17 === 0) {
|
||||||
|
const newPNG = new PNG({
|
||||||
|
width: Math.ceil(png.width / 22),
|
||||||
|
height: Math.ceil(png.height / 17),
|
||||||
|
})
|
||||||
|
|
||||||
|
for (let y = 0; y < png.height; y++) {
|
||||||
|
for (let x = 0; x < png.width; x++) {
|
||||||
|
const idx = (png.width * y + x) << 2
|
||||||
|
newPNG.data[idx] = png.data[idx]
|
||||||
|
newPNG.data[idx + 1] = png.data[idx + 1]
|
||||||
|
newPNG.data[idx + 2] = png.data[idx + 2]
|
||||||
|
newPNG.data[idx + 3] = png.data[idx + 3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let y = png.height; y < newPNG.height; y++) {
|
||||||
|
for (let x = png.width; x < newPNG.width; x++) {
|
||||||
|
const idx = (png.width * y + x) << 2
|
||||||
|
newPNG.data[idx] = 0
|
||||||
|
newPNG.data[idx + 1] = 0
|
||||||
|
newPNG.data[idx + 2] = 0
|
||||||
|
newPNG.data[idx + 3] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return createHashInternal(newPNG)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(png.width % 64 === 0 && png.height % 32 === 0) {
|
||||||
|
const newPNG = new PNG({
|
||||||
|
width: png.width,
|
||||||
|
height: png.height
|
||||||
|
})
|
||||||
|
|
||||||
|
for (let y = 0; y < png.height; y++) {
|
||||||
|
for (let x = 0; x < png.width; x++) {
|
||||||
|
const idx = (png.width * y + x) << 2
|
||||||
|
newPNG.data[idx] = png.data[idx]
|
||||||
|
newPNG.data[idx + 1] = png.data[idx + 1]
|
||||||
|
newPNG.data[idx + 2] = png.data[idx + 2]
|
||||||
|
newPNG.data[idx + 3] = png.data[idx + 3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return createHashInternal(png)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Invalid image size')
|
||||||
|
},
|
||||||
|
sign: async (data) => {
|
||||||
|
return createSign('RSA-SHA1').update(data).sign(createPrivateKey(config.privateKey), 'hex')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const createHashInternal = (png) => {
|
||||||
|
const hash = createHash('sha256')
|
||||||
|
for (let y = 0; y < png.height; y++) {
|
||||||
|
for (let x = 0; x < png.width; x++) {
|
||||||
|
const idx = (png.width * y + x) << 2
|
||||||
|
hash.update(png.data[idx].toString(16))
|
||||||
|
hash.update(png.data[idx + 1].toString(16))
|
||||||
|
hash.update(png.data[idx + 2].toString(16))
|
||||||
|
hash.update(png.data[idx + 3].toString(16))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hash.digest('hex')
|
||||||
}
|
}
|
@ -2663,6 +2663,11 @@ pkg-dir@^4.2.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
find-up "^4.0.0"
|
find-up "^4.0.0"
|
||||||
|
|
||||||
|
pngjs@^6.0.0:
|
||||||
|
version "6.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-6.0.0.tgz#ca9e5d2aa48db0228a52c419c3308e87720da821"
|
||||||
|
integrity sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==
|
||||||
|
|
||||||
prepend-http@^2.0.0:
|
prepend-http@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
|
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user