v1.0.0: parses bytes, packs type and bytes

This commit is contained in:
AJ ONeal 2019-10-12 17:45:32 -06:00
parent e2d048cacf
commit 72d95531d3
14 changed files with 382 additions and 2 deletions

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
*.gz
# ---> Node
# Logs
logs

8
.prettierrc Normal file
View File

@ -0,0 +1,8 @@
{
"bracketSpacing": true,
"printWidth": 80,
"singleQuote": true,
"tabWidth": 4,
"trailingComma": "none",
"useTabs": true
}

123
README.md
View File

@ -1,3 +1,122 @@
# pem.js
# @root/pem
Lightweight, Zero-Dependency, PEM (RFC 7468) encoder and decoder.
Lightweight, Zero-Dependency PEM encoder and decoder.
| ~300b gzipped
| ~650b minified
| ~1k full
|
- [x] VanillaJS
- [x] Zero-Dependency
- [x] Universal Support
- [x] Node.js
- [x] Browsers
# Support
This library supports PEM, which is pretty boring on its own.
Most likely you are also interested in some of the following:
- [keypairs.js](https://git.rootprojects.org/root/keypairs.js)
- RSA
- EC / ECDSA
- [x509.js](https://git.rootprojects.org/root/x509.js)
- [asn1.js](https://git.rootprojects.org/root/asn1.js)
# Usage
- PEM.parseBlock(str)
- PEM.packBlock(options)
Parsing
```js
var PEM = require('@root/pem/parser');
var block = PEM.parseBlock(
'-----BEGIN Type-----\nSGVsbG8sIOS4lueVjCE=\n-----END Type-----\n'
);
```
```js
{
bytes: `<48 65 6c 6c 6f 2c 20 e4 b8 96 e7 95 8c 21>`;
}
```
Packing
```js
var PEM = require('@root/pem/packer');
var block = PEM.packBlock({
type: 'Type',
// Buffer or Uint8Array or Array
bytes: [0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0xe4, 0xb8, 0x96, 0xe7, 0x95, 0x8c, 0x21]
);
```
```txt
-----BEGIN Type-----
SGVsbG8sIOS4lueVjCE=
-----END Type-----
```
# Install
## Node / Webpack
```js
npm install -g @root/pem
```
## Browsers
```html
<script src="https://unpkg.com/@root/pem/dist/pem.all.js"></script>
```
```html
<script src="https://unpkg.com/@root/pem/dist/pem.all.min.js"></script>
```
# A PEM Block
A Block represents a PEM encoded structure.
The encoded form is:
```txt
-----BEGIN Type-----
Headers
base64-encoded Bytes
-----END Type-----
```
where Headers is a possibly empty sequence of Key: Value lines.
(credit: https://golang.org/pkg/encoding/pem/)
# PEM History
PEM was introduced in 1993 via RFC 1421, but not formally
standardized until RFC 7468 in April of 2015.
It has served as the _de facto_ standard for a variety of
DER-encoded X509 schemas of ASN.1 data for cryptographic
keys and certificates such as:
- [x] PKCS#10 (Certificate Signing Request [CSR])
- [x] X509 Certificate (fullchain.pem, site.crt)
- [x] PKIX (cert.pem, privkey.pem, priv.key)
- [x] PKCS#1 (RSA Public and Private Keys)
- [x] PKCS#8 (RSA and ECDSA Keypairs)
- [x] SEC#1 (ECDSARSA Public and Private Keys)
# Legal
MPL-2.0 |
[Terms of Use](https://therootcompany.com/legal/#terms) |
[Privacy Policy](https://therootcompany.com/legal/#privacy)

23
browser/native.js Normal file
View File

@ -0,0 +1,23 @@
'use strict';
// "A little copying is better than a little dependency" - Rob Pike
var Enc = module.exports;
Enc.bufToBase64 = function(u8) {
var bin = '';
// map is not part of u8
u8.forEach(function(i) {
bin += String.fromCharCode(i);
});
return btoa(bin);
};
Enc.base64ToBuf = function(b64) {
return Uint8Array.from(
atob(b64)
.split('')
.map(function(ch) {
return ch.charCodeAt(0);
})
);
};

33
browser/parser.js Normal file
View File

@ -0,0 +1,33 @@
'use strict';
var Enc = require('@root/encoding/base64');
var PEM = module.exports;
PEM.packBlock = function(opts) {
// TODO allow for headers?
return (
'-----BEGIN ' +
opts.type +
'-----\n' +
Enc.bufToBase64(opts.bytes)
.match(/.{1,64}/g)
.join('\n') +
'\n' +
'-----END ' +
opts.type +
'-----'
);
};
// don't replace the full parseBlock, if it exists
PEM.parseBlock =
PEM.parseBlock ||
function(str) {
var der = str
.split(/\n/)
.filter(function(line) {
return !/-----/.test(line);
})
.join('');
return { bytes: Enc.base64ToBuf(der) };
};

17
build.sh Normal file
View File

@ -0,0 +1,17 @@
#!/bin/bash
# TODO convert to JS
cat browser/native.js parser.js packer.js > all.tmp.js
sed -i '' '/use strict/d' all.tmp.js
sed -i '' '/require/d' all.tmp.js
sed -i '' '/exports/d' all.tmp.js
echo ';(function () {' > all.js
echo "'use strict';" >> all.js
echo "var PEM = window.PEM = {};" >> all.js
echo "var Enc = {};" >> all.js
cat all.tmp.js >> all.js
rm all.tmp.js
echo '}());' >> all.js
mv all.js dist/pem.all.js
uglifyjs dist/pem.all.js > dist/pem.all.min.js

56
dist/pem.all.js vendored Normal file
View File

@ -0,0 +1,56 @@
;(function () {
'use strict';
var PEM = window.PEM = {};
var Enc = {};
// "A little copying is better than a little dependency" - Rob Pike
Enc.bufToBase64 = function(u8) {
var bin = '';
// map is not part of u8
u8.forEach(function(i) {
bin += String.fromCharCode(i);
});
return btoa(bin);
};
Enc.base64ToBuf = function(b64) {
return Uint8Array.from(
atob(b64)
.split('')
.map(function(ch) {
return ch.charCodeAt(0);
})
);
};
PEM.parseBlock = function(str) {
var der = str
.split(/\n/)
.filter(function(line) {
return !/-----/.test(line);
})
.join('');
return { bytes: Enc.base64ToBuf(der) };
};
PEM.packBlock = function(opts) {
// TODO allow for headers?
return (
'-----BEGIN ' +
opts.type +
'-----\n' +
Enc.bufToBase64(opts.bytes)
.match(/.{1,64}/g)
.join('\n') +
'\n' +
'-----END ' +
opts.type +
'-----'
);
};
}());

1
dist/pem.all.min.js vendored Normal file
View File

@ -0,0 +1 @@
(function(){"use strict";var PEM=window.PEM={};var Enc={};Enc.bufToBase64=function(u8){var bin="";u8.forEach(function(i){bin+=String.fromCharCode(i)});return btoa(bin)};Enc.base64ToBuf=function(b64){return Uint8Array.from(atob(b64).split("").map(function(ch){return ch.charCodeAt(0)}))};PEM.parseBlock=function(str){var der=str.split(/\n/).filter(function(line){return!/-----/.test(line)}).join("");return{bytes:Enc.base64ToBuf(der)}};PEM.packBlock=function(opts){return"-----BEGIN "+opts.type+"-----\n"+Enc.bufToBase64(opts.bytes).match(/.{1,64}/g).join("\n")+"\n"+"-----END "+opts.type+"-----"}})();

7
index.js Normal file
View File

@ -0,0 +1,7 @@
'use strict';
var PEM = require('./parser.js');
var PEMPacker = require('./packer.js');
PEM.packBlock = PEMPacker.packBlock;
module.exports = PEM;

12
node/native.js Normal file
View File

@ -0,0 +1,12 @@
'use strict';
// "A little copying is better than a little dependency" - Rob Pike
var Enc = module.exports;
Enc.bufToBase64 = function(buf) {
return Buffer.from(buf).toString('base64');
};
Enc.base64ToBuf = function(b64) {
return Buffer.from(b64, 'base64');
};

29
package.json Normal file
View File

@ -0,0 +1,29 @@
{
"name": "@root/pem",
"version": "1.0.0",
"description": "VanillaJS, Lightweight, Zero-Dependency, PEM encoder and decoder.",
"main": "index.js",
"browser": {
"./node/native.js": "./browser/native.js"
},
"scripts": {
"test": "node tests"
},
"repository": {
"type": "git",
"url": "https://git.rootprojects.org/root/pem.js.git"
},
"keywords": [
"PEM",
"x509",
"ASN.1",
"asn1",
"RFC7468",
"RFC1421",
"RFC",
"7468",
"1421"
],
"author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
"license": "MPL-2.0"
}

22
packer.js Normal file
View File

@ -0,0 +1,22 @@
'use strict';
var PEM = module.exports;
//var Enc = require('@root/encoding/base64');
var Enc = require('./node/native.js');
PEM.packBlock = function(opts) {
// TODO allow for headers?
return (
'-----BEGIN ' +
opts.type +
'-----\n' +
Enc.bufToBase64(opts.bytes)
.match(/.{1,64}/g)
.join('\n') +
'\n' +
'-----END ' +
opts.type +
'-----'
);
};

15
parser.js Normal file
View File

@ -0,0 +1,15 @@
'use strict';
var PEM = module.exports;
var Enc = require('./node/native.js');
PEM.parseBlock = function(str) {
var der = str
.split(/\n/)
.filter(function(line) {
return !/-----/.test(line);
})
.join('');
return { bytes: Enc.base64ToBuf(der) };
};

36
tests/index.js Normal file
View File

@ -0,0 +1,36 @@
'use strict';
var PEM = require('../');
//console.log(PEM);
// "Hello, 世界!";
var contents = 'SGVs\nbG8sIOS4\nlueVjCE=';
var pem = '-----BEGIN Type-----\n' + contents + '\n-----END Type-----\n';
var block = PEM.parseBlock(pem);
if (14 !== block.bytes.byteLength) {
throw new Error('should be 14 bytes');
}
if (0x48 !== block.bytes[0]) {
throw new Error('first byte should be 0x48 ("H")');
}
if (0x8c !== block.bytes[12]) {
throw new Error('13th byte should be 0x8c (3rd byte of "界")');
}
console.info("PASS: decodes 'bytes' field correctly");
var pem2 =
'-----BEGIN Type-----\n' +
contents.replace(/\n/g, '') +
'\n-----END Type-----';
block.type = 'Type';
if (pem2 !== PEM.packBlock(block)) {
console.debug(PEM.packBlock(block));
throw new Error('should pack PEM correctly');
}
console.info("PASS: encodes 'bytes' and 'type' fields correctly");