Skip to content

Instantly share code, notes, and snippets.

@jedisct1
Last active July 10, 2026 18:04
Show Gist options
  • Select an option

  • Save jedisct1/7c5d492f8b5323712ab5adf0be6c6696 to your computer and use it in GitHub Desktop.

Select an option

Save jedisct1/7c5d492f8b5323712ab5adf0be6c6696 to your computer and use it in GitHub Desktop.
# RS256 (RSA-SHA256, PKCS#1 v1.5) JWT signer in pure xvcl.
#
# The 2048-bit RSA key is hardcoded; the payload is signed at VCL RUNTIME.
# Fastly VCL has no loops and no big integers, so xvcl unrolls a 1024-bit
# Montgomery modular multiply (subroutine `mm`) and drives it with a fixed
# square-and-multiply call sequence derived from the CRT exponents dp, dq.
# Bignums are little-endian, stored as 10-digit decimal fields packed in one
# header (limb i = substr(buf, 10*i, 10)); radix is 2^31, 34 limbs per prime.
#const KC = 34
#const W = 31
#const MASK = 2147483647
#const FW = 10
#const P = 146940757555455584418374027822873203142565789710512619045156457640662925045088053709103347848419427162426921942975985473781757837029163145484077630514618829047184792111261700717338304682388932551821457994258731734097938799732652362736347872127999604450371008549967089201948910503944524733484401689040260486639
#const Q = 141770948727367031483039085955552651424361338049959442434001649684293351969295044009721187332495226292552568637644569830980040621217052145126495878743748962493279511556021202415730806307854671331995394379508908560299609088390016852410028858859430364096044673000441077772913472358214722037176236194039239572441
#const DP = 87776122152959865028837524073998856063419260896544829989148575006092937616768424167381585439030961960142386242055126955965058479360585745190596993408560241365461303177804664866916051848739556593546519660912691894017123244438006124002705080274334444888041482120369586266797349547872563571588275962040330771799
#const DQ = 69631888377332153995568078151628611268424967735632459412363689239478747982660759138027607291235314999633407569420953938834803661387547362550571064297763635102250243335471054283228694084922013755673890316645746031211134427059313860296113934690727436255665989886799790231965328924707778623596836395470178412153
#const QINV = 88376931446830195567607250260008915644980370263293821248747910177919294282505901963504996595391302633966982471805538102838941058646258299903767688454113406838568289543590639667945298377587186904806850845651157526048587066769617866700387908372585220894286897791674408684282145102411826625522566341152899496276
#const NPP = 1838221041
#const NPQ = 127249303
#const PREFIX = 8517306873408580822992635312440950711239255839406135638915695785157155190387115319509921306392695908259568017376177003703892468439867471313010266082044366152587365883633222577111472049055629505468998539970395284077667422034489754083521773830038551567748404355586664429892872238799740447530419140178373250649717644114389439609482724683280601140209723930557484934686269386505204448139030367437035720725484614708959520972713569161083070860431372724706744245785484250797231413335964222081666399605681989457294305123939770973418464768689184
#const HB = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9"
#const RP = 1 << (W * KC)
#const CP = (PREFIX << 256) % P
#const CQ = (PREFIX << 256) % Q
#const CPL = [(CP >> (W*i)) & MASK for i in range(KC)]
#const CQL = [(CQ >> (W*i)) & MASK for i in range(KC)]
#const PL = [(P >> (W*i)) & MASK for i in range(KC)]
#const QL = [(Q >> (W*i)) & MASK for i in range(KC)]
#const QINVR = (QINV * RP) % P
#const DPB = format(DP, 'b')
#const DQB = format(DQ, 'b')
#const PL_S = "".join([str(v).zfill(FW) for v in [(P >> (W*i)) & MASK for i in range(KC)]])
#const QL_S = "".join([str(v).zfill(FW) for v in [(Q >> (W*i)) & MASK for i in range(KC)]])
#const QINVR_S = "".join([str(v).zfill(FW) for v in [(QINVR >> (W*i)) & MASK for i in range(KC)]])
#const R2P_S = "".join([str(v).zfill(FW) for v in [(((RP*RP) % P) >> (W*i)) & MASK for i in range(KC)]])
#const R2Q_S = "".join([str(v).zfill(FW) for v in [(((RP*RP) % Q) >> (W*i)) & MASK for i in range(KC)]])
#const ONEP_S = "".join([str(v).zfill(FW) for v in [((RP % P) >> (W*i)) & MASK for i in range(KC)]])
#const ONEQ_S = "".join([str(v).zfill(FW) for v in [((RP % Q) >> (W*i)) & MASK for i in range(KC)]])
#const ONE_S = "".join([str(v).zfill(FW) for v in [(1 >> (W*i)) & MASK for i in range(KC)]])
sub ciosrow {
declare local var.x INTEGER;
declare local var.c INTEGER;
declare local var.m INTEGER;
declare local var.bi INTEGER;
declare local var.np INTEGER;
#for i in range(KC)
declare local var.a{{i}} INTEGER;
declare local var.n{{i}} INTEGER;
#endfor
#for i in range(KC + 2)
declare local var.t{{i}} INTEGER;
#endfor
#for i in range(KC)
set var.a{{i}} = std.atoi(substr(req.http.A, {{i*FW}}, {{FW}}));
set var.n{{i}} = std.atoi(substr(req.http.MOD, {{i*FW}}, {{FW}}));
#endfor
#for i in range(KC + 2)
set var.t{{i}} = std.atoi(substr(req.http.T, {{i*FW}}, {{FW}}));
#endfor
set var.bi = std.atoi(req.http.BI);
set var.np = std.atoi(req.http.NP);
set var.c = 0;
#for j in range(KC)
set var.x = var.a{{j}};
set var.x *= var.bi;
set var.x += var.t{{j}};
set var.x += var.c;
set var.c = var.x;
set var.c >>= 31;
set var.x &= {{MASK}};
set var.t{{j}} = var.x;
#endfor
set var.x = var.t{{KC}};
set var.x += var.c;
set var.c = var.x;
set var.c >>= 31;
set var.x &= {{MASK}};
set var.t{{KC}} = var.x;
set var.t{{KC+1}} = var.c;
set var.m = var.t0;
set var.m *= var.np;
set var.m &= {{MASK}};
set var.x = var.t0;
set var.c = var.m;
set var.c *= var.n0;
set var.x += var.c;
set var.c = var.x;
set var.c >>= 31;
#for j in range(1, KC)
set var.x = var.t{{j}};
set var.bi = var.m;
set var.bi *= var.n{{j}};
set var.x += var.bi;
set var.x += var.c;
set var.c = var.x;
set var.c >>= 31;
set var.x &= {{MASK}};
set var.t{{j-1}} = var.x;
#endfor
set var.x = var.t{{KC}};
set var.x += var.c;
set var.c = var.x;
set var.c >>= 31;
set var.x &= {{MASK}};
set var.t{{KC-1}} = var.x;
set var.x = var.t{{KC+1}};
set var.x += var.c;
set var.t{{KC}} = var.x;
set var.t{{KC+1}} = 0;
set req.http.T = "";
#for i in range(KC + 2)
set req.http.T = req.http.T + std.strpad(std.itoa(var.t{{i}}), {{FW}}, "0");
#endfor
}
sub mm {
set req.http.T = "{{"0" * (FW * (KC + 2))}}";
#for i in range(KC)
set req.http.BI = substr(req.http.O, {{i*FW}}, {{FW}});
call ciosrow;
#endfor
set req.http.A = substr(req.http.T, 0, {{KC*FW}});
}
sub sq { set req.http.O = req.http.A; call mm; }
sub mul { set req.http.O = req.http.BP; call mm; }
sub red {
declare local var.x INTEGER;
declare local var.brw INTEGER;
#for i in range(KC)
declare local var.t{{i}} INTEGER;
declare local var.n{{i}} INTEGER;
declare local var.s{{i}} INTEGER;
#endfor
#for i in range(KC)
set var.t{{i}} = std.atoi(substr(req.http.A, {{i*FW}}, {{FW}}));
set var.n{{i}} = std.atoi(substr(req.http.MOD, {{i*FW}}, {{FW}}));
#endfor
set var.brw = 0;
#for i in range(KC)
set var.x = var.t{{i}};
set var.x -= var.n{{i}};
set var.x -= var.brw;
set var.brw = var.x;
set var.brw >>= 31;
set var.brw &= 1;
set var.x &= {{MASK}};
set var.s{{i}} = var.x;
#endfor
if (var.brw == 0) {
#for i in range(KC)
set var.t{{i}} = var.s{{i}};
#endfor
}
set req.http.A = "";
#for i in range(KC)
set req.http.A = req.http.A + std.strpad(std.itoa(var.t{{i}}), {{FW}}, "0");
#endfor
}
sub macrow {
declare local var.x INTEGER;
declare local var.c INTEGER;
declare local var.qi INTEGER;
#for j in range(KC)
declare local var.h{{j}} INTEGER;
#endfor
#for i in range(2 * KC + 1)
declare local var.p{{i}} INTEGER;
#endfor
#for j in range(KC)
set var.h{{j}} = std.atoi(substr(req.http.H, {{j*FW}}, {{FW}}));
#endfor
#for i in range(2 * KC + 1)
set var.p{{i}} = std.atoi(substr(req.http.P, {{i*FW}}, {{FW}}));
#endfor
set var.qi = std.atoi(req.http.QI);
set var.c = 0;
#for j in range(KC)
set var.x = var.h{{j}};
set var.x *= var.qi;
set var.x += var.p{{j}};
set var.x += var.c;
set var.c = var.x;
set var.c >>= 31;
set var.x &= {{MASK}};
set var.p{{j}} = var.x;
#endfor
#for j in range(KC, 2 * KC + 1)
set var.x = var.p{{j}};
set var.x += var.c;
set var.c = var.x;
set var.c >>= 31;
set var.x &= {{MASK}};
set var.p{{j}} = var.x;
#endfor
set req.http.P = "";
#for i in range(2 * KC + 1)
set req.http.P = req.http.P + std.strpad(std.itoa(var.p{{i}}), {{FW}}, "0");
#endfor
}
sub qhmul {
set req.http.P = "{{"0" * (FW * (2 * KC + 1))}}";
#for i in range(KC)
set req.http.P = "{{"0" * FW}}" + substr(req.http.P, 0, {{2 * KC * FW}});
set req.http.QI = "{{QL[KC - 1 - i]}}";
call macrow;
#endfor
}
sub do_sign {
declare local var.x INTEGER;
declare local var.c INTEGER;
declare local var.borrow INTEGER;
declare local var.w INTEGER;
declare local var.tmp INTEGER;
#for i in range(16)
declare local var.hw{{i}} INTEGER;
#endfor
#for i in range(KC)
declare local var.h{{i}} INTEGER;
#endfor
#for i in range(69)
declare local var.p{{i}} INTEGER;
#endfor
#for i in range(KC)
declare local var.sq{{i}} INTEGER;
#endfor
#for i in range(128)
declare local var.o{{i}} INTEGER;
#endfor
set req.http.SI = "{{HB}}." + digest.base64url_nopad(req.http.X-Payload);
set req.http.HASH = digest.hash_sha256(req.http.SI);
#for wi in range(16)
set var.hw{{wi}} = std.strtol(substr(req.http.HASH, {{4*(15-wi)}}, 4), 16);
#endfor
#for j in range(KC)
#if (W*j)//16 < 16
set var.w = var.hw{{(W*j)//16}};
#if (W*j)//16 + 1 < 16
set var.tmp = var.hw{{(W*j)//16 + 1}};
set var.tmp <<= 16;
set var.w += var.tmp;
#endif
#if (W*j)//16 + 2 < 16
set var.tmp = var.hw{{(W*j)//16 + 2}};
set var.tmp <<= 32;
set var.w += var.tmp;
#endif
#if (W*j) % 16 != 0
set var.w >>= {{(W*j) % 16}};
#endif
set var.w &= {{MASK}};
set var.h{{j}} = var.w;
#else
set var.h{{j}} = 0;
#endif
#endfor
set var.c = 0;
#for i in range(KC)
set var.x = {{CPL[i]}};
set var.x += var.h{{i}};
set var.x += var.c;
set var.c = var.x;
set var.c >>= 31;
set var.x &= {{MASK}};
set var.p{{i}} = var.x;
#endfor
set req.http.A = "";
#for i in range(KC)
set req.http.A = req.http.A + std.strpad(std.itoa(var.p{{i}}), {{FW}}, "0");
#endfor
set req.http.MOD = "{{PL_S}}";
call red;
set req.http.MP = req.http.A;
set var.c = 0;
#for i in range(KC)
set var.x = {{CQL[i]}};
set var.x += var.h{{i}};
set var.x += var.c;
set var.c = var.x;
set var.c >>= 31;
set var.x &= {{MASK}};
set var.p{{i}} = var.x;
#endfor
set req.http.A = "";
#for i in range(KC)
set req.http.A = req.http.A + std.strpad(std.itoa(var.p{{i}}), {{FW}}, "0");
#endfor
set req.http.MOD = "{{QL_S}}";
call red;
set req.http.MQ = req.http.A;
set req.http.MOD = "{{PL_S}}";
set req.http.NP = "{{NPP}}";
set req.http.A = req.http.MP;
set req.http.O = "{{R2P_S}}";
call mm;
set req.http.BP = req.http.A;
set req.http.A = "{{ONEP_S}}";
#for b in DPB
call sq;
#if b == "1"
call mul;
#endif
#endfor
set req.http.O = "{{ONE_S}}";
call mm;
call red;
set req.http.SP = req.http.A;
set req.http.MOD = "{{QL_S}}";
set req.http.NP = "{{NPQ}}";
set req.http.A = req.http.MQ;
set req.http.O = "{{R2Q_S}}";
call mm;
set req.http.BP = req.http.A;
set req.http.A = "{{ONEQ_S}}";
#for b in DQB
call sq;
#if b == "1"
call mul;
#endif
#endfor
set req.http.O = "{{ONE_S}}";
call mm;
call red;
set req.http.SQ = req.http.A;
set var.borrow = 0;
#for i in range(KC)
set var.x = std.atoi(substr(req.http.SP, {{i*FW}}, {{FW}}));
set var.tmp = std.atoi(substr(req.http.SQ, {{i*FW}}, {{FW}}));
set var.x -= var.tmp;
set var.x -= var.borrow;
set var.borrow = var.x;
set var.borrow >>= 31;
set var.borrow &= 1;
set var.x &= {{MASK}};
set var.p{{i}} = var.x;
set var.sq{{i}} = std.atoi(substr(req.http.SQ, {{i*FW}}, {{FW}}));
#endfor
if (var.borrow == 1) {
set var.c = 0;
#for i in range(KC)
set var.x = var.p{{i}};
set var.x += {{PL[i]}};
set var.x += var.c;
set var.c = var.x;
set var.c >>= 31;
set var.x &= {{MASK}};
set var.p{{i}} = var.x;
#endfor
}
set req.http.A = "";
#for i in range(KC)
set req.http.A = req.http.A + std.strpad(std.itoa(var.p{{i}}), {{FW}}, "0");
#endfor
set req.http.MOD = "{{PL_S}}";
set req.http.NP = "{{NPP}}";
set req.http.O = "{{QINVR_S}}";
call mm;
call red;
set req.http.H = req.http.A;
call qhmul;
#for i in range(2 * KC + 1)
set var.p{{i}} = std.atoi(substr(req.http.P, {{i*FW}}, {{FW}}));
#endfor
set var.c = 0;
#for i in range(67)
set var.x = var.p{{i}};
#if i < KC
set var.x += var.sq{{i}};
#endif
set var.x += var.c;
set var.c = var.x;
set var.c >>= 31;
set var.x &= {{MASK}};
set var.p{{i}} = var.x;
#endfor
#for i in range(128)
set var.w = var.p{{(16*i)//31}};
#if (16*i)//31 + 1 < 67
set var.tmp = var.p{{(16*i)//31 + 1}};
set var.tmp <<= 31;
set var.w += var.tmp;
#endif
#if (16*i) % 31 != 0
set var.w >>= {{(16*i) % 31}};
#endif
set var.w &= 65535;
set var.o{{i}} = var.w;
#endfor
set req.http.HEX = "";
#for i in range(128)
set req.http.HEX = req.http.HEX + std.strpad(std.itoa(var.o{{127-i}}, 16), 4, "0");
#endfor
set req.http.B64 = bin.hex_to_base64(req.http.HEX);
set req.http.B64 = regsuball(req.http.B64, "[+]", "-");
set req.http.B64 = regsuball(req.http.B64, "[/]", "_");
set req.http.B64 = regsuball(req.http.B64, "=", "");
set req.http.X-Jwt = req.http.SI + "." + req.http.B64;
}
sub vcl_recv {
#FASTLY RECV
call do_sign;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment