은승우

all add

Showing 1000 changed files with 4289 additions and 223 deletions

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

var express = require('express');
var app = express();
var client_id = 'bIYcswH22VlQqT8OkkLm';
var client_secret = 'qLaERoks0u';
var query = "한국어";
app.get('/detectLangs', function (req, res) {
var api_url = 'https://openapi.naver.com/v1/papago/detectLangs';
var request = require('request');
var options = {
url: api_url,
form: {'query': query},
headers: {'X-Naver-Client-Id':client_id, 'X-Naver-Client-Secret': client_secret}
};
request.post(options, function (error, response, body) {
if (!error && response.statusCode == 200) {
res.writeHead(200, {'Content-Type': 'text/json;charset=utf-8'});
res.end(body);
} else {
res.status(response.statusCode).end();
console.log('error = ' + response.statusCode);
}
});
});
app.listen(3000, function () {
console.log('http://127.0.0.1:3000/detectLangs app listening on port 3000!');
});
\ No newline at end of file
......@@ -5,7 +5,7 @@ var request = require('request');
var https=require('https');
var http=require('http');
/*
var fs = require("fs");
var httpsOptions = {
......@@ -15,7 +15,6 @@ var httpsOptions = {
http.createServer(app).listen(80);
https.createServer(httpsOptions, app).listen(443);
*/
/* if ssl expired
var greenlock= require('greenlock-express');
const lex = greenlock .create({
......@@ -97,6 +96,7 @@ function handleEvent(event) {
'Ocp-Apim-Subscription-Key': subscriptionKey
},
body: fs.readFileSync(event.message.image)
console.log(body);
};
request.post(options, function (error, response, body) {
var data=JSON.stringify(body);
......
var express = require('express');
var app = express();
var client_id = 'bIYcswH22VlQqT8OkkLm';
var client_secret = 'qLaERoks0u';
var query = "힘쌔고 강한 아침!";
app.get('/translate', function (req, res) {
var api_url = 'https://openapi.naver.com/v1/papago/n2mt';
var request = require('request');
var options = {
url: api_url,
form: {'source':'ko', 'target':'en', 'text':query},
headers: {'X-Naver-Client-Id':client_id, 'X-Naver-Client-Secret': client_secret}
};
request.post(options, function (error, response, body) {
if (!error && response.statusCode == 200) {
res.writeHead(200, {'Content-Type': 'text/json;charset=utf-8'});
res.end(body);
} else {
res.status(response.statusCode).end();
console.log('error = ' + response.statusCode);
}
});
});
app.listen(3000, function () {
console.log('http://127.0.0.1:3000/translate app listening on port 3000!');
});
\ No newline at end of file
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -x "$basedir/node" ]; then
"$basedir/node" "$basedir/../cert-info/bin/cert-info.js" "$@"
ret=$?
else
node "$basedir/../cert-info/bin/cert-info.js" "$@"
ret=$?
fi
exit $ret
@IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\..\cert-info\bin\cert-info.js" %*
) ELSE (
@SETLOCAL
@SET PATHEXT=%PATHEXT:;.JS;=;%
node "%~dp0\..\cert-info\bin\cert-info.js" %*
)
\ No newline at end of file
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -x "$basedir/node" ]; then
"$basedir/node" "$basedir/../eckles/bin/eckles.js" "$@"
ret=$?
else
node "$basedir/../eckles/bin/eckles.js" "$@"
ret=$?
fi
exit $ret
@IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\..\eckles\bin\eckles.js" %*
) ELSE (
@SETLOCAL
@SET PATHEXT=%PATHEXT:;.JS;=;%
node "%~dp0\..\eckles\bin\eckles.js" %*
)
\ No newline at end of file
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -x "$basedir/node" ]; then
"$basedir/node" "$basedir/../@root/greenlock/bin/greenlock.js" "$@"
ret=$?
else
node "$basedir/../@root/greenlock/bin/greenlock.js" "$@"
ret=$?
fi
exit $ret
@IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\..\@root\greenlock\bin\greenlock.js" %*
) ELSE (
@SETLOCAL
@SET PATHEXT=%PATHEXT:;.JS;=;%
node "%~dp0\..\@root\greenlock\bin\greenlock.js" %*
)
\ No newline at end of file
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -x "$basedir/node" ]; then
"$basedir/node" "$basedir/../keypairs/bin/keypairs.js" "$@"
ret=$?
else
node "$basedir/../keypairs/bin/keypairs.js" "$@"
ret=$?
fi
exit $ret
@IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\..\keypairs\bin\keypairs.js" %*
) ELSE (
@SETLOCAL
@SET PATHEXT=%PATHEXT:;.JS;=;%
node "%~dp0\..\keypairs\bin\keypairs.js" %*
)
\ No newline at end of file
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -x "$basedir/node" ]; then
"$basedir/node" "$basedir/../rasha/bin/rasha.js" "$@"
ret=$?
else
node "$basedir/../rasha/bin/rasha.js" "$@"
ret=$?
fi
exit $ret
@IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\..\rasha\bin\rasha.js" %*
) ELSE (
@SETLOCAL
@SET PATHEXT=%PATHEXT:;.JS;=;%
node "%~dp0\..\rasha\bin\rasha.js" %*
)
\ No newline at end of file
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -x "$basedir/node" ]; then
"$basedir/node" "$basedir/../rsa-compat/bin/rsa-keygen.js" "$@"
ret=$?
else
node "$basedir/../rsa-compat/bin/rsa-keygen.js" "$@"
ret=$?
fi
exit $ret
@IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\..\rsa-compat\bin\rsa-keygen.js" %*
) ELSE (
@SETLOCAL
@SET PATHEXT=%PATHEXT:;.JS;=;%
node "%~dp0\..\rsa-compat\bin\rsa-keygen.js" %*
)
\ No newline at end of file
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -x "$basedir/node" ]; then
"$basedir/node" "$basedir/../sshpk/bin/sshpk-conv" "$@"
ret=$?
else
node "$basedir/../sshpk/bin/sshpk-conv" "$@"
ret=$?
fi
exit $ret
@IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\..\sshpk\bin\sshpk-conv" %*
) ELSE (
@SETLOCAL
@SET PATHEXT=%PATHEXT:;.JS;=;%
node "%~dp0\..\sshpk\bin\sshpk-conv" %*
)
\ No newline at end of file
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -x "$basedir/node" ]; then
"$basedir/node" "$basedir/../sshpk/bin/sshpk-sign" "$@"
ret=$?
else
node "$basedir/../sshpk/bin/sshpk-sign" "$@"
ret=$?
fi
exit $ret
@IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\..\sshpk\bin\sshpk-sign" %*
) ELSE (
@SETLOCAL
@SET PATHEXT=%PATHEXT:;.JS;=;%
node "%~dp0\..\sshpk\bin\sshpk-sign" %*
)
\ No newline at end of file
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -x "$basedir/node" ]; then
"$basedir/node" "$basedir/../sshpk/bin/sshpk-verify" "$@"
ret=$?
else
node "$basedir/../sshpk/bin/sshpk-verify" "$@"
ret=$?
fi
exit $ret
@IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\..\sshpk\bin\sshpk-verify" %*
) ELSE (
@SETLOCAL
@SET PATHEXT=%PATHEXT:;.JS;=;%
node "%~dp0\..\sshpk\bin\sshpk-verify" %*
)
\ No newline at end of file
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -x "$basedir/node" ]; then
"$basedir/node" "$basedir/../uuid/bin/uuid" "$@"
ret=$?
else
node "$basedir/../uuid/bin/uuid" "$@"
ret=$?
fi
exit $ret
@IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\..\uuid\bin\uuid" %*
) ELSE (
@SETLOCAL
@SET PATHEXT=%PATHEXT:;.JS;=;%
node "%~dp0\..\uuid\bin\uuid" %*
)
\ No newline at end of file
This diff is collapsed. Click to expand it.
# [Greenlock Manager](https://git.rootprojects.org/root/greenlock-manager.js)
Manages SSL Certificate issue and renewal for [Greenlock](https://git.rootprojects.org/root/greenlock-manager.js).
Saves global and per-site config to a local File Sytem (current), with optional encrypted Cloud backup (coming soon).
```bash
npm install --save greenlock@3
npm install --save greenlock-manager@3
```
# Greenlock Manager CLI & API
All manager plugins have the **same API**.
The manager-specific implementation is overlaid by Greenlock with error handling and common utilities,
and then exposed as documented here.
**Note**: Most people do not need to (and should not) not use the JavaScript API.
Instead, use the CLI (current) or Web API (coming soon).
## Initialize the Manager
```bash
npx greenlock init --manager cloud --manager-token 'xxxx' --manager-config-file './greenlock.json'
```
Note: You **should not** initialize greenlock directly as
this may make it incompatible with the CLI and Web GUI.
Instead use the file generated by the CLI `init` (shown above).
```js
Greenlock.create({
manager: "@greenlock/manager",
cloud: true,
token: "xxxx",
configFile: "./greenlock.json",
packageRoot: __dirname
});
```
## Set Subscriber and other global Defaults
```bash
npx greenlock defaults --subscriber-email jon@example.com --agree-to-terms true
```
```js
greenlock.manager.defaults({
subscriberEmail: "jon@example.com",
agreeToTerms: true
});
```
# Site Management
By "site" we mean a primary domain and, optionally, secondary domains, to be listed on an ssl certificate,
along with any configuration that is necessary for getting and renewing those certificates.
## Add a sites - domains and SSL certificates
```bash
npx greenlock add --subject example.com --altnames 'example.com,www.example.com'
```
```js
greenlock.sites.add({
subject: "example.com",
altnames: ["example.com", "www.example.com"]
});
```
## View site config
```bash
npx greenlock config --subject example.com
```
```js
greenlock.sites.get({
servername: "www.example.com",
wildname: "*.example.com"
});
```
## Update site config
```bash
npx greenlock update --subject example.com --challenge-dns-01 acme-dns-01-ovh --challenge-dns-01-token xxxx
```
```js
greenlock.sites.update({
subject: "www.example.com",
challenges: {
"dns-01": {
module: "acme-dns-01-ovh",
token: "xxxx"
}
}
});
```
## Remove a site
To stop automatic renewal of SSL certificates for a particular site.
You to restart renewal you must use `add()`.
```bash
npx greenlock remove --subject example.com
```
```js
greenlock.sites.remove({
subject: "example.com"
});
```
"use strict";
var MFS = require("greenlock-manager-fs");
// TODO @greenlock/manager-cloud
var Manager = module.exports;
Manager.create = function(opts) {
var mfs = MFS.create(opts);
var manager = {};
//
// REQUIRED (basic issuance)
//
if (mfs.get) {
manager.get = async function({ servername, wildname }) {
// (optional) `wildcard` may or may not exist
// if *you* support wildcard domains, *you* should handle them
return mfs.get({ servername, wildname });
};
} else {
// (optional)
// because the current version doesn't have get()
manager.get = createGetFromFind();
}
//
// REQUIRED (basic issuance)
//
manager.set = async function(opts) {
return mfs.set(opts);
};
//
// Optional (Fully Automatic Renewal)
//
manager.find = async function(opts) {
// { subject, servernames, altnames, renewBefore }
return mfs.find(opts);
};
//
// Optional (Special Remove Functionality)
// The default behavior is to set `deletedAt`
//
//manager.remove = async function(opts) {
// return mfs.remove(opts);
//};
//
// Optional (special settings save)
// Implemented here because this module IS the fallback
//
manager.defaults = async function(opts) {
return mfs.defaults(opts);
};
//
// Optional (for common deps and/or async initialization)
//
manager.init = async function(deps) {
return mfs.init(deps);
};
return manager;
//
// IGNORE
// Backwards compat for the first versions of greenlock-manager-fs
//
function createGetFromFind() {
return async function({ servername, wildname }) {
var servernames = [servername];
if (wildname) {
servernames.push(wildname);
}
return mfs
.find({
servernames: servernames,
// because the original manager used altnames here
altnames: servernames
})
.then(function(sites) {
return sites[0] || null;
});
};
}
};
{
"_from": "@greenlock/manager@^3.0.0",
"_id": "@greenlock/manager@3.0.0",
"_inBundle": false,
"_integrity": "sha512-ijgJrFdzJPmzrDk8aKXYoYR8LNfG3hXd9/s54ZY7IgxTulyPQ/qOPgl7sWgCxxLhZBzSY1xI6eC/6Y5TQ01agg==",
"_location": "/@greenlock/manager",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "@greenlock/manager@^3.0.0",
"name": "@greenlock/manager",
"escapedName": "@greenlock%2fmanager",
"scope": "@greenlock",
"rawSpec": "^3.0.0",
"saveSpec": null,
"fetchSpec": "^3.0.0"
},
"_requiredBy": [
"/@root/greenlock"
],
"_resolved": "https://registry.npmjs.org/@greenlock/manager/-/manager-3.0.0.tgz",
"_shasum": "dfc8155d101fd9c28b1fd5b777ba34ab7180ed57",
"_spec": "@greenlock/manager@^3.0.0",
"_where": "C:\\Users\\USER\\Documents\\Desktop\\옵소팀플\\LINEBOT\\node_modules\\@root\\greenlock",
"author": {
"name": "AJ ONeal",
"email": "coolaj86@gmail.com",
"url": "https://coolaj86.com/"
},
"bundleDependencies": false,
"dependencies": {
"greenlock-manager-fs": "^3.0.5"
},
"deprecated": false,
"description": "FileSytem-based Manager with optional encrypted Cloud backup for Greenlock SSL",
"devDependencies": {
"greenlock-manager-test": "^3.1.1"
},
"files": [
"*.js",
"lib"
],
"keywords": [
"greenlock",
"manager",
"cloud",
"fs",
"ssl",
"file",
"system"
],
"license": "MPL-2.0",
"main": "manager.js",
"name": "@greenlock/manager",
"repository": {
"type": "git",
"url": "https://git.rootprojects.org/root/greenlock-manager.js.git"
},
"scripts": {
"test": "node tests"
},
"version": "3.0.0"
}
## 6.8.3 (05 Nov 2019)
### Bug fix
* Add exception handler in middleware (#153)
### Feature
* Flex Message Update 1 (#173)
* Support friend statistics API (#161)
### Misc
* Update dependencies (#174)
## 6.8.2 (08 Aug 2019)
### Bug fix
* Fix LINEThings Scenario Execution Event Types (#158)
## 6.8.1 (29 Jul 2019)
### Bug fix
* Fix a type wrong in Template Message (#163)
### Feature
* Get `X-LINE-Request-Id` by using `responseData['x-line-request-id']` (#151 #157)
## 6.8.0 (25 Jun 2019)
### Feature
* Add new parameter in push/reply/multicast/broadcast API to catch up the newest bot API (#147)
* Add new APIs in bot API (#147)
- Get the target limit for additional messages
- Get number of messages sent this month
- Get number of sent broadcast messages
- Send broadcast message
### Breaking changes
* Deprecate Node 6 and start to support Node 12 (#139)
* Remove polyfills for Node 6 (#149)
### Type
* Add LINE Things Event (#150)
### Misc
* Update axios and other dependencies by running `npm audit fix` to fix vulnerabilities. (#148 #154)
## 6.7.0 (18 Apr 2019)
### Feature
* Add alt URL field to URI action (#135)
* Implement (un)linkRichMenuToMultipleUsers (#135)
### Type
* Fix typo in a type (#124)
## 6.6.0 (4 Mar 2019)
### Feature
* Add DeviceLinkEvent / DeviceUnlinkEvent (#123)
### Type
* Fix FlexSpacer to have optional 'size' property (#122)
### Misc
* Run `npm audit fix` to fix minor dependency vulnerability.
## 6.5.0 (16 Feb 2019)
### Feature
* Add APIs to get number of sent messages (#116)
* Add account link event (#117)
### Misc
* Fix a typo in doc (#119)
## 6.4.0 (19 Nov 2018)
### Feature
......
# line-bot-sdk-nodejs
# LINE Messaging API SDK for nodejs
[![Travis CI](https://travis-ci.org/line/line-bot-sdk-nodejs.svg?branch=master)](https://travis-ci.org/line/line-bot-sdk-nodejs)
[![npmjs](https://badge.fury.io/js/%40line%2Fbot-sdk.svg)](https://www.npmjs.com/package/@line/bot-sdk)
Node.js SDK for LINE Messaging API
## Getting Started
## Introduction
The LINE Messaging API SDK for nodejs makes it easy to develop bots using LINE Messaging API, and you can create a sample bot within minutes.
### Install
## Documentation
See the official API documentation for more information
- English: https://developers.line.biz/en/docs/messaging-api/overview/
- Japanese: https://developers.line.biz/ja/docs/messaging-api/overview/
line-bot-sdk-nodejs documentation: https://line.github.io/line-bot-sdk-nodejs/#getting-started
## Requirements
* **Node.js** 8 or higher
## Installation
Using [npm](https://www.npmjs.com/):
......@@ -15,30 +28,37 @@ Using [npm](https://www.npmjs.com/):
$ npm install @line/bot-sdk --save
```
### Documentation
For guide, API reference, and other information, please refer to
the [documentation](https://line.github.io/line-bot-sdk-nodejs/).
## Help and media
FAQ: https://developers.line.biz/en/faq/
### LINE Messaging API References
Community Q&A: https://www.line-community.me/questions
Here are links to official references for LINE Messaging API. It is recommended
reading them beforehand.
News: https://developers.line.biz/en/news/
* LINE API Reference [EN](https://developers.line.me/en/docs/messaging-api/reference/) [JA](https://developers.line.me/ja/docs/messaging-api/reference/)
* LINE Developers - Messaging API
* [Overview](https://developers.line.me/messaging-api/overview)
* [Getting started](https://developers.line.me/messaging-api/getting-started)
* [Joining groups and rooms](https://developers.line.me/messaging-api/joining-groups-and-rooms)
Twitter: @LINE_DEV
## Requirements
## Versioning
This project respects semantic versioning
* **Node.js** 6 or higher
See http://semver.org/
## Contributing
Please check [CONTRIBUTING](CONTRIBUTING.md) before making a contribution.
## License
[Apache License Version 2.0](LICENSE)
```
Copyright (C) 2016 LINE Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```
......
......@@ -5,9 +5,11 @@ export default class Client {
config: Types.ClientConfig;
private http;
constructor(config: Types.ClientConfig);
pushMessage(to: string, messages: Types.Message | Types.Message[]): Promise<any>;
replyMessage(replyToken: string, messages: Types.Message | Types.Message[]): Promise<any>;
multicast(to: string[], messages: Types.Message | Types.Message[]): Promise<any>;
private parseHTTPResponse;
pushMessage(to: string, messages: Types.Message | Types.Message[], notificationDisabled?: boolean): Promise<Types.MessageAPIResponseBase>;
replyMessage(replyToken: string, messages: Types.Message | Types.Message[], notificationDisabled?: boolean): Promise<Types.MessageAPIResponseBase>;
multicast(to: string[], messages: Types.Message | Types.Message[], notificationDisabled?: boolean): Promise<Types.MessageAPIResponseBase>;
broadcast(messages: Types.Message | Types.Message[], notificationDisabled?: boolean): Promise<Types.MessageAPIResponseBase>;
getProfile(userId: string): Promise<Types.Profile>;
private getChatMemberProfile;
getGroupMemberProfile(groupId: string, userId: string): Promise<Types.Profile>;
......@@ -25,6 +27,8 @@ export default class Client {
getRichMenuIdOfUser(userId: string): Promise<string>;
linkRichMenuToUser(userId: string, richMenuId: string): Promise<any>;
unlinkRichMenuFromUser(userId: string): Promise<any>;
linkRichMenuToMultipleUsers(richMenuId: string, userIds: string[]): Promise<any>;
unlinkRichMenusFromMultipleUsers(userIds: string[]): Promise<any>;
getRichMenuImage(richMenuId: string): Promise<Readable>;
setRichMenuImage(richMenuId: string, data: Buffer | Readable, contentType?: string): Promise<any>;
getRichMenuList(): Promise<Array<Types.RichMenuResponse>>;
......@@ -32,4 +36,23 @@ export default class Client {
getDefaultRichMenuId(): Promise<string>;
deleteDefaultRichMenu(): Promise<{}>;
getLinkToken(userId: string): Promise<string>;
getNumberOfSentReplyMessages(date: string): Promise<Types.NumberOfMessagesSentResponse>;
getNumberOfSentPushMessages(date: string): Promise<Types.NumberOfMessagesSentResponse>;
getNumberOfSentMulticastMessages(date: string): Promise<Types.NumberOfMessagesSentResponse>;
getTargetLimitForAdditionalMessages(): Promise<Types.TargetLimitForAdditionalMessages>;
getNumberOfMessagesSentThisMonth(): Promise<Types.NumberOfMessagesSentThisMonth>;
getNumberOfSentBroadcastMessages(date: string): Promise<Types.NumberOfMessagesSentResponse>;
getNumberOfMessageDeliveries(date: string): Promise<Types.NumberOfMessageDeliveriesResponse>;
getNumberOfFollowers(date: string): Promise<Types.NumberOfFollowersResponse>;
getFriendDemographics(): Promise<Types.FriendDemoGraphics>;
}
export declare class OAuth {
private http;
constructor();
issueAccessToken(client_id: string, client_secret: string): Promise<{
access_token: string;
expires_in: number;
token_type: "Bearer";
}>;
revokeAccessToken(access_token: string): Promise<{}>;
}
......
This diff is collapsed. Click to expand it.
/// <reference types="node" />
import { AxiosResponse } from "axios";
import { Readable } from "stream";
declare type httpClientConfig = {
baseURL?: string;
defaultHeaders?: any;
responseParser?: <T>(res: AxiosResponse) => T;
};
export default class HTTPClient {
private instance;
constructor(baseURL?: string, defaultHeaders?: any);
private config;
constructor(config?: httpClientConfig);
get<T>(url: string, params?: any): Promise<T>;
getStream(url: string, params?: any): Promise<Readable>;
post<T>(url: string, data?: any): Promise<T>;
post<T>(url: string, body?: any): Promise<T>;
postForm<T>(url: string, body?: any): Promise<T>;
postBinary<T>(url: string, data: Buffer | Readable, contentType?: string): Promise<T>;
delete<T>(url: string, params?: any): Promise<T>;
private wrapError;
}
export {};
......
......@@ -4,9 +4,12 @@ const axios_1 = require("axios");
const stream_1 = require("stream");
const exceptions_1 = require("./exceptions");
const fileType = require("file-type");
const qs = require("querystring");
const pkg = require("../package.json");
class HTTPClient {
constructor(baseURL, defaultHeaders) {
constructor(config = {}) {
this.config = config;
const { baseURL, defaultHeaders } = config;
this.instance = axios_1.default.create({
baseURL,
headers: Object.assign({}, defaultHeaders, {
......@@ -15,27 +18,40 @@ class HTTPClient {
});
this.instance.interceptors.response.use(res => res, err => Promise.reject(this.wrapError(err)));
}
get(url, params) {
return this.instance.get(url, { params }).then(res => res.data);
async get(url, params) {
const res = await this.instance.get(url, { params });
return res.data;
}
getStream(url, params) {
return this.instance
.get(url, { params, responseType: "stream" })
.then(res => res.data);
async getStream(url, params) {
const res = await this.instance.get(url, {
params,
responseType: "stream",
});
return res.data;
}
post(url, data) {
return this.instance
.post(url, data, { headers: { "Content-Type": "application/json" } })
.then(res => res.data);
async post(url, body) {
const res = await this.instance.post(url, body, {
headers: { "Content-Type": "application/json" },
});
const { responseParser } = this.config;
if (responseParser)
return responseParser(res);
else
return res.data;
}
postBinary(url, data, contentType) {
let getBuffer;
if (Buffer.isBuffer(data)) {
getBuffer = Promise.resolve(data);
}
else {
getBuffer = new Promise((resolve, reject) => {
if (data instanceof stream_1.Readable) {
async postForm(url, body) {
const res = await this.instance.post(url, qs.stringify(body), {
headers: { "Content-Type": "application/x-www-form-urlencoded" },
});
return res.data;
}
async postBinary(url, data, contentType) {
const buffer = await (async () => {
if (Buffer.isBuffer(data)) {
return data;
}
else if (data instanceof stream_1.Readable) {
return new Promise((resolve, reject) => {
const buffers = [];
let size = 0;
data.on("data", (chunk) => {
......@@ -44,25 +60,23 @@ class HTTPClient {
});
data.on("end", () => resolve(Buffer.concat(buffers, size)));
data.on("error", reject);
}
else {
reject(new Error("invalid data type for postBinary"));
}
});
}
return getBuffer.then(data => {
return this.instance
.post(url, data, {
headers: {
"Content-Type": contentType || fileType(data).mime,
"Content-Length": data.length,
},
})
.then(res => res.data);
});
}
else {
throw new Error("invalid data type for postBinary");
}
})();
const res = await this.instance.post(url, buffer, {
headers: {
"Content-Type": contentType || fileType(buffer).mime,
"Content-Length": buffer.length,
},
});
return res.data;
}
delete(url, params) {
return this.instance.delete(url, { params }).then(res => res.data);
async delete(url, params) {
const res = await this.instance.delete(url, { params });
return res.data;
}
wrapError(err) {
if (err.response) {
......
import Client from "./client";
import Client, { OAuth } from "./client";
import middleware from "./middleware";
import validateSignature from "./validate-signature";
export { Client, middleware, validateSignature };
export { Client, middleware, validateSignature, OAuth };
export * from "./exceptions";
export * from "./types";
......
......@@ -5,9 +5,11 @@ function __export(m) {
Object.defineProperty(exports, "__esModule", { value: true });
const client_1 = require("./client");
exports.Client = client_1.default;
exports.OAuth = client_1.OAuth;
const middleware_1 = require("./middleware");
exports.middleware = middleware_1.default;
const validate_signature_1 = require("./validate-signature");
exports.validateSignature = validate_signature_1.default;
// re-export exceptions and types
__export(require("./exceptions"));
__export(require("./types"));
......
......@@ -6,5 +6,5 @@ export declare type Request = http.IncomingMessage & {
};
export declare type Response = http.ServerResponse;
export declare type NextCallback = (err?: Error) => void;
export declare type Middleware = (req: Request, res: Response, next: NextCallback) => void;
export declare type Middleware = (req: Request, res: Response, next: NextCallback) => void | Promise<void>;
export default function middleware(config: Types.MiddlewareConfig): Middleware;
......
......@@ -11,7 +11,7 @@ function middleware(config) {
throw new Error("no channel secret");
}
const secret = config.channelSecret;
return (req, res, next) => {
const _middleware = async (req, res, next) => {
// header names are lower-cased
// https://nodejs.org/api/http.html#http_message_headers
const signature = req.headers["x-line-signature"];
......@@ -19,34 +19,34 @@ function middleware(config) {
next(new exceptions_1.SignatureValidationFailed("no signature"));
return;
}
let getBody;
if (isValidBody(req.rawBody)) {
// rawBody is provided in Google Cloud Functions and others
getBody = Promise.resolve(req.rawBody);
}
else if (isValidBody(req.body)) {
getBody = Promise.resolve(req.body);
}
else {
// body may not be parsed yet, parse it to a buffer
getBody = new Promise(resolve => {
body_parser_1.raw({ type: "*/*" })(req, res, () => resolve(req.body));
});
}
getBody.then(body => {
if (!validate_signature_1.default(body, secret, signature)) {
next(new exceptions_1.SignatureValidationFailed("signature validation failed", signature));
return;
const body = await (async () => {
if (isValidBody(req.rawBody)) {
// rawBody is provided in Google Cloud Functions and others
return req.rawBody;
}
const strBody = Buffer.isBuffer(body) ? body.toString() : body;
try {
req.body = JSON.parse(strBody);
next();
else if (isValidBody(req.body)) {
return req.body;
}
catch (err) {
next(new exceptions_1.JSONParseError(err.message, strBody));
else {
// body may not be parsed yet, parse it to a buffer
return new Promise((resolve, reject) => body_parser_1.raw({ type: "*/*" })(req, res, (error) => error ? reject(error) : resolve(req.body)));
}
});
})();
if (!validate_signature_1.default(body, secret, signature)) {
next(new exceptions_1.SignatureValidationFailed("signature validation failed", signature));
return;
}
const strBody = Buffer.isBuffer(body) ? body.toString() : body;
try {
req.body = JSON.parse(strBody);
next();
}
catch (err) {
next(new exceptions_1.JSONParseError(err.message, strBody));
}
};
return (req, res, next) => {
_middleware(req, res, next).catch(next);
};
}
exports.default = middleware;
......
This diff is collapsed. Click to expand it.
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LINE_REQUEST_ID_HTTP_HEADER_NAME = "x-line-request-id";
......
......@@ -2,35 +2,13 @@
Object.defineProperty(exports, "__esModule", { value: true });
const crypto_1 = require("crypto");
function s2b(str, encoding) {
if (Buffer.from) {
try {
return Buffer.from(str, encoding);
}
catch (err) {
if (err.name === "TypeError") {
return new Buffer(str, encoding);
}
throw err;
}
}
else {
return new Buffer(str, encoding);
}
return Buffer.from(str, encoding);
}
function safeCompare(a, b) {
if (a.length !== b.length) {
return false;
}
if (crypto_1.timingSafeEqual) {
return crypto_1.timingSafeEqual(a, b);
}
else {
let result = 0;
for (let i = 0; i < a.length; i++) {
result |= a[i] ^ b[i];
}
return result === 0;
}
return crypto_1.timingSafeEqual(a, b);
}
function validateSignature(body, channelSecret, signature) {
return safeCompare(crypto_1.createHmac("SHA256", channelSecret)
......
This diff is collapsed. Click to expand it.
import axios, { AxiosInstance, AxiosError } from "axios";
import axios, { AxiosInstance, AxiosError, AxiosResponse } from "axios";
import { Readable } from "stream";
import { HTTPError, ReadError, RequestError } from "./exceptions";
import * as fileType from "file-type";
import * as qs from "querystring";
const pkg = require("../package.json");
type httpClientConfig = {
baseURL?: string;
defaultHeaders?: any;
responseParser?: <T>(res: AxiosResponse) => T;
};
export default class HTTPClient {
private instance: AxiosInstance;
private config: httpClientConfig;
constructor(baseURL?: string, defaultHeaders?: any) {
constructor(config: httpClientConfig = {}) {
this.config = config;
const { baseURL, defaultHeaders } = config;
this.instance = axios.create({
baseURL,
headers: Object.assign({}, defaultHeaders, {
......@@ -22,34 +32,47 @@ export default class HTTPClient {
);
}
public get<T>(url: string, params?: any): Promise<T> {
return this.instance.get(url, { params }).then(res => res.data);
public async get<T>(url: string, params?: any): Promise<T> {
const res = await this.instance.get(url, { params });
return res.data;
}
public getStream(url: string, params?: any): Promise<Readable> {
return this.instance
.get(url, { params, responseType: "stream" })
.then(res => res.data as Readable);
public async getStream(url: string, params?: any): Promise<Readable> {
const res = await this.instance.get(url, {
params,
responseType: "stream",
});
return res.data as Readable;
}
public post<T>(url: string, data?: any): Promise<T> {
return this.instance
.post(url, data, { headers: { "Content-Type": "application/json" } })
.then(res => res.data);
public async post<T>(url: string, body?: any): Promise<T> {
const res = await this.instance.post(url, body, {
headers: { "Content-Type": "application/json" },
});
const { responseParser } = this.config;
if (responseParser) return responseParser<T>(res);
else return res.data;
}
public postBinary<T>(
public async postForm<T>(url: string, body?: any): Promise<T> {
const res = await this.instance.post(url, qs.stringify(body), {
headers: { "Content-Type": "application/x-www-form-urlencoded" },
});
return res.data;
}
public async postBinary<T>(
url: string,
data: Buffer | Readable,
contentType?: string,
): Promise<T> {
let getBuffer: Promise<Buffer>;
if (Buffer.isBuffer(data)) {
getBuffer = Promise.resolve(data);
} else {
getBuffer = new Promise((resolve, reject) => {
if (data instanceof Readable) {
const buffer = await (async (): Promise<Buffer> => {
if (Buffer.isBuffer(data)) {
return data;
} else if (data instanceof Readable) {
return new Promise<Buffer>((resolve, reject) => {
const buffers: Buffer[] = [];
let size = 0;
data.on("data", (chunk: Buffer) => {
......@@ -58,26 +81,25 @@ export default class HTTPClient {
});
data.on("end", () => resolve(Buffer.concat(buffers, size)));
data.on("error", reject);
} else {
reject(new Error("invalid data type for postBinary"));
}
});
}
});
} else {
throw new Error("invalid data type for postBinary");
}
})();
return getBuffer.then(data => {
return this.instance
.post(url, data, {
headers: {
"Content-Type": contentType || fileType(data).mime,
"Content-Length": data.length,
},
})
.then(res => res.data);
const res = await this.instance.post(url, buffer, {
headers: {
"Content-Type": contentType || fileType(buffer).mime,
"Content-Length": buffer.length,
},
});
return res.data;
}
public delete<T>(url: string, params?: any): Promise<T> {
return this.instance.delete(url, { params }).then(res => res.data);
public async delete<T>(url: string, params?: any): Promise<T> {
const res = await this.instance.delete(url, { params });
return res.data;
}
private wrapError(err: AxiosError): Error {
......
import Client from "./client";
import Client, { OAuth } from "./client";
import middleware from "./middleware";
import validateSignature from "./validate-signature";
export { Client, middleware, validateSignature };
export { Client, middleware, validateSignature, OAuth };
// re-export exceptions and types
export * from "./exceptions";
......
......@@ -12,7 +12,7 @@ export type Middleware = (
req: Request,
res: Response,
next: NextCallback,
) => void;
) => void | Promise<void>;
function isValidBody(body?: any): body is string | Buffer {
return (body && typeof body === "string") || Buffer.isBuffer(body);
......@@ -25,7 +25,7 @@ export default function middleware(config: Types.MiddlewareConfig): Middleware {
const secret = config.channelSecret;
return (req, res, next) => {
const _middleware: Middleware = async (req, res, next) => {
// header names are lower-cased
// https://nodejs.org/api/http.html#http_message_headers
const signature = req.headers["x-line-signature"] as string;
......@@ -35,38 +35,39 @@ export default function middleware(config: Types.MiddlewareConfig): Middleware {
return;
}
let getBody: Promise<string | Buffer>;
if (isValidBody((req as any).rawBody)) {
// rawBody is provided in Google Cloud Functions and others
getBody = Promise.resolve((req as any).rawBody);
} else if (isValidBody(req.body)) {
getBody = Promise.resolve(req.body);
} else {
// body may not be parsed yet, parse it to a buffer
getBody = new Promise(resolve => {
raw({ type: "*/*" })(req as any, res as any, () => resolve(req.body));
});
}
getBody.then(body => {
if (!validateSignature(body, secret, signature)) {
next(
new SignatureValidationFailed(
"signature validation failed",
signature,
const body = await (async (): Promise<string | Buffer> => {
if (isValidBody((req as any).rawBody)) {
// rawBody is provided in Google Cloud Functions and others
return (req as any).rawBody;
} else if (isValidBody(req.body)) {
return req.body;
} else {
// body may not be parsed yet, parse it to a buffer
return new Promise<Buffer>((resolve, reject) =>
raw({ type: "*/*" })(req as any, res as any, (error: Error) =>
error ? reject(error) : resolve(req.body),
),
);
return;
}
})();
if (!validateSignature(body, secret, signature)) {
next(
new SignatureValidationFailed("signature validation failed", signature),
);
return;
}
const strBody = Buffer.isBuffer(body) ? body.toString() : body;
const strBody = Buffer.isBuffer(body) ? body.toString() : body;
try {
req.body = JSON.parse(strBody);
next();
} catch (err) {
next(new JSONParseError(err.message, strBody));
}
});
try {
req.body = JSON.parse(strBody);
next();
} catch (err) {
next(new JSONParseError(err.message, strBody));
}
};
return (req, res, next): void => {
(<Promise<void>>_middleware(req, res, next)).catch(next);
};
}
......
This diff is collapsed. Click to expand it.
import { createHmac, timingSafeEqual } from "crypto";
function s2b(str: string, encoding: string): Buffer {
if (Buffer.from) {
try {
return Buffer.from(str, encoding);
} catch (err) {
if (err.name === "TypeError") {
return new Buffer(str, encoding);
}
throw err;
}
} else {
return new Buffer(str, encoding);
}
return Buffer.from(str, encoding);
}
function safeCompare(a: Buffer, b: Buffer): boolean {
if (a.length !== b.length) {
return false;
}
if (timingSafeEqual) {
return timingSafeEqual(a, b);
} else {
let result = 0;
for (let i = 0; i < a.length; i++) {
result |= a[i] ^ b[i];
}
return result === 0;
}
return timingSafeEqual(a, b);
}
export default function validateSignature(
......
{
"_from": "@line/bot-sdk",
"_id": "@line/bot-sdk@6.4.0",
"_from": "@line/bot-sdk@^6.4.0",
"_id": "@line/bot-sdk@6.8.3",
"_inBundle": false,
"_integrity": "sha512-N0FkrqFxTTleOpD6y7DTK8qbMYHr9Q8qZfrAmSYEFAGedM1HLJdbNNkStj5GT+svx+w+/ePF/n7nAEts0aJwkA==",
"_integrity": "sha512-nj2T4CQxw0W/juAlpj0kMTDScOh5QUK6xMCR2dZp+pN8B0vj/c+5uX3TyGB4ijz/NIsehgfKujPgzw7LhtYtJw==",
"_location": "/@line/bot-sdk",
"_phantomChildren": {},
"_requested": {
"type": "tag",
"type": "range",
"registry": true,
"raw": "@line/bot-sdk",
"raw": "@line/bot-sdk@^6.4.0",
"name": "@line/bot-sdk",
"escapedName": "@line%2fbot-sdk",
"scope": "@line",
"rawSpec": "",
"rawSpec": "^6.4.0",
"saveSpec": null,
"fetchSpec": "latest"
"fetchSpec": "^6.4.0"
},
"_requiredBy": [
"#USER",
"/"
],
"_resolved": "https://registry.npmjs.org/@line/bot-sdk/-/bot-sdk-6.4.0.tgz",
"_shasum": "18aa7659da26d3a8487614c74ad9ccb80ec4ca59",
"_spec": "@line/bot-sdk",
"_where": "C:\\Users\\KSI\\Desktop\\3-2\\OSS\\LineBot",
"_resolved": "https://registry.npmjs.org/@line/bot-sdk/-/bot-sdk-6.8.3.tgz",
"_shasum": "0a886461e8c16a8c89091fd5338f6071335636a6",
"_spec": "@line/bot-sdk@^6.4.0",
"_where": "C:\\Users\\USER\\Documents\\Desktop\\옵소팀플\\LINEBOT",
"bugs": {
"url": "https://github.com/line/line-bot-sdk-nodejs/issues"
},
......@@ -32,7 +32,7 @@
"@types/body-parser": "^1.16.8",
"@types/file-type": "^5.2.1",
"@types/node": "^7.0.31",
"axios": "^0.16.2",
"axios": "^0.19.0",
"body-parser": "^1.18.2",
"file-type": "^7.2.0"
},
......@@ -40,19 +40,21 @@
"description": "Node.js SDK for LINE Messaging API",
"devDependencies": {
"@types/express": "^4.0.35",
"@types/finalhandler": "^1.1.0",
"@types/mocha": "^2.2.41",
"del-cli": "^1.1.0",
"express": "^4.16.3",
"finalhandler": "^1.1.2",
"husky": "^0.14.3",
"mocha": "^5.2.0",
"nyc": "^12.0.2",
"nyc": "^14.1.1",
"prettier": "^1.15.2",
"ts-node": "^3.3.0",
"ts-node": "^8.3.0",
"typescript": "^3.1.6",
"vuepress": "^0.14.2"
"vuepress": "^0.14.10"
},
"engines": {
"node": ">=6"
"node": ">=8"
},
"files": [
"dist",
......@@ -102,5 +104,5 @@
"test": "API_BASE_URL=http://localhost:1234/ TEST_PORT=1234 TS_NODE_CACHE=0 nyc mocha"
},
"types": "dist/index.d.ts",
"version": "6.4.0"
"version": "6.8.3"
}
......
# Changelog
- v3 (Oct 2019)
- Add POST-as-GET for Let's Encrypt v2 release 2 (ACME / RFC 8555)
- Jump to v3 for parity with Greenlock
- Merge browser and node.js versions in one
- Drop all backwards-compat complexity
- Move to zero-external deps, using @root packages only
- v1.8
- more transitional prepwork for new v2 API
- support newer (simpler) dns-01 and http-01 libraries
- v1.5
- perform full test challenge first (even before nonce)
- v1.3
- Use node RSA keygen by default
- No non-optional external deps!
- v1.2
- fix some API out-of-specness
- doc some magic numbers (status)
- updated deps
- v1.1.0
- reduce dependencies (use lightweight @coolaj86/request instead of request)
- v1.0.5 - cleanup logging
- v1.0.4 - v6- compat use `promisify` from node's util or bluebird
- v1.0.3 - documentation cleanup
- v1.0.2
- use `options.contact` to provide raw contact array
- made `options.email` optional
- file cleanup
- v1.0.1
- Compat API is ready for use
- Eliminate debug logging
- Apr 10, 2018 - tested backwards-compatibility using greenlock.js
- Apr 5, 2018 - export http and dns challenge tests
- Apr 5, 2018 - test http and dns challenges (success and failure)
- Apr 5, 2018 - test subdomains and its wildcard
- Apr 5, 2018 - test two subdomains
- Apr 5, 2018 - test wildcard
- Apr 5, 2018 - completely match api for acme v1 (le-acme-core.js)
- Mar 21, 2018 - _mostly_ matches le-acme-core.js API
- Mar 21, 2018 - can now accept values (not hard coded)
- Mar 20, 2018 - SUCCESS - got a test certificate (hard-coded)
- Mar 20, 2018 - download certificate
- Mar 20, 2018 - poll for status
- Mar 20, 2018 - finalize order (submit csr)
- Mar 20, 2018 - generate domain keypair
- Mar 20, 2018 - respond to challenges
- Mar 16, 2018 - get challenges
- Mar 16, 2018 - new order
- Mar 15, 2018 - create account
- Mar 15, 2018 - generate account keypair
- Mar 15, 2018 - get nonce
- Mar 15, 2018 - get directory
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
'use strict';
var A = module.exports;
var U = require('./utils.js');
var Keypairs = require('@root/keypairs');
var Enc = require('@root/encoding/bytes');
var agreers = {};
A._getAccountKid = function(me, options) {
// It's just fine if there's no account, we'll go get the key id we need via the existing key
var kid =
options.kid ||
(options.account && (options.account.key && options.account.key.kid));
if (kid) {
return Promise.resolve(kid);
}
//return Promise.reject(new Error("must include KeyID"));
// This is an idempotent request. It'll return the same account for the same public key.
return A._registerAccount(me, options).then(function(account) {
return account.key.kid;
});
};
// ACME RFC Section 7.3 Account Creation
/*
{
"protected": base64url({
"alg": "ES256",
"jwk": {...},
"nonce": "6S8IqOGY7eL2lsGoTZYifg",
"url": "https://example.com/acme/new-account"
}),
"payload": base64url({
"termsOfServiceAgreed": true,
"onlyReturnExisting": false,
"contact": [
"mailto:cert-admin@example.com",
"mailto:admin@example.com"
]
}),
"signature": "RZPOnYoPs1PhjszF...-nh6X1qtOFPB519I"
}
*/
A._registerAccount = function(me, options) {
//#console.debug('[ACME.js] accounts.create');
function agree(agreed) {
var err;
if (!agreed) {
err = new Error("must agree to '" + me._tos + "'");
err.code = 'E_AGREE_TOS';
throw err;
}
return true;
}
function getAccount() {
return U._importKeypair(options.accountKey).then(function(pair) {
var contact;
if (options.contact) {
contact = options.contact.slice(0);
} else if (options.subscriberEmail) {
contact = ['mailto:' + options.subscriberEmail];
}
var accountRequest = {
termsOfServiceAgreed: true,
onlyReturnExisting: false,
contact: contact
};
var pub = pair.public;
return attachExtAcc(pub, accountRequest).then(function(accReq) {
var payload = JSON.stringify(accReq);
return U._jwsRequest(me, {
accountKey: options.accountKey,
url: me._directoryUrls.newAccount,
protected: { kid: false, jwk: pair.public },
payload: Enc.strToBuf(payload)
}).then(function(resp) {
var account = resp.body;
if (resp.statusCode < 200 || resp.statusCode >= 300) {
if ('string' !== typeof account) {
account = JSON.stringify(account);
}
throw new Error(
'account error: ' +
resp.statusCode +
' ' +
account +
'\n' +
payload
);
}
// the account id url is the "kid"
var kid = resp.headers.location;
if (!account) {
account = { _emptyResponse: true };
}
if (!account.key) {
account.key = {};
}
account.key.kid = kid;
return account;
});
});
});
}
// for external accounts (probably useless, but spec'd)
function attachExtAcc(pubkey, accountRequest) {
if (!options.externalAccount) {
return Promise.resolve(accountRequest);
}
return Keypairs.signJws({
// TODO is HMAC the standard, or is this arbitrary?
secret: options.externalAccount.secret,
protected: {
alg: options.externalAccount.alg || 'HS256',
kid: options.externalAccount.id,
url: me._directoryUrls.newAccount
},
payload: Enc.strToBuf(JSON.stringify(pubkey))
}).then(function(jws) {
accountRequest.externalAccountBinding = jws;
return accountRequest;
});
}
return Promise.resolve()
.then(function() {
//#console.debug('[ACME.js] agreeToTerms');
var agreeToTerms = options.agreeToTerms;
if (!agreeToTerms) {
agreeToTerms = function(terms) {
if (agreers[options.subscriberEmail]) {
return true;
}
agreers[options.subscriberEmail] = true;
console.info();
console.info(
'By using this software you (' +
options.subscriberEmail +
') are agreeing to the following:'
);
console.info(
'ACME Subscriber Agreement:',
terms.acmeSubscriberTermsUrl
);
console.info(
'Greenlock/ACME.js Terms of Use:',
terms.acmeJsTermsUrl
);
console.info();
return true;
};
} else if (true === agreeToTerms) {
agreeToTerms = function(terms) {
return terms && true;
};
}
return agreeToTerms({
acmeSubscriberTermsUrl: me._tos,
acmeJsTermsUrl: 'https://rootprojects.org/legal/#terms'
});
})
.then(agree)
.then(getAccount);
};
This diff is collapsed. Click to expand it.
#!/usr/bin/env node
(async function() {
'use strict';
var UglifyJS = require('uglify-js');
var path = require('path');
var fs = require('fs');
var promisify = require('util').promisify;
var readFile = promisify(fs.readFile);
var writeFile = promisify(fs.writeFile);
var gzip = promisify(require('zlib').gzip);
// The order is specific, and it matters
var files = await Promise.all(
[
'../lib/encoding.js',
'../lib/asn1-packer.js',
'../lib/x509.js',
'../lib/ecdsa.js',
'../lib/rsa.js',
'../lib/keypairs.js',
'../lib/asn1-parser.js',
'../lib/csr.js',
'../lib/acme.js'
].map(async function(file) {
return (await readFile(path.join(__dirname, file), 'utf8')).trim();
})
);
var header =
[
'// Copyright 2015-2019 AJ ONeal. All rights reserved',
'/* This Source Code Form is subject to the terms of the Mozilla Public',
' * License, v. 2.0. If a copy of the MPL was not distributed with this',
' * file, You can obtain one at http://mozilla.org/MPL/2.0/. */'
].join('\n') + '\n';
var file = header + files.join('\n') + '\n';
await writeFile(path.join(__dirname, '../dist', 'acme.js'), file);
await writeFile(
path.join(__dirname, '../dist', 'acme.js.gz'),
await gzip(file)
);
// TODO source maps?
var result = UglifyJS.minify(file, {
compress: true,
// mangling doesn't save significant
mangle: false
});
if (result.error) {
throw result.error;
}
file = header + result.code;
await writeFile(path.join(__dirname, '../dist', 'acme.min.js'), file);
await writeFile(
path.join(__dirname, '../dist', 'acme.min.js.gz'),
await gzip(file)
);
})();
'use strict';
var E = module.exports;
E.NO_SUITABLE_CHALLENGE = function(domain, challenges, presenters) {
// Bail with a descriptive message if no usable challenge could be selected
// For example, wildcards require dns-01 and, if we don't have that, we have to bail
var enabled = presenters.join(', ') || 'none';
var suitable =
challenges
.map(function(r) {
return r.type;
})
.join(', ') || 'none';
return new Error(
"None of the challenge types that you've enabled ( " +
enabled +
' )' +
" are suitable for validating the domain you've selected (" +
domain +
').' +
' You must enable one of ( ' +
suitable +
' ).'
);
};
E.UNHANDLED_ORDER_STATUS = function(options, domains, resp) {
return new Error(
"Didn't finalize order: Unhandled status '" +
resp.body.status +
"'." +
' This is not one of the known statuses...\n' +
"Requested: '" +
options.domains.join(', ') +
"'\n" +
"Validated: '" +
domains.join(', ') +
"'\n" +
JSON.stringify(resp.body, null, 2) +
'\n\n' +
'Please open an issue at https://git.rootprojects.org/root/acme.js'
);
};
E.DOUBLE_READY_ORDER = function(options, domains, resp) {
return new Error(
"Did not finalize order: status 'ready'." +
" Hmmm... this state shouldn't be possible here. That was the last state." +
" This one should at least be 'processing'.\n" +
"Requested: '" +
options.domains.join(', ') +
"'\n" +
"Validated: '" +
domains.join(', ') +
"'\n" +
JSON.stringify(resp.body, null, 2) +
'\n\n' +
'Please open an issue at https://git.rootprojects.org/root/acme.js'
);
};
E.ORDER_INVALID = function(options, domains, resp) {
return new Error(
"Did not finalize order: status 'invalid'." +
' Best guess: One or more of the domain challenges could not be verified' +
' (or the order was canceled).\n' +
"Requested: '" +
options.domains.join(', ') +
"'\n" +
"Validated: '" +
domains.join(', ') +
"'\n" +
JSON.stringify(resp.body, null, 2)
);
};
E.NO_AUTHORIZATIONS = function(options, resp) {
return new Error(
"[acme-v2.js] authorizations were not fetched for '" +
options.domains.join() +
"':\n" +
JSON.stringify(resp.body)
);
};
'use strict';
var native = module.exports;
native._canCheck = function(me) {
me._canCheck = {};
return me
.request({ url: me._baseUrl + '/api/_acme_api_/' })
.then(function(resp) {
if (resp.body.success) {
me._canCheck['http-01'] = true;
me._canCheck['dns-01'] = true;
}
})
.catch(function() {
// ignore
});
};
native._dns01 = function(me, ch) {
return me
.request({
url: me._baseUrl + '/api/dns/' + ch.dnsHost + '?type=TXT'
})
.then(function(resp) {
var err;
if (!resp.body || !Array.isArray(resp.body.answer)) {
err = new Error('failed to get DNS response');
console.error(err);
throw err;
}
if (!resp.body.answer.length) {
err = new Error('failed to get DNS answer record in response');
console.error(err);
throw err;
}
return {
answer: resp.body.answer.map(function(ans) {
return { data: ans.data, ttl: ans.ttl };
})
};
});
};
native._http01 = function(me, ch) {
var url = encodeURIComponent(ch.challengeUrl);
return me
.request({
url: me._baseUrl + '/api/http?url=' + url
})
.then(function(resp) {
return resp.body;
});
};
'use strict';
var UserAgent = module.exports;
UserAgent.get = function() {
return false;
};
'use strict';
var http = module.exports;
http.request = function(opts) {
opts.cors = true;
return window.fetch(opts.url, opts).then(function(resp) {
var headers = {};
var result = {
statusCode: resp.status,
headers: headers,
toJSON: function() {
return this;
}
};
Array.from(resp.headers.entries()).forEach(function(h) {
headers[h[0]] = h[1];
});
if (!headers['content-type']) {
return result;
}
if (/json/.test(headers['content-type'])) {
return resp.json().then(function(json) {
result.body = json;
return result;
});
}
return resp.text().then(function(txt) {
result.body = txt;
return result;
});
});
};
'use strict';
var sha2 = module.exports;
var encoder = new TextEncoder();
sha2.sum = function(alg, str) {
var data = str;
if ('string' === typeof data) {
data = encoder.encode(str);
}
var sha = 'SHA-' + String(alg).replace(/^sha-?/i, '');
return window.crypto.subtle.digest(sha, data);
};
'use strict';
var native = module.exports;
var promisify = require('util').promisify;
var resolveTxt = promisify(require('dns').resolveTxt);
var crypto = require('crypto');
native._canCheck = function(me) {
me._canCheck = {};
me._canCheck['http-01'] = true;
me._canCheck['dns-01'] = true;
return Promise.resolve();
};
native._dns01 = function(me, ch) {
// TODO use digd.js
return resolveTxt(ch.dnsHost).then(function(records) {
return {
answer: records.map(function(rr) {
return {
data: rr
};
})
};
});
};
native._http01 = function(me, ch) {
return new me.request({
url: ch.challengeUrl
}).then(function(resp) {
return resp.body;
});
};
// the hashcash here is for browser parity only
// basically we ask the client to find a needle in a haystack
// (very similar to CloudFlare's api protection)
native._hashcash = function(ch) {
if (!ch || !ch.nonce) {
ch = { nonce: 'xxx' };
}
return Promise.resolve()
.then(function() {
// only get easy answers
var len = ch.needle.length;
var start = ch.start || 0;
var end = ch.end || Math.ceil(len / 2);
var window = parseInt(end - start, 10) || 0;
var maxLen = 6;
var maxTries = Math.pow(2, maxLen * 8);
if (
len > maxLen ||
window < Math.ceil(len / 2) ||
ch.needle.toLowerCase() !== ch.needle ||
ch.alg !== 'SHA-256'
) {
// bail unless the server is issuing very easy challenges
throw new Error('possible and easy answers only, please');
}
var haystack;
var i;
var answer;
var needle = Buffer.from(ch.needle, 'hex');
for (i = 0; i < maxTries; i += 1) {
answer = i.toString(16);
if (answer.length % 2) {
answer = '0' + answer;
}
haystack = crypto
.createHash('sha256')
.update(Buffer.from(ch.nonce + answer, 'hex'))
.digest()
.slice(ch.start, ch.end);
if (-1 !== haystack.indexOf(needle)) {
return ch.nonce + ':' + answer;
}
}
return ch.nonce + ':xxx';
})
.catch(function() {
//console.log('[debug]', err);
// ignore any error
return ch.nonce + ':xxx';
});
};
'use strict';
var os = require('os');
var ver = require('../../package.json').version;
var UserAgent = module.exports;
UserAgent.get = function(me) {
// ACME clients MUST have an RFC7231-compliant User-Agent
// ex: Greenlock/v3 ACME.js/v3 node/v12.0.0 darwin/17.7.0 Darwin/x64
//
// See https://tools.ietf.org/html/rfc8555#section-6.1
// And https://tools.ietf.org/html/rfc7231#section-5.5.3
// And https://community.letsencrypt.org/t/user-agent-flag-explained/3843/2
var ua =
'ACME.js/' +
ver +
' ' +
process.release.name +
'/' +
process.version +
' ' +
os.platform() +
'/' +
os.release() +
' ' +
os.type() +
'/' +
process.arch;
var pkg = me.packageAgent;
if (pkg) {
ua = pkg + ' ' + ua;
}
return ua;
};
'use strict';
var http = module.exports;
var promisify = require('util').promisify;
var request = promisify(require('@root/request'));
http.request = function(opts) {
return request(opts);
};
/* global Promise */
'use strict';
var sha2 = module.exports;
var crypto = require('crypto');
sha2.sum = function(alg, str) {
return Promise.resolve().then(function() {
var sha = 'sha' + String(alg).replace(/^sha-?/i, '');
// utf8 is the default for strings
var buf = Buffer.from(str);
return crypto
.createHash(sha)
.update(buf)
.digest();
});
};
'use strict';
var M = module.exports;
var native = require('./lib/native.js');
// Keep track of active maintainers so that we know who to inform if
// something breaks or has a serious bug or flaw.
var oldCollegeTries = {};
M.init = function(me) {
if (oldCollegeTries[me.maintainerEmail]) {
return;
}
var tz = '';
try {
// Use timezone to stagger messages to maintainers
tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
} catch (e) {
// ignore node versions with no or incomplete Intl
}
// Use locale to know what language to use
var env = process.env;
var locale = env.LC_ALL || env.LC_MESSAGES || env.LANG || env.LANGUAGE;
try {
M._init(me, tz, locale);
} catch (e) {
//console.log(e);
// ignore
}
};
M._init = function(me, tz, locale) {
// prevent a stampede from misconfigured clients in an eternal loop
setTimeout(function() {
me.request({
method: 'GET',
url: 'https://api.rootprojects.org/api/nonce',
json: true
})
.then(function(resp) {
// in the browser this will work until solved, but in
// node this will bail unless the challenge is trivial
return native._hashcash(resp.body || {});
})
.then(function(hashcash) {
var req = {
headers: {
'x-root-nonce-v1': hashcash
},
method: 'POST',
url:
'https://api.rootprojects.org/api/projects/ACME.js/dependents',
json: {
maintainer: me.maintainerEmail,
package: me.packageAgent,
tz: tz,
locale: locale
}
};
return me
.request(req)
.catch(function(err) {
if (me.debug) {
console.error(
'error adding maintainer to support notices:'
);
console.error(err);
}
})
.then(function(/*resp*/) {
oldCollegeTries[me.maintainerEmail] = true;
//console.log(resp);
});
});
}, me.__timeout || 3000);
};
if (require.main === module) {
var ACME = require('./');
var acme = ACME.create({
maintainerEmail: 'aj+acme-test@rootprojects.org',
packageAgent: 'test/v0',
__timeout: 100
});
M.init(acme);
}
{
"_from": "@root/acme@^3.0.8",
"_id": "@root/acme@3.0.8",
"_inBundle": false,
"_integrity": "sha512-VmBvLvWdCDkolkanI9Dzm1ouSWPaAa2eCCwcDZcVQbWoNiUIOqbbd57fcMA/gZxLyuJPStD2WXFuEuSMPDxcww==",
"_location": "/@root/acme",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "@root/acme@^3.0.8",
"name": "@root/acme",
"escapedName": "@root%2facme",
"scope": "@root",
"rawSpec": "^3.0.8",
"saveSpec": null,
"fetchSpec": "^3.0.8"
},
"_requiredBy": [
"/@root/greenlock"
],
"_resolved": "https://registry.npmjs.org/@root/acme/-/acme-3.0.8.tgz",
"_shasum": "7ab1501b7e9e7aa25c59f4833862fd078d8f39c4",
"_spec": "@root/acme@^3.0.8",
"_where": "C:\\Users\\USER\\Documents\\Desktop\\옵소팀플\\LINEBOT\\node_modules\\@root\\greenlock",
"author": {
"name": "AJ ONeal",
"email": "coolaj86@gmail.com",
"url": "https://coolaj86.com/"
},
"browser": {
"./lib/native.js": "./lib/browser.js",
"./lib/node/sha2.js": "./lib/browser/sha2.js",
"./lib/node/http.js": "./lib/browser/http.js",
"./lib/node/client-user-agent.js": "./lib/browser/client-user-agent.js"
},
"bundleDependencies": false,
"dependencies": {
"@root/encoding": "^1.0.1",
"@root/keypairs": "^0.9.0",
"@root/pem": "^1.0.4",
"@root/request": "^1.3.11",
"@root/x509": "^0.7.2"
},
"deprecated": false,
"description": "Free SSL certificates for Node.js and Browsers. Issued via Let's Encrypt",
"devDependencies": {
"@root/csr": "^0.8.1",
"dig.js": "^1.3.9",
"dns-suite": "^1.2.13",
"dotenv": "^8.1.0",
"punycode": "^1.4.1"
},
"files": [
"*.js",
"lib",
"bin",
"scripts",
"dist"
],
"homepage": "https://rootprojects.org/acme/",
"keywords": [
"ACME",
"Let's Encrypt",
"EC",
"RSA",
"CSR",
"browser",
"greenlock",
"VanillaJS",
"ZeroSSL"
],
"license": "MPL-2.0",
"main": "acme.js",
"name": "@root/acme",
"repository": {
"type": "git",
"url": "https://git.rootprojects.org/root/acme.js.git"
},
"scripts": {
"build": "node_xxx bin/bundle.js",
"lint": "jshint lib bin",
"postinstall": "node scripts/postinstall",
"start": "node server.js",
"test": "node server.js"
},
"trulyOptionalDependencies": {
"eslint": "^6.5.1",
"webpack": "^4.41.0",
"webpack-cli": "^3.3.9"
},
"version": "3.0.8"
}
#!/usr/bin/env node
'use strict';
// TODO put postinstall message
'use strict';
var U = module.exports;
var Keypairs = require('@root/keypairs');
var UserAgent = require('./lib/node/client-user-agent.js');
// Handle nonce, signing, and request altogether
U._jwsRequest = function(me, bigopts) {
return U._getNonce(me).then(function(nonce) {
bigopts.protected.nonce = nonce;
bigopts.protected.url = bigopts.url;
// protected.alg: added by Keypairs.signJws
if (!bigopts.protected.jwk) {
// protected.kid must be overwritten due to ACME's interpretation of the spec
if (!('kid' in bigopts.protected)) {
bigopts.protected.kid = bigopts.kid;
}
}
// this will shasum the thumbprint the 2nd time
return Keypairs.signJws({
jwk: bigopts.accountKey,
protected: bigopts.protected,
payload: bigopts.payload
})
.then(function(jws) {
//#console.debug('[ACME.js] url: ' + bigopts.url + ':');
//#console.debug(jws);
return U._request(me, { url: bigopts.url, json: jws });
})
.catch(function(e) {
if (/badNonce$/.test(e.urn)) {
// retry badNonces
var retryable = bigopts._retries >= 2;
if (!retryable) {
bigopts._retries = (bigopts._retries || 0) + 1;
return U._jwsRequest(me, bigopts);
}
}
throw e;
});
});
};
U._getNonce = function(me) {
var nonce;
while (true) {
nonce = me._nonces.shift();
if (!nonce) {
break;
}
if (Date.now() - nonce.createdAt > 15 * 60 * 1000) {
nonce = null;
} else {
break;
}
}
if (nonce) {
return Promise.resolve(nonce.nonce);
}
// HEAD-as-HEAD ok
return U._request(me, {
method: 'HEAD',
url: me._directoryUrls.newNonce
}).then(function(resp) {
return resp.headers['replay-nonce'];
});
};
// Handle some ACME-specific defaults
U._request = function(me, opts) {
// no-op on browser
var ua = UserAgent.get(me, opts);
// Note: the required User-Agent string will be set in node, but not browsers
if (!opts.headers) {
opts.headers = {};
}
if (ua && !opts.headers['User-Agent']) {
opts.headers['User-Agent'] = ua;
}
if (opts.json) {
opts.headers.Accept = 'application/json';
if (true !== opts.json) {
opts.body = JSON.stringify(opts.json);
}
if (/*opts.jose ||*/ opts.json.protected) {
opts.headers['Content-Type'] = 'application/jose+json';
}
}
if (!opts.method) {
opts.method = 'GET';
if (opts.body) {
opts.method = 'POST';
}
}
//console.log('\n[debug] REQUEST');
//console.log(opts);
return me.__request(opts).then(function(resp) {
if (resp.toJSON) {
resp = resp.toJSON();
}
if (resp.headers['replay-nonce']) {
U._setNonce(me, resp.headers['replay-nonce']);
}
//console.log('[debug] RESPONSE:');
//console.log(resp.headers);
//console.log(resp.body);
var e;
var err;
if (resp.body) {
err = resp.body.error;
e = new Error('');
if (400 === resp.body.status) {
err = { type: resp.body.type, detail: resp.body.detail };
}
if (err) {
e.status = resp.body.status;
e.code = 'E_ACME';
if (e.status) {
e.message = '[' + e.status + '] ';
}
e.detail = err.detail;
e.message += err.detail || JSON.stringify(err);
e.urn = err.type;
e.uri = resp.body.url;
e._rawError = err;
e._rawBody = resp.body;
throw e;
}
}
return resp;
});
};
U._setNonce = function(me, nonce) {
me._nonces.unshift({ nonce: nonce, createdAt: Date.now() });
};
U._importKeypair = function(key) {
var p;
var pub;
if (key && key.kty) {
// nix the browser jwk extras
key.key_ops = undefined;
key.ext = undefined;
pub = Keypairs.neuter({ jwk: key });
p = Promise.resolve({
private: key,
public: pub
});
} else if ('string' === typeof key) {
p = Keypairs.import({ pem: key });
} else {
throw new Error('no private key given');
}
return p.then(function(pair) {
if (pair.public.kid) {
pair = JSON.parse(JSON.stringify(pair));
delete pair.public.kid;
delete pair.private.kid;
}
return pair;
});
};
'use strict';
var path = require('path');
module.exports = {
entry: './examples/app.js',
//entry: './acme.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'app.js'
//filename: 'acme.js',
//library: '@root/acme',
//libraryTarget: 'umd'
//globalObject: "typeof self !== 'undefined' ? self : this"
},
resolve: {
aliasFields: ['webpack', 'browser'],
mainFields: ['browser', 'main']
}
};
This diff is collapsed. Click to expand it.
# @root/asn1
Built by [The Root Company](https://therootcompany.com)
for [Greenlock](https://greenlock.domains)
and [ACME.js](https://git.rootprojects.org/root/acme.js)
Lightweight, Zero-Dependency ASN.1 encoder and decoder for Node.js and Browsers,
in less than 300 lines of vanilla JavaScript
| 1.6k gzipped
| 4.2k minified
| 8.4k pretty
|
- [x] Zero External Dependencies
- [x] Universal Support
- [x] Node.js
- [x] Browsers
- [x] Vanilla JS
This ASN.1 codec is built for simplicity. It encodes into DER format
and decodes into a simple, classless Array of Arrays and values.
Most people don't actually want to work with ANS.1 directly,
but rather intend to work with pre-defined x509 schemas.
If you're **most people**, you're actually looking for one or more of these:
- [pem.js](https://git.rootprojects.org/root/pem.js)
- [x509.js](https://git.rootprojects.org/root/x509.js)
- [csr.js](https://git.rootprojects.org/root/csr.js)
- [keypairs.js](https://git.rootprojects.org/root/keypairs.js)
- [encoding.js](https://git.rootprojects.org/root/encoding.js)
Want to [contribute](#contributions)?
Need [commercial support](#commercial-support)?
# Usage
ASN.1 DER consists values which have
- a type (2-bit class, 6-bit tag)
- a coded length
- zero or more values
Common types include:
```txt
0x30 SEQUENCE
0x02 INTEGER*
0x03 BIT STRING**
0x04 OCTET STRING
0x05 NULL
0x06 OBJECT IDENTIFIER
0x0C UTF8String
0x16 IA5String (ASCII)
0x17 UTCTime
0x31 SET
0xA0 context-specific***
0xA3 context-specific***
```
<small>\* INTEGERS are always BigInt-encoded (a leading '00' for positive numbers with a 1 in the most-significant-bit position)</small>
<small>\*\*BIT STRINGS have a leading "bit mask" which, for all practical purposes, is actually _always_ '00'</small>
<small>\*\*\* See <https://stackoverflow.com/a/15071901/151312></small>
The core value in this library is that it:
- correctly sums the byte length of children elements
- correctly encodes BigInts
## Parser Usage
There are three options:
- `der` (required) - the input bytes as a buffer
- `json` (default) - returns hex strings for values, rather than buffers
- `verbose` - returns a more human-friendly object that is useful for debugging
```js
ASN1.parse({ der: `<Buffer>`, json: true, verbose: true });
```
Default (hex) output:
```js
[
'30',
[
['02', '01'],
['04', '2c8996...'],
['a0', [['06', '2a8648...']]],
['a1', [['03', '04bdd8...']]]
]
];
```
Verbose output:
```js
{ type: 48,
lengthSize: 0,
length: 119,
children:
[ { type: 2, lengthSize: 0, length: 1, value: <Buffer 01> },
{ type: 4,
lengthSize: 0,
length: 32,
value:
<Buffer 2c 89 96 ...>,
children: [] },
{ type: 160, lengthSize: 0, length: 10, children: [Array] },
{ type: 161, lengthSize: 0, length: 68, children: [Array] } ] }
```
## Packer Usage
You can use either of two syntaxes. One is much easier to read than the other.
Ironically, hex strings are used in place of buffers for efficiency.
```js
ASN1.Any(hexType, hexBytes1, hexBytes2, ...);
ASN1.UInt(hexBigInt);
ASN1.BitStr(hexBitStream);
```
In practice, you'll be cascading the objects into a final hex string:
```
// result is a hex-encoded DER
var der = hexToBuf(
ASN1.Any('30' // Sequence
, ASN1.UInt('01') // Integer (Version 1)
, ASN1.Any('04', '07CAD7...') // Octet String
, ASN1.Any('A0', '06082A...') // [0] Object ID (context-specific)
, ASN1.Any('A1', // [1] (context-specific value)
ASN1.BitStr('04BDD8...')
)
)
);
```
Alternatively you can pack either the sparse array or verbose object, using hex strings or buffers:
- `json` when set to true will return a hex-encoded DER rather than a DER buffer
```js
var buf = Uint8Array.from([0x01]);
ASN1.pack(
[
'30',
[
['02', buf],
['04', '07CAD7...'],
['A0', '06082A...'],
['A1', ['03', '04BDD8...']]
]
],
{ json: false }
);
```
```js
var buf = Uint8Array.from([0x01]);
ASN1.pack(
{
type: 48,
children: [
{ type: 2, value: '01' },
{ type: 4, value: '2c 89 96 ...', children: [] },
{ type: 160, children: [...] },
{ type: 161, children: [...] }
]
},
{ json: false }
);
```
# Install
This package contains both node-specific and browser-specific code,
and the `package.json#browser` field ensures that your package manager
will automatically choose the correct code for your environment.
## Node (and Webpack)
```js
npm install -g @root/asn1
```
```js
var asn1 = require('@root/asn1');
```
```js
// just the packer
var asn1 = require('@root/asn1/packer');
// just the parser
var asn1 = require('@root/asn1/parser');
```
## Browsers (Vanilla JS)
```html
<script src="https://unpkg.com/@root/asn1/dist/asn1.all.js"></script>
```
```html
<script src="https://unpkg.com/@root/asn1/dist/asn1.all.min.js"></script>
```
```js
var ASN1 = window.ASN1;
```
# Examples
## Decoding DER to JSON-ASN.1
```js
var PEM = require('@root/pem/packer');
var Enc = require('@root/encoding');
var ASN1 = require('@root/asn1/parser');
```
```js
var pem = [
'-----BEGIN EC PRIVATE KEY-----',
'MHcCAQEEICyJlsaqkx2z9yx0H6rHA0lM3/7jXjxqn/VOhExHDuR6oAoGCCqGSM49',
'AwEHoUQDQgAEvdjQ3T6VBX82LIKDzepYgRsz3HgRwp83yPuonu6vqoshSQRe0Aye',
'mmdXUDX2wTZsmFSjhY9uroRiBbGZrigbKA==',
'-----END EC PRIVATE KEY-----'
].join('\n');
```
```js
var der = PEM.parseBlock(pem).bytes;
var asn1 = ASN1.parse({ der: der, json: true, verbose: false });
```
```json
[
"30",
[
["02", "01"],
[
"04",
"2c8996c6aa931db3f72c741faac703494cdffee35e3c6a9ff54e844c470ee47a"
],
["a0", [["06", "2a8648ce3d030107"]]],
[
"a1",
[
[
"03",
"04bdd8d0dd3e95057f362c8283cdea58811b33dc7811c29f37c8fba89eeeafaa8b2149045ed00c9e9a67575035f6c1366c9854a3858f6eae846205b199ae281b28"
]
]
]
]
]
```
```json
{
"type": 48,
"lengthSize": 0,
"length": 119,
"children": [
{ "type": 2, "lengthSize": 0, "length": 1, "value": "01" },
{
"type": 4,
"lengthSize": 0,
"length": 32,
"value": "2c8996c6aa931db3f72c741faac703494cdffee35e3c6a9ff54e844c470ee47a",
"children": []
},
{
"type": 160,
"lengthSize": 0,
"length": 10,
"children": [
{
"type": 6,
"lengthSize": 0,
"length": 8,
"value": "2a8648ce3d030107"
}
]
},
{
"type": 161,
"lengthSize": 0,
"length": 68,
"children": [
{
"type": 3,
"lengthSize": 0,
"length": 66,
"value": "04bdd8d0dd3e95057f362c8283cdea58811b33dc7811c29f37c8fba89eeeafaa8b2149045ed00c9e9a67575035f6c1366c9854a3858f6eae846205b199ae281b28",
"children": []
}
]
}
]
}
```
## Encoding ASN.1 to DER
Here's an example of an SEC1-encoded EC P-256 Public/Private Keypair:
```js
var ASN1 = require('@root/asn1/packer');
var Enc = require('@root/encoding');
var PEM = require('@root/pem/packer');
```
```js
// 1.2.840.10045.3.1.7
// prime256v1 (ANSI X9.62 named elliptic curve)
var OBJ_ID_EC_256 = '06 08 2A8648CE3D030107';
```
```js
var jwk = {
crv: 'P-256',
d: 'LImWxqqTHbP3LHQfqscDSUzf_uNePGqf9U6ETEcO5Ho',
kty: 'EC',
x: 'vdjQ3T6VBX82LIKDzepYgRsz3HgRwp83yPuonu6vqos',
y: 'IUkEXtAMnppnV1A19sE2bJhUo4WPbq6EYgWxma4oGyg',
kid: 'MnfJYyS9W5gUjrJLdn8ePMzik8ZJz2qc-VZmKOs_oCw'
};
var d = Enc.base64ToHex(jwk.d);
var x = Enc.base64ToHex(jwk.x);
var y = Enc.base64ToHex(jwk.y);
```
```
var der = Enc.hexToBuf(
ASN1.Any('30' // Sequence
, ASN1.UInt('01') // Integer (Version 1)
, ASN1.Any('04', d) // Octet String
, ASN1.Any('A0', OBJ_ID_EC_256) // [0] Object ID
, ASN1.Any('A1', // [1] Embedded EC/ASN1 public key
ASN1.BitStr('04' + x + y)
)
)
);
var pem = PEM.packBlock({
type: 'EC PRIVATE KEY',
bytes: der
});
```
# Disabiguation
`ASN1.Any(typ, hexVal, ...)`
There was once an actual ASN.1 type with the literal name 'Any'.
It was deprecated in 1994 and the `Any` in this API simply means "give any value"
# Contributions
Did this project save you some time? Maybe make your day? Even save the day?
Please say "thanks" via Paypal or Patreon:
- Paypal: [\$5](https://paypal.me/rootprojects/5) | [\$10](https://paypal.me/rootprojects/10) | Any amount: <paypal@therootcompany.com>
- Patreon: <https://patreon.com/rootprojects>
Where does your contribution go?
[Root](https://therootcompany.com) is a collection of experts
who trust each other and enjoy working together on deep-tech,
Indie Web projects.
Our goal is to operate as a sustainable community.
Your contributions - both in code and _especially_ monetarily -
help to not just this project, but also our broader work
of [projects](https://rootprojects.org) that fuel the **Indie Web**.
Also, we chat on [Keybase](https://keybase.io)
in [#rootprojects](https://keybase.io/team/rootprojects)
# Commercial Support
Do you need...
- more features?
- bugfixes, on _your_ timeline?
- custom code, built by experts?
- commercial support and licensing?
Contact <aj@therootcompany.com> for support options.
# Legal
Copyright [AJ ONeal](https://coolaj86.com),
[Root](https://therootcompany.com) 2018-2019
MPL-2.0 |
[Terms of Use](https://therootcompany.com/legal/#terms) |
[Privacy Policy](https://therootcompany.com/legal/#privacy)
This diff is collapsed. Click to expand it.
(function(){"use strict";var Enc=window.Encoding={};Enc.bufToBase64=function(u8){var bin="";u8.forEach(function(i){bin+=String.fromCharCode(i)});return btoa(bin)};Enc.strToBase64=function(str){return btoa(Enc.strToBin(str))};function _base64ToBin(b64){return atob(Enc.urlBase64ToBase64(b64))}Enc._base64ToBin=_base64ToBin;Enc.base64ToBuf=function(b64){return Enc.binToBuf(_base64ToBin(b64))};Enc.base64ToStr=function(b64){return Enc.binToStr(_base64ToBin(b64))};Enc.urlBase64ToBase64=function(u64){var r=u64%4;if(2===r){u64+="=="}else if(3===r){u64+="="}return u64.replace(/-/g,"+").replace(/_/g,"/")};Enc.base64ToUrlBase64=function(b64){return b64.replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")};Enc.bufToUrlBase64=function(buf){return Enc.base64ToUrlBase64(Enc.bufToBase64(buf))};Enc.strToUrlBase64=function(str){return Enc.bufToUrlBase64(Enc.strToBuf(str))};Enc.bufToHex=function(u8){var hex=[];var i,h;var len=u8.byteLength||u8.length;for(i=0;i<len;i+=1){h=u8[i].toString(16);if(2!==h.length){h="0"+h}hex.push(h)}return hex.join("").toLowerCase()};Enc.numToHex=function(d){d=d.toString(16);if(d.length%2){return"0"+d}return d};Enc.strToHex=function(str){return Enc._binToHex(Enc.strToBin(str))};Enc._binToHex=function(bin){return bin.split("").map(function(ch){var h=ch.charCodeAt(0).toString(16);if(2!==h.length){h="0"+h}return h}).join("")};Enc.hexToBuf=function(hex){var arr=[];hex.match(/.{2}/g).forEach(function(h){arr.push(parseInt(h,16))});return"undefined"!==typeof Uint8Array?new Uint8Array(arr):arr};Enc.hexToStr=function(hex){return Enc.binToStr(_hexToBin(hex))};function _hexToBin(hex){return hex.replace(/([0-9A-F]{2})/gi,function(_,p1){return String.fromCharCode("0x"+p1)})}Enc._hexToBin=_hexToBin;Enc.bufToBin=function(buf){var bin="";buf.forEach(function(ch){bin+=String.fromCharCode(ch)});return bin};Enc.strToBin=function(str){var escstr=encodeURIComponent(str);var binstr=escstr.replace(/%([0-9A-F]{2})/g,function(_,p1){return String.fromCharCode("0x"+p1)});return binstr};Enc.binToBuf=function(bin){var arr=bin.split("").map(function(ch){return ch.charCodeAt(0)});return"undefined"!==typeof Uint8Array?new Uint8Array(arr):arr};Enc.strToBuf=function(str){return Enc.binToBuf(Enc.strToBin(str))};Enc.binToStr=function(binstr){var escstr=binstr.replace(/(.)/g,function(m,p){var code=p.charCodeAt(0).toString(16).toUpperCase();if(code.length<2){code="0"+code}return"%"+code});return decodeURIComponent(escstr)};Enc.bufToStr=function(buf){return Enc.binToStr(Enc.bufToBin(buf))};Enc.base64ToHex=function(b64){return Enc.bufToHex(Enc.base64ToBuf(b64))};Enc.hexToBase64=function(hex){return btoa(Enc._hexToBin(hex))}})();(function(){"use strict";var ASN1=window.ASN1={};var Enc=window.Encoding;ASN1.ELOOPN=102;ASN1.ELOOP="uASN1.js Error: iterated over "+ASN1.ELOOPN+"+ elements (probably a malformed file)";ASN1.EDEEPN=60;ASN1.EDEEP="uASN1.js Error: element nested "+ASN1.EDEEPN+"+ layers deep (probably a malformed file)";ASN1.CTYPES=[48,49,160,161];ASN1.VTYPES=[1,2,5,6,12,130];ASN1.parseVerbose=function parseAsn1Helper(buf,opts){if(!opts){opts={}}function parseAsn1(buf,depth,eager){if(depth.length>=ASN1.EDEEPN){throw new Error(ASN1.EDEEP)}var index=2;var asn1={type:buf[0],lengthSize:0,length:buf[1]};var child;var iters=0;var adjust=0;var adjustedLen;if(128&asn1.length){asn1.lengthSize=127&asn1.length;asn1.length=parseInt(Enc.bufToHex(buf.slice(index,index+asn1.lengthSize)),16);index+=asn1.lengthSize}if(0===buf[index]&&(2===asn1.type||3===asn1.type)){if(asn1.length>1){index+=1;adjust=-1}}adjustedLen=asn1.length+adjust;function parseChildren(eager){asn1.children=[];while(iters<ASN1.ELOOPN&&index<2+asn1.length+asn1.lengthSize){iters+=1;depth.length+=1;child=parseAsn1(buf.slice(index,index+adjustedLen),depth,eager);depth.length-=1;index+=2+child.lengthSize+child.length;if(index>2+asn1.lengthSize+asn1.length){if(!eager){console.error(JSON.stringify(asn1,ASN1._replacer,2))}throw new Error("Parse error: child value length ("+child.length+") is greater than remaining parent length ("+(asn1.length-index)+" = "+asn1.length+" - "+index+")")}asn1.children.push(child)}if(index!==2+asn1.lengthSize+asn1.length){throw new Error("premature end-of-file")}if(iters>=ASN1.ELOOPN){throw new Error(ASN1.ELOOP)}delete asn1.value;return asn1}if(-1!==ASN1.CTYPES.indexOf(asn1.type)){return parseChildren(eager)}asn1.value=buf.slice(index,index+adjustedLen);if(opts.json){asn1.value=Enc.bufToHex(asn1.value)}if(-1!==ASN1.VTYPES.indexOf(asn1.type)){return asn1}try{return parseChildren(true)}catch(e){asn1.children.length=0;return asn1}}var asn1=parseAsn1(buf,[]);var len=buf.byteLength||buf.length;if(len!==2+asn1.lengthSize+asn1.length){throw new Error("Length of buffer does not match length of ASN.1 sequence.")}return asn1};ASN1._toArray=function toArray(next,opts){var typ=opts.json?Enc.numToHex(next.type):next.type;var val=next.value;if(val){if("string"!==typeof val&&opts.json){val=Enc.bufToHex(val)}return[typ,val]}return[typ,next.children.map(function(child){return toArray(child,opts)})]};ASN1.parse=function(opts){var opts2={json:false!==opts.json};var verbose=ASN1.parseVerbose(opts.der,opts2);if(opts.verbose){return verbose}return ASN1._toArray(verbose,opts2)};ASN1._replacer=function(k,v){if("type"===k){return"0x"+Enc.numToHex(v)}if(v&&"value"===k){return"0x"+Enc.bufToHex(v.data||v)}return v};function Any(){var args=Array.prototype.slice.call(arguments);var typ=args.shift();var str=args.join("").replace(/\s+/g,"").toLowerCase();var len=str.length/2;var lenlen=0;var hex=typ;if("number"===typeof hex){hex=Enc.numToHex(hex)}if(len!==Math.round(len)){throw new Error("invalid hex")}if(len>127){lenlen+=1;while(len>255){lenlen+=1;len=len>>8}}if(lenlen){hex+=Enc.numToHex(128+lenlen)}return hex+Enc.numToHex(str.length/2)+str}ASN1.Any=Any;ASN1.UInt=function UINT(){var str=Array.prototype.slice.call(arguments).join("");var first=parseInt(str.slice(0,2),16);if(128&first){str="00"+str}return Any("02",str)};ASN1.BitStr=function BITSTR(){var str=Array.prototype.slice.call(arguments).join("");return Any("03","00"+str)};ASN1._toArray=function toArray(next,opts){var typ=opts.json?Enc.numToHex(next.type):next.type;var val=next.value;if(val){if("string"!==typeof val&&opts.json){val=Enc.bufToHex(val)}return[typ,val]}return[typ,next.children.map(function(child){return toArray(child,opts)})]};ASN1._pack=function(arr){var typ=arr[0];if("number"===typeof arr[0]){typ=Enc.numToHex(arr[0])}var str="";if(Array.isArray(arr[1])){arr[1].forEach(function(a){str+=ASN1._pack(a)})}else if("string"===typeof arr[1]){str=arr[1]}else if(arr[1].byteLength){str=Enc.bufToHex(arr[1])}else{throw new Error("unexpected array")}if("03"===typ){return ASN1.BitStr(str)}else if("02"===typ){return ASN1.UInt(str)}else{return Any(typ,str)}};ASN1.pack=function(asn1,opts){if(!opts){opts={}}if(!Array.isArray(asn1)){asn1=ASN1._toArray(asn1,{json:true})}var result=ASN1._pack(asn1);if(opts.json){return result}return Enc.hexToBuf(result)}})();
;(function () {
'use strict';
var ASN1 = window.ASN1 = {};
var Enc = window.Encoding;
// Copyright 2018 AJ ONeal. All rights reserved
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//
// Parser
//
// Although I've only seen 9 max in https certificates themselves,
// but each domain list could have up to 100
ASN1.ELOOPN = 102;
ASN1.ELOOP =
'uASN1.js Error: iterated over ' +
ASN1.ELOOPN +
'+ elements (probably a malformed file)';
// I've seen https certificates go 29 deep
ASN1.EDEEPN = 60;
ASN1.EDEEP =
'uASN1.js Error: element nested ' +
ASN1.EDEEPN +
'+ layers deep (probably a malformed file)';
// Container Types are Sequence 0x30, Container Array? (0xA0, 0xA1)
// Value Types are Boolean 0x01, Integer 0x02, Null 0x05, Object ID 0x06, String 0x0C, 0x16, 0x13, 0x1e Value Array? (0x82)
// Bit String (0x03) and Octet String (0x04) may be values or containers
// Sometimes Bit String is used as a container (RSA Pub Spki)
ASN1.CTYPES = [0x30, 0x31, 0xa0, 0xa1];
ASN1.VTYPES = [0x01, 0x02, 0x05, 0x06, 0x0c, 0x82];
ASN1.parseVerbose = function parseAsn1Helper(buf, opts) {
if (!opts) {
opts = {};
}
//var ws = ' ';
function parseAsn1(buf, depth, eager) {
if (depth.length >= ASN1.EDEEPN) {
throw new Error(ASN1.EDEEP);
}
var index = 2; // we know, at minimum, data starts after type (0) and lengthSize (1)
var asn1 = { type: buf[0], lengthSize: 0, length: buf[1] };
var child;
var iters = 0;
var adjust = 0;
var adjustedLen;
// Determine how many bytes the length uses, and what it is
if (0x80 & asn1.length) {
asn1.lengthSize = 0x7f & asn1.length;
// I think that buf->hex->int solves the problem of Endianness... not sure
asn1.length = parseInt(
Enc.bufToHex(buf.slice(index, index + asn1.lengthSize)),
16
);
index += asn1.lengthSize;
}
// High-order bit Integers have a leading 0x00 to signify that they are positive.
// Bit Streams use the first byte to signify padding, which x.509 doesn't use.
if (0x00 === buf[index] && (0x02 === asn1.type || 0x03 === asn1.type)) {
// However, 0x00 on its own is a valid number
if (asn1.length > 1) {
index += 1;
adjust = -1;
}
}
adjustedLen = asn1.length + adjust;
//console.warn(depth.join(ws) + '0x' + Enc.numToHex(asn1.type), index, 'len:', asn1.length, asn1);
function parseChildren(eager) {
asn1.children = [];
//console.warn('1 len:', (2 + asn1.lengthSize + asn1.length), 'idx:', index, 'clen:', 0);
while (
iters < ASN1.ELOOPN &&
index < 2 + asn1.length + asn1.lengthSize
) {
iters += 1;
depth.length += 1;
child = parseAsn1(
buf.slice(index, index + adjustedLen),
depth,
eager
);
depth.length -= 1;
// The numbers don't match up exactly and I don't remember why...
// probably something with adjustedLen or some such, but the tests pass
index += 2 + child.lengthSize + child.length;
//console.warn('2 len:', (2 + asn1.lengthSize + asn1.length), 'idx:', index, 'clen:', (2 + child.lengthSize + child.length));
if (index > 2 + asn1.lengthSize + asn1.length) {
if (!eager) {
console.error(JSON.stringify(asn1, ASN1._replacer, 2));
}
throw new Error(
'Parse error: child value length (' +
child.length +
') is greater than remaining parent length (' +
(asn1.length - index) +
' = ' +
asn1.length +
' - ' +
index +
')'
);
}
asn1.children.push(child);
//console.warn(depth.join(ws) + '0x' + Enc.numToHex(asn1.type), index, 'len:', asn1.length, asn1);
}
if (index !== 2 + asn1.lengthSize + asn1.length) {
//console.warn('index:', index, 'length:', (2 + asn1.lengthSize + asn1.length));
throw new Error('premature end-of-file');
}
if (iters >= ASN1.ELOOPN) {
throw new Error(ASN1.ELOOP);
}
delete asn1.value;
return asn1;
}
// Recurse into types that are _always_ containers
if (-1 !== ASN1.CTYPES.indexOf(asn1.type)) {
return parseChildren(eager);
}
// Return types that are _always_ values
asn1.value = buf.slice(index, index + adjustedLen);
if (opts.json) {
asn1.value = Enc.bufToHex(asn1.value);
}
if (-1 !== ASN1.VTYPES.indexOf(asn1.type)) {
return asn1;
}
// For ambigious / unknown types, recurse and return on failure
// (and return child array size to zero)
try {
return parseChildren(true);
} catch (e) {
asn1.children.length = 0;
return asn1;
}
}
var asn1 = parseAsn1(buf, []);
var len = buf.byteLength || buf.length;
if (len !== 2 + asn1.lengthSize + asn1.length) {
throw new Error(
'Length of buffer does not match length of ASN.1 sequence.'
);
}
return asn1;
};
ASN1._toArray = function toArray(next, opts) {
var typ = opts.json ? Enc.numToHex(next.type) : next.type;
var val = next.value;
if (val) {
if ('string' !== typeof val && opts.json) {
val = Enc.bufToHex(val);
}
return [typ, val];
}
return [
typ,
next.children.map(function(child) {
return toArray(child, opts);
})
];
};
ASN1.parse = function(opts) {
var opts2 = { json: false !== opts.json };
var verbose = ASN1.parseVerbose(opts.der, opts2);
if (opts.verbose) {
return verbose;
}
return ASN1._toArray(verbose, opts2);
};
ASN1._replacer = function(k, v) {
if ('type' === k) {
return '0x' + Enc.numToHex(v);
}
if (v && 'value' === k) {
return '0x' + Enc.bufToHex(v.data || v);
}
return v;
};
//
// Packer
//
// Almost every ASN.1 type that's important for CSR
// can be represented generically with only a few rules.
function Any(/*type, hexstrings...*/) {
var args = Array.prototype.slice.call(arguments);
var typ = args.shift();
var str = args
.join('')
.replace(/\s+/g, '')
.toLowerCase();
var len = str.length / 2;
var lenlen = 0;
var hex = typ;
if ('number' === typeof hex) {
hex = Enc.numToHex(hex);
}
// We can't have an odd number of hex chars
if (len !== Math.round(len)) {
throw new Error('invalid hex');
}
// The first byte of any ASN.1 sequence is the type (Sequence, Integer, etc)
// The second byte is either the size of the value, or the size of its size
// 1. If the second byte is < 0x80 (128) it is considered the size
// 2. If it is > 0x80 then it describes the number of bytes of the size
// ex: 0x82 means the next 2 bytes describe the size of the value
// 3. The special case of exactly 0x80 is "indefinite" length (to end-of-file)
if (len > 127) {
lenlen += 1;
while (len > 255) {
lenlen += 1;
len = len >> 8;
}
}
if (lenlen) {
hex += Enc.numToHex(0x80 + lenlen);
}
return hex + Enc.numToHex(str.length / 2) + str;
}
ASN1.Any = Any;
// The Integer type has some special rules
ASN1.UInt = function UINT() {
var str = Array.prototype.slice.call(arguments).join('');
var first = parseInt(str.slice(0, 2), 16);
// If the first byte is 0x80 or greater, the number is considered negative
// Therefore we add a '00' prefix if the 0x80 bit is set
if (0x80 & first) {
str = '00' + str;
}
return Any('02', str);
};
// The Bit String type also has a special rule
ASN1.BitStr = function BITSTR() {
var str = Array.prototype.slice.call(arguments).join('');
// '00' is a mask of how many bits of the next byte to ignore
return Any('03', '00' + str);
};
ASN1._toArray = function toArray(next, opts) {
var typ = opts.json ? Enc.numToHex(next.type) : next.type;
var val = next.value;
if (val) {
if ('string' !== typeof val && opts.json) {
val = Enc.bufToHex(val);
}
return [typ, val];
}
return [
typ,
next.children.map(function(child) {
return toArray(child, opts);
})
];
};
ASN1._pack = function(arr) {
var typ = arr[0];
if ('number' === typeof arr[0]) {
typ = Enc.numToHex(arr[0]);
}
var str = '';
if (Array.isArray(arr[1])) {
arr[1].forEach(function(a) {
str += ASN1._pack(a);
});
} else if ('string' === typeof arr[1]) {
str = arr[1];
} else if (arr[1].byteLength) {
str = Enc.bufToHex(arr[1]);
} else {
throw new Error('unexpected array');
}
if ('03' === typ) {
return ASN1.BitStr(str);
} else if ('02' === typ) {
return ASN1.UInt(str);
} else {
return Any(typ, str);
}
};
// TODO should this return a buffer?
ASN1.pack = function(asn1, opts) {
if (!opts) {
opts = {};
}
if (!Array.isArray(asn1)) {
asn1 = ASN1._toArray(asn1, { json: true });
}
var result = ASN1._pack(asn1);
if (opts.json) {
return result;
}
return Enc.hexToBuf(result);
};
}());
(function(){"use strict";var ASN1=window.ASN1={};var Enc=window.Encoding;ASN1.ELOOPN=102;ASN1.ELOOP="uASN1.js Error: iterated over "+ASN1.ELOOPN+"+ elements (probably a malformed file)";ASN1.EDEEPN=60;ASN1.EDEEP="uASN1.js Error: element nested "+ASN1.EDEEPN+"+ layers deep (probably a malformed file)";ASN1.CTYPES=[48,49,160,161];ASN1.VTYPES=[1,2,5,6,12,130];ASN1.parseVerbose=function parseAsn1Helper(buf,opts){if(!opts){opts={}}function parseAsn1(buf,depth,eager){if(depth.length>=ASN1.EDEEPN){throw new Error(ASN1.EDEEP)}var index=2;var asn1={type:buf[0],lengthSize:0,length:buf[1]};var child;var iters=0;var adjust=0;var adjustedLen;if(128&asn1.length){asn1.lengthSize=127&asn1.length;asn1.length=parseInt(Enc.bufToHex(buf.slice(index,index+asn1.lengthSize)),16);index+=asn1.lengthSize}if(0===buf[index]&&(2===asn1.type||3===asn1.type)){if(asn1.length>1){index+=1;adjust=-1}}adjustedLen=asn1.length+adjust;function parseChildren(eager){asn1.children=[];while(iters<ASN1.ELOOPN&&index<2+asn1.length+asn1.lengthSize){iters+=1;depth.length+=1;child=parseAsn1(buf.slice(index,index+adjustedLen),depth,eager);depth.length-=1;index+=2+child.lengthSize+child.length;if(index>2+asn1.lengthSize+asn1.length){if(!eager){console.error(JSON.stringify(asn1,ASN1._replacer,2))}throw new Error("Parse error: child value length ("+child.length+") is greater than remaining parent length ("+(asn1.length-index)+" = "+asn1.length+" - "+index+")")}asn1.children.push(child)}if(index!==2+asn1.lengthSize+asn1.length){throw new Error("premature end-of-file")}if(iters>=ASN1.ELOOPN){throw new Error(ASN1.ELOOP)}delete asn1.value;return asn1}if(-1!==ASN1.CTYPES.indexOf(asn1.type)){return parseChildren(eager)}asn1.value=buf.slice(index,index+adjustedLen);if(opts.json){asn1.value=Enc.bufToHex(asn1.value)}if(-1!==ASN1.VTYPES.indexOf(asn1.type)){return asn1}try{return parseChildren(true)}catch(e){asn1.children.length=0;return asn1}}var asn1=parseAsn1(buf,[]);var len=buf.byteLength||buf.length;if(len!==2+asn1.lengthSize+asn1.length){throw new Error("Length of buffer does not match length of ASN.1 sequence.")}return asn1};ASN1._toArray=function toArray(next,opts){var typ=opts.json?Enc.numToHex(next.type):next.type;var val=next.value;if(val){if("string"!==typeof val&&opts.json){val=Enc.bufToHex(val)}return[typ,val]}return[typ,next.children.map(function(child){return toArray(child,opts)})]};ASN1.parse=function(opts){var opts2={json:false!==opts.json};var verbose=ASN1.parseVerbose(opts.der,opts2);if(opts.verbose){return verbose}return ASN1._toArray(verbose,opts2)};ASN1._replacer=function(k,v){if("type"===k){return"0x"+Enc.numToHex(v)}if(v&&"value"===k){return"0x"+Enc.bufToHex(v.data||v)}return v};function Any(){var args=Array.prototype.slice.call(arguments);var typ=args.shift();var str=args.join("").replace(/\s+/g,"").toLowerCase();var len=str.length/2;var lenlen=0;var hex=typ;if("number"===typeof hex){hex=Enc.numToHex(hex)}if(len!==Math.round(len)){throw new Error("invalid hex")}if(len>127){lenlen+=1;while(len>255){lenlen+=1;len=len>>8}}if(lenlen){hex+=Enc.numToHex(128+lenlen)}return hex+Enc.numToHex(str.length/2)+str}ASN1.Any=Any;ASN1.UInt=function UINT(){var str=Array.prototype.slice.call(arguments).join("");var first=parseInt(str.slice(0,2),16);if(128&first){str="00"+str}return Any("02",str)};ASN1.BitStr=function BITSTR(){var str=Array.prototype.slice.call(arguments).join("");return Any("03","00"+str)};ASN1._toArray=function toArray(next,opts){var typ=opts.json?Enc.numToHex(next.type):next.type;var val=next.value;if(val){if("string"!==typeof val&&opts.json){val=Enc.bufToHex(val)}return[typ,val]}return[typ,next.children.map(function(child){return toArray(child,opts)})]};ASN1._pack=function(arr){var typ=arr[0];if("number"===typeof arr[0]){typ=Enc.numToHex(arr[0])}var str="";if(Array.isArray(arr[1])){arr[1].forEach(function(a){str+=ASN1._pack(a)})}else if("string"===typeof arr[1]){str=arr[1]}else if(arr[1].byteLength){str=Enc.bufToHex(arr[1])}else{throw new Error("unexpected array")}if("03"===typ){return ASN1.BitStr(str)}else if("02"===typ){return ASN1.UInt(str)}else{return Any(typ,str)}};ASN1.pack=function(asn1,opts){if(!opts){opts={}}if(!Array.isArray(asn1)){asn1=ASN1._toArray(asn1,{json:true})}var result=ASN1._pack(asn1);if(opts.json){return result}return Enc.hexToBuf(result)}})();
No preview for this file type
<html>
<head>
<meta charset="UTF-8" />
</head>
<body>
<script src="./asn1.all.js"></script>
</body>
</html>
'use strict';
var ASN1 = module.exports;
var packer = require('./packer.js');
var parser = require('./parser.js');
Object.keys(parser).forEach(function(key) {
ASN1[key] = parser[key];
});
Object.keys(packer).forEach(function(key) {
ASN1[key] = packer[key];
});
{
"_from": "@root/asn1@^1.0.0",
"_id": "@root/asn1@1.0.0",
"_inBundle": false,
"_integrity": "sha512-0lfZNuOULKJDJmdIkP8V9RnbV3XaK6PAHD3swnFy4tZwtlMDzLKoM/dfNad7ut8Hu3r91wy9uK0WA/9zym5mig==",
"_location": "/@root/asn1",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "@root/asn1@^1.0.0",
"name": "@root/asn1",
"escapedName": "@root%2fasn1",
"scope": "@root",
"rawSpec": "^1.0.0",
"saveSpec": null,
"fetchSpec": "^1.0.0"
},
"_requiredBy": [
"/@root/csr",
"/@root/x509"
],
"_resolved": "https://registry.npmjs.org/@root/asn1/-/asn1-1.0.0.tgz",
"_shasum": "8748cf7b4497324de91a154b606ca1ddfe97c5d7",
"_spec": "@root/asn1@^1.0.0",
"_where": "C:\\Users\\USER\\Documents\\Desktop\\옵소팀플\\LINEBOT\\node_modules\\@root\\x509",
"author": {
"name": "AJ ONeal",
"email": "coolaj86@gmail.com",
"url": "https://coolaj86.com/"
},
"browser": {
"./node/native.js": "./browser/native.js"
},
"bundleDependencies": false,
"dependencies": {
"@root/encoding": "^1.0.1"
},
"deprecated": false,
"description": "VanillaJS, Lightweight, Zero-Dependency, ASN.1 encoder and decoder.",
"devDependencies": {
"@root/pem": "^1.0.3"
},
"files": [
"*.js",
"node",
"browser",
"dist"
],
"keywords": [
"ASN.1",
"asn1",
"x509",
"PEM"
],
"license": "MPL-2.0",
"main": "index.js",
"name": "@root/asn1",
"repository": {
"type": "git",
"url": "https://git.rootprojects.org/root/asn1.js.git"
},
"scripts": {
"test": "node tests"
},
"version": "1.0.0"
}
'use strict';
var ASN1 = module.exports;
var Enc = require('@root/encoding/hex');
//
// Packer
//
// Almost every ASN.1 type that's important for CSR
// can be represented generically with only a few rules.
function Any(/*type, hexstrings...*/) {
var args = Array.prototype.slice.call(arguments);
var typ = args.shift();
var str = args
.join('')
.replace(/\s+/g, '')
.toLowerCase();
var len = str.length / 2;
var lenlen = 0;
var hex = typ;
if ('number' === typeof hex) {
hex = Enc.numToHex(hex);
}
// We can't have an odd number of hex chars
if (len !== Math.round(len)) {
throw new Error('invalid hex');
}
// The first byte of any ASN.1 sequence is the type (Sequence, Integer, etc)
// The second byte is either the size of the value, or the size of its size
// 1. If the second byte is < 0x80 (128) it is considered the size
// 2. If it is > 0x80 then it describes the number of bytes of the size
// ex: 0x82 means the next 2 bytes describe the size of the value
// 3. The special case of exactly 0x80 is "indefinite" length (to end-of-file)
if (len > 127) {
lenlen += 1;
while (len > 255) {
lenlen += 1;
len = len >> 8;
}
}
if (lenlen) {
hex += Enc.numToHex(0x80 + lenlen);
}
return hex + Enc.numToHex(str.length / 2) + str;
}
ASN1.Any = Any;
// The Integer type has some special rules
ASN1.UInt = function UINT() {
var str = Array.prototype.slice.call(arguments).join('');
var first = parseInt(str.slice(0, 2), 16);
// If the first byte is 0x80 or greater, the number is considered negative
// Therefore we add a '00' prefix if the 0x80 bit is set
if (0x80 & first) {
str = '00' + str;
}
return Any('02', str);
};
// The Bit String type also has a special rule
ASN1.BitStr = function BITSTR() {
var str = Array.prototype.slice.call(arguments).join('');
// '00' is a mask of how many bits of the next byte to ignore
return Any('03', '00' + str);
};
ASN1._toArray = function toArray(next, opts) {
var typ = opts.json ? Enc.numToHex(next.type) : next.type;
var val = next.value;
if (val) {
if ('string' !== typeof val && opts.json) {
val = Enc.bufToHex(val);
}
return [typ, val];
}
return [
typ,
next.children.map(function(child) {
return toArray(child, opts);
})
];
};
ASN1._pack = function(arr) {
var typ = arr[0];
if ('number' === typeof arr[0]) {
typ = Enc.numToHex(arr[0]);
}
var str = '';
if (Array.isArray(arr[1])) {
arr[1].forEach(function(a) {
str += ASN1._pack(a);
});
} else if ('string' === typeof arr[1]) {
str = arr[1];
} else if (arr[1].byteLength) {
str = Enc.bufToHex(arr[1]);
} else {
throw new Error('unexpected array');
}
if ('03' === typ) {
return ASN1.BitStr(str);
} else if ('02' === typ) {
return ASN1.UInt(str);
} else {
return Any(typ, str);
}
};
// TODO should this return a buffer?
ASN1.pack = function(asn1, opts) {
if (!opts) {
opts = {};
}
if (!Array.isArray(asn1)) {
asn1 = ASN1._toArray(asn1, { json: true });
}
var result = ASN1._pack(asn1);
if (opts.json) {
return result;
}
return Enc.hexToBuf(result);
};
// Copyright 2018 AJ ONeal. All rights reserved
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
var ASN1 = module.exports;
var Enc = require('@root/encoding/hex');
//
// Parser
//
// Although I've only seen 9 max in https certificates themselves,
// but each domain list could have up to 100
ASN1.ELOOPN = 102;
ASN1.ELOOP =
'uASN1.js Error: iterated over ' +
ASN1.ELOOPN +
'+ elements (probably a malformed file)';
// I've seen https certificates go 29 deep
ASN1.EDEEPN = 60;
ASN1.EDEEP =
'uASN1.js Error: element nested ' +
ASN1.EDEEPN +
'+ layers deep (probably a malformed file)';
// Container Types are Sequence 0x30, Container Array? (0xA0, 0xA1)
// Value Types are Boolean 0x01, Integer 0x02, Null 0x05, Object ID 0x06, String 0x0C, 0x16, 0x13, 0x1e Value Array? (0x82)
// Bit String (0x03) and Octet String (0x04) may be values or containers
// Sometimes Bit String is used as a container (RSA Pub Spki)
ASN1.CTYPES = [0x30, 0x31, 0xa0, 0xa1];
ASN1.VTYPES = [0x01, 0x02, 0x05, 0x06, 0x0c, 0x82];
ASN1.parseVerbose = function parseAsn1Helper(buf, opts) {
if (!opts) {
opts = {};
}
//var ws = ' ';
function parseAsn1(buf, depth, eager) {
if (depth.length >= ASN1.EDEEPN) {
throw new Error(ASN1.EDEEP);
}
var index = 2; // we know, at minimum, data starts after type (0) and lengthSize (1)
var asn1 = { type: buf[0], lengthSize: 0, length: buf[1] };
var child;
var iters = 0;
var adjust = 0;
var adjustedLen;
// Determine how many bytes the length uses, and what it is
if (0x80 & asn1.length) {
asn1.lengthSize = 0x7f & asn1.length;
// I think that buf->hex->int solves the problem of Endianness... not sure
asn1.length = parseInt(
Enc.bufToHex(buf.slice(index, index + asn1.lengthSize)),
16
);
index += asn1.lengthSize;
}
// High-order bit Integers have a leading 0x00 to signify that they are positive.
// Bit Streams use the first byte to signify padding, which x.509 doesn't use.
if (0x00 === buf[index] && (0x02 === asn1.type || 0x03 === asn1.type)) {
// However, 0x00 on its own is a valid number
if (asn1.length > 1) {
index += 1;
adjust = -1;
}
}
adjustedLen = asn1.length + adjust;
//console.warn(depth.join(ws) + '0x' + Enc.numToHex(asn1.type), index, 'len:', asn1.length, asn1);
function parseChildren(eager) {
asn1.children = [];
//console.warn('1 len:', (2 + asn1.lengthSize + asn1.length), 'idx:', index, 'clen:', 0);
while (
iters < ASN1.ELOOPN &&
index < 2 + asn1.length + asn1.lengthSize
) {
iters += 1;
depth.length += 1;
child = parseAsn1(
buf.slice(index, index + adjustedLen),
depth,
eager
);
depth.length -= 1;
// The numbers don't match up exactly and I don't remember why...
// probably something with adjustedLen or some such, but the tests pass
index += 2 + child.lengthSize + child.length;
//console.warn('2 len:', (2 + asn1.lengthSize + asn1.length), 'idx:', index, 'clen:', (2 + child.lengthSize + child.length));
if (index > 2 + asn1.lengthSize + asn1.length) {
if (!eager) {
console.error(JSON.stringify(asn1, ASN1._replacer, 2));
}
throw new Error(
'Parse error: child value length (' +
child.length +
') is greater than remaining parent length (' +
(asn1.length - index) +
' = ' +
asn1.length +
' - ' +
index +
')'
);
}
asn1.children.push(child);
//console.warn(depth.join(ws) + '0x' + Enc.numToHex(asn1.type), index, 'len:', asn1.length, asn1);
}
if (index !== 2 + asn1.lengthSize + asn1.length) {
//console.warn('index:', index, 'length:', (2 + asn1.lengthSize + asn1.length));
throw new Error('premature end-of-file');
}
if (iters >= ASN1.ELOOPN) {
throw new Error(ASN1.ELOOP);
}
delete asn1.value;
return asn1;
}
// Recurse into types that are _always_ containers
if (-1 !== ASN1.CTYPES.indexOf(asn1.type)) {
return parseChildren(eager);
}
// Return types that are _always_ values
asn1.value = buf.slice(index, index + adjustedLen);
if (opts.json) {
asn1.value = Enc.bufToHex(asn1.value);
}
if (-1 !== ASN1.VTYPES.indexOf(asn1.type)) {
return asn1;
}
// For ambigious / unknown types, recurse and return on failure
// (and return child array size to zero)
try {
return parseChildren(true);
} catch (e) {
asn1.children.length = 0;
return asn1;
}
}
var asn1 = parseAsn1(buf, []);
var len = buf.byteLength || buf.length;
if (len !== 2 + asn1.lengthSize + asn1.length) {
throw new Error(
'Length of buffer does not match length of ASN.1 sequence.'
);
}
return asn1;
};
ASN1._toArray = function toArray(next, opts) {
var typ = opts.json ? Enc.numToHex(next.type) : next.type;
var val = next.value;
if (val) {
if ('string' !== typeof val && opts.json) {
val = Enc.bufToHex(val);
}
return [typ, val];
}
return [
typ,
next.children.map(function(child) {
return toArray(child, opts);
})
];
};
ASN1.parse = function(opts) {
var opts2 = { json: false !== opts.json };
var verbose = ASN1.parseVerbose(opts.der, opts2);
if (opts.verbose) {
return verbose;
}
return ASN1._toArray(verbose, opts2);
};
ASN1._replacer = function(k, v) {
if ('type' === k) {
return '0x' + Enc.numToHex(v);
}
if (v && 'value' === k) {
return '0x' + Enc.bufToHex(v.data || v);
}
return v;
};
This diff is collapsed. Click to expand it.
# @root/csr
Lightweight, Zero-Dependency CSR (Certificate Signing Request) generator and parser for Node.js and Browsers
# Usage
```js
var CSR = require('@root/csr');
var PEM = require('@root/pem/packer');
CSR.csr({
jwk: jwk,
domains: ['example.com', '*.example.com', 'foo.bar.example.com'],
encoding: 'pem'
}).then(function(der) {
var csr = PEM.packBlock({ type: 'CERTIFICATE REQUEST', bytes: der });
console.log(csr);
});
```
```txt
-----BEGIN CERTIFICATE REQUEST-----
MIIBHjCBxQIBADAWMRQwEgYDVQQDDAtleGFtcGxlLmNvbTBZMBMGByqGSM49AgEG
CCqGSM49AwEHA0IABFL897BlwE6Tmco/r7LpwVL2BdDx12zZr+BnA/0/PjkI0lsu
013u1+X5fe6vKnOIjcb5obaFnSQixuMGu3qcVnmgTTBLBgkqhkiG9w0BCQ4xPjA8
MDoGA1UdEQQzMDGCC2V4YW1wbGUuY29tgg0qLmV4YW1wbGUuY29tghNmb28uYmFy
LmV4YW1wbGUuY29tMAoGCCqGSM49BAMCA0gAMEUCIADRCWsMYBjm70Hqi08QrOcR
Gcz8uJTe7vZwqOGtykWiAiEA1FTbMskZR9w2ugFWXkWfBdb1W6cD2v6nK+J0wj2r
Q48=
-----END CERTIFICATE REQUEST-----
```
# Advanced Usage
Create an unsigned request
```
var CSR = require('@root/csr');
// Note: this requires the public key to embed it in the request
var hex = CSR.request({
jwk: jwk,
domains: ['example.com', '*.example.com', 'foo.bar.example.com'],
encoding: 'hex'
})
```
// Copyright 2018-present AJ ONeal. All rights reserved
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
/*global Promise*/
var Enc = require('@root/encoding');
var ASN1 = require('@root/asn1/packer'); // DER, actually
var Asn1 = ASN1.Any;
var BitStr = ASN1.BitStr;
var UInt = ASN1.UInt;
var Asn1Parser = require('@root/asn1/parser');
var PEM = require('@root/pem');
var X509 = require('@root/x509');
// TODO @root/keypairs/sign
var Keypairs = require('@root/keypairs');
// TODO find a way that the prior node-ish way of `module.exports = function () {}` isn't broken
var CSR = module.exports;
// { jwk, domains }
CSR.csr = function(opts) {
// We're using a Promise here to be compatible with the browser version
// which will probably use the webcrypto API for some of the conversions
return CSR._prepare(opts).then(function(opts) {
return CSR.create(opts).then(function(bytes) {
return CSR._encode(opts, bytes);
});
});
};
CSR._prepare = function(opts) {
return Promise.resolve().then(function() {
opts = JSON.parse(JSON.stringify(opts));
// We do a bit of extra error checking for user convenience
if (!opts) {
throw new Error(
'You must pass options with key and domains to rsacsr'
);
}
if (!Array.isArray(opts.domains) || 0 === opts.domains.length) {
new Error('You must pass options.domains as a non-empty array');
}
// I need to check that 例.中国 is a valid domain name
if (
!opts.domains.every(function(d) {
// allow punycode? xn--
if (
'string' === typeof d /*&& /\./.test(d) && !/--/.test(d)*/
) {
return true;
}
})
) {
throw new Error('You must pass options.domains as strings');
}
if (opts.jwk) {
return opts;
}
if (opts.key && opts.key.kty) {
opts.jwk = opts.key;
return opts;
}
if (!opts.pem && !opts.key) {
throw new Error('You must pass options.key as a JSON web key');
}
return Keypairs.import({ pem: opts.pem || opts.key }).then(function(
pair
) {
opts.jwk = pair.private;
return opts;
});
});
};
CSR._encode = function(opts, bytes) {
if ('der' === (opts.encoding || '').toLowerCase()) {
return bytes;
}
return PEM.packBlock({
type: 'CERTIFICATE REQUEST',
bytes: bytes /* { jwk: jwk, domains: opts.domains } */
});
};
// { jwk, domains }
CSR.create = function createCsr(opts) {
var hex = CSR.request({
jwk: opts.jwk,
domains: opts.domains,
encoding: 'hex'
});
return CSR._sign(opts.jwk, hex).then(function(csr) {
return Enc.hexToBuf(csr);
});
};
//
// EC / RSA
//
// { jwk, domains }
CSR.request = function createCsrBody(opts) {
var asn1pub;
if (/^EC/i.test(opts.jwk.kty)) {
asn1pub = X509.packCsrEcPublicKey(opts.jwk);
} else {
asn1pub = X509.packCsrRsaPublicKey(opts.jwk);
}
var hex = X509.packCsr(asn1pub, opts.domains);
if ('hex' === opts.encoding) {
return hex;
}
// der
return Enc.hexToBuf(hex);
};
CSR._sign = function csrEcSig(jwk, request) {
// Took some tips from https://gist.github.com/codermapuche/da4f96cdb6d5ff53b7ebc156ec46a10a
// TODO will have to convert web ECDSA signatures to PEM ECDSA signatures (but RSA should be the same)
// TODO have a consistent non-private way to sign
return Keypairs.sign(
{ jwk: jwk, format: 'x509' },
Enc.hexToBuf(request)
).then(function(sig) {
return CSR._toDer({
request: request,
signature: sig,
kty: jwk.kty
});
});
};
CSR._toDer = function encode(opts) {
var sty;
if (/^EC/i.test(opts.kty)) {
// 1.2.840.10045.4.3.2 ecdsaWithSHA256 (ANSI X9.62 ECDSA algorithm with SHA256)
sty = Asn1('30', Asn1('06', '2a8648ce3d040302'));
} else {
// 1.2.840.113549.1.1.11 sha256WithRSAEncryption (PKCS #1)
sty = Asn1('30', Asn1('06', '2a864886f70d01010b'), Asn1('05'));
}
return Asn1(
'30',
// The Full CSR Request Body
opts.request,
// The Signature Type
sty,
// The Signature
BitStr(Enc.bufToHex(opts.signature))
);
};
X509.packCsr = function(asn1pubkey, domains) {
return Asn1(
'30',
// Version (0)
UInt('00'),
// 2.5.4.3 commonName (X.520 DN component)
Asn1(
'30',
Asn1(
'31',
Asn1(
'30',
Asn1('06', '550403'),
// TODO utf8 => punycode
Asn1('0c', Enc.strToHex(domains[0]))
)
)
),
// Public Key (RSA or EC)
asn1pubkey,
// Request Body
Asn1(
'a0',
Asn1(
'30',
// 1.2.840.113549.1.9.14 extensionRequest (PKCS #9 via CRMF)
Asn1('06', '2a864886f70d01090e'),
Asn1(
'31',
Asn1(
'30',
Asn1(
'30',
// 2.5.29.17 subjectAltName (X.509 extension)
Asn1('06', '551d11'),
Asn1(
'04',
Asn1(
'30',
domains
.map(function(d) {
// TODO utf8 => punycode
return Asn1('82', Enc.strToHex(d));
})
.join('')
)
)
)
)
)
)
)
);
};
// TODO finish this later
// we want to parse the domains, the public key, and verify the signature
CSR._info = function(der) {
// standard base64 PEM
if ('string' === typeof der && '-' === der[0]) {
der = PEM.parseBlock(der).bytes;
}
// jose urlBase64 not-PEM
if ('string' === typeof der) {
der = Enc.base64ToBuf(der);
}
// not supporting binary-encoded base64
var c = Asn1Parser.parse({ der: der, verbose: true, json: false });
var kty;
// A cert has 3 parts: cert, signature meta, signature
if (c.children.length !== 3) {
throw new Error(
"doesn't look like a certificate request: expected 3 parts of header"
);
}
var sig = c.children[2];
if (sig.children.length) {
// ASN1/X509 EC
sig = sig.children[0];
sig = Asn1(
'30',
UInt(Enc.bufToHex(sig.children[0].value)),
UInt(Enc.bufToHex(sig.children[1].value))
);
sig = Enc.hexToBuf(sig);
kty = 'EC';
} else {
// Raw RSA Sig
sig = sig.value;
kty = 'RSA';
}
//c.children[1]; // signature type
var req = c.children[0];
if (4 !== req.children.length) {
throw new Error(
"doesn't look like a certificate request: expected 4 parts to request"
);
}
// 0 null
// 1 commonName / subject
var sub = Enc.bufToStr(
req.children[1].children[0].children[0].children[1].value
);
// 3 public key (type, key)
//console.log('oid', Enc.bufToHex(req.children[2].children[0].children[0].value));
var pub;
// TODO reuse ASN1 parser for these?
if ('EC' === kty) {
// throw away compression byte
pub = req.children[2].children[1].value.slice(1);
pub = { kty: kty, x: pub.slice(0, 32), y: pub.slice(32) };
while (0 === pub.x[0]) {
pub.x = pub.x.slice(1);
}
while (0 === pub.y[0]) {
pub.y = pub.y.slice(1);
}
if ((pub.x.length || pub.x.byteLength) > 48) {
pub.crv = 'P-521';
} else if ((pub.x.length || pub.x.byteLength) > 32) {
pub.crv = 'P-384';
} else {
pub.crv = 'P-256';
}
pub.x = Enc.bufToUrlBase64(pub.x);
pub.y = Enc.bufToUrlBase64(pub.y);
} else {
pub = req.children[2].children[1].children[0];
pub = {
kty: kty,
n: pub.children[0].value,
e: pub.children[1].value
};
while (0 === pub.n[0]) {
pub.n = pub.n.slice(1);
}
while (0 === pub.e[0]) {
pub.e = pub.e.slice(1);
}
pub.n = Enc.bufToUrlBase64(pub.n);
pub.e = Enc.bufToUrlBase64(pub.e);
}
// 4 extensions
var domains = req.children[3].children
.filter(function(seq) {
// 1.2.840.113549.1.9.14 extensionRequest (PKCS #9 via CRMF)
if ('2a864886f70d01090e' === Enc.bufToHex(seq.children[0].value)) {
return true;
}
})
.map(function(seq) {
return seq.children[1].children[0].children
.filter(function(seq2) {
// subjectAltName (X.509 extension)
if ('551d11' === Enc.bufToHex(seq2.children[0].value)) {
return true;
}
})
.map(function(seq2) {
return seq2.children[1].children[0].children.map(function(
name
) {
// TODO utf8 => punycode
return Enc.bufToStr(name.value);
});
})[0];
})[0];
return {
subject: sub,
altnames: domains,
jwk: pub,
signature: sig
};
};
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
No preview for this file type
;(function () {
'use strict';
var CSR = window.CSR = {};
var Enc = window.Encoding;
var X509 = window.X509;
var Keypairs = window.Keypairs;
var ASN1 = window.ASN1;
var PEM = window.PEM;
var Asn1Packer = window.ASN1;
// Copyright 2018-present AJ ONeal. All rights reserved
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*global Promise*/
var Asn1 = ASN1.Any;
var BitStr = ASN1.BitStr;
var UInt = ASN1.UInt;
// TODO @root/keypairs/sign
// { jwk, domains }
CSR.csr = function(opts) {
// We're using a Promise here to be compatible with the browser version
// which will probably use the webcrypto API for some of the conversions
return CSR._prepare(opts).then(function(opts) {
return CSR.create(opts).then(function(bytes) {
return CSR._encode(opts, bytes);
});
});
};
CSR._prepare = function(opts) {
return Promise.resolve().then(function() {
opts = JSON.parse(JSON.stringify(opts));
// We do a bit of extra error checking for user convenience
if (!opts) {
throw new Error(
'You must pass options with key and domains to rsacsr'
);
}
if (!Array.isArray(opts.domains) || 0 === opts.domains.length) {
new Error('You must pass options.domains as a non-empty array');
}
// I need to check that 例.中国 is a valid domain name
if (
!opts.domains.every(function(d) {
// allow punycode? xn--
if (
'string' === typeof d /*&& /\./.test(d) && !/--/.test(d)*/
) {
return true;
}
})
) {
throw new Error('You must pass options.domains as strings');
}
if (opts.jwk) {
return opts;
}
if (opts.key && opts.key.kty) {
opts.jwk = opts.key;
return opts;
}
if (!opts.pem && !opts.key) {
throw new Error('You must pass options.key as a JSON web key');
}
return Keypairs.import({ pem: opts.pem || opts.key }).then(function(
pair
) {
opts.jwk = pair.private;
return opts;
});
});
};
CSR._encode = function(opts, bytes) {
if ('der' === (opts.encoding || '').toLowerCase()) {
return bytes;
}
return PEM.packBlock({
type: 'CERTIFICATE REQUEST',
bytes: bytes /* { jwk: jwk, domains: opts.domains } */
});
};
// { jwk, domains }
CSR.create = function createCsr(opts) {
var hex = CSR.request({
jwk: opts.jwk,
domains: opts.domains,
encoding: 'hex'
});
return CSR._sign(opts.jwk, hex).then(function(csr) {
return Enc.hexToBuf(csr);
});
};
//
// EC / RSA
//
// { jwk, domains }
CSR.request = function createCsrBody(opts) {
var asn1pub;
if (/^EC/i.test(opts.jwk.kty)) {
asn1pub = X509.packCsrEcPublicKey(opts.jwk);
} else {
asn1pub = X509.packCsrRsaPublicKey(opts.jwk);
}
var hex = X509.packCsr(asn1pub, opts.domains);
if ('hex' === opts.encoding) {
return hex;
}
// der
return Enc.hexToBuf(hex);
};
CSR._sign = function csrEcSig(jwk, request) {
// Took some tips from https://gist.github.com/codermapuche/da4f96cdb6d5ff53b7ebc156ec46a10a
// TODO will have to convert web ECDSA signatures to PEM ECDSA signatures (but RSA should be the same)
// TODO have a consistent non-private way to sign
return Keypairs.sign(
{ jwk: jwk, format: 'x509' },
Enc.hexToBuf(request)
).then(function(sig) {
return CSR._toDer({
request: request,
signature: sig,
kty: jwk.kty
});
});
};
CSR._toDer = function encode(opts) {
var sty;
if (/^EC/i.test(opts.kty)) {
// 1.2.840.10045.4.3.2 ecdsaWithSHA256 (ANSI X9.62 ECDSA algorithm with SHA256)
sty = Asn1('30', Asn1('06', '2a8648ce3d040302'));
} else {
// 1.2.840.113549.1.1.11 sha256WithRSAEncryption (PKCS #1)
sty = Asn1('30', Asn1('06', '2a864886f70d01010b'), Asn1('05'));
}
return Asn1(
'30',
// The Full CSR Request Body
opts.request,
// The Signature Type
sty,
// The Signature
BitStr(Enc.bufToHex(opts.signature))
);
};
X509.packCsr = function(asn1pubkey, domains) {
return Asn1(
'30',
// Version (0)
UInt('00'),
// 2.5.4.3 commonName (X.520 DN component)
Asn1(
'30',
Asn1(
'31',
Asn1(
'30',
Asn1('06', '550403'),
// TODO utf8 => punycode
Asn1('0c', Enc.strToHex(domains[0]))
)
)
),
// Public Key (RSA or EC)
asn1pubkey,
// Request Body
Asn1(
'a0',
Asn1(
'30',
// 1.2.840.113549.1.9.14 extensionRequest (PKCS #9 via CRMF)
Asn1('06', '2a864886f70d01090e'),
Asn1(
'31',
Asn1(
'30',
Asn1(
'30',
// 2.5.29.17 subjectAltName (X.509 extension)
Asn1('06', '551d11'),
Asn1(
'04',
Asn1(
'30',
domains
.map(function(d) {
// TODO utf8 => punycode
return Asn1('82', Enc.strToHex(d));
})
.join('')
)
)
)
)
)
)
)
);
};
// TODO finish this later
// we want to parse the domains, the public key, and verify the signature
CSR._info = function(der) {
// standard base64 PEM
if ('string' === typeof der && '-' === der[0]) {
der = PEM.parseBlock(der).bytes;
}
// jose urlBase64 not-PEM
if ('string' === typeof der) {
der = Enc.base64ToBuf(der);
}
// not supporting binary-encoded base64
var c = Asn1Parser.parse({ der: der, verbose: true, json: false });
var kty;
// A cert has 3 parts: cert, signature meta, signature
if (c.children.length !== 3) {
throw new Error(
"doesn't look like a certificate request: expected 3 parts of header"
);
}
var sig = c.children[2];
if (sig.children.length) {
// ASN1/X509 EC
sig = sig.children[0];
sig = Asn1(
'30',
UInt(Enc.bufToHex(sig.children[0].value)),
UInt(Enc.bufToHex(sig.children[1].value))
);
sig = Enc.hexToBuf(sig);
kty = 'EC';
} else {
// Raw RSA Sig
sig = sig.value;
kty = 'RSA';
}
//c.children[1]; // signature type
var req = c.children[0];
if (4 !== req.children.length) {
throw new Error(
"doesn't look like a certificate request: expected 4 parts to request"
);
}
// 0 null
// 1 commonName / subject
var sub = Enc.bufToStr(
req.children[1].children[0].children[0].children[1].value
);
// 3 public key (type, key)
//console.log('oid', Enc.bufToHex(req.children[2].children[0].children[0].value));
var pub;
// TODO reuse ASN1 parser for these?
if ('EC' === kty) {
// throw away compression byte
pub = req.children[2].children[1].value.slice(1);
pub = { kty: kty, x: pub.slice(0, 32), y: pub.slice(32) };
while (0 === pub.x[0]) {
pub.x = pub.x.slice(1);
}
while (0 === pub.y[0]) {
pub.y = pub.y.slice(1);
}
if ((pub.x.length || pub.x.byteLength) > 48) {
pub.crv = 'P-521';
} else if ((pub.x.length || pub.x.byteLength) > 32) {
pub.crv = 'P-384';
} else {
pub.crv = 'P-256';
}
pub.x = Enc.bufToUrlBase64(pub.x);
pub.y = Enc.bufToUrlBase64(pub.y);
} else {
pub = req.children[2].children[1].children[0];
pub = {
kty: kty,
n: pub.children[0].value,
e: pub.children[1].value
};
while (0 === pub.n[0]) {
pub.n = pub.n.slice(1);
}
while (0 === pub.e[0]) {
pub.e = pub.e.slice(1);
}
pub.n = Enc.bufToUrlBase64(pub.n);
pub.e = Enc.bufToUrlBase64(pub.e);
}
// 4 extensions
var domains = req.children[3].children
.filter(function(seq) {
// 1.2.840.113549.1.9.14 extensionRequest (PKCS #9 via CRMF)
if ('2a864886f70d01090e' === Enc.bufToHex(seq.children[0].value)) {
return true;
}
})
.map(function(seq) {
return seq.children[1].children[0].children
.filter(function(seq2) {
// subjectAltName (X.509 extension)
if ('551d11' === Enc.bufToHex(seq2.children[0].value)) {
return true;
}
})
.map(function(seq2) {
return seq2.children[1].children[0].children.map(function(
name
) {
// TODO utf8 => punycode
return Enc.bufToStr(name.value);
});
})[0];
})[0];
return {
subject: sub,
altnames: domains,
jwk: pub,
signature: sig
};
};
}());
(function(){"use strict";var CSR=window.CSR={};var Enc=window.Encoding;var X509=window.X509;var Keypairs=window.Keypairs;var ASN1=window.ASN1;var PEM=window.PEM;var Asn1Packer=window.ASN1;var Asn1=ASN1.Any;var BitStr=ASN1.BitStr;var UInt=ASN1.UInt;CSR.csr=function(opts){return CSR._prepare(opts).then(function(opts){return CSR.create(opts).then(function(bytes){return CSR._encode(opts,bytes)})})};CSR._prepare=function(opts){return Promise.resolve().then(function(){opts=JSON.parse(JSON.stringify(opts));if(!opts){throw new Error("You must pass options with key and domains to rsacsr")}if(!Array.isArray(opts.domains)||0===opts.domains.length){new Error("You must pass options.domains as a non-empty array")}if(!opts.domains.every(function(d){if("string"===typeof d){return true}})){throw new Error("You must pass options.domains as strings")}if(opts.jwk){return opts}if(opts.key&&opts.key.kty){opts.jwk=opts.key;return opts}if(!opts.pem&&!opts.key){throw new Error("You must pass options.key as a JSON web key")}return Keypairs.import({pem:opts.pem||opts.key}).then(function(pair){opts.jwk=pair.private;return opts})})};CSR._encode=function(opts,bytes){if("der"===(opts.encoding||"").toLowerCase()){return bytes}return PEM.packBlock({type:"CERTIFICATE REQUEST",bytes:bytes})};CSR.create=function createCsr(opts){var hex=CSR.request({jwk:opts.jwk,domains:opts.domains,encoding:"hex"});return CSR._sign(opts.jwk,hex).then(function(csr){return Enc.hexToBuf(csr)})};CSR.request=function createCsrBody(opts){var asn1pub;if(/^EC/i.test(opts.jwk.kty)){asn1pub=X509.packCsrEcPublicKey(opts.jwk)}else{asn1pub=X509.packCsrRsaPublicKey(opts.jwk)}var hex=X509.packCsr(asn1pub,opts.domains);if("hex"===opts.encoding){return hex}return Enc.hexToBuf(hex)};CSR._sign=function csrEcSig(jwk,request){return Keypairs.sign({jwk:jwk,format:"x509"},Enc.hexToBuf(request)).then(function(sig){return CSR._toDer({request:request,signature:sig,kty:jwk.kty})})};CSR._toDer=function encode(opts){var sty;if(/^EC/i.test(opts.kty)){sty=Asn1("30",Asn1("06","2a8648ce3d040302"))}else{sty=Asn1("30",Asn1("06","2a864886f70d01010b"),Asn1("05"))}return Asn1("30",opts.request,sty,BitStr(Enc.bufToHex(opts.signature)))};X509.packCsr=function(asn1pubkey,domains){return Asn1("30",UInt("00"),Asn1("30",Asn1("31",Asn1("30",Asn1("06","550403"),Asn1("0c",Enc.strToHex(domains[0]))))),asn1pubkey,Asn1("a0",Asn1("30",Asn1("06","2a864886f70d01090e"),Asn1("31",Asn1("30",Asn1("30",Asn1("06","551d11"),Asn1("04",Asn1("30",domains.map(function(d){return Asn1("82",Enc.strToHex(d))}).join("")))))))))};CSR._info=function(der){if("string"===typeof der&&"-"===der[0]){der=PEM.parseBlock(der).bytes}if("string"===typeof der){der=Enc.base64ToBuf(der)}var c=Asn1Parser.parse({der:der,verbose:true,json:false});var kty;if(c.children.length!==3){throw new Error("doesn't look like a certificate request: expected 3 parts of header")}var sig=c.children[2];if(sig.children.length){sig=sig.children[0];sig=Asn1("30",UInt(Enc.bufToHex(sig.children[0].value)),UInt(Enc.bufToHex(sig.children[1].value)));sig=Enc.hexToBuf(sig);kty="EC"}else{sig=sig.value;kty="RSA"}var req=c.children[0];if(4!==req.children.length){throw new Error("doesn't look like a certificate request: expected 4 parts to request")}var sub=Enc.bufToStr(req.children[1].children[0].children[0].children[1].value);var pub;if("EC"===kty){pub=req.children[2].children[1].value.slice(1);pub={kty:kty,x:pub.slice(0,32),y:pub.slice(32)};while(0===pub.x[0]){pub.x=pub.x.slice(1)}while(0===pub.y[0]){pub.y=pub.y.slice(1)}if((pub.x.length||pub.x.byteLength)>48){pub.crv="P-521"}else if((pub.x.length||pub.x.byteLength)>32){pub.crv="P-384"}else{pub.crv="P-256"}pub.x=Enc.bufToUrlBase64(pub.x);pub.y=Enc.bufToUrlBase64(pub.y)}else{pub=req.children[2].children[1].children[0];pub={kty:kty,n:pub.children[0].value,e:pub.children[1].value};while(0===pub.n[0]){pub.n=pub.n.slice(1)}while(0===pub.e[0]){pub.e=pub.e.slice(1)}pub.n=Enc.bufToUrlBase64(pub.n);pub.e=Enc.bufToUrlBase64(pub.e)}var domains=req.children[3].children.filter(function(seq){if("2a864886f70d01090e"===Enc.bufToHex(seq.children[0].value)){return true}}).map(function(seq){return seq.children[1].children[0].children.filter(function(seq2){if("551d11"===Enc.bufToHex(seq2.children[0].value)){return true}}).map(function(seq2){return seq2.children[1].children[0].children.map(function(name){return Enc.bufToStr(name.value)})})[0]})[0];return{subject:sub,altnames:domains,jwk:pub,signature:sig}}})();
No preview for this file type
{
"_from": "@root/csr@^0.8.1",
"_id": "@root/csr@0.8.1",
"_inBundle": false,
"_integrity": "sha512-hKl0VuE549TK6SnS2Yn9nRvKbFZXn/oAg+dZJU/tlKl/f/0yRXeuUzf8akg3JjtJq+9E592zDqeXZ7yyrg8fSQ==",
"_location": "/@root/csr",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "@root/csr@^0.8.1",
"name": "@root/csr",
"escapedName": "@root%2fcsr",
"scope": "@root",
"rawSpec": "^0.8.1",
"saveSpec": null,
"fetchSpec": "^0.8.1"
},
"_requiredBy": [
"/@root/greenlock"
],
"_resolved": "https://registry.npmjs.org/@root/csr/-/csr-0.8.1.tgz",
"_shasum": "97a1b821331a4ed5895eee33bedb6ad49cdef74e",
"_spec": "@root/csr@^0.8.1",
"_where": "C:\\Users\\USER\\Documents\\Desktop\\옵소팀플\\LINEBOT\\node_modules\\@root\\greenlock",
"author": {
"name": "AJ ONeal",
"email": "coolaj86@gmail.com",
"url": "https://coolaj86.com/"
},
"bundleDependencies": false,
"dependencies": {
"@root/asn1": "^1.0.0",
"@root/pem": "^1.0.4",
"@root/x509": "^0.7.2"
},
"deprecated": false,
"description": "Lightweight, Zero-Dependency CSR (Certificate Signing Request) generator and parser for Node.js and Browsers",
"devDependencies": {
"@root/keypairs": "^0.9.0"
},
"files": [
"*.js",
"lib",
"dist"
],
"keywords": [
"CSR",
"ASN.1",
"DER",
"PEM",
"x509",
"RSA",
"EC",
"ECDSA",
"letsencrypt",
"ACME",
"asn1"
],
"license": "MPL-2.0",
"main": "csr.js",
"name": "@root/csr",
"repository": {
"type": "git",
"url": "https://git.rootprojects.org/root/csr.js.git"
},
"scripts": {
"test": "node tests"
},
"version": "0.8.1"
}
This diff is collapsed. Click to expand it.
# @root/encoding
Lightweight, Zero-dependency, translation between Unicode Strings, Binary Strings, Buffers, Base64, Hex, UCS-2, UTF-8, etc.
| < 1k gzipped | 2.6k minified | 3.6k full |
Works identically on all platforms:
- [x] Web Browsers
- Chrome
- Firefox
- Microsoft Edge
- Internet Explorer
- [x] Node.js
- [x] WebPack
# Usage
**Vanilla JS**
```html
<script src="https://unpkg.com/@root/encoding@1.0.0/dist/encoding.all.js"></script>
```
```html
<script src="https://unpkg.com/@root/encoding@1.0.0/dist/encoding.all.min.js"></script>
```
```js
var Enc = window.Encoding;
Enc.strToBuf('Hello, 世界!');
```
**WebPack**, Node
```js
var Enc = require('@root/encoding');
Enc.strToBuf('Hello, 世界!');
```
# Use cases
Typically you want to use this in a browser when you need to convert user input to some sort
of Byte Array for hashing or encoding in an ancient format.
For example:
- [x] Hashing passwords
- [x] Secure Remote Password
- [x] JWT and JWS signing and verifying
- [x] ASN1 parsing and packing
- [x] DER
- [x] x509
- [x] CSR
- [x] PEM
The purpose of this library is to make it easy to support common string and buffer encoding and decoding
in both Browsers and node with minimal code.
# Examples
Strings and Byte Arrays
```js
var Enc = require('@root/encoding/bytes');
Enc.binToStr(bin);
Enc.binToBuf(bin);
Enc.bufToBin(buf);
Enc.bufToStr(buf);
Enc.strToBin(str);
Enc.strToBuf(str);
```
Hex
```js
var Enc = require('@root/encoding/hex');
Enc.hexToBuf(hex);
Enc.hexToStr(hex);
Enc.bufToHex(buf);
Enc.strToHex(str);
```
Base64
```js
var Enc = require('@root/encoding/base64');
Enc.base64ToBuf(b64);
Enc.base64ToStr(b64);
Enc.bufToBase64(buf);
Enc.strToBase64(str);
```
URL Safe Base64
(all of `base64To*()` accept URL Safe Base64)
```js
var Enc = require('@root/encoding/base64');
Enc.base64ToUrlBase64(b64);
Enc.urlBase64ToBase64(u64);
Enc.bufToUrlBase64(buf);
Enc.strToUrlBase64(str);
```
Base64 and Hex
```
require('@root/encoding/base64');
require('@root/encoding/hex');
var Enc = require('@root/encoding');
Enc.hexToBase64(hex);
Enc.base64ToHex(b64);
```
# Browser API
(the Node API signatures are the same, but implemented with `Buffer`)
Conversions between these formats are supported:
- Strings and Buffers
- Hex
- Base64
## Strings and Buffers
JavaScript has two types of strings:
- _Binary Strings_, which we call `bin`
- _Unicode Strings_, which we call `str` (USC-2, essentially UTF-16)
- treated as UTF-8 for the purposes of `encodeURIComponent`
JavaScript has two (and a half) ways to support Byte Arrays:
- `Array`, which we call `arr`
- `Uint8Array`, which we call `buf` (of the `ArrayBuffer` family)
- `Buffer` (node-only, but implemented as `Uint8Array`)
The API for the conversions is centered around `Uint8Array` (`Buffer`) but,
for browser compatibility, sometimes requires the use of _Binary Strings_.
**API**
We provide conversions directly to each of the following:
| Name | Type | Description |
| :---- | :------------- | :-------------------------------------------- |
| `str` | Unicode String | Handled by `split('')` as two-byte characters |
| `bin` | Binary String | Handled by `split('')` as single-byte chars |
| `buf` | Byte Array | Truncated to single-byte chars |
The names and signatures of the functions are as follows:
To Buffer
- Binary String to Buffer
- binToBuf(bin)
- Unicode String (UTF-8) to Buffer
- strToBuf(str)
To Unicode String
- Binary String to Unicode String (UTF-8)
- binToStr(bin)
- Buffer to Unicode String (UTF-8)
- bufToStr(buf)
To Binary String
- Buffer to Binary String
- bufToBin(buf)
- Unicode String to Binary String
- strToBin(str)
It's very easy to convert from Binary Strings to Byte Arrays (`Uint8Array.from(bin.split(''))`)
and from `Uint8Array` to Binary String (`Array.from(buf).join('')`).
The real value is converting between Unicode Strings to (UTF-8) Binary Strings, and back:
```js
function toBin(str) {
var escstr = encodeURIComponent(str);
return escstr.replace(/%([0-9A-F]{2})/g, function(match, p1) {
return String.fromCharCode(parseInt(p1, 16));
});
}
```
```js
function toStr(bin) {
var escstr = bin.replace(/(.)/g, function(m, p) {
var code = p
.charCodeAt(0)
.toString(16)
.toUpperCase();
if (code.length < 2) {
code = '0' + code;
}
return '%' + code;
});
return decodeURIComponent(escstr);
}
```
## Hex
JavaScript does not have a native way to create hex, aside from small numbers:
```js
(12345).toString(16);
```
The hex functions convert to and from hexidecimal:
| Name | Type | Description |
| :---- | :--------- | :--------------------------------------------- |
| `hex` | Hex String | Handled by `split('')` as half-byte characters |
To Hex
- Binary String to Hex
- Enc.bufToHex(Enc.binToBuf(bin))
- Byte Array to Hex
- bufToHex
- Unicode String (UTF-8) to Hex
- strToHex
From Hex
- Hex to Binary String
- Enc.hexToBuf(Enc.bufToBin(hex))
- Hex to Byte Array
- hexToBuf
- Hex to Unicode String (UTF-8)
- hexToStr
However, assuming you have a single-byte string, it's very easy to convert back and forth:
```js
function toHex(any) {
var hex = [];
var i, h;
var len = any.byteLength || any.length;
for (i = 0; i < len; i += 1) {
h = any[i].toString(16);
if (h.length % 2) {
h = '0' + h;
}
hex.push(h);
}
return hex.join('').toLowerCase();
}
```
```js
function fromHex(hex) {
var arr = hex.match(/.{2}/g).map(function(h) {
return parseInt(h, 16);
});
return Uint8Array.from(arr);
}
```
## Base64
Browser JavaScript _does_ have a native way convert between Binary Strings and Base64:
```js
var b64 = btoa('An ASCII string is a Binary String');
// Note: A Unicode String is NOT
```
```js
var bin = atob('SGVsbG8sIOS4lueVjCE=');
```
However, it does **not** have a native way to convert between Unicode Strings and Binary Strings,
nor to and from URL Safe Base64.
The base64 module provides simpler conversions to and from Base 64 and URL Safe Base64:
| Name | Type | Description |
| :---- | :-------------- | :-------------------------------------------------------- |
| `b64` | Base64 | Standard Base64 as handled by `btoa` and `atob` |
| `u64` | URL Safe Base64 | Replaces `+` with `-` and `/` with `_`, and omits padding |
To Base64
- Unicode String (UTF-8) to Base64
- strToBase64(str)
- Binary String to Base64
- Enc.bufToBase64(Enc.binToBuf(bin))
- Byte Array to Base64
- bufToBase64(buf)
From Base64 (and URL Safe Base64)
- Base64 to Unicode String (UTF-8)
- base64ToStr(b64)
- Base64 to Binary String
- Enc.bufToBin(Enc.base64ToBuf(b64)))
- Base64 to Byte Array
- base64ToBuf(b64)
To URL Safe Base64
- Base64 to URL Safe Base64
- base64ToUrlBase64(b64);
- URL Safe Base64 to Base64
- urlBase64ToBase64(u64);
- Binary String to URL Safe Base64
- Enc.bufToUrlBase64(Enc.binToBuf(bin));
- Byte Array to URL Safe Base64
- bufToUrlBase64(buf);
- Unicode String (UTF-8) to URL Safe Base64
- strToUrlBase64(str);
# FAQ
## Why yet another encoding library?
We write code that works both in node and in browsers,
and we like to keep it small, light, and focused.
By using browser native functions rather than 're-inventing the wheel'
## Why not 'browserified' Buffer?
The most common 'browserified' `Buffer` implementations are quite large -
either because they don't use browser-native code or they guarantee perfect
compatibility with node's `Buffer`, which isn't necessary for most people.
On the other hand, Browsers have already been able to translate between
Base64, UTF-8, Binary Strings, and Byte Arrays (Buffers) all the way back
since _before_ IE6!
Using these browser-native methods eliminates hundreds of lines of code:
- `btoa` Binary String to Base64 (ASCII)
- `atob` Base64 (ASCII) to Binary String
- `encodeURIComponent` Unicode String to Hex-Escaped String
- `decodeURIComponent` Hex-Escaped String to Unicode String
- `String.prototype.charCodeAt` ASCII to Byte
- `String.fromCharCode` Byte to ASCII
The code is typically also much easier to read. In many cases the conversion is only one line long.
Since a node `Buffer` is actually an `ArrayBuffer`, node's `Buffer` really only has the advantage
of convenient conversions, so that's really all that needs to be implemented.
In the case of ancient browsers which do not support `Uint8Array`, the native `Array` is still
the best substitute.
## Why use this in node?
Unless you're writing code that's intended to work in the browser, you probably shouldn't -
Node's `Buffer` does the job quite well.
The one function you may still be interested in, which Node's `Buffer` omits, is this one:
```js
function toUrlSafeBase64(base64) {
return base64
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
}
```
HOWEVER, if you believe that browser users would benefit from your library, this is a much
better alternative for simple use cases where you're dealing with small bits of code.
'use strict';
var Enc = require('./bytes.js');
// to Base64
function bufToBase64(buf) {
// we want to maintain api compatability with browser APIs,
// so we assume that this could be a Uint8Array
return Buffer.from(buf).toString('base64');
}
Enc.bufToBase64 = bufToBase64;
Enc.strToBase64 = function(str) {
return Buffer.from(str).toString('base64');
};
// from Base64
function base64ToBuf(b64) {
// node handles URL Safe Base64 automatically
return Buffer.from(b64, 'base64');
}
Enc.base64ToBuf = base64ToBuf;
Enc.base64ToStr = function(b64) {
return base64ToBuf(b64).toString('utf8');
};
// URL Safe Base64
Enc.urlBase64ToBase64 = function(u64) {
var r = u64 % 4;
if (2 === r) {
u64 += '==';
} else if (3 === r) {
u64 += '=';
}
return u64.replace(/-/g, '+').replace(/_/g, '/');
};
Enc.base64ToUrlBase64 = function(b64) {
return b64
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
};
Enc.bufToUrlBase64 = function(buf) {
return Enc.base64ToUrlBase64(bufToBase64(buf));
};
Enc.strToUrlBase64 = function(str) {
return Enc.base64ToUrlBase64(bufToBase64(Buffer.from(str)));
};
module.exports = Enc;
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
No preview for this file type
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
No preview for this file type
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff could not be displayed because it is too large.
This diff is collapsed. Click to expand it.
This diff could not be displayed because it is too large.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.