feat(grpc): add decrypted mii data to 3DS response
Some checks failed
Build and Publish Docker Image / Build and Publish Docker Image (amd64) (push) Has been cancelled
Build and Publish Docker Image / Build and Publish Docker Image (arm64) (push) Has been cancelled

This commit is contained in:
Jemma Poffinbarger 2026-05-25 18:43:04 -05:00
parent 067363fd84
commit ab0f152eb3
5 changed files with 76 additions and 6 deletions

View File

@ -98,4 +98,5 @@ All configuration options are handled via environment variables
| `PN_FRIENDS_CONFIG_ACCOUNT_GRPC_PORT` | Port for your account server gRPC service | Yes |
| `PN_FRIENDS_CONFIG_ACCOUNT_GRPC_API_KEY` | API key for your account server gRPC service | No (Assumed to be an open gRPC API) |
| `PN_FRIENDS_CONFIG_HEALTH_CHECK_PORT` | Port for the basic UDP health check server | No |
| `PN_FRIENDS_CONFIG_ENABLE_BELLA` | Enables a debug user named "Bella" which is always assigned as your friend | No | No
| `PN_FRIENDS_CONFIG_ENABLE_BELLA` | Enables a debug user named "Bella" which is always assigned as your friend | No |
| `PN_FRIENDS_CONFIG_MII_DECRYPT_KEY` | AES key used to decrypt 3DS Mii data (as a hex string) | Yes |

View File

@ -14,6 +14,7 @@ type config struct {
AccountGRPCAPIKey string `envconf:"optional"`
HealthCheckPort uint16 `envconf:"optional"`
EnableBella bool `envconf:"optional"`
MiiDecryptKey string
}
var Config *config = &config{}

View File

@ -8,6 +8,7 @@ import (
database_3ds "github.com/PretendoNetwork/friends/database/3ds"
database_wiiu "github.com/PretendoNetwork/friends/database/wiiu"
"github.com/PretendoNetwork/friends/globals"
"github.com/PretendoNetwork/friends/utility"
pb "github.com/PretendoNetwork/grpc/go/friends/v2"
"github.com/PretendoNetwork/nex-go/v2/types"
"google.golang.org/grpc/codes"
@ -106,7 +107,11 @@ func (s *gRPCFriendsV2Server) GetUserData3DS(ctx context.Context, in *pb.GetUser
ProfanityFlag: bool(miiData.Mii.ProfanityFlag),
CharacterSet: uint32(miiData.Mii.CharacterSet),
MiiDataEncrypted: miiData.Mii.MiiData,
// TODO: Implement MiiData
}
mii_data, err := utility.DecryptMiiData(miiData.Mii.MiiData)
if err == nil {
mii.MiiData = mii_data
}
friendMii := &pb.FriendMii{
Pid: uint32(miiData.PID),

View File

@ -12,6 +12,7 @@ import (
database_3ds "github.com/PretendoNetwork/friends/database/3ds"
database_wiiu "github.com/PretendoNetwork/friends/database/wiiu"
"github.com/PretendoNetwork/friends/globals"
"github.com/PretendoNetwork/friends/utility"
pb "github.com/PretendoNetwork/grpc/go/friends/v2"
"github.com/PretendoNetwork/nex-go/v2/types"
friends_3ds_types "github.com/PretendoNetwork/nex-protocols-go/v2/friends-3ds/types"
@ -241,10 +242,15 @@ func (s *gRPCFriendsV2Server) GetUserFriendsData3DS(ctx context.Context, in *pb.
miiData := miiList[miiIndex]
mii := &pb.Mii{
Name: string(miiData.Mii.Name),
ProfanityFlag: bool(miiData.Mii.ProfanityFlag),
CharacterSet: uint32(miiData.Mii.CharacterSet),
MiiData: miiData.Mii.MiiData,
Name: string(miiData.Mii.Name),
ProfanityFlag: bool(miiData.Mii.ProfanityFlag),
CharacterSet: uint32(miiData.Mii.CharacterSet),
MiiDataEncrypted: miiData.Mii.MiiData,
}
mii_data, err := utility.DecryptMiiData(miiData.Mii.MiiData)
if err == nil {
mii.MiiData = mii_data
}
friendMii := &pb.FriendMii{

57
utility/decrypt_mii.go Normal file
View File

@ -0,0 +1,57 @@
package utility
import (
"crypto/aes"
"crypto/cipher"
"encoding/hex"
"fmt"
"github.com/PretendoNetwork/friends/globals"
"github.com/PretendoNetwork/nex-go/v2/types"
)
func ccmDecrypt(key, nonce, ciphertext []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
L := 15 - len(nonce)
counter := make([]byte, aes.BlockSize)
counter[0] = byte(L - 1)
copy(counter[1:1+len(nonce)], nonce)
stream := cipher.NewCTR(block, counter)
padded := make([]byte, aes.BlockSize+len(ciphertext))
copy(padded[aes.BlockSize:], ciphertext)
out := make([]byte, len(padded))
stream.XORKeyStream(out, padded)
return out[aes.BlockSize:], nil
}
func DecryptMiiData(mii_data types.Buffer) ([]byte, error) {
if len(mii_data) < 96 {
return nil, fmt.Errorf("Mii data length is incorrect: %d", len(mii_data))
}
nonce := mii_data[:8]
ciphertext := mii_data[8 : 8+0x58]
key, err := hex.DecodeString(globals.Config.MiiDecryptKey)
if err != nil {
return nil, err
}
content, err := ccmDecrypt(key, append(append([]byte{}, nonce...), 0, 0, 0, 0), ciphertext)
if err != nil {
return nil, err
}
result := make([]byte, 0, len(content)+len(nonce))
result = append(result, content[:12]...)
result = append(result, nonce...)
result = append(result, content[12:]...)
return result, nil
}