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
2d1ecb82
Commit
2d1ecb82
authored
6 years ago
by
Damyan Mitev
Committed by
Sasha Ilieva
6 years ago
Browse files
Options
Downloads
Patches
Plain Diff
Make ES Lint happy
parent
f329d221
Branches
Branches containing commit
Tags
Tags containing commit
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
+574
-569
574 additions, 569 deletions
javascript/src/utilities/pdfUtilities.js
with
574 additions
and
569 deletions
javascript/src/utilities/pdfUtilities.js
+
574
−
569
View file @
2d1ecb82
...
@@ -9,367 +9,362 @@
...
@@ -9,367 +9,362 @@
*/
*/
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
);
var
retVal
=
'
xref
\n
'
;
let
retVal
=
'
xref
\n
'
;
var
last
=
-
2
;
let
last
=
-
2
;
for
(
var
i
in
xrefEntries
)
{
for
(
let
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
);
var
offset
=
xrefEntries
[
i
].
offset
;
const
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
''
;
}
var
count
=
1
;
let
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
)
{
var
retVal
=
'
trailer <<
\n
'
;
let
retVal
=
'
trailer <<
\n
'
;
retVal
+=
'
/Size
'
+
(
size
)
+
'
\n
'
;
retVal
+=
'
/Size
'
+
size
+
'
\n
'
;
var
refRoot
=
topDict
.
getRaw
(
'
Root
'
);
const
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
'
;
}
}
var
refInfo
=
topDict
.
getRaw
(
'
Info
'
);
const
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
);
var
retVal
=
'
xref
\n
'
;
let
retVal
=
'
xref
\n
'
;
var
last
=
-
2
;
let
last
=
-
2
;
for
(
var
i
in
xrefEntries
)
{
for
(
let
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
);
var
offset
=
xrefEntries
[
i
].
offset
;
const
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
)
{
var
sorted
=
[];
const
sorted
=
[];
for
(
var
key
in
dict
)
{
for
(
const
key
in
dict
)
{
sorted
[
sorted
.
length
]
=
key
;
sorted
[
sorted
.
length
]
=
key
;
}
}
sorted
.
sort
();
sorted
.
sort
();
var
tempDict
=
{};
const
tempDict
=
{};
for
(
var
i
=
0
;
i
<
sorted
.
length
;
i
++
)
{
for
(
let
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
)
{
var
cutlen
=
to
-
from
;
const
cutlen
=
to
-
from
;
var
buf
=
new
Uint8Array
(
array
.
length
-
cutlen
);
const
buf
=
new
Uint8Array
(
array
.
length
-
cutlen
);
for
(
var
i
=
0
;
i
<
from
;
i
++
)
{
for
(
let
i
=
0
;
i
<
from
;
i
++
)
{
buf
[
i
]
=
array
[
i
];
buf
[
i
]
=
array
[
i
];
}
}
for
(
var
i
=
to
,
len
=
array
.
length
;
i
<
len
;
i
++
)
{
for
(
let
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
)
{
var
num
=
xrefBlocks
.
length
/
2
;
const
num
=
xrefBlocks
.
length
/
2
;
var
retVal
=
[];
const
retVal
=
[];
for
(
var
i
=
0
;
i
<
num
;
i
++
)
{
for
(
let
i
=
0
;
i
<
num
;
i
++
)
{
retVal
.
push
({
start
:
xrefBlocks
[
i
],
end
:
xrefBlocks
[
i
+
num
]});
retVal
.
push
({
start
:
xrefBlocks
[
i
],
}
end
:
xrefBlocks
[
i
+
num
]});
return
retVal
;
}
return
retVal
;
}
}
function
convertUint8ArrayToBinaryString
(
u8Array
)
{
function
convertUint8ArrayToBinaryString
(
u8Array
)
{
var
i
,
len
=
u8Array
.
length
,
b
_s
tr
=
""
;
let
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
(
var
i
=
0
,
len
=
array
.
length
;
i
<
len
;
i
++
)
{
for
(
let
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
)
{
var
s
=
"
000000000
"
+
num
;
const
s
=
"
000000000
"
+
num
;
return
s
.
substr
(
s
.
length
-
10
);
return
s
.
substr
(
s
.
length
-
10
);
}
}
function
pad5
(
num
)
{
function
pad5
(
num
)
{
var
s
=
"
0000
"
+
num
;
const
s
=
"
0000
"
+
num
;
return
s
.
substr
(
s
.
length
-
5
);
return
s
.
substr
(
s
.
length
-
5
);
}
}
function
pad2
(
num
)
{
function
pad2
(
num
)
{
var
s
=
"
0
"
+
num
;
const
s
=
"
0
"
+
num
;
return
s
.
substr
(
s
.
length
-
2
);
return
s
.
substr
(
s
.
length
-
2
);
}
}
function
findRootEntry
(
xref
)
{
function
findRootEntry
(
xref
)
{
var
rootNr
=
xref
.
root
.
objId
.
substring
(
0
,
xref
.
root
.
objId
.
length
-
1
);
const
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
var
currentOffset
=
current
.
offset
;
const
currentOffset
=
current
.
offset
;
var
currentMin
=
Number
.
MAX_SAFE_INTEGER
;
let
currentMin
=
Number
.
MAX_SAFE_INTEGER
;
var
currentMinIndex
=
-
1
;
let
currentMinIndex
=
-
1
;
for
(
var
i
in
xrefEntries
)
{
for
(
const
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
)
{
var
upd
=
stringToUint8Array
(
str
);
const
upd
=
stringToUint8Array
(
str
);
for
(
var
i
=
0
,
len
=
upd
.
length
;
i
<
len
;
i
++
)
{
for
(
let
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
)
{
var
buf
=
new
Uint8Array
(
array
.
length
+
(
to
-
from
));
const
buf
=
new
Uint8Array
(
array
.
length
+
(
to
-
from
));
for
(
var
i
=
0
,
len
=
array
.
length
;
i
<
len
;
i
++
)
{
for
(
let
i
=
0
,
len
=
array
.
length
;
i
<
len
;
i
++
)
{
buf
[
i
]
=
array
[
i
];
buf
[
i
]
=
array
[
i
];
}
}
for
(
var
i
=
0
,
len
=
(
to
-
from
)
;
i
<
len
;
i
++
)
{
for
(
let
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
)
{
var
ins
=
stringToUint8Array
(
str
);
const
ins
=
stringToUint8Array
(
str
);
var
buf
=
new
Uint8Array
(
array
.
length
+
ins
.
length
);
const
buf
=
new
Uint8Array
(
array
.
length
+
ins
.
length
);
for
(
var
i
=
0
;
i
<
pos
;
i
++
)
{
for
(
let
i
=
0
;
i
<
pos
;
i
++
)
{
buf
[
i
]
=
array
[
i
];
buf
[
i
]
=
array
[
i
];
}
}
for
(
var
i
=
0
;
i
<
ins
.
length
;
i
++
)
{
for
(
let
i
=
0
;
i
<
ins
.
length
;
i
++
)
{
buf
[
pos
+
i
]
=
ins
[
i
];
buf
[
pos
+
i
]
=
ins
[
i
];
}
}
for
(
var
i
=
pos
;
i
<
array
.
length
;
i
++
)
{
for
(
let
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
)
{
var
buf
=
new
Uint8Array
(
str
.
length
);
const
buf
=
new
Uint8Array
(
str
.
length
);
for
(
var
i
=
0
,
strLen
=
str
.
length
;
i
<
strLen
;
i
++
)
{
for
(
let
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
'
)
{
var
s
=
''
;
let
s
=
''
;
for
(
var
i
=
from
;
i
<
to
;
i
++
)
{
for
(
let
i
=
from
;
i
<
to
;
i
++
)
{
s
=
s
+
String
.
fromCharCode
(
buf
[
i
]);
s
=
s
+
String
.
fromCharCode
(
buf
[
i
]);
}
return
s
;
}
}
return
String
.
fromCharCode
.
apply
(
null
,
buf
);
return
s
;
}
return
String
.
fromCharCode
.
apply
(
null
,
buf
);
}
}
function
findFreeXrefNr
(
xrefEntries
,
used
)
{
function
findFreeXrefNr
(
xrefEntries
,
used
)
{
used
=
typeof
used
!==
'
undefined
'
?
used
:
[];
used
=
typeof
used
!==
'
undefined
'
?
used
:
[];
var
inc
=
used
.
length
;
let
inc
=
used
.
length
;
for
(
var
i
=
1
;
i
<
xrefEntries
.
length
;
i
++
)
{
for
(
let
i
=
1
;
i
<
xrefEntries
.
length
;
i
++
)
{
const
index
=
used
.
indexOf
(
i
);
var
index
=
used
.
indexOf
(
i
);
const
entry
=
xrefEntries
[
""
+
i
];
var
entry
=
xrefEntries
[
""
+
i
];
if
(
index
===
-
1
&&
(
typeof
entry
===
'
undefined
'
||
entry
.
free
))
{
if
(
index
===
-
1
&&
(
typeof
entry
===
'
undefined
'
||
entry
.
free
))
{
return
i
;
return
i
;
}
}
if
(
index
!==
-
1
)
{
if
(
index
!==
-
1
)
{
inc
--
;
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
;
var
search
=
stringToUint8Array
(
needle
);
const
search
=
stringToUint8Array
(
needle
);
var
match
=
0
;
let
match
=
0
;
for
(
var
i
=
start
;
i
<
uint8
.
length
&&
i
<
limit
;
i
++
)
{
for
(
let
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
;
var
search
=
stringToUint8Array
(
needle
);
const
search
=
stringToUint8Array
(
needle
);
var
match
=
search
.
length
-
1
;
let
match
=
search
.
length
-
1
;
for
(
var
i
=
start
;
i
>=
0
&&
i
<
limit
;
i
--
)
{
for
(
let
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
)
{
return
i
-
1
;
}
}
}
return
-
1
;
}
if
(
match
===
0
)
{
return
i
-
1
;
function
strHex
(
s
)
{
var
a
=
""
;
for
(
var
i
=
0
;
i
<
s
.
length
;
i
++
)
{
a
=
a
+
pad2
(
s
.
charCodeAt
(
i
).
toString
(
16
));
}
}
return
a
;
}
return
-
1
;
}
}
function
strHex
(
s
)
{
let
a
=
""
;
for
(
let
i
=
0
;
i
<
s
.
length
;
i
++
)
{
a
=
a
+
pad2
(
s
.
charCodeAt
(
i
).
toString
(
16
));
}
return
a
;
}
async
function
sha256
(
array
)
{
async
function
sha256
(
array
)
{
const
cryptoLib
=
getCrypto
();
const
cryptoLib
=
getCrypto
();
const
digestTmpBuf
=
await
cryptoLib
.
digest
({
name
:
"
SHA-256
"
},
array
);
const
digestTmpBuf
=
await
cryptoLib
.
digest
({
name
:
"
SHA-256
"
},
array
);
const
digestTmpArray
=
new
Uint8Array
(
digestTmpBuf
);
const
digestTmpArray
=
new
Uint8Array
(
digestTmpBuf
);
const
digestTmpStr
=
uint8ArrayToString
(
digestTmpArray
);
const
digestTmpStr
=
uint8ArrayToString
(
digestTmpArray
);
const
sha256Hex
=
strHex
(
digestTmpStr
);
const
sha256Hex
=
strHex
(
digestTmpStr
);
return
sha256Hex
;
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
(
var
i
in
xref
.
entries
)
{
for
(
const
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
)
{
}
if
(
xref
.
xrefBlocks
[
i
]
>=
offset
)
{
for
(
const
i
in
xref
.
xrefBlocks
)
{
xref
.
xrefBlocks
[
i
]
+
=
offset
Delta
;
if
(
xref
.
xrefBlocks
[
i
]
>
=
offset
)
{
}
xref
.
xrefBlocks
[
i
]
+=
offsetDelta
;
}
}
}
}
}
function
updateXrefBlocks
(
xrefBlocks
,
offset
,
offsetDelta
)
{
function
updateXrefBlocks
(
xrefBlocks
,
offset
,
offsetDelta
)
{
for
(
var
i
in
xrefBlocks
)
{
for
(
const
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
;
}
}
/**
/**
...
@@ -387,349 +382,359 @@ function round256(x) {
...
@@ -387,349 +382,359 @@ 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();
var
yyyy
=
date
.
getFullYear
().
toString
();
const
yyyy
=
date
.
getFullYear
().
toString
();
var
MM
=
pad2
(
date
.
getMonth
()
+
1
);
const
MM
=
pad2
(
date
.
getMonth
()
+
1
);
var
dd
=
pad2
(
date
.
getDate
());
const
dd
=
pad2
(
date
.
getDate
());
var
hh
=
pad2
(
date
.
getHours
());
const
hh
=
pad2
(
date
.
getHours
());
var
mm
=
pad2
(
date
.
getMinutes
());
const
mm
=
pad2
(
date
.
getMinutes
());
var
ss
=
pad2
(
date
.
getSeconds
());
const
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
)
{
var
sign
=
(
date
.
getTimezoneOffset
()
>
0
)
?
"
-
"
:
"
+
"
;
const
sign
=
date
.
getTimezoneOffset
()
>
0
?
"
-
"
:
"
+
"
;
var
offset
=
Math
.
abs
(
date
.
getTimezoneOffset
());
const
offset
=
Math
.
abs
(
date
.
getTimezoneOffset
());
var
hours
=
pad2
(
Math
.
floor
(
offset
/
60
));
const
hours
=
pad2
(
Math
.
floor
(
offset
/
60
));
var
minutes
=
pad2
(
offset
%
60
);
const
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
var
annotEntry
=
findFreeXrefNr
(
pdf
.
xref
.
entries
);
const
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
var
offsetForm
=
find
(
pdf
.
stream
.
bytes
,
'
<<
'
,
root
.
offset
,
rootSuccessor
.
offset
)
+
2
;
const
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>>
//
//
var
appendAcroForm
=
'
/AcroForm<</Fields[
'
+
annotEntry
+
'
0 R] /SigFlags 3>>
'
;
const
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[
var
pages
=
pdf
.
catalog
.
catDict
.
get
(
'
Pages
'
);
const
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
var
ref
=
pages
.
get
(
'
Kids
'
)[
0
];
const
ref
=
pages
.
get
(
'
Kids
'
)[
0
];
var
xref
=
pdf
.
xref
.
fetch
(
ref
);
const
xref
=
pdf
.
xref
.
fetch
(
ref
);
var
offsetContentEnd
=
xref
.
get
(
'
#Contents_offset
'
);
const
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
var
offsetContent
=
findBackwards
(
pdf
.
stream
.
bytes
,
'
/Contents
'
,
offsetContentEnd
);
let
offsetContent
=
findBackwards
(
pdf
.
stream
.
bytes
,
'
/Contents
'
,
offsetContentEnd
);
var
appendAnnots
=
'
/Annots[
'
+
annotEntry
+
'
0 R]
\n
'
;
const
appendAnnots
=
'
/Annots[
'
+
annotEntry
+
'
0 R]
\n
'
;
//now insert string into stream
//now insert string into stream
var
array
=
insertIntoArray
(
pdf
.
stream
.
bytes
,
offsetForm
,
appendAcroForm
);
let
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
);
var
array
=
insertIntoArray
(
array
,
offsetContent
,
appendAnnots
);
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
var
sigEntry
=
findFreeXrefNr
(
pdf
.
xref
.
entries
,
[
annotEntry
]);
const
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
//
//
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
'
;
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
'
;
// we want the offset just before the last xref table or entry
// we want the offset just before the last xref table or entry
var
blocks
=
findXrefBlocks
(
pdf
.
xref
.
xrefBlocks
);
const
blocks
=
findXrefBlocks
(
pdf
.
xref
.
xrefBlocks
);
var
offsetAnnot
=
blocks
[
0
].
start
;
let
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
var
offsetSig
=
offsetAnnot
+
append
.
length
;
let
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.
var
start
=
sigEntry
+
'
0 obj
\n
<</Contents <
'
;
const
start
=
sigEntry
+
'
0 obj
\n
<</Contents <
'
;
var
dummy
=
await
sign_pki
(
signingCert
,
certificateChain
,
privateKey
,
stringToUint8Array
(
'
A
'
),
date
);
const
dummy
=
await
signPki
(
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
var
crypto
=
new
Array
(
round256
(
dummy
.
length
*
2
)).
join
(
'
0
'
);
const
crypto
=
new
Array
(
round256
(
dummy
.
length
*
2
)).
join
(
'
0
'
);
var
middle
=
'
>
\n
/Type/Sig/SubFilter/adbe.pkcs7.detached/Location()/M(D:
'
+
now
(
date
)
+
'
\'
)
\n
/ByteRange
'
;
const
middle
=
'
>
\n
/Type/Sig/SubFilter/adbe.pkcs7.detached/Location()/M(D:
'
+
now
(
date
)
+
'
\'
)
\n
/ByteRange
'
;
var
byteRange
=
'
[0000000000 0000000000 0000000000 0000000000]
'
;
let
byteRange
=
'
[0000000000 0000000000 0000000000 0000000000]
'
;
var
end
=
'
/Filter/Adobe.PPKLite/Reason()/ContactInfo()>>
\n
endobj
\n\n
'
;
const
end
=
'
/Filter/Adobe.PPKLite/Reason()/ContactInfo()>>
\n
endobj
\n\n
'
;
//all together
//all together
var
append2
=
start
+
crypto
+
middle
+
byteRange
+
end
;
const
append2
=
start
+
crypto
+
middle
+
byteRange
+
end
;
var
offsetByteRange
=
start
.
length
+
crypto
.
length
+
middle
.
length
;
const
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
var
xrefBlocks
=
findXrefBlocks
(
pdf
.
xref
.
xrefBlocks
);
const
xrefBlocks
=
findXrefBlocks
(
pdf
.
xref
.
xrefBlocks
);
for
(
var
i
in
xrefBlocks
)
{
for
(
const
i
in
xrefBlocks
)
{
var
oldSize
=
array
.
length
;
const
oldSize
=
array
.
length
;
array
=
removeFromArray
(
array
,
xrefBlocks
[
i
].
start
,
xrefBlocks
[
i
].
end
);
array
=
removeFromArray
(
array
,
xrefBlocks
[
i
].
start
,
xrefBlocks
[
i
].
end
);
var
length
=
array
.
length
-
oldSize
;
const
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
var
offsetEOF
=
find
(
array
,
'
%%EOF
'
,
xrefBlocks
[
i
].
start
,
xrefBlocks
[
i
].
start
+
20
);
const
offsetEOF
=
find
(
array
,
'
%%EOF
'
,
xrefBlocks
[
i
].
start
,
xrefBlocks
[
i
].
start
+
20
);
if
(
offsetEOF
>
0
)
{
if
(
offsetEOF
>
0
)
{
var
lengthEOF
=
'
%%EOF
'
.
length
;
const
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
);
}
}
var
sha256Hex
=
await
sha256
(
array
);
const
sha256Hex
=
await
sha256
(
array
);
//add the new entries to the xref
//add the new entries to the xref
pdf
.
xref
.
entries
[
annotEntry
]
=
{
offset
:
offsetAnnot
,
gen
:
0
,
free
:
false
};
pdf
.
xref
.
entries
[
annotEntry
]
=
{
offset
:
offsetAnnot
,
pdf
.
xref
.
entries
[
sigEntry
]
=
{
offset
:
offsetSig
,
gen
:
0
,
free
:
false
};
gen
:
0
,
free
:
false
};
var
xrefTable
=
createXrefTable
(
pdf
.
xref
.
entries
);
pdf
.
xref
.
entries
[
sigEntry
]
=
{
offset
:
offsetSig
,
//also empty entries count as in the PDF spec, page 720 (example)
gen
:
0
,
xrefTable
+=
createTrailer
(
pdf
.
xref
.
topDict
,
array
.
length
,
sha256Hex
,
pdf
.
xref
.
entries
.
length
);
free
:
false
};
array
=
insertIntoArray
(
array
,
array
.
length
,
xrefTable
);
let
xrefTable
=
createXrefTable
(
pdf
.
xref
.
entries
);
//since we consolidate, no prev! [adjust /Prev -> rawparsing + offset]
//also empty entries count as in the PDF spec, page 720 (example)
var
from1
=
0
;
xrefTable
+=
createTrailer
(
pdf
.
xref
.
topDict
,
array
.
length
,
sha256Hex
,
pdf
.
xref
.
entries
.
length
);
var
to1
=
offsetSig
+
start
.
length
;
array
=
insertIntoArray
(
array
,
array
.
length
,
xrefTable
);
var
from2
=
to1
+
crypto
.
length
;
var
to2
=
(
array
.
length
-
from2
)
-
1
;
//since we consolidate, no prev! [adjust /Prev -> rawparsing + offset]
var
byteRange
=
'
[
'
+
pad10
(
from1
)
+
'
'
+
pad10
(
to1
-
1
)
+
'
'
+
pad10
(
from2
+
1
)
+
'
'
+
pad10
(
to2
)
+
'
]
'
;
const
from1
=
0
;
array
=
updateArray
(
array
,
(
offsetSig
+
offsetByteRange
),
byteRange
);
const
to1
=
offsetSig
+
start
.
length
;
var
data
=
removeFromArray
(
array
,
to1
-
1
,
from2
+
1
);
const
from2
=
to1
+
crypto
.
length
;
var
crypto2
=
await
sign_pki
(
signingCert
,
certificateChain
,
privateKey
,
data
.
buffer
,
date
);
const
to2
=
(
array
.
length
-
from2
)
-
1
;
array
=
updateArray
(
array
,
to1
,
crypto2
);
byteRange
=
'
[
'
+
pad10
(
from1
)
+
'
'
+
pad10
(
to1
-
1
)
+
'
'
+
pad10
(
from2
+
1
)
+
'
'
+
pad10
(
to2
)
+
'
]
'
;
return
array
;
array
=
updateArray
(
array
,
offsetSig
+
offsetByteRange
,
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
;
}
}
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
var
startRoot
=
pdf
.
stream
.
bytes
.
length
+
1
;
const
startRoot
=
pdf
.
stream
.
bytes
.
length
+
1
;
var
array
=
copyToEnd
(
pdf
.
stream
.
bytes
,
root
.
offset
-
1
,
rootSuccessor
.
offset
);
let
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:
var
offsetAcroForm
=
find
(
array
,
'
/AcroForm<</Fields
'
,
startRoot
);
const
offsetAcroForm
=
find
(
array
,
'
/AcroForm<</Fields
'
,
startRoot
);
var
endOffsetAcroForm
=
find
(
array
,
'
]
'
,
offsetAcroForm
);
const
endOffsetAcroForm
=
find
(
array
,
'
]
'
,
offsetAcroForm
);
var
annotEntry
=
findFreeXrefNr
(
pdf
.
xref
.
entries
);
const
annotEntry
=
findFreeXrefNr
(
pdf
.
xref
.
entries
);
var
sigEntry
=
findFreeXrefNr
(
pdf
.
xref
.
entries
,
[
annotEntry
]);
const
sigEntry
=
findFreeXrefNr
(
pdf
.
xref
.
entries
,
[
annotEntry
]);
var
appendAnnot
=
'
'
+
annotEntry
+
'
0 R
'
;
const
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
var
pages
=
pdf
.
catalog
.
catDict
.
get
(
'
Pages
'
);
const
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
var
contentRef
=
pages
.
get
(
'
Kids
'
)[
0
];
const
contentRef
=
pages
.
get
(
'
Kids
'
)[
0
];
var
xref
=
pdf
.
xref
.
fetch
(
contentRef
);
const
xref
=
pdf
.
xref
.
fetch
(
contentRef
);
var
offsetAnnotEnd
=
xref
.
get
(
'
#Annots_offset
'
);
const
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
var
endOffsetAnnot
=
find
(
array
,
'
]
'
,
offsetAnnotEnd
);
const
endOffsetAnnot
=
find
(
array
,
'
]
'
,
offsetAnnotEnd
);
var
xrefEntry
=
pdf
.
xref
.
getEntry
(
contentRef
.
num
);
const
xrefEntry
=
pdf
.
xref
.
getEntry
(
contentRef
.
num
);
var
xrefEntrySuccosser
=
findSuccessorEntry
(
pdf
.
xref
.
entries
,
xrefEntry
);
const
xrefEntrySuccosser
=
findSuccessorEntry
(
pdf
.
xref
.
entries
,
xrefEntry
);
var
offsetAnnotRelative
=
endOffsetAnnot
-
xrefEntrySuccosser
.
offset
;
const
offsetAnnotRelative
=
endOffsetAnnot
-
xrefEntrySuccosser
.
offset
;
var
startContent
=
array
.
length
;
const
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
);
var
startAnnot
=
array
.
length
;
const
startAnnot
=
array
.
length
;
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
'
;
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
'
;
array
=
insertIntoArray
(
array
,
startAnnot
,
append
);
array
=
insertIntoArray
(
array
,
startAnnot
,
append
);
var
startSig
=
array
.
length
;
const
startSig
=
array
.
length
;
var
start
=
sigEntry
+
'
0 obj
\n
<</Contents <
'
;
const
start
=
sigEntry
+
'
0 obj
\n
<</Contents <
'
;
var
dummy
=
await
sign_pki
(
signingCert
,
certificateChain
,
privateKey
,
stringToUint8Array
(
'
A
'
),
date
);
const
dummy
=
await
signPki
(
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
var
crypto
=
new
Array
(
round256
(
dummy
.
length
*
2
)).
join
(
'
0
'
);
const
crypto
=
new
Array
(
round256
(
dummy
.
length
*
2
)).
join
(
'
0
'
);
var
middle
=
'
>
\n
/Type/Sig/SubFilter/adbe.pkcs7.detached/Location()/M(D:
'
+
now
(
date
)
+
'
\'
)
\n
/ByteRange
'
;
const
middle
=
'
>
\n
/Type/Sig/SubFilter/adbe.pkcs7.detached/Location()/M(D:
'
+
now
(
date
)
+
'
\'
)
\n
/ByteRange
'
;
var
byteRange
=
'
[0000000000 0000000000 0000000000 0000000000]
'
;
let
byteRange
=
'
[0000000000 0000000000 0000000000 0000000000]
'
;
var
end
=
'
/Filter/Adobe.PPKLite/Reason()/ContactInfo()>>
\n
endobj
\n\n
'
;
const
end
=
'
/Filter/Adobe.PPKLite/Reason()/ContactInfo()>>
\n
endobj
\n\n
'
;
//all together
//all together
var
append2
=
start
+
crypto
+
middle
+
byteRange
+
end
;
const
append2
=
start
+
crypto
+
middle
+
byteRange
+
end
;
array
=
insertIntoArray
(
array
,
startSig
,
append2
);
array
=
insertIntoArray
(
array
,
startSig
,
append2
);
var
sha256Hex
=
await
sha256
(
array
);
const
sha256Hex
=
await
sha256
(
array
);
var
prev
=
pdf
.
xref
.
xrefBlocks
[
0
];
const
prev
=
pdf
.
xref
.
xrefBlocks
[
0
];
var
startxref
=
array
.
length
;
const
startxref
=
array
.
length
;
var
xrefEntries
=
[];
const
xrefEntries
=
[];
xrefEntries
[
0
]
=
{
offset
:
0
,
gen
:
65535
,
free
:
true
};
xrefEntries
[
0
]
=
{
offset
:
0
,
xrefEntries
[
pdf
.
xref
.
topDict
.
getRaw
(
'
Root
'
).
num
]
=
{
offset
:
startRoot
,
gen
:
0
,
free
:
false
};
gen
:
65535
,
xrefEntries
[
contentRef
.
num
]
=
{
offset
:
startContent
,
gen
:
0
,
free
:
false
};
free
:
true
};
xrefEntries
[
annotEntry
]
=
{
offset
:
startAnnot
,
gen
:
0
,
free
:
false
};
xrefEntries
[
pdf
.
xref
.
topDict
.
getRaw
(
'
Root
'
).
num
]
=
{
offset
:
startRoot
,
xrefEntries
[
sigEntry
]
=
{
offset
:
startSig
,
gen
:
0
,
free
:
false
};
gen
:
0
,
var
xrefTable
=
createXrefTableAppend
(
xrefEntries
);
free
:
false
};
xrefTable
+=
createTrailer
(
pdf
.
xref
.
topDict
,
startxref
,
sha256Hex
,
xrefEntries
.
length
,
prev
);
xrefEntries
[
contentRef
.
num
]
=
{
offset
:
startContent
,
array
=
insertIntoArray
(
array
,
array
.
length
,
xrefTable
);
gen
:
0
,
free
:
false
};
var
from1
=
0
;
xrefEntries
[
annotEntry
]
=
{
offset
:
startAnnot
,
var
to1
=
startSig
+
start
.
length
;
gen
:
0
,
var
from2
=
to1
+
crypto
.
length
;
free
:
false
};
var
to2
=
(
array
.
length
-
from2
)
-
1
;
xrefEntries
[
sigEntry
]
=
{
offset
:
startSig
,
var
byteRange
=
'
[
'
+
pad10
(
from1
)
+
'
'
+
pad10
(
to1
-
1
)
+
'
'
+
pad10
(
from2
+
1
)
+
'
'
+
pad10
(
to2
)
+
'
]
'
;
gen
:
0
,
free
:
false
};
array
=
updateArray
(
array
,
from2
+
middle
.
length
,
byteRange
);
let
xrefTable
=
createXrefTableAppend
(
xrefEntries
);
//now sign from1-to1 / from2-to2 and update byterange
xrefTable
+=
createTrailer
(
pdf
.
xref
.
topDict
,
startxref
,
sha256Hex
,
xrefEntries
.
length
,
prev
);
array
=
insertIntoArray
(
array
,
array
.
length
,
xrefTable
);
var
data
=
removeFromArray
(
array
,
to1
-
1
,
from2
+
1
);
var
crypto2
=
await
sign_pki
(
signingCert
,
certificateChain
,
privateKey
,
data
.
buffer
,
date
);
const
from1
=
0
;
array
=
updateArray
(
array
,
to1
,
crypto2
);
const
to1
=
startSig
+
start
.
length
;
return
array
;
const
from2
=
to1
+
crypto
.
length
;
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
)
{
var
pdf
=
new
pdfjsCoreDocument
.
PDFDocument
(
false
,
pdfArray
,
''
);
const
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
sign_pki
(
signingCert
,
certificateChain
,
privateKey
,
data
,
date
)
{
async
function
signPki
(
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
({
signedAttr
.
push
(
new
Attribute
({
type
:
"
1.2.840.113549.1.9.3
"
,
type
:
"
1.2.840.113549.1.9.3
"
,
values
:
[
values
:
[
new
ObjectIdentifier
({
value
:
"
1.2.840.113549.1.7.1
"
})
new
ObjectIdentifier
({
value
:
"
1.2.840.113549.1.7.1
"
})
]
]
}));
// contentType
}));
// contentType
signedAttr
.
push
(
new
Attribute
({
signedAttr
.
push
(
new
Attribute
({
type
:
"
1.2.840.113549.1.9.4
"
,
type
:
"
1.2.840.113549.1.9.4
"
,
values
:
[
values
:
[
new
OctetString
({
valueHex
:
digest
})
new
OctetString
({
valueHex
:
digest
})
]
]
}));
// messageDigest
}));
// messageDigest
signedAttr
.
push
(
new
Attribute
({
signedAttr
.
push
(
new
Attribute
({
type
:
"
1.2.840.113549.1.9.5
"
,
type
:
"
1.2.840.113549.1.9.5
"
,
values
:
[
values
:
[
new
UTCTime
({
valueDate
:
date
})
new
UTCTime
({
valueDate
:
date
})
]
]
}));
// signingTime
}));
// signingTime
const
cmsSignedSimpl
=
new
SignedData
({
const
cmsSignedSimpl
=
new
SignedData
({
version
:
1
,
encapContentInfo
:
new
EncapsulatedContentInfo
({
eContentType
:
"
1.2.840.113549.1.7.1
"
// "data" content type
}),
signerInfos
:
[
new
SignerInfo
({
version
:
1
,
version
:
1
,
encapContentInfo
:
new
EncapsulatedContentInfo
({
sid
:
new
IssuerAndSerialNumber
({
eContentType
:
"
1.2.840.113549.1.7.1
"
// "data" content type
issuer
:
signingCert
.
issuer
,
serialNumber
:
signingCert
.
serialNumber
}),
}),
signerInfos
:
[
signedAttrs
:
new
SignedAndUnsignedAttributes
({
new
SignerInfo
({
type
:
0
,
version
:
1
,
attributes
:
signedAttr
sid
:
new
IssuerAndSerialNumber
({
})
issuer
:
signingCert
.
issuer
,
})
serialNumber
:
signingCert
.
serialNumber
],
}),
certificates
:
certificateChain
signedAttrs
:
new
SignedAndUnsignedAttributes
({
});
type
:
0
,
attributes
:
signedAttr
})
})
],
certificates
:
certificateChain
});
const
signatureBuffer
=
await
cmsSignedSimpl
.
sign
(
privateKey
,
0
,
hashAlg
,
data
.
buffer
);
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
le
t
cmsSignedBuffer
=
_cmsSignedSchema
.
toBER
(
false
);
cons
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
)
{
async
function
signPdfObjects
(
pdfRaw
,
signingCert
,
certificateChain
,
privateKey
,
date
)
{
date
=
typeof
date
!==
'
undefined
'
?
date
:
new
Date
();
date
=
typeof
date
!==
'
undefined
'
?
date
:
new
Date
();
if
(
pdfRaw
instanceof
ArrayBuffer
)
{
if
(
pdfRaw
instanceof
ArrayBuffer
)
{
pdfRaw
=
new
Uint8Array
(
pdfRaw
);
pdfRaw
=
new
Uint8Array
(
pdfRaw
);
}
}
const
pdf
=
loadPdf
(
pdfRaw
);
var
pdf
=
loadPdf
(
pdfRaw
);
const
root
=
findRootEntry
(
pdf
.
xref
);
var
root
=
findRootEntry
(
pdf
.
xref
);
const
rootSuccessor
=
findSuccessorEntry
(
pdf
.
xref
.
entries
,
root
);
var
rootSuccessor
=
findSuccessorEntry
(
pdf
.
xref
.
entries
,
root
);
if
(
!
isSigInRoot
(
pdf
))
{
if
(
!
isSigInRoot
(
pdf
))
{
return
await
newSig
(
pdf
,
root
,
rootSuccessor
,
date
,
signingCert
,
certificateChain
,
privateKey
);
return
await
newSig
(
pdf
,
root
,
rootSuccessor
,
date
,
signingCert
,
certificateChain
,
privateKey
);
}
else
{
}
else
{
return
await
appendSig
(
pdf
,
root
,
rootSuccessor
,
date
,
signingCert
,
certificateChain
,
privateKey
);
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