1. はじめに
2021/10/23 00:00(JST)~2021/10/24 24:00(JST)に開催された「ASIS CTF Quals 2021」にチーム「N30Z30N」でソロ参加しました。Welcome 以外に 6 問を解き、578 点を獲得して得点を得た 741 チーム中 40 位でした。
深夜問題を解いて朝寝る、という「ゲゲゲな生活リズム」に陥ってしまい、「学校も試験もないけど仕事はある」ので途中で離脱も考えました。しかし最終日の夕方から未回答の Crypto 問を 2 つ倒すという謎展開が発生し、昨年より(数字上の)成績は up する結果となりました。
2. Writeup
2-1. Lagleg(Crypto, 201 pt, 18 solves)
Task
Broken crypto systems are lagleg, try to break it again!
# !/usr/bin/env python3
from Crypto.Util.number import *
from gmpy import *
from math import gcd
from flag import flag
def lag(k, a, n):
s, t = 2, a
if k == 0:
return 2
r = 0
while k % 2 == 0:
r += 1
k //= 2
B = bin(k)[2:]
for b in B:
if b == '0':
t = (s * t - a) % n
s = (s **2 - 2) % n
else:
s = (s * t - a) % n
t = (t** 2 - 2) % n
for _ in range(r):
s = (s ** 2 - 2) % n
return s
def legkey(nbit):
while True:
r = getRandomNBitInteger(nbit >> 1)
s = getRandomNBitInteger(nbit >> 3)
p, q = r**5 + s, s + r
if isPrime(p) and isPrime(q):
while True:
a = getRandomRange(2, q)
if q*legendre(a, p) - p*legendre(a, q) == p - q:
return p, q, a
def keygen(p, q, a):
e = 65537
if gcd(e, p**2 - 1) * gcd(e, q**2 - 1) < e:
d = inverse(e, (p**2 - 1) * (q**2 - 1))
x = pow(d, e, n)
y = lag(x, a, p * q)
return e, y, d
def encrypt(m, n, a, y, e):
assert m < n
r = getRandomRange(2, n)
enc = (lag(r, a, n), (lag(r, y, n) + lag(e, m, n)) % n)
return enc
p, q, a = legkey(512)
n = p * q
e, y, d = keygen(p, q, a)
m = bytes_to_long(flag)
c = encrypt(m, n, a, y, e)
print(f'a = {a}')
print(f'n = {n}')
print(f'y = {y}')
print(f'C = {c}')
a = 192948041792305023195893277034532781336
n = 772559547290160010920412621051392165317498598296084946084386444091060134053985973087541256301003639549317798291916637210182966424107689011211268907278162096553174971554689109947325734336069313105789282878112740205249104820920364881
y = 754843853942922590978288450377461057162899072980081889481597335367906588582097339709346991452615504422434823707721197330881973700388055679080814559570248350531810374624494389646277873934234170885190847719684200687267925979436889772
C = (9083709539234699681499154559006541145975405183323215645582033885264296926186620280958201308661746194284022873377667665062501349047202357817146222033735539058147945671541486202387767382626733526030628929826676457655813734637020574, 625771268848498566477216756364333384750869252753726246816617776940622341574266652518894117167008714362418009723919180248010211052475114496172513936468417590330695688907796560242492250071433491517329459840410014214097477377322316145)
Solution
p と q の作り方から、q は 高々(十進で) 40 桁くらいの数なので、msieve を使って n を分解しました(所要約 50 分)。
p と q が分かったので、x の値が計算できます。あとは、c から m を求めることになります。
lag(r, y, n) = lag(x, lag(r, a, n), n) と lag(d, ct, n)) = lag(d, lag(e, m, n), n)) = m は、ダミーの m を入れた計算試行をもとに Guess したものです。
(ちゃんとした理論武装は復習しながら補完する予定!?)
Solver
from Crypto.Util.number import long_to_bytes, inverse
from math import gcd
def keygen(p, q, a):
e = 65537
if gcd(e, p**2 - 1) * gcd(e, q**2 - 1) < e:
d = inverse(e, (p**2 - 1) * (q**2 - 1))
x = pow(d, e, n)
y = lag(x, a, p * q)
return e, y, d
def lag(k, a, n):
s, t = 2, a
if k == 0:
return 2
r = 0
while k % 2 == 0:
r += 1
k //= 2
B = bin(k)[2:]
for b in B:
if b == '0':
t = (s * t - a) % n
s = (s **2 - 2) % n
else:
s = (s * t - a) % n
t = (t** 2 - 2) % n
for _ in range(r):
s = (s ** 2 - 2) % n
return s
a = 192948041792305023195893277034532781336
n = 772559547290160010920412621051392165317498598296084946084386444091060134053985973087541256301003639549317798291916637210182966424107689011211268907278162096553174971554689109947325734336069313105789282878112740205249104820920364881
y = 754843853942922590978288450377461057162899072980081889481597335367906588582097339709346991452615504422434823707721197330881973700388055679080814559570248350531810374624494389646277873934234170885190847719684200687267925979436889772
C = (9083709539234699681499154559006541145975405183323215645582033885264296926186620280958201308661746194284022873377667665062501349047202357817146222033735539058147945671541486202387767382626733526030628929826676457655813734637020574, 625771268848498566477216756364333384750869252753726246816617776940622341574266652518894117167008714362418009723919180248010211052475114496172513936468417590330695688907796560242492250071433491517329459840410014214097477377322316145)
# by msieve153
p = 2550409808325137823968530358952437943026720514821649528243821115841528756062138960527791756972835188605545376096994088803005608017893116472171414836257826367010252535637928389285160343813850031
q = 302915847001663746574137782285124484351
assert p * q == n
e, y, d = keygen(p, q, a)
print(f"d={d}")
# d=538062293807748210425073365436378141893890370157686998223999073641973059143858547185432307560123604450541751452708821753054649593921861588374616199120801510769028094278418259821111969267405342068727427121195273649389491282836703625608576864378084568410068552968112511647386960827658441215595425435453081627394766022422271443374281541657618699527389499082843376820586363199973252939301589961866277146918490844609051234717197542299805844879307298569219975706345473
x = pow(d, e, n)
lag_r_a_n = C[0]
ct = C[1] - lag(x, lag_r_a_n, n)
print(long_to_bytes(lag(d, ct, n)))
Flag
ASIS{N0w_LUc4s_vers10n_Of_the_El_Gamal_3nCryp7iOn_5cH3mE_:P}
2-2. Pinhole(Crypto, 134 pt, 31 solves)
Task
Pinholes sometimes automatically closed after a period of time to minimize the security exposure. What about this pinhole?
# !/usr/bin/env sage
from sage.all import *
from Crypto.Util.number import *
from secret import a, flag
def random_poly(degree):
R.<x> = ZZ[]
f = x**degree
for i in range(1, degree):
f += randint(-3, 3) * x ** (degree - i)
return f
def genkey(a):
M, N = [SL2Z.random_element() for _ in '01']
A = N * matrix(ZZ, [[0, -1], [1, 1]]) * N**(-1)
B = N * matrix(ZZ, [[0, -1], [1, 0]]) * N**(-1)
r, s = [randint(5, 14) for _ in '01']
U, V = (B * A) ** r, (B * A**2) ** s
F = []
for _ in range(2):
Ux = [random_poly(randint(1, 4)) for _ in range(4)]
Ux = [Ux[i] - Ux[i](a) + U[i // 2][i % 2] for i in range(4)]
Ux = matrix([[Ux[0], Ux[1]], [Ux[2], Ux[3]]])
F.append(Ux)
X, Y = M * F[0] * M ** (-1), M * F[1] * M ** (-1)
pubkey, privkey = (X, Y), (M, a)
return pubkey, privkey
def encrypt(msg, pubkey):
X, Y = pubkey
C = Y
for b in msg:
C *= X ** (int(b) + 1) * Y
return C
pubkey, privkey = genkey(a)
msg = bin(bytes_to_long(flag.lstrip(b'ASIS{').rstrip(b'}')))[2:]
enc = encrypt(msg, pubkey)
print(f'pubkey = {pubkey}')
print(f'enc = {enc}')
(略)
Solution
【方針】
鍵の作り方なんかはぶっちゃけどうでも良くて、暗号文の作られ方に着目し、平文の bit が「1」なら X^2Y、「0」なら XY が乗ぜられている所に注意します。(余談ですけど、U, V のうちの「V」ってどこでも使ってないですよね。。。)
また、公開鍵や暗号文となる行列の各要素は「整数係数の多項式」ですが、x = 0 としても勿論それぞれの関係式は成立し、またこのケースでは x = 0 とすることで各行列の正則性を破壊しません。ですので、output.txt で示された pubkey(X と Y) 及び ENC に x = 0 を代入して考えも OK、ということでそうしました(Solver 内の X、Y、ENC 参照)。
【調査】
ENC に X と Y が何回乗ぜられているか、を求めます。行列式をもとに(det(ENC)がdet(X)、det(Y)で何回割れるかを)算出したところ、X が 319 回、Yが216回でした(実際には符号の差が生じ、割り切ると -1 が残って辻褄が合わなかったけどとりあえず目を瞑った)。つまり、msg の長さは 215 (メッセージを byte_to_long したものは215bit)ということが推測できます。
【復号】
ENC の右から「試しに」 X^2Y の逆行列を乗じてみて、その結果の行列の要素が全て整数ならば対応する平文の bit は「1」、非整数の要素があれば「0」と判断します(X や Yの行列式が「それなりの値」なのでこの手が使える)。その結果に応じて、ENC の右から X^2Y または X*Y の逆行列を乗じます。この処理を 215回 繰り返すと、ENC は Y と一致するはずです(実際には Y の第一行に-1を乗じたものが残りましたけど、フラグが出たからヨシ!?)
Solver
from Crypto.Util.number import *
X = matrix([[2802185,9559410],[-821422,-2802211]])
Y = matrix([[40630633,138967416],[-11893098,-40677503]])
ENC = matrix([[225504515705049899582576907659687103088715506040627476450077592357494310253893098474822018921325648245166542001817684455587455112040689449233285125682329582914511262045189344436617291664304598394601514185723235544493924410808817183700912519656630931157756880662296287576333798026342858153354266278921880920711043113749405410304847580930718771951227216370049472676993858477036837098044998231151021056695603427268185705958857865993592835991983831133767869146621943445930459023121589189646429094111039319574492111635634332802676477934698760233353298002582392490319637859859820349972449216023590702011453471569603689759259920793326845978362922374509799918679763420751884557973249531798423703900955653367110595093396263392127656332873176256838968961461984420769430666039342071871162588263681732190550807784407610547140320924059891114863533227965981882680150134616450287318664930056039595490914303545229368314963741644425391196072626935461450140798536287158615154445380945641005290815076167856274315321097609415766614219693417623508434775199541953769978170083678701621898364509046465218050175358797942215201195344285116526740715978475010705019560420426379195939563739915606652382075494495584121273890780733986848788488157264244848246318449831154832694724137669735967663460762872553345767689142605835421132640557803927556025315151881378070609291699843868608509659572646587830068559681993252627183179603390404155945759585127356751610426740673102569749461051568679049358448846631508894781771396539771929143626597123140736396127572898769648582583016297013953688025636739698133789713636417172489133051158863588797264717265901692894536317788342634965198434939830253329368943821246104006702867278160331960661645932092266321930682682003211776184683567494765667807866847564492643384933712754826056895100616398875319135706811468006035560010736058434772554613975177078819598967199633374476509776586761914019670168034397573469630114475634681039026543371725813189414526365541422171232396572783414902318059291071197285848023366721840818086092779475872382462429537430013716453382917445713011560659796721103030614932478628574824641833738678671227423,771130707373326066977594125223275988571687437843993734996197403240609913597110992079822532144494393224450576834978268655293034043626168169200911075465464805007062561366540257253340403678161412932191304707396368365643508706457033594866276415975870876839475625625906783305856579502591010785580155744928496215640605406225805225400363252907755587228508174859263655104172744932359115351520497557736268912696462593216676792666007282044411008880085841432762993321860774450351636522240571417634794968534512431048370432095177985730477491060074091370513972954876986530486540253961817617993531385415998788908966046941371583044906554202033345501618275589080166713883831051112258577337629772848525124811790898380303879069408358662178771231914436002240535642404187150890890079427045178983367671080328317282913065911395822220367380181068109630624111800318498992201512848528963579673535320254285561897592617891464266600937990632730543448342529125919078600900736128065719769116372298213011013390803965994874922980693621161679589823546646747787514531225279766969498390598763292854111325142111590363326073654782205744616205245640578292097670707289144272206445919516790549993701619485226397080803703820349958785996422042145804430583776567988873787038554384985589402305451124534339301416534347868010651314249359935666454166127880735142617859506081526795472554846569830327092928470025337499855821508174519003517470893710241620397299170352684336593457058400534596611362098851913699961161765753473595867056925745663309809032416001363219286197898642997907030379125437711980624268870864196611961853483284364897677289155287744610476185619288099651285426858589787906414048425389352220112954731377732303196367780013785666788923860919737589950053981583288484719422634532885633146555873703514566808535757927840275691966848981527936105238085078930712230552022527938952545827527686831427137394522561913295468139534466245491574225415034237023652107907698564041101832431286304242697057624649683782576455235209173501931876428338121179106901062436054922825251872812361006191536339973383147300861572170108274697677605101825341417026298616304393097207408043364988834],[65992128102768931485588076831551666626393037140843511670346459263864909865501690852097626858173879897476355721502467769042678823260808084710470598858929203916442645273215841575326904511718359864630543681024214000169213120015036973784795074318933872096649667973258025632346675111675011408338547750438710523208029774346982840978667589745040275219375475601545147318053896122543269233398764152067922864280703804722365918915245085290671248646792465141627679936608884659856029720430918903158032222641068319752606379419301886198149395272044303245122902766276334671004945449357356153450275508425153579579870736617896787836017273282640509349664017621108426983418841385759478470306550681010766863256510694573863672421526665973983665206654977704445324022560071656839625655095046622273621042374232386318424736192529797028940582219913703117985795273615063295878978352561274922837690278766285013134546011137663382110616599118379322514995981142227873475626659876926378948849801510126601192851192923548049119949693069133793264813182222168247335316598168064985290232241408873815184501249019494272113236804601208346876315129776148681793049814793585325522952844638373474432900960473946804280781516722684744685528000976927392576329328182174876202499093658328980271569702335738401385123617718362449818737743068041622795699620803248624580828348097935391186274904418490426186108919298413397722581150006473937849973583378156916671043327132369033117529114844583446696586187077642319296514694906397175613579774871747067889635535161144481315020378041431299630427697619801148763715792362252372979066549417387762246649111795972432882052566989373489751818155844979138975868479788592781688752483963892397281068556058637801183265496278928481850819177412624549183857134613577026619067231317572490695422133038644465206932631889485921764076201960431073974787375518636937424902876682666058239867665407631687112827201255830941442836190722612111492117432116573846184771734053045236874447312285323702635314498135371161665155648137247972109539064297448044853633600109370380392236431999060110983466472158675494079305880755146679614804602943152348473345063943573790414,225665354265097623727536703878161988067694202126232106237234217383185433557659860145228427003036306828454181110870680807560237686177011646324772246994278835788669605973037380917952594323171779343255917817748423720583205084916343828427171298571850485157595574437538020399396155172885954344170258535978566481483160646072723335207835764299995527224332856576293387781422349478980141471129338177700703905428137907506445826191088900988951947279113046669637265112968198845412017741092669482539327546553928164351686993372328604234842018618243676930489289372257388750531995416141789514598828356877869375330534851056955157291953671913895382863737484402660724747444847590654890171575019690774215319984718251926229204782931366919014032852736955708474809200639140543023268272492945748866257124244056073739182796797844771429312447221099112156024271791064041640375619692439047061617956648301236872008834073932126472229854719520734769676969193752417580016133612188084969760423310745241520764029855163661208118108498162946211976084015829117882272592275690312536269430595079152618592120002071486519396991428367919150621293148109196765673256096644646086756728511349859655151977575872807803465765797216948370776917004633525034263726442712927269583767136296906670355246100971430709276069950026182786758021271917058859219772098812384140406821436043062901693829210469138784485404972202155049888827923696347160145358491323935945498956516918615676103759247531235444426000088311907801439265189211631803510036271019739895185167039855195245834793258329776846499466467777252367175551956984861156176169223809927103564942440097158830826340397977717134369380660848726125834032467764885625608925293918532429375898413177239500751296481882472906225607381653283530632989300586923451849268032820584086340612045947569151409650378217108935195702219732589334658507235610830438499096720206503535998997583537462680297022265194471987947771241219901556410521701497974693727778308864450620868675681418446254959938002087124131022627163756645705315997577546717920181361897874008956540449975637636008088030200759265613124318823699905647695236925151302482857718720132512884037]])
assert det(ENC) % det(X)^319 * det(Y)^216 == det(X)^319 * det(Y)^216 % det(ENC) == 0
A = X*Y #when bit is 0
B = X*X*Y #when bit is 1
A_inv = A.inverse()
B_inv = B.inverse()
def check(M):
for i in range(2):
for j in range(2):
if "/" in str(M[i][j]):
return False
return True
msg = ""
while(True):
if check(ENC * B_inv):
msg = "1" + msg
ENC = ENC * B_inv
else:
msg = "0" + msg
ENC = ENC * A_inv
if len(msg)== 215:
break
print("ASIS{" + long_to_bytes(int(msg,2)).decode() + "}")
Flag
ASIS{L0OpHo13S_iN_cRyp705YST3mS!}
2-3. Spiritual(Crypto, 79 pt, 60 solves)
Task
The meaning of spirituality has developed and expanded over time, but what does it mean here?
(この他に、接続先サーバの IP アドレスとポート番号を提示)
Solution
有限体 Fp 上の楕円曲線の order が与えられ、その Fq(q = p**n)上での order を計算しろ、という問題が延々と出されます。 x^3 + a*x + b の a、b は伏せられているので、法則性があるのだと考え「楕円曲線 有限体 位数」でググったところ、
がヒットし、ここに計算の実装まで書かれていました。
これをもとに solver を作ったのですが、この問題が何問繰り返されるかわからなかったので、最後は例外発生で終わるというクソ仕様です。
Solver
import telnetlib
HOST = REDACTED
PORT = REDACTED
tn = telnetlib.Telnet(HOST, PORT)
def readline():
return tn.read_until(b"\n")
def sendline(x):
tn.write((str(x) + "\n").encode())
def GetOrd(p, t, n): #E(Fp) = p + 1 - t
a = 2
b = t
for i in range(n-1):
t_k = t * b - p * a
a = b
b = t_k
return -t_k + p**n + 1
for i in range(4):
print(readline())
while(True):
for i in range(2):
print(readline())
p = int(readline().strip().decode()[6:])
print(f"p={p}")
readline()
readline()
k = int(readline().strip().decode()[6:])
print(f"k={k}")
n = int(readline().strip().decode()[74:].replace("?",""))
print(f"n={n}")
t = p + 1 - k
x = GetOrd(p, t, n)
print(f"x={x}")
sendline(x)
print(readline())
Flag
ASIS{wH47_iZ_mY_5P1R!TuAL_4NiMal!???}
2-4. Madras(Crypto, 59 pt, 88 solves)
Task
Madras is a great place to learn the occult! The occult, is a category of supernatural beliefs and practices which generally fall outside the scope of religion and science. Do you want to go Madras?
# !/usr/bin/env python3
from Crypto.Util.number import *
from flag import FLAG
def gentuple(nbit):
a, b, c = [getPrime(nbit // 3) for _ in '012']
return a, b, c
def encrypt(msg, params):
a, b, c = params
e, n = 65537, a * b * c
m = bytes_to_long(msg)
assert m < n
enc = pow(m, e, n)
return enc
nbit = 513
a, b, c = gentuple(nbit)
enc = encrypt(FLAG, (a, b, c))
print(f'a*b + c = {a*b + c}')
print(f'b*c + a = {b*c + a}')
print(f'c*a + b = {c*a + b}')
print(f'enc % a = {enc % a}')
print(f'enc % b = {enc % b}')
print(f'enc % c = {enc % c}')
print(f'enc = {enc}')
a*b + c = 4553352994596121904719118095314305574744898996748617662645730434291671964711800262656927311612741715902
b*c + a = 4414187148384348278031172865715942397786003125047353436418952679980677617016484927045195450392723110402
c*a + b = 2621331497797998680087841425011881226283342008022511638116013676175393387095787512291008541271355772802
enc % a = 1235691098253903868470929520042453631250042769029968
enc % b = 2235727505835415157472856687960365216626058343546572
enc % c = 1197976933648163722609601772402895844093866589777721
enc = 6238548897897912462708514382106387305984378113132192980353695746912882399991285268937548949835500837749446265632471076030233510866260067177632747513323223
Solution
まずは最初の 3 式を連立させて a、b、c を計算します。ゴリゴリ計算し(笑)、
a = 1644376501336761869533914527999140316946467005479211
b = 2769045283056871559108237639832652911114008081576651
c = 1594118801665580510615541222527591707834932058213541
を得ます。(終結式使えばスマートにできたらしいけど、まぁいいか。。。)
次に、(a,b,cはすべて相異なっていたので)ed ≡ 1 mod (a-1)(b-1)(c-1) となる d を求め、pow(enc, d, n) で複号します、
※enc % a、enc % b、enc % c は使いませんでした。
Solver
from Crypto.Util.number import long_to_bytes
import gmpy2
a = 1644376501336761869533914527999140316946467005479211
b = 2769045283056871559108237639832652911114008081576651
c = 1594118801665580510615541222527591707834932058213541
enc = 6238548897897912462708514382106387305984378113132192980353695746912882399991285268937548949835500837749446265632471076030233510866260067177632747513323223
phi = (a-1) * (b-1) * (c-1)
d = gmpy2.invert(65537, phi)
print(long_to_bytes(pow(enc, d, a*b*c)).decode())
Flag
ASIS{m4dRa5_iZ_RSA_l1k3_cH41L3n9E?!!}
2-5. Crypto Warm up(Crypto[Warmup], 41 pt, 147 solves)
Task
Recovering secrets is hard, but there are always some easy parts to do it!
# !/usr/bin/env python3
from Crypto.Util.number import *
import string
from secret import is_valid, flag
def random_str(l):
rstr = ''
for _ in range(l):
rstr += string.printable[:94][getRandomRange(0, 93)]
return rstr
def encrypt(msg, nbit):
l, p = len(msg), getPrime(nbit)
rstr = random_str(p - l)
msg += rstr
while True:
s = getRandomNBitInteger(1024)
if is_valid(s, p):
break
enc = msg[0]
for i in range(p-1):
enc += msg[pow(s, i, p)]
return enc
nbit = 15
enc = encrypt(flag, nbit)
print(f'enc = {enc}')
(略)
Solution
s を総当たりし、"ASIS{" を含むものを候補として出力します。2 つ候補が出たので、その中から最も「まともな」ものを選びました。
Solver
with open("output.txt","r") as f:
enc = f.read()[len("enc = "):]
p = len(enc)
for s in range(2, p-1):
d = {0 : enc[0]}
for i in range(p-1):
if pow(s, i, p) in d.keys():
break
d[pow(s, i, p)] = enc[i+1]
if len(d) == p:
msg = ""
for i in range(p):
msg += d[i]
if "ASIS{" in msg:
print(f"s={s}")
print(msg)
候補は 2 つ、s = 8562のとき「ASIS{_how_dFC.YptZTh1S?h0mx_m4d;_lGD_w;dr_CUYpI0_5J2T3+?k!!!*Z}」、s = 10927のとき「ASIS{_how_d3CrYpt_Th1S_h0m3_m4dE_anD_wEird_CrYp70_5yST3M?!!!!!!}」が出現します。可読性の高い後者を正解とみなしました。
Flag
ASIS{_how_d3CrYpt_Th1S_h0m3_m4dE_anD_wEird_CrYp70_5yST3M?!!!!!!}
2-6. Factory(Misc[Warmup], 31 pt, 250 solves)
Task
In the simplest terms, factory misco-graphy is the ratio of output to input!
「factory.pdf」なるPDFファイルが与えられます。

Solution
バイナリエディタで中を見てみると、終端(%%EOF)が 2 つあって、1 つ目の後ろには別な PDF が連結されているようでした。

2 分割してそれぞれを適当な名前(たとえば head.pdf と tail.pdf)で保存して、 Acrobat Reader 等で読み込むと、前半にあった方の PDF ファイルでフラグを確認できます。

Flag
ASIS{PDF_1N_PDF_iZ_A_T4sK_fOR_fOreEnSic5_L0v3RS}
3. 感想
昨年より多く問題が解けたので、「やった感」があり、それはそれでよかったです。
ただ Crypto 問全般として、スクリプト内で使わない値を取得していたり、Typo 訂正が 2 問も入ったりとやや gdgdで(しかも Damas は d のサイズが Guessing だったとか)、作問チェック体制に疑問を持たざるを得なかった点が少々残念でした。