Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
V
Vereign Client Library
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Container registry
Model registry
Operate
Environments
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Terms and privacy
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Code
Vereign Client Library
Commits
2ee83d93
Commit
2ee83d93
authored
6 years ago
by
Damyan Mitev
Committed by
Sasha Ilieva
6 years ago
Browse files
Options
Downloads
Patches
Plain Diff
Revert "Make ES Lint happy"
This reverts commit
bc806cba
.
parent
2d1ecb82
No related branches found
No related tags found
3 merge requests
!48
269 implement workflow for signing already uploaded document dev
,
!47
269 implement workflow for signing already uploaded document
,
!41
268 convert odt file to pdf
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
javascript/src/utilities/pdfUtilities.js
+569
-574
569 additions, 574 deletions
javascript/src/utilities/pdfUtilities.js
with
569 additions
and
574 deletions
javascript/src/utilities/pdfUtilities.js
+
569
−
574
View file @
2ee83d93
...
@@ -9,362 +9,367 @@
...
@@ -9,362 +9,367 @@
*/
*/
import
{
import
{
ObjectIdentifier
,
ObjectIdentifier
,
UTCTime
,
UTCTime
,
OctetString
OctetString
}
from
'
asn1js
'
;
}
from
'
asn1js
'
;
import
{
import
{
ContentInfo
,
ContentInfo
,
SignedData
,
SignedData
,
Attribute
,
Attribute
,
SignerInfo
,
SignerInfo
,
IssuerAndSerialNumber
,
IssuerAndSerialNumber
,
SignedAndUnsignedAttributes
,
SignedAndUnsignedAttributes
,
EncapsulatedContentInfo
,
EncapsulatedContentInfo
,
getCrypto
getCrypto
}
from
'
pkijs
'
;
}
from
'
pkijs
'
;
import
{
import
{
parseCertificate
,
parseCertificate
,
parsePrivateKey
parsePrivateKey
,
}
from
'
./signingUtilities
'
;
}
from
'
./signingUtilities
'
;
//keep this import to include the patched version of pdfjs library
//keep this import to include the patched version of pdfjs library
import
{
PDFJS
}
from
"
../lib/pdfjs.parser.js
"
;
import
{
PDFJS
}
from
"
../lib/pdfjs.parser.js
"
;
function
createXrefTable
(
xrefEntries
)
{
function
createXrefTable
(
xrefEntries
)
{
xrefEntries
=
sortOnKeys
(
xrefEntries
);
xrefEntries
=
sortOnKeys
(
xrefEntries
);
let
retVal
=
'
xref
\n
'
;
var
retVal
=
'
xref
\n
'
;
let
last
=
-
2
;
var
last
=
-
2
;
for
(
let
i
in
xrefEntries
)
{
for
(
var
i
in
xrefEntries
)
{
i
=
parseInt
(
i
);
i
=
parseInt
(
i
);
if
(
typeof
xrefEntries
[
i
].
offset
===
'
undefined
'
)
{
continue
;
}
if
(
typeof
xrefEntries
[
i
].
offset
===
'
undefined
'
)
{
continue
;
}
retVal
+=
calcFlow
(
i
,
last
,
xrefEntries
);
retVal
+=
calcFlow
(
i
,
last
,
xrefEntries
);
const
offset
=
xrefEntries
[
i
].
offset
;
var
offset
=
xrefEntries
[
i
].
offset
;
retVal
+=
pad10
(
offset
)
+
'
'
+
pad5
(
xrefEntries
[
i
].
gen
)
+
'
'
+
(
xrefEntries
[
i
].
free
?
'
f
'
:
'
n
'
)
+
'
\n
'
;
retVal
+=
pad10
(
offset
)
+
'
'
+
pad5
(
xrefEntries
[
i
].
gen
)
+
'
'
+
(
xrefEntries
[
i
].
free
?
'
f
'
:
'
n
'
)
+
'
\n
'
;
last
=
i
;
last
=
i
;
}
}
return
retVal
;
return
retVal
;
}
}
function
calcFlow
(
i
,
last
,
xrefEntries
)
{
function
calcFlow
(
i
,
last
,
xrefEntries
)
{
if
(
last
+
1
===
i
)
{
return
''
;
}
if
(
last
+
1
===
i
)
{
return
''
;}
let
count
=
1
;
var
count
=
1
;
while
(
typeof
xrefEntries
[
i
+
count
]
!==
'
undefined
'
&&
while
(
typeof
xrefEntries
[
(
i
+
count
)
]
!==
'
undefined
'
typeof
xrefEntries
[
i
+
count
].
offset
!==
'
undefined
'
)
{
count
++
;
}
&&
typeof
xrefEntries
[
(
i
+
count
)
].
offset
!==
'
undefined
'
)
{
count
++
;}
return
i
+
'
'
+
count
+
'
\n
'
;
return
i
+
'
'
+
count
+
'
\n
'
;
}
}
function
createTrailer
(
topDict
,
startxref
,
sha256Hex
,
size
,
prev
)
{
function
createTrailer
(
topDict
,
startxref
,
sha256Hex
,
size
,
prev
)
{
let
retVal
=
'
trailer <<
\n
'
;
var
retVal
=
'
trailer <<
\n
'
;
retVal
+=
'
/Size
'
+
size
+
'
\n
'
;
retVal
+=
'
/Size
'
+
(
size
)
+
'
\n
'
;
const
refRoot
=
topDict
.
getRaw
(
'
Root
'
);
var
refRoot
=
topDict
.
getRaw
(
'
Root
'
);
if
(
typeof
refRoot
!==
'
undefined
'
)
{
if
(
typeof
refRoot
!==
'
undefined
'
)
{
retVal
+=
'
/Root
'
+
refRoot
.
num
+
'
'
+
refRoot
.
gen
+
'
R
\n
'
;
retVal
+=
'
/Root
'
+
refRoot
.
num
+
'
'
+
refRoot
.
gen
+
'
R
\n
'
;
}
}
const
refInfo
=
topDict
.
getRaw
(
'
Info
'
);
var
refInfo
=
topDict
.
getRaw
(
'
Info
'
);
if
(
typeof
refInfo
!==
'
undefined
'
)
{
if
(
typeof
refInfo
!==
'
undefined
'
)
{
retVal
+=
'
/Info
'
+
refInfo
.
num
+
'
'
+
refInfo
.
gen
+
'
R
\n
'
;
retVal
+=
'
/Info
'
+
refInfo
.
num
+
'
'
+
refInfo
.
gen
+
'
R
\n
'
;
}
}
retVal
+=
'
/ID [<
'
+
sha256Hex
.
substring
(
0
,
32
)
+
'
><
'
+
sha256Hex
.
substring
(
32
,
64
)
+
'
>]
\n
'
;
retVal
+=
'
/ID [<
'
+
sha256Hex
.
substring
(
0
,
32
)
+
'
><
'
+
sha256Hex
.
substring
(
32
,
64
)
+
'
>]
\n
'
;
if
(
typeof
prev
!==
'
undefined
'
)
{
if
(
typeof
prev
!==
'
undefined
'
)
{
retVal
+=
'
/Prev
'
+
prev
+
'
\n
'
;
retVal
+=
'
/Prev
'
+
prev
+
'
\n
'
;
}
}
retVal
+=
'
>>
\n
'
;
retVal
+=
'
>>
\n
'
;
retVal
+=
'
startxref
\n
'
;
retVal
+=
'
startxref
\n
'
;
retVal
+=
startxref
+
'
\n
'
;
retVal
+=
startxref
+
'
\n
'
;
retVal
+=
'
%%EOF
\n
'
;
retVal
+=
'
%%EOF
\n
'
;
return
retVal
;
return
retVal
;
}
}
function
createXrefTableAppend
(
xrefEntries
)
{
function
createXrefTableAppend
(
xrefEntries
)
{
xrefEntries
=
sortOnKeys
(
xrefEntries
);
xrefEntries
=
sortOnKeys
(
xrefEntries
);
let
retVal
=
'
xref
\n
'
;
var
retVal
=
'
xref
\n
'
;
let
last
=
-
2
;
var
last
=
-
2
;
for
(
let
i
in
xrefEntries
)
{
for
(
var
i
in
xrefEntries
)
{
i
=
parseInt
(
i
);
i
=
parseInt
(
i
);
if
(
typeof
xrefEntries
[
i
].
offset
===
'
undefined
'
)
{
continue
;
}
if
(
typeof
xrefEntries
[
i
].
offset
===
'
undefined
'
)
{
continue
;
}
retVal
+=
calcFlow
(
i
,
last
,
xrefEntries
);
retVal
+=
calcFlow
(
i
,
last
,
xrefEntries
);
const
offset
=
xrefEntries
[
i
].
offset
;
var
offset
=
xrefEntries
[
i
].
offset
;
retVal
+=
pad10
(
offset
)
+
'
'
+
pad5
(
xrefEntries
[
i
].
gen
)
+
'
'
+
(
xrefEntries
[
i
].
free
?
'
f
'
:
'
n
'
)
+
'
\n
'
;
retVal
+=
pad10
(
offset
)
+
'
'
+
pad5
(
xrefEntries
[
i
].
gen
)
+
'
'
+
(
xrefEntries
[
i
].
free
?
'
f
'
:
'
n
'
)
+
'
\n
'
;
last
=
i
;
last
=
i
;
}
}
return
retVal
;
return
retVal
;
}
}
//http://stackoverflow.com/questions/10946880/sort-a-dictionary-or-whatever-key-value-data-structure-in-js-on-word-number-ke
//http://stackoverflow.com/questions/10946880/sort-a-dictionary-or-whatever-key-value-data-structure-in-js-on-word-number-ke
function
sortOnKeys
(
dict
)
{
function
sortOnKeys
(
dict
)
{
const
sorted
=
[];
var
sorted
=
[];
for
(
const
key
in
dict
)
{
for
(
var
key
in
dict
)
{
sorted
[
sorted
.
length
]
=
key
;
sorted
[
sorted
.
length
]
=
key
;
}
}
sorted
.
sort
();
sorted
.
sort
();
const
tempDict
=
{};
var
tempDict
=
{};
for
(
let
i
=
0
;
i
<
sorted
.
length
;
i
++
)
{
for
(
var
i
=
0
;
i
<
sorted
.
length
;
i
++
)
{
tempDict
[
sorted
[
i
]]
=
dict
[
sorted
[
i
]];
tempDict
[
sorted
[
i
]]
=
dict
[
sorted
[
i
]];
}
}
return
tempDict
;
return
tempDict
;
}
}
function
removeFromArray
(
array
,
from
,
to
)
{
function
removeFromArray
(
array
,
from
,
to
)
{
const
cutlen
=
to
-
from
;
var
cutlen
=
to
-
from
;
const
buf
=
new
Uint8Array
(
array
.
length
-
cutlen
);
var
buf
=
new
Uint8Array
(
array
.
length
-
cutlen
);
for
(
let
i
=
0
;
i
<
from
;
i
++
)
{
for
(
var
i
=
0
;
i
<
from
;
i
++
)
{
buf
[
i
]
=
array
[
i
];
buf
[
i
]
=
array
[
i
];
}
}
for
(
let
i
=
to
,
len
=
array
.
length
;
i
<
len
;
i
++
)
{
for
(
var
i
=
to
,
len
=
array
.
length
;
i
<
len
;
i
++
)
{
buf
[
i
-
cutlen
]
=
array
[
i
];
buf
[
i
-
cutlen
]
=
array
[
i
];
}
}
return
buf
;
return
buf
;
}
}
function
findXrefBlocks
(
xrefBlocks
)
{
function
findXrefBlocks
(
xrefBlocks
)
{
const
num
=
xrefBlocks
.
length
/
2
;
var
num
=
xrefBlocks
.
length
/
2
;
const
retVal
=
[];
var
retVal
=
[];
for
(
let
i
=
0
;
i
<
num
;
i
++
)
{
for
(
var
i
=
0
;
i
<
num
;
i
++
)
{
retVal
.
push
({
start
:
xrefBlocks
[
i
],
retVal
.
push
({
start
:
xrefBlocks
[
i
],
end
:
xrefBlocks
[
i
+
num
]});
end
:
xrefBlocks
[
i
+
num
]});
}
}
return
retVal
;
return
retVal
;
}
}
function
convertUint8ArrayToBinaryString
(
u8Array
)
{
function
convertUint8ArrayToBinaryString
(
u8Array
)
{
let
i
,
len
=
u8Array
.
length
,
b
S
tr
=
""
;
var
i
,
len
=
u8Array
.
length
,
b
_s
tr
=
""
;
for
(
i
=
0
;
i
<
len
;
i
++
)
{
for
(
i
=
0
;
i
<
len
;
i
++
)
{
b
S
tr
+=
String
.
fromCharCode
(
u8Array
[
i
]);
b
_s
tr
+=
String
.
fromCharCode
(
u8Array
[
i
]);
}
}
return
b
S
tr
;
return
b
_s
tr
;
}
}
function
arrayObjectIndexOf
(
array
,
start
,
end
,
orig
)
{
function
arrayObjectIndexOf
(
array
,
start
,
end
,
orig
)
{
for
(
let
i
=
0
,
len
=
array
.
length
;
i
<
len
;
i
++
)
{
for
(
var
i
=
0
,
len
=
array
.
length
;
i
<
len
;
i
++
)
{
if
((
array
[
i
].
start
===
start
)
&&
(
array
[
i
].
end
===
end
)
&&
(
array
[
i
].
orig
===
orig
))
{
if
((
array
[
i
].
start
===
start
)
&&
(
array
[
i
].
end
===
end
)
&&
(
array
[
i
].
orig
===
orig
))
{
return
i
;
return
i
;
}
}
}
}
return
-
1
;
return
-
1
;
}
}
function
pad10
(
num
)
{
function
pad10
(
num
)
{
const
s
=
"
000000000
"
+
num
;
var
s
=
"
000000000
"
+
num
;
return
s
.
substr
(
s
.
length
-
10
);
return
s
.
substr
(
s
.
length
-
10
);
}
}
function
pad5
(
num
)
{
function
pad5
(
num
)
{
const
s
=
"
0000
"
+
num
;
var
s
=
"
0000
"
+
num
;
return
s
.
substr
(
s
.
length
-
5
);
return
s
.
substr
(
s
.
length
-
5
);
}
}
function
pad2
(
num
)
{
function
pad2
(
num
)
{
const
s
=
"
0
"
+
num
;
var
s
=
"
0
"
+
num
;
return
s
.
substr
(
s
.
length
-
2
);
return
s
.
substr
(
s
.
length
-
2
);
}
}
function
findRootEntry
(
xref
)
{
function
findRootEntry
(
xref
)
{
const
rootNr
=
xref
.
root
.
objId
.
substring
(
0
,
xref
.
root
.
objId
.
length
-
1
);
var
rootNr
=
xref
.
root
.
objId
.
substring
(
0
,
xref
.
root
.
objId
.
length
-
1
);
return
xref
.
entries
[
rootNr
];
return
xref
.
entries
[
rootNr
];
}
}
function
findSuccessorEntry
(
xrefEntries
,
current
)
{
function
findSuccessorEntry
(
xrefEntries
,
current
)
{
//find it first
//find it first
const
currentOffset
=
current
.
offset
;
var
currentOffset
=
current
.
offset
;
let
currentMin
=
Number
.
MAX_SAFE_INTEGER
;
var
currentMin
=
Number
.
MAX_SAFE_INTEGER
;
let
currentMinIndex
=
-
1
;
var
currentMinIndex
=
-
1
;
for
(
const
i
in
xrefEntries
)
{
for
(
var
i
in
xrefEntries
)
{
if
(
xrefEntries
[
i
].
offset
>
currentOffset
)
{
if
(
xrefEntries
[
i
].
offset
>
currentOffset
)
{
if
(
xrefEntries
[
i
].
offset
<
currentMin
)
{
if
(
xrefEntries
[
i
].
offset
<
currentMin
)
{
currentMin
=
xrefEntries
[
i
].
offset
;
currentMin
=
xrefEntries
[
i
].
offset
;
currentMinIndex
=
i
;
currentMinIndex
=
i
;
}
}
}
}
}
}
if
(
currentMinIndex
===
-
1
)
{
if
(
currentMinIndex
===
-
1
)
{
return
current
;
return
current
;
}
}
return
xrefEntries
[
currentMinIndex
];
return
xrefEntries
[
currentMinIndex
];
}
}
function
updateArray
(
array
,
pos
,
str
)
{
function
updateArray
(
array
,
pos
,
str
)
{
const
upd
=
stringToUint8Array
(
str
);
var
upd
=
stringToUint8Array
(
str
);
for
(
let
i
=
0
,
len
=
upd
.
length
;
i
<
len
;
i
++
)
{
for
(
var
i
=
0
,
len
=
upd
.
length
;
i
<
len
;
i
++
)
{
array
[
i
+
pos
]
=
upd
[
i
];
array
[
i
+
pos
]
=
upd
[
i
];
}
}
return
array
;
return
array
;
}
}
function
copyToEnd
(
array
,
from
,
to
)
{
function
copyToEnd
(
array
,
from
,
to
)
{
const
buf
=
new
Uint8Array
(
array
.
length
+
(
to
-
from
));
var
buf
=
new
Uint8Array
(
array
.
length
+
(
to
-
from
));
for
(
let
i
=
0
,
len
=
array
.
length
;
i
<
len
;
i
++
)
{
for
(
var
i
=
0
,
len
=
array
.
length
;
i
<
len
;
i
++
)
{
buf
[
i
]
=
array
[
i
];
buf
[
i
]
=
array
[
i
];
}
}
for
(
let
i
=
0
,
len
=
to
-
from
;
i
<
len
;
i
++
)
{
for
(
var
i
=
0
,
len
=
(
to
-
from
)
;
i
<
len
;
i
++
)
{
buf
[
array
.
length
+
i
]
=
array
[
from
+
i
];
buf
[
array
.
length
+
i
]
=
array
[
from
+
i
];
}
}
return
buf
;
return
buf
;
}
}
function
insertIntoArray
(
array
,
pos
,
str
)
{
function
insertIntoArray
(
array
,
pos
,
str
)
{
const
ins
=
stringToUint8Array
(
str
);
var
ins
=
stringToUint8Array
(
str
);
const
buf
=
new
Uint8Array
(
array
.
length
+
ins
.
length
);
var
buf
=
new
Uint8Array
(
array
.
length
+
ins
.
length
);
for
(
let
i
=
0
;
i
<
pos
;
i
++
)
{
for
(
var
i
=
0
;
i
<
pos
;
i
++
)
{
buf
[
i
]
=
array
[
i
];
buf
[
i
]
=
array
[
i
];
}
}
for
(
let
i
=
0
;
i
<
ins
.
length
;
i
++
)
{
for
(
var
i
=
0
;
i
<
ins
.
length
;
i
++
)
{
buf
[
pos
+
i
]
=
ins
[
i
];
buf
[
pos
+
i
]
=
ins
[
i
];
}
}
for
(
let
i
=
pos
;
i
<
array
.
length
;
i
++
)
{
for
(
var
i
=
pos
;
i
<
array
.
length
;
i
++
)
{
buf
[
ins
.
length
+
i
]
=
array
[
i
];
buf
[
ins
.
length
+
i
]
=
array
[
i
];
}
}
return
buf
;
return
buf
;
}
}
function
stringToUint8Array
(
str
)
{
function
stringToUint8Array
(
str
)
{
const
buf
=
new
Uint8Array
(
str
.
length
);
var
buf
=
new
Uint8Array
(
str
.
length
);
for
(
let
i
=
0
,
strLen
=
str
.
length
;
i
<
strLen
;
i
++
)
{
for
(
var
i
=
0
,
strLen
=
str
.
length
;
i
<
strLen
;
i
++
)
{
buf
[
i
]
=
str
.
charCodeAt
(
i
);
buf
[
i
]
=
str
.
charCodeAt
(
i
);
}
}
return
buf
;
return
buf
;
}
}
function
uint8ArrayToString
(
buf
,
from
,
to
)
{
function
uint8ArrayToString
(
buf
,
from
,
to
)
{
if
(
typeof
from
!==
'
undefined
'
&&
typeof
to
!==
'
undefined
'
)
{
if
(
typeof
from
!==
'
undefined
'
&&
typeof
to
!==
'
undefined
'
)
{
let
s
=
''
;
var
s
=
''
;
for
(
let
i
=
from
;
i
<
to
;
i
++
)
{
for
(
var
i
=
from
;
i
<
to
;
i
++
)
{
s
=
s
+
String
.
fromCharCode
(
buf
[
i
]);
s
=
s
+
String
.
fromCharCode
(
buf
[
i
]);
}
return
s
;
}
}
return
s
;
return
String
.
fromCharCode
.
apply
(
null
,
buf
);
}
return
String
.
fromCharCode
.
apply
(
null
,
buf
);
}
}
function
findFreeXrefNr
(
xrefEntries
,
used
)
{
used
=
typeof
used
!==
'
undefined
'
?
used
:
[];
let
inc
=
used
.
length
;
for
(
let
i
=
1
;
i
<
xrefEntries
.
length
;
i
++
)
{
const
index
=
used
.
indexOf
(
i
);
function
findFreeXrefNr
(
xrefEntries
,
used
)
{
const
entry
=
xrefEntries
[
""
+
i
];
used
=
typeof
used
!==
'
undefined
'
?
used
:
[];
if
(
index
===
-
1
&&
(
typeof
entry
===
'
undefined
'
||
entry
.
free
))
{
var
inc
=
used
.
length
;
return
i
;
}
for
(
var
i
=
1
;
i
<
xrefEntries
.
length
;
i
++
)
{
if
(
index
!==
-
1
)
{
inc
--
;
var
index
=
used
.
indexOf
(
i
);
var
entry
=
xrefEntries
[
""
+
i
];
if
(
index
===
-
1
&&
(
typeof
entry
===
'
undefined
'
||
entry
.
free
))
{
return
i
;
}
if
(
index
!==
-
1
)
{
inc
--
;
}
}
}
}
return
xrefEntries
.
length
+
inc
;
return
xrefEntries
.
length
+
inc
;
}
}
function
find
(
uint8
,
needle
,
start
,
limit
)
{
function
find
(
uint8
,
needle
,
start
,
limit
)
{
start
=
typeof
start
!==
'
undefined
'
?
start
:
0
;
start
=
typeof
start
!==
'
undefined
'
?
start
:
0
;
limit
=
typeof
limit
!==
'
undefined
'
?
limit
:
Number
.
MAX_SAFE_INTEGER
;
limit
=
typeof
limit
!==
'
undefined
'
?
limit
:
Number
.
MAX_SAFE_INTEGER
;
const
search
=
stringToUint8Array
(
needle
);
var
search
=
stringToUint8Array
(
needle
);
let
match
=
0
;
var
match
=
0
;
for
(
let
i
=
start
;
i
<
uint8
.
length
&&
i
<
limit
;
i
++
)
{
for
(
var
i
=
start
;
i
<
uint8
.
length
&&
i
<
limit
;
i
++
)
{
if
(
uint8
[
i
]
===
search
[
match
])
{
if
(
uint8
[
i
]
===
search
[
match
])
{
match
++
;
match
++
;
}
else
{
}
else
{
match
=
0
;
match
=
0
;
if
(
uint8
[
i
]
===
search
[
match
])
{
if
(
uint8
[
i
]
===
search
[
match
])
{
match
++
;
match
++
;
}
}
}
}
if
(
match
===
search
.
length
)
{
if
(
match
===
search
.
length
)
{
return
(
i
+
1
)
-
match
;
return
(
i
+
1
)
-
match
;
}
}
}
}
return
-
1
;
return
-
1
;
}
}
function
findBackwards
(
uint8
,
needle
,
start
,
limit
)
{
function
findBackwards
(
uint8
,
needle
,
start
,
limit
)
{
start
=
typeof
start
!==
'
undefined
'
?
start
:
uint8
.
length
;
start
=
typeof
start
!==
'
undefined
'
?
start
:
uint8
.
length
;
limit
=
typeof
limit
!==
'
undefined
'
?
limit
:
Number
.
MAX_SAFE_INTEGER
;
limit
=
typeof
limit
!==
'
undefined
'
?
limit
:
Number
.
MAX_SAFE_INTEGER
;
const
search
=
stringToUint8Array
(
needle
);
var
search
=
stringToUint8Array
(
needle
);
let
match
=
search
.
length
-
1
;
var
match
=
search
.
length
-
1
;
for
(
let
i
=
start
;
i
>=
0
&&
i
<
limit
;
i
--
)
{
for
(
var
i
=
start
;
i
>=
0
&&
i
<
limit
;
i
--
)
{
if
(
uint8
[
i
]
===
search
[
match
])
{
if
(
uint8
[
i
]
===
search
[
match
])
{
match
--
;
match
--
;
}
else
{
}
else
{
match
=
search
.
length
-
1
;
match
=
search
.
length
-
1
;
if
(
uint8
[
i
]
===
search
[
match
])
{
if
(
uint8
[
i
]
===
search
[
match
])
{
match
--
;
match
--
;
}
}
}
}
if
(
match
===
0
)
{
if
(
match
===
0
)
{
return
i
-
1
;
return
i
-
1
;
}
}
}
}
return
-
1
;
return
-
1
;
}
}
function
strHex
(
s
)
{
function
strHex
(
s
)
{
let
a
=
""
;
var
a
=
""
;
for
(
let
i
=
0
;
i
<
s
.
length
;
i
++
)
{
for
(
var
i
=
0
;
i
<
s
.
length
;
i
++
)
{
a
=
a
+
pad2
(
s
.
charCodeAt
(
i
).
toString
(
16
));
a
=
a
+
pad2
(
s
.
charCodeAt
(
i
).
toString
(
16
));
}
}
return
a
;
return
a
;
}
}
async
function
sha256
(
array
)
{
async
function
sha256
(
array
)
{
const
cryptoLib
=
getCrypto
();
const
digestTmpBuf
=
await
cryptoLib
.
digest
({
name
:
"
SHA-256
"
},
array
);
const
cryptoLib
=
getCrypto
();
const
digestTmpArray
=
new
Uint8Array
(
digestTmpBuf
);
const
digestTmpBuf
=
await
cryptoLib
.
digest
({
name
:
"
SHA-256
"
},
array
);
const
digestTmpStr
=
uint8ArrayToString
(
digestTmpArray
);
const
digestTmpArray
=
new
Uint8Array
(
digestTmpBuf
);
const
sha256Hex
=
strHex
(
digestTmpStr
);
const
digestTmpStr
=
uint8ArrayToString
(
digestTmpArray
);
return
sha256Hex
;
const
sha256Hex
=
strHex
(
digestTmpStr
);
return
sha256Hex
;
}
}
function
isSigInRoot
(
pdf
)
{
function
isSigInRoot
(
pdf
)
{
if
(
typeof
pdf
.
acroForm
===
'
undefined
'
)
{
if
(
typeof
pdf
.
acroForm
===
'
undefined
'
)
{
return
false
;
return
false
;
}
}
return
pdf
.
acroForm
.
get
(
'
SigFlags
'
)
===
3
;
return
pdf
.
acroForm
.
get
(
'
SigFlags
'
)
===
3
;
}
}
function
updateXrefOffset
(
xref
,
offset
,
offsetDelta
)
{
function
updateXrefOffset
(
xref
,
offset
,
offsetDelta
)
{
for
(
const
i
in
xref
.
entries
)
{
for
(
var
i
in
xref
.
entries
)
{
if
(
xref
.
entries
[
i
].
offset
>=
offset
)
{
if
(
xref
.
entries
[
i
].
offset
>=
offset
)
{
xref
.
entries
[
i
].
offset
+=
offsetDelta
;
xref
.
entries
[
i
].
offset
+=
offsetDelta
;
}
}
}
}
for
(
var
i
in
xref
.
xrefBlocks
)
{
for
(
const
i
in
xref
.
xrefBlocks
)
{
if
(
xref
.
xrefBlocks
[
i
]
>=
offset
)
{
if
(
xref
.
xrefBlocks
[
i
]
>
=
offset
)
{
xref
.
xrefBlocks
[
i
]
+
=
offset
Delta
;
xref
.
xrefBlocks
[
i
]
+=
offsetDelta
;
}
}
}
}
}
}
function
updateXrefBlocks
(
xrefBlocks
,
offset
,
offsetDelta
)
{
function
updateXrefBlocks
(
xrefBlocks
,
offset
,
offsetDelta
)
{
for
(
const
i
in
xrefBlocks
)
{
for
(
var
i
in
xrefBlocks
)
{
if
(
xrefBlocks
[
i
].
start
>=
offset
)
{
if
(
xrefBlocks
[
i
].
start
>=
offset
)
{
xrefBlocks
[
i
].
start
+=
offsetDelta
;
xrefBlocks
[
i
].
start
+=
offsetDelta
;
}
}
if
(
xrefBlocks
[
i
].
end
>=
offset
)
{
if
(
xrefBlocks
[
i
].
end
>=
offset
)
{
xrefBlocks
[
i
].
end
+=
offsetDelta
;
xrefBlocks
[
i
].
end
+=
offsetDelta
;
}
}
}
}
}
}
function
updateOffset
(
pos
,
offset
,
offsetDelta
)
{
function
updateOffset
(
pos
,
offset
,
offsetDelta
)
{
if
(
pos
>=
offset
)
{
if
(
pos
>=
offset
)
{
return
pos
+
offsetDelta
;
return
pos
+
offsetDelta
;
}
}
return
pos
;
return
pos
;
}
}
function
round256
(
x
)
{
function
round256
(
x
)
{
return
(
Math
.
ceil
(
x
/
256
)
*
256
)
-
1
;
return
(
Math
.
ceil
(
x
/
256
)
*
256
)
-
1
;
}
}
/**
/**
...
@@ -382,359 +387,349 @@ function round256(x) {
...
@@ -382,359 +387,349 @@ function round256(x) {
* mm shall be the absolute value of the offset from UT in minutes (00–59)
* mm shall be the absolute value of the offset from UT in minutes (00–59)
*/
*/
function
now
(
date
)
{
function
now
(
date
)
{
//date = typeof date !== 'undefined' ? date : new Date();
//date = typeof date !== 'undefined' ? date : new Date();
const
yyyy
=
date
.
getFullYear
().
toString
();
var
yyyy
=
date
.
getFullYear
().
toString
();
const
MM
=
pad2
(
date
.
getMonth
()
+
1
);
var
MM
=
pad2
(
date
.
getMonth
()
+
1
);
const
dd
=
pad2
(
date
.
getDate
());
var
dd
=
pad2
(
date
.
getDate
());
const
hh
=
pad2
(
date
.
getHours
());
var
hh
=
pad2
(
date
.
getHours
());
const
mm
=
pad2
(
date
.
getMinutes
());
var
mm
=
pad2
(
date
.
getMinutes
());
const
ss
=
pad2
(
date
.
getSeconds
());
var
ss
=
pad2
(
date
.
getSeconds
());
return
yyyy
+
MM
+
dd
+
hh
+
mm
+
ss
+
createOffset
(
date
);
return
yyyy
+
MM
+
dd
+
hh
+
mm
+
ss
+
createOffset
(
date
);
}
}
function
createOffset
(
date
)
{
function
createOffset
(
date
)
{
const
sign
=
date
.
getTimezoneOffset
()
>
0
?
"
-
"
:
"
+
"
;
var
sign
=
(
date
.
getTimezoneOffset
()
>
0
)
?
"
-
"
:
"
+
"
;
const
offset
=
Math
.
abs
(
date
.
getTimezoneOffset
());
var
offset
=
Math
.
abs
(
date
.
getTimezoneOffset
());
const
hours
=
pad2
(
Math
.
floor
(
offset
/
60
));
var
hours
=
pad2
(
Math
.
floor
(
offset
/
60
));
const
minutes
=
pad2
(
offset
%
60
);
var
minutes
=
pad2
(
offset
%
60
);
return
sign
+
hours
+
"
'
"
+
minutes
;
return
sign
+
hours
+
"
'
"
+
minutes
;
}
}
async
function
newSig
(
pdf
,
root
,
rootSuccessor
,
date
,
signingCert
,
certificateChain
,
privateKey
)
{
async
function
newSig
(
pdf
,
root
,
rootSuccessor
,
date
,
signingCert
,
certificateChain
,
privateKey
)
{
// {annotEntry} is the ref to the annot widget. If we enlarge the array, make sure all the offsets
// {annotEntry} is the ref to the annot widget. If we enlarge the array, make sure all the offsets
// after the modification will be updated -> xref table and startxref
// after the modification will be updated -> xref table and startxref
const
annotEntry
=
findFreeXrefNr
(
pdf
.
xref
.
entries
);
var
annotEntry
=
findFreeXrefNr
(
pdf
.
xref
.
entries
);
// we'll store all the modifications we make, as we need to adjust the offset in the PDF
// we'll store all the modifications we make, as we need to adjust the offset in the PDF
const
offsetForm
=
find
(
pdf
.
stream
.
bytes
,
'
<<
'
,
root
.
offset
,
rootSuccessor
.
offset
)
+
2
;
var
offsetForm
=
find
(
pdf
.
stream
.
bytes
,
'
<<
'
,
root
.
offset
,
rootSuccessor
.
offset
)
+
2
;
//first we need to find the root element and add the following:
//first we need to find the root element and add the following:
//
//
// /AcroForm<</Fields[{annotEntry} 0 R] /SigFlags 3>>
// /AcroForm<</Fields[{annotEntry} 0 R] /SigFlags 3>>
//
//
const
appendAcroForm
=
'
/AcroForm<</Fields[
'
+
annotEntry
+
'
0 R] /SigFlags 3>>
'
;
var
appendAcroForm
=
'
/AcroForm<</Fields[
'
+
annotEntry
+
'
0 R] /SigFlags 3>>
'
;
//before we insert the acroform, we find the right place for annotentry
//before we insert the acroform, we find the right place for annotentry
//we need to add Annots [x y R] to the /Type /Page section. We can do that by searching /Contents[
//we need to add Annots [x y R] to the /Type /Page section. We can do that by searching /Contents[
const
pages
=
pdf
.
catalog
.
catDict
.
get
(
'
Pages
'
);
var
pages
=
pdf
.
catalog
.
catDict
.
get
(
'
Pages
'
);
//get first page, we have hidden sig, so don't bother
//get first page, we have hidden sig, so don't bother
const
ref
=
pages
.
get
(
'
Kids
'
)[
0
];
var
ref
=
pages
.
get
(
'
Kids
'
)[
0
];
const
xref
=
pdf
.
xref
.
fetch
(
ref
);
var
xref
=
pdf
.
xref
.
fetch
(
ref
);
const
offsetContentEnd
=
xref
.
get
(
'
#Contents_offset
'
);
var
offsetContentEnd
=
xref
.
get
(
'
#Contents_offset
'
);
//we now search backwards, this is safe as we don't expect user content here
//we now search backwards, this is safe as we don't expect user content here
let
offsetContent
=
findBackwards
(
pdf
.
stream
.
bytes
,
'
/Contents
'
,
offsetContentEnd
);
var
offsetContent
=
findBackwards
(
pdf
.
stream
.
bytes
,
'
/Contents
'
,
offsetContentEnd
);
const
appendAnnots
=
'
/Annots[
'
+
annotEntry
+
'
0 R]
\n
'
;
var
appendAnnots
=
'
/Annots[
'
+
annotEntry
+
'
0 R]
\n
'
;
//now insert string into stream
//now insert string into stream
let
array
=
insertIntoArray
(
pdf
.
stream
.
bytes
,
offsetForm
,
appendAcroForm
);
var
array
=
insertIntoArray
(
pdf
.
stream
.
bytes
,
offsetForm
,
appendAcroForm
);
//recalculate the offsets in the xref table, only update those that are affected
//recalculate the offsets in the xref table, only update those that are affected
updateXrefOffset
(
pdf
.
xref
,
offsetForm
,
appendAcroForm
.
length
);
updateXrefOffset
(
pdf
.
xref
,
offsetForm
,
appendAcroForm
.
length
);
offsetContent
=
updateOffset
(
offsetContent
,
offsetForm
,
appendAcroForm
.
length
);
offsetContent
=
updateOffset
(
offsetContent
,
offsetForm
,
appendAcroForm
.
length
);
array
=
insertIntoArray
(
array
,
offsetContent
,
appendAnnots
);
var
array
=
insertIntoArray
(
array
,
offsetContent
,
appendAnnots
);
updateXrefOffset
(
pdf
.
xref
,
offsetContent
,
appendAnnots
.
length
);
updateXrefOffset
(
pdf
.
xref
,
offsetContent
,
appendAnnots
.
length
);
offsetContent
=
-
1
;
//not needed anymore, don't update when offset changes
offsetContent
=
-
1
;
//not needed anymore, don't update when offset changes
//Then add to the next free object (annotEntry)
//Then add to the next free object (annotEntry)
//add right before the xref table or stream
//add right before the xref table or stream
//if its a table, place element before the xref table
//if its a table, place element before the xref table
//
//
// sigEntry is the ref to the signature content. Next we need the signature object
// sigEntry is the ref to the signature content. Next we need the signature object
const
sigEntry
=
findFreeXrefNr
(
pdf
.
xref
.
entries
,
[
annotEntry
]);
var
sigEntry
=
findFreeXrefNr
(
pdf
.
xref
.
entries
,
[
annotEntry
]);
//
//
// {annotEntry} 0 obj
// {annotEntry} 0 obj
// <</F 132/Type/Annot/Subtype/Widget/Rect[0 0 0 0]/FT/Sig/DR<<>>/T(signature)/V Y 0 R>>
// <</F 132/Type/Annot/Subtype/Widget/Rect[0 0 0 0]/FT/Sig/DR<<>>/T(signature)/V Y 0 R>>
// endobj
// endobj
//
//
const
append
=
annotEntry
+
'
0 obj
\n
<</F 132/Type/Annot/Subtype/Widget/Rect[0 0 0 0]/FT/Sig/DR<<>>/T(signature
'
+
annotEntry
+
'
)/V
'
+
sigEntry
+
'
0 R>>
\n
endobj
\n\n
'
;
var
append
=
annotEntry
+
'
0 obj
\n
<</F 132/Type/Annot/Subtype/Widget/Rect[0 0 0 0]/FT/Sig/DR<<>>/T(signature
'
+
annotEntry
+
'
)/V
'
+
sigEntry
+
'
0 R>>
\n
endobj
\n\n
'
;
// we want the offset just before the last xref table or entry
// we want the offset just before the last xref table or entry
const
blocks
=
findXrefBlocks
(
pdf
.
xref
.
xrefBlocks
);
var
blocks
=
findXrefBlocks
(
pdf
.
xref
.
xrefBlocks
);
let
offsetAnnot
=
blocks
[
0
].
start
;
var
offsetAnnot
=
blocks
[
0
].
start
;
array
=
insertIntoArray
(
array
,
offsetAnnot
,
append
);
array
=
insertIntoArray
(
array
,
offsetAnnot
,
append
);
//no updateXrefOffset, as the next entry will be following
//no updateXrefOffset, as the next entry will be following
//
//
// {sigEntry} 0 obj
// {sigEntry} 0 obj
// <</Contents <0481801e6d931d561563fb254e27c846e08325570847ed63d6f9e35 ... b2c8788a5>
// <</Contents <0481801e6d931d561563fb254e27c846e08325570847ed63d6f9e35 ... b2c8788a5>
// /Type/Sig/SubFilter/adbe.pkcs7.detached/Location(Ghent)/M(D:20120928104114+02'00')
// /Type/Sig/SubFilter/adbe.pkcs7.detached/Location(Ghent)/M(D:20120928104114+02'00')
// /ByteRange [A B C D]/Filter/Adobe.PPKLite/Reason(Test)/ContactInfo()>>
// /ByteRange [A B C D]/Filter/Adobe.PPKLite/Reason(Test)/ContactInfo()>>
// endobj
// endobj
//
//
//the next entry goes below the above
//the next entry goes below the above
let
offsetSig
=
offsetAnnot
+
append
.
length
;
var
offsetSig
=
offsetAnnot
+
append
.
length
;
// Both {annotEntry} and {sigEntry} objects need to be added to the last xref table. The byte range needs
// Both {annotEntry} and {sigEntry} objects need to be added to the last xref table. The byte range needs
// to be adjusted. Since the signature will always be in a gap, use first an empty sig
// to be adjusted. Since the signature will always be in a gap, use first an empty sig
// to check the size, add ~25% size, then calculate the signature and place in the empty
// to check the size, add ~25% size, then calculate the signature and place in the empty
// space.
// space.
const
start
=
sigEntry
+
'
0 obj
\n
<</Contents <
'
;
var
start
=
sigEntry
+
'
0 obj
\n
<</Contents <
'
;
const
dummy
=
await
signPki
(
signingCert
,
certificateChain
,
privateKey
,
stringToUint8Array
(
'
A
'
),
date
);
var
dummy
=
await
sign_pki
(
signingCert
,
certificateChain
,
privateKey
,
stringToUint8Array
(
'
A
'
),
date
);
//TODO: Adobe thinks its important to have the right size, no idea why this is the case
//TODO: Adobe thinks its important to have the right size, no idea why this is the case
const
crypto
=
new
Array
(
round256
(
dummy
.
length
*
2
)).
join
(
'
0
'
);
var
crypto
=
new
Array
(
round256
(
dummy
.
length
*
2
)).
join
(
'
0
'
);
const
middle
=
'
>
\n
/Type/Sig/SubFilter/adbe.pkcs7.detached/Location()/M(D:
'
+
now
(
date
)
+
'
\'
)
\n
/ByteRange
'
;
var
middle
=
'
>
\n
/Type/Sig/SubFilter/adbe.pkcs7.detached/Location()/M(D:
'
+
now
(
date
)
+
'
\'
)
\n
/ByteRange
'
;
let
byteRange
=
'
[0000000000 0000000000 0000000000 0000000000]
'
;
var
byteRange
=
'
[0000000000 0000000000 0000000000 0000000000]
'
;
const
end
=
'
/Filter/Adobe.PPKLite/Reason()/ContactInfo()>>
\n
endobj
\n\n
'
;
var
end
=
'
/Filter/Adobe.PPKLite/Reason()/ContactInfo()>>
\n
endobj
\n\n
'
;
//all together
//all together
const
append2
=
start
+
crypto
+
middle
+
byteRange
+
end
;
var
append2
=
start
+
crypto
+
middle
+
byteRange
+
end
;
const
offsetByteRange
=
start
.
length
+
crypto
.
length
+
middle
.
length
;
var
offsetByteRange
=
start
.
length
+
crypto
.
length
+
middle
.
length
;
array
=
insertIntoArray
(
array
,
offsetSig
,
append2
);
array
=
insertIntoArray
(
array
,
offsetSig
,
append2
);
updateXrefOffset
(
pdf
.
xref
,
offsetAnnot
,
append2
.
length
+
append
.
length
);
updateXrefOffset
(
pdf
.
xref
,
offsetAnnot
,
append2
.
length
+
append
.
length
);
//find the xref tables, remove them and also the EOF, as we'll write a new table
//find the xref tables, remove them and also the EOF, as we'll write a new table
const
xrefBlocks
=
findXrefBlocks
(
pdf
.
xref
.
xrefBlocks
);
var
xrefBlocks
=
findXrefBlocks
(
pdf
.
xref
.
xrefBlocks
);
for
(
const
i
in
xrefBlocks
)
{
for
(
var
i
in
xrefBlocks
)
{
const
oldSize
=
array
.
length
;
var
oldSize
=
array
.
length
;
array
=
removeFromArray
(
array
,
xrefBlocks
[
i
].
start
,
xrefBlocks
[
i
].
end
);
array
=
removeFromArray
(
array
,
xrefBlocks
[
i
].
start
,
xrefBlocks
[
i
].
end
);
const
length
=
array
.
length
-
oldSize
;
var
length
=
array
.
length
-
oldSize
;
updateXrefOffset
(
pdf
.
xref
,
xrefBlocks
[
i
].
start
,
length
);
updateXrefOffset
(
pdf
.
xref
,
xrefBlocks
[
i
].
start
,
length
);
//check for %%EOF and remove it as well
//check for %%EOF and remove it as well
const
offsetEOF
=
find
(
array
,
'
%%EOF
'
,
xrefBlocks
[
i
].
start
,
xrefBlocks
[
i
].
start
+
20
);
var
offsetEOF
=
find
(
array
,
'
%%EOF
'
,
xrefBlocks
[
i
].
start
,
xrefBlocks
[
i
].
start
+
20
);
if
(
offsetEOF
>
0
)
{
if
(
offsetEOF
>
0
)
{
const
lengthEOF
=
'
%%EOF
'
.
length
;
var
lengthEOF
=
'
%%EOF
'
.
length
;
array
=
removeFromArray
(
array
,
offsetEOF
,
offsetEOF
+
lengthEOF
);
array
=
removeFromArray
(
array
,
offsetEOF
,
offsetEOF
+
lengthEOF
);
updateXrefOffset
(
pdf
.
xref
,
offsetEOF
,
-
lengthEOF
);
updateXrefOffset
(
pdf
.
xref
,
offsetEOF
,
-
lengthEOF
);
updateXrefBlocks
(
xrefBlocks
,
offsetEOF
,
-
lengthEOF
);
updateXrefBlocks
(
xrefBlocks
,
offsetEOF
,
-
lengthEOF
);
offsetAnnot
=
updateOffset
(
offsetAnnot
,
offsetEOF
,
-
lengthEOF
);
offsetAnnot
=
updateOffset
(
offsetAnnot
,
offsetEOF
,
-
lengthEOF
);
offsetSig
=
updateOffset
(
offsetSig
,
offsetEOF
,
-
lengthEOF
);
offsetSig
=
updateOffset
(
offsetSig
,
offsetEOF
,
-
lengthEOF
);
}
}
updateXrefBlocks
(
xrefBlocks
,
xrefBlocks
[
i
].
start
,
length
);
updateXrefBlocks
(
xrefBlocks
,
xrefBlocks
[
i
].
start
,
length
);
offsetAnnot
=
updateOffset
(
offsetAnnot
,
xrefBlocks
[
i
].
start
,
length
);
offsetAnnot
=
updateOffset
(
offsetAnnot
,
xrefBlocks
[
i
].
start
,
length
);
offsetSig
=
updateOffset
(
offsetSig
,
xrefBlocks
[
i
].
start
,
length
);
offsetSig
=
updateOffset
(
offsetSig
,
xrefBlocks
[
i
].
start
,
length
);
}
}
const
sha256Hex
=
await
sha256
(
array
);
var
sha256Hex
=
await
sha256
(
array
);
//add the new entries to the xref
//add the new entries to the xref
pdf
.
xref
.
entries
[
annotEntry
]
=
{
offset
:
offsetAnnot
,
pdf
.
xref
.
entries
[
annotEntry
]
=
{
offset
:
offsetAnnot
,
gen
:
0
,
free
:
false
};
gen
:
0
,
pdf
.
xref
.
entries
[
sigEntry
]
=
{
offset
:
offsetSig
,
gen
:
0
,
free
:
false
};
free
:
false
};
pdf
.
xref
.
entries
[
sigEntry
]
=
{
offset
:
offsetSig
,
var
xrefTable
=
createXrefTable
(
pdf
.
xref
.
entries
);
gen
:
0
,
//also empty entries count as in the PDF spec, page 720 (example)
free
:
false
};
xrefTable
+=
createTrailer
(
pdf
.
xref
.
topDict
,
array
.
length
,
sha256Hex
,
pdf
.
xref
.
entries
.
length
);
array
=
insertIntoArray
(
array
,
array
.
length
,
xrefTable
);
let
xrefTable
=
createXrefTable
(
pdf
.
xref
.
entries
);
//also empty entries count as in the PDF spec, page 720 (example)
//since we consolidate, no prev! [adjust /Prev -> rawparsing + offset]
xrefTable
+=
createTrailer
(
pdf
.
xref
.
topDict
,
array
.
length
,
sha256Hex
,
pdf
.
xref
.
entries
.
length
);
var
from1
=
0
;
array
=
insertIntoArray
(
array
,
array
.
length
,
xrefTable
);
var
to1
=
offsetSig
+
start
.
length
;
var
from2
=
to1
+
crypto
.
length
;
//since we consolidate, no prev! [adjust /Prev -> rawparsing + offset]
var
to2
=
(
array
.
length
-
from2
)
-
1
;
const
from1
=
0
;
var
byteRange
=
'
[
'
+
pad10
(
from1
)
+
'
'
+
pad10
(
to1
-
1
)
+
'
'
+
pad10
(
from2
+
1
)
+
'
'
+
pad10
(
to2
)
+
'
]
'
;
const
to1
=
offsetSig
+
start
.
length
;
array
=
updateArray
(
array
,
(
offsetSig
+
offsetByteRange
),
byteRange
);
const
from2
=
to1
+
crypto
.
length
;
var
data
=
removeFromArray
(
array
,
to1
-
1
,
from2
+
1
);
const
to2
=
(
array
.
length
-
from2
)
-
1
;
var
crypto2
=
await
sign_pki
(
signingCert
,
certificateChain
,
privateKey
,
data
.
buffer
,
date
);
byteRange
=
'
[
'
+
pad10
(
from1
)
+
'
'
+
pad10
(
to1
-
1
)
+
'
'
+
pad10
(
from2
+
1
)
+
'
'
+
pad10
(
to2
)
+
'
]
'
;
array
=
updateArray
(
array
,
to1
,
crypto2
);
array
=
updateArray
(
array
,
offsetSig
+
offsetByteRange
,
byteRange
);
return
array
;
const
data
=
removeFromArray
(
array
,
to1
-
1
,
from2
+
1
);
const
crypto2
=
await
signPki
(
signingCert
,
certificateChain
,
privateKey
,
data
.
buffer
,
date
);
array
=
updateArray
(
array
,
to1
,
crypto2
);
return
array
;
}
}
async
function
appendSig
(
pdf
,
root
,
rootSuccessor
,
date
,
signingCert
,
certificateChain
,
privateKey
)
{
async
function
appendSig
(
pdf
,
root
,
rootSuccessor
,
date
,
signingCert
,
certificateChain
,
privateKey
)
{
//copy root and the entry with contents to the end
//copy root and the entry with contents to the end
const
startRoot
=
pdf
.
stream
.
bytes
.
length
+
1
;
var
startRoot
=
pdf
.
stream
.
bytes
.
length
+
1
;
let
array
=
copyToEnd
(
pdf
.
stream
.
bytes
,
root
.
offset
-
1
,
rootSuccessor
.
offset
);
var
array
=
copyToEnd
(
pdf
.
stream
.
bytes
,
root
.
offset
-
1
,
rootSuccessor
.
offset
);
//since we signed the first one, we know how the pdf has to look like:
//since we signed the first one, we know how the pdf has to look like:
const
offsetAcroForm
=
find
(
array
,
'
/AcroForm<</Fields
'
,
startRoot
);
var
offsetAcroForm
=
find
(
array
,
'
/AcroForm<</Fields
'
,
startRoot
);
const
endOffsetAcroForm
=
find
(
array
,
'
]
'
,
offsetAcroForm
);
var
endOffsetAcroForm
=
find
(
array
,
'
]
'
,
offsetAcroForm
);
const
annotEntry
=
findFreeXrefNr
(
pdf
.
xref
.
entries
);
var
annotEntry
=
findFreeXrefNr
(
pdf
.
xref
.
entries
);
const
sigEntry
=
findFreeXrefNr
(
pdf
.
xref
.
entries
,
[
annotEntry
]);
var
sigEntry
=
findFreeXrefNr
(
pdf
.
xref
.
entries
,
[
annotEntry
]);
const
appendAnnot
=
'
'
+
annotEntry
+
'
0 R
'
;
var
appendAnnot
=
'
'
+
annotEntry
+
'
0 R
'
;
array
=
insertIntoArray
(
array
,
endOffsetAcroForm
,
appendAnnot
);
array
=
insertIntoArray
(
array
,
endOffsetAcroForm
,
appendAnnot
);
//we need to add Annots [x y R] to the /Type /Page section. We can do that by searching /Annots
//we need to add Annots [x y R] to the /Type /Page section. We can do that by searching /Annots
const
pages
=
pdf
.
catalog
.
catDict
.
get
(
'
Pages
'
);
var
pages
=
pdf
.
catalog
.
catDict
.
get
(
'
Pages
'
);
//get first page, we have hidden sig, so don't bother
//get first page, we have hidden sig, so don't bother
const
contentRef
=
pages
.
get
(
'
Kids
'
)[
0
];
var
contentRef
=
pages
.
get
(
'
Kids
'
)[
0
];
const
xref
=
pdf
.
xref
.
fetch
(
contentRef
);
var
xref
=
pdf
.
xref
.
fetch
(
contentRef
);
const
offsetAnnotEnd
=
xref
.
get
(
'
#Annots_offset
'
);
var
offsetAnnotEnd
=
xref
.
get
(
'
#Annots_offset
'
);
//we now search ], this is safe as we signed it previously
//we now search ], this is safe as we signed it previously
const
endOffsetAnnot
=
find
(
array
,
'
]
'
,
offsetAnnotEnd
);
var
endOffsetAnnot
=
find
(
array
,
'
]
'
,
offsetAnnotEnd
);
const
xrefEntry
=
pdf
.
xref
.
getEntry
(
contentRef
.
num
);
var
xrefEntry
=
pdf
.
xref
.
getEntry
(
contentRef
.
num
);
const
xrefEntrySuccosser
=
findSuccessorEntry
(
pdf
.
xref
.
entries
,
xrefEntry
);
var
xrefEntrySuccosser
=
findSuccessorEntry
(
pdf
.
xref
.
entries
,
xrefEntry
);
const
offsetAnnotRelative
=
endOffsetAnnot
-
xrefEntrySuccosser
.
offset
;
var
offsetAnnotRelative
=
endOffsetAnnot
-
xrefEntrySuccosser
.
offset
;
const
startContent
=
array
.
length
;
var
startContent
=
array
.
length
;
array
=
copyToEnd
(
array
,
xrefEntry
.
offset
,
xrefEntrySuccosser
.
offset
);
array
=
copyToEnd
(
array
,
xrefEntry
.
offset
,
xrefEntrySuccosser
.
offset
);
array
=
insertIntoArray
(
array
,
array
.
length
+
offsetAnnotRelative
,
appendAnnot
);
array
=
insertIntoArray
(
array
,
array
.
length
+
offsetAnnotRelative
,
appendAnnot
);
const
startAnnot
=
array
.
length
;
var
startAnnot
=
array
.
length
;
const
append
=
annotEntry
+
'
0 obj
\n
<</F 132/Type/Annot/Subtype/Widget/Rect[0 0 0 0]/FT/Sig/DR<<>>/T(signature
'
+
annotEntry
+
'
)/V
'
+
sigEntry
+
'
0 R>>
\n
endobj
\n\n
'
;
var
append
=
annotEntry
+
'
0 obj
\n
<</F 132/Type/Annot/Subtype/Widget/Rect[0 0 0 0]/FT/Sig/DR<<>>/T(signature
'
+
annotEntry
+
'
)/V
'
+
sigEntry
+
'
0 R>>
\n
endobj
\n\n
'
;
array
=
insertIntoArray
(
array
,
startAnnot
,
append
);
array
=
insertIntoArray
(
array
,
startAnnot
,
append
);
const
startSig
=
array
.
length
;
var
startSig
=
array
.
length
;
const
start
=
sigEntry
+
'
0 obj
\n
<</Contents <
'
;
var
start
=
sigEntry
+
'
0 obj
\n
<</Contents <
'
;
const
dummy
=
await
signPki
(
signingCert
,
certificateChain
,
privateKey
,
stringToUint8Array
(
'
A
'
),
date
);
var
dummy
=
await
sign_pki
(
signingCert
,
certificateChain
,
privateKey
,
stringToUint8Array
(
'
A
'
),
date
);
//TODO: Adobe thinks its important to have the right size, no idea why this is the case
//TODO: Adobe thinks its important to have the right size, no idea why this is the case
const
crypto
=
new
Array
(
round256
(
dummy
.
length
*
2
)).
join
(
'
0
'
);
var
crypto
=
new
Array
(
round256
(
dummy
.
length
*
2
)).
join
(
'
0
'
);
const
middle
=
'
>
\n
/Type/Sig/SubFilter/adbe.pkcs7.detached/Location()/M(D:
'
+
now
(
date
)
+
'
\'
)
\n
/ByteRange
'
;
var
middle
=
'
>
\n
/Type/Sig/SubFilter/adbe.pkcs7.detached/Location()/M(D:
'
+
now
(
date
)
+
'
\'
)
\n
/ByteRange
'
;
let
byteRange
=
'
[0000000000 0000000000 0000000000 0000000000]
'
;
var
byteRange
=
'
[0000000000 0000000000 0000000000 0000000000]
'
;
const
end
=
'
/Filter/Adobe.PPKLite/Reason()/ContactInfo()>>
\n
endobj
\n\n
'
;
var
end
=
'
/Filter/Adobe.PPKLite/Reason()/ContactInfo()>>
\n
endobj
\n\n
'
;
//all together
//all together
const
append2
=
start
+
crypto
+
middle
+
byteRange
+
end
;
var
append2
=
start
+
crypto
+
middle
+
byteRange
+
end
;
array
=
insertIntoArray
(
array
,
startSig
,
append2
);
array
=
insertIntoArray
(
array
,
startSig
,
append2
);
const
sha256Hex
=
await
sha256
(
array
);
var
sha256Hex
=
await
sha256
(
array
);
const
prev
=
pdf
.
xref
.
xrefBlocks
[
0
];
var
prev
=
pdf
.
xref
.
xrefBlocks
[
0
];
const
startxref
=
array
.
length
;
var
startxref
=
array
.
length
;
const
xrefEntries
=
[];
var
xrefEntries
=
[];
xrefEntries
[
0
]
=
{
offset
:
0
,
xrefEntries
[
0
]
=
{
offset
:
0
,
gen
:
65535
,
free
:
true
};
gen
:
65535
,
xrefEntries
[
pdf
.
xref
.
topDict
.
getRaw
(
'
Root
'
).
num
]
=
{
offset
:
startRoot
,
gen
:
0
,
free
:
false
};
free
:
true
};
xrefEntries
[
contentRef
.
num
]
=
{
offset
:
startContent
,
gen
:
0
,
free
:
false
};
xrefEntries
[
pdf
.
xref
.
topDict
.
getRaw
(
'
Root
'
).
num
]
=
{
offset
:
startRoot
,
xrefEntries
[
annotEntry
]
=
{
offset
:
startAnnot
,
gen
:
0
,
free
:
false
};
gen
:
0
,
xrefEntries
[
sigEntry
]
=
{
offset
:
startSig
,
gen
:
0
,
free
:
false
};
free
:
false
};
var
xrefTable
=
createXrefTableAppend
(
xrefEntries
);
xrefEntries
[
contentRef
.
num
]
=
{
offset
:
startContent
,
xrefTable
+=
createTrailer
(
pdf
.
xref
.
topDict
,
startxref
,
sha256Hex
,
xrefEntries
.
length
,
prev
);
gen
:
0
,
array
=
insertIntoArray
(
array
,
array
.
length
,
xrefTable
);
free
:
false
};
xrefEntries
[
annotEntry
]
=
{
offset
:
startAnnot
,
var
from1
=
0
;
gen
:
0
,
var
to1
=
startSig
+
start
.
length
;
free
:
false
};
var
from2
=
to1
+
crypto
.
length
;
xrefEntries
[
sigEntry
]
=
{
offset
:
startSig
,
var
to2
=
(
array
.
length
-
from2
)
-
1
;
gen
:
0
,
var
byteRange
=
'
[
'
+
pad10
(
from1
)
+
'
'
+
pad10
(
to1
-
1
)
+
'
'
+
pad10
(
from2
+
1
)
+
'
'
+
pad10
(
to2
)
+
'
]
'
;
free
:
false
};
let
xrefTable
=
createXrefTableAppend
(
xrefEntries
);
array
=
updateArray
(
array
,
from2
+
middle
.
length
,
byteRange
);
xrefTable
+=
createTrailer
(
pdf
.
xref
.
topDict
,
startxref
,
sha256Hex
,
xrefEntries
.
length
,
prev
);
//now sign from1-to1 / from2-to2 and update byterange
array
=
insertIntoArray
(
array
,
array
.
length
,
xrefTable
);
var
data
=
removeFromArray
(
array
,
to1
-
1
,
from2
+
1
);
const
from1
=
0
;
var
crypto2
=
await
sign_pki
(
signingCert
,
certificateChain
,
privateKey
,
data
.
buffer
,
date
);
const
to1
=
startSig
+
start
.
length
;
array
=
updateArray
(
array
,
to1
,
crypto2
);
const
from2
=
to1
+
crypto
.
length
;
return
array
;
const
to2
=
(
array
.
length
-
from2
)
-
1
;
byteRange
=
'
[
'
+
pad10
(
from1
)
+
'
'
+
pad10
(
to1
-
1
)
+
'
'
+
pad10
(
from2
+
1
)
+
'
'
+
pad10
(
to2
)
+
'
]
'
;
array
=
updateArray
(
array
,
from2
+
middle
.
length
,
byteRange
);
//now sign from1-to1 / from2-to2 and update byterange
const
data
=
removeFromArray
(
array
,
to1
-
1
,
from2
+
1
);
const
crypto2
=
await
signPki
(
signingCert
,
certificateChain
,
privateKey
,
data
.
buffer
,
date
);
array
=
updateArray
(
array
,
to1
,
crypto2
);
return
array
;
}
}
function
loadPdf
(
pdfArray
)
{
function
loadPdf
(
pdfArray
)
{
const
pdf
=
new
pdfjsCoreDocument
.
PDFDocument
(
false
,
pdfArray
,
''
);
var
pdf
=
new
pdfjsCoreDocument
.
PDFDocument
(
false
,
pdfArray
,
''
);
pdf
.
parseStartXRef
();
pdf
.
parseStartXRef
();
pdf
.
parse
();
pdf
.
parse
();
return
pdf
;
return
pdf
;
}
}
//data must be Uint8Array
//data must be Uint8Array
async
function
signPki
(
signingCert
,
certificateChain
,
privateKey
,
data
,
date
)
{
async
function
sign_pki
(
signingCert
,
certificateChain
,
privateKey
,
data
,
date
)
{
const
crypto
=
getCrypto
();
const
crypto
=
getCrypto
();
//date = typeof date !== 'undefined' ? date : new Date();
//date = typeof date !== 'undefined' ? date : new Date();
const
hashAlg
=
"
SHA-256
"
;
const
hashAlg
=
"
SHA-256
"
;
const
digest
=
await
crypto
.
digest
({
name
:
hashAlg
},
data
);
const
digest
=
await
crypto
.
digest
({
name
:
hashAlg
},
data
);
const
signedAttr
=
[];
const
signedAttr
=
[];
signedAttr
.
push
(
new
Attribute
({
type
:
"
1.2.840.113549.1.9.3
"
,
signedAttr
.
push
(
new
Attribute
({
values
:
[
type
:
"
1.2.840.113549.1.9.3
"
,
new
ObjectIdentifier
({
value
:
"
1.2.840.113549.1.7.1
"
})
values
:
[
]
new
ObjectIdentifier
({
value
:
"
1.2.840.113549.1.7.1
"
})
}));
// contentType
]
}));
// contentType
signedAttr
.
push
(
new
Attribute
({
type
:
"
1.2.840.113549.1.9.4
"
,
signedAttr
.
push
(
new
Attribute
({
values
:
[
type
:
"
1.2.840.113549.1.9.4
"
,
new
OctetString
({
valueHex
:
digest
})
values
:
[
]
new
OctetString
({
valueHex
:
digest
})
}));
// messageDigest
]
}));
// messageDigest
signedAttr
.
push
(
new
Attribute
({
type
:
"
1.2.840.113549.1.9.5
"
,
signedAttr
.
push
(
new
Attribute
({
values
:
[
type
:
"
1.2.840.113549.1.9.5
"
,
new
UTCTime
({
valueDate
:
date
})
values
:
[
]
new
UTCTime
({
valueDate
:
date
})
}));
// signingTime
]
}));
// signingTime
const
cmsSignedSimpl
=
new
SignedData
({
version
:
1
,
const
cmsSignedSimpl
=
new
SignedData
({
encapContentInfo
:
new
EncapsulatedContentInfo
({
eContentType
:
"
1.2.840.113549.1.7.1
"
// "data" content type
}),
signerInfos
:
[
new
SignerInfo
({
version
:
1
,
version
:
1
,
sid
:
new
IssuerAndSerialNumber
({
encapContentInfo
:
new
EncapsulatedContentInfo
({
issuer
:
signingCert
.
issuer
,
eContentType
:
"
1.2.840.113549.1.7.1
"
// "data" content type
serialNumber
:
signingCert
.
serialNumber
}),
}),
signedAttrs
:
new
SignedAndUnsignedAttributes
({
signerInfos
:
[
type
:
0
,
new
SignerInfo
({
attributes
:
signedAttr
version
:
1
,
})
sid
:
new
IssuerAndSerialNumber
({
})
issuer
:
signingCert
.
issuer
,
],
serialNumber
:
signingCert
.
serialNumber
certificates
:
certificateChain
}),
});
signedAttrs
:
new
SignedAndUnsignedAttributes
({
type
:
0
,
attributes
:
signedAttr
})
})
],
certificates
:
certificateChain
});
await
cmsSignedSimpl
.
sign
(
privateKey
,
0
,
hashAlg
,
data
.
buffer
);
const
signatureBuffer
=
await
cmsSignedSimpl
.
sign
(
privateKey
,
0
,
hashAlg
,
data
.
buffer
);
const
cmsSignedSchema
=
cmsSignedSimpl
.
toSchema
(
true
);
const
cmsSignedSchema
=
cmsSignedSimpl
.
toSchema
(
true
);
const
cmsContentSimp
=
new
ContentInfo
({
const
cmsContentSimp
=
new
ContentInfo
({
contentType
:
"
1.2.840.113549.1.7.2
"
,
contentType
:
"
1.2.840.113549.1.7.2
"
,
content
:
cmsSignedSchema
content
:
cmsSignedSchema
});
});
const
_cmsSignedSchema
=
cmsContentSimp
.
toSchema
();
const
_cmsSignedSchema
=
cmsContentSimp
.
toSchema
();
//region Make length of some elements in "indefinite form"
//region Make length of some elements in "indefinite form"
_cmsSignedSchema
.
lenBlock
.
isIndefiniteForm
=
true
;
_cmsSignedSchema
.
lenBlock
.
isIndefiniteForm
=
true
;
const
block1
=
_cmsSignedSchema
.
valueBlock
.
value
[
1
];
const
block1
=
_cmsSignedSchema
.
valueBlock
.
value
[
1
];
block1
.
lenBlock
.
isIndefiniteForm
=
true
;
block1
.
lenBlock
.
isIndefiniteForm
=
true
;
const
block2
=
block1
.
valueBlock
.
value
[
0
];
const
block2
=
block1
.
valueBlock
.
value
[
0
];
block2
.
lenBlock
.
isIndefiniteForm
=
true
;
block2
.
lenBlock
.
isIndefiniteForm
=
true
;
//endregion
//endregion
cons
t
cmsSignedBuffer
=
_cmsSignedSchema
.
toBER
(
false
);
le
t
cmsSignedBuffer
=
_cmsSignedSchema
.
toBER
(
false
);
const
cmsSignedArray
=
new
Uint8Array
(
cmsSignedBuffer
);
const
cmsSignedArray
=
new
Uint8Array
(
cmsSignedBuffer
);
const
cmsSignedString
=
uint8ArrayToString
(
cmsSignedArray
);
const
cmsSignedString
=
uint8ArrayToString
(
cmsSignedArray
);
const
hex
=
strHex
(
cmsSignedString
);
const
hex
=
strHex
(
cmsSignedString
);
return
hex
;
return
hex
;
}
}
async
function
signPdfObjects
(
pdfRaw
,
signingCert
,
certificateChain
,
privateKey
,
date
)
{
date
=
typeof
date
!==
'
undefined
'
?
date
:
new
Date
();
async
function
signPdfObjects
(
pdfRaw
,
signingCert
,
certificateChain
,
privateKey
,
date
)
{
if
(
pdfRaw
instanceof
ArrayBuffer
)
{
date
=
typeof
date
!==
'
undefined
'
?
date
:
new
Date
();
pdfRaw
=
new
Uint8Array
(
pdfRaw
);
if
(
pdfRaw
instanceof
ArrayBuffer
)
{
}
pdfRaw
=
new
Uint8Array
(
pdfRaw
);
const
pdf
=
loadPdf
(
pdfRaw
);
}
const
root
=
findRootEntry
(
pdf
.
xref
);
var
pdf
=
loadPdf
(
pdfRaw
);
const
rootSuccessor
=
findSuccessorEntry
(
pdf
.
xref
.
entries
,
root
);
var
root
=
findRootEntry
(
pdf
.
xref
);
if
(
!
isSigInRoot
(
pdf
))
{
var
rootSuccessor
=
findSuccessorEntry
(
pdf
.
xref
.
entries
,
root
);
return
await
newSig
(
pdf
,
root
,
rootSuccessor
,
date
,
signingCert
,
certificateChain
,
privateKey
);
if
(
!
isSigInRoot
(
pdf
))
{
}
else
{
return
await
newSig
(
pdf
,
root
,
rootSuccessor
,
date
,
signingCert
,
certificateChain
,
privateKey
);
return
await
appendSig
(
pdf
,
root
,
rootSuccessor
,
date
,
signingCert
,
certificateChain
,
privateKey
);
}
else
{
}
return
await
appendSig
(
pdf
,
root
,
rootSuccessor
,
date
,
signingCert
,
certificateChain
,
privateKey
);
}
}
}
export
function
signPdf
(
pdfRaw
,
signingCert
,
certificateChain
,
privateKey
)
{
export
function
signPdf
(
pdfRaw
,
signingCert
,
certificateChain
,
privateKey
)
{
const
signingCertObj
=
parseCertificate
(
signingCert
);
const
signingCertObj
=
parseCertificate
(
signingCert
);
const
certificateChainObj
=
[];
const
certificateChainObj
=
[];
certificateChainObj
[
0
]
=
parseCertificate
(
signingCert
);
certificateChainObj
[
0
]
=
parseCertificate
(
signingCert
);
for
(
let
i
=
0
;
i
<
certificateChain
.
length
;
i
++
)
{
for
(
let
i
=
0
;
i
<
certificateChain
.
length
;
i
++
)
{
certificateChainObj
[
i
+
1
]
=
parseCertificate
(
certificateChain
[
i
])
;
certificateChainObj
[
i
+
1
]
=
parseCertificate
(
certificateChain
[
i
])
}
}
return
parsePrivateKey
(
privateKey
).
then
(
privateKeyDecoded
=>
{
return
parsePrivateKey
(
privateKey
).
then
(
privateKeyDecoded
=>
{
return
signPdfObjects
(
pdfRaw
,
signingCertObj
,
certificateChainObj
,
privateKeyDecoded
);
return
signPdfObjects
(
pdfRaw
,
signingCertObj
,
certificateChainObj
,
privateKeyDecoded
);
});
});
}
}
\ No newline at end of file
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment