corrade-nucleus-nucleons – Blame information for rev 26
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
22 | office | 1 | /** |
2 | * Javascript implementation of PKCS#1 PSS signature padding. |
||
3 | * |
||
4 | * @author Stefan Siegl |
||
5 | * |
||
6 | * Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de> |
||
7 | */ |
||
8 | var forge = require('./forge'); |
||
9 | require('./random'); |
||
10 | require('./util'); |
||
11 | |||
12 | // shortcut for PSS API |
||
13 | var pss = module.exports = forge.pss = forge.pss || {}; |
||
14 | |||
15 | /** |
||
16 | * Creates a PSS signature scheme object. |
||
17 | * |
||
18 | * There are several ways to provide a salt for encoding: |
||
19 | * |
||
20 | * 1. Specify the saltLength only and the built-in PRNG will generate it. |
||
21 | * 2. Specify the saltLength and a custom PRNG with 'getBytesSync' defined that |
||
22 | * will be used. |
||
23 | * 3. Specify the salt itself as a forge.util.ByteBuffer. |
||
24 | * |
||
25 | * @param options the options to use: |
||
26 | * md the message digest object to use, a forge md instance. |
||
27 | * mgf the mask generation function to use, a forge mgf instance. |
||
28 | * [saltLength] the length of the salt in octets. |
||
29 | * [prng] the pseudo-random number generator to use to produce a salt. |
||
30 | * [salt] the salt to use when encoding. |
||
31 | * |
||
32 | * @return a signature scheme object. |
||
33 | */ |
||
34 | pss.create = function(options) { |
||
35 | // backwards compatibility w/legacy args: hash, mgf, sLen |
||
36 | if(arguments.length === 3) { |
||
37 | options = { |
||
38 | md: arguments[0], |
||
39 | mgf: arguments[1], |
||
40 | saltLength: arguments[2] |
||
41 | }; |
||
42 | } |
||
43 | |||
44 | var hash = options.md; |
||
45 | var mgf = options.mgf; |
||
46 | var hLen = hash.digestLength; |
||
47 | |||
48 | var salt_ = options.salt || null; |
||
49 | if(typeof salt_ === 'string') { |
||
50 | // assume binary-encoded string |
||
51 | salt_ = forge.util.createBuffer(salt_); |
||
52 | } |
||
53 | |||
54 | var sLen; |
||
55 | if('saltLength' in options) { |
||
56 | sLen = options.saltLength; |
||
57 | } else if(salt_ !== null) { |
||
58 | sLen = salt_.length(); |
||
59 | } else { |
||
60 | throw new Error('Salt length not specified or specific salt not given.'); |
||
61 | } |
||
62 | |||
63 | if(salt_ !== null && salt_.length() !== sLen) { |
||
64 | throw new Error('Given salt length does not match length of given salt.'); |
||
65 | } |
||
66 | |||
67 | var prng = options.prng || forge.random; |
||
68 | |||
69 | var pssobj = {}; |
||
70 | |||
71 | /** |
||
72 | * Encodes a PSS signature. |
||
73 | * |
||
74 | * This function implements EMSA-PSS-ENCODE as per RFC 3447, section 9.1.1. |
||
75 | * |
||
76 | * @param md the message digest object with the hash to sign. |
||
77 | * @param modsBits the length of the RSA modulus in bits. |
||
78 | * |
||
79 | * @return the encoded message as a binary-encoded string of length |
||
80 | * ceil((modBits - 1) / 8). |
||
81 | */ |
||
82 | pssobj.encode = function(md, modBits) { |
||
83 | var i; |
||
84 | var emBits = modBits - 1; |
||
85 | var emLen = Math.ceil(emBits / 8); |
||
86 | |||
87 | /* 2. Let mHash = Hash(M), an octet string of length hLen. */ |
||
88 | var mHash = md.digest().getBytes(); |
||
89 | |||
90 | /* 3. If emLen < hLen + sLen + 2, output "encoding error" and stop. */ |
||
91 | if(emLen < hLen + sLen + 2) {> |
||
92 | < hLen + sLen + 2) { throw new Error('Message is too long to encrypt.');> |
||
93 | < hLen + sLen + 2) { }> |
||
94 | |||
95 | < hLen + sLen + 2) { /* 4. Generate a random octet string salt of length sLen; if sLen = 0,> |
||
96 | < hLen + sLen + 2) { * then salt is the empty string. */> |
||
97 | < hLen + sLen + 2) { var salt;> |
||
98 | < hLen + sLen + 2) { if(salt_ === null) {> |
||
99 | < hLen + sLen + 2) { salt = prng.getBytesSync(sLen);> |
||
100 | < hLen + sLen + 2) { } else {> |
||
101 | < hLen + sLen + 2) { salt = salt_.bytes();> |
||
102 | < hLen + sLen + 2) { }> |
||
103 | |||
104 | < hLen + sLen + 2) { /* 5. Let M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt; */> |
||
105 | < hLen + sLen + 2) { var m_ = new forge.util.ByteBuffer();> |
||
106 | < hLen + sLen + 2) { m_.fillWithByte(0, 8);> |
||
107 | < hLen + sLen + 2) { m_.putBytes(mHash);> |
||
108 | < hLen + sLen + 2) { m_.putBytes(salt);> |
||
109 | |||
110 | < hLen + sLen + 2) { /* 6. Let H = Hash(M'), an octet string of length hLen. */> |
||
111 | < hLen + sLen + 2) { hash.start();> |
||
112 | < hLen + sLen + 2) { hash.update(m_.getBytes());> |
||
113 | < hLen + sLen + 2) { var h = hash.digest().getBytes();> |
||
114 | |||
115 | < hLen + sLen + 2) { /* 7. Generate an octet string PS consisting of emLen - sLen - hLen - 2> |
||
116 | < hLen + sLen + 2) { * zero octets. The length of PS may be 0. */> |
||
117 | < hLen + sLen + 2) { var ps = new forge.util.ByteBuffer();> |
||
118 | < hLen + sLen + 2) { ps.fillWithByte(0, emLen - sLen - hLen - 2);> |
||
119 | |||
120 | < hLen + sLen + 2) { /* 8. Let DB = PS || 0x01 || salt; DB is an octet string of length> |
||
121 | < hLen + sLen + 2) { * emLen - hLen - 1. */> |
||
122 | < hLen + sLen + 2) { ps.putByte(0x01);> |
||
123 | < hLen + sLen + 2) { ps.putBytes(salt);> |
||
124 | < hLen + sLen + 2) { var db = ps.getBytes();> |
||
125 | |||
126 | < hLen + sLen + 2) { /* 9. Let dbMask = MGF(H, emLen - hLen - 1). */> |
||
127 | < hLen + sLen + 2) { var maskLen = emLen - hLen - 1;> |
||
128 | < hLen + sLen + 2) { var dbMask = mgf.generate(h, maskLen);> |
||
129 | |||
130 | < hLen + sLen + 2) { /* 10. Let maskedDB = DB \xor dbMask. */> |
||
131 | < hLen + sLen + 2) { var maskedDB = '';> |
||
132 | < hLen + sLen + 2) { for(i = 0; i < maskLen; i++) {>> |
||
133 | < hLen + sLen + 2) {< maskLen; i++) { maskedDB += String.fromCharCode(db.charCodeAt(i) ^ dbMask.charCodeAt(i));>> |
||
134 | < hLen + sLen + 2) {< maskLen; i++) { }>> |
||
135 | |||
136 | < hLen + sLen + 2) {< maskLen; i++) { /* 11. Set the leftmost 8emLen - emBits bits of the leftmost octet in>> |
||
137 | < hLen + sLen + 2) {< maskLen; i++) { * maskedDB to zero. */>> |
||
138 | < hLen + sLen + 2) {< maskLen; i++) { var mask = (0xFF00 >> (8 * emLen - emBits)) & 0xFF;>> |
||
139 | < hLen + sLen + 2) {< maskLen; i++) { maskedDB = String.fromCharCode(maskedDB.charCodeAt(0) & ~mask) +>> |
||
140 | < hLen + sLen + 2) {< maskLen; i++) { maskedDB.substr(1);>> |
||
141 | |||
142 | < hLen + sLen + 2) {< maskLen; i++) { /* 12. Let EM = maskedDB || H || 0xbc.>> |
||
143 | < hLen + sLen + 2) {< maskLen; i++) { * 13. Output EM. */>> |
||
144 | < hLen + sLen + 2) {< maskLen; i++) { return maskedDB + h + String.fromCharCode(0xbc);>> |
||
145 | < hLen + sLen + 2) {< maskLen; i++) { };>> |
||
146 | |||
147 | < hLen + sLen + 2) {< maskLen; i++) { /**>> |
||
148 | < hLen + sLen + 2) {< maskLen; i++) { * Verifies a PSS signature.>> |
||
149 | < hLen + sLen + 2) {< maskLen; i++) { *>> |
||
150 | < hLen + sLen + 2) {< maskLen; i++) { * This function implements EMSA-PSS-VERIFY as per RFC 3447, section 9.1.2.>> |
||
151 | < hLen + sLen + 2) {< maskLen; i++) { *>> |
||
152 | < hLen + sLen + 2) {< maskLen; i++) { * @param mHash the message digest hash, as a binary-encoded string, to>> |
||
153 | < hLen + sLen + 2) {< maskLen; i++) { * compare against the signature.>> |
||
154 | < hLen + sLen + 2) {< maskLen; i++) { * @param em the encoded message, as a binary-encoded string>> |
||
155 | < hLen + sLen + 2) {< maskLen; i++) { * (RSA decryption result).>> |
||
156 | < hLen + sLen + 2) {< maskLen; i++) { * @param modsBits the length of the RSA modulus in bits.>> |
||
157 | < hLen + sLen + 2) {< maskLen; i++) { *>> |
||
158 | < hLen + sLen + 2) {< maskLen; i++) { * @return true if the signature was verified, false if not.>> |
||
159 | < hLen + sLen + 2) {< maskLen; i++) { */>> |
||
160 | < hLen + sLen + 2) {< maskLen; i++) { pssobj.verify = function(mHash, em, modBits) {>> |
||
161 | < hLen + sLen + 2) {< maskLen; i++) { var i;>> |
||
162 | < hLen + sLen + 2) {< maskLen; i++) { var emBits = modBits - 1;>> |
||
163 | < hLen + sLen + 2) {< maskLen; i++) { var emLen = Math.ceil(emBits / 8);>> |
||
164 | |||
165 | < hLen + sLen + 2) {< maskLen; i++) { /* c. Convert the message representative m to an encoded message EM>> |
||
166 | < hLen + sLen + 2) {< maskLen; i++) { * of length emLen = ceil((modBits - 1) / 8) octets, where modBits>> |
||
167 | < hLen + sLen + 2) {< maskLen; i++) { * is the length in bits of the RSA modulus n */>> |
||
168 | < hLen + sLen + 2) {< maskLen; i++) { em = em.substr(-emLen);>> |
||
169 | |||
170 | < hLen + sLen + 2) {< maskLen; i++) { /* 3. If emLen < hLen + sLen + 2, output "inconsistent" and stop. */>> |
||
171 | < hLen + sLen + 2) {< maskLen; i++) { if(emLen < hLen + sLen + 2) {>> |
||
172 | < hLen + sLen + 2) {< maskLen; i++) { throw new Error('Inconsistent parameters to PSS signature verification.');>> |
||
173 | < hLen + sLen + 2) {< maskLen; i++) { }>> |
||
174 | |||
175 | < hLen + sLen + 2) {< maskLen; i++) { /* 4. If the rightmost octet of EM does not have hexadecimal value>> |
||
176 | < hLen + sLen + 2) {< maskLen; i++) { * 0xbc, output "inconsistent" and stop. */>> |
||
177 | < hLen + sLen + 2) {< maskLen; i++) { if(em.charCodeAt(emLen - 1) !== 0xbc) {>> |
||
178 | < hLen + sLen + 2) {< maskLen; i++) { throw new Error('Encoded message does not end in 0xBC.');>> |
||
179 | < hLen + sLen + 2) {< maskLen; i++) { }>> |
||
180 | |||
181 | < hLen + sLen + 2) {< maskLen; i++) { /* 5. Let maskedDB be the leftmost emLen - hLen - 1 octets of EM, and>> |
||
182 | < hLen + sLen + 2) {< maskLen; i++) { * let H be the next hLen octets. */>> |
||
183 | < hLen + sLen + 2) {< maskLen; i++) { var maskLen = emLen - hLen - 1;>> |
||
184 | < hLen + sLen + 2) {< maskLen; i++) { var maskedDB = em.substr(0, maskLen);>> |
||
185 | < hLen + sLen + 2) {< maskLen; i++) { var h = em.substr(maskLen, hLen);>> |
||
186 | |||
187 | < hLen + sLen + 2) {< maskLen; i++) { /* 6. If the leftmost 8emLen - emBits bits of the leftmost octet in>> |
||
188 | < hLen + sLen + 2) {< maskLen; i++) { * maskedDB are not all equal to zero, output "inconsistent" and stop. */>> |
||
189 | < hLen + sLen + 2) {< maskLen; i++) { var mask = (0xFF00 >> (8 * emLen - emBits)) & 0xFF;>> |
||
190 | < hLen + sLen + 2) {< maskLen; i++) { if((maskedDB.charCodeAt(0) & mask) !== 0) {>> |
||
191 | < hLen + sLen + 2) {< maskLen; i++) { throw new Error('Bits beyond keysize not zero as expected.');>> |
||
192 | < hLen + sLen + 2) {< maskLen; i++) { }>> |
||
193 | |||
194 | < hLen + sLen + 2) {< maskLen; i++) { /* 7. Let dbMask = MGF(H, emLen - hLen - 1). */>> |
||
195 | < hLen + sLen + 2) {< maskLen; i++) { var dbMask = mgf.generate(h, maskLen);>> |
||
196 | |||
197 | < hLen + sLen + 2) {< maskLen; i++) { /* 8. Let DB = maskedDB \xor dbMask. */>> |
||
198 | < hLen + sLen + 2) {< maskLen; i++) { var db = '';>> |
||
199 | < hLen + sLen + 2) {< maskLen; i++) { for(i = 0; i < maskLen; i++) {>> |
||
200 | < hLen + sLen + 2) {< maskLen; i++) { db += String.fromCharCode(maskedDB.charCodeAt(i) ^ dbMask.charCodeAt(i));>> |
||
201 | < hLen + sLen + 2) {< maskLen; i++) { }>> |
||
202 | |||
203 | < hLen + sLen + 2) {< maskLen; i++) { /* 9. Set the leftmost 8emLen - emBits bits of the leftmost octet>> |
||
204 | < hLen + sLen + 2) {< maskLen; i++) { * in DB to zero. */>> |
||
205 | < hLen + sLen + 2) {< maskLen; i++) { db = String.fromCharCode(db.charCodeAt(0) & ~mask) + db.substr(1);>> |
||
206 | |||
207 | < hLen + sLen + 2) {< maskLen; i++) { /* 10. If the emLen - hLen - sLen - 2 leftmost octets of DB are not zero>> |
||
208 | < hLen + sLen + 2) {< maskLen; i++) { * or if the octet at position emLen - hLen - sLen - 1 (the leftmost>> |
||
209 | < hLen + sLen + 2) {< maskLen; i++) { * position is "position 1") does not have hexadecimal value 0x01,>> |
||
210 | < hLen + sLen + 2) {< maskLen; i++) { * output "inconsistent" and stop. */>> |
||
211 | < hLen + sLen + 2) {< maskLen; i++) { var checkLen = emLen - hLen - sLen - 2;>> |
||
212 | < hLen + sLen + 2) {< maskLen; i++) { for(i = 0; i < checkLen; i++) {>> |
||
213 | < hLen + sLen + 2) {< maskLen; i++) { if(db.charCodeAt(i) !== 0x00) {>> |
||
214 | < hLen + sLen + 2) {< maskLen; i++) { throw new Error('Leftmost octets not zero as expected');>> |
||
215 | < hLen + sLen + 2) {< maskLen; i++) { }>> |
||
216 | < hLen + sLen + 2) {< maskLen; i++) { }>> |
||
217 | |||
218 | < hLen + sLen + 2) {< maskLen; i++) { if(db.charCodeAt(checkLen) !== 0x01) {>> |
||
219 | < hLen + sLen + 2) {< maskLen; i++) { throw new Error('Inconsistent PSS signature, 0x01 marker not found');>> |
||
220 | < hLen + sLen + 2) {< maskLen; i++) { }>> |
||
221 | |||
222 | < hLen + sLen + 2) {< maskLen; i++) { /* 11. Let salt be the last sLen octets of DB. */>> |
||
223 | < hLen + sLen + 2) {< maskLen; i++) { var salt = db.substr(-sLen);>> |
||
224 | |||
225 | < hLen + sLen + 2) {< maskLen; i++) { /* 12. Let M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt */>> |
||
226 | < hLen + sLen + 2) {< maskLen; i++) { var m_ = new forge.util.ByteBuffer();>> |
||
227 | < hLen + sLen + 2) {< maskLen; i++) { m_.fillWithByte(0, 8);>> |
||
228 | < hLen + sLen + 2) {< maskLen; i++) { m_.putBytes(mHash);>> |
||
229 | < hLen + sLen + 2) {< maskLen; i++) { m_.putBytes(salt);>> |
||
230 | |||
231 | < hLen + sLen + 2) {< maskLen; i++) { /* 13. Let H' = Hash(M'), an octet string of length hLen. */>> |
||
232 | < hLen + sLen + 2) {< maskLen; i++) { hash.start();>> |
||
233 | < hLen + sLen + 2) {< maskLen; i++) { hash.update(m_.getBytes());>> |
||
234 | < hLen + sLen + 2) {< maskLen; i++) { var h_ = hash.digest().getBytes();>> |
||
235 | |||
236 | < hLen + sLen + 2) {< maskLen; i++) { /* 14. If H = H', output "consistent." Otherwise, output "inconsistent." */>> |
||
237 | < hLen + sLen + 2) {< maskLen; i++) { return h === h_;>> |
||
238 | < hLen + sLen + 2) {< maskLen; i++) { };>> |
||
239 | |||
240 | < hLen + sLen + 2) {< maskLen; i++) { return pssobj;>> |
||
241 | < hLen + sLen + 2) {< maskLen; i++) {};>> |