1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 var utils
= require('utils');
6 var internalAPI
= require('enterprise.platformKeys.internalAPI');
7 var intersect
= require('enterprise.platformKeys.utils').intersect
;
8 var KeyPair
= require('enterprise.platformKeys.KeyPair').KeyPair
;
9 var keyModule
= require('enterprise.platformKeys.Key');
10 var getSpki
= keyModule
.getSpki
;
11 var KeyUsage
= keyModule
.KeyUsage
;
13 var normalizeAlgorithm
=
14 requireNative('enterprise_platform_keys_natives').NormalizeAlgorithm
;
16 // This error is thrown by the internal and public API's token functions and
17 // must be rethrown by this custom binding. Keep this in sync with the C++ part
19 var errorInvalidToken
= "The token is not valid.";
21 // The following errors are specified in WebCrypto.
22 // TODO(pneubeck): These should be DOMExceptions.
23 function CreateNotSupportedError() {
24 return new Error('The algorithm is not supported');
27 function CreateInvalidAccessError() {
28 return new Error('The requested operation is not valid for the provided key');
31 function CreateDataError() {
32 return new Error('Data provided to an operation does not meet requirements');
35 function CreateSyntaxError() {
36 return new Error('A required parameter was missing or out-of-range');
39 function CreateOperationError() {
40 return new Error('The operation failed for an operation-specific reason');
43 // Catches an |internalErrorInvalidToken|. If so, forwards it to |reject| and
45 function catchInvalidTokenError(reject
) {
46 if (chrome
.runtime
.lastError
&&
47 chrome
.runtime
.lastError
.message
== errorInvalidToken
) {
48 reject(chrome
.runtime
.lastError
);
54 // Returns true if |array| is a BigInteger describing the standard public
55 // exponent 65537. In particular, it ignores leading zeros as required by the
56 // BigInteger definition in WebCrypto.
57 function equalsStandardPublicExponent(array
) {
58 var expected
= [0x01, 0x00, 0x01];
59 if (array
.length
< expected
.length
)
61 for (var i
= 0; i
< array
.length
; i
++) {
62 var expectedDigit
= 0;
63 if (i
< expected
.length
) {
64 // |expected| is symmetric, endianness doesn't matter.
65 expectedDigit
= expected
[i
];
67 if (array
[array
.length
- 1 - i
] !== expectedDigit
)
74 * Implementation of WebCrypto.SubtleCrypto used in enterprise.platformKeys.
75 * @param {string} tokenId The id of the backing Token.
78 var SubtleCryptoImpl = function(tokenId
) {
79 this.tokenId
= tokenId
;
82 SubtleCryptoImpl
.prototype.generateKey
=
83 function(algorithm
, extractable
, keyUsages
) {
84 var subtleCrypto
= this;
85 return new Promise(function(resolve
, reject
) {
86 // TODO(pneubeck): Apply the algorithm normalization of the WebCrypto
90 // Note: This deviates from WebCrypto.SubtleCrypto.
91 throw CreateNotSupportedError();
93 if (intersect(keyUsages
, [KeyUsage
.sign
, KeyUsage
.verify
]).length
!=
95 throw CreateDataError();
97 var normalizedAlgorithmParameters
=
98 normalizeAlgorithm(algorithm
, 'GenerateKey');
99 if (!normalizedAlgorithmParameters
) {
100 // TODO(pneubeck): It's not clear from the WebCrypto spec which error to
102 throw CreateSyntaxError();
105 // normalizeAlgorithm returns an array, but publicExponent should be a
107 normalizedAlgorithmParameters
.publicExponent
=
108 new Uint8Array(normalizedAlgorithmParameters
.publicExponent
);
110 if (normalizedAlgorithmParameters
.name
!== 'RSASSA-PKCS1-v1_5' ||
111 !equalsStandardPublicExponent(
112 normalizedAlgorithmParameters
.publicExponent
)) {
113 // Note: This deviates from WebCrypto.SubtleCrypto.
114 throw CreateNotSupportedError();
117 internalAPI
.generateKey(subtleCrypto
.tokenId
,
118 normalizedAlgorithmParameters
.modulusLength
,
120 if (catchInvalidTokenError(reject
))
122 if (chrome
.runtime
.lastError
) {
123 reject(CreateOperationError());
126 resolve(new KeyPair(spki
, normalizedAlgorithmParameters
, keyUsages
));
131 SubtleCryptoImpl
.prototype.sign = function(algorithm
, key
, dataView
) {
132 var subtleCrypto
= this;
133 return new Promise(function(resolve
, reject
) {
134 if (key
.type
!= 'private' || key
.usages
.indexOf(KeyUsage
.sign
) == -1)
135 throw CreateInvalidAccessError();
137 var normalizedAlgorithmParameters
=
138 normalizeAlgorithm(algorithm
, 'Sign');
139 if (!normalizedAlgorithmParameters
) {
140 // TODO(pneubeck): It's not clear from the WebCrypto spec which error to
142 throw CreateSyntaxError();
145 // Create an ArrayBuffer that equals the dataView. Note that dataView.buffer
146 // might contain more data than dataView.
147 var data
= dataView
.buffer
.slice(dataView
.byteOffset
,
148 dataView
.byteOffset
+ dataView
.byteLength
);
149 internalAPI
.sign(subtleCrypto
.tokenId
,
151 key
.algorithm
.hash
.name
,
153 function(signature
) {
154 if (catchInvalidTokenError(reject
))
156 if (chrome
.runtime
.lastError
) {
157 reject(CreateOperationError());
165 SubtleCryptoImpl
.prototype.exportKey = function(format
, key
) {
166 return new Promise(function(resolve
, reject
) {
167 if (format
== 'pkcs8') {
168 // Either key.type is not 'private' or the key is not extractable. In both
169 // cases the error is the same.
170 throw CreateInvalidAccessError();
171 } else if (format
== 'spki') {
172 if (key
.type
!= 'public')
173 throw CreateInvalidAccessError();
174 resolve(getSpki(key
));
176 // TODO(pneubeck): It should be possible to export to format 'jwk'.
177 throw CreateNotSupportedError();
182 exports
.SubtleCrypto
=
183 utils
.expose('SubtleCrypto',
185 {functions
:['generateKey', 'sign', 'exportKey']});