1 | // Copyright 2018 Joyent, Inc.
|
---|
2 |
|
---|
3 | module.exports = {
|
---|
4 | read: read,
|
---|
5 | write: write
|
---|
6 | };
|
---|
7 |
|
---|
8 | var assert = require('assert-plus');
|
---|
9 | var Buffer = require('safer-buffer').Buffer;
|
---|
10 | var utils = require('../utils');
|
---|
11 | var Key = require('../key');
|
---|
12 | var PrivateKey = require('../private-key');
|
---|
13 |
|
---|
14 | var pem = require('./pem');
|
---|
15 | var ssh = require('./ssh');
|
---|
16 | var rfc4253 = require('./rfc4253');
|
---|
17 | var dnssec = require('./dnssec');
|
---|
18 | var putty = require('./putty');
|
---|
19 |
|
---|
20 | var DNSSEC_PRIVKEY_HEADER_PREFIX = 'Private-key-format: v1';
|
---|
21 |
|
---|
22 | function read(buf, options) {
|
---|
23 | if (typeof (buf) === 'string') {
|
---|
24 | if (buf.trim().match(/^[-]+[ ]*BEGIN/))
|
---|
25 | return (pem.read(buf, options));
|
---|
26 | if (buf.match(/^\s*ssh-[a-z]/))
|
---|
27 | return (ssh.read(buf, options));
|
---|
28 | if (buf.match(/^\s*ecdsa-/))
|
---|
29 | return (ssh.read(buf, options));
|
---|
30 | if (buf.match(/^putty-user-key-file-2:/i))
|
---|
31 | return (putty.read(buf, options));
|
---|
32 | if (findDNSSECHeader(buf))
|
---|
33 | return (dnssec.read(buf, options));
|
---|
34 | buf = Buffer.from(buf, 'binary');
|
---|
35 | } else {
|
---|
36 | assert.buffer(buf);
|
---|
37 | if (findPEMHeader(buf))
|
---|
38 | return (pem.read(buf, options));
|
---|
39 | if (findSSHHeader(buf))
|
---|
40 | return (ssh.read(buf, options));
|
---|
41 | if (findPuTTYHeader(buf))
|
---|
42 | return (putty.read(buf, options));
|
---|
43 | if (findDNSSECHeader(buf))
|
---|
44 | return (dnssec.read(buf, options));
|
---|
45 | }
|
---|
46 | if (buf.readUInt32BE(0) < buf.length)
|
---|
47 | return (rfc4253.read(buf, options));
|
---|
48 | throw (new Error('Failed to auto-detect format of key'));
|
---|
49 | }
|
---|
50 |
|
---|
51 | function findPuTTYHeader(buf) {
|
---|
52 | var offset = 0;
|
---|
53 | while (offset < buf.length &&
|
---|
54 | (buf[offset] === 32 || buf[offset] === 10 || buf[offset] === 9))
|
---|
55 | ++offset;
|
---|
56 | if (offset + 22 <= buf.length &&
|
---|
57 | buf.slice(offset, offset + 22).toString('ascii').toLowerCase() ===
|
---|
58 | 'putty-user-key-file-2:')
|
---|
59 | return (true);
|
---|
60 | return (false);
|
---|
61 | }
|
---|
62 |
|
---|
63 | function findSSHHeader(buf) {
|
---|
64 | var offset = 0;
|
---|
65 | while (offset < buf.length &&
|
---|
66 | (buf[offset] === 32 || buf[offset] === 10 || buf[offset] === 9))
|
---|
67 | ++offset;
|
---|
68 | if (offset + 4 <= buf.length &&
|
---|
69 | buf.slice(offset, offset + 4).toString('ascii') === 'ssh-')
|
---|
70 | return (true);
|
---|
71 | if (offset + 6 <= buf.length &&
|
---|
72 | buf.slice(offset, offset + 6).toString('ascii') === 'ecdsa-')
|
---|
73 | return (true);
|
---|
74 | return (false);
|
---|
75 | }
|
---|
76 |
|
---|
77 | function findPEMHeader(buf) {
|
---|
78 | var offset = 0;
|
---|
79 | while (offset < buf.length &&
|
---|
80 | (buf[offset] === 32 || buf[offset] === 10))
|
---|
81 | ++offset;
|
---|
82 | if (buf[offset] !== 45)
|
---|
83 | return (false);
|
---|
84 | while (offset < buf.length &&
|
---|
85 | (buf[offset] === 45))
|
---|
86 | ++offset;
|
---|
87 | while (offset < buf.length &&
|
---|
88 | (buf[offset] === 32))
|
---|
89 | ++offset;
|
---|
90 | if (offset + 5 > buf.length ||
|
---|
91 | buf.slice(offset, offset + 5).toString('ascii') !== 'BEGIN')
|
---|
92 | return (false);
|
---|
93 | return (true);
|
---|
94 | }
|
---|
95 |
|
---|
96 | function findDNSSECHeader(buf) {
|
---|
97 | // private case first
|
---|
98 | if (buf.length <= DNSSEC_PRIVKEY_HEADER_PREFIX.length)
|
---|
99 | return (false);
|
---|
100 | var headerCheck = buf.slice(0, DNSSEC_PRIVKEY_HEADER_PREFIX.length);
|
---|
101 | if (headerCheck.toString('ascii') === DNSSEC_PRIVKEY_HEADER_PREFIX)
|
---|
102 | return (true);
|
---|
103 |
|
---|
104 | // public-key RFC3110 ?
|
---|
105 | // 'domain.com. IN KEY ...' or 'domain.com. IN DNSKEY ...'
|
---|
106 | // skip any comment-lines
|
---|
107 | if (typeof (buf) !== 'string') {
|
---|
108 | buf = buf.toString('ascii');
|
---|
109 | }
|
---|
110 | var lines = buf.split('\n');
|
---|
111 | var line = 0;
|
---|
112 | /* JSSTYLED */
|
---|
113 | while (lines[line].match(/^\;/))
|
---|
114 | line++;
|
---|
115 | if (lines[line].toString('ascii').match(/\. IN KEY /))
|
---|
116 | return (true);
|
---|
117 | if (lines[line].toString('ascii').match(/\. IN DNSKEY /))
|
---|
118 | return (true);
|
---|
119 | return (false);
|
---|
120 | }
|
---|
121 |
|
---|
122 | function write(key, options) {
|
---|
123 | throw (new Error('"auto" format cannot be used for writing'));
|
---|
124 | }
|
---|