mirror of
https://github.com/MengMengCode/VoCat.git
synced 2026-08-22 15:53:43 +08:00
83 lines
2.4 KiB
Go
83 lines
2.4 KiB
Go
package device
|
|
|
|
import (
|
|
"context"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
var (
|
|
ErrESIMNicknameTooLong = errors.New("esim: profile nickname must not exceed 64 characters")
|
|
ErrESIMNicknameProfileNotFound = errors.New("esim: profile to rename was not found on the eUICC")
|
|
)
|
|
|
|
func buildSetNicknameRequest(iccid, nickname string) ([]byte, error) {
|
|
bcd, err := encodeICCID(strings.TrimSpace(iccid))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !utf8.ValidString(nickname) {
|
|
return nil, errors.New("esim: profile nickname is not valid UTF-8")
|
|
}
|
|
if utf8.RuneCountInString(nickname) > 64 {
|
|
return nil, ErrESIMNicknameTooLong
|
|
}
|
|
// SGP.22 ES10c SetNicknameRequest:
|
|
// BF29 { 5A <ICCID BCD> 90 <UTF-8 nickname> }.
|
|
return derConstruct(0xBF29, derEncode(0x5A, bcd), derEncode(0x90, []byte(nickname))), nil
|
|
}
|
|
|
|
func setNicknameResult(payload []byte) (byte, bool) {
|
|
nodes := derParse(payload)
|
|
if len(nodes) != 1 || nodes[0].tag != 0xBF29 {
|
|
return 0, false
|
|
}
|
|
result := derFindValue(payload, 0x80)
|
|
if len(result) != 1 {
|
|
return 0, false
|
|
}
|
|
return result[0], true
|
|
}
|
|
|
|
// ESIMRenameProfile updates the on-card Profile Nickname through ES10c. It
|
|
// does not enable, disable, download, or delete a profile.
|
|
func (manager *Manager) ESIMRenameProfile(ctx context.Context, id, iccid, nickname, aidHex string) error {
|
|
request, err := buildSetNicknameRequest(iccid, nickname)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
manager.lockESIM()
|
|
defer manager.unlockESIM()
|
|
if err := manager.waitForESIMRecovery(ctx, id); err != nil {
|
|
return err
|
|
}
|
|
channel, err := manager.openEuiccAID(ctx, id, targetEuiccAID(aidHex))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer channel.close(context.Background())
|
|
|
|
commitContext, cancelCommit := context.WithTimeout(context.WithoutCancel(ctx), csimAPDUTimeout)
|
|
payload, err := channel.es10(commitContext, request)
|
|
cancelCommit()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
result, ok := setNicknameResult(payload)
|
|
if !ok {
|
|
return fmt.Errorf("esim: unexpected SetNickname response %s", strings.ToUpper(hex.EncodeToString(payload)))
|
|
}
|
|
switch result {
|
|
case 0:
|
|
manager.renameCachedProfile(id, strings.TrimSpace(iccid), nickname)
|
|
return nil
|
|
case 1:
|
|
return fmt.Errorf("%w (result=0x%02X)", ErrESIMNicknameProfileNotFound, result)
|
|
default:
|
|
return fmt.Errorf("esim: eUICC rejected SetNickname, result=0x%02X (raw %s)", result, strings.ToUpper(hex.EncodeToString(payload)))
|
|
}
|
|
}
|