Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
d89823be
Commit
d89823be
authored
Jun 17, 2016
by
Jason Simmons
Committed by
GitHub
Jun 17, 2016
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Remove packages/flx and the FLX code signing scheme (#4612)
parent
3a88a973
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
2 additions
and
584 deletions
+2
-584
test.sh
dev/bots/test.sh
+0
-1
flx.dart
packages/flutter_tools/lib/src/flx.dart
+2
-23
pubspec.yaml
packages/flutter_tools/pubspec.yaml
+0
-3
bundle.dart
packages/flx/lib/bundle.dart
+0
-144
signing.dart
packages/flx/lib/signing.dart
+0
-187
pubspec.yaml
packages/flx/pubspec.yaml
+0
-21
all.dart
packages/flx/test/all.dart
+0
-11
bundle_test.dart
packages/flx/test/bundle_test.dart
+0
-71
signing_test.dart
packages/flx/test/signing_test.dart
+0
-123
No files found.
dev/bots/test.sh
View file @
d89823be
...
...
@@ -16,7 +16,6 @@ flutter analyze --flutter-repo
(
cd
packages/flutter_sprites
;
flutter
test
)
(
cd
packages/flutter_test
;
flutter
test
)
(
cd
packages/flutter_tools
;
dart
-c
test
/all.dart
)
(
cd
packages/flx
;
dart
-c
test
/all.dart
)
(
cd
dev/manual_tests
;
flutter
test
)
(
cd
examples/hello_world
;
flutter
test
)
...
...
packages/flutter_tools/lib/src/flx.dart
View file @
d89823be
...
...
@@ -6,8 +6,6 @@ import 'dart:async';
import
'dart:convert'
;
import
'dart:io'
;
import
'package:flx/bundle.dart'
;
import
'package:flx/signing.dart'
;
import
'package:json_schema/json_schema.dart'
;
import
'package:path/path.dart'
as
path
;
import
'package:yaml/yaml.dart'
;
...
...
@@ -449,29 +447,10 @@ Future<int> assemble({
if
(
fontManifest
!=
null
)
zipBuilder
.
addEntry
(
fontManifest
);
AsymmetricKeyPair
<
PublicKey
,
PrivateKey
>
keyPair
=
keyPairFromPrivateKeyFileSync
(
privateKeyPath
);
printTrace
(
'KeyPair from
$privateKeyPath
:
$keyPair
.'
);
if
(
keyPair
!=
null
)
{
printTrace
(
'Calling CipherParameters.seedRandom().'
);
CipherParameters
.
get
().
seedRandom
();
}
File
zipFile
=
new
File
(
outputPath
.
substring
(
0
,
outputPath
.
length
-
4
)
+
'.zip'
);
printTrace
(
'Encoding zip file to
${zipFile.path}
'
);
zipBuilder
.
createZip
(
zipFile
,
new
Directory
(
workingDirPath
));
List
<
int
>
zipBytes
=
zipFile
.
readAsBytesSync
();
ensureDirectoryExists
(
outputPath
);
printTrace
(
'Creating flx at
$outputPath
.'
);
Bundle
bundle
=
new
Bundle
.
fromContent
(
path:
outputPath
,
manifest:
manifestDescriptor
,
contentBytes:
zipBytes
,
keyPair:
keyPair
);
bundle
.
writeSync
();
printTrace
(
'Encoding zip file to
$outputPath
'
);
zipBuilder
.
createZip
(
new
File
(
outputPath
),
new
Directory
(
workingDirPath
));
printTrace
(
'Built
$outputPath
.'
);
...
...
packages/flutter_tools/pubspec.yaml
View file @
d89823be
...
...
@@ -26,9 +26,6 @@ dependencies:
xml
:
^2.4.1
yaml
:
^2.1.3
flx
:
path
:
../flx
# We depend on very specific internal implementation details of the
# 'test' package, which change between versions, so here we pin it
# precisely.
...
...
packages/flx/lib/bundle.dart
deleted
100644 → 0
View file @
3a88a973
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:async'
;
import
'dart:convert'
;
import
'dart:io'
;
import
'dart:typed_data'
;
import
'package:bignum/bignum.dart'
;
import
'signing.dart'
;
// Magic string we put at the top of all bundle files.
const
String
kBundleMagic
=
'#!mojo mojo:flutter
\n
'
;
// Prefix of the above, used when reading bundle files. This allows us to be
// more flexbile about what we accept.
const
String
kBundleMagicPrefix
=
'#!mojo '
;
typedef
Stream
<
List
<
int
>>
StreamOpener
();
Future
<
List
<
int
>>
_readBytesWithLength
(
RandomAccessFile
file
)
async
{
ByteData
buffer
=
new
ByteData
(
4
);
await
file
.
readInto
(
buffer
.
buffer
.
asUint8List
());
int
length
=
buffer
.
getUint32
(
0
,
Endianness
.
LITTLE_ENDIAN
);
return
await
file
.
read
(
length
);
}
const
int
kMaxLineLen
=
10
*
1024
;
const
int
kNewline
=
0x0A
;
Future
<
String
>
_readLine
(
RandomAccessFile
file
)
async
{
String
line
=
''
;
while
(
line
.
length
<
kMaxLineLen
)
{
int
byte
=
await
file
.
readByte
();
if
(
byte
==
-
1
||
byte
==
kNewline
)
break
;
line
+=
new
String
.
fromCharCode
(
byte
);
}
return
line
;
}
// Writes a 32-bit length followed by the content of [bytes].
void
_writeBytesWithLengthSync
(
RandomAccessFile
outputFile
,
List
<
int
>
bytes
)
{
if
(
bytes
==
null
)
bytes
=
new
Uint8List
(
0
);
assert
(
bytes
.
length
<
0xffffffff
);
ByteData
length
=
new
ByteData
(
4
)..
setUint32
(
0
,
bytes
.
length
,
Endianness
.
LITTLE_ENDIAN
);
outputFile
.
writeFromSync
(
length
.
buffer
.
asUint8List
());
outputFile
.
writeFromSync
(
bytes
);
}
// Represents a parsed .flx Bundle. Contains information from the bundle's
// header, as well as an open File handle positioned where the zip content
// begins.
// The bundle format is:
// #!mojo <any string>\n
// <32-bit length><signature of the manifest data>
// <32-bit length><manifest data>
// <zip content>
//
// The manifest is a JSON string containing the following keys:
// (optional) name: the name of the package.
// version: the package version.
// update-url: the base URL to download a new manifest and bundle.
// key: a BASE-64 encoded DER-encoded ASN.1 representation of the Q point of the
// ECDSA public key that was used to sign this manifest.
// content-hash: an integer SHA-256 hash value of the <zip content>.
class
Bundle
{
Bundle
.
_fromFile
(
this
.
path
);
Bundle
.
fromContent
({
this
.
path
,
this
.
manifest
,
List
<
int
>
contentBytes
,
AsymmetricKeyPair
<
PublicKey
,
PrivateKey
>
keyPair
})
:
_contentBytes
=
contentBytes
{
assert
(
path
!=
null
);
assert
(
manifest
!=
null
);
assert
(
_contentBytes
!=
null
);
manifestBytes
=
serializeManifest
(
manifest
,
keyPair
?.
publicKey
,
_contentBytes
);
signatureBytes
=
signManifest
(
manifestBytes
,
keyPair
?.
privateKey
);
_openContentStream
=
()
=>
new
Stream
<
List
<
int
>>.
fromIterable
(<
List
<
int
>>[
_contentBytes
]);
}
final
String
path
;
List
<
int
>
signatureBytes
;
List
<
int
>
manifestBytes
;
Map
<
String
,
dynamic
>
manifest
;
// Callback to open a Stream containing the bundle content data.
StreamOpener
_openContentStream
;
// Zip content bytes. Only valid when created in memory.
List
<
int
>
_contentBytes
;
Future
<
bool
>
_readHeader
()
async
{
RandomAccessFile
file
=
await
new
File
(
path
).
open
();
String
magic
=
await
_readLine
(
file
);
if
(!
magic
.
startsWith
(
kBundleMagicPrefix
))
{
file
.
close
();
return
false
;
}
signatureBytes
=
await
_readBytesWithLength
(
file
);
manifestBytes
=
await
_readBytesWithLength
(
file
);
int
contentOffset
=
await
file
.
position
();
_openContentStream
=
()
=>
new
File
(
path
).
openRead
(
contentOffset
);
file
.
close
();
String
manifestString
=
UTF8
.
decode
(
manifestBytes
);
manifest
=
JSON
.
decode
(
manifestString
);
return
true
;
}
static
Future
<
Bundle
>
readHeader
(
String
path
)
async
{
Bundle
bundle
=
new
Bundle
.
_fromFile
(
path
);
if
(!
await
bundle
.
_readHeader
())
return
null
;
return
bundle
;
}
// Verifies that the package has a valid signature and content.
Future
<
bool
>
verifyContent
()
async
{
if
(!
verifyManifestSignature
(
manifest
,
manifestBytes
,
signatureBytes
))
return
false
;
Stream
<
List
<
int
>>
content
=
_openContentStream
();
BigInteger
expectedHash
=
new
BigInteger
(
manifest
[
'content-hash'
],
10
);
if
(!
await
verifyContentHash
(
expectedHash
,
content
))
return
false
;
return
true
;
}
// Writes the in-memory representation to disk.
void
writeSync
()
{
assert
(
_contentBytes
!=
null
);
RandomAccessFile
outputFile
=
new
File
(
path
).
openSync
(
mode:
FileMode
.
WRITE
);
outputFile
.
writeStringSync
(
kBundleMagic
);
_writeBytesWithLengthSync
(
outputFile
,
signatureBytes
);
_writeBytesWithLengthSync
(
outputFile
,
manifestBytes
);
outputFile
.
writeFromSync
(
_contentBytes
);
outputFile
.
close
();
}
}
packages/flx/lib/signing.dart
deleted
100644 → 0
View file @
3a88a973
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:async'
;
import
'dart:convert'
;
import
'dart:io'
;
import
'dart:typed_data'
;
import
'package:asn1lib/asn1lib.dart'
;
import
'package:bignum/bignum.dart'
;
import
'package:crypto/crypto.dart'
hide
Digest
;
import
'package:pointycastle/pointycastle.dart'
;
export
'package:pointycastle/pointycastle.dart'
show
AsymmetricKeyPair
,
PublicKey
,
PrivateKey
;
// The ECDSA algorithm parameters we're using. These match the parameters used
// by the Flutter updater package.
class
CipherParameters
{
final
String
signerAlgorithm
=
'SHA-256/ECDSA'
;
final
String
hashAlgorithm
=
'SHA-256'
;
final
ECDomainParameters
domain
=
new
ECDomainParameters
(
'prime256v1'
);
SecureRandom
get
random
{
if
(
_random
==
null
)
_initRandom
(
new
Uint8List
(
16
),
new
Uint8List
(
16
));
return
_random
;
}
// Seeds our secure random number generator using data from /dev/urandom.
// Disclaimer: I don't really understand why we need 2 parameters for
// pointycastle's API.
void
seedRandom
()
{
if
(
_random
!=
null
)
return
;
try
{
RandomAccessFile
file
=
new
File
(
"/dev/urandom"
).
openSync
();
Uint8List
key
=
new
Uint8List
.
fromList
(
file
.
readSync
(
16
));
Uint8List
iv
=
new
Uint8List
.
fromList
(
file
.
readSync
(
16
));
_initRandom
(
key
,
iv
);
file
.
closeSync
();
}
on
FileSystemException
{
// TODO(mpcomplete): need an entropy source on Windows. We might get this
// for free from Dart itself soon.
print
(
"Warning: Failed to seed random number generator. No /dev/urandom."
);
}
}
SecureRandom
_random
;
void
_initRandom
(
Uint8List
key
,
Uint8List
iv
)
{
KeyParameter
keyParam
=
new
KeyParameter
(
key
);
ParametersWithIV
<
KeyParameter
>
params
=
new
ParametersWithIV
<
KeyParameter
>(
keyParam
,
iv
);
_random
=
new
SecureRandom
(
'AES/CTR/AUTO-SEED-PRNG'
)
..
seed
(
params
);
}
static
CipherParameters
get
()
=>
_params
;
static
CipherParameters
_init
()
{
return
new
CipherParameters
();
}
}
final
CipherParameters
_params
=
CipherParameters
.
_init
();
// Returns a serialized manifest, with the public key and hash of the content
// included.
Uint8List
serializeManifest
(
Map
<
String
,
dynamic
>
manifestDescriptor
,
ECPublicKey
publicKey
,
Uint8List
zipBytes
)
{
if
(
manifestDescriptor
==
null
)
return
null
;
final
List
<
String
>
kSavedKeys
=
<
String
>[
'name'
,
'version'
,
'update-url'
];
Map
<
String
,
dynamic
>
outputManifest
=
new
Map
<
String
,
dynamic
>();
manifestDescriptor
.
forEach
((
String
key
,
dynamic
value
)
{
if
(
kSavedKeys
.
contains
(
key
))
outputManifest
[
key
]
=
value
;
});
if
(
publicKey
!=
null
)
outputManifest
[
'key'
]
=
BASE64
.
encode
(
publicKey
.
Q
.
getEncoded
());
List
<
int
>
hash
=
sha256
.
convert
(
zipBytes
).
bytes
;
BigInteger
zipHashInt
=
new
BigInteger
.
fromBytes
(
1
,
hash
);
outputManifest
[
'content-hash'
]
=
zipHashInt
.
intValue
();
return
new
Uint8List
.
fromList
(
UTF8
.
encode
(
JSON
.
encode
(
outputManifest
)));
}
// Returns the ASN.1 encoded signature of the input manifestBytes.
Uint8List
signManifest
(
Uint8List
manifestBytes
,
ECPrivateKey
privateKey
)
{
if
(
manifestBytes
==
null
||
privateKey
==
null
)
return
new
Uint8List
(
0
);
Signer
signer
=
new
Signer
(
_params
.
signerAlgorithm
);
PrivateKeyParameter
<
PrivateKey
>
params
=
new
PrivateKeyParameter
<
PrivateKey
>(
privateKey
);
signer
.
init
(
true
,
new
ParametersWithRandom
<
PrivateKeyParameter
<
PrivateKey
>>(
params
,
_params
.
random
));
ECSignature
signature
=
signer
.
generateSignature
(
manifestBytes
);
ASN1Sequence
asn1
=
new
ASN1Sequence
()
..
add
(
new
ASN1Integer
(
signature
.
r
))
..
add
(
new
ASN1Integer
(
signature
.
s
));
return
asn1
.
encodedBytes
;
}
bool
verifyManifestSignature
(
Map
<
String
,
dynamic
>
manifest
,
Uint8List
manifestBytes
,
Uint8List
signatureBytes
)
{
ECSignature
signature
=
_asn1ParseSignature
(
signatureBytes
);
if
(
signature
==
null
)
return
false
;
List
<
int
>
keyBytes
=
BASE64
.
decode
(
manifest
[
'key'
]);
ECPoint
q
=
_params
.
domain
.
curve
.
decodePoint
(
keyBytes
);
PublicKey
publicKey
=
new
ECPublicKey
(
q
,
_params
.
domain
);
Signer
signer
=
new
Signer
(
_params
.
signerAlgorithm
);
signer
.
init
(
false
,
new
PublicKeyParameter
<
PublicKey
>(
publicKey
));
return
signer
.
verifySignature
(
manifestBytes
,
signature
);
}
Future
<
bool
>
verifyContentHash
(
BigInteger
expectedHash
,
Stream
<
List
<
int
>>
content
)
async
{
// Hash the file incrementally.
Digest
hasher
=
new
Digest
(
_params
.
hashAlgorithm
);
await
content
.
forEach
((
List
<
int
>
chunk
)
{
hasher
.
update
(
chunk
,
0
,
chunk
.
length
);
});
Uint8List
hashBytes
=
new
Uint8List
(
hasher
.
digestSize
);
int
len
=
hasher
.
doFinal
(
hashBytes
,
0
);
hashBytes
=
hashBytes
.
sublist
(
0
,
len
);
BigInteger
actualHash
=
new
BigInteger
.
fromBytes
(
1
,
hashBytes
);
return
expectedHash
==
actualHash
;
}
// Parses a DER-encoded ASN.1 ECDSA private key block.
ECPrivateKey
_asn1ParsePrivateKey
(
ECDomainParameters
ecDomain
,
Uint8List
privateKey
)
{
ASN1Parser
parser
=
new
ASN1Parser
(
privateKey
);
ASN1Sequence
seq
=
parser
.
nextObject
();
assert
(
seq
.
elements
.
length
>=
2
);
ASN1OctetString
keyOct
=
seq
.
elements
[
1
];
BigInteger
d
=
new
BigInteger
.
fromBytes
(
1
,
keyOct
.
octets
);
return
new
ECPrivateKey
(
d
,
ecDomain
);
}
// Parses a DER-encoded ASN.1 ECDSA signature block.
ECSignature
_asn1ParseSignature
(
Uint8List
signature
)
{
try
{
ASN1Parser
parser
=
new
ASN1Parser
(
signature
);
ASN1Object
object
=
parser
.
nextObject
();
if
(
object
is
!
ASN1Sequence
)
return
null
;
ASN1Sequence
sequence
=
object
;
if
(!(
sequence
.
elements
.
length
==
2
&&
sequence
.
elements
[
0
]
is
ASN1Integer
&&
sequence
.
elements
[
1
]
is
ASN1Integer
))
return
null
;
ASN1Integer
r
=
sequence
.
elements
[
0
];
ASN1Integer
s
=
sequence
.
elements
[
1
];
return
new
ECSignature
(
r
.
valueAsPositiveBigInteger
,
s
.
valueAsPositiveBigInteger
);
}
on
ASN1Exception
{
return
null
;
}
}
PublicKey
_publicKeyFromPrivateKey
(
ECPrivateKey
privateKey
)
{
ECPoint
Q
=
privateKey
.
parameters
.
G
*
privateKey
.
d
;
return
new
ECPublicKey
(
Q
,
privateKey
.
parameters
);
}
AsymmetricKeyPair
<
PublicKey
,
PrivateKey
>
keyPairFromPrivateKeyFileSync
(
String
privateKeyPath
)
{
File
file
=
new
File
(
privateKeyPath
);
if
(!
file
.
existsSync
())
return
null
;
return
keyPairFromPrivateKeyBytes
(
file
.
readAsBytesSync
());
}
AsymmetricKeyPair
<
PublicKey
,
PrivateKey
>
keyPairFromPrivateKeyBytes
(
List
<
int
>
privateKeyBytes
)
{
ECPrivateKey
privateKey
=
_asn1ParsePrivateKey
(
_params
.
domain
,
new
Uint8List
.
fromList
(
privateKeyBytes
));
if
(
privateKey
==
null
)
return
null
;
PublicKey
publicKey
=
_publicKeyFromPrivateKey
(
privateKey
);
return
new
AsymmetricKeyPair
<
PublicKey
,
PrivateKey
>(
publicKey
,
privateKey
);
}
packages/flx/pubspec.yaml
deleted
100644 → 0
View file @
3a88a973
name
:
flx
version
:
0.0.10
author
:
Flutter Authors <flutter-dev@googlegroups.com>
description
:
Library for dealing with Flutter bundle (.flx) files
homepage
:
https://github.com/flutter/flutter/tree/master/packages/flx
dependencies
:
bignum
:
^0.1.0
asn1lib
:
^0.4.1
pointycastle
:
0.10.0
crypto
:
'
>=1.1.1
<3.0.0'
environment
:
sdk
:
'
>=1.16.0
<2.0.0'
dev_dependencies
:
flutter_test
:
path
:
../flutter_test
# Exclude this package from the hosted API docs.
dartdoc
:
nodoc
:
true
packages/flx/test/all.dart
deleted
100644 → 0
View file @
3a88a973
// Copyright (c) 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'bundle_test.dart'
as
bundle_test
;
import
'signing_test.dart'
as
signing_test
;
void
main
(
)
{
bundle_test
.
main
();
signing_test
.
main
();
}
packages/flx/test/bundle_test.dart
deleted
100644 → 0
View file @
3a88a973
import
'dart:async'
;
import
'dart:convert'
;
import
'dart:io'
;
import
'dart:typed_data'
;
import
'package:flx/bundle.dart'
;
import
'package:flx/signing.dart'
;
import
'package:test/test.dart'
;
Future
<
Null
>
main
()
async
{
// The following constant was generated via the openssl shell commands:
// openssl ecparam -genkey -name prime256v1 -out privatekey.pem
// openssl ec -in privatekey.pem -outform DER | base64
const
String
kPrivateKeyBase64
=
'MHcCAQEEIG4Xt+MgsdP/o89kAHz7EVVLKkN+DUfpaBtZfMyFGbUgoAoGCCqGSM49AwEHoUQDQgAElPtbBVPPqKHYXYAgHaxB2hL6sXeFc99YLijTAuAPe2Nbhywan+v4k+nFm0TJJW/mkV+nH+fyBZ98t4UcFCqkOg=='
;
final
List
<
int
>
kPrivateKeyDER
=
BASE64
.
decode
(
kPrivateKeyBase64
);
// Test manifest.
final
Map
<
String
,
dynamic
>
kManifest
=
<
String
,
dynamic
>{
'name'
:
'test app'
,
'version'
:
'1.0.0'
};
// Simple test byte pattern.
final
Uint8List
kTestBytes
=
new
Uint8List
.
fromList
(<
int
>[
1
,
2
,
3
]);
// Create a temp dir and file for the bundle.
Directory
tempDir
=
Directory
.
systemTemp
.
createTempSync
(
'bundle_test'
);
String
bundlePath
=
tempDir
.
path
+
'/bundle.flx'
;
AsymmetricKeyPair
<
PublicKey
,
PrivateKey
>
keyPair
=
keyPairFromPrivateKeyBytes
(
kPrivateKeyDER
);
Map
<
String
,
dynamic
>
manifest
=
JSON
.
decode
(
UTF8
.
decode
(
serializeManifest
(
kManifest
,
keyPair
.
publicKey
,
kTestBytes
)
));
test
(
'verifyContent works'
,
()
async
{
Bundle
bundle
=
new
Bundle
.
fromContent
(
path:
bundlePath
,
manifest:
manifest
,
contentBytes:
kTestBytes
,
keyPair:
keyPair
);
bool
verifies
=
await
bundle
.
verifyContent
();
expect
(
verifies
,
equals
(
true
));
});
test
(
'write/read works'
,
()
async
{
Bundle
bundle
=
new
Bundle
.
fromContent
(
path:
bundlePath
,
manifest:
manifest
,
contentBytes:
kTestBytes
,
keyPair:
keyPair
);
bundle
.
writeSync
();
Bundle
diskBundle
=
await
Bundle
.
readHeader
(
bundlePath
);
expect
(
diskBundle
!=
null
,
equals
(
true
));
expect
(
diskBundle
.
manifestBytes
,
equals
(
bundle
.
manifestBytes
));
expect
(
diskBundle
.
signatureBytes
,
equals
(
bundle
.
signatureBytes
));
expect
(
diskBundle
.
manifest
[
'key'
],
equals
(
bundle
.
manifest
[
'key'
]));
expect
(
diskBundle
.
manifest
[
'key'
],
equals
(
manifest
[
'key'
]));
bool
verifies
=
await
diskBundle
.
verifyContent
();
expect
(
verifies
,
equals
(
true
));
});
test
(
'cleanup'
,
()
async
{
tempDir
.
deleteSync
(
recursive:
true
);
});
}
packages/flx/test/signing_test.dart
deleted
100644 → 0
View file @
3a88a973
import
'dart:async'
;
import
'dart:convert'
;
import
'dart:typed_data'
;
import
'package:bignum/bignum.dart'
;
import
'package:flx/signing.dart'
;
import
'package:pointycastle/pointycastle.dart'
hide
CipherParameters
;
import
'package:test/test.dart'
;
Future
<
Null
>
main
()
async
{
// The following constant was generated via the openssl shell commands:
// openssl ecparam -genkey -name prime256v1 -out privatekey.pem
// openssl ec -in privatekey.pem -outform DER | base64
const
String
kPrivateKeyBase64
=
'MHcCAQEEIG4Xt+MgsdP/o89kAHz7EVVLKkN+DUfpaBtZfMyFGbUgoAoGCCqGSM49AwEHoUQDQgAElPtbBVPPqKHYXYAgHaxB2hL6sXeFc99YLijTAuAPe2Nbhywan+v4k+nFm0TJJW/mkV+nH+fyBZ98t4UcFCqkOg=='
;
final
List
<
int
>
kPrivateKeyDER
=
BASE64
.
decode
(
kPrivateKeyBase64
);
// Unpacked values of the above private key.
const
int
kPrivateKeyD
=
0x6e17b7e320b1d3ffa3cf64007cfb11554b2a437e0d47e9681b597ccc8519b520
;
const
int
kPublicKeyQx
=
0x94fb5b0553cfa8a1d85d80201dac41da12fab1778573df582e28d302e00f7b63
;
const
int
kPublicKeyQy
=
0x5b872c1a9febf893e9c59b44c9256fe6915fa71fe7f2059f7cb7851c142aa43a
;
// Test manifest.
final
Map
<
String
,
dynamic
>
kManifest
=
<
String
,
dynamic
>{
'name'
:
'test app'
,
'version'
:
'1.0.0'
};
// Simple test byte pattern (flat and in chunked form) and its SHA-256 hash.
final
Uint8List
kTestBytes
=
new
Uint8List
.
fromList
(<
int
>[
1
,
2
,
3
]);
final
List
<
Uint8List
>
kTestBytesList
=
<
Uint8List
>[
new
Uint8List
.
fromList
(<
int
>[
1
,
2
]),
new
Uint8List
.
fromList
(<
int
>[
3
])];
final
int
kTestHash
=
0x039058c6f2c0cb492c533b0a4d14ef77cc0f78abccced5287d84a1a2011cfb81
;
// Set up a key generator.
CipherParameters
cipher
=
CipherParameters
.
get
();
cipher
.
seedRandom
();
ECKeyGeneratorParameters
ecParams
=
new
ECKeyGeneratorParameters
(
cipher
.
domain
);
ParametersWithRandom
<
ECKeyGeneratorParameters
>
keyGeneratorParams
=
new
ParametersWithRandom
<
ECKeyGeneratorParameters
>(
ecParams
,
cipher
.
random
);
KeyGenerator
keyGenerator
=
new
KeyGenerator
(
'EC'
);
keyGenerator
.
init
(
keyGeneratorParams
);
test
(
'can read openssl key pair'
,
()
{
AsymmetricKeyPair
<
PublicKey
,
PrivateKey
>
keyPair
=
keyPairFromPrivateKeyBytes
(
kPrivateKeyDER
);
expect
(
keyPair
!=
null
,
equals
(
true
));
ECPrivateKey
keyPairPrivateKey
=
keyPair
.
privateKey
;
ECPublicKey
keyPairPublicKey
=
keyPair
.
publicKey
;
expect
(
keyPairPrivateKey
.
d
.
intValue
(),
equals
(
kPrivateKeyD
));
expect
(
keyPairPublicKey
.
Q
.
x
.
toBigInteger
().
intValue
(),
equals
(
kPublicKeyQx
));
expect
(
keyPairPublicKey
.
Q
.
y
.
toBigInteger
().
intValue
(),
equals
(
kPublicKeyQy
));
});
test
(
'serializeManifest adds key and content-hash'
,
()
{
AsymmetricKeyPair
<
PublicKey
,
PrivateKey
>
keyPair
=
keyPairFromPrivateKeyBytes
(
kPrivateKeyDER
);
ECPublicKey
keyPairPublicKey
=
keyPair
.
publicKey
;
Uint8List
manifestBytes
=
serializeManifest
(
kManifest
,
keyPairPublicKey
,
kTestBytes
);
Map
<
String
,
dynamic
>
decodedManifest
=
JSON
.
decode
(
UTF8
.
decode
(
manifestBytes
));
String
expectedKey
=
BASE64
.
encode
(
keyPairPublicKey
.
Q
.
getEncoded
());
expect
(
decodedManifest
!=
null
,
equals
(
true
));
expect
(
decodedManifest
[
'name'
],
equals
(
kManifest
[
'name'
]));
expect
(
decodedManifest
[
'version'
],
equals
(
kManifest
[
'version'
]));
expect
(
decodedManifest
[
'key'
],
equals
(
expectedKey
));
expect
(
decodedManifest
[
'content-hash'
],
equals
(
kTestHash
));
});
test
(
'signManifest and verifyManifestSignature work'
,
()
{
AsymmetricKeyPair
<
PublicKey
,
PrivateKey
>
keyPair
=
keyPairFromPrivateKeyBytes
(
kPrivateKeyDER
);
ECPrivateKey
keyPairPrivateKey
=
keyPair
.
privateKey
;
ECPublicKey
keyPairPublicKey
=
keyPair
.
publicKey
;
Map
<
String
,
dynamic
>
manifest
=
JSON
.
decode
(
UTF8
.
decode
(
serializeManifest
(
kManifest
,
keyPairPublicKey
,
kTestBytes
))
);
Uint8List
signatureBytes
=
signManifest
(
kTestBytes
,
keyPairPrivateKey
);
bool
verifies
=
verifyManifestSignature
(
manifest
,
kTestBytes
,
signatureBytes
);
expect
(
verifies
,
equals
(
true
));
// Ensure it fails with invalid signature or content.
Uint8List
badBytes
=
new
Uint8List
.
fromList
(<
int
>[
42
]);
verifies
=
verifyManifestSignature
(
manifest
,
kTestBytes
,
badBytes
);
expect
(
verifies
,
equals
(
false
));
verifies
=
verifyManifestSignature
(
manifest
,
badBytes
,
signatureBytes
);
expect
(
verifies
,
equals
(
false
));
});
test
(
'signing works with arbitrary key'
,
()
{
AsymmetricKeyPair
<
PublicKey
,
PrivateKey
>
keyPair
=
keyGenerator
.
generateKeyPair
();
ECPrivateKey
keyPairPrivateKey
=
keyPair
.
privateKey
;
ECPublicKey
keyPairPublicKey
=
keyPair
.
publicKey
;
String
failReason
=
'offending private key:
${keyPairPrivateKey.d}
'
;
Map
<
String
,
dynamic
>
manifest
=
JSON
.
decode
(
UTF8
.
decode
(
serializeManifest
(
kManifest
,
keyPairPublicKey
,
kTestBytes
))
);
Uint8List
signatureBytes
=
signManifest
(
kTestBytes
,
keyPairPrivateKey
);
bool
verifies
=
verifyManifestSignature
(
manifest
,
kTestBytes
,
signatureBytes
);
expect
(
verifies
,
equals
(
true
),
reason:
failReason
);
// Ensure it fails with invalid signature or content.
Uint8List
badBytes
=
new
Uint8List
.
fromList
(<
int
>[
42
]);
verifies
=
verifyManifestSignature
(
manifest
,
kTestBytes
,
badBytes
);
expect
(
verifies
,
equals
(
false
),
reason:
failReason
);
verifies
=
verifyManifestSignature
(
manifest
,
badBytes
,
signatureBytes
);
expect
(
verifies
,
equals
(
false
),
reason:
failReason
);
});
test
(
'verifyContentHash works'
,
()
async
{
Stream
<
Uint8List
>
contentStream
=
new
Stream
<
Uint8List
>.
fromIterable
(
kTestBytesList
);
bool
verifies
=
await
verifyContentHash
(
new
BigInteger
(
kTestHash
),
contentStream
);
expect
(
verifies
,
equals
(
true
));
// Ensure it fails with invalid hash or content.
contentStream
=
new
Stream
<
Uint8List
>.
fromIterable
(
kTestBytesList
);
verifies
=
await
verifyContentHash
(
new
BigInteger
(
0xdeadbeef
),
contentStream
);
expect
(
verifies
,
equals
(
false
));
Stream
<
Uint8List
>
badContentStream
=
new
Stream
<
Uint8List
>.
fromIterable
(
<
Uint8List
>[
new
Uint8List
.
fromList
(<
int
>[
42
])]
);
verifies
=
await
verifyContentHash
(
new
BigInteger
(
kTestHash
),
badContentStream
);
expect
(
verifies
,
equals
(
false
));
});
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment