mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-22 20:56:42 +00:00
Merge branch 'bzz' of github.com:ethersphere/go-ethereum into bzz
This commit is contained in:
commit
c29c1fd2b3
34 changed files with 2011 additions and 264 deletions
|
|
@ -1,64 +1,65 @@
|
||||||
var contract = web3.eth.contractFromAbi([
|
var walletABI = [
|
||||||
{
|
{
|
||||||
|
"name":"confirm",
|
||||||
|
"type":"function",
|
||||||
"constant":false,
|
"constant":false,
|
||||||
"inputs":[
|
"inputs":[
|
||||||
{"name":"_h","type":"hash256"}
|
{"name":"_h","type":"hash256"}
|
||||||
],
|
],
|
||||||
"name":"confirm",
|
"outputs":[]
|
||||||
"outputs":[],
|
|
||||||
"type":"function"
|
|
||||||
},{
|
},{
|
||||||
|
"name":"execute",
|
||||||
"constant":false,
|
"constant":false,
|
||||||
|
"type":"function",
|
||||||
"inputs":[
|
"inputs":[
|
||||||
{"name":_to,"type":"address"},
|
{"name":"_to","type":"address"},
|
||||||
{"name":"_value","type":"uint256"},
|
{"name":"_value","type":"uint256"},
|
||||||
{"name":"_data","type":"bytes"}
|
{"name":"_data","type":"bytes"}
|
||||||
],
|
],
|
||||||
"name":"execute",
|
|
||||||
"outputs":[
|
"outputs":[
|
||||||
{"name":"_r","type":"hash256"}
|
{"name":"_r","type":"hash256"}
|
||||||
],
|
]
|
||||||
"type":"function"
|
|
||||||
},{
|
},{
|
||||||
|
"name":"kill",
|
||||||
|
"type":"function",
|
||||||
"constant":false,
|
"constant":false,
|
||||||
"inputs":[
|
"inputs":[
|
||||||
{"name":"_to","type":"address"}
|
{"name":"_to","type":"address"}
|
||||||
],"name":"kill",
|
],
|
||||||
"outputs":[],
|
"outputs":[]
|
||||||
"type":"function"
|
|
||||||
},{
|
},{
|
||||||
|
"name":"changeOwner",
|
||||||
|
"type":"function",
|
||||||
"constant":false,
|
"constant":false,
|
||||||
"inputs":[
|
"inputs":[
|
||||||
{"name":"_from","type":"address"},
|
{"name":"_from","type":"address"},
|
||||||
{"name":"_to","type":"address"}
|
{"name":"_to","type":"address"}
|
||||||
],
|
],
|
||||||
"name":"changeOwner",
|
"outputs":[]
|
||||||
"outputs":[],
|
|
||||||
"type":"function"
|
|
||||||
},{
|
},{
|
||||||
|
"name":"CashIn",
|
||||||
|
"type":"event",
|
||||||
"inputs":[
|
"inputs":[
|
||||||
{"indexed":false,"name":"value","type":"uint256"}
|
{"indexed":false,"name":"value","type":"uint256"}
|
||||||
],
|
]
|
||||||
"name":"CashIn",
|
|
||||||
"type":"event"
|
|
||||||
},{
|
},{
|
||||||
|
"name":"SingleTransact",
|
||||||
|
"type":"event",
|
||||||
"inputs":[
|
"inputs":[
|
||||||
{"indexed":true,"name":"out","type":"string32"},
|
{"indexed":true,"name":"out","type":"string32"},
|
||||||
{"indexed":false,"name":"owner","type":"address"},
|
{"indexed":false,"name":"owner","type":"address"},
|
||||||
{"indexed":false,"name":"value","type":"uint256"},
|
{"indexed":false,"name":"value","type":"uint256"},
|
||||||
{"indexed":false,"name":"to","type":"address"}
|
{"indexed":false,"name":"to","type":"address"}
|
||||||
],
|
]
|
||||||
"name":"SingleTransact",
|
|
||||||
"type":"event"
|
|
||||||
},{
|
},{
|
||||||
|
"name":"MultiTransact",
|
||||||
|
"type":"event",
|
||||||
"inputs":[
|
"inputs":[
|
||||||
{"indexed":true,"name":"out","type":"string32"},
|
{"indexed":true,"name":"out","type":"string32"},
|
||||||
{"indexed":false,"name":"owner","type":"address"},
|
{"indexed":false,"name":"owner","type":"address"},
|
||||||
{"indexed":false,"name":"operation","type":"hash256"},
|
{"indexed":false,"name":"operation","type":"hash256"},
|
||||||
{"indexed":false,"name":"value","type":"uint256"},
|
{"indexed":false,"name":"value","type":"uint256"},
|
||||||
{"indexed":false,"name":"to","type":"address"}
|
{"indexed":false,"name":"to","type":"address"}
|
||||||
],
|
]
|
||||||
"name":"MultiTransact",
|
|
||||||
"type":"event"
|
|
||||||
}
|
}
|
||||||
]);
|
];
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
|
|
||||||
<!doctype>
|
<!doctype>
|
||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
|
<meta name="badge" content="10">
|
||||||
<script type="text/javascript" src="../ext/bignumber.min.js"></script>
|
<script type="text/javascript" src="../ext/bignumber.min.js"></script>
|
||||||
<script type="text/javascript" src="../ext/ethereum.js/dist/ethereum.js"></script>
|
<script type="text/javascript" src="../ext/ethereum.js/dist/ethereum.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
@ -60,7 +60,7 @@
|
||||||
var web3 = require('web3');
|
var web3 = require('web3');
|
||||||
var eth = web3.eth;
|
var eth = web3.eth;
|
||||||
|
|
||||||
web3.setProvider(new web3.providers.HttpSyncProvider('http://localhost:8080'));
|
web3.setProvider(new web3.providers.HttpSyncProvider('http://localhost:8545'));
|
||||||
|
|
||||||
document.querySelector("#number").innerHTML = eth.number;
|
document.querySelector("#number").innerHTML = eth.number;
|
||||||
document.querySelector("#coinbase").innerHTML = eth.coinbase
|
document.querySelector("#coinbase").innerHTML = eth.coinbase
|
||||||
|
|
|
||||||
|
|
@ -341,7 +341,7 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: peerLabel
|
id: peerCounterLabel
|
||||||
font.pixelSize: 10
|
font.pixelSize: 10
|
||||||
text: "0 / 0"
|
text: "0 / 0"
|
||||||
}
|
}
|
||||||
|
|
@ -926,7 +926,6 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function setWalletValue(value) {
|
function setWalletValue(value) {
|
||||||
walletValueLabel.text = value
|
walletValueLabel.text = value
|
||||||
}
|
}
|
||||||
|
|
@ -936,17 +935,11 @@ ApplicationWindow {
|
||||||
var view = mainView.addPlugin(name)
|
var view = mainView.addPlugin(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
function setPeers(text) {
|
function clearPeers() { peerModel.clear() }
|
||||||
peerLabel.text = text
|
function addPeer(peer) { peerModel.append(peer) }
|
||||||
}
|
|
||||||
|
|
||||||
function addPeer(peer) {
|
function setPeerCounters(text) {
|
||||||
// We could just append the whole peer object but it cries if you try to alter them
|
peerCounterLabel.text = text
|
||||||
peerModel.append({ip: peer.ip, port: peer.port, lastResponse:timeAgo(peer.lastSend), latency: peer.latency, version: peer.version, caps: peer.caps})
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetPeers(){
|
|
||||||
peerModel.clear()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function timeAgo(unixTs){
|
function timeAgo(unixTs){
|
||||||
|
|
@ -984,8 +977,8 @@ ApplicationWindow {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
id: peerTable
|
id: peerTable
|
||||||
model: peerModel
|
model: peerModel
|
||||||
TableViewColumn{width: 200; role: "ip" ; title: "IP" }
|
TableViewColumn{width: 180; role: "addr" ; title: "Remote Address" }
|
||||||
TableViewColumn{width: 260; role: "version" ; title: "Version" }
|
TableViewColumn{width: 280; role: "nodeID" ; title: "Node ID" }
|
||||||
TableViewColumn{width: 180; role: "caps" ; title: "Capabilities" }
|
TableViewColumn{width: 180; role: "caps" ; title: "Capabilities" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -334,16 +334,35 @@ Rectangle {
|
||||||
}
|
}
|
||||||
z: 10
|
z: 10
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
interval: 500; running: true; repeat: true
|
||||||
|
onTriggered: {
|
||||||
|
webview.runJavaScript("try{document.querySelector('meta[name=badge]').getAttribute('content')}catch(e){}", function(badge) {
|
||||||
|
if (badge) {
|
||||||
|
menuItem.secondaryTitle = badge;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onLoadingChanged: {
|
onLoadingChanged: {
|
||||||
if (loadRequest.status == WebEngineView.LoadSucceededStatus) {
|
if (loadRequest.status == WebEngineView.LoadSucceededStatus) {
|
||||||
webview.runJavaScript("document.title", function(pageTitle) {
|
webview.runJavaScript("document.title", function(pageTitle) {
|
||||||
menuItem.title = pageTitle;
|
menuItem.title = pageTitle;
|
||||||
});
|
});
|
||||||
|
|
||||||
//var topBarStyle
|
webView.runJavaScript("try{document.querySelector(\"meta[name='ethereum-dapp-url-bar-style']\").getAttribute(\"content\")}catch(e){}", function(topBarStyle){
|
||||||
webView.runJavaScript("document.querySelector(\"meta[name='ethereum-dapp-url-bar-style']\").getAttribute(\"content\")", function(topBarStyle){
|
if (!topBarStyle) {
|
||||||
if (topBarStyle=="transparent") {
|
showFullUrlBar(true);
|
||||||
|
navBarBackground.visible = true;
|
||||||
|
back.visible = true;
|
||||||
|
appInfoPane.anchors.leftMargin = 0;
|
||||||
|
appInfoPaneShadow.anchors.leftMargin = 0;
|
||||||
|
webview.anchors.topMargin = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (topBarStyle=="transparent") {
|
||||||
// Adjust for a transparent sidebar Dapp
|
// Adjust for a transparent sidebar Dapp
|
||||||
navBarBackground.visible = false;
|
navBarBackground.visible = false;
|
||||||
back.visible = false;
|
back.visible = false;
|
||||||
|
|
@ -358,12 +377,9 @@ Rectangle {
|
||||||
appInfoPane.anchors.leftMargin = 0;
|
appInfoPane.anchors.leftMargin = 0;
|
||||||
appInfoPaneShadow.anchors.leftMargin = 0;
|
appInfoPaneShadow.anchors.leftMargin = 0;
|
||||||
webview.anchors.topMargin = 0;
|
webview.anchors.topMargin = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
webview.runJavaScript(eth.readFile("bignumber.min.js"));
|
webview.runJavaScript(eth.readFile("bignumber.min.js"));
|
||||||
webview.runJavaScript(eth.readFile("ethereum.js/dist/ethereum.js"));
|
webview.runJavaScript(eth.readFile("ethereum.js/dist/ethereum.js"));
|
||||||
|
|
||||||
|
|
@ -371,7 +387,9 @@ Rectangle {
|
||||||
var matches = cleanTitle.match(/^[a-z]*\:\/\/([^\/?#]+)(?:[\/?#]|$)/i);
|
var matches = cleanTitle.match(/^[a-z]*\:\/\/([^\/?#]+)(?:[\/?#]|$)/i);
|
||||||
var domain = matches && matches[1];
|
var domain = matches && matches[1];
|
||||||
|
|
||||||
|
if (domain)
|
||||||
appDomain.text = domain //webview.url.replace("a", "z")
|
appDomain.text = domain //webview.url.replace("a", "z")
|
||||||
|
if (webview.title)
|
||||||
appTitle.text = webview.title
|
appTitle.text = webview.title
|
||||||
|
|
||||||
showFullUrlBar(false);
|
showFullUrlBar(false);
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,6 @@ Rectangle {
|
||||||
anchors {
|
anchors {
|
||||||
left: root.left
|
left: root.left
|
||||||
right: root.right
|
right: root.right
|
||||||
top: sizeGrip.bottom
|
|
||||||
bottom: root.bottom
|
bottom: root.bottom
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,6 @@ Rectangle {
|
||||||
id: lastBlockLabel
|
id: lastBlockLabel
|
||||||
objectName: "lastBlockLabel"
|
objectName: "lastBlockLabel"
|
||||||
text: "---"
|
text: "---"
|
||||||
font.pixelSize: 10
|
|
||||||
anchors.right: peerGroup.left
|
|
||||||
anchors.rightMargin: 5
|
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
//menuItem.secondaryTitle = text
|
//menuItem.secondaryTitle = text
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,8 @@ Rectangle {
|
||||||
Label {
|
Label {
|
||||||
objectName: "balanceLabel"
|
objectName: "balanceLabel"
|
||||||
visible: false
|
visible: false
|
||||||
font.pixelSize: 10
|
|
||||||
anchors.right: lastBlockLabel.left
|
|
||||||
anchors.rightMargin: 5
|
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
|
balance.text = text
|
||||||
menuItem.secondaryTitle = text
|
menuItem.secondaryTitle = text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -449,6 +450,7 @@ func (gui *Gui) update() {
|
||||||
|
|
||||||
case <-peerUpdateTicker.C:
|
case <-peerUpdateTicker.C:
|
||||||
gui.setPeerInfo()
|
gui.setPeerInfo()
|
||||||
|
|
||||||
case <-generalUpdateTicker.C:
|
case <-generalUpdateTicker.C:
|
||||||
statusText := "#" + gui.eth.ChainManager().CurrentBlock().Number().String()
|
statusText := "#" + gui.eth.ChainManager().CurrentBlock().Number().String()
|
||||||
lastBlockLabel.Set("text", statusText)
|
lastBlockLabel.Set("text", statusText)
|
||||||
|
|
@ -499,12 +501,34 @@ NumGC: %d
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type qmlpeer struct{ Addr, NodeID, Caps string }
|
||||||
|
|
||||||
|
type peersByID []*qmlpeer
|
||||||
|
|
||||||
|
func (s peersByID) Len() int { return len(s) }
|
||||||
|
func (s peersByID) Less(i, j int) bool { return s[i].NodeID < s[j].NodeID }
|
||||||
|
func (s peersByID) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||||
|
|
||||||
func (gui *Gui) setPeerInfo() {
|
func (gui *Gui) setPeerInfo() {
|
||||||
gui.win.Root().Call("setPeers", fmt.Sprintf("%d / %d", gui.eth.PeerCount(), gui.eth.MaxPeers))
|
peers := gui.eth.Peers()
|
||||||
gui.win.Root().Call("resetPeers")
|
qpeers := make(peersByID, len(peers))
|
||||||
//for _, peer := range gui.xeth.Peers() {
|
for i, p := range peers {
|
||||||
//gui.win.Root().Call("addPeer", peer)
|
qpeers[i] = &qmlpeer{
|
||||||
//}
|
NodeID: p.ID().String(),
|
||||||
|
Addr: p.RemoteAddr().String(),
|
||||||
|
Caps: fmt.Sprint(p.Caps()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// we need to sort the peers because they jump around randomly
|
||||||
|
// otherwise. order returned by eth.Peers is random because they
|
||||||
|
// are taken from a map.
|
||||||
|
sort.Sort(qpeers)
|
||||||
|
|
||||||
|
gui.win.Root().Call("setPeerCounters", fmt.Sprintf("%d / %d", len(peers), gui.eth.MaxPeers()))
|
||||||
|
gui.win.Root().Call("clearPeers")
|
||||||
|
for _, p := range qpeers {
|
||||||
|
gui.win.Root().Call("addPeer", p)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) privateKey() string {
|
func (gui *Gui) privateKey() string {
|
||||||
|
|
|
||||||
|
|
@ -73,11 +73,6 @@ func (self *UiLib) Notef(args []interface{}) {
|
||||||
guilogger.Infoln(args...)
|
guilogger.Infoln(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *UiLib) PastPeers() *ethutil.List {
|
|
||||||
return ethutil.NewList([]string{})
|
|
||||||
//return ethutil.NewList(eth.PastPeers())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *UiLib) ImportTx(rlpTx string) {
|
func (self *UiLib) ImportTx(rlpTx string) {
|
||||||
tx := types.NewTransactionFromBytes(ethutil.Hex2Bytes(rlpTx))
|
tx := types.NewTransactionFromBytes(ethutil.Hex2Bytes(rlpTx))
|
||||||
err := self.eth.TxPool().Add(tx)
|
err := self.eth.TxPool().Add(tx)
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@ func (self *Block) ParentHash() []byte {
|
||||||
|
|
||||||
func (self *Block) String() string {
|
func (self *Block) String() string {
|
||||||
return fmt.Sprintf(`BLOCK(%x): Size: %v TD: %v {
|
return fmt.Sprintf(`BLOCK(%x): Size: %v TD: %v {
|
||||||
|
NoNonce: %x
|
||||||
Header:
|
Header:
|
||||||
[
|
[
|
||||||
%v
|
%v
|
||||||
|
|
@ -218,7 +219,7 @@ Transactions:
|
||||||
Uncles:
|
Uncles:
|
||||||
%v
|
%v
|
||||||
}
|
}
|
||||||
`, self.header.Hash(), self.Size(), self.Td, self.header, self.transactions, self.uncles)
|
`, self.header.Hash(), self.Size(), self.Td, self.header.HashNoNonce(), self.header, self.transactions, self.uncles)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Header) String() string {
|
func (self *Header) String() string {
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ type Receipt struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewReceipt(root []byte, cumalativeGasUsed *big.Int) *Receipt {
|
func NewReceipt(root []byte, cumalativeGasUsed *big.Int) *Receipt {
|
||||||
return &Receipt{PostState: ethutil.CopyBytes(root), CumulativeGasUsed: cumalativeGasUsed}
|
return &Receipt{PostState: ethutil.CopyBytes(root), CumulativeGasUsed: new(big.Int).Set(cumalativeGasUsed)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRecieptFromValue(val *ethutil.Value) *Receipt {
|
func NewRecieptFromValue(val *ethutil.Value) *Receipt {
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,10 @@ import (
|
||||||
"code.google.com/p/go-uuid/uuid"
|
"code.google.com/p/go-uuid/uuid"
|
||||||
"code.google.com/p/go.crypto/pbkdf2"
|
"code.google.com/p/go.crypto/pbkdf2"
|
||||||
"code.google.com/p/go.crypto/ripemd160"
|
"code.google.com/p/go.crypto/ripemd160"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto/ecies"
|
||||||
"github.com/ethereum/go-ethereum/crypto/secp256k1"
|
"github.com/ethereum/go-ethereum/crypto/secp256k1"
|
||||||
"github.com/ethereum/go-ethereum/crypto/sha3"
|
"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||||
"github.com/ethereum/go-ethereum/ethutil"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
"github.com/obscuren/ecies"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
|
||||||
24
crypto/ecies/.gitignore
vendored
Normal file
24
crypto/ecies/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
||||||
|
|
||||||
|
*~
|
||||||
28
crypto/ecies/LICENSE
Normal file
28
crypto/ecies/LICENSE
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
Copyright (c) 2013 Kyle Isom <kyle@tyrfingr.is>
|
||||||
|
Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
94
crypto/ecies/README
Normal file
94
crypto/ecies/README
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
# NOTE
|
||||||
|
|
||||||
|
This implementation is direct fork of Kylom's implementation. I claim no authorship over this code apart from some minor modifications.
|
||||||
|
Please be aware this code **has not yet been reviewed**.
|
||||||
|
|
||||||
|
ecies implements the Elliptic Curve Integrated Encryption Scheme.
|
||||||
|
|
||||||
|
The package is designed to be compliant with the appropriate NIST
|
||||||
|
standards, and therefore doesn't support the full SEC 1 algorithm set.
|
||||||
|
|
||||||
|
|
||||||
|
STATUS:
|
||||||
|
|
||||||
|
ecies should be ready for use. The ASN.1 support is only complete so
|
||||||
|
far as to supported the listed algorithms before.
|
||||||
|
|
||||||
|
|
||||||
|
CAVEATS
|
||||||
|
|
||||||
|
1. CMAC support is currently not present.
|
||||||
|
|
||||||
|
|
||||||
|
SUPPORTED ALGORITHMS
|
||||||
|
|
||||||
|
SYMMETRIC CIPHERS HASH FUNCTIONS
|
||||||
|
AES128 SHA-1
|
||||||
|
AES192 SHA-224
|
||||||
|
AES256 SHA-256
|
||||||
|
SHA-384
|
||||||
|
ELLIPTIC CURVE SHA-512
|
||||||
|
P256
|
||||||
|
P384 KEY DERIVATION FUNCTION
|
||||||
|
P521 NIST SP 800-65a Concatenation KDF
|
||||||
|
|
||||||
|
Curve P224 isn't supported because it does not provide a minimum security
|
||||||
|
level of AES128 with HMAC-SHA1. According to NIST SP 800-57, the security
|
||||||
|
level of P224 is 112 bits of security. Symmetric ciphers use CTR-mode;
|
||||||
|
message tags are computed using HMAC-<HASH> function.
|
||||||
|
|
||||||
|
|
||||||
|
CURVE SELECTION
|
||||||
|
|
||||||
|
According to NIST SP 800-57, the following curves should be selected:
|
||||||
|
|
||||||
|
+----------------+-------+
|
||||||
|
| SYMMETRIC SIZE | CURVE |
|
||||||
|
+----------------+-------+
|
||||||
|
| 128-bit | P256 |
|
||||||
|
+----------------+-------+
|
||||||
|
| 192-bit | P384 |
|
||||||
|
+----------------+-------+
|
||||||
|
| 256-bit | P521 |
|
||||||
|
+----------------+-------+
|
||||||
|
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
1. Look at serialising the parameters with the SEC 1 ASN.1 module.
|
||||||
|
2. Validate ASN.1 formats with SEC 1.
|
||||||
|
|
||||||
|
|
||||||
|
TEST VECTORS
|
||||||
|
|
||||||
|
The only test vectors I've found so far date from 1993, predating AES
|
||||||
|
and including only 163-bit curves. Therefore, there are no published
|
||||||
|
test vectors to compare to.
|
||||||
|
|
||||||
|
|
||||||
|
LICENSE
|
||||||
|
|
||||||
|
ecies is released under the same license as the Go source code. See the
|
||||||
|
LICENSE file for details.
|
||||||
|
|
||||||
|
|
||||||
|
REFERENCES
|
||||||
|
|
||||||
|
* SEC (Standard for Efficient Cryptography) 1, version 2.0: Elliptic
|
||||||
|
Curve Cryptography; Certicom, May 2009.
|
||||||
|
http://www.secg.org/sec1-v2.pdf
|
||||||
|
* GEC (Guidelines for Efficient Cryptography) 2, version 0.3: Test
|
||||||
|
Vectors for SEC 1; Certicom, September 1999.
|
||||||
|
http://read.pudn.com/downloads168/doc/772358/TestVectorsforSEC%201-gec2.pdf
|
||||||
|
* NIST SP 800-56a: Recommendation for Pair-Wise Key Establishment Schemes
|
||||||
|
Using Discrete Logarithm Cryptography. National Institute of Standards
|
||||||
|
and Technology, May 2007.
|
||||||
|
http://csrc.nist.gov/publications/nistpubs/800-56A/SP800-56A_Revision1_Mar08-2007.pdf
|
||||||
|
* Suite B Implementer’s Guide to NIST SP 800-56A. National Security
|
||||||
|
Agency, July 28, 2009.
|
||||||
|
http://www.nsa.gov/ia/_files/SuiteB_Implementer_G-113808.pdf
|
||||||
|
* NIST SP 800-57: Recommendation for Key Management – Part 1: General
|
||||||
|
(Revision 3). National Institute of Standards and Technology, July
|
||||||
|
2012.
|
||||||
|
http://csrc.nist.gov/publications/nistpubs/800-57/sp800-57_part1_rev3_general.pdf
|
||||||
|
|
||||||
556
crypto/ecies/asn1.go
Normal file
556
crypto/ecies/asn1.go
Normal file
|
|
@ -0,0 +1,556 @@
|
||||||
|
package ecies
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/sha1"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/sha512"
|
||||||
|
"encoding/asn1"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"hash"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
secgScheme = []int{1, 3, 132, 1}
|
||||||
|
shaScheme = []int{2, 16, 840, 1, 101, 3, 4, 2}
|
||||||
|
ansiX962Scheme = []int{1, 2, 840, 10045}
|
||||||
|
x963Scheme = []int{1, 2, 840, 63, 0}
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrInvalidPrivateKey = fmt.Errorf("ecies: invalid private key")
|
||||||
|
|
||||||
|
func doScheme(base, v []int) asn1.ObjectIdentifier {
|
||||||
|
var oidInts asn1.ObjectIdentifier
|
||||||
|
oidInts = append(oidInts, base...)
|
||||||
|
return append(oidInts, v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// curve OID code taken from crypto/x509, including
|
||||||
|
// - oidNameCurve*
|
||||||
|
// - namedCurveFromOID
|
||||||
|
// - oidFromNamedCurve
|
||||||
|
// RFC 5480, 2.1.1.1. Named Curve
|
||||||
|
//
|
||||||
|
// secp224r1 OBJECT IDENTIFIER ::= {
|
||||||
|
// iso(1) identified-organization(3) certicom(132) curve(0) 33 }
|
||||||
|
//
|
||||||
|
// secp256r1 OBJECT IDENTIFIER ::= {
|
||||||
|
// iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3)
|
||||||
|
// prime(1) 7 }
|
||||||
|
//
|
||||||
|
// secp384r1 OBJECT IDENTIFIER ::= {
|
||||||
|
// iso(1) identified-organization(3) certicom(132) curve(0) 34 }
|
||||||
|
//
|
||||||
|
// secp521r1 OBJECT IDENTIFIER ::= {
|
||||||
|
// iso(1) identified-organization(3) certicom(132) curve(0) 35 }
|
||||||
|
//
|
||||||
|
// NB: secp256r1 is equivalent to prime256v1
|
||||||
|
type secgNamedCurve asn1.ObjectIdentifier
|
||||||
|
|
||||||
|
var (
|
||||||
|
secgNamedCurveP224 = secgNamedCurve{1, 3, 132, 0, 33}
|
||||||
|
secgNamedCurveP256 = secgNamedCurve{1, 2, 840, 10045, 3, 1, 7}
|
||||||
|
secgNamedCurveP384 = secgNamedCurve{1, 3, 132, 0, 34}
|
||||||
|
secgNamedCurveP521 = secgNamedCurve{1, 3, 132, 0, 35}
|
||||||
|
rawCurveP224 = []byte{6, 5, 4, 3, 1, 2, 9, 4, 0, 3, 3}
|
||||||
|
rawCurveP256 = []byte{6, 8, 4, 2, 1, 3, 4, 7, 2, 2, 0, 6, 6, 1, 3, 1, 7}
|
||||||
|
rawCurveP384 = []byte{6, 5, 4, 3, 1, 2, 9, 4, 0, 3, 4}
|
||||||
|
rawCurveP521 = []byte{6, 5, 4, 3, 1, 2, 9, 4, 0, 3, 5}
|
||||||
|
)
|
||||||
|
|
||||||
|
func rawCurve(curve elliptic.Curve) []byte {
|
||||||
|
switch curve {
|
||||||
|
case elliptic.P224():
|
||||||
|
return rawCurveP224
|
||||||
|
case elliptic.P256():
|
||||||
|
return rawCurveP256
|
||||||
|
case elliptic.P384():
|
||||||
|
return rawCurveP384
|
||||||
|
case elliptic.P521():
|
||||||
|
return rawCurveP521
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (curve secgNamedCurve) Equal(curve2 secgNamedCurve) bool {
|
||||||
|
if len(curve) != len(curve2) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, _ := range curve {
|
||||||
|
if curve[i] != curve2[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func namedCurveFromOID(curve secgNamedCurve) elliptic.Curve {
|
||||||
|
switch {
|
||||||
|
case curve.Equal(secgNamedCurveP224):
|
||||||
|
return elliptic.P224()
|
||||||
|
case curve.Equal(secgNamedCurveP256):
|
||||||
|
return elliptic.P256()
|
||||||
|
case curve.Equal(secgNamedCurveP384):
|
||||||
|
return elliptic.P384()
|
||||||
|
case curve.Equal(secgNamedCurveP521):
|
||||||
|
return elliptic.P521()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func oidFromNamedCurve(curve elliptic.Curve) (secgNamedCurve, bool) {
|
||||||
|
switch curve {
|
||||||
|
case elliptic.P224():
|
||||||
|
return secgNamedCurveP224, true
|
||||||
|
case elliptic.P256():
|
||||||
|
return secgNamedCurveP256, true
|
||||||
|
case elliptic.P384():
|
||||||
|
return secgNamedCurveP384, true
|
||||||
|
case elliptic.P521():
|
||||||
|
return secgNamedCurveP521, true
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// asnAlgorithmIdentifier represents the ASN.1 structure of the same name. See RFC
|
||||||
|
// 5280, section 4.1.1.2.
|
||||||
|
type asnAlgorithmIdentifier struct {
|
||||||
|
Algorithm asn1.ObjectIdentifier
|
||||||
|
Parameters asn1.RawValue `asn1:"optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a asnAlgorithmIdentifier) Cmp(b asnAlgorithmIdentifier) bool {
|
||||||
|
if len(a.Algorithm) != len(b.Algorithm) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, _ := range a.Algorithm {
|
||||||
|
if a.Algorithm[i] != b.Algorithm[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type asnHashFunction asnAlgorithmIdentifier
|
||||||
|
|
||||||
|
var (
|
||||||
|
oidSHA1 = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 26}
|
||||||
|
oidSHA224 = doScheme(shaScheme, []int{4})
|
||||||
|
oidSHA256 = doScheme(shaScheme, []int{1})
|
||||||
|
oidSHA384 = doScheme(shaScheme, []int{2})
|
||||||
|
oidSHA512 = doScheme(shaScheme, []int{3})
|
||||||
|
)
|
||||||
|
|
||||||
|
func hashFromOID(oid asn1.ObjectIdentifier) func() hash.Hash {
|
||||||
|
switch {
|
||||||
|
case oid.Equal(oidSHA1):
|
||||||
|
return sha1.New
|
||||||
|
case oid.Equal(oidSHA224):
|
||||||
|
return sha256.New224
|
||||||
|
case oid.Equal(oidSHA256):
|
||||||
|
return sha256.New
|
||||||
|
case oid.Equal(oidSHA384):
|
||||||
|
return sha512.New384
|
||||||
|
case oid.Equal(oidSHA512):
|
||||||
|
return sha512.New
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func oidFromHash(hash crypto.Hash) (asn1.ObjectIdentifier, bool) {
|
||||||
|
switch hash {
|
||||||
|
case crypto.SHA1:
|
||||||
|
return oidSHA1, true
|
||||||
|
case crypto.SHA224:
|
||||||
|
return oidSHA224, true
|
||||||
|
case crypto.SHA256:
|
||||||
|
return oidSHA256, true
|
||||||
|
case crypto.SHA384:
|
||||||
|
return oidSHA384, true
|
||||||
|
case crypto.SHA512:
|
||||||
|
return oidSHA512, true
|
||||||
|
default:
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
asnAlgoSHA1 = asnHashFunction{
|
||||||
|
Algorithm: oidSHA1,
|
||||||
|
}
|
||||||
|
asnAlgoSHA224 = asnHashFunction{
|
||||||
|
Algorithm: oidSHA224,
|
||||||
|
}
|
||||||
|
asnAlgoSHA256 = asnHashFunction{
|
||||||
|
Algorithm: oidSHA256,
|
||||||
|
}
|
||||||
|
asnAlgoSHA384 = asnHashFunction{
|
||||||
|
Algorithm: oidSHA384,
|
||||||
|
}
|
||||||
|
asnAlgoSHA512 = asnHashFunction{
|
||||||
|
Algorithm: oidSHA512,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// type ASNasnSubjectPublicKeyInfo struct {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
|
||||||
|
type asnSubjectPublicKeyInfo struct {
|
||||||
|
Algorithm asn1.ObjectIdentifier
|
||||||
|
PublicKey asn1.BitString
|
||||||
|
Supplements ecpksSupplements `asn1:"optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type asnECPKAlgorithms struct {
|
||||||
|
Type asn1.ObjectIdentifier
|
||||||
|
}
|
||||||
|
|
||||||
|
var idPublicKeyType = doScheme(ansiX962Scheme, []int{2})
|
||||||
|
var idEcPublicKey = doScheme(idPublicKeyType, []int{1})
|
||||||
|
var idEcPublicKeySupplemented = doScheme(idPublicKeyType, []int{0})
|
||||||
|
|
||||||
|
func curveToRaw(curve elliptic.Curve) (rv asn1.RawValue, ok bool) {
|
||||||
|
switch curve {
|
||||||
|
case elliptic.P224(), elliptic.P256(), elliptic.P384(), elliptic.P521():
|
||||||
|
raw := rawCurve(curve)
|
||||||
|
return asn1.RawValue{
|
||||||
|
Tag: 30,
|
||||||
|
Bytes: raw[2:],
|
||||||
|
FullBytes: raw,
|
||||||
|
}, true
|
||||||
|
default:
|
||||||
|
return rv, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func asnECPublicKeyType(curve elliptic.Curve) (algo asnAlgorithmIdentifier, ok bool) {
|
||||||
|
raw, ok := curveToRaw(curve)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
return asnAlgorithmIdentifier{Algorithm: idEcPublicKey,
|
||||||
|
Parameters: raw}, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type asnECPrivKeyVer int
|
||||||
|
|
||||||
|
var asnECPrivKeyVer1 asnECPrivKeyVer = 1
|
||||||
|
|
||||||
|
type asnPrivateKey struct {
|
||||||
|
Version asnECPrivKeyVer
|
||||||
|
Private []byte
|
||||||
|
Curve secgNamedCurve `asn1:"optional"`
|
||||||
|
Public asn1.BitString
|
||||||
|
}
|
||||||
|
|
||||||
|
var asnECDH = doScheme(secgScheme, []int{12})
|
||||||
|
|
||||||
|
type asnECDHAlgorithm asnAlgorithmIdentifier
|
||||||
|
|
||||||
|
var (
|
||||||
|
dhSinglePass_stdDH_sha1kdf = asnECDHAlgorithm{
|
||||||
|
Algorithm: doScheme(x963Scheme, []int{2}),
|
||||||
|
}
|
||||||
|
dhSinglePass_stdDH_sha256kdf = asnECDHAlgorithm{
|
||||||
|
Algorithm: doScheme(secgScheme, []int{11, 1}),
|
||||||
|
}
|
||||||
|
dhSinglePass_stdDH_sha384kdf = asnECDHAlgorithm{
|
||||||
|
Algorithm: doScheme(secgScheme, []int{11, 2}),
|
||||||
|
}
|
||||||
|
dhSinglePass_stdDH_sha224kdf = asnECDHAlgorithm{
|
||||||
|
Algorithm: doScheme(secgScheme, []int{11, 0}),
|
||||||
|
}
|
||||||
|
dhSinglePass_stdDH_sha512kdf = asnECDHAlgorithm{
|
||||||
|
Algorithm: doScheme(secgScheme, []int{11, 3}),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a asnECDHAlgorithm) Cmp(b asnECDHAlgorithm) bool {
|
||||||
|
if len(a.Algorithm) != len(b.Algorithm) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, _ := range a.Algorithm {
|
||||||
|
if a.Algorithm[i] != b.Algorithm[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// asnNISTConcatenation is the only supported KDF at this time.
|
||||||
|
type asnKeyDerivationFunction asnAlgorithmIdentifier
|
||||||
|
|
||||||
|
var asnNISTConcatenationKDF = asnKeyDerivationFunction{
|
||||||
|
Algorithm: doScheme(secgScheme, []int{17, 1}),
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a asnKeyDerivationFunction) Cmp(b asnKeyDerivationFunction) bool {
|
||||||
|
if len(a.Algorithm) != len(b.Algorithm) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, _ := range a.Algorithm {
|
||||||
|
if a.Algorithm[i] != b.Algorithm[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var eciesRecommendedParameters = doScheme(secgScheme, []int{7})
|
||||||
|
var eciesSpecifiedParameters = doScheme(secgScheme, []int{8})
|
||||||
|
|
||||||
|
type asnECIESParameters struct {
|
||||||
|
KDF asnKeyDerivationFunction `asn1:"optional"`
|
||||||
|
Sym asnSymmetricEncryption `asn1:"optional"`
|
||||||
|
MAC asnMessageAuthenticationCode `asn1:"optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type asnSymmetricEncryption asnAlgorithmIdentifier
|
||||||
|
|
||||||
|
var (
|
||||||
|
aes128CTRinECIES = asnSymmetricEncryption{
|
||||||
|
Algorithm: doScheme(secgScheme, []int{21, 0}),
|
||||||
|
}
|
||||||
|
aes192CTRinECIES = asnSymmetricEncryption{
|
||||||
|
Algorithm: doScheme(secgScheme, []int{21, 1}),
|
||||||
|
}
|
||||||
|
aes256CTRinECIES = asnSymmetricEncryption{
|
||||||
|
Algorithm: doScheme(secgScheme, []int{21, 2}),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a asnSymmetricEncryption) Cmp(b asnSymmetricEncryption) bool {
|
||||||
|
if len(a.Algorithm) != len(b.Algorithm) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, _ := range a.Algorithm {
|
||||||
|
if a.Algorithm[i] != b.Algorithm[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type asnMessageAuthenticationCode asnAlgorithmIdentifier
|
||||||
|
|
||||||
|
var (
|
||||||
|
hmacFull = asnMessageAuthenticationCode{
|
||||||
|
Algorithm: doScheme(secgScheme, []int{22}),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a asnMessageAuthenticationCode) Cmp(b asnMessageAuthenticationCode) bool {
|
||||||
|
if len(a.Algorithm) != len(b.Algorithm) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, _ := range a.Algorithm {
|
||||||
|
if a.Algorithm[i] != b.Algorithm[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type ecpksSupplements struct {
|
||||||
|
ECDomain secgNamedCurve
|
||||||
|
ECCAlgorithms eccAlgorithmSet
|
||||||
|
}
|
||||||
|
|
||||||
|
type eccAlgorithmSet struct {
|
||||||
|
ECDH asnECDHAlgorithm `asn1:"optional"`
|
||||||
|
ECIES asnECIESParameters `asn1:"optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalSubjectPublicKeyInfo(pub *PublicKey) (subj asnSubjectPublicKeyInfo, err error) {
|
||||||
|
subj.Algorithm = idEcPublicKeySupplemented
|
||||||
|
curve, ok := oidFromNamedCurve(pub.Curve)
|
||||||
|
if !ok {
|
||||||
|
err = ErrInvalidPublicKey
|
||||||
|
return
|
||||||
|
}
|
||||||
|
subj.Supplements.ECDomain = curve
|
||||||
|
if pub.Params != nil {
|
||||||
|
subj.Supplements.ECCAlgorithms.ECDH = paramsToASNECDH(pub.Params)
|
||||||
|
subj.Supplements.ECCAlgorithms.ECIES = paramsToASNECIES(pub.Params)
|
||||||
|
}
|
||||||
|
pubkey := elliptic.Marshal(pub.Curve, pub.X, pub.Y)
|
||||||
|
subj.PublicKey = asn1.BitString{
|
||||||
|
BitLength: len(pubkey) * 8,
|
||||||
|
Bytes: pubkey,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode a public key to DER format.
|
||||||
|
func MarshalPublic(pub *PublicKey) ([]byte, error) {
|
||||||
|
subj, err := marshalSubjectPublicKeyInfo(pub)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return asn1.Marshal(subj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode a DER-encoded public key.
|
||||||
|
func UnmarshalPublic(in []byte) (pub *PublicKey, err error) {
|
||||||
|
var subj asnSubjectPublicKeyInfo
|
||||||
|
|
||||||
|
if _, err = asn1.Unmarshal(in, &subj); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !subj.Algorithm.Equal(idEcPublicKeySupplemented) {
|
||||||
|
err = ErrInvalidPublicKey
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pub = new(PublicKey)
|
||||||
|
pub.Curve = namedCurveFromOID(subj.Supplements.ECDomain)
|
||||||
|
x, y := elliptic.Unmarshal(pub.Curve, subj.PublicKey.Bytes)
|
||||||
|
if x == nil {
|
||||||
|
err = ErrInvalidPublicKey
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pub.X = x
|
||||||
|
pub.Y = y
|
||||||
|
pub.Params = new(ECIESParams)
|
||||||
|
asnECIEStoParams(subj.Supplements.ECCAlgorithms.ECIES, pub.Params)
|
||||||
|
asnECDHtoParams(subj.Supplements.ECCAlgorithms.ECDH, pub.Params)
|
||||||
|
if pub.Params == nil {
|
||||||
|
if pub.Params = ParamsFromCurve(pub.Curve); pub.Params == nil {
|
||||||
|
err = ErrInvalidPublicKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalPrivateKey(prv *PrivateKey) (ecprv asnPrivateKey, err error) {
|
||||||
|
ecprv.Version = asnECPrivKeyVer1
|
||||||
|
ecprv.Private = prv.D.Bytes()
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
ecprv.Curve, ok = oidFromNamedCurve(prv.PublicKey.Curve)
|
||||||
|
if !ok {
|
||||||
|
err = ErrInvalidPrivateKey
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var pub []byte
|
||||||
|
if pub, err = MarshalPublic(&prv.PublicKey); err != nil {
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
ecprv.Public = asn1.BitString{
|
||||||
|
BitLength: len(pub) * 8,
|
||||||
|
Bytes: pub,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode a private key to DER format.
|
||||||
|
func MarshalPrivate(prv *PrivateKey) ([]byte, error) {
|
||||||
|
ecprv, err := marshalPrivateKey(prv)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return asn1.Marshal(ecprv)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode a private key from a DER-encoded format.
|
||||||
|
func UnmarshalPrivate(in []byte) (prv *PrivateKey, err error) {
|
||||||
|
var ecprv asnPrivateKey
|
||||||
|
|
||||||
|
if _, err = asn1.Unmarshal(in, &ecprv); err != nil {
|
||||||
|
return
|
||||||
|
} else if ecprv.Version != asnECPrivKeyVer1 {
|
||||||
|
err = ErrInvalidPrivateKey
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
privateCurve := namedCurveFromOID(ecprv.Curve)
|
||||||
|
if privateCurve == nil {
|
||||||
|
err = ErrInvalidPrivateKey
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
prv = new(PrivateKey)
|
||||||
|
prv.D = new(big.Int).SetBytes(ecprv.Private)
|
||||||
|
|
||||||
|
if pub, err := UnmarshalPublic(ecprv.Public.Bytes); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
prv.PublicKey = *pub
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export a public key to PEM format.
|
||||||
|
func ExportPublicPEM(pub *PublicKey) (out []byte, err error) {
|
||||||
|
der, err := MarshalPublic(pub)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var block pem.Block
|
||||||
|
block.Type = "ELLIPTIC CURVE PUBLIC KEY"
|
||||||
|
block.Bytes = der
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
err = pem.Encode(buf, &block)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
out = buf.Bytes()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export a private key to PEM format.
|
||||||
|
func ExportPrivatePEM(prv *PrivateKey) (out []byte, err error) {
|
||||||
|
der, err := MarshalPrivate(prv)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var block pem.Block
|
||||||
|
block.Type = "ELLIPTIC CURVE PRIVATE KEY"
|
||||||
|
block.Bytes = der
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
err = pem.Encode(buf, &block)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
out = buf.Bytes()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import a PEM-encoded public key.
|
||||||
|
func ImportPublicPEM(in []byte) (pub *PublicKey, err error) {
|
||||||
|
p, _ := pem.Decode(in)
|
||||||
|
if p == nil || p.Type != "ELLIPTIC CURVE PUBLIC KEY" {
|
||||||
|
return nil, ErrInvalidPublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
pub, err = UnmarshalPublic(p.Bytes)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import a PEM-encoded private key.
|
||||||
|
func ImportPrivatePEM(in []byte) (prv *PrivateKey, err error) {
|
||||||
|
p, _ := pem.Decode(in)
|
||||||
|
if p == nil || p.Type != "ELLIPTIC CURVE PRIVATE KEY" {
|
||||||
|
return nil, ErrInvalidPrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
prv, err = UnmarshalPrivate(p.Bytes)
|
||||||
|
return
|
||||||
|
}
|
||||||
331
crypto/ecies/ecies.go
Normal file
331
crypto/ecies/ecies.go
Normal file
|
|
@ -0,0 +1,331 @@
|
||||||
|
package ecies
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/subtle"
|
||||||
|
"fmt"
|
||||||
|
"hash"
|
||||||
|
"io"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrImport = fmt.Errorf("ecies: failed to import key")
|
||||||
|
ErrInvalidCurve = fmt.Errorf("ecies: invalid elliptic curve")
|
||||||
|
ErrInvalidParams = fmt.Errorf("ecies: invalid ECIES parameters")
|
||||||
|
ErrInvalidPublicKey = fmt.Errorf("ecies: invalid public key")
|
||||||
|
ErrSharedKeyIsPointAtInfinity = fmt.Errorf("ecies: shared key is point at infinity")
|
||||||
|
ErrSharedKeyTooBig = fmt.Errorf("ecies: shared key params are too big")
|
||||||
|
)
|
||||||
|
|
||||||
|
// PublicKey is a representation of an elliptic curve public key.
|
||||||
|
type PublicKey struct {
|
||||||
|
X *big.Int
|
||||||
|
Y *big.Int
|
||||||
|
elliptic.Curve
|
||||||
|
Params *ECIESParams
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export an ECIES public key as an ECDSA public key.
|
||||||
|
func (pub *PublicKey) ExportECDSA() *ecdsa.PublicKey {
|
||||||
|
return &ecdsa.PublicKey{pub.Curve, pub.X, pub.Y}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import an ECDSA public key as an ECIES public key.
|
||||||
|
func ImportECDSAPublic(pub *ecdsa.PublicKey) *PublicKey {
|
||||||
|
return &PublicKey{
|
||||||
|
X: pub.X,
|
||||||
|
Y: pub.Y,
|
||||||
|
Curve: pub.Curve,
|
||||||
|
Params: ParamsFromCurve(pub.Curve),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrivateKey is a representation of an elliptic curve private key.
|
||||||
|
type PrivateKey struct {
|
||||||
|
PublicKey
|
||||||
|
D *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export an ECIES private key as an ECDSA private key.
|
||||||
|
func (prv *PrivateKey) ExportECDSA() *ecdsa.PrivateKey {
|
||||||
|
pub := &prv.PublicKey
|
||||||
|
pubECDSA := pub.ExportECDSA()
|
||||||
|
return &ecdsa.PrivateKey{*pubECDSA, prv.D}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import an ECDSA private key as an ECIES private key.
|
||||||
|
func ImportECDSA(prv *ecdsa.PrivateKey) *PrivateKey {
|
||||||
|
pub := ImportECDSAPublic(&prv.PublicKey)
|
||||||
|
return &PrivateKey{*pub, prv.D}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate an elliptic curve public / private keypair. If params is nil,
|
||||||
|
// the recommended default paramters for the key will be chosen.
|
||||||
|
func GenerateKey(rand io.Reader, curve elliptic.Curve, params *ECIESParams) (prv *PrivateKey, err error) {
|
||||||
|
pb, x, y, err := elliptic.GenerateKey(curve, rand)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
prv = new(PrivateKey)
|
||||||
|
prv.PublicKey.X = x
|
||||||
|
prv.PublicKey.Y = y
|
||||||
|
prv.PublicKey.Curve = curve
|
||||||
|
prv.D = new(big.Int).SetBytes(pb)
|
||||||
|
if params == nil {
|
||||||
|
params = ParamsFromCurve(curve)
|
||||||
|
}
|
||||||
|
prv.PublicKey.Params = params
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxSharedKeyLength returns the maximum length of the shared key the
|
||||||
|
// public key can produce.
|
||||||
|
func MaxSharedKeyLength(pub *PublicKey) int {
|
||||||
|
return (pub.Curve.Params().BitSize + 7) / 8
|
||||||
|
}
|
||||||
|
|
||||||
|
// ECDH key agreement method used to establish secret keys for encryption.
|
||||||
|
func (prv *PrivateKey) GenerateShared(pub *PublicKey, skLen, macLen int) (sk []byte, err error) {
|
||||||
|
if prv.PublicKey.Curve != pub.Curve {
|
||||||
|
return nil, ErrInvalidCurve
|
||||||
|
}
|
||||||
|
if skLen+macLen > MaxSharedKeyLength(pub) {
|
||||||
|
return nil, ErrSharedKeyTooBig
|
||||||
|
}
|
||||||
|
x, _ := pub.Curve.ScalarMult(pub.X, pub.Y, prv.D.Bytes())
|
||||||
|
if x == nil {
|
||||||
|
return nil, ErrSharedKeyIsPointAtInfinity
|
||||||
|
}
|
||||||
|
|
||||||
|
sk = make([]byte, skLen+macLen)
|
||||||
|
skBytes := x.Bytes()
|
||||||
|
copy(sk[len(sk)-len(skBytes):], skBytes)
|
||||||
|
return sk, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrKeyDataTooLong = fmt.Errorf("ecies: can't supply requested key data")
|
||||||
|
ErrSharedTooLong = fmt.Errorf("ecies: shared secret is too long")
|
||||||
|
ErrInvalidMessage = fmt.Errorf("ecies: invalid message")
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
big2To32 = new(big.Int).Exp(big.NewInt(2), big.NewInt(32), nil)
|
||||||
|
big2To32M1 = new(big.Int).Sub(big2To32, big.NewInt(1))
|
||||||
|
)
|
||||||
|
|
||||||
|
func incCounter(ctr []byte) {
|
||||||
|
if ctr[3]++; ctr[3] != 0 {
|
||||||
|
return
|
||||||
|
} else if ctr[2]++; ctr[2] != 0 {
|
||||||
|
return
|
||||||
|
} else if ctr[1]++; ctr[1] != 0 {
|
||||||
|
return
|
||||||
|
} else if ctr[0]++; ctr[0] != 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// NIST SP 800-56 Concatenation Key Derivation Function (see section 5.8.1).
|
||||||
|
func concatKDF(hash hash.Hash, z, s1 []byte, kdLen int) (k []byte, err error) {
|
||||||
|
if s1 == nil {
|
||||||
|
s1 = make([]byte, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
reps := ((kdLen + 7) * 8) / (hash.BlockSize() * 8)
|
||||||
|
if big.NewInt(int64(reps)).Cmp(big2To32M1) > 0 {
|
||||||
|
fmt.Println(big2To32M1)
|
||||||
|
return nil, ErrKeyDataTooLong
|
||||||
|
}
|
||||||
|
|
||||||
|
counter := []byte{0, 0, 0, 1}
|
||||||
|
k = make([]byte, 0)
|
||||||
|
|
||||||
|
for i := 0; i <= reps; i++ {
|
||||||
|
hash.Write(counter)
|
||||||
|
hash.Write(z)
|
||||||
|
hash.Write(s1)
|
||||||
|
k = append(k, hash.Sum(nil)...)
|
||||||
|
hash.Reset()
|
||||||
|
incCounter(counter)
|
||||||
|
}
|
||||||
|
|
||||||
|
k = k[:kdLen]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// messageTag computes the MAC of a message (called the tag) as per
|
||||||
|
// SEC 1, 3.5.
|
||||||
|
func messageTag(hash func() hash.Hash, km, msg, shared []byte) []byte {
|
||||||
|
if shared == nil {
|
||||||
|
shared = make([]byte, 0)
|
||||||
|
}
|
||||||
|
mac := hmac.New(hash, km)
|
||||||
|
mac.Write(msg)
|
||||||
|
tag := mac.Sum(nil)
|
||||||
|
return tag
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate an initialisation vector for CTR mode.
|
||||||
|
func generateIV(params *ECIESParams, rand io.Reader) (iv []byte, err error) {
|
||||||
|
iv = make([]byte, params.BlockSize)
|
||||||
|
_, err = io.ReadFull(rand, iv)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// symEncrypt carries out CTR encryption using the block cipher specified in the
|
||||||
|
// parameters.
|
||||||
|
func symEncrypt(rand io.Reader, params *ECIESParams, key, m []byte) (ct []byte, err error) {
|
||||||
|
c, err := params.Cipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
iv, err := generateIV(params, rand)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctr := cipher.NewCTR(c, iv)
|
||||||
|
|
||||||
|
ct = make([]byte, len(m)+params.BlockSize)
|
||||||
|
copy(ct, iv)
|
||||||
|
ctr.XORKeyStream(ct[params.BlockSize:], m)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// symDecrypt carries out CTR decryption using the block cipher specified in
|
||||||
|
// the parameters
|
||||||
|
func symDecrypt(rand io.Reader, params *ECIESParams, key, ct []byte) (m []byte, err error) {
|
||||||
|
c, err := params.Cipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctr := cipher.NewCTR(c, ct[:params.BlockSize])
|
||||||
|
|
||||||
|
m = make([]byte, len(ct)-params.BlockSize)
|
||||||
|
ctr.XORKeyStream(m, ct[params.BlockSize:])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt encrypts a message using ECIES as specified in SEC 1, 5.1. If
|
||||||
|
// the shared information parameters aren't being used, they should be
|
||||||
|
// nil.
|
||||||
|
func Encrypt(rand io.Reader, pub *PublicKey, m, s1, s2 []byte) (ct []byte, err error) {
|
||||||
|
params := pub.Params
|
||||||
|
if params == nil {
|
||||||
|
if params = ParamsFromCurve(pub.Curve); params == nil {
|
||||||
|
err = ErrUnsupportedECIESParameters
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
R, err := GenerateKey(rand, pub.Curve, params)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := params.Hash()
|
||||||
|
z, err := R.GenerateShared(pub, params.KeyLen, params.KeyLen)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
K, err := concatKDF(hash, z, s1, params.KeyLen+params.KeyLen)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Ke := K[:params.KeyLen]
|
||||||
|
Km := K[params.KeyLen:]
|
||||||
|
hash.Write(Km)
|
||||||
|
Km = hash.Sum(nil)
|
||||||
|
hash.Reset()
|
||||||
|
|
||||||
|
em, err := symEncrypt(rand, params, Ke, m)
|
||||||
|
if err != nil || len(em) <= params.BlockSize {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
d := messageTag(params.Hash, Km, em, s2)
|
||||||
|
|
||||||
|
Rb := elliptic.Marshal(pub.Curve, R.PublicKey.X, R.PublicKey.Y)
|
||||||
|
ct = make([]byte, len(Rb)+len(em)+len(d))
|
||||||
|
copy(ct, Rb)
|
||||||
|
copy(ct[len(Rb):], em)
|
||||||
|
copy(ct[len(Rb)+len(em):], d)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt decrypts an ECIES ciphertext.
|
||||||
|
func (prv *PrivateKey) Decrypt(rand io.Reader, c, s1, s2 []byte) (m []byte, err error) {
|
||||||
|
if c == nil || len(c) == 0 {
|
||||||
|
err = ErrInvalidMessage
|
||||||
|
return
|
||||||
|
}
|
||||||
|
params := prv.PublicKey.Params
|
||||||
|
if params == nil {
|
||||||
|
if params = ParamsFromCurve(prv.PublicKey.Curve); params == nil {
|
||||||
|
err = ErrUnsupportedECIESParameters
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hash := params.Hash()
|
||||||
|
|
||||||
|
var (
|
||||||
|
rLen int
|
||||||
|
hLen int = hash.Size()
|
||||||
|
mStart int
|
||||||
|
mEnd int
|
||||||
|
)
|
||||||
|
|
||||||
|
switch c[0] {
|
||||||
|
case 2, 3, 4:
|
||||||
|
rLen = ((prv.PublicKey.Curve.Params().BitSize + 7) / 4)
|
||||||
|
if len(c) < (rLen + hLen + 1) {
|
||||||
|
err = ErrInvalidMessage
|
||||||
|
return
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
err = ErrInvalidPublicKey
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mStart = rLen
|
||||||
|
mEnd = len(c) - hLen
|
||||||
|
|
||||||
|
R := new(PublicKey)
|
||||||
|
R.Curve = prv.PublicKey.Curve
|
||||||
|
R.X, R.Y = elliptic.Unmarshal(R.Curve, c[:rLen])
|
||||||
|
if R.X == nil {
|
||||||
|
err = ErrInvalidPublicKey
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
z, err := prv.GenerateShared(R, params.KeyLen, params.KeyLen)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
K, err := concatKDF(hash, z, s1, params.KeyLen+params.KeyLen)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Ke := K[:params.KeyLen]
|
||||||
|
Km := K[params.KeyLen:]
|
||||||
|
hash.Write(Km)
|
||||||
|
Km = hash.Sum(nil)
|
||||||
|
hash.Reset()
|
||||||
|
|
||||||
|
d := messageTag(params.Hash, Km, c[mStart:mEnd], s2)
|
||||||
|
if subtle.ConstantTimeCompare(c[mEnd:], d) != 1 {
|
||||||
|
err = ErrInvalidMessage
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
m, err = symDecrypt(rand, params, Ke, c[mStart:mEnd])
|
||||||
|
return
|
||||||
|
}
|
||||||
489
crypto/ecies/ecies_test.go
Normal file
489
crypto/ecies/ecies_test.go
Normal file
|
|
@ -0,0 +1,489 @@
|
||||||
|
package ecies
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var dumpEnc bool
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flDump := flag.Bool("dump", false, "write encrypted test message to file")
|
||||||
|
flag.Parse()
|
||||||
|
dumpEnc = *flDump
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the KDF generates appropriately sized keys.
|
||||||
|
func TestKDF(t *testing.T) {
|
||||||
|
msg := []byte("Hello, world")
|
||||||
|
h := sha256.New()
|
||||||
|
|
||||||
|
k, err := concatKDF(h, msg, nil, 64)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
if len(k) != 64 {
|
||||||
|
fmt.Printf("KDF: generated key is the wrong size (%d instead of 64\n",
|
||||||
|
len(k))
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var skLen int
|
||||||
|
var ErrBadSharedKeys = fmt.Errorf("ecies: shared keys don't match")
|
||||||
|
|
||||||
|
// cmpParams compares a set of ECIES parameters. We assume, as per the
|
||||||
|
// docs, that AES is the only supported symmetric encryption algorithm.
|
||||||
|
func cmpParams(p1, p2 *ECIESParams) bool {
|
||||||
|
if p1.hashAlgo != p2.hashAlgo {
|
||||||
|
return false
|
||||||
|
} else if p1.KeyLen != p2.KeyLen {
|
||||||
|
return false
|
||||||
|
} else if p1.BlockSize != p2.BlockSize {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// cmpPublic returns true if the two public keys represent the same pojnt.
|
||||||
|
func cmpPublic(pub1, pub2 PublicKey) bool {
|
||||||
|
if pub1.X == nil || pub1.Y == nil {
|
||||||
|
fmt.Println(ErrInvalidPublicKey.Error())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if pub2.X == nil || pub2.Y == nil {
|
||||||
|
fmt.Println(ErrInvalidPublicKey.Error())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
pub1Out := elliptic.Marshal(pub1.Curve, pub1.X, pub1.Y)
|
||||||
|
pub2Out := elliptic.Marshal(pub2.Curve, pub2.X, pub2.Y)
|
||||||
|
|
||||||
|
return bytes.Equal(pub1Out, pub2Out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// cmpPrivate returns true if the two private keys are the same.
|
||||||
|
func cmpPrivate(prv1, prv2 *PrivateKey) bool {
|
||||||
|
if prv1 == nil || prv1.D == nil {
|
||||||
|
return false
|
||||||
|
} else if prv2 == nil || prv2.D == nil {
|
||||||
|
return false
|
||||||
|
} else if prv1.D.Cmp(prv2.D) != 0 {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return cmpPublic(prv1.PublicKey, prv2.PublicKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the ECDH component.
|
||||||
|
func TestSharedKey(t *testing.T) {
|
||||||
|
prv1, err := GenerateKey(rand.Reader, DefaultCurve, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
skLen = MaxSharedKeyLength(&prv1.PublicKey) / 2
|
||||||
|
|
||||||
|
prv2, err := GenerateKey(rand.Reader, DefaultCurve, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
sk1, err := prv1.GenerateShared(&prv2.PublicKey, skLen, skLen)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
sk2, err := prv2.GenerateShared(&prv1.PublicKey, skLen, skLen)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(sk1, sk2) {
|
||||||
|
fmt.Println(ErrBadSharedKeys.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that the key generation code fails when too much key data is
|
||||||
|
// requested.
|
||||||
|
func TestTooBigSharedKey(t *testing.T) {
|
||||||
|
prv1, err := GenerateKey(rand.Reader, DefaultCurve, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
prv2, err := GenerateKey(rand.Reader, DefaultCurve, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = prv1.GenerateShared(&prv2.PublicKey, skLen*2, skLen*2)
|
||||||
|
if err != ErrSharedKeyTooBig {
|
||||||
|
fmt.Println("ecdh: shared key should be too large for curve")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = prv2.GenerateShared(&prv1.PublicKey, skLen*2, skLen*2)
|
||||||
|
if err != ErrSharedKeyTooBig {
|
||||||
|
fmt.Println("ecdh: shared key should be too large for curve")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure a public key can be successfully marshalled and unmarshalled, and
|
||||||
|
// that the decoded key is the same as the original.
|
||||||
|
func TestMarshalPublic(t *testing.T) {
|
||||||
|
prv, err := GenerateKey(rand.Reader, DefaultCurve, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := MarshalPublic(&prv.PublicKey)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub, err := UnmarshalPublic(out)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmpPublic(prv.PublicKey, *pub) {
|
||||||
|
fmt.Println("ecies: failed to unmarshal public key")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that a private key can be encoded into DER format, and that
|
||||||
|
// the resulting key is properly parsed back into a public key.
|
||||||
|
func TestMarshalPrivate(t *testing.T) {
|
||||||
|
prv, err := GenerateKey(rand.Reader, DefaultCurve, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := MarshalPrivate(prv)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if dumpEnc {
|
||||||
|
ioutil.WriteFile("test.out", out, 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
prv2, err := UnmarshalPrivate(out)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmpPrivate(prv, prv2) {
|
||||||
|
fmt.Println("ecdh: private key import failed")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that a private key can be successfully encoded to PEM format, and
|
||||||
|
// the resulting key is properly parsed back in.
|
||||||
|
func TestPrivatePEM(t *testing.T) {
|
||||||
|
prv, err := GenerateKey(rand.Reader, DefaultCurve, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := ExportPrivatePEM(prv)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if dumpEnc {
|
||||||
|
ioutil.WriteFile("test.key", out, 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
prv2, err := ImportPrivatePEM(out)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
} else if !cmpPrivate(prv, prv2) {
|
||||||
|
fmt.Println("ecdh: import from PEM failed")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that a public key can be successfully encoded to PEM format, and
|
||||||
|
// the resulting key is properly parsed back in.
|
||||||
|
func TestPublicPEM(t *testing.T) {
|
||||||
|
prv, err := GenerateKey(rand.Reader, DefaultCurve, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := ExportPublicPEM(&prv.PublicKey)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if dumpEnc {
|
||||||
|
ioutil.WriteFile("test.pem", out, 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub2, err := ImportPublicPEM(out)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
} else if !cmpPublic(prv.PublicKey, *pub2) {
|
||||||
|
fmt.Println("ecdh: import from PEM failed")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Benchmark the generation of P256 keys.
|
||||||
|
func BenchmarkGenerateKeyP256(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
if _, err := GenerateKey(rand.Reader, elliptic.P256(), nil); err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
b.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Benchmark the generation of P256 shared keys.
|
||||||
|
func BenchmarkGenSharedKeyP256(b *testing.B) {
|
||||||
|
prv, err := GenerateKey(rand.Reader, elliptic.P256(), nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
b.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, err := prv.GenerateShared(&prv.PublicKey, skLen, skLen)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
b.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that an encrypted message can be successfully decrypted.
|
||||||
|
func TestEncryptDecrypt(t *testing.T) {
|
||||||
|
prv1, err := GenerateKey(rand.Reader, DefaultCurve, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
prv2, err := GenerateKey(rand.Reader, DefaultCurve, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
message := []byte("Hello, world.")
|
||||||
|
ct, err := Encrypt(rand.Reader, &prv2.PublicKey, message, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
pt, err := prv2.Decrypt(rand.Reader, ct, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(pt, message) {
|
||||||
|
fmt.Println("ecies: plaintext doesn't match message")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = prv1.Decrypt(rand.Reader, ct, nil, nil)
|
||||||
|
if err == nil {
|
||||||
|
fmt.Println("ecies: encryption should not have succeeded")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestMarshalEncryption validates the encode/decode produces a valid
|
||||||
|
// ECIES encryption key.
|
||||||
|
func TestMarshalEncryption(t *testing.T) {
|
||||||
|
prv1, err := GenerateKey(rand.Reader, DefaultCurve, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := MarshalPrivate(prv1)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
prv2, err := UnmarshalPrivate(out)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
message := []byte("Hello, world.")
|
||||||
|
ct, err := Encrypt(rand.Reader, &prv2.PublicKey, message, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
pt, err := prv2.Decrypt(rand.Reader, ct, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(pt, message) {
|
||||||
|
fmt.Println("ecies: plaintext doesn't match message")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = prv1.Decrypt(rand.Reader, ct, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type testCase struct {
|
||||||
|
Curve elliptic.Curve
|
||||||
|
Name string
|
||||||
|
Expected bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var testCases = []testCase{
|
||||||
|
testCase{
|
||||||
|
Curve: elliptic.P224(),
|
||||||
|
Name: "P224",
|
||||||
|
Expected: false,
|
||||||
|
},
|
||||||
|
testCase{
|
||||||
|
Curve: elliptic.P256(),
|
||||||
|
Name: "P256",
|
||||||
|
Expected: true,
|
||||||
|
},
|
||||||
|
testCase{
|
||||||
|
Curve: elliptic.P384(),
|
||||||
|
Name: "P384",
|
||||||
|
Expected: true,
|
||||||
|
},
|
||||||
|
testCase{
|
||||||
|
Curve: elliptic.P521(),
|
||||||
|
Name: "P521",
|
||||||
|
Expected: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test parameter selection for each curve, and that P224 fails automatic
|
||||||
|
// parameter selection (see README for a discussion of P224). Ensures that
|
||||||
|
// selecting a set of parameters automatically for the given curve works.
|
||||||
|
func TestParamSelection(t *testing.T) {
|
||||||
|
for _, c := range testCases {
|
||||||
|
testParamSelection(t, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testParamSelection(t *testing.T, c testCase) {
|
||||||
|
params := ParamsFromCurve(c.Curve)
|
||||||
|
if params == nil && c.Expected {
|
||||||
|
fmt.Printf("%s (%s)\n", ErrInvalidParams.Error(), c.Name)
|
||||||
|
t.FailNow()
|
||||||
|
} else if params != nil && !c.Expected {
|
||||||
|
fmt.Printf("ecies: parameters should be invalid (%s)\n",
|
||||||
|
c.Name)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
prv1, err := GenerateKey(rand.Reader, DefaultCurve, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("%s (%s)\n", err.Error(), c.Name)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
prv2, err := GenerateKey(rand.Reader, DefaultCurve, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("%s (%s)\n", err.Error(), c.Name)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
message := []byte("Hello, world.")
|
||||||
|
ct, err := Encrypt(rand.Reader, &prv2.PublicKey, message, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("%s (%s)\n", err.Error(), c.Name)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
pt, err := prv2.Decrypt(rand.Reader, ct, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("%s (%s)\n", err.Error(), c.Name)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(pt, message) {
|
||||||
|
fmt.Printf("ecies: plaintext doesn't match message (%s)\n",
|
||||||
|
c.Name)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = prv1.Decrypt(rand.Reader, ct, nil, nil)
|
||||||
|
if err == nil {
|
||||||
|
fmt.Printf("ecies: encryption should not have succeeded (%s)\n",
|
||||||
|
c.Name)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that the basic public key validation in the decryption operation
|
||||||
|
// works.
|
||||||
|
func TestBasicKeyValidation(t *testing.T) {
|
||||||
|
badBytes := []byte{0, 1, 5, 6, 7, 8, 9}
|
||||||
|
|
||||||
|
prv, err := GenerateKey(rand.Reader, DefaultCurve, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
message := []byte("Hello, world.")
|
||||||
|
ct, err := Encrypt(rand.Reader, &prv.PublicKey, message, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, b := range badBytes {
|
||||||
|
ct[0] = b
|
||||||
|
_, err := prv.Decrypt(rand.Reader, ct, nil, nil)
|
||||||
|
if err != ErrInvalidPublicKey {
|
||||||
|
fmt.Println("ecies: validated an invalid key")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
181
crypto/ecies/params.go
Normal file
181
crypto/ecies/params.go
Normal file
|
|
@ -0,0 +1,181 @@
|
||||||
|
package ecies
|
||||||
|
|
||||||
|
// This file contains parameters for ECIES encryption, specifying the
|
||||||
|
// symmetric encryption and HMAC parameters.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/sha512"
|
||||||
|
"fmt"
|
||||||
|
"hash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The default curve for this package is the NIST P256 curve, which
|
||||||
|
// provides security equivalent to AES-128.
|
||||||
|
var DefaultCurve = elliptic.P256()
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrUnsupportedECDHAlgorithm = fmt.Errorf("ecies: unsupported ECDH algorithm")
|
||||||
|
ErrUnsupportedECIESParameters = fmt.Errorf("ecies: unsupported ECIES parameters")
|
||||||
|
)
|
||||||
|
|
||||||
|
type ECIESParams struct {
|
||||||
|
Hash func() hash.Hash // hash function
|
||||||
|
hashAlgo crypto.Hash
|
||||||
|
Cipher func([]byte) (cipher.Block, error) // symmetric cipher
|
||||||
|
BlockSize int // block size of symmetric cipher
|
||||||
|
KeyLen int // length of symmetric key
|
||||||
|
}
|
||||||
|
|
||||||
|
// Standard ECIES parameters:
|
||||||
|
// * ECIES using AES128 and HMAC-SHA-256-16
|
||||||
|
// * ECIES using AES256 and HMAC-SHA-256-32
|
||||||
|
// * ECIES using AES256 and HMAC-SHA-384-48
|
||||||
|
// * ECIES using AES256 and HMAC-SHA-512-64
|
||||||
|
|
||||||
|
var (
|
||||||
|
ECIES_AES128_SHA256 = &ECIESParams{
|
||||||
|
Hash: sha256.New,
|
||||||
|
hashAlgo: crypto.SHA256,
|
||||||
|
Cipher: aes.NewCipher,
|
||||||
|
BlockSize: aes.BlockSize,
|
||||||
|
KeyLen: 16,
|
||||||
|
}
|
||||||
|
|
||||||
|
ECIES_AES256_SHA256 = &ECIESParams{
|
||||||
|
Hash: sha256.New,
|
||||||
|
hashAlgo: crypto.SHA256,
|
||||||
|
Cipher: aes.NewCipher,
|
||||||
|
BlockSize: aes.BlockSize,
|
||||||
|
KeyLen: 32,
|
||||||
|
}
|
||||||
|
|
||||||
|
ECIES_AES256_SHA384 = &ECIESParams{
|
||||||
|
Hash: sha512.New384,
|
||||||
|
hashAlgo: crypto.SHA384,
|
||||||
|
Cipher: aes.NewCipher,
|
||||||
|
BlockSize: aes.BlockSize,
|
||||||
|
KeyLen: 32,
|
||||||
|
}
|
||||||
|
|
||||||
|
ECIES_AES256_SHA512 = &ECIESParams{
|
||||||
|
Hash: sha512.New,
|
||||||
|
hashAlgo: crypto.SHA512,
|
||||||
|
Cipher: aes.NewCipher,
|
||||||
|
BlockSize: aes.BlockSize,
|
||||||
|
KeyLen: 32,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var paramsFromCurve = map[elliptic.Curve]*ECIESParams{
|
||||||
|
elliptic.P256(): ECIES_AES128_SHA256,
|
||||||
|
elliptic.P384(): ECIES_AES256_SHA384,
|
||||||
|
elliptic.P521(): ECIES_AES256_SHA512,
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddParamsForCurve(curve elliptic.Curve, params *ECIESParams) {
|
||||||
|
paramsFromCurve[curve] = params
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParamsFromCurve selects parameters optimal for the selected elliptic curve.
|
||||||
|
// Only the curves P256, P384, and P512 are supported.
|
||||||
|
func ParamsFromCurve(curve elliptic.Curve) (params *ECIESParams) {
|
||||||
|
return paramsFromCurve[curve]
|
||||||
|
|
||||||
|
/*
|
||||||
|
switch curve {
|
||||||
|
case elliptic.P256():
|
||||||
|
return ECIES_AES128_SHA256
|
||||||
|
case elliptic.P384():
|
||||||
|
return ECIES_AES256_SHA384
|
||||||
|
case elliptic.P521():
|
||||||
|
return ECIES_AES256_SHA512
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// ASN.1 encode the ECIES parameters relevant to the encryption operations.
|
||||||
|
func paramsToASNECIES(params *ECIESParams) (asnParams asnECIESParameters) {
|
||||||
|
if nil == params {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
asnParams.KDF = asnNISTConcatenationKDF
|
||||||
|
asnParams.MAC = hmacFull
|
||||||
|
switch params.KeyLen {
|
||||||
|
case 16:
|
||||||
|
asnParams.Sym = aes128CTRinECIES
|
||||||
|
case 24:
|
||||||
|
asnParams.Sym = aes192CTRinECIES
|
||||||
|
case 32:
|
||||||
|
asnParams.Sym = aes256CTRinECIES
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ASN.1 encode the ECIES parameters relevant to ECDH.
|
||||||
|
func paramsToASNECDH(params *ECIESParams) (algo asnECDHAlgorithm) {
|
||||||
|
switch params.hashAlgo {
|
||||||
|
case crypto.SHA224:
|
||||||
|
algo = dhSinglePass_stdDH_sha224kdf
|
||||||
|
case crypto.SHA256:
|
||||||
|
algo = dhSinglePass_stdDH_sha256kdf
|
||||||
|
case crypto.SHA384:
|
||||||
|
algo = dhSinglePass_stdDH_sha384kdf
|
||||||
|
case crypto.SHA512:
|
||||||
|
algo = dhSinglePass_stdDH_sha512kdf
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ASN.1 decode the ECIES parameters relevant to the encryption stage.
|
||||||
|
func asnECIEStoParams(asnParams asnECIESParameters, params *ECIESParams) {
|
||||||
|
if !asnParams.KDF.Cmp(asnNISTConcatenationKDF) {
|
||||||
|
params = nil
|
||||||
|
return
|
||||||
|
} else if !asnParams.MAC.Cmp(hmacFull) {
|
||||||
|
params = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case asnParams.Sym.Cmp(aes128CTRinECIES):
|
||||||
|
params.KeyLen = 16
|
||||||
|
params.BlockSize = 16
|
||||||
|
params.Cipher = aes.NewCipher
|
||||||
|
case asnParams.Sym.Cmp(aes192CTRinECIES):
|
||||||
|
params.KeyLen = 24
|
||||||
|
params.BlockSize = 16
|
||||||
|
params.Cipher = aes.NewCipher
|
||||||
|
case asnParams.Sym.Cmp(aes256CTRinECIES):
|
||||||
|
params.KeyLen = 32
|
||||||
|
params.BlockSize = 16
|
||||||
|
params.Cipher = aes.NewCipher
|
||||||
|
default:
|
||||||
|
params = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ASN.1 decode the ECIES parameters relevant to ECDH.
|
||||||
|
func asnECDHtoParams(asnParams asnECDHAlgorithm, params *ECIESParams) {
|
||||||
|
if asnParams.Cmp(dhSinglePass_stdDH_sha224kdf) {
|
||||||
|
params.hashAlgo = crypto.SHA224
|
||||||
|
params.Hash = sha256.New224
|
||||||
|
} else if asnParams.Cmp(dhSinglePass_stdDH_sha256kdf) {
|
||||||
|
params.hashAlgo = crypto.SHA256
|
||||||
|
params.Hash = sha256.New
|
||||||
|
} else if asnParams.Cmp(dhSinglePass_stdDH_sha384kdf) {
|
||||||
|
params.hashAlgo = crypto.SHA384
|
||||||
|
params.Hash = sha512.New384
|
||||||
|
} else if asnParams.Cmp(dhSinglePass_stdDH_sha512kdf) {
|
||||||
|
params.hashAlgo = crypto.SHA512
|
||||||
|
params.Hash = sha512.New
|
||||||
|
} else {
|
||||||
|
params = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -68,10 +68,10 @@ import (
|
||||||
"code.google.com/p/go.crypto/scrypt"
|
"code.google.com/p/go.crypto/scrypt"
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
crand "crypto/rand"
|
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto/randentropy"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
|
@ -116,7 +116,7 @@ func (ks keyStorePassphrase) GetKeyAddresses() (addresses [][]byte, err error) {
|
||||||
|
|
||||||
func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) {
|
func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) {
|
||||||
authArray := []byte(auth)
|
authArray := []byte(auth)
|
||||||
salt := GetEntropyCSPRNG(32)
|
salt := randentropy.GetEntropyMixed(32)
|
||||||
derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen)
|
derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -131,7 +131,7 @@ func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
iv := GetEntropyCSPRNG(aes.BlockSize) // 16
|
iv := randentropy.GetEntropyMixed(aes.BlockSize) // 16
|
||||||
AES256CBCEncrypter := cipher.NewCBCEncrypter(AES256Block, iv)
|
AES256CBCEncrypter := cipher.NewCBCEncrypter(AES256Block, iv)
|
||||||
cipherText := make([]byte, len(toEncrypt))
|
cipherText := make([]byte, len(toEncrypt))
|
||||||
AES256CBCEncrypter.CryptBlocks(cipherText, toEncrypt)
|
AES256CBCEncrypter.CryptBlocks(cipherText, toEncrypt)
|
||||||
|
|
@ -196,12 +196,3 @@ func DecryptKey(ks keyStorePassphrase, keyAddr []byte, auth string) (keyBytes []
|
||||||
}
|
}
|
||||||
return keyBytes, keyId, err
|
return keyBytes, keyId, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetEntropyCSPRNG(n int) []byte {
|
|
||||||
mainBuff := make([]byte, n)
|
|
||||||
_, err := io.ReadFull(crand.Reader, mainBuff)
|
|
||||||
if err != nil {
|
|
||||||
panic("key generation: reading from crypto/rand failed: " + err.Error())
|
|
||||||
}
|
|
||||||
return mainBuff
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
crand "crypto/rand"
|
"github.com/ethereum/go-ethereum/crypto/randentropy"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
@ -9,7 +9,7 @@ import (
|
||||||
func TestKeyStorePlain(t *testing.T) {
|
func TestKeyStorePlain(t *testing.T) {
|
||||||
ks := NewKeyStorePlain(DefaultDataDir())
|
ks := NewKeyStorePlain(DefaultDataDir())
|
||||||
pass := "" // not used but required by API
|
pass := "" // not used but required by API
|
||||||
k1, err := ks.GenerateNewKey(crand.Reader, pass)
|
k1, err := ks.GenerateNewKey(randentropy.Reader, pass)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -37,7 +37,7 @@ func TestKeyStorePlain(t *testing.T) {
|
||||||
func TestKeyStorePassphrase(t *testing.T) {
|
func TestKeyStorePassphrase(t *testing.T) {
|
||||||
ks := NewKeyStorePassphrase(DefaultDataDir())
|
ks := NewKeyStorePassphrase(DefaultDataDir())
|
||||||
pass := "foo"
|
pass := "foo"
|
||||||
k1, err := ks.GenerateNewKey(crand.Reader, pass)
|
k1, err := ks.GenerateNewKey(randentropy.Reader, pass)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -63,7 +63,7 @@ func TestKeyStorePassphrase(t *testing.T) {
|
||||||
func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
|
func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
|
||||||
ks := NewKeyStorePassphrase(DefaultDataDir())
|
ks := NewKeyStorePassphrase(DefaultDataDir())
|
||||||
pass := "foo"
|
pass := "foo"
|
||||||
k1, err := ks.GenerateNewKey(crand.Reader, pass)
|
k1, err := ks.GenerateNewKey(randentropy.Reader, pass)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
84
crypto/randentropy/rand_entropy.go
Normal file
84
crypto/randentropy/rand_entropy.go
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
package randentropy
|
||||||
|
|
||||||
|
import (
|
||||||
|
crand "crypto/rand"
|
||||||
|
"encoding/binary"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Reader io.Reader = &randEntropy{}
|
||||||
|
|
||||||
|
type randEntropy struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*randEntropy) Read(bytes []byte) (n int, err error) {
|
||||||
|
readBytes := GetEntropyMixed(len(bytes))
|
||||||
|
copy(bytes, readBytes)
|
||||||
|
return len(bytes), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: copied from crypto.go , move to sha3 package?
|
||||||
|
func Sha3(data []byte) []byte {
|
||||||
|
d := sha3.NewKeccak256()
|
||||||
|
d.Write(data)
|
||||||
|
|
||||||
|
return d.Sum(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: verify. this needs to be audited
|
||||||
|
// we start with crypt/rand, then XOR in additional entropy from OS
|
||||||
|
func GetEntropyMixed(n int) []byte {
|
||||||
|
startTime := time.Now().UnixNano()
|
||||||
|
// for each source, we take SHA3 of the source and use it as seed to math/rand
|
||||||
|
// then read bytes from it and XOR them onto the bytes read from crypto/rand
|
||||||
|
mainBuff := GetEntropyCSPRNG(n)
|
||||||
|
// 1. OS entropy sources
|
||||||
|
startTimeBytes := make([]byte, 32)
|
||||||
|
binary.PutVarint(startTimeBytes, startTime)
|
||||||
|
startTimeHash := Sha3(startTimeBytes)
|
||||||
|
mixBytes(mainBuff, startTimeHash)
|
||||||
|
|
||||||
|
pid := os.Getpid()
|
||||||
|
pidBytes := make([]byte, 32)
|
||||||
|
binary.PutUvarint(pidBytes, uint64(pid))
|
||||||
|
pidHash := Sha3(pidBytes)
|
||||||
|
mixBytes(mainBuff, pidHash)
|
||||||
|
|
||||||
|
osEnv := os.Environ()
|
||||||
|
osEnvBytes := []byte(strings.Join(osEnv, ""))
|
||||||
|
osEnvHash := Sha3(osEnvBytes)
|
||||||
|
mixBytes(mainBuff, osEnvHash)
|
||||||
|
|
||||||
|
// not all OS have hostname in env variables
|
||||||
|
osHostName, err := os.Hostname()
|
||||||
|
if err != nil {
|
||||||
|
osHostNameBytes := []byte(osHostName)
|
||||||
|
osHostNameHash := Sha3(osHostNameBytes)
|
||||||
|
mixBytes(mainBuff, osHostNameHash)
|
||||||
|
}
|
||||||
|
return mainBuff
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetEntropyCSPRNG(n int) []byte {
|
||||||
|
mainBuff := make([]byte, n)
|
||||||
|
_, err := io.ReadFull(crand.Reader, mainBuff)
|
||||||
|
if err != nil {
|
||||||
|
panic("reading from crypto/rand failed: " + err.Error())
|
||||||
|
}
|
||||||
|
return mainBuff
|
||||||
|
}
|
||||||
|
|
||||||
|
func mixBytes(buff []byte, mixBuff []byte) []byte {
|
||||||
|
bytesToMix := len(buff)
|
||||||
|
if bytesToMix > 32 {
|
||||||
|
bytesToMix = 32
|
||||||
|
}
|
||||||
|
for i := 0; i < bytesToMix; i++ {
|
||||||
|
buff[i] ^= mixBuff[i]
|
||||||
|
}
|
||||||
|
return buff
|
||||||
|
}
|
||||||
|
|
@ -15,6 +15,7 @@ import "C"
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto/randentropy"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -68,7 +69,7 @@ func GenerateKeyPair() ([]byte, []byte) {
|
||||||
const seckey_len = 32
|
const seckey_len = 32
|
||||||
|
|
||||||
var pubkey []byte = make([]byte, pubkey_len)
|
var pubkey []byte = make([]byte, pubkey_len)
|
||||||
var seckey []byte = RandByte(seckey_len)
|
var seckey []byte = randentropy.GetEntropyMixed(seckey_len)
|
||||||
|
|
||||||
var pubkey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&pubkey[0]))
|
var pubkey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&pubkey[0]))
|
||||||
var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0]))
|
var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0]))
|
||||||
|
|
@ -124,7 +125,7 @@ int secp256k1_ecdsa_sign_compact(const unsigned char *msg, int msglen,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func Sign(msg []byte, seckey []byte) ([]byte, error) {
|
func Sign(msg []byte, seckey []byte) ([]byte, error) {
|
||||||
nonce := RandByte(32)
|
nonce := randentropy.GetEntropyMixed(32)
|
||||||
|
|
||||||
var sig []byte = make([]byte, 65)
|
var sig []byte = make([]byte, 65)
|
||||||
var recid C.int
|
var recid C.int
|
||||||
|
|
|
||||||
|
|
@ -1,97 +0,0 @@
|
||||||
package secp256k1
|
|
||||||
|
|
||||||
import (
|
|
||||||
crand "crypto/rand"
|
|
||||||
"io"
|
|
||||||
mrand "math/rand"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
Note:
|
|
||||||
|
|
||||||
- On windows cryto/rand uses CrytoGenRandom which uses RC4 which is insecure
|
|
||||||
- Android random number generator is known to be insecure.
|
|
||||||
- Linux uses /dev/urandom , which is thought to be secure and uses entropy pool
|
|
||||||
|
|
||||||
Therefore the output is salted.
|
|
||||||
*/
|
|
||||||
|
|
||||||
//finalizer from MurmerHash3
|
|
||||||
func mmh3f(key uint64) uint64 {
|
|
||||||
key ^= key >> 33
|
|
||||||
key *= 0xff51afd7ed558ccd
|
|
||||||
key ^= key >> 33
|
|
||||||
key *= 0xc4ceb9fe1a85ec53
|
|
||||||
key ^= key >> 33
|
|
||||||
return key
|
|
||||||
}
|
|
||||||
|
|
||||||
//knuth hash
|
|
||||||
func knuth_hash(in []byte) uint64 {
|
|
||||||
var acc uint64 = 3074457345618258791
|
|
||||||
for i := 0; i < len(in); i++ {
|
|
||||||
acc += uint64(in[i])
|
|
||||||
acc *= 3074457345618258799
|
|
||||||
}
|
|
||||||
return acc
|
|
||||||
}
|
|
||||||
|
|
||||||
var _rand *mrand.Rand
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
var seed1 uint64 = mmh3f(uint64(time.Now().UnixNano()))
|
|
||||||
var seed2 uint64 = knuth_hash([]byte(strings.Join(os.Environ(), "")))
|
|
||||||
var seed3 uint64 = mmh3f(uint64(os.Getpid()))
|
|
||||||
|
|
||||||
_rand = mrand.New(mrand.NewSource(int64(seed1 ^ seed2 ^ seed3)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func saltByte(n int) []byte {
|
|
||||||
buff := make([]byte, n)
|
|
||||||
for i := 0; i < len(buff); i++ {
|
|
||||||
var v uint64 = uint64(_rand.Int63())
|
|
||||||
var b byte
|
|
||||||
for j := 0; j < 8; j++ {
|
|
||||||
b ^= byte(v & 0xff)
|
|
||||||
v = v >> 8
|
|
||||||
}
|
|
||||||
buff[i] = b
|
|
||||||
}
|
|
||||||
return buff
|
|
||||||
}
|
|
||||||
|
|
||||||
//On Unix-like systems, Reader reads from /dev/urandom.
|
|
||||||
//On Windows systems, Reader uses the CryptGenRandom API.
|
|
||||||
|
|
||||||
//use entropy pool etc and cryptographic random number generator
|
|
||||||
//mix in time
|
|
||||||
//mix in mix in cpu cycle count
|
|
||||||
func RandByte(n int) []byte {
|
|
||||||
buff := make([]byte, n)
|
|
||||||
ret, err := io.ReadFull(crand.Reader, buff)
|
|
||||||
if len(buff) != ret || err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
buff2 := saltByte(n)
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
buff[i] ^= buff2[2]
|
|
||||||
}
|
|
||||||
return buff
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
On Unix-like systems, Reader reads from /dev/urandom.
|
|
||||||
On Windows systems, Reader uses the CryptGenRandom API.
|
|
||||||
*/
|
|
||||||
func RandByteWeakCrypto(n int) []byte {
|
|
||||||
buff := make([]byte, n)
|
|
||||||
ret, err := io.ReadFull(crand.Reader, buff)
|
|
||||||
if len(buff) != ret || err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return buff
|
|
||||||
}
|
|
||||||
|
|
@ -3,6 +3,7 @@ package secp256k1
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto/randentropy"
|
||||||
"log"
|
"log"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
@ -12,7 +13,7 @@ const SigSize = 65 //64+1
|
||||||
|
|
||||||
func Test_Secp256_00(t *testing.T) {
|
func Test_Secp256_00(t *testing.T) {
|
||||||
|
|
||||||
var nonce []byte = RandByte(32) //going to get bitcoins stolen!
|
var nonce []byte = randentropy.GetEntropyMixed(32) //going to get bitcoins stolen!
|
||||||
|
|
||||||
if len(nonce) != 32 {
|
if len(nonce) != 32 {
|
||||||
t.Fatal()
|
t.Fatal()
|
||||||
|
|
@ -50,7 +51,7 @@ func Test_Secp256_01(t *testing.T) {
|
||||||
//test size of messages
|
//test size of messages
|
||||||
func Test_Secp256_02s(t *testing.T) {
|
func Test_Secp256_02s(t *testing.T) {
|
||||||
pubkey, seckey := GenerateKeyPair()
|
pubkey, seckey := GenerateKeyPair()
|
||||||
msg := RandByte(32)
|
msg := randentropy.GetEntropyMixed(32)
|
||||||
sig, _ := Sign(msg, seckey)
|
sig, _ := Sign(msg, seckey)
|
||||||
CompactSigTest(sig)
|
CompactSigTest(sig)
|
||||||
if sig == nil {
|
if sig == nil {
|
||||||
|
|
@ -73,7 +74,7 @@ func Test_Secp256_02s(t *testing.T) {
|
||||||
//test signing message
|
//test signing message
|
||||||
func Test_Secp256_02(t *testing.T) {
|
func Test_Secp256_02(t *testing.T) {
|
||||||
pubkey1, seckey := GenerateKeyPair()
|
pubkey1, seckey := GenerateKeyPair()
|
||||||
msg := RandByte(32)
|
msg := randentropy.GetEntropyMixed(32)
|
||||||
sig, _ := Sign(msg, seckey)
|
sig, _ := Sign(msg, seckey)
|
||||||
if sig == nil {
|
if sig == nil {
|
||||||
t.Fatal("Signature nil")
|
t.Fatal("Signature nil")
|
||||||
|
|
@ -96,7 +97,7 @@ func Test_Secp256_02(t *testing.T) {
|
||||||
//test pubkey recovery
|
//test pubkey recovery
|
||||||
func Test_Secp256_02a(t *testing.T) {
|
func Test_Secp256_02a(t *testing.T) {
|
||||||
pubkey1, seckey1 := GenerateKeyPair()
|
pubkey1, seckey1 := GenerateKeyPair()
|
||||||
msg := RandByte(32)
|
msg := randentropy.GetEntropyMixed(32)
|
||||||
sig, _ := Sign(msg, seckey1)
|
sig, _ := Sign(msg, seckey1)
|
||||||
|
|
||||||
if sig == nil {
|
if sig == nil {
|
||||||
|
|
@ -125,7 +126,7 @@ func Test_Secp256_02a(t *testing.T) {
|
||||||
func Test_Secp256_03(t *testing.T) {
|
func Test_Secp256_03(t *testing.T) {
|
||||||
_, seckey := GenerateKeyPair()
|
_, seckey := GenerateKeyPair()
|
||||||
for i := 0; i < TESTS; i++ {
|
for i := 0; i < TESTS; i++ {
|
||||||
msg := RandByte(32)
|
msg := randentropy.GetEntropyMixed(32)
|
||||||
sig, _ := Sign(msg, seckey)
|
sig, _ := Sign(msg, seckey)
|
||||||
CompactSigTest(sig)
|
CompactSigTest(sig)
|
||||||
|
|
||||||
|
|
@ -141,7 +142,7 @@ func Test_Secp256_03(t *testing.T) {
|
||||||
func Test_Secp256_04(t *testing.T) {
|
func Test_Secp256_04(t *testing.T) {
|
||||||
for i := 0; i < TESTS; i++ {
|
for i := 0; i < TESTS; i++ {
|
||||||
pubkey1, seckey := GenerateKeyPair()
|
pubkey1, seckey := GenerateKeyPair()
|
||||||
msg := RandByte(32)
|
msg := randentropy.GetEntropyMixed(32)
|
||||||
sig, _ := Sign(msg, seckey)
|
sig, _ := Sign(msg, seckey)
|
||||||
CompactSigTest(sig)
|
CompactSigTest(sig)
|
||||||
|
|
||||||
|
|
@ -164,7 +165,7 @@ func Test_Secp256_04(t *testing.T) {
|
||||||
// -SIPA look at this
|
// -SIPA look at this
|
||||||
|
|
||||||
func randSig() []byte {
|
func randSig() []byte {
|
||||||
sig := RandByte(65)
|
sig := randentropy.GetEntropyMixed(65)
|
||||||
sig[32] &= 0x70
|
sig[32] &= 0x70
|
||||||
sig[64] %= 4
|
sig[64] %= 4
|
||||||
return sig
|
return sig
|
||||||
|
|
@ -172,7 +173,7 @@ func randSig() []byte {
|
||||||
|
|
||||||
func Test_Secp256_06a_alt0(t *testing.T) {
|
func Test_Secp256_06a_alt0(t *testing.T) {
|
||||||
pubkey1, seckey := GenerateKeyPair()
|
pubkey1, seckey := GenerateKeyPair()
|
||||||
msg := RandByte(32)
|
msg := randentropy.GetEntropyMixed(32)
|
||||||
sig, _ := Sign(msg, seckey)
|
sig, _ := Sign(msg, seckey)
|
||||||
|
|
||||||
if sig == nil {
|
if sig == nil {
|
||||||
|
|
@ -203,12 +204,12 @@ func Test_Secp256_06a_alt0(t *testing.T) {
|
||||||
|
|
||||||
func Test_Secp256_06b(t *testing.T) {
|
func Test_Secp256_06b(t *testing.T) {
|
||||||
pubkey1, seckey := GenerateKeyPair()
|
pubkey1, seckey := GenerateKeyPair()
|
||||||
msg := RandByte(32)
|
msg := randentropy.GetEntropyMixed(32)
|
||||||
sig, _ := Sign(msg, seckey)
|
sig, _ := Sign(msg, seckey)
|
||||||
|
|
||||||
fail_count := 0
|
fail_count := 0
|
||||||
for i := 0; i < TESTS; i++ {
|
for i := 0; i < TESTS; i++ {
|
||||||
msg = RandByte(32)
|
msg = randentropy.GetEntropyMixed(32)
|
||||||
pubkey2, _ := RecoverPubkey(msg, sig)
|
pubkey2, _ := RecoverPubkey(msg, sig)
|
||||||
if bytes.Equal(pubkey1, pubkey2) == true {
|
if bytes.Equal(pubkey1, pubkey2) == true {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,14 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/whisper"
|
"github.com/ethereum/go-ethereum/whisper"
|
||||||
)
|
)
|
||||||
|
|
||||||
var logger = ethlogger.NewLogger("SERV")
|
var (
|
||||||
var jsonlogger = ethlogger.NewJsonLogger()
|
logger = ethlogger.NewLogger("SERV")
|
||||||
|
jsonlogger = ethlogger.NewJsonLogger()
|
||||||
|
|
||||||
|
defaultBootNodes = []*discover.Node{
|
||||||
|
discover.MustParseNode("enode://6cdd090303f394a1cac34ecc9f7cda18127eafa2a3a06de39f6d920b0e583e062a7362097c7c65ee490a758b442acd5c80c6fce4b148c6a391e946b45131365b@54.169.166.226:30303"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Name string
|
Name string
|
||||||
|
|
@ -53,6 +59,9 @@ type Config struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *Config) parseBootNodes() []*discover.Node {
|
func (cfg *Config) parseBootNodes() []*discover.Node {
|
||||||
|
if cfg.BootNodes == "" {
|
||||||
|
return defaultBootNodes
|
||||||
|
}
|
||||||
var ns []*discover.Node
|
var ns []*discover.Node
|
||||||
for _, url := range strings.Split(cfg.BootNodes, " ") {
|
for _, url := range strings.Split(cfg.BootNodes, " ") {
|
||||||
if url == "" {
|
if url == "" {
|
||||||
|
|
|
||||||
|
|
@ -17,32 +17,35 @@ type CpuMiner struct {
|
||||||
|
|
||||||
func NewCpuMiner(index int, pow pow.PoW) *CpuMiner {
|
func NewCpuMiner(index int, pow pow.PoW) *CpuMiner {
|
||||||
miner := &CpuMiner{
|
miner := &CpuMiner{
|
||||||
c: make(chan *types.Block, 1),
|
|
||||||
quit: make(chan struct{}),
|
|
||||||
quitCurrentOp: make(chan struct{}, 1),
|
|
||||||
pow: pow,
|
pow: pow,
|
||||||
index: index,
|
index: index,
|
||||||
}
|
}
|
||||||
go miner.update()
|
|
||||||
|
|
||||||
return miner
|
return miner
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CpuMiner) Work() chan<- *types.Block { return self.c }
|
func (self *CpuMiner) Work() chan<- *types.Block { return self.c }
|
||||||
func (self *CpuMiner) Pow() pow.PoW { return self.pow }
|
func (self *CpuMiner) Pow() pow.PoW { return self.pow }
|
||||||
func (self *CpuMiner) SetNonceCh(ch chan<- Work) { self.returnCh = ch }
|
func (self *CpuMiner) SetWorkCh(ch chan<- Work) { self.returnCh = ch }
|
||||||
|
|
||||||
func (self *CpuMiner) Stop() {
|
func (self *CpuMiner) Stop() {
|
||||||
close(self.quit)
|
close(self.quit)
|
||||||
close(self.quitCurrentOp)
|
close(self.quitCurrentOp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *CpuMiner) Start() {
|
||||||
|
self.quit = make(chan struct{})
|
||||||
|
self.quitCurrentOp = make(chan struct{}, 1)
|
||||||
|
self.c = make(chan *types.Block, 1)
|
||||||
|
|
||||||
|
go self.update()
|
||||||
|
}
|
||||||
|
|
||||||
func (self *CpuMiner) update() {
|
func (self *CpuMiner) update() {
|
||||||
out:
|
out:
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case block := <-self.c:
|
case block := <-self.c:
|
||||||
// make sure it's open
|
|
||||||
self.quitCurrentOp <- struct{}{}
|
self.quitCurrentOp <- struct{}{}
|
||||||
|
|
||||||
go self.mine(block)
|
go self.mine(block)
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,9 @@ type Work struct {
|
||||||
|
|
||||||
type Agent interface {
|
type Agent interface {
|
||||||
Work() chan<- *types.Block
|
Work() chan<- *types.Block
|
||||||
SetNonceCh(chan<- Work)
|
SetWorkCh(chan<- Work)
|
||||||
Stop()
|
Stop()
|
||||||
|
Start()
|
||||||
Pow() pow.PoW
|
Pow() pow.PoW
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,6 +87,11 @@ func (self *worker) start() {
|
||||||
|
|
||||||
self.quit = make(chan struct{})
|
self.quit = make(chan struct{})
|
||||||
|
|
||||||
|
// spin up agents
|
||||||
|
for _, agent := range self.agents {
|
||||||
|
agent.Start()
|
||||||
|
}
|
||||||
|
|
||||||
go self.update()
|
go self.update()
|
||||||
go self.wait()
|
go self.wait()
|
||||||
}
|
}
|
||||||
|
|
@ -98,7 +104,7 @@ func (self *worker) stop() {
|
||||||
|
|
||||||
func (self *worker) register(agent Agent) {
|
func (self *worker) register(agent Agent) {
|
||||||
self.agents = append(self.agents, agent)
|
self.agents = append(self.agents, agent)
|
||||||
agent.SetNonceCh(self.recv)
|
agent.SetWorkCh(self.recv)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *worker) update() {
|
func (self *worker) update() {
|
||||||
|
|
@ -108,15 +114,17 @@ out:
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case event := <-events.Chan():
|
case event := <-events.Chan():
|
||||||
switch event := event.(type) {
|
switch event.(type) {
|
||||||
case core.ChainEvent:
|
case core.ChainEvent:
|
||||||
self.commitNewWork()
|
self.commitNewWork()
|
||||||
case core.TxPreEvent:
|
case core.TxPreEvent:
|
||||||
if err := self.commitTransaction(event.Tx); err != nil {
|
self.commitNewWork()
|
||||||
self.push()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case <-self.quit:
|
case <-self.quit:
|
||||||
|
// stop all agents
|
||||||
|
for _, agent := range self.agents {
|
||||||
|
agent.Stop()
|
||||||
|
}
|
||||||
break out
|
break out
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -131,8 +139,11 @@ func (self *worker) wait() {
|
||||||
if block.Number().Uint64() == work.Number && block.Nonce() == nil {
|
if block.Number().Uint64() == work.Number && block.Nonce() == nil {
|
||||||
self.current.block.Header().Nonce = work.Nonce
|
self.current.block.Header().Nonce = work.Nonce
|
||||||
|
|
||||||
self.chain.InsertChain(types.Blocks{self.current.block})
|
if err := self.chain.InsertChain(types.Blocks{self.current.block}); err == nil {
|
||||||
self.mux.Post(core.NewMinedBlockEvent{self.current.block})
|
self.mux.Post(core.NewMinedBlockEvent{self.current.block})
|
||||||
|
} else {
|
||||||
|
self.commitNewWork()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
@ -141,9 +152,10 @@ func (self *worker) wait() {
|
||||||
|
|
||||||
func (self *worker) push() {
|
func (self *worker) push() {
|
||||||
if self.mining {
|
if self.mining {
|
||||||
self.current.state.Update(ethutil.Big0)
|
self.current.block.Header().GasUsed = self.current.totalUsedGas
|
||||||
self.current.block.SetRoot(self.current.state.Root())
|
self.current.block.SetRoot(self.current.state.Root())
|
||||||
|
|
||||||
|
// push new work to agents
|
||||||
for _, agent := range self.agents {
|
for _, agent := range self.agents {
|
||||||
agent.Work() <- self.current.block
|
agent.Work() <- self.current.block
|
||||||
}
|
}
|
||||||
|
|
@ -169,14 +181,18 @@ func (self *worker) commitNewWork() {
|
||||||
// Break on gas limit
|
// Break on gas limit
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
minerlogger.Infoln(err)
|
|
||||||
remove = append(remove, tx)
|
remove = append(remove, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
minerlogger.Infoln(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.eth.TxPool().RemoveSet(remove)
|
self.eth.TxPool().RemoveSet(remove)
|
||||||
|
|
||||||
self.current.coinbase.AddAmount(core.BlockReward)
|
self.current.coinbase.AddAmount(core.BlockReward)
|
||||||
|
|
||||||
|
self.current.state.Update(ethutil.Big0)
|
||||||
self.push()
|
self.push()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -213,7 +229,9 @@ func (self *worker) commitTransaction(tx *types.Transaction) error {
|
||||||
snapshot := self.current.state.Copy()
|
snapshot := self.current.state.Copy()
|
||||||
receipt, txGas, err := self.proc.ApplyTransaction(self.current.coinbase, self.current.state, self.current.block, tx, self.current.totalUsedGas, true)
|
receipt, txGas, err := self.proc.ApplyTransaction(self.current.coinbase, self.current.state, self.current.block, tx, self.current.totalUsedGas, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if core.IsNonceErr(err) || core.IsGasLimitErr(err) {
|
||||||
self.current.state.Set(snapshot)
|
self.current.state.Set(snapshot)
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,10 @@ import (
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto/ecies"
|
||||||
"github.com/ethereum/go-ethereum/crypto/secp256k1"
|
"github.com/ethereum/go-ethereum/crypto/secp256k1"
|
||||||
ethlogger "github.com/ethereum/go-ethereum/logger"
|
ethlogger "github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
"github.com/obscuren/ecies"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var clogger = ethlogger.NewLogger("CRYPTOID")
|
var clogger = ethlogger.NewLogger("CRYPTOID")
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
package p2p
|
package p2p
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
// Protocol represents a P2P subprotocol implementation.
|
// Protocol represents a P2P subprotocol implementation.
|
||||||
type Protocol struct {
|
type Protocol struct {
|
||||||
// Name should contain the official protocol name,
|
// Name should contain the official protocol name,
|
||||||
|
|
@ -37,6 +39,10 @@ func (cap Cap) RlpData() interface{} {
|
||||||
return []interface{}{cap.Name, cap.Version}
|
return []interface{}{cap.Name, cap.Version}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cap Cap) String() string {
|
||||||
|
return fmt.Sprintf("%s/%d", cap.Name, cap.Version)
|
||||||
|
}
|
||||||
|
|
||||||
type capsByName []Cap
|
type capsByName []Cap
|
||||||
|
|
||||||
func (cs capsByName) Len() int { return len(cs) }
|
func (cs capsByName) Len() int { return len(cs) }
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ type EasyPow struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *EasyPow {
|
func New() *EasyPow {
|
||||||
return &EasyPow{turbo: true}
|
return &EasyPow{turbo: false}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pow *EasyPow) GetHashrate() int64 {
|
func (pow *EasyPow) GetHashrate() int64 {
|
||||||
|
|
@ -42,6 +42,8 @@ func (pow *EasyPow) Search(block pow.Block, stop <-chan struct{}) []byte {
|
||||||
starti := i
|
starti := i
|
||||||
start := time.Now().UnixNano()
|
start := time.Now().UnixNano()
|
||||||
|
|
||||||
|
defer func() { pow.HashRate = 0 }()
|
||||||
|
|
||||||
// Make sure stop is empty
|
// Make sure stop is empty
|
||||||
empty:
|
empty:
|
||||||
for {
|
for {
|
||||||
|
|
@ -55,7 +57,6 @@ empty:
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-stop:
|
case <-stop:
|
||||||
pow.HashRate = 0
|
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
i++
|
i++
|
||||||
|
|
|
||||||
|
|
@ -262,6 +262,7 @@ func TestStateLog(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStateTransaction(t *testing.T) {
|
func TestStateTransaction(t *testing.T) {
|
||||||
|
t.Skip()
|
||||||
const fn = "../files/StateTests/stTransactionTest.json"
|
const fn = "../files/StateTests/stTransactionTest.json"
|
||||||
RunVmTest(fn, t)
|
RunVmTest(fn, t)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,9 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto/ecies"
|
||||||
"github.com/ethereum/go-ethereum/ethutil"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/obscuren/ecies"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,10 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto/ecies"
|
||||||
"github.com/ethereum/go-ethereum/event/filter"
|
"github.com/ethereum/go-ethereum/event/filter"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/obscuren/ecies"
|
|
||||||
"gopkg.in/fatih/set.v0"
|
"gopkg.in/fatih/set.v0"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue