mirror of
https://github.com/eiblog/eiblog.git
synced 2026-03-01 00:34:58 +08:00
update vendor and use single static
This commit is contained in:
1 parent
da7b726e8d
commit
3ff5977941
312 files changed
+70988
-77
No files matched your search
+2
-6
@@ -1,18 +1,14 @@
|
|||||||
.git
|
.git
|
||||||
|
conf
|
||||||
vendor
|
vendor
|
||||||
setting
|
setting
|
||||||
conf
|
|
||||||
static
|
|
||||||
views
|
|
||||||
docs
|
docs
|
||||||
!static/tzdata
|
**/.DS_Store
|
||||||
Dockerfile
|
Dockerfile
|
||||||
glide.yaml
|
glide.yaml
|
||||||
glide.lock
|
glide.lock
|
||||||
*.yml
|
*.yml
|
||||||
*.md
|
|
||||||
*.go
|
*.go
|
||||||
*.sh
|
*.sh
|
||||||
*.DS_Store
|
|
||||||
.gitignore
|
.gitignore
|
||||||
.dockerignore
|
.dockerignore
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
*.DS_Store
|
**/.DS_Store
|
||||||
*.exe
|
*.exe
|
||||||
conf/ssl/domain.*
|
conf/ssl/domain.*
|
||||||
eiblog
|
eiblog
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ func HandlePost(c *gin.Context) {
|
|||||||
h["Title"] = "撰写文章 | " + Ei.BTitle
|
h["Title"] = "撰写文章 | " + Ei.BTitle
|
||||||
}
|
}
|
||||||
h["Path"] = c.Request.URL.Path
|
h["Path"] = c.Request.URL.Path
|
||||||
h["Domain"] = setting.Conf.Mode.Domains[0]
|
h["Domain"] = setting.Conf.Mode.Domain
|
||||||
h["Series"] = Ei.Series
|
h["Series"] = Ei.Series
|
||||||
var tags []T
|
var tags []T
|
||||||
for tag, _ := range Ei.Tags {
|
for tag, _ := range Ei.Tags {
|
||||||
|
|||||||
+28
-16
@@ -2,28 +2,40 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCheckEmail(t *testing.T) {
|
func TestCheckEmail(t *testing.T) {
|
||||||
e := "xx@email.com"
|
emails := []string{
|
||||||
e1 := "xxxxemail.com"
|
"xx@email.com",
|
||||||
e2 := "xxx#email.com"
|
"xxxxemail.com",
|
||||||
|
"xxx#email.com",
|
||||||
|
}
|
||||||
|
|
||||||
t.Log(CheckEmail(e))
|
for i, v := range emails {
|
||||||
t.Log(CheckEmail(e1))
|
if i == 0 {
|
||||||
t.Log(CheckEmail(e2))
|
assert.True(t, CheckEmail(v))
|
||||||
|
} else {
|
||||||
|
assert.False(t, CheckEmail(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCheckDomain(t *testing.T) {
|
func TestCheckDomain(t *testing.T) {
|
||||||
d := "123.com"
|
domains := []string{
|
||||||
d1 := "http://123.com"
|
"123.com",
|
||||||
d2 := "https://123.com"
|
"http://123.com",
|
||||||
d3 := "123#.com"
|
"https://123.com",
|
||||||
d4 := "123.coooom"
|
"123#.com",
|
||||||
|
"123.coooom",
|
||||||
|
}
|
||||||
|
|
||||||
t.Log(CheckDomain(d))
|
for i, v := range domains {
|
||||||
t.Log(CheckDomain(d1))
|
if i > 2 {
|
||||||
t.Log(CheckDomain(d2))
|
assert.False(t, CheckDomain(v))
|
||||||
t.Log(CheckDomain(d3))
|
} else {
|
||||||
t.Log(CheckDomain(d4))
|
assert.True(t, CheckDomain(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+2
-3
@@ -62,12 +62,11 @@ mode:
|
|||||||
httpport: 9000
|
httpport: 9000
|
||||||
# https server
|
# https server
|
||||||
enablehttps: true
|
enablehttps: true
|
||||||
autocert: true
|
autocert: false
|
||||||
httpsport: 9001
|
httpsport: 9001
|
||||||
certfile: conf/ssl/domain.rsa.pem
|
certfile: conf/ssl/domain.rsa.pem
|
||||||
keyfile: conf/ssl/domain.rsa.key
|
keyfile: conf/ssl/domain.rsa.key
|
||||||
domains:
|
domain: deepzz.com
|
||||||
- deepzz.com
|
|
||||||
# twitter地址: twitter.com/chenqijing2
|
# twitter地址: twitter.com/chenqijing2
|
||||||
twitter:
|
twitter:
|
||||||
card: summary
|
card: summary
|
||||||
|
|||||||
+4
-8
@@ -16,14 +16,10 @@ func TestPostComment(t *testing.T) {
|
|||||||
AuthorName: "deepzz",
|
AuthorName: "deepzz",
|
||||||
}
|
}
|
||||||
|
|
||||||
id := PostComment(pc)
|
id, err := PostComment(pc)
|
||||||
if id == "" {
|
if err != nil {
|
||||||
t.Error("post failed")
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.Log("post success")
|
t.Log("post success", id)
|
||||||
}
|
|
||||||
|
|
||||||
func TestApprovePost(t *testing.T) {
|
|
||||||
ApprovePost()
|
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,6 @@ services:
|
|||||||
- /data/eiblog/logdata:/eiblog/logdata
|
- /data/eiblog/logdata:/eiblog/logdata
|
||||||
- /data/eiblog/conf:/eiblog/conf
|
- /data/eiblog/conf:/eiblog/conf
|
||||||
- /data/eiblog/static:/eiblog/static
|
- /data/eiblog/static:/eiblog/static
|
||||||
- /data/eiblog/views:/eiblog/views
|
|
||||||
links:
|
links:
|
||||||
- elasticsearch
|
- elasticsearch
|
||||||
- mongodb
|
- mongodb
|
||||||
|
|||||||
@@ -20,13 +20,47 @@ import (
|
|||||||
func Filter() gin.HandlerFunc {
|
func Filter() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
// 过滤黑名单
|
// 过滤黑名单
|
||||||
BlackFilter(c)
|
if BlackFilter(c) {
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 重定向
|
||||||
|
if Redirect(c) {
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
// 用户cookie,用于统计
|
// 用户cookie,用于统计
|
||||||
UserCookie(c)
|
UserCookie(c)
|
||||||
c.Next()
|
c.Next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 黑名单过滤
|
||||||
|
func BlackFilter(c *gin.Context) bool {
|
||||||
|
ip := c.ClientIP()
|
||||||
|
if setting.BlackIP[ip] {
|
||||||
|
c.String(http.StatusForbidden, "Your IP is blacklisted.")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重定向
|
||||||
|
func Redirect(c *gin.Context) bool {
|
||||||
|
fmt.Println("scheme=======", c.Request.URL.Scheme)
|
||||||
|
if setting.Conf.Mode.EnableHttps && c.Request.ProtoMajor == 1 {
|
||||||
|
var port string
|
||||||
|
if strings.Contains(c.Request.Host, ":") {
|
||||||
|
port = fmt.Sprintf(":%d", setting.Conf.Mode.HttpsPort)
|
||||||
|
}
|
||||||
|
c.Redirect(http.StatusMovedPermanently, "https://"+setting.Conf.Mode.Domain+port+c.Request.RequestURI)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// 用户识别
|
// 用户识别
|
||||||
func UserCookie(c *gin.Context) {
|
func UserCookie(c *gin.Context) {
|
||||||
cookie, err := c.Cookie("u")
|
cookie, err := c.Cookie("u")
|
||||||
@@ -35,15 +69,6 @@ func UserCookie(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 黑名单过滤
|
|
||||||
func BlackFilter(c *gin.Context) {
|
|
||||||
ip := c.ClientIP()
|
|
||||||
if setting.BlackIP[ip] {
|
|
||||||
c.Abort()
|
|
||||||
c.String(http.StatusForbidden, "Your IP is blacklisted.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析静态文件版本
|
// 解析静态文件版本
|
||||||
func StaticVersion(c *gin.Context) (version int) {
|
func StaticVersion(c *gin.Context) (version int) {
|
||||||
cookie, err := c.Request.Cookie("v")
|
cookie, err := c.Request.Cookie("v")
|
||||||
@@ -61,7 +86,7 @@ func GetBase() gin.H {
|
|||||||
"CopyYear": time.Now().Year(),
|
"CopyYear": time.Now().Year(),
|
||||||
"BTitle": Ei.BTitle,
|
"BTitle": Ei.BTitle,
|
||||||
"BeiAn": Ei.BeiAn,
|
"BeiAn": Ei.BeiAn,
|
||||||
"Domain": setting.Conf.Mode.Domains[0],
|
"Domain": setting.Conf.Mode.Domain,
|
||||||
"Kodo": setting.Conf.Kodo,
|
"Kodo": setting.Conf.Kodo,
|
||||||
"ShortName": setting.Conf.Disqus.ShortName,
|
"ShortName": setting.Conf.Disqus.ShortName,
|
||||||
}
|
}
|
||||||
@@ -232,6 +257,14 @@ func HandleSitemap(c *gin.Context) {
|
|||||||
http.ServeFile(c.Writer, c.Request, "static/sitemap.xml")
|
http.ServeFile(c.Writer, c.Request, "static/sitemap.xml")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func HandleCrossDomain(c *gin.Context) {
|
||||||
|
http.ServeFile(c.Writer, c.Request, "static/crossdomain.xml")
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleFavicon(c *gin.Context) {
|
||||||
|
http.ServeFile(c.Writer, c.Request, "static/favicon.ico")
|
||||||
|
}
|
||||||
|
|
||||||
// 服务端推送谷歌统计
|
// 服务端推送谷歌统计
|
||||||
func HandleBeacon(c *gin.Context) {
|
func HandleBeacon(c *gin.Context) {
|
||||||
ua := c.Request.UserAgent()
|
ua := c.Request.UserAgent()
|
||||||
|
|||||||
Generated
+9
-2
@@ -1,5 +1,5 @@
|
|||||||
hash: bd360fa297ed66950543990f9433cdcdf13c29dd99d9a01b49027e236b2cb9da
|
hash: bd360fa297ed66950543990f9433cdcdf13c29dd99d9a01b49027e236b2cb9da
|
||||||
updated: 2017-07-08T14:52:31.866715976+08:00
|
updated: 2017-07-08T19:25:55.246137015+08:00
|
||||||
imports:
|
imports:
|
||||||
- name: github.com/boj/redistore
|
- name: github.com/boj/redistore
|
||||||
version: 4562487a4bee9a7c272b72bfaeda4917d0a47ab9
|
version: 4562487a4bee9a7c272b72bfaeda4917d0a47ab9
|
||||||
@@ -15,10 +15,12 @@ imports:
|
|||||||
- tmpl
|
- tmpl
|
||||||
- uuid
|
- uuid
|
||||||
- name: github.com/garyburd/redigo
|
- name: github.com/garyburd/redigo
|
||||||
version: 57f1cd7de6175c96b423e7ac2534ff2b39e2ef79
|
version: 9f3a0116c9f72c5a56f958206a43dc881b502c37
|
||||||
subpackages:
|
subpackages:
|
||||||
- internal
|
- internal
|
||||||
- redis
|
- redis
|
||||||
|
- name: github.com/gin-gonic/autotls
|
||||||
|
version: 9261e1c52a0eb595c531ff77c06cdfb6fdb111a4
|
||||||
- name: github.com/gin-gonic/contrib
|
- name: github.com/gin-gonic/contrib
|
||||||
version: d4fc5a96cc0d29cb0e862bb1312dd6f4fedfcaee
|
version: d4fc5a96cc0d29cb0e862bb1312dd6f4fedfcaee
|
||||||
subpackages:
|
subpackages:
|
||||||
@@ -44,6 +46,11 @@ imports:
|
|||||||
version: fc9e8d8ef48496124e79ae0df75490096eccf6fe
|
version: fc9e8d8ef48496124e79ae0df75490096eccf6fe
|
||||||
- name: github.com/shurcooL/sanitized_anchor_name
|
- name: github.com/shurcooL/sanitized_anchor_name
|
||||||
version: 541ff5ee47f1dddf6a5281af78307d921524bcb5
|
version: 541ff5ee47f1dddf6a5281af78307d921524bcb5
|
||||||
|
- name: golang.org/x/crypto
|
||||||
|
version: a48ac81e47fd6f9ed1258f3b60ae9e75f93cb7ed
|
||||||
|
subpackages:
|
||||||
|
- acme
|
||||||
|
- acme/autocert
|
||||||
- name: golang.org/x/net
|
- name: golang.org/x/net
|
||||||
version: f315505cf3349909cdf013ea56690da34e96a451
|
version: f315505cf3349909cdf013ea56690da34e96a451
|
||||||
subpackages:
|
subpackages:
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
// Package main provides ...
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestReadDir(t *testing.T) {
|
||||||
|
files := ReadDir("setting", func(name string) bool { return false })
|
||||||
|
assert.Len(t, files, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIgnoreHtmlTag(t *testing.T) {
|
||||||
|
testStr := []string{
|
||||||
|
"<script>hello</script>",
|
||||||
|
}
|
||||||
|
|
||||||
|
expectStr := []string{
|
||||||
|
"hello",
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, v := range testStr {
|
||||||
|
assert.Equal(t, expectStr[i], IgnoreHtmlTag(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPickFirstImage(t *testing.T) {
|
||||||
|
testStr := []string{
|
||||||
|
`<img width="480" height="310" alt="acme_aliyun_1" src="https://st.deepzz.com/blog/img/acme_aliyun_1.jpg">`,
|
||||||
|
`<img width="480" height="310" alt="acme_aliyun_1" data-src="https://st.deepzz.com/acme_aliyun_1.jpg"><img width="480" height="310" alt="acme_aliyun_1" src="https://st.deepzz.com/blog/img/acme_aliyun_1.jpg">`,
|
||||||
|
}
|
||||||
|
|
||||||
|
expectStr := []string{
|
||||||
|
"",
|
||||||
|
"https://st.deepzz.com/acme_aliyun_1.jpg",
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, v := range testStr {
|
||||||
|
assert.Equal(t, expectStr[i], PickFirstImage(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCovertStr(t *testing.T) {
|
||||||
|
testStr := []string{
|
||||||
|
time.Now().Format("2006-01-02T15:04:05"),
|
||||||
|
}
|
||||||
|
|
||||||
|
expectStr := []string{
|
||||||
|
JUST_NOW,
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, v := range testStr {
|
||||||
|
assert.Equal(t, expectStr[i], ConvertStr(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,7 +25,7 @@ func (*superfeedr) PingFunc(slug string) {
|
|||||||
}
|
}
|
||||||
vals := url.Values{}
|
vals := url.Values{}
|
||||||
vals.Set("hub.mode", "publish")
|
vals.Set("hub.mode", "publish")
|
||||||
vals.Add("hub.url", "https://"+setting.Conf.Mode.Domains[0]+"/post/"+slug+".html")
|
vals.Add("hub.url", "https://"+setting.Conf.Mode.Domain+"/post/"+slug+".html")
|
||||||
res, err := http.PostForm(setting.Conf.FeedrURL, vals)
|
res, err := http.PostForm(setting.Conf.FeedrURL, vals)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logd.Error(err)
|
logd.Error(err)
|
||||||
@@ -64,7 +64,7 @@ func (p *pingRPC) PingFunc(slug string) {
|
|||||||
if len(setting.Conf.PingRPCs) == 0 {
|
if len(setting.Conf.PingRPCs) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.Params.Param[1].Value = "https://" + setting.Conf.Mode.Domains[0] + "/post/" + slug + ".html"
|
p.Params.Param[1].Value = "https://" + setting.Conf.Mode.Domain + "/post/" + slug + ".html"
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
buf.WriteString(xml.Header)
|
buf.WriteString(xml.Header)
|
||||||
enc := xml.NewEncoder(buf)
|
enc := xml.NewEncoder(buf)
|
||||||
@@ -98,9 +98,9 @@ func init() {
|
|||||||
pr := &pingRPC{MethodName: "weblogUpdates.extendedPing"}
|
pr := &pingRPC{MethodName: "weblogUpdates.extendedPing"}
|
||||||
pr.Params.Param = [4]rpcValue{
|
pr.Params.Param = [4]rpcValue{
|
||||||
0: rpcValue{Value: Ei.BTitle},
|
0: rpcValue{Value: Ei.BTitle},
|
||||||
1: rpcValue{Value: "https://" + setting.Conf.Mode.Domains[0]},
|
1: rpcValue{Value: "https://" + setting.Conf.Mode.Domain},
|
||||||
2: rpcValue{},
|
2: rpcValue{},
|
||||||
3: rpcValue{Value: "https://" + setting.Conf.Mode.Domains[0] + "/rss.html"},
|
3: rpcValue{Value: "https://" + setting.Conf.Mode.Domain + "/rss.html"},
|
||||||
}
|
}
|
||||||
Pings = append(Pings, pr)
|
Pings = append(Pings, pr)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,8 @@ func init() {
|
|||||||
router.GET("/opensearch.xml", HandleOpenSearch)
|
router.GET("/opensearch.xml", HandleOpenSearch)
|
||||||
router.GET("/sitemap.xml", HandleSitemap)
|
router.GET("/sitemap.xml", HandleSitemap)
|
||||||
router.GET("/robots.txt", HandleRobots)
|
router.GET("/robots.txt", HandleRobots)
|
||||||
|
router.GET("/crossdomain.xml", HandleCrossDomain)
|
||||||
|
router.GET("/favicon.ico", HandleFavicon)
|
||||||
// 后台相关
|
// 后台相关
|
||||||
admin := router.Group("/admin")
|
admin := router.Group("/admin")
|
||||||
admin.GET("/login", HandleLogin)
|
admin.GET("/login", HandleLogin)
|
||||||
@@ -95,10 +97,10 @@ func Run() {
|
|||||||
)
|
)
|
||||||
if setting.Conf.Mode.EnableHttp {
|
if setting.Conf.Mode.EnableHttp {
|
||||||
go func() {
|
go func() {
|
||||||
logd.Infof("http server Running on %d\n", setting.Conf.Mode.HttpPort)
|
logd.Printf("http server Running on %d\n", setting.Conf.Mode.HttpPort)
|
||||||
err = router.Run(fmt.Sprintf(":%d", setting.Conf.Mode.HttpPort))
|
err = router.Run(fmt.Sprintf(":%d", setting.Conf.Mode.HttpPort))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logd.Info("ListenAndServe: ", err)
|
logd.Error("ListenAndServe: ", err)
|
||||||
time.Sleep(100 * time.Microsecond)
|
time.Sleep(100 * time.Microsecond)
|
||||||
endRunning <- true
|
endRunning <- true
|
||||||
}
|
}
|
||||||
@@ -107,20 +109,21 @@ func Run() {
|
|||||||
if setting.Conf.Mode.EnableHttps {
|
if setting.Conf.Mode.EnableHttps {
|
||||||
if setting.Conf.Mode.AutoCert {
|
if setting.Conf.Mode.AutoCert {
|
||||||
go func() {
|
go func() {
|
||||||
logd.Info("https server Running on 443")
|
logd.Print("https server Running on 443")
|
||||||
err = autotls.Run(router, setting.Conf.Mode.Domains...)
|
err = autotls.Run(router, setting.Conf.Mode.Domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logd.Info("ListenAndServe: ", err)
|
logd.Error("ListenAndServe: ", err)
|
||||||
time.Sleep(100 * time.Microsecond)
|
time.Sleep(100 * time.Microsecond)
|
||||||
endRunning <- true
|
endRunning <- true
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
} else {
|
} else {
|
||||||
go func() {
|
go func() {
|
||||||
logd.Infof("https server Running on %d\n", setting.Conf.Mode.HttpsPort)
|
logd.Printf("https server Running on %d\n", setting.Conf.Mode.HttpsPort)
|
||||||
err = router.RunTLS(fmt.Sprintf(":%d", setting.Conf.Mode.HttpsPort), setting.Conf.Mode.CertFile, setting.Conf.Mode.KeyFile)
|
err = router.RunTLS(fmt.Sprintf(":%d", setting.Conf.Mode.HttpsPort),
|
||||||
|
setting.Conf.Mode.CertFile, setting.Conf.Mode.KeyFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logd.Info("ListenAndServe: ", err)
|
logd.Error("ListenAndServe: ", err)
|
||||||
time.Sleep(100 * time.Microsecond)
|
time.Sleep(100 * time.Microsecond)
|
||||||
endRunning <- true
|
endRunning <- true
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -64,7 +64,7 @@ type Config struct {
|
|||||||
CertFile string
|
CertFile string
|
||||||
KeyFile string
|
KeyFile string
|
||||||
AutoCert bool
|
AutoCert bool
|
||||||
Domains []string
|
Domain string
|
||||||
}
|
}
|
||||||
Twitter struct { // twitter信息
|
Twitter struct { // twitter信息
|
||||||
Card string
|
Card string
|
||||||
|
|||||||
@@ -2,15 +2,11 @@
|
|||||||
package setting
|
package setting
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestInit(t *testing.T) {
|
func TestInit(t *testing.T) {
|
||||||
data, err := json.Marshal(Conf)
|
assert.NotNil(t, Conf)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Log(string(data))
|
|
||||||
}
|
}
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB |
+3
-3
@@ -333,7 +333,7 @@ func StringMap(result interface{}, err error) (map[string]string, error) {
|
|||||||
key, okKey := values[i].([]byte)
|
key, okKey := values[i].([]byte)
|
||||||
value, okValue := values[i+1].([]byte)
|
value, okValue := values[i+1].([]byte)
|
||||||
if !okKey || !okValue {
|
if !okKey || !okValue {
|
||||||
return nil, errors.New("redigo: ScanMap key not a bulk string value")
|
return nil, errors.New("redigo: StringMap key not a bulk string value")
|
||||||
}
|
}
|
||||||
m[string(key)] = string(value)
|
m[string(key)] = string(value)
|
||||||
}
|
}
|
||||||
@@ -355,7 +355,7 @@ func IntMap(result interface{}, err error) (map[string]int, error) {
|
|||||||
for i := 0; i < len(values); i += 2 {
|
for i := 0; i < len(values); i += 2 {
|
||||||
key, ok := values[i].([]byte)
|
key, ok := values[i].([]byte)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("redigo: ScanMap key not a bulk string value")
|
return nil, errors.New("redigo: IntMap key not a bulk string value")
|
||||||
}
|
}
|
||||||
value, err := Int(values[i+1], nil)
|
value, err := Int(values[i+1], nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -381,7 +381,7 @@ func Int64Map(result interface{}, err error) (map[string]int64, error) {
|
|||||||
for i := 0; i < len(values); i += 2 {
|
for i := 0; i < len(values); i += 2 {
|
||||||
key, ok := values[i].([]byte)
|
key, ok := values[i].([]byte)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("redigo: ScanMap key not a bulk string value")
|
return nil, errors.New("redigo: Int64Map key not a bulk string value")
|
||||||
}
|
}
|
||||||
value, err := Int64(values[i+1], nil)
|
value, err := Int64(values[i+1], nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
# unifying the coding style for different editors and IDEs => editorconfig.org
|
||||||
|
|
||||||
|
; indicate this is the root of the project
|
||||||
|
root = true
|
||||||
|
|
||||||
|
###########################################################
|
||||||
|
; common
|
||||||
|
###########################################################
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
|
||||||
|
end_of_line = LF
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
###########################################################
|
||||||
|
; make
|
||||||
|
###########################################################
|
||||||
|
|
||||||
|
[Makefile]
|
||||||
|
indent_style = tab
|
||||||
|
|
||||||
|
[makefile]
|
||||||
|
indent_style = tab
|
||||||
|
|
||||||
|
###########################################################
|
||||||
|
; markdown
|
||||||
|
###########################################################
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
###########################################################
|
||||||
|
; golang
|
||||||
|
###########################################################
|
||||||
|
|
||||||
|
[*.go]
|
||||||
|
indent_style = tab
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
# 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
|
||||||
|
*.test
|
||||||
|
*.prof
|
||||||
|
|
||||||
|
vendor/*
|
||||||
|
!vendor/vendor.json
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
language: go
|
||||||
|
sudo: false
|
||||||
|
go:
|
||||||
|
- 1.6.x
|
||||||
|
- 1.7.x
|
||||||
|
- 1.8.x
|
||||||
|
- master
|
||||||
|
|
||||||
|
git:
|
||||||
|
depth: 3
|
||||||
|
|
||||||
|
install:
|
||||||
|
- go get -v github.com/kardianos/govendor
|
||||||
|
- govendor sync
|
||||||
|
- go get -u github.com/campoy/embedmd
|
||||||
|
|
||||||
|
script:
|
||||||
|
- embedmd -d README.md
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
webhooks:
|
||||||
|
urls:
|
||||||
|
- https://webhooks.gitter.im/e/7f95bf605c4d356372f4
|
||||||
|
on_success: change # options: [always|never|change] default: always
|
||||||
|
on_failure: always # options: [always|never|change] default: always
|
||||||
|
on_start: false # default: false
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2017 Gin-Gonic
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
+67
@@ -0,0 +1,67 @@
|
|||||||
|
# autotls
|
||||||
|
|
||||||
|
[](https://travis-ci.org/gin-gonic/autotls)
|
||||||
|
[](https://goreportcard.com/report/github.com/gin-gonic/autotls)
|
||||||
|
[](https://godoc.org/github.com/gin-gonic/autotls)
|
||||||
|
[](https://gitter.im/gin-gonic/autotls?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
|
|
||||||
|
Support Let's Encrypt for a Go server application.
|
||||||
|
|
||||||
|
## example
|
||||||
|
|
||||||
|
example for 1-line LetsEncrypt HTTPS servers.
|
||||||
|
|
||||||
|
[embedmd]:# (example/example1.go go)
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/autotls"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
r := gin.Default()
|
||||||
|
|
||||||
|
// Ping handler
|
||||||
|
r.GET("/ping", func(c *gin.Context) {
|
||||||
|
c.String(200, "pong")
|
||||||
|
})
|
||||||
|
|
||||||
|
log.Fatal(autotls.Run(r, "example1.com", "example2.com"))
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
example for custom autocert manager.
|
||||||
|
|
||||||
|
[embedmd]:# (example/example2.go go)
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/autotls"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"golang.org/x/crypto/acme/autocert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
r := gin.Default()
|
||||||
|
|
||||||
|
// Ping handler
|
||||||
|
r.GET("/ping", func(c *gin.Context) {
|
||||||
|
c.String(200, "pong")
|
||||||
|
})
|
||||||
|
|
||||||
|
m := autocert.Manager{
|
||||||
|
Prompt: autocert.AcceptTOS,
|
||||||
|
HostPolicy: autocert.HostWhitelist("example1.com", "example2.com"),
|
||||||
|
Cache: autocert.DirCache("/var/www/.cache"),
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Fatal(autotls.RunWithManager(r, &m))
|
||||||
|
}
|
||||||
|
```
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
package autotls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/acme/autocert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Run support 1-line LetsEncrypt HTTPS servers
|
||||||
|
func Run(r http.Handler, domain ...string) error {
|
||||||
|
return http.Serve(autocert.NewListener(domain...), r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunWithManager support custom autocert manager
|
||||||
|
func RunWithManager(r http.Handler, m *autocert.Manager) error {
|
||||||
|
s := &http.Server{
|
||||||
|
Addr: ":https",
|
||||||
|
TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},
|
||||||
|
Handler: r,
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.ListenAndServeTLS("", "")
|
||||||
|
}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
// Support Let's Encrypt for a Go server application.
|
||||||
|
//
|
||||||
|
// package main
|
||||||
|
//
|
||||||
|
// import (
|
||||||
|
// "log"
|
||||||
|
//
|
||||||
|
// "github.com/gin-gonic/autotls"
|
||||||
|
// "github.com/gin-gonic/gin"
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// func main() {
|
||||||
|
// r := gin.Default()
|
||||||
|
//
|
||||||
|
// // Ping handler
|
||||||
|
// r.GET("/ping", func(c *gin.Context) {
|
||||||
|
// c.String(200, "pong")
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// log.Fatal(autotls.Run(r, "example1.com", "example2.com"))
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
package autotls
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/autotls"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
r := gin.Default()
|
||||||
|
|
||||||
|
// Ping handler
|
||||||
|
r.GET("/ping", func(c *gin.Context) {
|
||||||
|
c.String(200, "pong")
|
||||||
|
})
|
||||||
|
|
||||||
|
log.Fatal(autotls.Run(r, "example1.com", "example2.com"))
|
||||||
|
}
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/autotls"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"golang.org/x/crypto/acme/autocert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
r := gin.Default()
|
||||||
|
|
||||||
|
// Ping handler
|
||||||
|
r.GET("/ping", func(c *gin.Context) {
|
||||||
|
c.String(200, "pong")
|
||||||
|
})
|
||||||
|
|
||||||
|
m := autocert.Manager{
|
||||||
|
Prompt: autocert.AcceptTOS,
|
||||||
|
HostPolicy: autocert.HostWhitelist("example1.com", "example2.com"),
|
||||||
|
Cache: autocert.DirCache("/var/www/.cache"),
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Fatal(autotls.RunWithManager(r, &m))
|
||||||
|
}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"comment": "",
|
||||||
|
"ignore": "test",
|
||||||
|
"package": [
|
||||||
|
{
|
||||||
|
"checksumSHA1": "QNHLX/9+KNzu0oCCdqUSEX4kyiY=",
|
||||||
|
"path": "golang.org/x/crypto/acme",
|
||||||
|
"revision": "cbc3d0884eac986df6e78a039b8792e869bff863",
|
||||||
|
"revisionTime": "2017-04-09T18:29:52Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "TrKJW+flz7JulXU3sqnBJjGzgQc=",
|
||||||
|
"path": "golang.org/x/crypto/acme/autocert",
|
||||||
|
"revision": "5ef0053f77724838734b6945dd364d3847e5de1d",
|
||||||
|
"revisionTime": "2017-06-29T04:06:47Z"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rootPath": "github.com/gin-gonic/autotls"
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
# Treat all files in this repo as binary, with no git magic updating
|
||||||
|
# line endings. Windows users contributing to Go will need to use a
|
||||||
|
# modern version of git and editors capable of LF line endings.
|
||||||
|
#
|
||||||
|
# We'll prevent accidental CRLF line endings from entering the repo
|
||||||
|
# via the git-review gofmt checks.
|
||||||
|
#
|
||||||
|
# See golang.org/issue/9281
|
||||||
|
|
||||||
|
* -text
|
||||||
Generated
+2
@@ -0,0 +1,2 @@
|
|||||||
|
# Add no patterns to .hgignore except for files generated by the build.
|
||||||
|
last-change
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
# This source code refers to The Go Authors for copyright purposes.
|
||||||
|
# The master list of authors is in the main Go distribution,
|
||||||
|
# visible at https://tip.golang.org/AUTHORS.
|
||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
# Contributing to Go
|
||||||
|
|
||||||
|
Go is an open source project.
|
||||||
|
|
||||||
|
It is the work of hundreds of contributors. We appreciate your help!
|
||||||
|
|
||||||
|
|
||||||
|
## Filing issues
|
||||||
|
|
||||||
|
When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions:
|
||||||
|
|
||||||
|
1. What version of Go are you using (`go version`)?
|
||||||
|
2. What operating system and processor architecture are you using?
|
||||||
|
3. What did you do?
|
||||||
|
4. What did you expect to see?
|
||||||
|
5. What did you see instead?
|
||||||
|
|
||||||
|
General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
|
||||||
|
The gophers there will answer or ask you to file an issue if you've tripped over a bug.
|
||||||
|
|
||||||
|
## Contributing code
|
||||||
|
|
||||||
|
Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
|
||||||
|
before sending patches.
|
||||||
|
|
||||||
|
**We do not accept GitHub pull requests**
|
||||||
|
(we use [Gerrit](https://code.google.com/p/gerrit/) instead for code review).
|
||||||
|
|
||||||
|
Unless otherwise noted, the Go source files are distributed under
|
||||||
|
the BSD-style license found in the LICENSE file.
|
||||||
|
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
# This source code was written by the Go contributors.
|
||||||
|
# The master list of contributors is in the main Go distribution,
|
||||||
|
# visible at https://tip.golang.org/CONTRIBUTORS.
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
Copyright (c) 2009 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.
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
Additional IP Rights Grant (Patents)
|
||||||
|
|
||||||
|
"This implementation" means the copyrightable works distributed by
|
||||||
|
Google as part of the Go project.
|
||||||
|
|
||||||
|
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||||
|
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||||
|
patent license to make, have made, use, offer to sell, sell, import,
|
||||||
|
transfer and otherwise run, modify and propagate the contents of this
|
||||||
|
implementation of Go, where such license applies only to those patent
|
||||||
|
claims, both currently owned or controlled by Google and acquired in
|
||||||
|
the future, licensable by Google that are necessarily infringed by this
|
||||||
|
implementation of Go. This grant does not include claims that would be
|
||||||
|
infringed only as a consequence of further modification of this
|
||||||
|
implementation. If you or your agent or exclusive licensee institute or
|
||||||
|
order or agree to the institution of patent litigation against any
|
||||||
|
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||||
|
that this implementation of Go or any code incorporated within this
|
||||||
|
implementation of Go constitutes direct or contributory patent
|
||||||
|
infringement, or inducement of patent infringement, then any patent
|
||||||
|
rights granted to you under this License for this implementation of Go
|
||||||
|
shall terminate as of the date such litigation is filed.
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
This repository holds supplementary Go cryptography libraries.
|
||||||
|
|
||||||
|
To submit changes to this repository, see https://golang.org/doc/contribute.html.
|
||||||
+1086
File diff suppressed because it is too large.
Load diff
+1346
File diff suppressed because it is too large.
Load diff
+819
@@ -0,0 +1,819 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package autocert provides automatic access to certificates from Let's Encrypt
|
||||||
|
// and any other ACME-based CA.
|
||||||
|
//
|
||||||
|
// This package is a work in progress and makes no API stability promises.
|
||||||
|
package autocert
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
mathrand "math/rand"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/acme"
|
||||||
|
)
|
||||||
|
|
||||||
|
// createCertRetryAfter is how much time to wait before removing a failed state
|
||||||
|
// entry due to an unsuccessful createCert call.
|
||||||
|
// This is a variable instead of a const for testing.
|
||||||
|
// TODO: Consider making it configurable or an exp backoff?
|
||||||
|
var createCertRetryAfter = time.Minute
|
||||||
|
|
||||||
|
// pseudoRand is safe for concurrent use.
|
||||||
|
var pseudoRand *lockedMathRand
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
src := mathrand.NewSource(timeNow().UnixNano())
|
||||||
|
pseudoRand = &lockedMathRand{rnd: mathrand.New(src)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AcceptTOS is a Manager.Prompt function that always returns true to
|
||||||
|
// indicate acceptance of the CA's Terms of Service during account
|
||||||
|
// registration.
|
||||||
|
func AcceptTOS(tosURL string) bool { return true }
|
||||||
|
|
||||||
|
// HostPolicy specifies which host names the Manager is allowed to respond to.
|
||||||
|
// It returns a non-nil error if the host should be rejected.
|
||||||
|
// The returned error is accessible via tls.Conn.Handshake and its callers.
|
||||||
|
// See Manager's HostPolicy field and GetCertificate method docs for more details.
|
||||||
|
type HostPolicy func(ctx context.Context, host string) error
|
||||||
|
|
||||||
|
// HostWhitelist returns a policy where only the specified host names are allowed.
|
||||||
|
// Only exact matches are currently supported. Subdomains, regexp or wildcard
|
||||||
|
// will not match.
|
||||||
|
func HostWhitelist(hosts ...string) HostPolicy {
|
||||||
|
whitelist := make(map[string]bool, len(hosts))
|
||||||
|
for _, h := range hosts {
|
||||||
|
whitelist[h] = true
|
||||||
|
}
|
||||||
|
return func(_ context.Context, host string) error {
|
||||||
|
if !whitelist[host] {
|
||||||
|
return errors.New("acme/autocert: host not configured")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultHostPolicy is used when Manager.HostPolicy is not set.
|
||||||
|
func defaultHostPolicy(context.Context, string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manager is a stateful certificate manager built on top of acme.Client.
|
||||||
|
// It obtains and refreshes certificates automatically,
|
||||||
|
// as well as providing them to a TLS server via tls.Config.
|
||||||
|
//
|
||||||
|
// To preserve issued certificates and improve overall performance,
|
||||||
|
// use a cache implementation of Cache. For instance, DirCache.
|
||||||
|
type Manager struct {
|
||||||
|
// Prompt specifies a callback function to conditionally accept a CA's Terms of Service (TOS).
|
||||||
|
// The registration may require the caller to agree to the CA's TOS.
|
||||||
|
// If so, Manager calls Prompt with a TOS URL provided by the CA. Prompt should report
|
||||||
|
// whether the caller agrees to the terms.
|
||||||
|
//
|
||||||
|
// To always accept the terms, the callers can use AcceptTOS.
|
||||||
|
Prompt func(tosURL string) bool
|
||||||
|
|
||||||
|
// Cache optionally stores and retrieves previously-obtained certificates.
|
||||||
|
// If nil, certs will only be cached for the lifetime of the Manager.
|
||||||
|
//
|
||||||
|
// Manager passes the Cache certificates data encoded in PEM, with private/public
|
||||||
|
// parts combined in a single Cache.Put call, private key first.
|
||||||
|
Cache Cache
|
||||||
|
|
||||||
|
// HostPolicy controls which domains the Manager will attempt
|
||||||
|
// to retrieve new certificates for. It does not affect cached certs.
|
||||||
|
//
|
||||||
|
// If non-nil, HostPolicy is called before requesting a new cert.
|
||||||
|
// If nil, all hosts are currently allowed. This is not recommended,
|
||||||
|
// as it opens a potential attack where clients connect to a server
|
||||||
|
// by IP address and pretend to be asking for an incorrect host name.
|
||||||
|
// Manager will attempt to obtain a certificate for that host, incorrectly,
|
||||||
|
// eventually reaching the CA's rate limit for certificate requests
|
||||||
|
// and making it impossible to obtain actual certificates.
|
||||||
|
//
|
||||||
|
// See GetCertificate for more details.
|
||||||
|
HostPolicy HostPolicy
|
||||||
|
|
||||||
|
// RenewBefore optionally specifies how early certificates should
|
||||||
|
// be renewed before they expire.
|
||||||
|
//
|
||||||
|
// If zero, they're renewed 30 days before expiration.
|
||||||
|
RenewBefore time.Duration
|
||||||
|
|
||||||
|
// Client is used to perform low-level operations, such as account registration
|
||||||
|
// and requesting new certificates.
|
||||||
|
// If Client is nil, a zero-value acme.Client is used with acme.LetsEncryptURL
|
||||||
|
// directory endpoint and a newly-generated ECDSA P-256 key.
|
||||||
|
//
|
||||||
|
// Mutating the field after the first call of GetCertificate method will have no effect.
|
||||||
|
Client *acme.Client
|
||||||
|
|
||||||
|
// Email optionally specifies a contact email address.
|
||||||
|
// This is used by CAs, such as Let's Encrypt, to notify about problems
|
||||||
|
// with issued certificates.
|
||||||
|
//
|
||||||
|
// If the Client's account key is already registered, Email is not used.
|
||||||
|
Email string
|
||||||
|
|
||||||
|
// ForceRSA makes the Manager generate certificates with 2048-bit RSA keys.
|
||||||
|
//
|
||||||
|
// If false, a default is used. Currently the default
|
||||||
|
// is EC-based keys using the P-256 curve.
|
||||||
|
ForceRSA bool
|
||||||
|
|
||||||
|
clientMu sync.Mutex
|
||||||
|
client *acme.Client // initialized by acmeClient method
|
||||||
|
|
||||||
|
stateMu sync.Mutex
|
||||||
|
state map[string]*certState // keyed by domain name
|
||||||
|
|
||||||
|
// tokenCert is keyed by token domain name, which matches server name
|
||||||
|
// of ClientHello. Keys always have ".acme.invalid" suffix.
|
||||||
|
tokenCertMu sync.RWMutex
|
||||||
|
tokenCert map[string]*tls.Certificate
|
||||||
|
|
||||||
|
// renewal tracks the set of domains currently running renewal timers.
|
||||||
|
// It is keyed by domain name.
|
||||||
|
renewalMu sync.Mutex
|
||||||
|
renewal map[string]*domainRenewal
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCertificate implements the tls.Config.GetCertificate hook.
|
||||||
|
// It provides a TLS certificate for hello.ServerName host, including answering
|
||||||
|
// *.acme.invalid (TLS-SNI) challenges. All other fields of hello are ignored.
|
||||||
|
//
|
||||||
|
// If m.HostPolicy is non-nil, GetCertificate calls the policy before requesting
|
||||||
|
// a new cert. A non-nil error returned from m.HostPolicy halts TLS negotiation.
|
||||||
|
// The error is propagated back to the caller of GetCertificate and is user-visible.
|
||||||
|
// This does not affect cached certs. See HostPolicy field description for more details.
|
||||||
|
func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||||
|
if m.Prompt == nil {
|
||||||
|
return nil, errors.New("acme/autocert: Manager.Prompt not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
name := hello.ServerName
|
||||||
|
if name == "" {
|
||||||
|
return nil, errors.New("acme/autocert: missing server name")
|
||||||
|
}
|
||||||
|
if !strings.Contains(strings.Trim(name, "."), ".") {
|
||||||
|
return nil, errors.New("acme/autocert: server name component count invalid")
|
||||||
|
}
|
||||||
|
if strings.ContainsAny(name, `/\`) {
|
||||||
|
return nil, errors.New("acme/autocert: server name contains invalid character")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// check whether this is a token cert requested for TLS-SNI challenge
|
||||||
|
if strings.HasSuffix(name, ".acme.invalid") {
|
||||||
|
m.tokenCertMu.RLock()
|
||||||
|
defer m.tokenCertMu.RUnlock()
|
||||||
|
if cert := m.tokenCert[name]; cert != nil {
|
||||||
|
return cert, nil
|
||||||
|
}
|
||||||
|
if cert, err := m.cacheGet(ctx, name); err == nil {
|
||||||
|
return cert, nil
|
||||||
|
}
|
||||||
|
// TODO: cache error results?
|
||||||
|
return nil, fmt.Errorf("acme/autocert: no token cert for %q", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// regular domain
|
||||||
|
name = strings.TrimSuffix(name, ".") // golang.org/issue/18114
|
||||||
|
cert, err := m.cert(ctx, name)
|
||||||
|
if err == nil {
|
||||||
|
return cert, nil
|
||||||
|
}
|
||||||
|
if err != ErrCacheMiss {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// first-time
|
||||||
|
if err := m.hostPolicy()(ctx, name); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cert, err = m.createCert(ctx, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
m.cachePut(ctx, name, cert)
|
||||||
|
return cert, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// cert returns an existing certificate either from m.state or cache.
|
||||||
|
// If a certificate is found in cache but not in m.state, the latter will be filled
|
||||||
|
// with the cached value.
|
||||||
|
func (m *Manager) cert(ctx context.Context, name string) (*tls.Certificate, error) {
|
||||||
|
m.stateMu.Lock()
|
||||||
|
if s, ok := m.state[name]; ok {
|
||||||
|
m.stateMu.Unlock()
|
||||||
|
s.RLock()
|
||||||
|
defer s.RUnlock()
|
||||||
|
return s.tlscert()
|
||||||
|
}
|
||||||
|
defer m.stateMu.Unlock()
|
||||||
|
cert, err := m.cacheGet(ctx, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
signer, ok := cert.PrivateKey.(crypto.Signer)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("acme/autocert: private key cannot sign")
|
||||||
|
}
|
||||||
|
if m.state == nil {
|
||||||
|
m.state = make(map[string]*certState)
|
||||||
|
}
|
||||||
|
s := &certState{
|
||||||
|
key: signer,
|
||||||
|
cert: cert.Certificate,
|
||||||
|
leaf: cert.Leaf,
|
||||||
|
}
|
||||||
|
m.state[name] = s
|
||||||
|
go m.renew(name, s.key, s.leaf.NotAfter)
|
||||||
|
return cert, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// cacheGet always returns a valid certificate, or an error otherwise.
|
||||||
|
// If a cached certficate exists but is not valid, ErrCacheMiss is returned.
|
||||||
|
func (m *Manager) cacheGet(ctx context.Context, domain string) (*tls.Certificate, error) {
|
||||||
|
if m.Cache == nil {
|
||||||
|
return nil, ErrCacheMiss
|
||||||
|
}
|
||||||
|
data, err := m.Cache.Get(ctx, domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// private
|
||||||
|
priv, pub := pem.Decode(data)
|
||||||
|
if priv == nil || !strings.Contains(priv.Type, "PRIVATE") {
|
||||||
|
return nil, ErrCacheMiss
|
||||||
|
}
|
||||||
|
privKey, err := parsePrivateKey(priv.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// public
|
||||||
|
var pubDER [][]byte
|
||||||
|
for len(pub) > 0 {
|
||||||
|
var b *pem.Block
|
||||||
|
b, pub = pem.Decode(pub)
|
||||||
|
if b == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
pubDER = append(pubDER, b.Bytes)
|
||||||
|
}
|
||||||
|
if len(pub) > 0 {
|
||||||
|
// Leftover content not consumed by pem.Decode. Corrupt. Ignore.
|
||||||
|
return nil, ErrCacheMiss
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify and create TLS cert
|
||||||
|
leaf, err := validCert(domain, pubDER, privKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, ErrCacheMiss
|
||||||
|
}
|
||||||
|
tlscert := &tls.Certificate{
|
||||||
|
Certificate: pubDER,
|
||||||
|
PrivateKey: privKey,
|
||||||
|
Leaf: leaf,
|
||||||
|
}
|
||||||
|
return tlscert, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) cachePut(ctx context.Context, domain string, tlscert *tls.Certificate) error {
|
||||||
|
if m.Cache == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// contains PEM-encoded data
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
// private
|
||||||
|
switch key := tlscert.PrivateKey.(type) {
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
if err := encodeECDSAKey(&buf, key); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
b := x509.MarshalPKCS1PrivateKey(key)
|
||||||
|
pb := &pem.Block{Type: "RSA PRIVATE KEY", Bytes: b}
|
||||||
|
if err := pem.Encode(&buf, pb); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return errors.New("acme/autocert: unknown private key type")
|
||||||
|
}
|
||||||
|
|
||||||
|
// public
|
||||||
|
for _, b := range tlscert.Certificate {
|
||||||
|
pb := &pem.Block{Type: "CERTIFICATE", Bytes: b}
|
||||||
|
if err := pem.Encode(&buf, pb); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.Cache.Put(ctx, domain, buf.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeECDSAKey(w io.Writer, key *ecdsa.PrivateKey) error {
|
||||||
|
b, err := x509.MarshalECPrivateKey(key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pb := &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
|
||||||
|
return pem.Encode(w, pb)
|
||||||
|
}
|
||||||
|
|
||||||
|
// createCert starts the domain ownership verification and returns a certificate
|
||||||
|
// for that domain upon success.
|
||||||
|
//
|
||||||
|
// If the domain is already being verified, it waits for the existing verification to complete.
|
||||||
|
// Either way, createCert blocks for the duration of the whole process.
|
||||||
|
func (m *Manager) createCert(ctx context.Context, domain string) (*tls.Certificate, error) {
|
||||||
|
// TODO: maybe rewrite this whole piece using sync.Once
|
||||||
|
state, err := m.certState(domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// state may exist if another goroutine is already working on it
|
||||||
|
// in which case just wait for it to finish
|
||||||
|
if !state.locked {
|
||||||
|
state.RLock()
|
||||||
|
defer state.RUnlock()
|
||||||
|
return state.tlscert()
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are the first; state is locked.
|
||||||
|
// Unblock the readers when domain ownership is verified
|
||||||
|
// and the we got the cert or the process failed.
|
||||||
|
defer state.Unlock()
|
||||||
|
state.locked = false
|
||||||
|
|
||||||
|
der, leaf, err := m.authorizedCert(ctx, state.key, domain)
|
||||||
|
if err != nil {
|
||||||
|
// Remove the failed state after some time,
|
||||||
|
// making the manager call createCert again on the following TLS hello.
|
||||||
|
time.AfterFunc(createCertRetryAfter, func() {
|
||||||
|
defer testDidRemoveState(domain)
|
||||||
|
m.stateMu.Lock()
|
||||||
|
defer m.stateMu.Unlock()
|
||||||
|
// Verify the state hasn't changed and it's still invalid
|
||||||
|
// before deleting.
|
||||||
|
s, ok := m.state[domain]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err := validCert(domain, s.cert, s.key); err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
delete(m.state, domain)
|
||||||
|
})
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
state.cert = der
|
||||||
|
state.leaf = leaf
|
||||||
|
go m.renew(domain, state.key, state.leaf.NotAfter)
|
||||||
|
return state.tlscert()
|
||||||
|
}
|
||||||
|
|
||||||
|
// certState returns a new or existing certState.
|
||||||
|
// If a new certState is returned, state.exist is false and the state is locked.
|
||||||
|
// The returned error is non-nil only in the case where a new state could not be created.
|
||||||
|
func (m *Manager) certState(domain string) (*certState, error) {
|
||||||
|
m.stateMu.Lock()
|
||||||
|
defer m.stateMu.Unlock()
|
||||||
|
if m.state == nil {
|
||||||
|
m.state = make(map[string]*certState)
|
||||||
|
}
|
||||||
|
// existing state
|
||||||
|
if state, ok := m.state[domain]; ok {
|
||||||
|
return state, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// new locked state
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
key crypto.Signer
|
||||||
|
)
|
||||||
|
if m.ForceRSA {
|
||||||
|
key, err = rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
} else {
|
||||||
|
key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
state := &certState{
|
||||||
|
key: key,
|
||||||
|
locked: true,
|
||||||
|
}
|
||||||
|
state.Lock() // will be unlocked by m.certState caller
|
||||||
|
m.state[domain] = state
|
||||||
|
return state, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// authorizedCert starts domain ownership verification process and requests a new cert upon success.
|
||||||
|
// The key argument is the certificate private key.
|
||||||
|
func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, domain string) (der [][]byte, leaf *x509.Certificate, err error) {
|
||||||
|
if err := m.verify(ctx, domain); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
client, err := m.acmeClient(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
csr, err := certRequest(key, domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
der, _, err = client.CreateCert(ctx, csr, 0, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
leaf, err = validCert(domain, der, key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return der, leaf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify starts a new identifier (domain) authorization flow.
|
||||||
|
// It prepares a challenge response and then blocks until the authorization
|
||||||
|
// is marked as "completed" by the CA (either succeeded or failed).
|
||||||
|
//
|
||||||
|
// verify returns nil iff the verification was successful.
|
||||||
|
func (m *Manager) verify(ctx context.Context, domain string) error {
|
||||||
|
client, err := m.acmeClient(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// start domain authorization and get the challenge
|
||||||
|
authz, err := client.Authorize(ctx, domain)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// maybe don't need to at all
|
||||||
|
if authz.Status == acme.StatusValid {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// pick a challenge: prefer tls-sni-02 over tls-sni-01
|
||||||
|
// TODO: consider authz.Combinations
|
||||||
|
var chal *acme.Challenge
|
||||||
|
for _, c := range authz.Challenges {
|
||||||
|
if c.Type == "tls-sni-02" {
|
||||||
|
chal = c
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if c.Type == "tls-sni-01" {
|
||||||
|
chal = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if chal == nil {
|
||||||
|
return errors.New("acme/autocert: no supported challenge type found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a token cert for the challenge response
|
||||||
|
var (
|
||||||
|
cert tls.Certificate
|
||||||
|
name string
|
||||||
|
)
|
||||||
|
switch chal.Type {
|
||||||
|
case "tls-sni-01":
|
||||||
|
cert, name, err = client.TLSSNI01ChallengeCert(chal.Token)
|
||||||
|
case "tls-sni-02":
|
||||||
|
cert, name, err = client.TLSSNI02ChallengeCert(chal.Token)
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("acme/autocert: unknown challenge type %q", chal.Type)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.putTokenCert(ctx, name, &cert)
|
||||||
|
defer func() {
|
||||||
|
// verification has ended at this point
|
||||||
|
// don't need token cert anymore
|
||||||
|
go m.deleteTokenCert(name)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// ready to fulfill the challenge
|
||||||
|
if _, err := client.Accept(ctx, chal); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// wait for the CA to validate
|
||||||
|
_, err = client.WaitAuthorization(ctx, authz.URI)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// putTokenCert stores the cert under the named key in both m.tokenCert map
|
||||||
|
// and m.Cache.
|
||||||
|
func (m *Manager) putTokenCert(ctx context.Context, name string, cert *tls.Certificate) {
|
||||||
|
m.tokenCertMu.Lock()
|
||||||
|
defer m.tokenCertMu.Unlock()
|
||||||
|
if m.tokenCert == nil {
|
||||||
|
m.tokenCert = make(map[string]*tls.Certificate)
|
||||||
|
}
|
||||||
|
m.tokenCert[name] = cert
|
||||||
|
m.cachePut(ctx, name, cert)
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteTokenCert removes the token certificate for the specified domain name
|
||||||
|
// from both m.tokenCert map and m.Cache.
|
||||||
|
func (m *Manager) deleteTokenCert(name string) {
|
||||||
|
m.tokenCertMu.Lock()
|
||||||
|
defer m.tokenCertMu.Unlock()
|
||||||
|
delete(m.tokenCert, name)
|
||||||
|
if m.Cache != nil {
|
||||||
|
m.Cache.Delete(context.Background(), name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// renew starts a cert renewal timer loop, one per domain.
|
||||||
|
//
|
||||||
|
// The loop is scheduled in two cases:
|
||||||
|
// - a cert was fetched from cache for the first time (wasn't in m.state)
|
||||||
|
// - a new cert was created by m.createCert
|
||||||
|
//
|
||||||
|
// The key argument is a certificate private key.
|
||||||
|
// The exp argument is the cert expiration time (NotAfter).
|
||||||
|
func (m *Manager) renew(domain string, key crypto.Signer, exp time.Time) {
|
||||||
|
m.renewalMu.Lock()
|
||||||
|
defer m.renewalMu.Unlock()
|
||||||
|
if m.renewal[domain] != nil {
|
||||||
|
// another goroutine is already on it
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if m.renewal == nil {
|
||||||
|
m.renewal = make(map[string]*domainRenewal)
|
||||||
|
}
|
||||||
|
dr := &domainRenewal{m: m, domain: domain, key: key}
|
||||||
|
m.renewal[domain] = dr
|
||||||
|
dr.start(exp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// stopRenew stops all currently running cert renewal timers.
|
||||||
|
// The timers are not restarted during the lifetime of the Manager.
|
||||||
|
func (m *Manager) stopRenew() {
|
||||||
|
m.renewalMu.Lock()
|
||||||
|
defer m.renewalMu.Unlock()
|
||||||
|
for name, dr := range m.renewal {
|
||||||
|
delete(m.renewal, name)
|
||||||
|
dr.stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) accountKey(ctx context.Context) (crypto.Signer, error) {
|
||||||
|
const keyName = "acme_account.key"
|
||||||
|
|
||||||
|
genKey := func() (*ecdsa.PrivateKey, error) {
|
||||||
|
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Cache == nil {
|
||||||
|
return genKey()
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := m.Cache.Get(ctx, keyName)
|
||||||
|
if err == ErrCacheMiss {
|
||||||
|
key, err := genKey()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := encodeECDSAKey(&buf, key); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := m.Cache.Put(ctx, keyName, buf.Bytes()); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return key, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
priv, _ := pem.Decode(data)
|
||||||
|
if priv == nil || !strings.Contains(priv.Type, "PRIVATE") {
|
||||||
|
return nil, errors.New("acme/autocert: invalid account key found in cache")
|
||||||
|
}
|
||||||
|
return parsePrivateKey(priv.Bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) acmeClient(ctx context.Context) (*acme.Client, error) {
|
||||||
|
m.clientMu.Lock()
|
||||||
|
defer m.clientMu.Unlock()
|
||||||
|
if m.client != nil {
|
||||||
|
return m.client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
client := m.Client
|
||||||
|
if client == nil {
|
||||||
|
client = &acme.Client{DirectoryURL: acme.LetsEncryptURL}
|
||||||
|
}
|
||||||
|
if client.Key == nil {
|
||||||
|
var err error
|
||||||
|
client.Key, err = m.accountKey(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var contact []string
|
||||||
|
if m.Email != "" {
|
||||||
|
contact = []string{"mailto:" + m.Email}
|
||||||
|
}
|
||||||
|
a := &acme.Account{Contact: contact}
|
||||||
|
_, err := client.Register(ctx, a, m.Prompt)
|
||||||
|
if ae, ok := err.(*acme.Error); err == nil || ok && ae.StatusCode == http.StatusConflict {
|
||||||
|
// conflict indicates the key is already registered
|
||||||
|
m.client = client
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
return m.client, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) hostPolicy() HostPolicy {
|
||||||
|
if m.HostPolicy != nil {
|
||||||
|
return m.HostPolicy
|
||||||
|
}
|
||||||
|
return defaultHostPolicy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) renewBefore() time.Duration {
|
||||||
|
if m.RenewBefore > renewJitter {
|
||||||
|
return m.RenewBefore
|
||||||
|
}
|
||||||
|
return 720 * time.Hour // 30 days
|
||||||
|
}
|
||||||
|
|
||||||
|
// certState is ready when its mutex is unlocked for reading.
|
||||||
|
type certState struct {
|
||||||
|
sync.RWMutex
|
||||||
|
locked bool // locked for read/write
|
||||||
|
key crypto.Signer // private key for cert
|
||||||
|
cert [][]byte // DER encoding
|
||||||
|
leaf *x509.Certificate // parsed cert[0]; always non-nil if cert != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// tlscert creates a tls.Certificate from s.key and s.cert.
|
||||||
|
// Callers should wrap it in s.RLock() and s.RUnlock().
|
||||||
|
func (s *certState) tlscert() (*tls.Certificate, error) {
|
||||||
|
if s.key == nil {
|
||||||
|
return nil, errors.New("acme/autocert: missing signer")
|
||||||
|
}
|
||||||
|
if len(s.cert) == 0 {
|
||||||
|
return nil, errors.New("acme/autocert: missing certificate")
|
||||||
|
}
|
||||||
|
return &tls.Certificate{
|
||||||
|
PrivateKey: s.key,
|
||||||
|
Certificate: s.cert,
|
||||||
|
Leaf: s.leaf,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// certRequest creates a certificate request for the given common name cn
|
||||||
|
// and optional SANs.
|
||||||
|
func certRequest(key crypto.Signer, cn string, san ...string) ([]byte, error) {
|
||||||
|
req := &x509.CertificateRequest{
|
||||||
|
Subject: pkix.Name{CommonName: cn},
|
||||||
|
DNSNames: san,
|
||||||
|
}
|
||||||
|
return x509.CreateCertificateRequest(rand.Reader, req, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates
|
||||||
|
// PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys.
|
||||||
|
// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
|
||||||
|
//
|
||||||
|
// Inspired by parsePrivateKey in crypto/tls/tls.go.
|
||||||
|
func parsePrivateKey(der []byte) (crypto.Signer, error) {
|
||||||
|
if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
|
||||||
|
return key, nil
|
||||||
|
}
|
||||||
|
if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
|
||||||
|
switch key := key.(type) {
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
return key, nil
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
return key, nil
|
||||||
|
default:
|
||||||
|
return nil, errors.New("acme/autocert: unknown private key type in PKCS#8 wrapping")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if key, err := x509.ParseECPrivateKey(der); err == nil {
|
||||||
|
return key, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.New("acme/autocert: failed to parse private key")
|
||||||
|
}
|
||||||
|
|
||||||
|
// validCert parses a cert chain provided as der argument and verifies the leaf, der[0],
|
||||||
|
// corresponds to the private key, as well as the domain match and expiration dates.
|
||||||
|
// It doesn't do any revocation checking.
|
||||||
|
//
|
||||||
|
// The returned value is the verified leaf cert.
|
||||||
|
func validCert(domain string, der [][]byte, key crypto.Signer) (leaf *x509.Certificate, err error) {
|
||||||
|
// parse public part(s)
|
||||||
|
var n int
|
||||||
|
for _, b := range der {
|
||||||
|
n += len(b)
|
||||||
|
}
|
||||||
|
pub := make([]byte, n)
|
||||||
|
n = 0
|
||||||
|
for _, b := range der {
|
||||||
|
n += copy(pub[n:], b)
|
||||||
|
}
|
||||||
|
x509Cert, err := x509.ParseCertificates(pub)
|
||||||
|
if len(x509Cert) == 0 {
|
||||||
|
return nil, errors.New("acme/autocert: no public key found")
|
||||||
|
}
|
||||||
|
// verify the leaf is not expired and matches the domain name
|
||||||
|
leaf = x509Cert[0]
|
||||||
|
now := timeNow()
|
||||||
|
if now.Before(leaf.NotBefore) {
|
||||||
|
return nil, errors.New("acme/autocert: certificate is not valid yet")
|
||||||
|
}
|
||||||
|
if now.After(leaf.NotAfter) {
|
||||||
|
return nil, errors.New("acme/autocert: expired certificate")
|
||||||
|
}
|
||||||
|
if err := leaf.VerifyHostname(domain); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// ensure the leaf corresponds to the private key
|
||||||
|
switch pub := leaf.PublicKey.(type) {
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
prv, ok := key.(*rsa.PrivateKey)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("acme/autocert: private key type does not match public key type")
|
||||||
|
}
|
||||||
|
if pub.N.Cmp(prv.N) != 0 {
|
||||||
|
return nil, errors.New("acme/autocert: private key does not match public key")
|
||||||
|
}
|
||||||
|
case *ecdsa.PublicKey:
|
||||||
|
prv, ok := key.(*ecdsa.PrivateKey)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("acme/autocert: private key type does not match public key type")
|
||||||
|
}
|
||||||
|
if pub.X.Cmp(prv.X) != 0 || pub.Y.Cmp(prv.Y) != 0 {
|
||||||
|
return nil, errors.New("acme/autocert: private key does not match public key")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, errors.New("acme/autocert: unknown public key algorithm")
|
||||||
|
}
|
||||||
|
return leaf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func retryAfter(v string) time.Duration {
|
||||||
|
if i, err := strconv.Atoi(v); err == nil {
|
||||||
|
return time.Duration(i) * time.Second
|
||||||
|
}
|
||||||
|
if t, err := http.ParseTime(v); err == nil {
|
||||||
|
return t.Sub(timeNow())
|
||||||
|
}
|
||||||
|
return time.Second
|
||||||
|
}
|
||||||
|
|
||||||
|
type lockedMathRand struct {
|
||||||
|
sync.Mutex
|
||||||
|
rnd *mathrand.Rand
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *lockedMathRand) int63n(max int64) int64 {
|
||||||
|
r.Lock()
|
||||||
|
n := r.rnd.Int63n(max)
|
||||||
|
r.Unlock()
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// For easier testing.
|
||||||
|
var (
|
||||||
|
timeNow = time.Now
|
||||||
|
|
||||||
|
// Called when a state is removed.
|
||||||
|
testDidRemoveState = func(domain string) {}
|
||||||
|
)
|
||||||
+606
@@ -0,0 +1,606 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package autocert
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"html/template"
|
||||||
|
"io"
|
||||||
|
"math/big"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/acme"
|
||||||
|
)
|
||||||
|
|
||||||
|
var discoTmpl = template.Must(template.New("disco").Parse(`{
|
||||||
|
"new-reg": "{{.}}/new-reg",
|
||||||
|
"new-authz": "{{.}}/new-authz",
|
||||||
|
"new-cert": "{{.}}/new-cert"
|
||||||
|
}`))
|
||||||
|
|
||||||
|
var authzTmpl = template.Must(template.New("authz").Parse(`{
|
||||||
|
"status": "pending",
|
||||||
|
"challenges": [
|
||||||
|
{
|
||||||
|
"uri": "{{.}}/challenge/1",
|
||||||
|
"type": "tls-sni-01",
|
||||||
|
"token": "token-01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uri": "{{.}}/challenge/2",
|
||||||
|
"type": "tls-sni-02",
|
||||||
|
"token": "token-02"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`))
|
||||||
|
|
||||||
|
type memCache struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
keyData map[string][]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memCache) Get(ctx context.Context, key string) ([]byte, error) {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
|
||||||
|
v, ok := m.keyData[key]
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrCacheMiss
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memCache) Put(ctx context.Context, key string, data []byte) error {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
|
||||||
|
m.keyData[key] = data
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memCache) Delete(ctx context.Context, key string) error {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
|
||||||
|
delete(m.keyData, key)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMemCache() *memCache {
|
||||||
|
return &memCache{
|
||||||
|
keyData: make(map[string][]byte),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func dummyCert(pub interface{}, san ...string) ([]byte, error) {
|
||||||
|
return dateDummyCert(pub, time.Now(), time.Now().Add(90*24*time.Hour), san...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dateDummyCert(pub interface{}, start, end time.Time, san ...string) ([]byte, error) {
|
||||||
|
// use EC key to run faster on 386
|
||||||
|
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t := &x509.Certificate{
|
||||||
|
SerialNumber: big.NewInt(1),
|
||||||
|
NotBefore: start,
|
||||||
|
NotAfter: end,
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
KeyUsage: x509.KeyUsageKeyEncipherment,
|
||||||
|
DNSNames: san,
|
||||||
|
}
|
||||||
|
if pub == nil {
|
||||||
|
pub = &key.PublicKey
|
||||||
|
}
|
||||||
|
return x509.CreateCertificate(rand.Reader, t, t, pub, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodePayload(v interface{}, r io.Reader) error {
|
||||||
|
var req struct{ Payload string }
|
||||||
|
if err := json.NewDecoder(r).Decode(&req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
payload, err := base64.RawURLEncoding.DecodeString(req.Payload)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return json.Unmarshal(payload, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetCertificate(t *testing.T) {
|
||||||
|
man := &Manager{Prompt: AcceptTOS}
|
||||||
|
defer man.stopRenew()
|
||||||
|
hello := &tls.ClientHelloInfo{ServerName: "example.org"}
|
||||||
|
testGetCertificate(t, man, "example.org", hello)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetCertificate_trailingDot(t *testing.T) {
|
||||||
|
man := &Manager{Prompt: AcceptTOS}
|
||||||
|
defer man.stopRenew()
|
||||||
|
hello := &tls.ClientHelloInfo{ServerName: "example.org."}
|
||||||
|
testGetCertificate(t, man, "example.org", hello)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetCertificate_ForceRSA(t *testing.T) {
|
||||||
|
man := &Manager{
|
||||||
|
Prompt: AcceptTOS,
|
||||||
|
Cache: newMemCache(),
|
||||||
|
ForceRSA: true,
|
||||||
|
}
|
||||||
|
defer man.stopRenew()
|
||||||
|
hello := &tls.ClientHelloInfo{ServerName: "example.org"}
|
||||||
|
testGetCertificate(t, man, "example.org", hello)
|
||||||
|
|
||||||
|
cert, err := man.cacheGet(context.Background(), "example.org")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("man.cacheGet: %v", err)
|
||||||
|
}
|
||||||
|
if _, ok := cert.PrivateKey.(*rsa.PrivateKey); !ok {
|
||||||
|
t.Errorf("cert.PrivateKey is %T; want *rsa.PrivateKey", cert.PrivateKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetCertificate_nilPrompt(t *testing.T) {
|
||||||
|
man := &Manager{}
|
||||||
|
defer man.stopRenew()
|
||||||
|
url, finish := startACMEServerStub(t, man, "example.org")
|
||||||
|
defer finish()
|
||||||
|
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
man.Client = &acme.Client{
|
||||||
|
Key: key,
|
||||||
|
DirectoryURL: url,
|
||||||
|
}
|
||||||
|
hello := &tls.ClientHelloInfo{ServerName: "example.org"}
|
||||||
|
if _, err := man.GetCertificate(hello); err == nil {
|
||||||
|
t.Error("got certificate for example.org; wanted error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetCertificate_expiredCache(t *testing.T) {
|
||||||
|
// Make an expired cert and cache it.
|
||||||
|
pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
tmpl := &x509.Certificate{
|
||||||
|
SerialNumber: big.NewInt(1),
|
||||||
|
Subject: pkix.Name{CommonName: "example.org"},
|
||||||
|
NotAfter: time.Now(),
|
||||||
|
}
|
||||||
|
pub, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &pk.PublicKey, pk)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
tlscert := &tls.Certificate{
|
||||||
|
Certificate: [][]byte{pub},
|
||||||
|
PrivateKey: pk,
|
||||||
|
}
|
||||||
|
|
||||||
|
man := &Manager{Prompt: AcceptTOS, Cache: newMemCache()}
|
||||||
|
defer man.stopRenew()
|
||||||
|
if err := man.cachePut(context.Background(), "example.org", tlscert); err != nil {
|
||||||
|
t.Fatalf("man.cachePut: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The expired cached cert should trigger a new cert issuance
|
||||||
|
// and return without an error.
|
||||||
|
hello := &tls.ClientHelloInfo{ServerName: "example.org"}
|
||||||
|
testGetCertificate(t, man, "example.org", hello)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetCertificate_failedAttempt(t *testing.T) {
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
}))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
const example = "example.org"
|
||||||
|
d := createCertRetryAfter
|
||||||
|
f := testDidRemoveState
|
||||||
|
defer func() {
|
||||||
|
createCertRetryAfter = d
|
||||||
|
testDidRemoveState = f
|
||||||
|
}()
|
||||||
|
createCertRetryAfter = 0
|
||||||
|
done := make(chan struct{})
|
||||||
|
testDidRemoveState = func(domain string) {
|
||||||
|
if domain != example {
|
||||||
|
t.Errorf("testDidRemoveState: domain = %q; want %q", domain, example)
|
||||||
|
}
|
||||||
|
close(done)
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
man := &Manager{
|
||||||
|
Prompt: AcceptTOS,
|
||||||
|
Client: &acme.Client{
|
||||||
|
Key: key,
|
||||||
|
DirectoryURL: ts.URL,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
defer man.stopRenew()
|
||||||
|
hello := &tls.ClientHelloInfo{ServerName: example}
|
||||||
|
if _, err := man.GetCertificate(hello); err == nil {
|
||||||
|
t.Error("GetCertificate: err is nil")
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-time.After(5 * time.Second):
|
||||||
|
t.Errorf("took too long to remove the %q state", example)
|
||||||
|
case <-done:
|
||||||
|
man.stateMu.Lock()
|
||||||
|
defer man.stateMu.Unlock()
|
||||||
|
if v, exist := man.state[example]; exist {
|
||||||
|
t.Errorf("state exists for %q: %+v", example, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// startACMEServerStub runs an ACME server
|
||||||
|
// The domain argument is the expected domain name of a certificate request.
|
||||||
|
func startACMEServerStub(t *testing.T, man *Manager, domain string) (url string, finish func()) {
|
||||||
|
// echo token-02 | shasum -a 256
|
||||||
|
// then divide result in 2 parts separated by dot
|
||||||
|
tokenCertName := "4e8eb87631187e9ff2153b56b13a4dec.13a35d002e485d60ff37354b32f665d9.token.acme.invalid"
|
||||||
|
verifyTokenCert := func() {
|
||||||
|
hello := &tls.ClientHelloInfo{ServerName: tokenCertName}
|
||||||
|
_, err := man.GetCertificate(hello)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("verifyTokenCert: GetCertificate(%q): %v", tokenCertName, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ACME CA server stub
|
||||||
|
var ca *httptest.Server
|
||||||
|
ca = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Replay-Nonce", "nonce")
|
||||||
|
if r.Method == "HEAD" {
|
||||||
|
// a nonce request
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch r.URL.Path {
|
||||||
|
// discovery
|
||||||
|
case "/":
|
||||||
|
if err := discoTmpl.Execute(w, ca.URL); err != nil {
|
||||||
|
t.Errorf("discoTmpl: %v", err)
|
||||||
|
}
|
||||||
|
// client key registration
|
||||||
|
case "/new-reg":
|
||||||
|
w.Write([]byte("{}"))
|
||||||
|
// domain authorization
|
||||||
|
case "/new-authz":
|
||||||
|
w.Header().Set("Location", ca.URL+"/authz/1")
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
if err := authzTmpl.Execute(w, ca.URL); err != nil {
|
||||||
|
t.Errorf("authzTmpl: %v", err)
|
||||||
|
}
|
||||||
|
// accept tls-sni-02 challenge
|
||||||
|
case "/challenge/2":
|
||||||
|
verifyTokenCert()
|
||||||
|
w.Write([]byte("{}"))
|
||||||
|
// authorization status
|
||||||
|
case "/authz/1":
|
||||||
|
w.Write([]byte(`{"status": "valid"}`))
|
||||||
|
// cert request
|
||||||
|
case "/new-cert":
|
||||||
|
var req struct {
|
||||||
|
CSR string `json:"csr"`
|
||||||
|
}
|
||||||
|
decodePayload(&req, r.Body)
|
||||||
|
b, _ := base64.RawURLEncoding.DecodeString(req.CSR)
|
||||||
|
csr, err := x509.ParseCertificateRequest(b)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("new-cert: CSR: %v", err)
|
||||||
|
}
|
||||||
|
if csr.Subject.CommonName != domain {
|
||||||
|
t.Errorf("CommonName in CSR = %q; want %q", csr.Subject.CommonName, domain)
|
||||||
|
}
|
||||||
|
der, err := dummyCert(csr.PublicKey, domain)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("new-cert: dummyCert: %v", err)
|
||||||
|
}
|
||||||
|
chainUp := fmt.Sprintf("<%s/ca-cert>; rel=up", ca.URL)
|
||||||
|
w.Header().Set("Link", chainUp)
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
w.Write(der)
|
||||||
|
// CA chain cert
|
||||||
|
case "/ca-cert":
|
||||||
|
der, err := dummyCert(nil, "ca")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("ca-cert: dummyCert: %v", err)
|
||||||
|
}
|
||||||
|
w.Write(der)
|
||||||
|
default:
|
||||||
|
t.Errorf("unrecognized r.URL.Path: %s", r.URL.Path)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
finish = func() {
|
||||||
|
ca.Close()
|
||||||
|
|
||||||
|
// make sure token cert was removed
|
||||||
|
cancel := make(chan struct{})
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
defer close(done)
|
||||||
|
tick := time.NewTicker(100 * time.Millisecond)
|
||||||
|
defer tick.Stop()
|
||||||
|
for {
|
||||||
|
hello := &tls.ClientHelloInfo{ServerName: tokenCertName}
|
||||||
|
if _, err := man.GetCertificate(hello); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-tick.C:
|
||||||
|
case <-cancel:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
case <-time.After(5 * time.Second):
|
||||||
|
close(cancel)
|
||||||
|
t.Error("token cert was not removed")
|
||||||
|
<-done
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ca.URL, finish
|
||||||
|
}
|
||||||
|
|
||||||
|
// tests man.GetCertificate flow using the provided hello argument.
|
||||||
|
// The domain argument is the expected domain name of a certificate request.
|
||||||
|
func testGetCertificate(t *testing.T, man *Manager, domain string, hello *tls.ClientHelloInfo) {
|
||||||
|
url, finish := startACMEServerStub(t, man, domain)
|
||||||
|
defer finish()
|
||||||
|
|
||||||
|
// use EC key to run faster on 386
|
||||||
|
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
man.Client = &acme.Client{
|
||||||
|
Key: key,
|
||||||
|
DirectoryURL: url,
|
||||||
|
}
|
||||||
|
|
||||||
|
// simulate tls.Config.GetCertificate
|
||||||
|
var tlscert *tls.Certificate
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
tlscert, err = man.GetCertificate(hello)
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case <-time.After(time.Minute):
|
||||||
|
t.Fatal("man.GetCertificate took too long to return")
|
||||||
|
case <-done:
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("man.GetCertificate: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify the tlscert is the same we responded with from the CA stub
|
||||||
|
if len(tlscert.Certificate) == 0 {
|
||||||
|
t.Fatal("len(tlscert.Certificate) is 0")
|
||||||
|
}
|
||||||
|
cert, err := x509.ParseCertificate(tlscert.Certificate[0])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("x509.ParseCertificate: %v", err)
|
||||||
|
}
|
||||||
|
if len(cert.DNSNames) == 0 || cert.DNSNames[0] != domain {
|
||||||
|
t.Errorf("cert.DNSNames = %v; want %q", cert.DNSNames, domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccountKeyCache(t *testing.T) {
|
||||||
|
m := Manager{Cache: newMemCache()}
|
||||||
|
ctx := context.Background()
|
||||||
|
k1, err := m.accountKey(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
k2, err := m.accountKey(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(k1, k2) {
|
||||||
|
t.Errorf("account keys don't match: k1 = %#v; k2 = %#v", k1, k2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCache(t *testing.T) {
|
||||||
|
privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
tmpl := &x509.Certificate{
|
||||||
|
SerialNumber: big.NewInt(1),
|
||||||
|
Subject: pkix.Name{CommonName: "example.org"},
|
||||||
|
NotAfter: time.Now().Add(time.Hour),
|
||||||
|
}
|
||||||
|
pub, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &privKey.PublicKey, privKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
tlscert := &tls.Certificate{
|
||||||
|
Certificate: [][]byte{pub},
|
||||||
|
PrivateKey: privKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
man := &Manager{Cache: newMemCache()}
|
||||||
|
defer man.stopRenew()
|
||||||
|
ctx := context.Background()
|
||||||
|
if err := man.cachePut(ctx, "example.org", tlscert); err != nil {
|
||||||
|
t.Fatalf("man.cachePut: %v", err)
|
||||||
|
}
|
||||||
|
res, err := man.cacheGet(ctx, "example.org")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("man.cacheGet: %v", err)
|
||||||
|
}
|
||||||
|
if res == nil {
|
||||||
|
t.Fatal("res is nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHostWhitelist(t *testing.T) {
|
||||||
|
policy := HostWhitelist("example.com", "example.org", "*.example.net")
|
||||||
|
tt := []struct {
|
||||||
|
host string
|
||||||
|
allow bool
|
||||||
|
}{
|
||||||
|
{"example.com", true},
|
||||||
|
{"example.org", true},
|
||||||
|
{"one.example.com", false},
|
||||||
|
{"two.example.org", false},
|
||||||
|
{"three.example.net", false},
|
||||||
|
{"dummy", false},
|
||||||
|
}
|
||||||
|
for i, test := range tt {
|
||||||
|
err := policy(nil, test.host)
|
||||||
|
if err != nil && test.allow {
|
||||||
|
t.Errorf("%d: policy(%q): %v; want nil", i, test.host, err)
|
||||||
|
}
|
||||||
|
if err == nil && !test.allow {
|
||||||
|
t.Errorf("%d: policy(%q): nil; want an error", i, test.host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidCert(t *testing.T) {
|
||||||
|
key1, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
key2, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
key3, err := rsa.GenerateKey(rand.Reader, 512)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
cert1, err := dummyCert(key1.Public(), "example.org")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
cert2, err := dummyCert(key2.Public(), "example.org")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
cert3, err := dummyCert(key3.Public(), "example.org")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
now := time.Now()
|
||||||
|
early, err := dateDummyCert(key1.Public(), now.Add(time.Hour), now.Add(2*time.Hour), "example.org")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
expired, err := dateDummyCert(key1.Public(), now.Add(-2*time.Hour), now.Add(-time.Hour), "example.org")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tt := []struct {
|
||||||
|
domain string
|
||||||
|
key crypto.Signer
|
||||||
|
cert [][]byte
|
||||||
|
ok bool
|
||||||
|
}{
|
||||||
|
{"example.org", key1, [][]byte{cert1}, true},
|
||||||
|
{"example.org", key3, [][]byte{cert3}, true},
|
||||||
|
{"example.org", key1, [][]byte{cert1, cert2, cert3}, true},
|
||||||
|
{"example.org", key1, [][]byte{cert1, {1}}, false},
|
||||||
|
{"example.org", key1, [][]byte{{1}}, false},
|
||||||
|
{"example.org", key1, [][]byte{cert2}, false},
|
||||||
|
{"example.org", key2, [][]byte{cert1}, false},
|
||||||
|
{"example.org", key1, [][]byte{cert3}, false},
|
||||||
|
{"example.org", key3, [][]byte{cert1}, false},
|
||||||
|
{"example.net", key1, [][]byte{cert1}, false},
|
||||||
|
{"example.org", key1, [][]byte{early}, false},
|
||||||
|
{"example.org", key1, [][]byte{expired}, false},
|
||||||
|
}
|
||||||
|
for i, test := range tt {
|
||||||
|
leaf, err := validCert(test.domain, test.cert, test.key)
|
||||||
|
if err != nil && test.ok {
|
||||||
|
t.Errorf("%d: err = %v", i, err)
|
||||||
|
}
|
||||||
|
if err == nil && !test.ok {
|
||||||
|
t.Errorf("%d: err is nil", i)
|
||||||
|
}
|
||||||
|
if err == nil && test.ok && leaf == nil {
|
||||||
|
t.Errorf("%d: leaf is nil", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type cacheGetFunc func(ctx context.Context, key string) ([]byte, error)
|
||||||
|
|
||||||
|
func (f cacheGetFunc) Get(ctx context.Context, key string) ([]byte, error) {
|
||||||
|
return f(ctx, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f cacheGetFunc) Put(ctx context.Context, key string, data []byte) error {
|
||||||
|
return fmt.Errorf("unsupported Put of %q = %q", key, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f cacheGetFunc) Delete(ctx context.Context, key string) error {
|
||||||
|
return fmt.Errorf("unsupported Delete of %q", key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestManagerGetCertificateBogusSNI(t *testing.T) {
|
||||||
|
m := Manager{
|
||||||
|
Prompt: AcceptTOS,
|
||||||
|
Cache: cacheGetFunc(func(ctx context.Context, key string) ([]byte, error) {
|
||||||
|
return nil, fmt.Errorf("cache.Get of %s", key)
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
wantErr string
|
||||||
|
}{
|
||||||
|
{"foo.com", "cache.Get of foo.com"},
|
||||||
|
{"foo.com.", "cache.Get of foo.com"},
|
||||||
|
{`a\b.com`, "acme/autocert: server name contains invalid character"},
|
||||||
|
{`a/b.com`, "acme/autocert: server name contains invalid character"},
|
||||||
|
{"", "acme/autocert: missing server name"},
|
||||||
|
{"foo", "acme/autocert: server name component count invalid"},
|
||||||
|
{".foo", "acme/autocert: server name component count invalid"},
|
||||||
|
{"foo.", "acme/autocert: server name component count invalid"},
|
||||||
|
{"fo.o", "cache.Get of fo.o"},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
_, err := m.GetCertificate(&tls.ClientHelloInfo{ServerName: tt.name})
|
||||||
|
got := fmt.Sprint(err)
|
||||||
|
if got != tt.wantErr {
|
||||||
|
t.Errorf("GetCertificate(SNI = %q) = %q; want %q", tt.name, got, tt.wantErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+130
@@ -0,0 +1,130 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package autocert
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrCacheMiss is returned when a certificate is not found in cache.
|
||||||
|
var ErrCacheMiss = errors.New("acme/autocert: certificate cache miss")
|
||||||
|
|
||||||
|
// Cache is used by Manager to store and retrieve previously obtained certificates
|
||||||
|
// as opaque data.
|
||||||
|
//
|
||||||
|
// The key argument of the methods refers to a domain name but need not be an FQDN.
|
||||||
|
// Cache implementations should not rely on the key naming pattern.
|
||||||
|
type Cache interface {
|
||||||
|
// Get returns a certificate data for the specified key.
|
||||||
|
// If there's no such key, Get returns ErrCacheMiss.
|
||||||
|
Get(ctx context.Context, key string) ([]byte, error)
|
||||||
|
|
||||||
|
// Put stores the data in the cache under the specified key.
|
||||||
|
// Underlying implementations may use any data storage format,
|
||||||
|
// as long as the reverse operation, Get, results in the original data.
|
||||||
|
Put(ctx context.Context, key string, data []byte) error
|
||||||
|
|
||||||
|
// Delete removes a certificate data from the cache under the specified key.
|
||||||
|
// If there's no such key in the cache, Delete returns nil.
|
||||||
|
Delete(ctx context.Context, key string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// DirCache implements Cache using a directory on the local filesystem.
|
||||||
|
// If the directory does not exist, it will be created with 0700 permissions.
|
||||||
|
type DirCache string
|
||||||
|
|
||||||
|
// Get reads a certificate data from the specified file name.
|
||||||
|
func (d DirCache) Get(ctx context.Context, name string) ([]byte, error) {
|
||||||
|
name = filepath.Join(string(d), name)
|
||||||
|
var (
|
||||||
|
data []byte
|
||||||
|
err error
|
||||||
|
done = make(chan struct{})
|
||||||
|
)
|
||||||
|
go func() {
|
||||||
|
data, err = ioutil.ReadFile(name)
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
case <-done:
|
||||||
|
}
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil, ErrCacheMiss
|
||||||
|
}
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put writes the certificate data to the specified file name.
|
||||||
|
// The file will be created with 0600 permissions.
|
||||||
|
func (d DirCache) Put(ctx context.Context, name string, data []byte) error {
|
||||||
|
if err := os.MkdirAll(string(d), 0700); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
done := make(chan struct{})
|
||||||
|
var err error
|
||||||
|
go func() {
|
||||||
|
defer close(done)
|
||||||
|
var tmp string
|
||||||
|
if tmp, err = d.writeTempFile(name, data); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
// Don't overwrite the file if the context was canceled.
|
||||||
|
default:
|
||||||
|
newName := filepath.Join(string(d), name)
|
||||||
|
err = os.Rename(tmp, newName)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
case <-done:
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete removes the specified file name.
|
||||||
|
func (d DirCache) Delete(ctx context.Context, name string) error {
|
||||||
|
name = filepath.Join(string(d), name)
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
done = make(chan struct{})
|
||||||
|
)
|
||||||
|
go func() {
|
||||||
|
err = os.Remove(name)
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
case <-done:
|
||||||
|
}
|
||||||
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeTempFile writes b to a temporary file, closes the file and returns its path.
|
||||||
|
func (d DirCache) writeTempFile(prefix string, b []byte) (string, error) {
|
||||||
|
// TempFile uses 0600 permissions
|
||||||
|
f, err := ioutil.TempFile(string(d), prefix)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if _, err := f.Write(b); err != nil {
|
||||||
|
f.Close()
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return f.Name(), f.Close()
|
||||||
|
}
|
||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package autocert
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// make sure DirCache satisfies Cache interface
|
||||||
|
var _ Cache = DirCache("/")
|
||||||
|
|
||||||
|
func TestDirCache(t *testing.T) {
|
||||||
|
dir, err := ioutil.TempDir("", "autocert")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
dir = filepath.Join(dir, "certs") // a nonexistent dir
|
||||||
|
cache := DirCache(dir)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// test cache miss
|
||||||
|
if _, err := cache.Get(ctx, "nonexistent"); err != ErrCacheMiss {
|
||||||
|
t.Errorf("get: %v; want ErrCacheMiss", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// test put/get
|
||||||
|
b1 := []byte{1}
|
||||||
|
if err := cache.Put(ctx, "dummy", b1); err != nil {
|
||||||
|
t.Fatalf("put: %v", err)
|
||||||
|
}
|
||||||
|
b2, err := cache.Get(ctx, "dummy")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("get: %v", err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(b1, b2) {
|
||||||
|
t.Errorf("b1 = %v; want %v", b1, b2)
|
||||||
|
}
|
||||||
|
name := filepath.Join(dir, "dummy")
|
||||||
|
if _, err := os.Stat(name); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// test delete
|
||||||
|
if err := cache.Delete(ctx, "dummy"); err != nil {
|
||||||
|
t.Fatalf("delete: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := cache.Get(ctx, "dummy"); err != ErrCacheMiss {
|
||||||
|
t.Errorf("get: %v; want ErrCacheMiss", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
// Copyright 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package autocert_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/acme/autocert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleNewListener() {
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Fprintf(w, "Hello, TLS user! Your config: %+v", r.TLS)
|
||||||
|
})
|
||||||
|
log.Fatal(http.Serve(autocert.NewListener("example.com"), mux))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleManager() {
|
||||||
|
m := autocert.Manager{
|
||||||
|
Prompt: autocert.AcceptTOS,
|
||||||
|
HostPolicy: autocert.HostWhitelist("example.org"),
|
||||||
|
}
|
||||||
|
s := &http.Server{
|
||||||
|
Addr: ":https",
|
||||||
|
TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},
|
||||||
|
}
|
||||||
|
s.ListenAndServeTLS("", "")
|
||||||
|
}
|
||||||
+160
@@ -0,0 +1,160 @@
|
|||||||
|
// Copyright 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package autocert
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewListener returns a net.Listener that listens on the standard TLS
|
||||||
|
// port (443) on all interfaces and returns *tls.Conn connections with
|
||||||
|
// LetsEncrypt certificates for the provided domain or domains.
|
||||||
|
//
|
||||||
|
// It enables one-line HTTPS servers:
|
||||||
|
//
|
||||||
|
// log.Fatal(http.Serve(autocert.NewListener("example.com"), handler))
|
||||||
|
//
|
||||||
|
// NewListener is a convenience function for a common configuration.
|
||||||
|
// More complex or custom configurations can use the autocert.Manager
|
||||||
|
// type instead.
|
||||||
|
//
|
||||||
|
// Use of this function implies acceptance of the LetsEncrypt Terms of
|
||||||
|
// Service. If domains is not empty, the provided domains are passed
|
||||||
|
// to HostWhitelist. If domains is empty, the listener will do
|
||||||
|
// LetsEncrypt challenges for any requested domain, which is not
|
||||||
|
// recommended.
|
||||||
|
//
|
||||||
|
// Certificates are cached in a "golang-autocert" directory under an
|
||||||
|
// operating system-specific cache or temp directory. This may not
|
||||||
|
// be suitable for servers spanning multiple machines.
|
||||||
|
//
|
||||||
|
// The returned listener uses a *tls.Config that enables HTTP/2, and
|
||||||
|
// should only be used with servers that support HTTP/2.
|
||||||
|
//
|
||||||
|
// The returned Listener also enables TCP keep-alives on the accepted
|
||||||
|
// connections. The returned *tls.Conn are returned before their TLS
|
||||||
|
// handshake has completed.
|
||||||
|
func NewListener(domains ...string) net.Listener {
|
||||||
|
m := &Manager{
|
||||||
|
Prompt: AcceptTOS,
|
||||||
|
}
|
||||||
|
if len(domains) > 0 {
|
||||||
|
m.HostPolicy = HostWhitelist(domains...)
|
||||||
|
}
|
||||||
|
dir := cacheDir()
|
||||||
|
if err := os.MkdirAll(dir, 0700); err != nil {
|
||||||
|
log.Printf("warning: autocert.NewListener not using a cache: %v", err)
|
||||||
|
} else {
|
||||||
|
m.Cache = DirCache(dir)
|
||||||
|
}
|
||||||
|
return m.Listener()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listener listens on the standard TLS port (443) on all interfaces
|
||||||
|
// and returns a net.Listener returning *tls.Conn connections.
|
||||||
|
//
|
||||||
|
// The returned listener uses a *tls.Config that enables HTTP/2, and
|
||||||
|
// should only be used with servers that support HTTP/2.
|
||||||
|
//
|
||||||
|
// The returned Listener also enables TCP keep-alives on the accepted
|
||||||
|
// connections. The returned *tls.Conn are returned before their TLS
|
||||||
|
// handshake has completed.
|
||||||
|
//
|
||||||
|
// Unlike NewListener, it is the caller's responsibility to initialize
|
||||||
|
// the Manager m's Prompt, Cache, HostPolicy, and other desired options.
|
||||||
|
func (m *Manager) Listener() net.Listener {
|
||||||
|
ln := &listener{
|
||||||
|
m: m,
|
||||||
|
conf: &tls.Config{
|
||||||
|
GetCertificate: m.GetCertificate, // bonus: panic on nil m
|
||||||
|
NextProtos: []string{"h2", "http/1.1"}, // Enable HTTP/2
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ln.tcpListener, ln.tcpListenErr = net.Listen("tcp", ":443")
|
||||||
|
return ln
|
||||||
|
}
|
||||||
|
|
||||||
|
type listener struct {
|
||||||
|
m *Manager
|
||||||
|
conf *tls.Config
|
||||||
|
|
||||||
|
tcpListener net.Listener
|
||||||
|
tcpListenErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ln *listener) Accept() (net.Conn, error) {
|
||||||
|
if ln.tcpListenErr != nil {
|
||||||
|
return nil, ln.tcpListenErr
|
||||||
|
}
|
||||||
|
conn, err := ln.tcpListener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tcpConn := conn.(*net.TCPConn)
|
||||||
|
|
||||||
|
// Because Listener is a convenience function, help out with
|
||||||
|
// this too. This is not possible for the caller to set once
|
||||||
|
// we return a *tcp.Conn wrapping an inaccessible net.Conn.
|
||||||
|
// If callers don't want this, they can do things the manual
|
||||||
|
// way and tweak as needed. But this is what net/http does
|
||||||
|
// itself, so copy that. If net/http changes, we can change
|
||||||
|
// here too.
|
||||||
|
tcpConn.SetKeepAlive(true)
|
||||||
|
tcpConn.SetKeepAlivePeriod(3 * time.Minute)
|
||||||
|
|
||||||
|
return tls.Server(tcpConn, ln.conf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ln *listener) Addr() net.Addr {
|
||||||
|
if ln.tcpListener != nil {
|
||||||
|
return ln.tcpListener.Addr()
|
||||||
|
}
|
||||||
|
// net.Listen failed. Return something non-nil in case callers
|
||||||
|
// call Addr before Accept:
|
||||||
|
return &net.TCPAddr{IP: net.IP{0, 0, 0, 0}, Port: 443}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ln *listener) Close() error {
|
||||||
|
if ln.tcpListenErr != nil {
|
||||||
|
return ln.tcpListenErr
|
||||||
|
}
|
||||||
|
return ln.tcpListener.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func homeDir() string {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
return os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
|
||||||
|
}
|
||||||
|
if h := os.Getenv("HOME"); h != "" {
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
return "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
func cacheDir() string {
|
||||||
|
const base = "golang-autocert"
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "darwin":
|
||||||
|
return filepath.Join(homeDir(), "Library", "Caches", base)
|
||||||
|
case "windows":
|
||||||
|
for _, ev := range []string{"APPDATA", "CSIDL_APPDATA", "TEMP", "TMP"} {
|
||||||
|
if v := os.Getenv(ev); v != "" {
|
||||||
|
return filepath.Join(v, base)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Worst case:
|
||||||
|
return filepath.Join(homeDir(), base)
|
||||||
|
}
|
||||||
|
if xdg := os.Getenv("XDG_CACHE_HOME"); xdg != "" {
|
||||||
|
return filepath.Join(xdg, base)
|
||||||
|
}
|
||||||
|
return filepath.Join(homeDir(), ".cache", base)
|
||||||
|
}
|
||||||
+124
@@ -0,0 +1,124 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package autocert
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// renewJitter is the maximum deviation from Manager.RenewBefore.
|
||||||
|
const renewJitter = time.Hour
|
||||||
|
|
||||||
|
// domainRenewal tracks the state used by the periodic timers
|
||||||
|
// renewing a single domain's cert.
|
||||||
|
type domainRenewal struct {
|
||||||
|
m *Manager
|
||||||
|
domain string
|
||||||
|
key crypto.Signer
|
||||||
|
|
||||||
|
timerMu sync.Mutex
|
||||||
|
timer *time.Timer
|
||||||
|
}
|
||||||
|
|
||||||
|
// start starts a cert renewal timer at the time
|
||||||
|
// defined by the certificate expiration time exp.
|
||||||
|
//
|
||||||
|
// If the timer is already started, calling start is a noop.
|
||||||
|
func (dr *domainRenewal) start(exp time.Time) {
|
||||||
|
dr.timerMu.Lock()
|
||||||
|
defer dr.timerMu.Unlock()
|
||||||
|
if dr.timer != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dr.timer = time.AfterFunc(dr.next(exp), dr.renew)
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop stops the cert renewal timer.
|
||||||
|
// If the timer is already stopped, calling stop is a noop.
|
||||||
|
func (dr *domainRenewal) stop() {
|
||||||
|
dr.timerMu.Lock()
|
||||||
|
defer dr.timerMu.Unlock()
|
||||||
|
if dr.timer == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dr.timer.Stop()
|
||||||
|
dr.timer = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// renew is called periodically by a timer.
|
||||||
|
// The first renew call is kicked off by dr.start.
|
||||||
|
func (dr *domainRenewal) renew() {
|
||||||
|
dr.timerMu.Lock()
|
||||||
|
defer dr.timerMu.Unlock()
|
||||||
|
if dr.timer == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
|
||||||
|
defer cancel()
|
||||||
|
// TODO: rotate dr.key at some point?
|
||||||
|
next, err := dr.do(ctx)
|
||||||
|
if err != nil {
|
||||||
|
next = renewJitter / 2
|
||||||
|
next += time.Duration(pseudoRand.int63n(int64(next)))
|
||||||
|
}
|
||||||
|
dr.timer = time.AfterFunc(next, dr.renew)
|
||||||
|
testDidRenewLoop(next, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// do is similar to Manager.createCert but it doesn't lock a Manager.state item.
|
||||||
|
// Instead, it requests a new certificate independently and, upon success,
|
||||||
|
// replaces dr.m.state item with a new one and updates cache for the given domain.
|
||||||
|
//
|
||||||
|
// It may return immediately if the expiration date of the currently cached cert
|
||||||
|
// is far enough in the future.
|
||||||
|
//
|
||||||
|
// The returned value is a time interval after which the renewal should occur again.
|
||||||
|
func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) {
|
||||||
|
// a race is likely unavoidable in a distributed environment
|
||||||
|
// but we try nonetheless
|
||||||
|
if tlscert, err := dr.m.cacheGet(ctx, dr.domain); err == nil {
|
||||||
|
next := dr.next(tlscert.Leaf.NotAfter)
|
||||||
|
if next > dr.m.renewBefore()+renewJitter {
|
||||||
|
return next, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
der, leaf, err := dr.m.authorizedCert(ctx, dr.key, dr.domain)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
state := &certState{
|
||||||
|
key: dr.key,
|
||||||
|
cert: der,
|
||||||
|
leaf: leaf,
|
||||||
|
}
|
||||||
|
tlscert, err := state.tlscert()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
dr.m.cachePut(ctx, dr.domain, tlscert)
|
||||||
|
dr.m.stateMu.Lock()
|
||||||
|
defer dr.m.stateMu.Unlock()
|
||||||
|
// m.state is guaranteed to be non-nil at this point
|
||||||
|
dr.m.state[dr.domain] = state
|
||||||
|
return dr.next(leaf.NotAfter), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dr *domainRenewal) next(expiry time.Time) time.Duration {
|
||||||
|
d := expiry.Sub(timeNow()) - dr.m.renewBefore()
|
||||||
|
// add a bit of randomness to renew deadline
|
||||||
|
n := pseudoRand.int63n(int64(renewJitter))
|
||||||
|
d -= time.Duration(n)
|
||||||
|
if d < 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
var testDidRenewLoop = func(next time.Duration, err error) {}
|
||||||
+191
@@ -0,0 +1,191 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package autocert
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/acme"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRenewalNext(t *testing.T) {
|
||||||
|
now := time.Now()
|
||||||
|
timeNow = func() time.Time { return now }
|
||||||
|
defer func() { timeNow = time.Now }()
|
||||||
|
|
||||||
|
man := &Manager{RenewBefore: 7 * 24 * time.Hour}
|
||||||
|
defer man.stopRenew()
|
||||||
|
tt := []struct {
|
||||||
|
expiry time.Time
|
||||||
|
min, max time.Duration
|
||||||
|
}{
|
||||||
|
{now.Add(90 * 24 * time.Hour), 83*24*time.Hour - renewJitter, 83 * 24 * time.Hour},
|
||||||
|
{now.Add(time.Hour), 0, 1},
|
||||||
|
{now, 0, 1},
|
||||||
|
{now.Add(-time.Hour), 0, 1},
|
||||||
|
}
|
||||||
|
|
||||||
|
dr := &domainRenewal{m: man}
|
||||||
|
for i, test := range tt {
|
||||||
|
next := dr.next(test.expiry)
|
||||||
|
if next < test.min || test.max < next {
|
||||||
|
t.Errorf("%d: next = %v; want between %v and %v", i, next, test.min, test.max)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRenewFromCache(t *testing.T) {
|
||||||
|
const domain = "example.org"
|
||||||
|
|
||||||
|
// ACME CA server stub
|
||||||
|
var ca *httptest.Server
|
||||||
|
ca = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Replay-Nonce", "nonce")
|
||||||
|
if r.Method == "HEAD" {
|
||||||
|
// a nonce request
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch r.URL.Path {
|
||||||
|
// discovery
|
||||||
|
case "/":
|
||||||
|
if err := discoTmpl.Execute(w, ca.URL); err != nil {
|
||||||
|
t.Fatalf("discoTmpl: %v", err)
|
||||||
|
}
|
||||||
|
// client key registration
|
||||||
|
case "/new-reg":
|
||||||
|
w.Write([]byte("{}"))
|
||||||
|
// domain authorization
|
||||||
|
case "/new-authz":
|
||||||
|
w.Header().Set("Location", ca.URL+"/authz/1")
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
w.Write([]byte(`{"status": "valid"}`))
|
||||||
|
// cert request
|
||||||
|
case "/new-cert":
|
||||||
|
var req struct {
|
||||||
|
CSR string `json:"csr"`
|
||||||
|
}
|
||||||
|
decodePayload(&req, r.Body)
|
||||||
|
b, _ := base64.RawURLEncoding.DecodeString(req.CSR)
|
||||||
|
csr, err := x509.ParseCertificateRequest(b)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("new-cert: CSR: %v", err)
|
||||||
|
}
|
||||||
|
der, err := dummyCert(csr.PublicKey, domain)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("new-cert: dummyCert: %v", err)
|
||||||
|
}
|
||||||
|
chainUp := fmt.Sprintf("<%s/ca-cert>; rel=up", ca.URL)
|
||||||
|
w.Header().Set("Link", chainUp)
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
w.Write(der)
|
||||||
|
// CA chain cert
|
||||||
|
case "/ca-cert":
|
||||||
|
der, err := dummyCert(nil, "ca")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ca-cert: dummyCert: %v", err)
|
||||||
|
}
|
||||||
|
w.Write(der)
|
||||||
|
default:
|
||||||
|
t.Errorf("unrecognized r.URL.Path: %s", r.URL.Path)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
defer ca.Close()
|
||||||
|
|
||||||
|
// use EC key to run faster on 386
|
||||||
|
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
man := &Manager{
|
||||||
|
Prompt: AcceptTOS,
|
||||||
|
Cache: newMemCache(),
|
||||||
|
RenewBefore: 24 * time.Hour,
|
||||||
|
Client: &acme.Client{
|
||||||
|
Key: key,
|
||||||
|
DirectoryURL: ca.URL,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
defer man.stopRenew()
|
||||||
|
|
||||||
|
// cache an almost expired cert
|
||||||
|
now := time.Now()
|
||||||
|
cert, err := dateDummyCert(key.Public(), now.Add(-2*time.Hour), now.Add(time.Minute), domain)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
tlscert := &tls.Certificate{PrivateKey: key, Certificate: [][]byte{cert}}
|
||||||
|
if err := man.cachePut(context.Background(), domain, tlscert); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// veriy the renewal happened
|
||||||
|
defer func() {
|
||||||
|
testDidRenewLoop = func(next time.Duration, err error) {}
|
||||||
|
}()
|
||||||
|
done := make(chan struct{})
|
||||||
|
testDidRenewLoop = func(next time.Duration, err error) {
|
||||||
|
defer close(done)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("testDidRenewLoop: %v", err)
|
||||||
|
}
|
||||||
|
// Next should be about 90 days:
|
||||||
|
// dummyCert creates 90days expiry + account for man.RenewBefore.
|
||||||
|
// Previous expiration was within 1 min.
|
||||||
|
future := 88 * 24 * time.Hour
|
||||||
|
if next < future {
|
||||||
|
t.Errorf("testDidRenewLoop: next = %v; want >= %v", next, future)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure the new cert is cached
|
||||||
|
after := time.Now().Add(future)
|
||||||
|
tlscert, err := man.cacheGet(context.Background(), domain)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("man.cacheGet: %v", err)
|
||||||
|
}
|
||||||
|
if !tlscert.Leaf.NotAfter.After(after) {
|
||||||
|
t.Errorf("cache leaf.NotAfter = %v; want > %v", tlscert.Leaf.NotAfter, after)
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify the old cert is also replaced in memory
|
||||||
|
man.stateMu.Lock()
|
||||||
|
defer man.stateMu.Unlock()
|
||||||
|
s := man.state[domain]
|
||||||
|
if s == nil {
|
||||||
|
t.Fatalf("m.state[%q] is nil", domain)
|
||||||
|
}
|
||||||
|
tlscert, err = s.tlscert()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("s.tlscert: %v", err)
|
||||||
|
}
|
||||||
|
if !tlscert.Leaf.NotAfter.After(after) {
|
||||||
|
t.Errorf("state leaf.NotAfter = %v; want > %v", tlscert.Leaf.NotAfter, after)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// trigger renew
|
||||||
|
hello := &tls.ClientHelloInfo{ServerName: domain}
|
||||||
|
if _, err := man.GetCertificate(hello); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for renew loop
|
||||||
|
select {
|
||||||
|
case <-time.After(10 * time.Second):
|
||||||
|
t.Fatal("renew took too long to occur")
|
||||||
|
case <-done:
|
||||||
|
}
|
||||||
|
}
|
||||||
+153
@@ -0,0 +1,153 @@
|
|||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha256"
|
||||||
|
_ "crypto/sha512" // need for EC keys
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
// jwsEncodeJSON signs claimset using provided key and a nonce.
|
||||||
|
// The result is serialized in JSON format.
|
||||||
|
// See https://tools.ietf.org/html/rfc7515#section-7.
|
||||||
|
func jwsEncodeJSON(claimset interface{}, key crypto.Signer, nonce string) ([]byte, error) {
|
||||||
|
jwk, err := jwkEncode(key.Public())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
alg, sha := jwsHasher(key)
|
||||||
|
if alg == "" || !sha.Available() {
|
||||||
|
return nil, ErrUnsupportedKey
|
||||||
|
}
|
||||||
|
phead := fmt.Sprintf(`{"alg":%q,"jwk":%s,"nonce":%q}`, alg, jwk, nonce)
|
||||||
|
phead = base64.RawURLEncoding.EncodeToString([]byte(phead))
|
||||||
|
cs, err := json.Marshal(claimset)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
payload := base64.RawURLEncoding.EncodeToString(cs)
|
||||||
|
hash := sha.New()
|
||||||
|
hash.Write([]byte(phead + "." + payload))
|
||||||
|
sig, err := jwsSign(key, sha, hash.Sum(nil))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
enc := struct {
|
||||||
|
Protected string `json:"protected"`
|
||||||
|
Payload string `json:"payload"`
|
||||||
|
Sig string `json:"signature"`
|
||||||
|
}{
|
||||||
|
Protected: phead,
|
||||||
|
Payload: payload,
|
||||||
|
Sig: base64.RawURLEncoding.EncodeToString(sig),
|
||||||
|
}
|
||||||
|
return json.Marshal(&enc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// jwkEncode encodes public part of an RSA or ECDSA key into a JWK.
|
||||||
|
// The result is also suitable for creating a JWK thumbprint.
|
||||||
|
// https://tools.ietf.org/html/rfc7517
|
||||||
|
func jwkEncode(pub crypto.PublicKey) (string, error) {
|
||||||
|
switch pub := pub.(type) {
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
// https://tools.ietf.org/html/rfc7518#section-6.3.1
|
||||||
|
n := pub.N
|
||||||
|
e := big.NewInt(int64(pub.E))
|
||||||
|
// Field order is important.
|
||||||
|
// See https://tools.ietf.org/html/rfc7638#section-3.3 for details.
|
||||||
|
return fmt.Sprintf(`{"e":"%s","kty":"RSA","n":"%s"}`,
|
||||||
|
base64.RawURLEncoding.EncodeToString(e.Bytes()),
|
||||||
|
base64.RawURLEncoding.EncodeToString(n.Bytes()),
|
||||||
|
), nil
|
||||||
|
case *ecdsa.PublicKey:
|
||||||
|
// https://tools.ietf.org/html/rfc7518#section-6.2.1
|
||||||
|
p := pub.Curve.Params()
|
||||||
|
n := p.BitSize / 8
|
||||||
|
if p.BitSize%8 != 0 {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
x := pub.X.Bytes()
|
||||||
|
if n > len(x) {
|
||||||
|
x = append(make([]byte, n-len(x)), x...)
|
||||||
|
}
|
||||||
|
y := pub.Y.Bytes()
|
||||||
|
if n > len(y) {
|
||||||
|
y = append(make([]byte, n-len(y)), y...)
|
||||||
|
}
|
||||||
|
// Field order is important.
|
||||||
|
// See https://tools.ietf.org/html/rfc7638#section-3.3 for details.
|
||||||
|
return fmt.Sprintf(`{"crv":"%s","kty":"EC","x":"%s","y":"%s"}`,
|
||||||
|
p.Name,
|
||||||
|
base64.RawURLEncoding.EncodeToString(x),
|
||||||
|
base64.RawURLEncoding.EncodeToString(y),
|
||||||
|
), nil
|
||||||
|
}
|
||||||
|
return "", ErrUnsupportedKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// jwsSign signs the digest using the given key.
|
||||||
|
// It returns ErrUnsupportedKey if the key type is unknown.
|
||||||
|
// The hash is used only for RSA keys.
|
||||||
|
func jwsSign(key crypto.Signer, hash crypto.Hash, digest []byte) ([]byte, error) {
|
||||||
|
switch key := key.(type) {
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
return key.Sign(rand.Reader, digest, hash)
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
r, s, err := ecdsa.Sign(rand.Reader, key, digest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rb, sb := r.Bytes(), s.Bytes()
|
||||||
|
size := key.Params().BitSize / 8
|
||||||
|
if size%8 > 0 {
|
||||||
|
size++
|
||||||
|
}
|
||||||
|
sig := make([]byte, size*2)
|
||||||
|
copy(sig[size-len(rb):], rb)
|
||||||
|
copy(sig[size*2-len(sb):], sb)
|
||||||
|
return sig, nil
|
||||||
|
}
|
||||||
|
return nil, ErrUnsupportedKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// jwsHasher indicates suitable JWS algorithm name and a hash function
|
||||||
|
// to use for signing a digest with the provided key.
|
||||||
|
// It returns ("", 0) if the key is not supported.
|
||||||
|
func jwsHasher(key crypto.Signer) (string, crypto.Hash) {
|
||||||
|
switch key := key.(type) {
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
return "RS256", crypto.SHA256
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
switch key.Params().Name {
|
||||||
|
case "P-256":
|
||||||
|
return "ES256", crypto.SHA256
|
||||||
|
case "P-384":
|
||||||
|
return "ES384", crypto.SHA384
|
||||||
|
case "P-521":
|
||||||
|
return "ES512", crypto.SHA512
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// JWKThumbprint creates a JWK thumbprint out of pub
|
||||||
|
// as specified in https://tools.ietf.org/html/rfc7638.
|
||||||
|
func JWKThumbprint(pub crypto.PublicKey) (string, error) {
|
||||||
|
jwk, err := jwkEncode(pub)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
b := sha256.Sum256([]byte(jwk))
|
||||||
|
return base64.RawURLEncoding.EncodeToString(b[:]), nil
|
||||||
|
}
|
||||||
+319
@@ -0,0 +1,319 @@
|
|||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
testKeyPEM = `
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEowIBAAKCAQEA4xgZ3eRPkwoRvy7qeRUbmMDe0V+xH9eWLdu0iheeLlrmD2mq
|
||||||
|
WXfP9IeSKApbn34g8TuAS9g5zhq8ELQ3kmjr+KV86GAMgI6VAcGlq3QrzpTCf/30
|
||||||
|
Ab7+zawrfRaFONa1HwEzPY1KHnGVkxJc85gNkwYI9SY2RHXtvln3zs5wITNrdosq
|
||||||
|
EXeaIkVYBEhbhNu54pp3kxo6TuWLi9e6pXeWetEwmlBwtWZlPoib2j3TxLBksKZf
|
||||||
|
oyFyek380mHgJAumQ/I2fjj98/97mk3ihOY4AgVdCDj1z/GCoZkG5Rq7nbCGyosy
|
||||||
|
KWyDX00Zs+nNqVhoLeIvXC4nnWdJMZ6rogxyQQIDAQABAoIBACIEZTOI1Kao9nmV
|
||||||
|
9IeIsuaR1Y61b9neOF/MLmIVIZu+AAJFCMB4Iw11FV6sFodwpEyeZhx2WkpWVN+H
|
||||||
|
r19eGiLX3zsL0DOdqBJoSIHDWCCMxgnYJ6nvS0nRxX3qVrBp8R2g12Ub+gNPbmFm
|
||||||
|
ecf/eeERIVxfifd9VsyRu34eDEvcmKFuLYbElFcPh62xE3x12UZvV/sN7gXbawpP
|
||||||
|
G+w255vbE5MoaKdnnO83cTFlcHvhn24M/78qP7Te5OAeelr1R89kYxQLpuGe4fbS
|
||||||
|
zc6E3ym5Td6urDetGGrSY1Eu10/8sMusX+KNWkm+RsBRbkyKq72ks/qKpOxOa+c6
|
||||||
|
9gm+Y8ECgYEA/iNUyg1ubRdH11p82l8KHtFC1DPE0V1gSZsX29TpM5jS4qv46K+s
|
||||||
|
8Ym1zmrORM8x+cynfPx1VQZQ34EYeCMIX212ryJ+zDATl4NE0I4muMvSiH9vx6Xc
|
||||||
|
7FmhNnaYzPsBL5Tm9nmtQuP09YEn8poiOJFiDs/4olnD5ogA5O4THGkCgYEA5MIL
|
||||||
|
qWYBUuqbEWLRtMruUtpASclrBqNNsJEsMGbeqBJmoMxdHeSZckbLOrqm7GlMyNRJ
|
||||||
|
Ne/5uWRGSzaMYuGmwsPpERzqEvYFnSrpjW5YtXZ+JtxFXNVfm9Z1gLLgvGpOUCIU
|
||||||
|
RbpoDckDe1vgUuk3y5+DjZihs+rqIJ45XzXTzBkCgYBWuf3segruJZy5rEKhTv+o
|
||||||
|
JqeUvRn0jNYYKFpLBeyTVBrbie6GkbUGNIWbrK05pC+c3K9nosvzuRUOQQL1tJbd
|
||||||
|
4gA3oiD9U4bMFNr+BRTHyZ7OQBcIXdz3t1qhuHVKtnngIAN1p25uPlbRFUNpshnt
|
||||||
|
jgeVoHlsBhApcs5DUc+pyQKBgDzeHPg/+g4z+nrPznjKnktRY1W+0El93kgi+J0Q
|
||||||
|
YiJacxBKEGTJ1MKBb8X6sDurcRDm22wMpGfd9I5Cv2v4GsUsF7HD/cx5xdih+G73
|
||||||
|
c4clNj/k0Ff5Nm1izPUno4C+0IOl7br39IPmfpSuR6wH/h6iHQDqIeybjxyKvT1G
|
||||||
|
N0rRAoGBAKGD+4ZI/E1MoJ5CXB8cDDMHagbE3cq/DtmYzE2v1DFpQYu5I4PCm5c7
|
||||||
|
EQeIP6dZtv8IMgtGIb91QX9pXvP0aznzQKwYIA8nZgoENCPfiMTPiEDT9e/0lObO
|
||||||
|
9XWsXpbSTsRPj0sv1rB+UzBJ0PgjK4q2zOF0sNo7b1+6nlM3BWPx
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
|
`
|
||||||
|
|
||||||
|
// This thumbprint is for the testKey defined above.
|
||||||
|
testKeyThumbprint = "6nicxzh6WETQlrvdchkz-U3e3DOQZ4heJKU63rfqMqQ"
|
||||||
|
|
||||||
|
// openssl ecparam -name secp256k1 -genkey -noout
|
||||||
|
testKeyECPEM = `
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
MHcCAQEEIK07hGLr0RwyUdYJ8wbIiBS55CjnkMD23DWr+ccnypWLoAoGCCqGSM49
|
||||||
|
AwEHoUQDQgAE5lhEug5xK4xBDZ2nAbaxLtaLiv85bxJ7ePd1dkO23HThqIrvawF5
|
||||||
|
QAaS/RNouybCiRhRjI3EaxLkQwgrCw0gqQ==
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
|
`
|
||||||
|
// openssl ecparam -name secp384r1 -genkey -noout
|
||||||
|
testKeyEC384PEM = `
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
MIGkAgEBBDAQ4lNtXRORWr1bgKR1CGysr9AJ9SyEk4jiVnlUWWUChmSNL+i9SLSD
|
||||||
|
Oe/naPqXJ6CgBwYFK4EEACKhZANiAAQzKtj+Ms0vHoTX5dzv3/L5YMXOWuI5UKRj
|
||||||
|
JigpahYCqXD2BA1j0E/2xt5vlPf+gm0PL+UHSQsCokGnIGuaHCsJAp3ry0gHQEke
|
||||||
|
WYXapUUFdvaK1R2/2hn5O+eiQM8YzCg=
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
|
`
|
||||||
|
// openssl ecparam -name secp521r1 -genkey -noout
|
||||||
|
testKeyEC512PEM = `
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
MIHcAgEBBEIBSNZKFcWzXzB/aJClAb305ibalKgtDA7+70eEkdPt28/3LZMM935Z
|
||||||
|
KqYHh/COcxuu3Kt8azRAUz3gyr4zZKhlKUSgBwYFK4EEACOhgYkDgYYABAHUNKbx
|
||||||
|
7JwC7H6pa2sV0tERWhHhB3JmW+OP6SUgMWryvIKajlx73eS24dy4QPGrWO9/ABsD
|
||||||
|
FqcRSkNVTXnIv6+0mAF25knqIBIg5Q8M9BnOu9GGAchcwt3O7RDHmqewnJJDrbjd
|
||||||
|
GGnm6rb+NnWR9DIopM0nKNkToWoF/hzopxu4Ae/GsQ==
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
|
`
|
||||||
|
// 1. openssl ec -in key.pem -noout -text
|
||||||
|
// 2. remove first byte, 04 (the header); the rest is X and Y
|
||||||
|
// 3. convert each with: echo <val> | xxd -r -p | base64 -w 100 | tr -d '=' | tr '/+' '_-'
|
||||||
|
testKeyECPubX = "5lhEug5xK4xBDZ2nAbaxLtaLiv85bxJ7ePd1dkO23HQ"
|
||||||
|
testKeyECPubY = "4aiK72sBeUAGkv0TaLsmwokYUYyNxGsS5EMIKwsNIKk"
|
||||||
|
testKeyEC384PubX = "MyrY_jLNLx6E1-Xc79_y-WDFzlriOVCkYyYoKWoWAqlw9gQNY9BP9sbeb5T3_oJt"
|
||||||
|
testKeyEC384PubY = "Dy_lB0kLAqJBpyBrmhwrCQKd68tIB0BJHlmF2qVFBXb2itUdv9oZ-TvnokDPGMwo"
|
||||||
|
testKeyEC512PubX = "AdQ0pvHsnALsfqlraxXS0RFaEeEHcmZb44_pJSAxavK8gpqOXHvd5Lbh3LhA8atY738AGwMWpxFKQ1VNeci_r7SY"
|
||||||
|
testKeyEC512PubY = "AXbmSeogEiDlDwz0Gc670YYByFzC3c7tEMeap7CckkOtuN0Yaebqtv42dZH0MiikzSco2ROhagX-HOinG7gB78ax"
|
||||||
|
|
||||||
|
// echo -n '{"crv":"P-256","kty":"EC","x":"<testKeyECPubX>","y":"<testKeyECPubY>"}' | \
|
||||||
|
// openssl dgst -binary -sha256 | base64 | tr -d '=' | tr '/+' '_-'
|
||||||
|
testKeyECThumbprint = "zedj-Bd1Zshp8KLePv2MB-lJ_Hagp7wAwdkA0NUTniU"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
testKey *rsa.PrivateKey
|
||||||
|
testKeyEC *ecdsa.PrivateKey
|
||||||
|
testKeyEC384 *ecdsa.PrivateKey
|
||||||
|
testKeyEC512 *ecdsa.PrivateKey
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
testKey = parseRSA(testKeyPEM, "testKeyPEM")
|
||||||
|
testKeyEC = parseEC(testKeyECPEM, "testKeyECPEM")
|
||||||
|
testKeyEC384 = parseEC(testKeyEC384PEM, "testKeyEC384PEM")
|
||||||
|
testKeyEC512 = parseEC(testKeyEC512PEM, "testKeyEC512PEM")
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodePEM(s, name string) []byte {
|
||||||
|
d, _ := pem.Decode([]byte(s))
|
||||||
|
if d == nil {
|
||||||
|
panic("no block found in " + name)
|
||||||
|
}
|
||||||
|
return d.Bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseRSA(s, name string) *rsa.PrivateKey {
|
||||||
|
b := decodePEM(s, name)
|
||||||
|
k, err := x509.ParsePKCS1PrivateKey(b)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("%s: %v", name, err))
|
||||||
|
}
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseEC(s, name string) *ecdsa.PrivateKey {
|
||||||
|
b := decodePEM(s, name)
|
||||||
|
k, err := x509.ParseECPrivateKey(b)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("%s: %v", name, err))
|
||||||
|
}
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJWSEncodeJSON(t *testing.T) {
|
||||||
|
claims := struct{ Msg string }{"Hello JWS"}
|
||||||
|
// JWS signed with testKey and "nonce" as the nonce value
|
||||||
|
// JSON-serialized JWS fields are split for easier testing
|
||||||
|
const (
|
||||||
|
// {"alg":"RS256","jwk":{"e":"AQAB","kty":"RSA","n":"..."},"nonce":"nonce"}
|
||||||
|
protected = "eyJhbGciOiJSUzI1NiIsImp3ayI6eyJlIjoiQVFBQiIsImt0eSI6" +
|
||||||
|
"IlJTQSIsIm4iOiI0eGdaM2VSUGt3b1J2eTdxZVJVYm1NRGUwVi14" +
|
||||||
|
"SDllV0xkdTBpaGVlTGxybUQybXFXWGZQOUllU0tBcGJuMzRnOFR1" +
|
||||||
|
"QVM5ZzV6aHE4RUxRM2ttanItS1Y4NkdBTWdJNlZBY0dscTNRcnpw" +
|
||||||
|
"VENmXzMwQWI3LXphd3JmUmFGT05hMUh3RXpQWTFLSG5HVmt4SmM4" +
|
||||||
|
"NWdOa3dZSTlTWTJSSFh0dmxuM3pzNXdJVE5yZG9zcUVYZWFJa1ZZ" +
|
||||||
|
"QkVoYmhOdTU0cHAza3hvNlR1V0xpOWU2cFhlV2V0RXdtbEJ3dFda" +
|
||||||
|
"bFBvaWIyajNUeExCa3NLWmZveUZ5ZWszODBtSGdKQXVtUV9JMmZq" +
|
||||||
|
"ajk4Xzk3bWszaWhPWTRBZ1ZkQ0RqMXpfR0NvWmtHNVJxN25iQ0d5" +
|
||||||
|
"b3N5S1d5RFgwMFpzLW5OcVZob0xlSXZYQzRubldkSk1aNnJvZ3h5" +
|
||||||
|
"UVEifSwibm9uY2UiOiJub25jZSJ9"
|
||||||
|
// {"Msg":"Hello JWS"}
|
||||||
|
payload = "eyJNc2ciOiJIZWxsbyBKV1MifQ"
|
||||||
|
signature = "eAGUikStX_UxyiFhxSLMyuyBcIB80GeBkFROCpap2sW3EmkU_ggF" +
|
||||||
|
"knaQzxrTfItICSAXsCLIquZ5BbrSWA_4vdEYrwWtdUj7NqFKjHRa" +
|
||||||
|
"zpLHcoR7r1rEHvkoP1xj49lS5fc3Wjjq8JUhffkhGbWZ8ZVkgPdC" +
|
||||||
|
"4tMBWiQDoth-x8jELP_3LYOB_ScUXi2mETBawLgOT2K8rA0Vbbmx" +
|
||||||
|
"hWNlOWuUf-8hL5YX4IOEwsS8JK_TrTq5Zc9My0zHJmaieqDV0UlP" +
|
||||||
|
"k0onFjPFkGm7MrPSgd0MqRG-4vSAg2O4hDo7rKv4n8POjjXlNQvM" +
|
||||||
|
"9IPLr8qZ7usYBKhEGwX3yq_eicAwBw"
|
||||||
|
)
|
||||||
|
|
||||||
|
b, err := jwsEncodeJSON(claims, testKey, "nonce")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var jws struct{ Protected, Payload, Signature string }
|
||||||
|
if err := json.Unmarshal(b, &jws); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if jws.Protected != protected {
|
||||||
|
t.Errorf("protected:\n%s\nwant:\n%s", jws.Protected, protected)
|
||||||
|
}
|
||||||
|
if jws.Payload != payload {
|
||||||
|
t.Errorf("payload:\n%s\nwant:\n%s", jws.Payload, payload)
|
||||||
|
}
|
||||||
|
if jws.Signature != signature {
|
||||||
|
t.Errorf("signature:\n%s\nwant:\n%s", jws.Signature, signature)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJWSEncodeJSONEC(t *testing.T) {
|
||||||
|
tt := []struct {
|
||||||
|
key *ecdsa.PrivateKey
|
||||||
|
x, y string
|
||||||
|
alg, crv string
|
||||||
|
}{
|
||||||
|
{testKeyEC, testKeyECPubX, testKeyECPubY, "ES256", "P-256"},
|
||||||
|
{testKeyEC384, testKeyEC384PubX, testKeyEC384PubY, "ES384", "P-384"},
|
||||||
|
{testKeyEC512, testKeyEC512PubX, testKeyEC512PubY, "ES512", "P-521"},
|
||||||
|
}
|
||||||
|
for i, test := range tt {
|
||||||
|
claims := struct{ Msg string }{"Hello JWS"}
|
||||||
|
b, err := jwsEncodeJSON(claims, test.key, "nonce")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%d: %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var jws struct{ Protected, Payload, Signature string }
|
||||||
|
if err := json.Unmarshal(b, &jws); err != nil {
|
||||||
|
t.Errorf("%d: %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err = base64.RawURLEncoding.DecodeString(jws.Protected)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%d: jws.Protected: %v", i, err)
|
||||||
|
}
|
||||||
|
var head struct {
|
||||||
|
Alg string
|
||||||
|
Nonce string
|
||||||
|
JWK struct {
|
||||||
|
Crv string
|
||||||
|
Kty string
|
||||||
|
X string
|
||||||
|
Y string
|
||||||
|
} `json:"jwk"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(b, &head); err != nil {
|
||||||
|
t.Errorf("%d: jws.Protected: %v", i, err)
|
||||||
|
}
|
||||||
|
if head.Alg != test.alg {
|
||||||
|
t.Errorf("%d: head.Alg = %q; want %q", i, head.Alg, test.alg)
|
||||||
|
}
|
||||||
|
if head.Nonce != "nonce" {
|
||||||
|
t.Errorf("%d: head.Nonce = %q; want nonce", i, head.Nonce)
|
||||||
|
}
|
||||||
|
if head.JWK.Crv != test.crv {
|
||||||
|
t.Errorf("%d: head.JWK.Crv = %q; want %q", i, head.JWK.Crv, test.crv)
|
||||||
|
}
|
||||||
|
if head.JWK.Kty != "EC" {
|
||||||
|
t.Errorf("%d: head.JWK.Kty = %q; want EC", i, head.JWK.Kty)
|
||||||
|
}
|
||||||
|
if head.JWK.X != test.x {
|
||||||
|
t.Errorf("%d: head.JWK.X = %q; want %q", i, head.JWK.X, test.x)
|
||||||
|
}
|
||||||
|
if head.JWK.Y != test.y {
|
||||||
|
t.Errorf("%d: head.JWK.Y = %q; want %q", i, head.JWK.Y, test.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJWKThumbprintRSA(t *testing.T) {
|
||||||
|
// Key example from RFC 7638
|
||||||
|
const base64N = "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAt" +
|
||||||
|
"VT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn6" +
|
||||||
|
"4tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FD" +
|
||||||
|
"W2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n9" +
|
||||||
|
"1CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINH" +
|
||||||
|
"aQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw"
|
||||||
|
const base64E = "AQAB"
|
||||||
|
const expected = "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs"
|
||||||
|
|
||||||
|
b, err := base64.RawURLEncoding.DecodeString(base64N)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error parsing example key N: %v", err)
|
||||||
|
}
|
||||||
|
n := new(big.Int).SetBytes(b)
|
||||||
|
|
||||||
|
b, err = base64.RawURLEncoding.DecodeString(base64E)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error parsing example key E: %v", err)
|
||||||
|
}
|
||||||
|
e := new(big.Int).SetBytes(b)
|
||||||
|
|
||||||
|
pub := &rsa.PublicKey{N: n, E: int(e.Uint64())}
|
||||||
|
th, err := JWKThumbprint(pub)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if th != expected {
|
||||||
|
t.Errorf("thumbprint = %q; want %q", th, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJWKThumbprintEC(t *testing.T) {
|
||||||
|
// Key example from RFC 7520
|
||||||
|
// expected was computed with
|
||||||
|
// echo -n '{"crv":"P-521","kty":"EC","x":"<base64X>","y":"<base64Y>"}' | \
|
||||||
|
// openssl dgst -binary -sha256 | \
|
||||||
|
// base64 | \
|
||||||
|
// tr -d '=' | tr '/+' '_-'
|
||||||
|
const (
|
||||||
|
base64X = "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9A5RkT" +
|
||||||
|
"KqjqvjyekWF-7ytDyRXYgCF5cj0Kt"
|
||||||
|
base64Y = "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVySsUda" +
|
||||||
|
"QkAgDPrwQrJmbnX9cwlGfP-HqHZR1"
|
||||||
|
expected = "dHri3SADZkrush5HU_50AoRhcKFryN-PI6jPBtPL55M"
|
||||||
|
)
|
||||||
|
|
||||||
|
b, err := base64.RawURLEncoding.DecodeString(base64X)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error parsing example key X: %v", err)
|
||||||
|
}
|
||||||
|
x := new(big.Int).SetBytes(b)
|
||||||
|
|
||||||
|
b, err = base64.RawURLEncoding.DecodeString(base64Y)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error parsing example key Y: %v", err)
|
||||||
|
}
|
||||||
|
y := new(big.Int).SetBytes(b)
|
||||||
|
|
||||||
|
pub := &ecdsa.PublicKey{Curve: elliptic.P521(), X: x, Y: y}
|
||||||
|
th, err := JWKThumbprint(pub)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if th != expected {
|
||||||
|
t.Errorf("thumbprint = %q; want %q", th, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJWKThumbprintErrUnsupportedKey(t *testing.T) {
|
||||||
|
_, err := JWKThumbprint(struct{}{})
|
||||||
|
if err != ErrUnsupportedKey {
|
||||||
|
t.Errorf("err = %q; want %q", err, ErrUnsupportedKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
+295
@@ -0,0 +1,295 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ACME server response statuses used to describe Authorization and Challenge states.
|
||||||
|
const (
|
||||||
|
StatusUnknown = "unknown"
|
||||||
|
StatusPending = "pending"
|
||||||
|
StatusProcessing = "processing"
|
||||||
|
StatusValid = "valid"
|
||||||
|
StatusInvalid = "invalid"
|
||||||
|
StatusRevoked = "revoked"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CRLReasonCode identifies the reason for a certificate revocation.
|
||||||
|
type CRLReasonCode int
|
||||||
|
|
||||||
|
// CRL reason codes as defined in RFC 5280.
|
||||||
|
const (
|
||||||
|
CRLReasonUnspecified CRLReasonCode = 0
|
||||||
|
CRLReasonKeyCompromise CRLReasonCode = 1
|
||||||
|
CRLReasonCACompromise CRLReasonCode = 2
|
||||||
|
CRLReasonAffiliationChanged CRLReasonCode = 3
|
||||||
|
CRLReasonSuperseded CRLReasonCode = 4
|
||||||
|
CRLReasonCessationOfOperation CRLReasonCode = 5
|
||||||
|
CRLReasonCertificateHold CRLReasonCode = 6
|
||||||
|
CRLReasonRemoveFromCRL CRLReasonCode = 8
|
||||||
|
CRLReasonPrivilegeWithdrawn CRLReasonCode = 9
|
||||||
|
CRLReasonAACompromise CRLReasonCode = 10
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrUnsupportedKey is returned when an unsupported key type is encountered.
|
||||||
|
var ErrUnsupportedKey = errors.New("acme: unknown key type; only RSA and ECDSA are supported")
|
||||||
|
|
||||||
|
// Error is an ACME error, defined in Problem Details for HTTP APIs doc
|
||||||
|
// http://tools.ietf.org/html/draft-ietf-appsawg-http-problem.
|
||||||
|
type Error struct {
|
||||||
|
// StatusCode is The HTTP status code generated by the origin server.
|
||||||
|
StatusCode int
|
||||||
|
// ProblemType is a URI reference that identifies the problem type,
|
||||||
|
// typically in a "urn:acme:error:xxx" form.
|
||||||
|
ProblemType string
|
||||||
|
// Detail is a human-readable explanation specific to this occurrence of the problem.
|
||||||
|
Detail string
|
||||||
|
// Header is the original server error response headers.
|
||||||
|
// It may be nil.
|
||||||
|
Header http.Header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Error() string {
|
||||||
|
return fmt.Sprintf("%d %s: %s", e.StatusCode, e.ProblemType, e.Detail)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthorizationError indicates that an authorization for an identifier
|
||||||
|
// did not succeed.
|
||||||
|
// It contains all errors from Challenge items of the failed Authorization.
|
||||||
|
type AuthorizationError struct {
|
||||||
|
// URI uniquely identifies the failed Authorization.
|
||||||
|
URI string
|
||||||
|
|
||||||
|
// Identifier is an AuthzID.Value of the failed Authorization.
|
||||||
|
Identifier string
|
||||||
|
|
||||||
|
// Errors is a collection of non-nil error values of Challenge items
|
||||||
|
// of the failed Authorization.
|
||||||
|
Errors []error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AuthorizationError) Error() string {
|
||||||
|
e := make([]string, len(a.Errors))
|
||||||
|
for i, err := range a.Errors {
|
||||||
|
e[i] = err.Error()
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("acme: authorization error for %s: %s", a.Identifier, strings.Join(e, "; "))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RateLimit reports whether err represents a rate limit error and
|
||||||
|
// any Retry-After duration returned by the server.
|
||||||
|
//
|
||||||
|
// See the following for more details on rate limiting:
|
||||||
|
// https://tools.ietf.org/html/draft-ietf-acme-acme-05#section-5.6
|
||||||
|
func RateLimit(err error) (time.Duration, bool) {
|
||||||
|
e, ok := err.(*Error)
|
||||||
|
if !ok {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
// Some CA implementations may return incorrect values.
|
||||||
|
// Use case-insensitive comparison.
|
||||||
|
if !strings.HasSuffix(strings.ToLower(e.ProblemType), ":ratelimited") {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
if e.Header == nil {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
return retryAfter(e.Header.Get("Retry-After"), 0), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Account is a user account. It is associated with a private key.
|
||||||
|
type Account struct {
|
||||||
|
// URI is the account unique ID, which is also a URL used to retrieve
|
||||||
|
// account data from the CA.
|
||||||
|
URI string
|
||||||
|
|
||||||
|
// Contact is a slice of contact info used during registration.
|
||||||
|
Contact []string
|
||||||
|
|
||||||
|
// The terms user has agreed to.
|
||||||
|
// A value not matching CurrentTerms indicates that the user hasn't agreed
|
||||||
|
// to the actual Terms of Service of the CA.
|
||||||
|
AgreedTerms string
|
||||||
|
|
||||||
|
// Actual terms of a CA.
|
||||||
|
CurrentTerms string
|
||||||
|
|
||||||
|
// Authz is the authorization URL used to initiate a new authz flow.
|
||||||
|
Authz string
|
||||||
|
|
||||||
|
// Authorizations is a URI from which a list of authorizations
|
||||||
|
// granted to this account can be fetched via a GET request.
|
||||||
|
Authorizations string
|
||||||
|
|
||||||
|
// Certificates is a URI from which a list of certificates
|
||||||
|
// issued for this account can be fetched via a GET request.
|
||||||
|
Certificates string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Directory is ACME server discovery data.
|
||||||
|
type Directory struct {
|
||||||
|
// RegURL is an account endpoint URL, allowing for creating new
|
||||||
|
// and modifying existing accounts.
|
||||||
|
RegURL string
|
||||||
|
|
||||||
|
// AuthzURL is used to initiate Identifier Authorization flow.
|
||||||
|
AuthzURL string
|
||||||
|
|
||||||
|
// CertURL is a new certificate issuance endpoint URL.
|
||||||
|
CertURL string
|
||||||
|
|
||||||
|
// RevokeURL is used to initiate a certificate revocation flow.
|
||||||
|
RevokeURL string
|
||||||
|
|
||||||
|
// Term is a URI identifying the current terms of service.
|
||||||
|
Terms string
|
||||||
|
|
||||||
|
// Website is an HTTP or HTTPS URL locating a website
|
||||||
|
// providing more information about the ACME server.
|
||||||
|
Website string
|
||||||
|
|
||||||
|
// CAA consists of lowercase hostname elements, which the ACME server
|
||||||
|
// recognises as referring to itself for the purposes of CAA record validation
|
||||||
|
// as defined in RFC6844.
|
||||||
|
CAA []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Challenge encodes a returned CA challenge.
|
||||||
|
// Its Error field may be non-nil if the challenge is part of an Authorization
|
||||||
|
// with StatusInvalid.
|
||||||
|
type Challenge struct {
|
||||||
|
// Type is the challenge type, e.g. "http-01", "tls-sni-02", "dns-01".
|
||||||
|
Type string
|
||||||
|
|
||||||
|
// URI is where a challenge response can be posted to.
|
||||||
|
URI string
|
||||||
|
|
||||||
|
// Token is a random value that uniquely identifies the challenge.
|
||||||
|
Token string
|
||||||
|
|
||||||
|
// Status identifies the status of this challenge.
|
||||||
|
Status string
|
||||||
|
|
||||||
|
// Error indicates the reason for an authorization failure
|
||||||
|
// when this challenge was used.
|
||||||
|
// The type of a non-nil value is *Error.
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authorization encodes an authorization response.
|
||||||
|
type Authorization struct {
|
||||||
|
// URI uniquely identifies a authorization.
|
||||||
|
URI string
|
||||||
|
|
||||||
|
// Status identifies the status of an authorization.
|
||||||
|
Status string
|
||||||
|
|
||||||
|
// Identifier is what the account is authorized to represent.
|
||||||
|
Identifier AuthzID
|
||||||
|
|
||||||
|
// Challenges that the client needs to fulfill in order to prove possession
|
||||||
|
// of the identifier (for pending authorizations).
|
||||||
|
// For final authorizations, the challenges that were used.
|
||||||
|
Challenges []*Challenge
|
||||||
|
|
||||||
|
// A collection of sets of challenges, each of which would be sufficient
|
||||||
|
// to prove possession of the identifier.
|
||||||
|
// Clients must complete a set of challenges that covers at least one set.
|
||||||
|
// Challenges are identified by their indices in the challenges array.
|
||||||
|
// If this field is empty, the client needs to complete all challenges.
|
||||||
|
Combinations [][]int
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthzID is an identifier that an account is authorized to represent.
|
||||||
|
type AuthzID struct {
|
||||||
|
Type string // The type of identifier, e.g. "dns".
|
||||||
|
Value string // The identifier itself, e.g. "example.org".
|
||||||
|
}
|
||||||
|
|
||||||
|
// wireAuthz is ACME JSON representation of Authorization objects.
|
||||||
|
type wireAuthz struct {
|
||||||
|
Status string
|
||||||
|
Challenges []wireChallenge
|
||||||
|
Combinations [][]int
|
||||||
|
Identifier struct {
|
||||||
|
Type string
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *wireAuthz) authorization(uri string) *Authorization {
|
||||||
|
a := &Authorization{
|
||||||
|
URI: uri,
|
||||||
|
Status: z.Status,
|
||||||
|
Identifier: AuthzID{Type: z.Identifier.Type, Value: z.Identifier.Value},
|
||||||
|
Combinations: z.Combinations, // shallow copy
|
||||||
|
Challenges: make([]*Challenge, len(z.Challenges)),
|
||||||
|
}
|
||||||
|
for i, v := range z.Challenges {
|
||||||
|
a.Challenges[i] = v.challenge()
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *wireAuthz) error(uri string) *AuthorizationError {
|
||||||
|
err := &AuthorizationError{
|
||||||
|
URI: uri,
|
||||||
|
Identifier: z.Identifier.Value,
|
||||||
|
}
|
||||||
|
for _, raw := range z.Challenges {
|
||||||
|
if raw.Error != nil {
|
||||||
|
err.Errors = append(err.Errors, raw.Error.error(nil))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// wireChallenge is ACME JSON challenge representation.
|
||||||
|
type wireChallenge struct {
|
||||||
|
URI string `json:"uri"`
|
||||||
|
Type string
|
||||||
|
Token string
|
||||||
|
Status string
|
||||||
|
Error *wireError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wireChallenge) challenge() *Challenge {
|
||||||
|
v := &Challenge{
|
||||||
|
URI: c.URI,
|
||||||
|
Type: c.Type,
|
||||||
|
Token: c.Token,
|
||||||
|
Status: c.Status,
|
||||||
|
}
|
||||||
|
if v.Status == "" {
|
||||||
|
v.Status = StatusPending
|
||||||
|
}
|
||||||
|
if c.Error != nil {
|
||||||
|
v.Error = c.Error.error(nil)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// wireError is a subset of fields of the Problem Details object
|
||||||
|
// as described in https://tools.ietf.org/html/rfc7807#section-3.1.
|
||||||
|
type wireError struct {
|
||||||
|
Status int
|
||||||
|
Type string
|
||||||
|
Detail string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *wireError) error(h http.Header) *Error {
|
||||||
|
return &Error{
|
||||||
|
StatusCode: e.Status,
|
||||||
|
ProblemType: e.Type,
|
||||||
|
Detail: e.Detail,
|
||||||
|
Header: h,
|
||||||
|
}
|
||||||
|
}
|
||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
// Copyright 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRateLimit(t *testing.T) {
|
||||||
|
now := time.Date(2017, 04, 27, 10, 0, 0, 0, time.UTC)
|
||||||
|
f := timeNow
|
||||||
|
defer func() { timeNow = f }()
|
||||||
|
timeNow = func() time.Time { return now }
|
||||||
|
|
||||||
|
h120, hTime := http.Header{}, http.Header{}
|
||||||
|
h120.Set("Retry-After", "120")
|
||||||
|
hTime.Set("Retry-After", "Tue Apr 27 11:00:00 2017")
|
||||||
|
|
||||||
|
err1 := &Error{
|
||||||
|
ProblemType: "urn:ietf:params:acme:error:nolimit",
|
||||||
|
Header: h120,
|
||||||
|
}
|
||||||
|
err2 := &Error{
|
||||||
|
ProblemType: "urn:ietf:params:acme:error:rateLimited",
|
||||||
|
Header: h120,
|
||||||
|
}
|
||||||
|
err3 := &Error{
|
||||||
|
ProblemType: "urn:ietf:params:acme:error:rateLimited",
|
||||||
|
Header: nil,
|
||||||
|
}
|
||||||
|
err4 := &Error{
|
||||||
|
ProblemType: "urn:ietf:params:acme:error:rateLimited",
|
||||||
|
Header: hTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
tt := []struct {
|
||||||
|
err error
|
||||||
|
res time.Duration
|
||||||
|
ok bool
|
||||||
|
}{
|
||||||
|
{nil, 0, false},
|
||||||
|
{errors.New("dummy"), 0, false},
|
||||||
|
{err1, 0, false},
|
||||||
|
{err2, 2 * time.Minute, true},
|
||||||
|
{err3, 0, true},
|
||||||
|
{err4, time.Hour, true},
|
||||||
|
}
|
||||||
|
for i, test := range tt {
|
||||||
|
res, ok := RateLimit(test.err)
|
||||||
|
if ok != test.ok {
|
||||||
|
t.Errorf("%d: RateLimit(%+v): ok = %v; want %v", i, test.err, ok, test.ok)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if res != test.res {
|
||||||
|
t.Errorf("%d: RateLimit(%+v) = %v; want %v", i, test.err, res, test.res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package bcrypt
|
||||||
|
|
||||||
|
import "encoding/base64"
|
||||||
|
|
||||||
|
const alphabet = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||||
|
|
||||||
|
var bcEncoding = base64.NewEncoding(alphabet)
|
||||||
|
|
||||||
|
func base64Encode(src []byte) []byte {
|
||||||
|
n := bcEncoding.EncodedLen(len(src))
|
||||||
|
dst := make([]byte, n)
|
||||||
|
bcEncoding.Encode(dst, src)
|
||||||
|
for dst[n-1] == '=' {
|
||||||
|
n--
|
||||||
|
}
|
||||||
|
return dst[:n]
|
||||||
|
}
|
||||||
|
|
||||||
|
func base64Decode(src []byte) ([]byte, error) {
|
||||||
|
numOfEquals := 4 - (len(src) % 4)
|
||||||
|
for i := 0; i < numOfEquals; i++ {
|
||||||
|
src = append(src, '=')
|
||||||
|
}
|
||||||
|
|
||||||
|
dst := make([]byte, bcEncoding.DecodedLen(len(src)))
|
||||||
|
n, err := bcEncoding.Decode(dst, src)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dst[:n], nil
|
||||||
|
}
|
||||||
+295
@@ -0,0 +1,295 @@
|
|||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package bcrypt implements Provos and Mazières's bcrypt adaptive hashing
|
||||||
|
// algorithm. See http://www.usenix.org/event/usenix99/provos/provos.pdf
|
||||||
|
package bcrypt // import "golang.org/x/crypto/bcrypt"
|
||||||
|
|
||||||
|
// The code is a port of Provos and Mazières's C implementation.
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/subtle"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/blowfish"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MinCost int = 4 // the minimum allowable cost as passed in to GenerateFromPassword
|
||||||
|
MaxCost int = 31 // the maximum allowable cost as passed in to GenerateFromPassword
|
||||||
|
DefaultCost int = 10 // the cost that will actually be set if a cost below MinCost is passed into GenerateFromPassword
|
||||||
|
)
|
||||||
|
|
||||||
|
// The error returned from CompareHashAndPassword when a password and hash do
|
||||||
|
// not match.
|
||||||
|
var ErrMismatchedHashAndPassword = errors.New("crypto/bcrypt: hashedPassword is not the hash of the given password")
|
||||||
|
|
||||||
|
// The error returned from CompareHashAndPassword when a hash is too short to
|
||||||
|
// be a bcrypt hash.
|
||||||
|
var ErrHashTooShort = errors.New("crypto/bcrypt: hashedSecret too short to be a bcrypted password")
|
||||||
|
|
||||||
|
// The error returned from CompareHashAndPassword when a hash was created with
|
||||||
|
// a bcrypt algorithm newer than this implementation.
|
||||||
|
type HashVersionTooNewError byte
|
||||||
|
|
||||||
|
func (hv HashVersionTooNewError) Error() string {
|
||||||
|
return fmt.Sprintf("crypto/bcrypt: bcrypt algorithm version '%c' requested is newer than current version '%c'", byte(hv), majorVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The error returned from CompareHashAndPassword when a hash starts with something other than '$'
|
||||||
|
type InvalidHashPrefixError byte
|
||||||
|
|
||||||
|
func (ih InvalidHashPrefixError) Error() string {
|
||||||
|
return fmt.Sprintf("crypto/bcrypt: bcrypt hashes must start with '$', but hashedSecret started with '%c'", byte(ih))
|
||||||
|
}
|
||||||
|
|
||||||
|
type InvalidCostError int
|
||||||
|
|
||||||
|
func (ic InvalidCostError) Error() string {
|
||||||
|
return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed range (%d,%d)", int(ic), int(MinCost), int(MaxCost))
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
majorVersion = '2'
|
||||||
|
minorVersion = 'a'
|
||||||
|
maxSaltSize = 16
|
||||||
|
maxCryptedHashSize = 23
|
||||||
|
encodedSaltSize = 22
|
||||||
|
encodedHashSize = 31
|
||||||
|
minHashSize = 59
|
||||||
|
)
|
||||||
|
|
||||||
|
// magicCipherData is an IV for the 64 Blowfish encryption calls in
|
||||||
|
// bcrypt(). It's the string "OrpheanBeholderScryDoubt" in big-endian bytes.
|
||||||
|
var magicCipherData = []byte{
|
||||||
|
0x4f, 0x72, 0x70, 0x68,
|
||||||
|
0x65, 0x61, 0x6e, 0x42,
|
||||||
|
0x65, 0x68, 0x6f, 0x6c,
|
||||||
|
0x64, 0x65, 0x72, 0x53,
|
||||||
|
0x63, 0x72, 0x79, 0x44,
|
||||||
|
0x6f, 0x75, 0x62, 0x74,
|
||||||
|
}
|
||||||
|
|
||||||
|
type hashed struct {
|
||||||
|
hash []byte
|
||||||
|
salt []byte
|
||||||
|
cost int // allowed range is MinCost to MaxCost
|
||||||
|
major byte
|
||||||
|
minor byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateFromPassword returns the bcrypt hash of the password at the given
|
||||||
|
// cost. If the cost given is less than MinCost, the cost will be set to
|
||||||
|
// DefaultCost, instead. Use CompareHashAndPassword, as defined in this package,
|
||||||
|
// to compare the returned hashed password with its cleartext version.
|
||||||
|
func GenerateFromPassword(password []byte, cost int) ([]byte, error) {
|
||||||
|
p, err := newFromPassword(password, cost)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return p.Hash(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareHashAndPassword compares a bcrypt hashed password with its possible
|
||||||
|
// plaintext equivalent. Returns nil on success, or an error on failure.
|
||||||
|
func CompareHashAndPassword(hashedPassword, password []byte) error {
|
||||||
|
p, err := newFromHash(hashedPassword)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
otherHash, err := bcrypt(password, p.cost, p.salt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
otherP := &hashed{otherHash, p.salt, p.cost, p.major, p.minor}
|
||||||
|
if subtle.ConstantTimeCompare(p.Hash(), otherP.Hash()) == 1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrMismatchedHashAndPassword
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cost returns the hashing cost used to create the given hashed
|
||||||
|
// password. When, in the future, the hashing cost of a password system needs
|
||||||
|
// to be increased in order to adjust for greater computational power, this
|
||||||
|
// function allows one to establish which passwords need to be updated.
|
||||||
|
func Cost(hashedPassword []byte) (int, error) {
|
||||||
|
p, err := newFromHash(hashedPassword)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return p.cost, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFromPassword(password []byte, cost int) (*hashed, error) {
|
||||||
|
if cost < MinCost {
|
||||||
|
cost = DefaultCost
|
||||||
|
}
|
||||||
|
p := new(hashed)
|
||||||
|
p.major = majorVersion
|
||||||
|
p.minor = minorVersion
|
||||||
|
|
||||||
|
err := checkCost(cost)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p.cost = cost
|
||||||
|
|
||||||
|
unencodedSalt := make([]byte, maxSaltSize)
|
||||||
|
_, err = io.ReadFull(rand.Reader, unencodedSalt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
p.salt = base64Encode(unencodedSalt)
|
||||||
|
hash, err := bcrypt(password, p.cost, p.salt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p.hash = hash
|
||||||
|
return p, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFromHash(hashedSecret []byte) (*hashed, error) {
|
||||||
|
if len(hashedSecret) < minHashSize {
|
||||||
|
return nil, ErrHashTooShort
|
||||||
|
}
|
||||||
|
p := new(hashed)
|
||||||
|
n, err := p.decodeVersion(hashedSecret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hashedSecret = hashedSecret[n:]
|
||||||
|
n, err = p.decodeCost(hashedSecret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hashedSecret = hashedSecret[n:]
|
||||||
|
|
||||||
|
// The "+2" is here because we'll have to append at most 2 '=' to the salt
|
||||||
|
// when base64 decoding it in expensiveBlowfishSetup().
|
||||||
|
p.salt = make([]byte, encodedSaltSize, encodedSaltSize+2)
|
||||||
|
copy(p.salt, hashedSecret[:encodedSaltSize])
|
||||||
|
|
||||||
|
hashedSecret = hashedSecret[encodedSaltSize:]
|
||||||
|
p.hash = make([]byte, len(hashedSecret))
|
||||||
|
copy(p.hash, hashedSecret)
|
||||||
|
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func bcrypt(password []byte, cost int, salt []byte) ([]byte, error) {
|
||||||
|
cipherData := make([]byte, len(magicCipherData))
|
||||||
|
copy(cipherData, magicCipherData)
|
||||||
|
|
||||||
|
c, err := expensiveBlowfishSetup(password, uint32(cost), salt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 24; i += 8 {
|
||||||
|
for j := 0; j < 64; j++ {
|
||||||
|
c.Encrypt(cipherData[i:i+8], cipherData[i:i+8])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bug compatibility with C bcrypt implementations. We only encode 23 of
|
||||||
|
// the 24 bytes encrypted.
|
||||||
|
hsh := base64Encode(cipherData[:maxCryptedHashSize])
|
||||||
|
return hsh, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func expensiveBlowfishSetup(key []byte, cost uint32, salt []byte) (*blowfish.Cipher, error) {
|
||||||
|
csalt, err := base64Decode(salt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bug compatibility with C bcrypt implementations. They use the trailing
|
||||||
|
// NULL in the key string during expansion.
|
||||||
|
// We copy the key to prevent changing the underlying array.
|
||||||
|
ckey := append(key[:len(key):len(key)], 0)
|
||||||
|
|
||||||
|
c, err := blowfish.NewSaltedCipher(ckey, csalt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var i, rounds uint64
|
||||||
|
rounds = 1 << cost
|
||||||
|
for i = 0; i < rounds; i++ {
|
||||||
|
blowfish.ExpandKey(ckey, c)
|
||||||
|
blowfish.ExpandKey(csalt, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *hashed) Hash() []byte {
|
||||||
|
arr := make([]byte, 60)
|
||||||
|
arr[0] = '$'
|
||||||
|
arr[1] = p.major
|
||||||
|
n := 2
|
||||||
|
if p.minor != 0 {
|
||||||
|
arr[2] = p.minor
|
||||||
|
n = 3
|
||||||
|
}
|
||||||
|
arr[n] = '$'
|
||||||
|
n += 1
|
||||||
|
copy(arr[n:], []byte(fmt.Sprintf("%02d", p.cost)))
|
||||||
|
n += 2
|
||||||
|
arr[n] = '$'
|
||||||
|
n += 1
|
||||||
|
copy(arr[n:], p.salt)
|
||||||
|
n += encodedSaltSize
|
||||||
|
copy(arr[n:], p.hash)
|
||||||
|
n += encodedHashSize
|
||||||
|
return arr[:n]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *hashed) decodeVersion(sbytes []byte) (int, error) {
|
||||||
|
if sbytes[0] != '$' {
|
||||||
|
return -1, InvalidHashPrefixError(sbytes[0])
|
||||||
|
}
|
||||||
|
if sbytes[1] > majorVersion {
|
||||||
|
return -1, HashVersionTooNewError(sbytes[1])
|
||||||
|
}
|
||||||
|
p.major = sbytes[1]
|
||||||
|
n := 3
|
||||||
|
if sbytes[2] != '$' {
|
||||||
|
p.minor = sbytes[2]
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// sbytes should begin where decodeVersion left off.
|
||||||
|
func (p *hashed) decodeCost(sbytes []byte) (int, error) {
|
||||||
|
cost, err := strconv.Atoi(string(sbytes[0:2]))
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
err = checkCost(cost)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
p.cost = cost
|
||||||
|
return 3, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *hashed) String() string {
|
||||||
|
return fmt.Sprintf("&{hash: %#v, salt: %#v, cost: %d, major: %c, minor: %c}", string(p.hash), p.salt, p.cost, p.major, p.minor)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkCost(cost int) error {
|
||||||
|
if cost < MinCost || cost > MaxCost {
|
||||||
|
return InvalidCostError(cost)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+243
@@ -0,0 +1,243 @@
|
|||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package bcrypt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBcryptingIsEasy(t *testing.T) {
|
||||||
|
pass := []byte("mypassword")
|
||||||
|
hp, err := GenerateFromPassword(pass, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GenerateFromPassword error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if CompareHashAndPassword(hp, pass) != nil {
|
||||||
|
t.Errorf("%v should hash %s correctly", hp, pass)
|
||||||
|
}
|
||||||
|
|
||||||
|
notPass := "notthepass"
|
||||||
|
err = CompareHashAndPassword(hp, []byte(notPass))
|
||||||
|
if err != ErrMismatchedHashAndPassword {
|
||||||
|
t.Errorf("%v and %s should be mismatched", hp, notPass)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBcryptingIsCorrect(t *testing.T) {
|
||||||
|
pass := []byte("allmine")
|
||||||
|
salt := []byte("XajjQvNhvvRt5GSeFk1xFe")
|
||||||
|
expectedHash := []byte("$2a$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.wU1qD4aFDcga")
|
||||||
|
|
||||||
|
hash, err := bcrypt(pass, 10, salt)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("bcrypt blew up: %v", err)
|
||||||
|
}
|
||||||
|
if !bytes.HasSuffix(expectedHash, hash) {
|
||||||
|
t.Errorf("%v should be the suffix of %v", hash, expectedHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
h, err := newFromHash(expectedHash)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unable to parse %s: %v", string(expectedHash), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is not the safe way to compare these hashes. We do this only for
|
||||||
|
// testing clarity. Use bcrypt.CompareHashAndPassword()
|
||||||
|
if err == nil && !bytes.Equal(expectedHash, h.Hash()) {
|
||||||
|
t.Errorf("Parsed hash %v should equal %v", h.Hash(), expectedHash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVeryShortPasswords(t *testing.T) {
|
||||||
|
key := []byte("k")
|
||||||
|
salt := []byte("XajjQvNhvvRt5GSeFk1xFe")
|
||||||
|
_, err := bcrypt(key, 10, salt)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("One byte key resulted in error: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTooLongPasswordsWork(t *testing.T) {
|
||||||
|
salt := []byte("XajjQvNhvvRt5GSeFk1xFe")
|
||||||
|
// One byte over the usual 56 byte limit that blowfish has
|
||||||
|
tooLongPass := []byte("012345678901234567890123456789012345678901234567890123456")
|
||||||
|
tooLongExpected := []byte("$2a$10$XajjQvNhvvRt5GSeFk1xFe5l47dONXg781AmZtd869sO8zfsHuw7C")
|
||||||
|
hash, err := bcrypt(tooLongPass, 10, salt)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("bcrypt blew up on long password: %v", err)
|
||||||
|
}
|
||||||
|
if !bytes.HasSuffix(tooLongExpected, hash) {
|
||||||
|
t.Errorf("%v should be the suffix of %v", hash, tooLongExpected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type InvalidHashTest struct {
|
||||||
|
err error
|
||||||
|
hash []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var invalidTests = []InvalidHashTest{
|
||||||
|
{ErrHashTooShort, []byte("$2a$10$fooo")},
|
||||||
|
{ErrHashTooShort, []byte("$2a")},
|
||||||
|
{HashVersionTooNewError('3'), []byte("$3a$10$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
|
||||||
|
{InvalidHashPrefixError('%'), []byte("%2a$10$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
|
||||||
|
{InvalidCostError(32), []byte("$2a$32$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidHashErrors(t *testing.T) {
|
||||||
|
check := func(name string, expected, err error) {
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("%s: Should have returned an error", name)
|
||||||
|
}
|
||||||
|
if err != nil && err != expected {
|
||||||
|
t.Errorf("%s gave err %v but should have given %v", name, err, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, iht := range invalidTests {
|
||||||
|
_, err := newFromHash(iht.hash)
|
||||||
|
check("newFromHash", iht.err, err)
|
||||||
|
err = CompareHashAndPassword(iht.hash, []byte("anything"))
|
||||||
|
check("CompareHashAndPassword", iht.err, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnpaddedBase64Encoding(t *testing.T) {
|
||||||
|
original := []byte{101, 201, 101, 75, 19, 227, 199, 20, 239, 236, 133, 32, 30, 109, 243, 30}
|
||||||
|
encodedOriginal := []byte("XajjQvNhvvRt5GSeFk1xFe")
|
||||||
|
|
||||||
|
encoded := base64Encode(original)
|
||||||
|
|
||||||
|
if !bytes.Equal(encodedOriginal, encoded) {
|
||||||
|
t.Errorf("Encoded %v should have equaled %v", encoded, encodedOriginal)
|
||||||
|
}
|
||||||
|
|
||||||
|
decoded, err := base64Decode(encodedOriginal)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("base64Decode blew up: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(decoded, original) {
|
||||||
|
t.Errorf("Decoded %v should have equaled %v", decoded, original)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCost(t *testing.T) {
|
||||||
|
suffix := "XajjQvNhvvRt5GSeFk1xFe5l47dONXg781AmZtd869sO8zfsHuw7C"
|
||||||
|
for _, vers := range []string{"2a", "2"} {
|
||||||
|
for _, cost := range []int{4, 10} {
|
||||||
|
s := fmt.Sprintf("$%s$%02d$%s", vers, cost, suffix)
|
||||||
|
h := []byte(s)
|
||||||
|
actual, err := Cost(h)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Cost, error: %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if actual != cost {
|
||||||
|
t.Errorf("Cost, expected: %d, actual: %d", cost, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err := Cost([]byte("$a$a$" + suffix))
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Cost, malformed but no error returned")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCostValidationInHash(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pass := []byte("mypassword")
|
||||||
|
|
||||||
|
for c := 0; c < MinCost; c++ {
|
||||||
|
p, _ := newFromPassword(pass, c)
|
||||||
|
if p.cost != DefaultCost {
|
||||||
|
t.Errorf("newFromPassword should default costs below %d to %d, but was %d", MinCost, DefaultCost, p.cost)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p, _ := newFromPassword(pass, 14)
|
||||||
|
if p.cost != 14 {
|
||||||
|
t.Errorf("newFromPassword should default cost to 14, but was %d", p.cost)
|
||||||
|
}
|
||||||
|
|
||||||
|
hp, _ := newFromHash(p.Hash())
|
||||||
|
if p.cost != hp.cost {
|
||||||
|
t.Errorf("newFromHash should maintain the cost at %d, but was %d", p.cost, hp.cost)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := newFromPassword(pass, 32)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("newFromPassword: should return a cost error")
|
||||||
|
}
|
||||||
|
if err != InvalidCostError(32) {
|
||||||
|
t.Errorf("newFromPassword: should return cost error, got %#v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCostReturnsWithLeadingZeroes(t *testing.T) {
|
||||||
|
hp, _ := newFromPassword([]byte("abcdefgh"), 7)
|
||||||
|
cost := hp.Hash()[4:7]
|
||||||
|
expected := []byte("07$")
|
||||||
|
|
||||||
|
if !bytes.Equal(expected, cost) {
|
||||||
|
t.Errorf("single digit costs in hash should have leading zeros: was %v instead of %v", cost, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMinorNotRequired(t *testing.T) {
|
||||||
|
noMinorHash := []byte("$2$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.wU1qD4aFDcga")
|
||||||
|
h, err := newFromHash(noMinorHash)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("No minor hash blew up: %s", err)
|
||||||
|
}
|
||||||
|
if h.minor != 0 {
|
||||||
|
t.Errorf("Should leave minor version at 0, but was %d", h.minor)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(noMinorHash, h.Hash()) {
|
||||||
|
t.Errorf("Should generate hash %v, but created %v", noMinorHash, h.Hash())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkEqual(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
passwd := []byte("somepasswordyoulike")
|
||||||
|
hash, _ := GenerateFromPassword(passwd, 10)
|
||||||
|
b.StartTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
CompareHashAndPassword(hash, passwd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkGeneration(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
passwd := []byte("mylongpassword1234")
|
||||||
|
b.StartTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
GenerateFromPassword(passwd, 10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// See Issue https://github.com/golang/go/issues/20425.
|
||||||
|
func TestNoSideEffectsFromCompare(t *testing.T) {
|
||||||
|
source := []byte("passw0rd123456")
|
||||||
|
password := source[:len(source)-6]
|
||||||
|
token := source[len(source)-6:]
|
||||||
|
want := make([]byte, len(source))
|
||||||
|
copy(want, source)
|
||||||
|
|
||||||
|
wantHash := []byte("$2a$10$LK9XRuhNxHHCvjX3tdkRKei1QiCDUKrJRhZv7WWZPuQGRUM92rOUa")
|
||||||
|
_ = CompareHashAndPassword(wantHash, password)
|
||||||
|
|
||||||
|
got := bytes.Join([][]byte{password, token}, []byte(""))
|
||||||
|
if !bytes.Equal(got, want) {
|
||||||
|
t.Errorf("got=%q want=%q", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
+194
@@ -0,0 +1,194 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package blake2b implements the BLAKE2b hash algorithm as
|
||||||
|
// defined in RFC 7693.
|
||||||
|
package blake2b // import "golang.org/x/crypto/blake2b"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"hash"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// The blocksize of BLAKE2b in bytes.
|
||||||
|
BlockSize = 128
|
||||||
|
// The hash size of BLAKE2b-512 in bytes.
|
||||||
|
Size = 64
|
||||||
|
// The hash size of BLAKE2b-384 in bytes.
|
||||||
|
Size384 = 48
|
||||||
|
// The hash size of BLAKE2b-256 in bytes.
|
||||||
|
Size256 = 32
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
useAVX2 bool
|
||||||
|
useAVX bool
|
||||||
|
useSSE4 bool
|
||||||
|
)
|
||||||
|
|
||||||
|
var errKeySize = errors.New("blake2b: invalid key size")
|
||||||
|
|
||||||
|
var iv = [8]uint64{
|
||||||
|
0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
|
||||||
|
0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sum512 returns the BLAKE2b-512 checksum of the data.
|
||||||
|
func Sum512(data []byte) [Size]byte {
|
||||||
|
var sum [Size]byte
|
||||||
|
checkSum(&sum, Size, data)
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sum384 returns the BLAKE2b-384 checksum of the data.
|
||||||
|
func Sum384(data []byte) [Size384]byte {
|
||||||
|
var sum [Size]byte
|
||||||
|
var sum384 [Size384]byte
|
||||||
|
checkSum(&sum, Size384, data)
|
||||||
|
copy(sum384[:], sum[:Size384])
|
||||||
|
return sum384
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sum256 returns the BLAKE2b-256 checksum of the data.
|
||||||
|
func Sum256(data []byte) [Size256]byte {
|
||||||
|
var sum [Size]byte
|
||||||
|
var sum256 [Size256]byte
|
||||||
|
checkSum(&sum, Size256, data)
|
||||||
|
copy(sum256[:], sum[:Size256])
|
||||||
|
return sum256
|
||||||
|
}
|
||||||
|
|
||||||
|
// New512 returns a new hash.Hash computing the BLAKE2b-512 checksum. A non-nil
|
||||||
|
// key turns the hash into a MAC. The key must between zero and 64 bytes long.
|
||||||
|
func New512(key []byte) (hash.Hash, error) { return newDigest(Size, key) }
|
||||||
|
|
||||||
|
// New384 returns a new hash.Hash computing the BLAKE2b-384 checksum. A non-nil
|
||||||
|
// key turns the hash into a MAC. The key must between zero and 64 bytes long.
|
||||||
|
func New384(key []byte) (hash.Hash, error) { return newDigest(Size384, key) }
|
||||||
|
|
||||||
|
// New256 returns a new hash.Hash computing the BLAKE2b-256 checksum. A non-nil
|
||||||
|
// key turns the hash into a MAC. The key must between zero and 64 bytes long.
|
||||||
|
func New256(key []byte) (hash.Hash, error) { return newDigest(Size256, key) }
|
||||||
|
|
||||||
|
func newDigest(hashSize int, key []byte) (*digest, error) {
|
||||||
|
if len(key) > Size {
|
||||||
|
return nil, errKeySize
|
||||||
|
}
|
||||||
|
d := &digest{
|
||||||
|
size: hashSize,
|
||||||
|
keyLen: len(key),
|
||||||
|
}
|
||||||
|
copy(d.key[:], key)
|
||||||
|
d.Reset()
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkSum(sum *[Size]byte, hashSize int, data []byte) {
|
||||||
|
h := iv
|
||||||
|
h[0] ^= uint64(hashSize) | (1 << 16) | (1 << 24)
|
||||||
|
var c [2]uint64
|
||||||
|
|
||||||
|
if length := len(data); length > BlockSize {
|
||||||
|
n := length &^ (BlockSize - 1)
|
||||||
|
if length == n {
|
||||||
|
n -= BlockSize
|
||||||
|
}
|
||||||
|
hashBlocks(&h, &c, 0, data[:n])
|
||||||
|
data = data[n:]
|
||||||
|
}
|
||||||
|
|
||||||
|
var block [BlockSize]byte
|
||||||
|
offset := copy(block[:], data)
|
||||||
|
remaining := uint64(BlockSize - offset)
|
||||||
|
if c[0] < remaining {
|
||||||
|
c[1]--
|
||||||
|
}
|
||||||
|
c[0] -= remaining
|
||||||
|
|
||||||
|
hashBlocks(&h, &c, 0xFFFFFFFFFFFFFFFF, block[:])
|
||||||
|
|
||||||
|
for i, v := range h[:(hashSize+7)/8] {
|
||||||
|
binary.LittleEndian.PutUint64(sum[8*i:], v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type digest struct {
|
||||||
|
h [8]uint64
|
||||||
|
c [2]uint64
|
||||||
|
size int
|
||||||
|
block [BlockSize]byte
|
||||||
|
offset int
|
||||||
|
|
||||||
|
key [BlockSize]byte
|
||||||
|
keyLen int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *digest) BlockSize() int { return BlockSize }
|
||||||
|
|
||||||
|
func (d *digest) Size() int { return d.size }
|
||||||
|
|
||||||
|
func (d *digest) Reset() {
|
||||||
|
d.h = iv
|
||||||
|
d.h[0] ^= uint64(d.size) | (uint64(d.keyLen) << 8) | (1 << 16) | (1 << 24)
|
||||||
|
d.offset, d.c[0], d.c[1] = 0, 0, 0
|
||||||
|
if d.keyLen > 0 {
|
||||||
|
d.block = d.key
|
||||||
|
d.offset = BlockSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *digest) Write(p []byte) (n int, err error) {
|
||||||
|
n = len(p)
|
||||||
|
|
||||||
|
if d.offset > 0 {
|
||||||
|
remaining := BlockSize - d.offset
|
||||||
|
if n <= remaining {
|
||||||
|
d.offset += copy(d.block[d.offset:], p)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
copy(d.block[d.offset:], p[:remaining])
|
||||||
|
hashBlocks(&d.h, &d.c, 0, d.block[:])
|
||||||
|
d.offset = 0
|
||||||
|
p = p[remaining:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if length := len(p); length > BlockSize {
|
||||||
|
nn := length &^ (BlockSize - 1)
|
||||||
|
if length == nn {
|
||||||
|
nn -= BlockSize
|
||||||
|
}
|
||||||
|
hashBlocks(&d.h, &d.c, 0, p[:nn])
|
||||||
|
p = p[nn:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(p) > 0 {
|
||||||
|
d.offset += copy(d.block[:], p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *digest) Sum(b []byte) []byte {
|
||||||
|
var block [BlockSize]byte
|
||||||
|
copy(block[:], d.block[:d.offset])
|
||||||
|
remaining := uint64(BlockSize - d.offset)
|
||||||
|
|
||||||
|
c := d.c
|
||||||
|
if c[0] < remaining {
|
||||||
|
c[1]--
|
||||||
|
}
|
||||||
|
c[0] -= remaining
|
||||||
|
|
||||||
|
h := d.h
|
||||||
|
hashBlocks(&h, &c, 0xFFFFFFFFFFFFFFFF, block[:])
|
||||||
|
|
||||||
|
var sum [Size]byte
|
||||||
|
for i, v := range h[:(d.size+7)/8] {
|
||||||
|
binary.LittleEndian.PutUint64(sum[8*i:], v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(b, sum[:d.size]...)
|
||||||
|
}
|
||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build go1.7,amd64,!gccgo,!appengine
|
||||||
|
|
||||||
|
package blake2b
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
useAVX2 = supportsAVX2()
|
||||||
|
useAVX = supportsAVX()
|
||||||
|
useSSE4 = supportsSSE4()
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func supportsSSE4() bool
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func supportsAVX() bool
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func supportsAVX2() bool
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func hashBlocksAVX2(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func hashBlocksAVX(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func hashBlocksSSE4(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
|
||||||
|
|
||||||
|
func hashBlocks(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) {
|
||||||
|
if useAVX2 {
|
||||||
|
hashBlocksAVX2(h, c, flag, blocks)
|
||||||
|
} else if useAVX {
|
||||||
|
hashBlocksAVX(h, c, flag, blocks)
|
||||||
|
} else if useSSE4 {
|
||||||
|
hashBlocksSSE4(h, c, flag, blocks)
|
||||||
|
} else {
|
||||||
|
hashBlocksGeneric(h, c, flag, blocks)
|
||||||
|
}
|
||||||
|
}
|
||||||
+762
@@ -0,0 +1,762 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build go1.7,amd64,!gccgo,!appengine
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
DATA ·AVX2_iv0<>+0x00(SB)/8, $0x6a09e667f3bcc908
|
||||||
|
DATA ·AVX2_iv0<>+0x08(SB)/8, $0xbb67ae8584caa73b
|
||||||
|
DATA ·AVX2_iv0<>+0x10(SB)/8, $0x3c6ef372fe94f82b
|
||||||
|
DATA ·AVX2_iv0<>+0x18(SB)/8, $0xa54ff53a5f1d36f1
|
||||||
|
GLOBL ·AVX2_iv0<>(SB), (NOPTR+RODATA), $32
|
||||||
|
|
||||||
|
DATA ·AVX2_iv1<>+0x00(SB)/8, $0x510e527fade682d1
|
||||||
|
DATA ·AVX2_iv1<>+0x08(SB)/8, $0x9b05688c2b3e6c1f
|
||||||
|
DATA ·AVX2_iv1<>+0x10(SB)/8, $0x1f83d9abfb41bd6b
|
||||||
|
DATA ·AVX2_iv1<>+0x18(SB)/8, $0x5be0cd19137e2179
|
||||||
|
GLOBL ·AVX2_iv1<>(SB), (NOPTR+RODATA), $32
|
||||||
|
|
||||||
|
DATA ·AVX2_c40<>+0x00(SB)/8, $0x0201000706050403
|
||||||
|
DATA ·AVX2_c40<>+0x08(SB)/8, $0x0a09080f0e0d0c0b
|
||||||
|
DATA ·AVX2_c40<>+0x10(SB)/8, $0x0201000706050403
|
||||||
|
DATA ·AVX2_c40<>+0x18(SB)/8, $0x0a09080f0e0d0c0b
|
||||||
|
GLOBL ·AVX2_c40<>(SB), (NOPTR+RODATA), $32
|
||||||
|
|
||||||
|
DATA ·AVX2_c48<>+0x00(SB)/8, $0x0100070605040302
|
||||||
|
DATA ·AVX2_c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a
|
||||||
|
DATA ·AVX2_c48<>+0x10(SB)/8, $0x0100070605040302
|
||||||
|
DATA ·AVX2_c48<>+0x18(SB)/8, $0x09080f0e0d0c0b0a
|
||||||
|
GLOBL ·AVX2_c48<>(SB), (NOPTR+RODATA), $32
|
||||||
|
|
||||||
|
DATA ·AVX_iv0<>+0x00(SB)/8, $0x6a09e667f3bcc908
|
||||||
|
DATA ·AVX_iv0<>+0x08(SB)/8, $0xbb67ae8584caa73b
|
||||||
|
GLOBL ·AVX_iv0<>(SB), (NOPTR+RODATA), $16
|
||||||
|
|
||||||
|
DATA ·AVX_iv1<>+0x00(SB)/8, $0x3c6ef372fe94f82b
|
||||||
|
DATA ·AVX_iv1<>+0x08(SB)/8, $0xa54ff53a5f1d36f1
|
||||||
|
GLOBL ·AVX_iv1<>(SB), (NOPTR+RODATA), $16
|
||||||
|
|
||||||
|
DATA ·AVX_iv2<>+0x00(SB)/8, $0x510e527fade682d1
|
||||||
|
DATA ·AVX_iv2<>+0x08(SB)/8, $0x9b05688c2b3e6c1f
|
||||||
|
GLOBL ·AVX_iv2<>(SB), (NOPTR+RODATA), $16
|
||||||
|
|
||||||
|
DATA ·AVX_iv3<>+0x00(SB)/8, $0x1f83d9abfb41bd6b
|
||||||
|
DATA ·AVX_iv3<>+0x08(SB)/8, $0x5be0cd19137e2179
|
||||||
|
GLOBL ·AVX_iv3<>(SB), (NOPTR+RODATA), $16
|
||||||
|
|
||||||
|
DATA ·AVX_c40<>+0x00(SB)/8, $0x0201000706050403
|
||||||
|
DATA ·AVX_c40<>+0x08(SB)/8, $0x0a09080f0e0d0c0b
|
||||||
|
GLOBL ·AVX_c40<>(SB), (NOPTR+RODATA), $16
|
||||||
|
|
||||||
|
DATA ·AVX_c48<>+0x00(SB)/8, $0x0100070605040302
|
||||||
|
DATA ·AVX_c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a
|
||||||
|
GLOBL ·AVX_c48<>(SB), (NOPTR+RODATA), $16
|
||||||
|
|
||||||
|
#define VPERMQ_0x39_Y1_Y1 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xc9; BYTE $0x39
|
||||||
|
#define VPERMQ_0x93_Y1_Y1 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xc9; BYTE $0x93
|
||||||
|
#define VPERMQ_0x4E_Y2_Y2 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xd2; BYTE $0x4e
|
||||||
|
#define VPERMQ_0x93_Y3_Y3 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xdb; BYTE $0x93
|
||||||
|
#define VPERMQ_0x39_Y3_Y3 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xdb; BYTE $0x39
|
||||||
|
|
||||||
|
#define ROUND_AVX2(m0, m1, m2, m3, t, c40, c48) \
|
||||||
|
VPADDQ m0, Y0, Y0; \
|
||||||
|
VPADDQ Y1, Y0, Y0; \
|
||||||
|
VPXOR Y0, Y3, Y3; \
|
||||||
|
VPSHUFD $-79, Y3, Y3; \
|
||||||
|
VPADDQ Y3, Y2, Y2; \
|
||||||
|
VPXOR Y2, Y1, Y1; \
|
||||||
|
VPSHUFB c40, Y1, Y1; \
|
||||||
|
VPADDQ m1, Y0, Y0; \
|
||||||
|
VPADDQ Y1, Y0, Y0; \
|
||||||
|
VPXOR Y0, Y3, Y3; \
|
||||||
|
VPSHUFB c48, Y3, Y3; \
|
||||||
|
VPADDQ Y3, Y2, Y2; \
|
||||||
|
VPXOR Y2, Y1, Y1; \
|
||||||
|
VPADDQ Y1, Y1, t; \
|
||||||
|
VPSRLQ $63, Y1, Y1; \
|
||||||
|
VPXOR t, Y1, Y1; \
|
||||||
|
VPERMQ_0x39_Y1_Y1; \
|
||||||
|
VPERMQ_0x4E_Y2_Y2; \
|
||||||
|
VPERMQ_0x93_Y3_Y3; \
|
||||||
|
VPADDQ m2, Y0, Y0; \
|
||||||
|
VPADDQ Y1, Y0, Y0; \
|
||||||
|
VPXOR Y0, Y3, Y3; \
|
||||||
|
VPSHUFD $-79, Y3, Y3; \
|
||||||
|
VPADDQ Y3, Y2, Y2; \
|
||||||
|
VPXOR Y2, Y1, Y1; \
|
||||||
|
VPSHUFB c40, Y1, Y1; \
|
||||||
|
VPADDQ m3, Y0, Y0; \
|
||||||
|
VPADDQ Y1, Y0, Y0; \
|
||||||
|
VPXOR Y0, Y3, Y3; \
|
||||||
|
VPSHUFB c48, Y3, Y3; \
|
||||||
|
VPADDQ Y3, Y2, Y2; \
|
||||||
|
VPXOR Y2, Y1, Y1; \
|
||||||
|
VPADDQ Y1, Y1, t; \
|
||||||
|
VPSRLQ $63, Y1, Y1; \
|
||||||
|
VPXOR t, Y1, Y1; \
|
||||||
|
VPERMQ_0x39_Y3_Y3; \
|
||||||
|
VPERMQ_0x4E_Y2_Y2; \
|
||||||
|
VPERMQ_0x93_Y1_Y1
|
||||||
|
|
||||||
|
#define VMOVQ_SI_X11_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x1E
|
||||||
|
#define VMOVQ_SI_X12_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x26
|
||||||
|
#define VMOVQ_SI_X13_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x2E
|
||||||
|
#define VMOVQ_SI_X14_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x36
|
||||||
|
#define VMOVQ_SI_X15_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x3E
|
||||||
|
|
||||||
|
#define VMOVQ_SI_X11(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x5E; BYTE $n
|
||||||
|
#define VMOVQ_SI_X12(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x66; BYTE $n
|
||||||
|
#define VMOVQ_SI_X13(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x6E; BYTE $n
|
||||||
|
#define VMOVQ_SI_X14(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x76; BYTE $n
|
||||||
|
#define VMOVQ_SI_X15(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x7E; BYTE $n
|
||||||
|
|
||||||
|
#define VPINSRQ_1_SI_X11_0 BYTE $0xC4; BYTE $0x63; BYTE $0xA1; BYTE $0x22; BYTE $0x1E; BYTE $0x01
|
||||||
|
#define VPINSRQ_1_SI_X12_0 BYTE $0xC4; BYTE $0x63; BYTE $0x99; BYTE $0x22; BYTE $0x26; BYTE $0x01
|
||||||
|
#define VPINSRQ_1_SI_X13_0 BYTE $0xC4; BYTE $0x63; BYTE $0x91; BYTE $0x22; BYTE $0x2E; BYTE $0x01
|
||||||
|
#define VPINSRQ_1_SI_X14_0 BYTE $0xC4; BYTE $0x63; BYTE $0x89; BYTE $0x22; BYTE $0x36; BYTE $0x01
|
||||||
|
#define VPINSRQ_1_SI_X15_0 BYTE $0xC4; BYTE $0x63; BYTE $0x81; BYTE $0x22; BYTE $0x3E; BYTE $0x01
|
||||||
|
|
||||||
|
#define VPINSRQ_1_SI_X11(n) BYTE $0xC4; BYTE $0x63; BYTE $0xA1; BYTE $0x22; BYTE $0x5E; BYTE $n; BYTE $0x01
|
||||||
|
#define VPINSRQ_1_SI_X12(n) BYTE $0xC4; BYTE $0x63; BYTE $0x99; BYTE $0x22; BYTE $0x66; BYTE $n; BYTE $0x01
|
||||||
|
#define VPINSRQ_1_SI_X13(n) BYTE $0xC4; BYTE $0x63; BYTE $0x91; BYTE $0x22; BYTE $0x6E; BYTE $n; BYTE $0x01
|
||||||
|
#define VPINSRQ_1_SI_X14(n) BYTE $0xC4; BYTE $0x63; BYTE $0x89; BYTE $0x22; BYTE $0x76; BYTE $n; BYTE $0x01
|
||||||
|
#define VPINSRQ_1_SI_X15(n) BYTE $0xC4; BYTE $0x63; BYTE $0x81; BYTE $0x22; BYTE $0x7E; BYTE $n; BYTE $0x01
|
||||||
|
|
||||||
|
#define VMOVQ_R8_X15 BYTE $0xC4; BYTE $0x41; BYTE $0xF9; BYTE $0x6E; BYTE $0xF8
|
||||||
|
#define VPINSRQ_1_R9_X15 BYTE $0xC4; BYTE $0x43; BYTE $0x81; BYTE $0x22; BYTE $0xF9; BYTE $0x01
|
||||||
|
|
||||||
|
// load msg: Y12 = (i0, i1, i2, i3)
|
||||||
|
// i0, i1, i2, i3 must not be 0
|
||||||
|
#define LOAD_MSG_AVX2_Y12(i0, i1, i2, i3) \
|
||||||
|
VMOVQ_SI_X12(i0*8); \
|
||||||
|
VMOVQ_SI_X11(i2*8); \
|
||||||
|
VPINSRQ_1_SI_X12(i1*8); \
|
||||||
|
VPINSRQ_1_SI_X11(i3*8); \
|
||||||
|
VINSERTI128 $1, X11, Y12, Y12
|
||||||
|
|
||||||
|
// load msg: Y13 = (i0, i1, i2, i3)
|
||||||
|
// i0, i1, i2, i3 must not be 0
|
||||||
|
#define LOAD_MSG_AVX2_Y13(i0, i1, i2, i3) \
|
||||||
|
VMOVQ_SI_X13(i0*8); \
|
||||||
|
VMOVQ_SI_X11(i2*8); \
|
||||||
|
VPINSRQ_1_SI_X13(i1*8); \
|
||||||
|
VPINSRQ_1_SI_X11(i3*8); \
|
||||||
|
VINSERTI128 $1, X11, Y13, Y13
|
||||||
|
|
||||||
|
// load msg: Y14 = (i0, i1, i2, i3)
|
||||||
|
// i0, i1, i2, i3 must not be 0
|
||||||
|
#define LOAD_MSG_AVX2_Y14(i0, i1, i2, i3) \
|
||||||
|
VMOVQ_SI_X14(i0*8); \
|
||||||
|
VMOVQ_SI_X11(i2*8); \
|
||||||
|
VPINSRQ_1_SI_X14(i1*8); \
|
||||||
|
VPINSRQ_1_SI_X11(i3*8); \
|
||||||
|
VINSERTI128 $1, X11, Y14, Y14
|
||||||
|
|
||||||
|
// load msg: Y15 = (i0, i1, i2, i3)
|
||||||
|
// i0, i1, i2, i3 must not be 0
|
||||||
|
#define LOAD_MSG_AVX2_Y15(i0, i1, i2, i3) \
|
||||||
|
VMOVQ_SI_X15(i0*8); \
|
||||||
|
VMOVQ_SI_X11(i2*8); \
|
||||||
|
VPINSRQ_1_SI_X15(i1*8); \
|
||||||
|
VPINSRQ_1_SI_X11(i3*8); \
|
||||||
|
VINSERTI128 $1, X11, Y15, Y15
|
||||||
|
|
||||||
|
#define LOAD_MSG_AVX2_0_2_4_6_1_3_5_7_8_10_12_14_9_11_13_15() \
|
||||||
|
VMOVQ_SI_X12_0; \
|
||||||
|
VMOVQ_SI_X11(4*8); \
|
||||||
|
VPINSRQ_1_SI_X12(2*8); \
|
||||||
|
VPINSRQ_1_SI_X11(6*8); \
|
||||||
|
VINSERTI128 $1, X11, Y12, Y12; \
|
||||||
|
LOAD_MSG_AVX2_Y13(1, 3, 5, 7); \
|
||||||
|
LOAD_MSG_AVX2_Y14(8, 10, 12, 14); \
|
||||||
|
LOAD_MSG_AVX2_Y15(9, 11, 13, 15)
|
||||||
|
|
||||||
|
#define LOAD_MSG_AVX2_14_4_9_13_10_8_15_6_1_0_11_5_12_2_7_3() \
|
||||||
|
LOAD_MSG_AVX2_Y12(14, 4, 9, 13); \
|
||||||
|
LOAD_MSG_AVX2_Y13(10, 8, 15, 6); \
|
||||||
|
VMOVQ_SI_X11(11*8); \
|
||||||
|
VPSHUFD $0x4E, 0*8(SI), X14; \
|
||||||
|
VPINSRQ_1_SI_X11(5*8); \
|
||||||
|
VINSERTI128 $1, X11, Y14, Y14; \
|
||||||
|
LOAD_MSG_AVX2_Y15(12, 2, 7, 3)
|
||||||
|
|
||||||
|
#define LOAD_MSG_AVX2_11_12_5_15_8_0_2_13_10_3_7_9_14_6_1_4() \
|
||||||
|
VMOVQ_SI_X11(5*8); \
|
||||||
|
VMOVDQU 11*8(SI), X12; \
|
||||||
|
VPINSRQ_1_SI_X11(15*8); \
|
||||||
|
VINSERTI128 $1, X11, Y12, Y12; \
|
||||||
|
VMOVQ_SI_X13(8*8); \
|
||||||
|
VMOVQ_SI_X11(2*8); \
|
||||||
|
VPINSRQ_1_SI_X13_0; \
|
||||||
|
VPINSRQ_1_SI_X11(13*8); \
|
||||||
|
VINSERTI128 $1, X11, Y13, Y13; \
|
||||||
|
LOAD_MSG_AVX2_Y14(10, 3, 7, 9); \
|
||||||
|
LOAD_MSG_AVX2_Y15(14, 6, 1, 4)
|
||||||
|
|
||||||
|
#define LOAD_MSG_AVX2_7_3_13_11_9_1_12_14_2_5_4_15_6_10_0_8() \
|
||||||
|
LOAD_MSG_AVX2_Y12(7, 3, 13, 11); \
|
||||||
|
LOAD_MSG_AVX2_Y13(9, 1, 12, 14); \
|
||||||
|
LOAD_MSG_AVX2_Y14(2, 5, 4, 15); \
|
||||||
|
VMOVQ_SI_X15(6*8); \
|
||||||
|
VMOVQ_SI_X11_0; \
|
||||||
|
VPINSRQ_1_SI_X15(10*8); \
|
||||||
|
VPINSRQ_1_SI_X11(8*8); \
|
||||||
|
VINSERTI128 $1, X11, Y15, Y15
|
||||||
|
|
||||||
|
#define LOAD_MSG_AVX2_9_5_2_10_0_7_4_15_14_11_6_3_1_12_8_13() \
|
||||||
|
LOAD_MSG_AVX2_Y12(9, 5, 2, 10); \
|
||||||
|
VMOVQ_SI_X13_0; \
|
||||||
|
VMOVQ_SI_X11(4*8); \
|
||||||
|
VPINSRQ_1_SI_X13(7*8); \
|
||||||
|
VPINSRQ_1_SI_X11(15*8); \
|
||||||
|
VINSERTI128 $1, X11, Y13, Y13; \
|
||||||
|
LOAD_MSG_AVX2_Y14(14, 11, 6, 3); \
|
||||||
|
LOAD_MSG_AVX2_Y15(1, 12, 8, 13)
|
||||||
|
|
||||||
|
#define LOAD_MSG_AVX2_2_6_0_8_12_10_11_3_4_7_15_1_13_5_14_9() \
|
||||||
|
VMOVQ_SI_X12(2*8); \
|
||||||
|
VMOVQ_SI_X11_0; \
|
||||||
|
VPINSRQ_1_SI_X12(6*8); \
|
||||||
|
VPINSRQ_1_SI_X11(8*8); \
|
||||||
|
VINSERTI128 $1, X11, Y12, Y12; \
|
||||||
|
LOAD_MSG_AVX2_Y13(12, 10, 11, 3); \
|
||||||
|
LOAD_MSG_AVX2_Y14(4, 7, 15, 1); \
|
||||||
|
LOAD_MSG_AVX2_Y15(13, 5, 14, 9)
|
||||||
|
|
||||||
|
#define LOAD_MSG_AVX2_12_1_14_4_5_15_13_10_0_6_9_8_7_3_2_11() \
|
||||||
|
LOAD_MSG_AVX2_Y12(12, 1, 14, 4); \
|
||||||
|
LOAD_MSG_AVX2_Y13(5, 15, 13, 10); \
|
||||||
|
VMOVQ_SI_X14_0; \
|
||||||
|
VPSHUFD $0x4E, 8*8(SI), X11; \
|
||||||
|
VPINSRQ_1_SI_X14(6*8); \
|
||||||
|
VINSERTI128 $1, X11, Y14, Y14; \
|
||||||
|
LOAD_MSG_AVX2_Y15(7, 3, 2, 11)
|
||||||
|
|
||||||
|
#define LOAD_MSG_AVX2_13_7_12_3_11_14_1_9_5_15_8_2_0_4_6_10() \
|
||||||
|
LOAD_MSG_AVX2_Y12(13, 7, 12, 3); \
|
||||||
|
LOAD_MSG_AVX2_Y13(11, 14, 1, 9); \
|
||||||
|
LOAD_MSG_AVX2_Y14(5, 15, 8, 2); \
|
||||||
|
VMOVQ_SI_X15_0; \
|
||||||
|
VMOVQ_SI_X11(6*8); \
|
||||||
|
VPINSRQ_1_SI_X15(4*8); \
|
||||||
|
VPINSRQ_1_SI_X11(10*8); \
|
||||||
|
VINSERTI128 $1, X11, Y15, Y15
|
||||||
|
|
||||||
|
#define LOAD_MSG_AVX2_6_14_11_0_15_9_3_8_12_13_1_10_2_7_4_5() \
|
||||||
|
VMOVQ_SI_X12(6*8); \
|
||||||
|
VMOVQ_SI_X11(11*8); \
|
||||||
|
VPINSRQ_1_SI_X12(14*8); \
|
||||||
|
VPINSRQ_1_SI_X11_0; \
|
||||||
|
VINSERTI128 $1, X11, Y12, Y12; \
|
||||||
|
LOAD_MSG_AVX2_Y13(15, 9, 3, 8); \
|
||||||
|
VMOVQ_SI_X11(1*8); \
|
||||||
|
VMOVDQU 12*8(SI), X14; \
|
||||||
|
VPINSRQ_1_SI_X11(10*8); \
|
||||||
|
VINSERTI128 $1, X11, Y14, Y14; \
|
||||||
|
VMOVQ_SI_X15(2*8); \
|
||||||
|
VMOVDQU 4*8(SI), X11; \
|
||||||
|
VPINSRQ_1_SI_X15(7*8); \
|
||||||
|
VINSERTI128 $1, X11, Y15, Y15
|
||||||
|
|
||||||
|
#define LOAD_MSG_AVX2_10_8_7_1_2_4_6_5_15_9_3_13_11_14_12_0() \
|
||||||
|
LOAD_MSG_AVX2_Y12(10, 8, 7, 1); \
|
||||||
|
VMOVQ_SI_X13(2*8); \
|
||||||
|
VPSHUFD $0x4E, 5*8(SI), X11; \
|
||||||
|
VPINSRQ_1_SI_X13(4*8); \
|
||||||
|
VINSERTI128 $1, X11, Y13, Y13; \
|
||||||
|
LOAD_MSG_AVX2_Y14(15, 9, 3, 13); \
|
||||||
|
VMOVQ_SI_X15(11*8); \
|
||||||
|
VMOVQ_SI_X11(12*8); \
|
||||||
|
VPINSRQ_1_SI_X15(14*8); \
|
||||||
|
VPINSRQ_1_SI_X11_0; \
|
||||||
|
VINSERTI128 $1, X11, Y15, Y15
|
||||||
|
|
||||||
|
// func hashBlocksAVX2(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
|
||||||
|
TEXT ·hashBlocksAVX2(SB), 4, $320-48 // frame size = 288 + 32 byte alignment
|
||||||
|
MOVQ h+0(FP), AX
|
||||||
|
MOVQ c+8(FP), BX
|
||||||
|
MOVQ flag+16(FP), CX
|
||||||
|
MOVQ blocks_base+24(FP), SI
|
||||||
|
MOVQ blocks_len+32(FP), DI
|
||||||
|
|
||||||
|
MOVQ SP, DX
|
||||||
|
MOVQ SP, R9
|
||||||
|
ADDQ $31, R9
|
||||||
|
ANDQ $~31, R9
|
||||||
|
MOVQ R9, SP
|
||||||
|
|
||||||
|
MOVQ CX, 16(SP)
|
||||||
|
XORQ CX, CX
|
||||||
|
MOVQ CX, 24(SP)
|
||||||
|
|
||||||
|
VMOVDQU ·AVX2_c40<>(SB), Y4
|
||||||
|
VMOVDQU ·AVX2_c48<>(SB), Y5
|
||||||
|
|
||||||
|
VMOVDQU 0(AX), Y8
|
||||||
|
VMOVDQU 32(AX), Y9
|
||||||
|
VMOVDQU ·AVX2_iv0<>(SB), Y6
|
||||||
|
VMOVDQU ·AVX2_iv1<>(SB), Y7
|
||||||
|
|
||||||
|
MOVQ 0(BX), R8
|
||||||
|
MOVQ 8(BX), R9
|
||||||
|
MOVQ R9, 8(SP)
|
||||||
|
|
||||||
|
loop:
|
||||||
|
ADDQ $128, R8
|
||||||
|
MOVQ R8, 0(SP)
|
||||||
|
CMPQ R8, $128
|
||||||
|
JGE noinc
|
||||||
|
INCQ R9
|
||||||
|
MOVQ R9, 8(SP)
|
||||||
|
|
||||||
|
noinc:
|
||||||
|
VMOVDQA Y8, Y0
|
||||||
|
VMOVDQA Y9, Y1
|
||||||
|
VMOVDQA Y6, Y2
|
||||||
|
VPXOR 0(SP), Y7, Y3
|
||||||
|
|
||||||
|
LOAD_MSG_AVX2_0_2_4_6_1_3_5_7_8_10_12_14_9_11_13_15()
|
||||||
|
VMOVDQA Y12, 32(SP)
|
||||||
|
VMOVDQA Y13, 64(SP)
|
||||||
|
VMOVDQA Y14, 96(SP)
|
||||||
|
VMOVDQA Y15, 128(SP)
|
||||||
|
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
|
||||||
|
LOAD_MSG_AVX2_14_4_9_13_10_8_15_6_1_0_11_5_12_2_7_3()
|
||||||
|
VMOVDQA Y12, 160(SP)
|
||||||
|
VMOVDQA Y13, 192(SP)
|
||||||
|
VMOVDQA Y14, 224(SP)
|
||||||
|
VMOVDQA Y15, 256(SP)
|
||||||
|
|
||||||
|
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
|
||||||
|
LOAD_MSG_AVX2_11_12_5_15_8_0_2_13_10_3_7_9_14_6_1_4()
|
||||||
|
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
|
||||||
|
LOAD_MSG_AVX2_7_3_13_11_9_1_12_14_2_5_4_15_6_10_0_8()
|
||||||
|
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
|
||||||
|
LOAD_MSG_AVX2_9_5_2_10_0_7_4_15_14_11_6_3_1_12_8_13()
|
||||||
|
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
|
||||||
|
LOAD_MSG_AVX2_2_6_0_8_12_10_11_3_4_7_15_1_13_5_14_9()
|
||||||
|
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
|
||||||
|
LOAD_MSG_AVX2_12_1_14_4_5_15_13_10_0_6_9_8_7_3_2_11()
|
||||||
|
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
|
||||||
|
LOAD_MSG_AVX2_13_7_12_3_11_14_1_9_5_15_8_2_0_4_6_10()
|
||||||
|
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
|
||||||
|
LOAD_MSG_AVX2_6_14_11_0_15_9_3_8_12_13_1_10_2_7_4_5()
|
||||||
|
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
|
||||||
|
LOAD_MSG_AVX2_10_8_7_1_2_4_6_5_15_9_3_13_11_14_12_0()
|
||||||
|
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
|
||||||
|
|
||||||
|
ROUND_AVX2(32(SP), 64(SP), 96(SP), 128(SP), Y10, Y4, Y5)
|
||||||
|
ROUND_AVX2(160(SP), 192(SP), 224(SP), 256(SP), Y10, Y4, Y5)
|
||||||
|
|
||||||
|
VPXOR Y0, Y8, Y8
|
||||||
|
VPXOR Y1, Y9, Y9
|
||||||
|
VPXOR Y2, Y8, Y8
|
||||||
|
VPXOR Y3, Y9, Y9
|
||||||
|
|
||||||
|
LEAQ 128(SI), SI
|
||||||
|
SUBQ $128, DI
|
||||||
|
JNE loop
|
||||||
|
|
||||||
|
MOVQ R8, 0(BX)
|
||||||
|
MOVQ R9, 8(BX)
|
||||||
|
|
||||||
|
VMOVDQU Y8, 0(AX)
|
||||||
|
VMOVDQU Y9, 32(AX)
|
||||||
|
VZEROUPPER
|
||||||
|
|
||||||
|
MOVQ DX, SP
|
||||||
|
RET
|
||||||
|
|
||||||
|
#define VPUNPCKLQDQ_X2_X2_X15 BYTE $0xC5; BYTE $0x69; BYTE $0x6C; BYTE $0xFA
|
||||||
|
#define VPUNPCKLQDQ_X3_X3_X15 BYTE $0xC5; BYTE $0x61; BYTE $0x6C; BYTE $0xFB
|
||||||
|
#define VPUNPCKLQDQ_X7_X7_X15 BYTE $0xC5; BYTE $0x41; BYTE $0x6C; BYTE $0xFF
|
||||||
|
#define VPUNPCKLQDQ_X13_X13_X15 BYTE $0xC4; BYTE $0x41; BYTE $0x11; BYTE $0x6C; BYTE $0xFD
|
||||||
|
#define VPUNPCKLQDQ_X14_X14_X15 BYTE $0xC4; BYTE $0x41; BYTE $0x09; BYTE $0x6C; BYTE $0xFE
|
||||||
|
|
||||||
|
#define VPUNPCKHQDQ_X15_X2_X2 BYTE $0xC4; BYTE $0xC1; BYTE $0x69; BYTE $0x6D; BYTE $0xD7
|
||||||
|
#define VPUNPCKHQDQ_X15_X3_X3 BYTE $0xC4; BYTE $0xC1; BYTE $0x61; BYTE $0x6D; BYTE $0xDF
|
||||||
|
#define VPUNPCKHQDQ_X15_X6_X6 BYTE $0xC4; BYTE $0xC1; BYTE $0x49; BYTE $0x6D; BYTE $0xF7
|
||||||
|
#define VPUNPCKHQDQ_X15_X7_X7 BYTE $0xC4; BYTE $0xC1; BYTE $0x41; BYTE $0x6D; BYTE $0xFF
|
||||||
|
#define VPUNPCKHQDQ_X15_X3_X2 BYTE $0xC4; BYTE $0xC1; BYTE $0x61; BYTE $0x6D; BYTE $0xD7
|
||||||
|
#define VPUNPCKHQDQ_X15_X7_X6 BYTE $0xC4; BYTE $0xC1; BYTE $0x41; BYTE $0x6D; BYTE $0xF7
|
||||||
|
#define VPUNPCKHQDQ_X15_X13_X3 BYTE $0xC4; BYTE $0xC1; BYTE $0x11; BYTE $0x6D; BYTE $0xDF
|
||||||
|
#define VPUNPCKHQDQ_X15_X13_X7 BYTE $0xC4; BYTE $0xC1; BYTE $0x11; BYTE $0x6D; BYTE $0xFF
|
||||||
|
|
||||||
|
#define SHUFFLE_AVX() \
|
||||||
|
VMOVDQA X6, X13; \
|
||||||
|
VMOVDQA X2, X14; \
|
||||||
|
VMOVDQA X4, X6; \
|
||||||
|
VPUNPCKLQDQ_X13_X13_X15; \
|
||||||
|
VMOVDQA X5, X4; \
|
||||||
|
VMOVDQA X6, X5; \
|
||||||
|
VPUNPCKHQDQ_X15_X7_X6; \
|
||||||
|
VPUNPCKLQDQ_X7_X7_X15; \
|
||||||
|
VPUNPCKHQDQ_X15_X13_X7; \
|
||||||
|
VPUNPCKLQDQ_X3_X3_X15; \
|
||||||
|
VPUNPCKHQDQ_X15_X2_X2; \
|
||||||
|
VPUNPCKLQDQ_X14_X14_X15; \
|
||||||
|
VPUNPCKHQDQ_X15_X3_X3; \
|
||||||
|
|
||||||
|
#define SHUFFLE_AVX_INV() \
|
||||||
|
VMOVDQA X2, X13; \
|
||||||
|
VMOVDQA X4, X14; \
|
||||||
|
VPUNPCKLQDQ_X2_X2_X15; \
|
||||||
|
VMOVDQA X5, X4; \
|
||||||
|
VPUNPCKHQDQ_X15_X3_X2; \
|
||||||
|
VMOVDQA X14, X5; \
|
||||||
|
VPUNPCKLQDQ_X3_X3_X15; \
|
||||||
|
VMOVDQA X6, X14; \
|
||||||
|
VPUNPCKHQDQ_X15_X13_X3; \
|
||||||
|
VPUNPCKLQDQ_X7_X7_X15; \
|
||||||
|
VPUNPCKHQDQ_X15_X6_X6; \
|
||||||
|
VPUNPCKLQDQ_X14_X14_X15; \
|
||||||
|
VPUNPCKHQDQ_X15_X7_X7; \
|
||||||
|
|
||||||
|
#define HALF_ROUND_AVX(v0, v1, v2, v3, v4, v5, v6, v7, m0, m1, m2, m3, t0, c40, c48) \
|
||||||
|
VPADDQ m0, v0, v0; \
|
||||||
|
VPADDQ v2, v0, v0; \
|
||||||
|
VPADDQ m1, v1, v1; \
|
||||||
|
VPADDQ v3, v1, v1; \
|
||||||
|
VPXOR v0, v6, v6; \
|
||||||
|
VPXOR v1, v7, v7; \
|
||||||
|
VPSHUFD $-79, v6, v6; \
|
||||||
|
VPSHUFD $-79, v7, v7; \
|
||||||
|
VPADDQ v6, v4, v4; \
|
||||||
|
VPADDQ v7, v5, v5; \
|
||||||
|
VPXOR v4, v2, v2; \
|
||||||
|
VPXOR v5, v3, v3; \
|
||||||
|
VPSHUFB c40, v2, v2; \
|
||||||
|
VPSHUFB c40, v3, v3; \
|
||||||
|
VPADDQ m2, v0, v0; \
|
||||||
|
VPADDQ v2, v0, v0; \
|
||||||
|
VPADDQ m3, v1, v1; \
|
||||||
|
VPADDQ v3, v1, v1; \
|
||||||
|
VPXOR v0, v6, v6; \
|
||||||
|
VPXOR v1, v7, v7; \
|
||||||
|
VPSHUFB c48, v6, v6; \
|
||||||
|
VPSHUFB c48, v7, v7; \
|
||||||
|
VPADDQ v6, v4, v4; \
|
||||||
|
VPADDQ v7, v5, v5; \
|
||||||
|
VPXOR v4, v2, v2; \
|
||||||
|
VPXOR v5, v3, v3; \
|
||||||
|
VPADDQ v2, v2, t0; \
|
||||||
|
VPSRLQ $63, v2, v2; \
|
||||||
|
VPXOR t0, v2, v2; \
|
||||||
|
VPADDQ v3, v3, t0; \
|
||||||
|
VPSRLQ $63, v3, v3; \
|
||||||
|
VPXOR t0, v3, v3
|
||||||
|
|
||||||
|
// load msg: X12 = (i0, i1), X13 = (i2, i3), X14 = (i4, i5), X15 = (i6, i7)
|
||||||
|
// i0, i1, i2, i3, i4, i5, i6, i7 must not be 0
|
||||||
|
#define LOAD_MSG_AVX(i0, i1, i2, i3, i4, i5, i6, i7) \
|
||||||
|
VMOVQ_SI_X12(i0*8); \
|
||||||
|
VMOVQ_SI_X13(i2*8); \
|
||||||
|
VMOVQ_SI_X14(i4*8); \
|
||||||
|
VMOVQ_SI_X15(i6*8); \
|
||||||
|
VPINSRQ_1_SI_X12(i1*8); \
|
||||||
|
VPINSRQ_1_SI_X13(i3*8); \
|
||||||
|
VPINSRQ_1_SI_X14(i5*8); \
|
||||||
|
VPINSRQ_1_SI_X15(i7*8)
|
||||||
|
|
||||||
|
// load msg: X12 = (0, 2), X13 = (4, 6), X14 = (1, 3), X15 = (5, 7)
|
||||||
|
#define LOAD_MSG_AVX_0_2_4_6_1_3_5_7() \
|
||||||
|
VMOVQ_SI_X12_0; \
|
||||||
|
VMOVQ_SI_X13(4*8); \
|
||||||
|
VMOVQ_SI_X14(1*8); \
|
||||||
|
VMOVQ_SI_X15(5*8); \
|
||||||
|
VPINSRQ_1_SI_X12(2*8); \
|
||||||
|
VPINSRQ_1_SI_X13(6*8); \
|
||||||
|
VPINSRQ_1_SI_X14(3*8); \
|
||||||
|
VPINSRQ_1_SI_X15(7*8)
|
||||||
|
|
||||||
|
// load msg: X12 = (1, 0), X13 = (11, 5), X14 = (12, 2), X15 = (7, 3)
|
||||||
|
#define LOAD_MSG_AVX_1_0_11_5_12_2_7_3() \
|
||||||
|
VPSHUFD $0x4E, 0*8(SI), X12; \
|
||||||
|
VMOVQ_SI_X13(11*8); \
|
||||||
|
VMOVQ_SI_X14(12*8); \
|
||||||
|
VMOVQ_SI_X15(7*8); \
|
||||||
|
VPINSRQ_1_SI_X13(5*8); \
|
||||||
|
VPINSRQ_1_SI_X14(2*8); \
|
||||||
|
VPINSRQ_1_SI_X15(3*8)
|
||||||
|
|
||||||
|
// load msg: X12 = (11, 12), X13 = (5, 15), X14 = (8, 0), X15 = (2, 13)
|
||||||
|
#define LOAD_MSG_AVX_11_12_5_15_8_0_2_13() \
|
||||||
|
VMOVDQU 11*8(SI), X12; \
|
||||||
|
VMOVQ_SI_X13(5*8); \
|
||||||
|
VMOVQ_SI_X14(8*8); \
|
||||||
|
VMOVQ_SI_X15(2*8); \
|
||||||
|
VPINSRQ_1_SI_X13(15*8); \
|
||||||
|
VPINSRQ_1_SI_X14_0; \
|
||||||
|
VPINSRQ_1_SI_X15(13*8)
|
||||||
|
|
||||||
|
// load msg: X12 = (2, 5), X13 = (4, 15), X14 = (6, 10), X15 = (0, 8)
|
||||||
|
#define LOAD_MSG_AVX_2_5_4_15_6_10_0_8() \
|
||||||
|
VMOVQ_SI_X12(2*8); \
|
||||||
|
VMOVQ_SI_X13(4*8); \
|
||||||
|
VMOVQ_SI_X14(6*8); \
|
||||||
|
VMOVQ_SI_X15_0; \
|
||||||
|
VPINSRQ_1_SI_X12(5*8); \
|
||||||
|
VPINSRQ_1_SI_X13(15*8); \
|
||||||
|
VPINSRQ_1_SI_X14(10*8); \
|
||||||
|
VPINSRQ_1_SI_X15(8*8)
|
||||||
|
|
||||||
|
// load msg: X12 = (9, 5), X13 = (2, 10), X14 = (0, 7), X15 = (4, 15)
|
||||||
|
#define LOAD_MSG_AVX_9_5_2_10_0_7_4_15() \
|
||||||
|
VMOVQ_SI_X12(9*8); \
|
||||||
|
VMOVQ_SI_X13(2*8); \
|
||||||
|
VMOVQ_SI_X14_0; \
|
||||||
|
VMOVQ_SI_X15(4*8); \
|
||||||
|
VPINSRQ_1_SI_X12(5*8); \
|
||||||
|
VPINSRQ_1_SI_X13(10*8); \
|
||||||
|
VPINSRQ_1_SI_X14(7*8); \
|
||||||
|
VPINSRQ_1_SI_X15(15*8)
|
||||||
|
|
||||||
|
// load msg: X12 = (2, 6), X13 = (0, 8), X14 = (12, 10), X15 = (11, 3)
|
||||||
|
#define LOAD_MSG_AVX_2_6_0_8_12_10_11_3() \
|
||||||
|
VMOVQ_SI_X12(2*8); \
|
||||||
|
VMOVQ_SI_X13_0; \
|
||||||
|
VMOVQ_SI_X14(12*8); \
|
||||||
|
VMOVQ_SI_X15(11*8); \
|
||||||
|
VPINSRQ_1_SI_X12(6*8); \
|
||||||
|
VPINSRQ_1_SI_X13(8*8); \
|
||||||
|
VPINSRQ_1_SI_X14(10*8); \
|
||||||
|
VPINSRQ_1_SI_X15(3*8)
|
||||||
|
|
||||||
|
// load msg: X12 = (0, 6), X13 = (9, 8), X14 = (7, 3), X15 = (2, 11)
|
||||||
|
#define LOAD_MSG_AVX_0_6_9_8_7_3_2_11() \
|
||||||
|
MOVQ 0*8(SI), X12; \
|
||||||
|
VPSHUFD $0x4E, 8*8(SI), X13; \
|
||||||
|
MOVQ 7*8(SI), X14; \
|
||||||
|
MOVQ 2*8(SI), X15; \
|
||||||
|
VPINSRQ_1_SI_X12(6*8); \
|
||||||
|
VPINSRQ_1_SI_X14(3*8); \
|
||||||
|
VPINSRQ_1_SI_X15(11*8)
|
||||||
|
|
||||||
|
// load msg: X12 = (6, 14), X13 = (11, 0), X14 = (15, 9), X15 = (3, 8)
|
||||||
|
#define LOAD_MSG_AVX_6_14_11_0_15_9_3_8() \
|
||||||
|
MOVQ 6*8(SI), X12; \
|
||||||
|
MOVQ 11*8(SI), X13; \
|
||||||
|
MOVQ 15*8(SI), X14; \
|
||||||
|
MOVQ 3*8(SI), X15; \
|
||||||
|
VPINSRQ_1_SI_X12(14*8); \
|
||||||
|
VPINSRQ_1_SI_X13_0; \
|
||||||
|
VPINSRQ_1_SI_X14(9*8); \
|
||||||
|
VPINSRQ_1_SI_X15(8*8)
|
||||||
|
|
||||||
|
// load msg: X12 = (5, 15), X13 = (8, 2), X14 = (0, 4), X15 = (6, 10)
|
||||||
|
#define LOAD_MSG_AVX_5_15_8_2_0_4_6_10() \
|
||||||
|
MOVQ 5*8(SI), X12; \
|
||||||
|
MOVQ 8*8(SI), X13; \
|
||||||
|
MOVQ 0*8(SI), X14; \
|
||||||
|
MOVQ 6*8(SI), X15; \
|
||||||
|
VPINSRQ_1_SI_X12(15*8); \
|
||||||
|
VPINSRQ_1_SI_X13(2*8); \
|
||||||
|
VPINSRQ_1_SI_X14(4*8); \
|
||||||
|
VPINSRQ_1_SI_X15(10*8)
|
||||||
|
|
||||||
|
// load msg: X12 = (12, 13), X13 = (1, 10), X14 = (2, 7), X15 = (4, 5)
|
||||||
|
#define LOAD_MSG_AVX_12_13_1_10_2_7_4_5() \
|
||||||
|
VMOVDQU 12*8(SI), X12; \
|
||||||
|
MOVQ 1*8(SI), X13; \
|
||||||
|
MOVQ 2*8(SI), X14; \
|
||||||
|
VPINSRQ_1_SI_X13(10*8); \
|
||||||
|
VPINSRQ_1_SI_X14(7*8); \
|
||||||
|
VMOVDQU 4*8(SI), X15
|
||||||
|
|
||||||
|
// load msg: X12 = (15, 9), X13 = (3, 13), X14 = (11, 14), X15 = (12, 0)
|
||||||
|
#define LOAD_MSG_AVX_15_9_3_13_11_14_12_0() \
|
||||||
|
MOVQ 15*8(SI), X12; \
|
||||||
|
MOVQ 3*8(SI), X13; \
|
||||||
|
MOVQ 11*8(SI), X14; \
|
||||||
|
MOVQ 12*8(SI), X15; \
|
||||||
|
VPINSRQ_1_SI_X12(9*8); \
|
||||||
|
VPINSRQ_1_SI_X13(13*8); \
|
||||||
|
VPINSRQ_1_SI_X14(14*8); \
|
||||||
|
VPINSRQ_1_SI_X15_0
|
||||||
|
|
||||||
|
// func hashBlocksAVX(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
|
||||||
|
TEXT ·hashBlocksAVX(SB), 4, $288-48 // frame size = 272 + 16 byte alignment
|
||||||
|
MOVQ h+0(FP), AX
|
||||||
|
MOVQ c+8(FP), BX
|
||||||
|
MOVQ flag+16(FP), CX
|
||||||
|
MOVQ blocks_base+24(FP), SI
|
||||||
|
MOVQ blocks_len+32(FP), DI
|
||||||
|
|
||||||
|
MOVQ SP, BP
|
||||||
|
MOVQ SP, R9
|
||||||
|
ADDQ $15, R9
|
||||||
|
ANDQ $~15, R9
|
||||||
|
MOVQ R9, SP
|
||||||
|
|
||||||
|
VMOVDQU ·AVX_c40<>(SB), X0
|
||||||
|
VMOVDQU ·AVX_c48<>(SB), X1
|
||||||
|
VMOVDQA X0, X8
|
||||||
|
VMOVDQA X1, X9
|
||||||
|
|
||||||
|
VMOVDQU ·AVX_iv3<>(SB), X0
|
||||||
|
VMOVDQA X0, 0(SP)
|
||||||
|
XORQ CX, 0(SP) // 0(SP) = ·AVX_iv3 ^ (CX || 0)
|
||||||
|
|
||||||
|
VMOVDQU 0(AX), X10
|
||||||
|
VMOVDQU 16(AX), X11
|
||||||
|
VMOVDQU 32(AX), X2
|
||||||
|
VMOVDQU 48(AX), X3
|
||||||
|
|
||||||
|
MOVQ 0(BX), R8
|
||||||
|
MOVQ 8(BX), R9
|
||||||
|
|
||||||
|
loop:
|
||||||
|
ADDQ $128, R8
|
||||||
|
CMPQ R8, $128
|
||||||
|
JGE noinc
|
||||||
|
INCQ R9
|
||||||
|
|
||||||
|
noinc:
|
||||||
|
VMOVQ_R8_X15
|
||||||
|
VPINSRQ_1_R9_X15
|
||||||
|
|
||||||
|
VMOVDQA X10, X0
|
||||||
|
VMOVDQA X11, X1
|
||||||
|
VMOVDQU ·AVX_iv0<>(SB), X4
|
||||||
|
VMOVDQU ·AVX_iv1<>(SB), X5
|
||||||
|
VMOVDQU ·AVX_iv2<>(SB), X6
|
||||||
|
|
||||||
|
VPXOR X15, X6, X6
|
||||||
|
VMOVDQA 0(SP), X7
|
||||||
|
|
||||||
|
LOAD_MSG_AVX_0_2_4_6_1_3_5_7()
|
||||||
|
VMOVDQA X12, 16(SP)
|
||||||
|
VMOVDQA X13, 32(SP)
|
||||||
|
VMOVDQA X14, 48(SP)
|
||||||
|
VMOVDQA X15, 64(SP)
|
||||||
|
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||||
|
SHUFFLE_AVX()
|
||||||
|
LOAD_MSG_AVX(8, 10, 12, 14, 9, 11, 13, 15)
|
||||||
|
VMOVDQA X12, 80(SP)
|
||||||
|
VMOVDQA X13, 96(SP)
|
||||||
|
VMOVDQA X14, 112(SP)
|
||||||
|
VMOVDQA X15, 128(SP)
|
||||||
|
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||||
|
SHUFFLE_AVX_INV()
|
||||||
|
|
||||||
|
LOAD_MSG_AVX(14, 4, 9, 13, 10, 8, 15, 6)
|
||||||
|
VMOVDQA X12, 144(SP)
|
||||||
|
VMOVDQA X13, 160(SP)
|
||||||
|
VMOVDQA X14, 176(SP)
|
||||||
|
VMOVDQA X15, 192(SP)
|
||||||
|
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||||
|
SHUFFLE_AVX()
|
||||||
|
LOAD_MSG_AVX_1_0_11_5_12_2_7_3()
|
||||||
|
VMOVDQA X12, 208(SP)
|
||||||
|
VMOVDQA X13, 224(SP)
|
||||||
|
VMOVDQA X14, 240(SP)
|
||||||
|
VMOVDQA X15, 256(SP)
|
||||||
|
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||||
|
SHUFFLE_AVX_INV()
|
||||||
|
|
||||||
|
LOAD_MSG_AVX_11_12_5_15_8_0_2_13()
|
||||||
|
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||||
|
SHUFFLE_AVX()
|
||||||
|
LOAD_MSG_AVX(10, 3, 7, 9, 14, 6, 1, 4)
|
||||||
|
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||||
|
SHUFFLE_AVX_INV()
|
||||||
|
|
||||||
|
LOAD_MSG_AVX(7, 3, 13, 11, 9, 1, 12, 14)
|
||||||
|
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||||
|
SHUFFLE_AVX()
|
||||||
|
LOAD_MSG_AVX_2_5_4_15_6_10_0_8()
|
||||||
|
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||||
|
SHUFFLE_AVX_INV()
|
||||||
|
|
||||||
|
LOAD_MSG_AVX_9_5_2_10_0_7_4_15()
|
||||||
|
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||||
|
SHUFFLE_AVX()
|
||||||
|
LOAD_MSG_AVX(14, 11, 6, 3, 1, 12, 8, 13)
|
||||||
|
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||||
|
SHUFFLE_AVX_INV()
|
||||||
|
|
||||||
|
LOAD_MSG_AVX_2_6_0_8_12_10_11_3()
|
||||||
|
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||||
|
SHUFFLE_AVX()
|
||||||
|
LOAD_MSG_AVX(4, 7, 15, 1, 13, 5, 14, 9)
|
||||||
|
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||||
|
SHUFFLE_AVX_INV()
|
||||||
|
|
||||||
|
LOAD_MSG_AVX(12, 1, 14, 4, 5, 15, 13, 10)
|
||||||
|
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||||
|
SHUFFLE_AVX()
|
||||||
|
LOAD_MSG_AVX_0_6_9_8_7_3_2_11()
|
||||||
|
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||||
|
SHUFFLE_AVX_INV()
|
||||||
|
|
||||||
|
LOAD_MSG_AVX(13, 7, 12, 3, 11, 14, 1, 9)
|
||||||
|
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||||
|
SHUFFLE_AVX()
|
||||||
|
LOAD_MSG_AVX_5_15_8_2_0_4_6_10()
|
||||||
|
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||||
|
SHUFFLE_AVX_INV()
|
||||||
|
|
||||||
|
LOAD_MSG_AVX_6_14_11_0_15_9_3_8()
|
||||||
|
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||||
|
SHUFFLE_AVX()
|
||||||
|
LOAD_MSG_AVX_12_13_1_10_2_7_4_5()
|
||||||
|
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||||
|
SHUFFLE_AVX_INV()
|
||||||
|
|
||||||
|
LOAD_MSG_AVX(10, 8, 7, 1, 2, 4, 6, 5)
|
||||||
|
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||||
|
SHUFFLE_AVX()
|
||||||
|
LOAD_MSG_AVX_15_9_3_13_11_14_12_0()
|
||||||
|
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||||
|
SHUFFLE_AVX_INV()
|
||||||
|
|
||||||
|
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 16(SP), 32(SP), 48(SP), 64(SP), X15, X8, X9)
|
||||||
|
SHUFFLE_AVX()
|
||||||
|
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 80(SP), 96(SP), 112(SP), 128(SP), X15, X8, X9)
|
||||||
|
SHUFFLE_AVX_INV()
|
||||||
|
|
||||||
|
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 144(SP), 160(SP), 176(SP), 192(SP), X15, X8, X9)
|
||||||
|
SHUFFLE_AVX()
|
||||||
|
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 208(SP), 224(SP), 240(SP), 256(SP), X15, X8, X9)
|
||||||
|
SHUFFLE_AVX_INV()
|
||||||
|
|
||||||
|
VMOVDQU 32(AX), X14
|
||||||
|
VMOVDQU 48(AX), X15
|
||||||
|
VPXOR X0, X10, X10
|
||||||
|
VPXOR X1, X11, X11
|
||||||
|
VPXOR X2, X14, X14
|
||||||
|
VPXOR X3, X15, X15
|
||||||
|
VPXOR X4, X10, X10
|
||||||
|
VPXOR X5, X11, X11
|
||||||
|
VPXOR X6, X14, X2
|
||||||
|
VPXOR X7, X15, X3
|
||||||
|
VMOVDQU X2, 32(AX)
|
||||||
|
VMOVDQU X3, 48(AX)
|
||||||
|
|
||||||
|
LEAQ 128(SI), SI
|
||||||
|
SUBQ $128, DI
|
||||||
|
JNE loop
|
||||||
|
|
||||||
|
VMOVDQU X10, 0(AX)
|
||||||
|
VMOVDQU X11, 16(AX)
|
||||||
|
|
||||||
|
MOVQ R8, 0(BX)
|
||||||
|
MOVQ R9, 8(BX)
|
||||||
|
VZEROUPPER
|
||||||
|
|
||||||
|
MOVQ BP, SP
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func supportsAVX2() bool
|
||||||
|
TEXT ·supportsAVX2(SB), 4, $0-1
|
||||||
|
MOVQ runtime·support_avx2(SB), AX
|
||||||
|
MOVB AX, ret+0(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func supportsAVX() bool
|
||||||
|
TEXT ·supportsAVX(SB), 4, $0-1
|
||||||
|
MOVQ runtime·support_avx(SB), AX
|
||||||
|
MOVB AX, ret+0(FP)
|
||||||
|
RET
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !go1.7,amd64,!gccgo,!appengine
|
||||||
|
|
||||||
|
package blake2b
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
useSSE4 = supportsSSE4()
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func supportsSSE4() bool
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func hashBlocksSSE4(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
|
||||||
|
|
||||||
|
func hashBlocks(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) {
|
||||||
|
if useSSE4 {
|
||||||
|
hashBlocksSSE4(h, c, flag, blocks)
|
||||||
|
} else {
|
||||||
|
hashBlocksGeneric(h, c, flag, blocks)
|
||||||
|
}
|
||||||
|
}
|
||||||
+290
@@ -0,0 +1,290 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build amd64,!gccgo,!appengine
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
DATA ·iv0<>+0x00(SB)/8, $0x6a09e667f3bcc908
|
||||||
|
DATA ·iv0<>+0x08(SB)/8, $0xbb67ae8584caa73b
|
||||||
|
GLOBL ·iv0<>(SB), (NOPTR+RODATA), $16
|
||||||
|
|
||||||
|
DATA ·iv1<>+0x00(SB)/8, $0x3c6ef372fe94f82b
|
||||||
|
DATA ·iv1<>+0x08(SB)/8, $0xa54ff53a5f1d36f1
|
||||||
|
GLOBL ·iv1<>(SB), (NOPTR+RODATA), $16
|
||||||
|
|
||||||
|
DATA ·iv2<>+0x00(SB)/8, $0x510e527fade682d1
|
||||||
|
DATA ·iv2<>+0x08(SB)/8, $0x9b05688c2b3e6c1f
|
||||||
|
GLOBL ·iv2<>(SB), (NOPTR+RODATA), $16
|
||||||
|
|
||||||
|
DATA ·iv3<>+0x00(SB)/8, $0x1f83d9abfb41bd6b
|
||||||
|
DATA ·iv3<>+0x08(SB)/8, $0x5be0cd19137e2179
|
||||||
|
GLOBL ·iv3<>(SB), (NOPTR+RODATA), $16
|
||||||
|
|
||||||
|
DATA ·c40<>+0x00(SB)/8, $0x0201000706050403
|
||||||
|
DATA ·c40<>+0x08(SB)/8, $0x0a09080f0e0d0c0b
|
||||||
|
GLOBL ·c40<>(SB), (NOPTR+RODATA), $16
|
||||||
|
|
||||||
|
DATA ·c48<>+0x00(SB)/8, $0x0100070605040302
|
||||||
|
DATA ·c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a
|
||||||
|
GLOBL ·c48<>(SB), (NOPTR+RODATA), $16
|
||||||
|
|
||||||
|
#define SHUFFLE(v2, v3, v4, v5, v6, v7, t1, t2) \
|
||||||
|
MOVO v4, t1; \
|
||||||
|
MOVO v5, v4; \
|
||||||
|
MOVO t1, v5; \
|
||||||
|
MOVO v6, t1; \
|
||||||
|
PUNPCKLQDQ v6, t2; \
|
||||||
|
PUNPCKHQDQ v7, v6; \
|
||||||
|
PUNPCKHQDQ t2, v6; \
|
||||||
|
PUNPCKLQDQ v7, t2; \
|
||||||
|
MOVO t1, v7; \
|
||||||
|
MOVO v2, t1; \
|
||||||
|
PUNPCKHQDQ t2, v7; \
|
||||||
|
PUNPCKLQDQ v3, t2; \
|
||||||
|
PUNPCKHQDQ t2, v2; \
|
||||||
|
PUNPCKLQDQ t1, t2; \
|
||||||
|
PUNPCKHQDQ t2, v3
|
||||||
|
|
||||||
|
#define SHUFFLE_INV(v2, v3, v4, v5, v6, v7, t1, t2) \
|
||||||
|
MOVO v4, t1; \
|
||||||
|
MOVO v5, v4; \
|
||||||
|
MOVO t1, v5; \
|
||||||
|
MOVO v2, t1; \
|
||||||
|
PUNPCKLQDQ v2, t2; \
|
||||||
|
PUNPCKHQDQ v3, v2; \
|
||||||
|
PUNPCKHQDQ t2, v2; \
|
||||||
|
PUNPCKLQDQ v3, t2; \
|
||||||
|
MOVO t1, v3; \
|
||||||
|
MOVO v6, t1; \
|
||||||
|
PUNPCKHQDQ t2, v3; \
|
||||||
|
PUNPCKLQDQ v7, t2; \
|
||||||
|
PUNPCKHQDQ t2, v6; \
|
||||||
|
PUNPCKLQDQ t1, t2; \
|
||||||
|
PUNPCKHQDQ t2, v7
|
||||||
|
|
||||||
|
#define HALF_ROUND(v0, v1, v2, v3, v4, v5, v6, v7, m0, m1, m2, m3, t0, c40, c48) \
|
||||||
|
PADDQ m0, v0; \
|
||||||
|
PADDQ m1, v1; \
|
||||||
|
PADDQ v2, v0; \
|
||||||
|
PADDQ v3, v1; \
|
||||||
|
PXOR v0, v6; \
|
||||||
|
PXOR v1, v7; \
|
||||||
|
PSHUFD $0xB1, v6, v6; \
|
||||||
|
PSHUFD $0xB1, v7, v7; \
|
||||||
|
PADDQ v6, v4; \
|
||||||
|
PADDQ v7, v5; \
|
||||||
|
PXOR v4, v2; \
|
||||||
|
PXOR v5, v3; \
|
||||||
|
PSHUFB c40, v2; \
|
||||||
|
PSHUFB c40, v3; \
|
||||||
|
PADDQ m2, v0; \
|
||||||
|
PADDQ m3, v1; \
|
||||||
|
PADDQ v2, v0; \
|
||||||
|
PADDQ v3, v1; \
|
||||||
|
PXOR v0, v6; \
|
||||||
|
PXOR v1, v7; \
|
||||||
|
PSHUFB c48, v6; \
|
||||||
|
PSHUFB c48, v7; \
|
||||||
|
PADDQ v6, v4; \
|
||||||
|
PADDQ v7, v5; \
|
||||||
|
PXOR v4, v2; \
|
||||||
|
PXOR v5, v3; \
|
||||||
|
MOVOU v2, t0; \
|
||||||
|
PADDQ v2, t0; \
|
||||||
|
PSRLQ $63, v2; \
|
||||||
|
PXOR t0, v2; \
|
||||||
|
MOVOU v3, t0; \
|
||||||
|
PADDQ v3, t0; \
|
||||||
|
PSRLQ $63, v3; \
|
||||||
|
PXOR t0, v3
|
||||||
|
|
||||||
|
#define LOAD_MSG(m0, m1, m2, m3, src, i0, i1, i2, i3, i4, i5, i6, i7) \
|
||||||
|
MOVQ i0*8(src), m0; \
|
||||||
|
PINSRQ $1, i1*8(src), m0; \
|
||||||
|
MOVQ i2*8(src), m1; \
|
||||||
|
PINSRQ $1, i3*8(src), m1; \
|
||||||
|
MOVQ i4*8(src), m2; \
|
||||||
|
PINSRQ $1, i5*8(src), m2; \
|
||||||
|
MOVQ i6*8(src), m3; \
|
||||||
|
PINSRQ $1, i7*8(src), m3
|
||||||
|
|
||||||
|
// func hashBlocksSSE4(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
|
||||||
|
TEXT ·hashBlocksSSE4(SB), 4, $288-48 // frame size = 272 + 16 byte alignment
|
||||||
|
MOVQ h+0(FP), AX
|
||||||
|
MOVQ c+8(FP), BX
|
||||||
|
MOVQ flag+16(FP), CX
|
||||||
|
MOVQ blocks_base+24(FP), SI
|
||||||
|
MOVQ blocks_len+32(FP), DI
|
||||||
|
|
||||||
|
MOVQ SP, BP
|
||||||
|
MOVQ SP, R9
|
||||||
|
ADDQ $15, R9
|
||||||
|
ANDQ $~15, R9
|
||||||
|
MOVQ R9, SP
|
||||||
|
|
||||||
|
MOVOU ·iv3<>(SB), X0
|
||||||
|
MOVO X0, 0(SP)
|
||||||
|
XORQ CX, 0(SP) // 0(SP) = ·iv3 ^ (CX || 0)
|
||||||
|
|
||||||
|
MOVOU ·c40<>(SB), X13
|
||||||
|
MOVOU ·c48<>(SB), X14
|
||||||
|
|
||||||
|
MOVOU 0(AX), X12
|
||||||
|
MOVOU 16(AX), X15
|
||||||
|
|
||||||
|
MOVQ 0(BX), R8
|
||||||
|
MOVQ 8(BX), R9
|
||||||
|
|
||||||
|
loop:
|
||||||
|
ADDQ $128, R8
|
||||||
|
CMPQ R8, $128
|
||||||
|
JGE noinc
|
||||||
|
INCQ R9
|
||||||
|
|
||||||
|
noinc:
|
||||||
|
MOVQ R8, X8
|
||||||
|
PINSRQ $1, R9, X8
|
||||||
|
|
||||||
|
MOVO X12, X0
|
||||||
|
MOVO X15, X1
|
||||||
|
MOVOU 32(AX), X2
|
||||||
|
MOVOU 48(AX), X3
|
||||||
|
MOVOU ·iv0<>(SB), X4
|
||||||
|
MOVOU ·iv1<>(SB), X5
|
||||||
|
MOVOU ·iv2<>(SB), X6
|
||||||
|
|
||||||
|
PXOR X8, X6
|
||||||
|
MOVO 0(SP), X7
|
||||||
|
|
||||||
|
LOAD_MSG(X8, X9, X10, X11, SI, 0, 2, 4, 6, 1, 3, 5, 7)
|
||||||
|
MOVO X8, 16(SP)
|
||||||
|
MOVO X9, 32(SP)
|
||||||
|
MOVO X10, 48(SP)
|
||||||
|
MOVO X11, 64(SP)
|
||||||
|
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||||
|
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||||
|
LOAD_MSG(X8, X9, X10, X11, SI, 8, 10, 12, 14, 9, 11, 13, 15)
|
||||||
|
MOVO X8, 80(SP)
|
||||||
|
MOVO X9, 96(SP)
|
||||||
|
MOVO X10, 112(SP)
|
||||||
|
MOVO X11, 128(SP)
|
||||||
|
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||||
|
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||||
|
|
||||||
|
LOAD_MSG(X8, X9, X10, X11, SI, 14, 4, 9, 13, 10, 8, 15, 6)
|
||||||
|
MOVO X8, 144(SP)
|
||||||
|
MOVO X9, 160(SP)
|
||||||
|
MOVO X10, 176(SP)
|
||||||
|
MOVO X11, 192(SP)
|
||||||
|
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||||
|
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||||
|
LOAD_MSG(X8, X9, X10, X11, SI, 1, 0, 11, 5, 12, 2, 7, 3)
|
||||||
|
MOVO X8, 208(SP)
|
||||||
|
MOVO X9, 224(SP)
|
||||||
|
MOVO X10, 240(SP)
|
||||||
|
MOVO X11, 256(SP)
|
||||||
|
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||||
|
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||||
|
|
||||||
|
LOAD_MSG(X8, X9, X10, X11, SI, 11, 12, 5, 15, 8, 0, 2, 13)
|
||||||
|
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||||
|
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||||
|
LOAD_MSG(X8, X9, X10, X11, SI, 10, 3, 7, 9, 14, 6, 1, 4)
|
||||||
|
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||||
|
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||||
|
|
||||||
|
LOAD_MSG(X8, X9, X10, X11, SI, 7, 3, 13, 11, 9, 1, 12, 14)
|
||||||
|
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||||
|
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||||
|
LOAD_MSG(X8, X9, X10, X11, SI, 2, 5, 4, 15, 6, 10, 0, 8)
|
||||||
|
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||||
|
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||||
|
|
||||||
|
LOAD_MSG(X8, X9, X10, X11, SI, 9, 5, 2, 10, 0, 7, 4, 15)
|
||||||
|
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||||
|
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||||
|
LOAD_MSG(X8, X9, X10, X11, SI, 14, 11, 6, 3, 1, 12, 8, 13)
|
||||||
|
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||||
|
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||||
|
|
||||||
|
LOAD_MSG(X8, X9, X10, X11, SI, 2, 6, 0, 8, 12, 10, 11, 3)
|
||||||
|
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||||
|
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||||
|
LOAD_MSG(X8, X9, X10, X11, SI, 4, 7, 15, 1, 13, 5, 14, 9)
|
||||||
|
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||||
|
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||||
|
|
||||||
|
LOAD_MSG(X8, X9, X10, X11, SI, 12, 1, 14, 4, 5, 15, 13, 10)
|
||||||
|
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||||
|
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||||
|
LOAD_MSG(X8, X9, X10, X11, SI, 0, 6, 9, 8, 7, 3, 2, 11)
|
||||||
|
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||||
|
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||||
|
|
||||||
|
LOAD_MSG(X8, X9, X10, X11, SI, 13, 7, 12, 3, 11, 14, 1, 9)
|
||||||
|
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||||
|
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||||
|
LOAD_MSG(X8, X9, X10, X11, SI, 5, 15, 8, 2, 0, 4, 6, 10)
|
||||||
|
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||||
|
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||||
|
|
||||||
|
LOAD_MSG(X8, X9, X10, X11, SI, 6, 14, 11, 0, 15, 9, 3, 8)
|
||||||
|
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||||
|
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||||
|
LOAD_MSG(X8, X9, X10, X11, SI, 12, 13, 1, 10, 2, 7, 4, 5)
|
||||||
|
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||||
|
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||||
|
|
||||||
|
LOAD_MSG(X8, X9, X10, X11, SI, 10, 8, 7, 1, 2, 4, 6, 5)
|
||||||
|
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||||
|
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||||
|
LOAD_MSG(X8, X9, X10, X11, SI, 15, 9, 3, 13, 11, 14, 12, 0)
|
||||||
|
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||||
|
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||||
|
|
||||||
|
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, 16(SP), 32(SP), 48(SP), 64(SP), X11, X13, X14)
|
||||||
|
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||||
|
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, 80(SP), 96(SP), 112(SP), 128(SP), X11, X13, X14)
|
||||||
|
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||||
|
|
||||||
|
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, 144(SP), 160(SP), 176(SP), 192(SP), X11, X13, X14)
|
||||||
|
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||||
|
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, 208(SP), 224(SP), 240(SP), 256(SP), X11, X13, X14)
|
||||||
|
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||||
|
|
||||||
|
MOVOU 32(AX), X10
|
||||||
|
MOVOU 48(AX), X11
|
||||||
|
PXOR X0, X12
|
||||||
|
PXOR X1, X15
|
||||||
|
PXOR X2, X10
|
||||||
|
PXOR X3, X11
|
||||||
|
PXOR X4, X12
|
||||||
|
PXOR X5, X15
|
||||||
|
PXOR X6, X10
|
||||||
|
PXOR X7, X11
|
||||||
|
MOVOU X10, 32(AX)
|
||||||
|
MOVOU X11, 48(AX)
|
||||||
|
|
||||||
|
LEAQ 128(SI), SI
|
||||||
|
SUBQ $128, DI
|
||||||
|
JNE loop
|
||||||
|
|
||||||
|
MOVOU X12, 0(AX)
|
||||||
|
MOVOU X15, 16(AX)
|
||||||
|
|
||||||
|
MOVQ R8, 0(BX)
|
||||||
|
MOVQ R9, 8(BX)
|
||||||
|
|
||||||
|
MOVQ BP, SP
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func supportsSSE4() bool
|
||||||
|
TEXT ·supportsSSE4(SB), 4, $0-1
|
||||||
|
MOVL $1, AX
|
||||||
|
CPUID
|
||||||
|
SHRL $19, CX // Bit 19 indicates SSE4 support
|
||||||
|
ANDL $1, CX // CX != 0 if support SSE4
|
||||||
|
MOVB CX, ret+0(FP)
|
||||||
|
RET
|
||||||
+179
@@ -0,0 +1,179 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package blake2b
|
||||||
|
|
||||||
|
import "encoding/binary"
|
||||||
|
|
||||||
|
// the precomputed values for BLAKE2b
|
||||||
|
// there are 12 16-byte arrays - one for each round
|
||||||
|
// the entries are calculated from the sigma constants.
|
||||||
|
var precomputed = [12][16]byte{
|
||||||
|
{0, 2, 4, 6, 1, 3, 5, 7, 8, 10, 12, 14, 9, 11, 13, 15},
|
||||||
|
{14, 4, 9, 13, 10, 8, 15, 6, 1, 0, 11, 5, 12, 2, 7, 3},
|
||||||
|
{11, 12, 5, 15, 8, 0, 2, 13, 10, 3, 7, 9, 14, 6, 1, 4},
|
||||||
|
{7, 3, 13, 11, 9, 1, 12, 14, 2, 5, 4, 15, 6, 10, 0, 8},
|
||||||
|
{9, 5, 2, 10, 0, 7, 4, 15, 14, 11, 6, 3, 1, 12, 8, 13},
|
||||||
|
{2, 6, 0, 8, 12, 10, 11, 3, 4, 7, 15, 1, 13, 5, 14, 9},
|
||||||
|
{12, 1, 14, 4, 5, 15, 13, 10, 0, 6, 9, 8, 7, 3, 2, 11},
|
||||||
|
{13, 7, 12, 3, 11, 14, 1, 9, 5, 15, 8, 2, 0, 4, 6, 10},
|
||||||
|
{6, 14, 11, 0, 15, 9, 3, 8, 12, 13, 1, 10, 2, 7, 4, 5},
|
||||||
|
{10, 8, 7, 1, 2, 4, 6, 5, 15, 9, 3, 13, 11, 14, 12, 0},
|
||||||
|
{0, 2, 4, 6, 1, 3, 5, 7, 8, 10, 12, 14, 9, 11, 13, 15}, // equal to the first
|
||||||
|
{14, 4, 9, 13, 10, 8, 15, 6, 1, 0, 11, 5, 12, 2, 7, 3}, // equal to the second
|
||||||
|
}
|
||||||
|
|
||||||
|
func hashBlocksGeneric(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) {
|
||||||
|
var m [16]uint64
|
||||||
|
c0, c1 := c[0], c[1]
|
||||||
|
|
||||||
|
for i := 0; i < len(blocks); {
|
||||||
|
c0 += BlockSize
|
||||||
|
if c0 < BlockSize {
|
||||||
|
c1++
|
||||||
|
}
|
||||||
|
|
||||||
|
v0, v1, v2, v3, v4, v5, v6, v7 := h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7]
|
||||||
|
v8, v9, v10, v11, v12, v13, v14, v15 := iv[0], iv[1], iv[2], iv[3], iv[4], iv[5], iv[6], iv[7]
|
||||||
|
v12 ^= c0
|
||||||
|
v13 ^= c1
|
||||||
|
v14 ^= flag
|
||||||
|
|
||||||
|
for j := range m {
|
||||||
|
m[j] = binary.LittleEndian.Uint64(blocks[i:])
|
||||||
|
i += 8
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := range precomputed {
|
||||||
|
s := &(precomputed[j])
|
||||||
|
|
||||||
|
v0 += m[s[0]]
|
||||||
|
v0 += v4
|
||||||
|
v12 ^= v0
|
||||||
|
v12 = v12<<(64-32) | v12>>32
|
||||||
|
v8 += v12
|
||||||
|
v4 ^= v8
|
||||||
|
v4 = v4<<(64-24) | v4>>24
|
||||||
|
v1 += m[s[1]]
|
||||||
|
v1 += v5
|
||||||
|
v13 ^= v1
|
||||||
|
v13 = v13<<(64-32) | v13>>32
|
||||||
|
v9 += v13
|
||||||
|
v5 ^= v9
|
||||||
|
v5 = v5<<(64-24) | v5>>24
|
||||||
|
v2 += m[s[2]]
|
||||||
|
v2 += v6
|
||||||
|
v14 ^= v2
|
||||||
|
v14 = v14<<(64-32) | v14>>32
|
||||||
|
v10 += v14
|
||||||
|
v6 ^= v10
|
||||||
|
v6 = v6<<(64-24) | v6>>24
|
||||||
|
v3 += m[s[3]]
|
||||||
|
v3 += v7
|
||||||
|
v15 ^= v3
|
||||||
|
v15 = v15<<(64-32) | v15>>32
|
||||||
|
v11 += v15
|
||||||
|
v7 ^= v11
|
||||||
|
v7 = v7<<(64-24) | v7>>24
|
||||||
|
|
||||||
|
v0 += m[s[4]]
|
||||||
|
v0 += v4
|
||||||
|
v12 ^= v0
|
||||||
|
v12 = v12<<(64-16) | v12>>16
|
||||||
|
v8 += v12
|
||||||
|
v4 ^= v8
|
||||||
|
v4 = v4<<(64-63) | v4>>63
|
||||||
|
v1 += m[s[5]]
|
||||||
|
v1 += v5
|
||||||
|
v13 ^= v1
|
||||||
|
v13 = v13<<(64-16) | v13>>16
|
||||||
|
v9 += v13
|
||||||
|
v5 ^= v9
|
||||||
|
v5 = v5<<(64-63) | v5>>63
|
||||||
|
v2 += m[s[6]]
|
||||||
|
v2 += v6
|
||||||
|
v14 ^= v2
|
||||||
|
v14 = v14<<(64-16) | v14>>16
|
||||||
|
v10 += v14
|
||||||
|
v6 ^= v10
|
||||||
|
v6 = v6<<(64-63) | v6>>63
|
||||||
|
v3 += m[s[7]]
|
||||||
|
v3 += v7
|
||||||
|
v15 ^= v3
|
||||||
|
v15 = v15<<(64-16) | v15>>16
|
||||||
|
v11 += v15
|
||||||
|
v7 ^= v11
|
||||||
|
v7 = v7<<(64-63) | v7>>63
|
||||||
|
|
||||||
|
v0 += m[s[8]]
|
||||||
|
v0 += v5
|
||||||
|
v15 ^= v0
|
||||||
|
v15 = v15<<(64-32) | v15>>32
|
||||||
|
v10 += v15
|
||||||
|
v5 ^= v10
|
||||||
|
v5 = v5<<(64-24) | v5>>24
|
||||||
|
v1 += m[s[9]]
|
||||||
|
v1 += v6
|
||||||
|
v12 ^= v1
|
||||||
|
v12 = v12<<(64-32) | v12>>32
|
||||||
|
v11 += v12
|
||||||
|
v6 ^= v11
|
||||||
|
v6 = v6<<(64-24) | v6>>24
|
||||||
|
v2 += m[s[10]]
|
||||||
|
v2 += v7
|
||||||
|
v13 ^= v2
|
||||||
|
v13 = v13<<(64-32) | v13>>32
|
||||||
|
v8 += v13
|
||||||
|
v7 ^= v8
|
||||||
|
v7 = v7<<(64-24) | v7>>24
|
||||||
|
v3 += m[s[11]]
|
||||||
|
v3 += v4
|
||||||
|
v14 ^= v3
|
||||||
|
v14 = v14<<(64-32) | v14>>32
|
||||||
|
v9 += v14
|
||||||
|
v4 ^= v9
|
||||||
|
v4 = v4<<(64-24) | v4>>24
|
||||||
|
|
||||||
|
v0 += m[s[12]]
|
||||||
|
v0 += v5
|
||||||
|
v15 ^= v0
|
||||||
|
v15 = v15<<(64-16) | v15>>16
|
||||||
|
v10 += v15
|
||||||
|
v5 ^= v10
|
||||||
|
v5 = v5<<(64-63) | v5>>63
|
||||||
|
v1 += m[s[13]]
|
||||||
|
v1 += v6
|
||||||
|
v12 ^= v1
|
||||||
|
v12 = v12<<(64-16) | v12>>16
|
||||||
|
v11 += v12
|
||||||
|
v6 ^= v11
|
||||||
|
v6 = v6<<(64-63) | v6>>63
|
||||||
|
v2 += m[s[14]]
|
||||||
|
v2 += v7
|
||||||
|
v13 ^= v2
|
||||||
|
v13 = v13<<(64-16) | v13>>16
|
||||||
|
v8 += v13
|
||||||
|
v7 ^= v8
|
||||||
|
v7 = v7<<(64-63) | v7>>63
|
||||||
|
v3 += m[s[15]]
|
||||||
|
v3 += v4
|
||||||
|
v14 ^= v3
|
||||||
|
v14 = v14<<(64-16) | v14>>16
|
||||||
|
v9 += v14
|
||||||
|
v4 ^= v9
|
||||||
|
v4 = v4<<(64-63) | v4>>63
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
h[0] ^= v0 ^ v8
|
||||||
|
h[1] ^= v1 ^ v9
|
||||||
|
h[2] ^= v2 ^ v10
|
||||||
|
h[3] ^= v3 ^ v11
|
||||||
|
h[4] ^= v4 ^ v12
|
||||||
|
h[5] ^= v5 ^ v13
|
||||||
|
h[6] ^= v6 ^ v14
|
||||||
|
h[7] ^= v7 ^ v15
|
||||||
|
}
|
||||||
|
c[0], c[1] = c0, c1
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !amd64 appengine gccgo
|
||||||
|
|
||||||
|
package blake2b
|
||||||
|
|
||||||
|
func hashBlocks(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) {
|
||||||
|
hashBlocksGeneric(h, c, flag, blocks)
|
||||||
|
}
|
||||||
+448
@@ -0,0 +1,448 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package blake2b
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"hash"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func fromHex(s string) []byte {
|
||||||
|
b, err := hex.DecodeString(s)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHashes(t *testing.T) {
|
||||||
|
defer func(sse4, avx, avx2 bool) {
|
||||||
|
useSSE4, useAVX, useAVX2 = sse4, avx, avx2
|
||||||
|
}(useSSE4, useAVX, useAVX2)
|
||||||
|
|
||||||
|
if useAVX2 {
|
||||||
|
t.Log("AVX2 version")
|
||||||
|
testHashes(t)
|
||||||
|
useAVX2 = false
|
||||||
|
}
|
||||||
|
if useAVX {
|
||||||
|
t.Log("AVX version")
|
||||||
|
testHashes(t)
|
||||||
|
useAVX = false
|
||||||
|
}
|
||||||
|
if useSSE4 {
|
||||||
|
t.Log("SSE4 version")
|
||||||
|
testHashes(t)
|
||||||
|
useSSE4 = false
|
||||||
|
}
|
||||||
|
t.Log("generic version")
|
||||||
|
testHashes(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testHashes(t *testing.T) {
|
||||||
|
key, _ := hex.DecodeString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f")
|
||||||
|
|
||||||
|
input := make([]byte, 255)
|
||||||
|
for i := range input {
|
||||||
|
input[i] = byte(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, expectedHex := range hashes {
|
||||||
|
h, err := New512(key)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("#%d: error from New512: %v", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
h.Write(input[:i])
|
||||||
|
sum := h.Sum(nil)
|
||||||
|
|
||||||
|
if gotHex := fmt.Sprintf("%x", sum); gotHex != expectedHex {
|
||||||
|
t.Fatalf("#%d (single write): got %s, wanted %s", i, gotHex, expectedHex)
|
||||||
|
}
|
||||||
|
|
||||||
|
h.Reset()
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
h.Write(input[j : j+1])
|
||||||
|
}
|
||||||
|
|
||||||
|
sum = h.Sum(sum[:0])
|
||||||
|
if gotHex := fmt.Sprintf("%x", sum); gotHex != expectedHex {
|
||||||
|
t.Fatalf("#%d (byte-by-byte): got %s, wanted %s", i, gotHex, expectedHex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateSequence(out []byte, seed uint32) {
|
||||||
|
a := 0xDEAD4BAD * seed // prime
|
||||||
|
b := uint32(1)
|
||||||
|
|
||||||
|
for i := range out { // fill the buf
|
||||||
|
a, b = b, a+b
|
||||||
|
out[i] = byte(b >> 24)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func computeMAC(msg []byte, hashSize int, key []byte) (sum []byte) {
|
||||||
|
var h hash.Hash
|
||||||
|
switch hashSize {
|
||||||
|
case Size:
|
||||||
|
h, _ = New512(key)
|
||||||
|
case Size384:
|
||||||
|
h, _ = New384(key)
|
||||||
|
case Size256:
|
||||||
|
h, _ = New256(key)
|
||||||
|
case 20:
|
||||||
|
h, _ = newDigest(20, key)
|
||||||
|
default:
|
||||||
|
panic("unexpected hashSize")
|
||||||
|
}
|
||||||
|
|
||||||
|
h.Write(msg)
|
||||||
|
return h.Sum(sum)
|
||||||
|
}
|
||||||
|
|
||||||
|
func computeHash(msg []byte, hashSize int) (sum []byte) {
|
||||||
|
switch hashSize {
|
||||||
|
case Size:
|
||||||
|
hash := Sum512(msg)
|
||||||
|
return hash[:]
|
||||||
|
case Size384:
|
||||||
|
hash := Sum384(msg)
|
||||||
|
return hash[:]
|
||||||
|
case Size256:
|
||||||
|
hash := Sum256(msg)
|
||||||
|
return hash[:]
|
||||||
|
case 20:
|
||||||
|
var hash [64]byte
|
||||||
|
checkSum(&hash, 20, msg)
|
||||||
|
return hash[:20]
|
||||||
|
default:
|
||||||
|
panic("unexpected hashSize")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test function from RFC 7693.
|
||||||
|
func TestSelfTest(t *testing.T) {
|
||||||
|
hashLens := [4]int{20, 32, 48, 64}
|
||||||
|
msgLens := [6]int{0, 3, 128, 129, 255, 1024}
|
||||||
|
|
||||||
|
msg := make([]byte, 1024)
|
||||||
|
key := make([]byte, 64)
|
||||||
|
|
||||||
|
h, _ := New256(nil)
|
||||||
|
for _, hashSize := range hashLens {
|
||||||
|
for _, msgLength := range msgLens {
|
||||||
|
generateSequence(msg[:msgLength], uint32(msgLength)) // unkeyed hash
|
||||||
|
|
||||||
|
md := computeHash(msg[:msgLength], hashSize)
|
||||||
|
h.Write(md)
|
||||||
|
|
||||||
|
generateSequence(key[:], uint32(hashSize)) // keyed hash
|
||||||
|
md = computeMAC(msg[:msgLength], hashSize, key[:hashSize])
|
||||||
|
h.Write(md)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sum := h.Sum(nil)
|
||||||
|
expected := [32]byte{
|
||||||
|
0xc2, 0x3a, 0x78, 0x00, 0xd9, 0x81, 0x23, 0xbd,
|
||||||
|
0x10, 0xf5, 0x06, 0xc6, 0x1e, 0x29, 0xda, 0x56,
|
||||||
|
0x03, 0xd7, 0x63, 0xb8, 0xbb, 0xad, 0x2e, 0x73,
|
||||||
|
0x7f, 0x5e, 0x76, 0x5a, 0x7b, 0xcc, 0xd4, 0x75,
|
||||||
|
}
|
||||||
|
if !bytes.Equal(sum, expected[:]) {
|
||||||
|
t.Fatalf("got %x, wanted %x", sum, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Benchmarks
|
||||||
|
|
||||||
|
func benchmarkSum(b *testing.B, size int) {
|
||||||
|
data := make([]byte, size)
|
||||||
|
b.SetBytes(int64(size))
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
Sum512(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func benchmarkWrite(b *testing.B, size int) {
|
||||||
|
data := make([]byte, size)
|
||||||
|
h, _ := New512(nil)
|
||||||
|
b.SetBytes(int64(size))
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
h.Write(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkWrite128(b *testing.B) { benchmarkWrite(b, 128) }
|
||||||
|
func BenchmarkWrite1K(b *testing.B) { benchmarkWrite(b, 1024) }
|
||||||
|
|
||||||
|
func BenchmarkSum128(b *testing.B) { benchmarkSum(b, 128) }
|
||||||
|
func BenchmarkSum1K(b *testing.B) { benchmarkSum(b, 1024) }
|
||||||
|
|
||||||
|
// These values were taken from https://blake2.net/blake2b-test.txt.
|
||||||
|
var hashes = []string{
|
||||||
|
"10ebb67700b1868efb4417987acf4690ae9d972fb7a590c2f02871799aaa4786b5e996e8f0f4eb981fc214b005f42d2ff4233499391653df7aefcbc13fc51568",
|
||||||
|
"961f6dd1e4dd30f63901690c512e78e4b45e4742ed197c3c5e45c549fd25f2e4187b0bc9fe30492b16b0d0bc4ef9b0f34c7003fac09a5ef1532e69430234cebd",
|
||||||
|
"da2cfbe2d8409a0f38026113884f84b50156371ae304c4430173d08a99d9fb1b983164a3770706d537f49e0c916d9f32b95cc37a95b99d857436f0232c88a965",
|
||||||
|
"33d0825dddf7ada99b0e7e307104ad07ca9cfd9692214f1561356315e784f3e5a17e364ae9dbb14cb2036df932b77f4b292761365fb328de7afdc6d8998f5fc1",
|
||||||
|
"beaa5a3d08f3807143cf621d95cd690514d0b49efff9c91d24b59241ec0eefa5f60196d407048bba8d2146828ebcb0488d8842fd56bb4f6df8e19c4b4daab8ac",
|
||||||
|
"098084b51fd13deae5f4320de94a688ee07baea2800486689a8636117b46c1f4c1f6af7f74ae7c857600456a58a3af251dc4723a64cc7c0a5ab6d9cac91c20bb",
|
||||||
|
"6044540d560853eb1c57df0077dd381094781cdb9073e5b1b3d3f6c7829e12066bbaca96d989a690de72ca3133a83652ba284a6d62942b271ffa2620c9e75b1f",
|
||||||
|
"7a8cfe9b90f75f7ecb3acc053aaed6193112b6f6a4aeeb3f65d3de541942deb9e2228152a3c4bbbe72fc3b12629528cfbb09fe630f0474339f54abf453e2ed52",
|
||||||
|
"380beaf6ea7cc9365e270ef0e6f3a64fb902acae51dd5512f84259ad2c91f4bc4108db73192a5bbfb0cbcf71e46c3e21aee1c5e860dc96e8eb0b7b8426e6abe9",
|
||||||
|
"60fe3c4535e1b59d9a61ea8500bfac41a69dffb1ceadd9aca323e9a625b64da5763bad7226da02b9c8c4f1a5de140ac5a6c1124e4f718ce0b28ea47393aa6637",
|
||||||
|
"4fe181f54ad63a2983feaaf77d1e7235c2beb17fa328b6d9505bda327df19fc37f02c4b6f0368ce23147313a8e5738b5fa2a95b29de1c7f8264eb77b69f585cd",
|
||||||
|
"f228773ce3f3a42b5f144d63237a72d99693adb8837d0e112a8a0f8ffff2c362857ac49c11ec740d1500749dac9b1f4548108bf3155794dcc9e4082849e2b85b",
|
||||||
|
"962452a8455cc56c8511317e3b1f3b2c37df75f588e94325fdd77070359cf63a9ae6e930936fdf8e1e08ffca440cfb72c28f06d89a2151d1c46cd5b268ef8563",
|
||||||
|
"43d44bfa18768c59896bf7ed1765cb2d14af8c260266039099b25a603e4ddc5039d6ef3a91847d1088d401c0c7e847781a8a590d33a3c6cb4df0fab1c2f22355",
|
||||||
|
"dcffa9d58c2a4ca2cdbb0c7aa4c4c1d45165190089f4e983bb1c2cab4aaeff1fa2b5ee516fecd780540240bf37e56c8bcca7fab980e1e61c9400d8a9a5b14ac6",
|
||||||
|
"6fbf31b45ab0c0b8dad1c0f5f4061379912dde5aa922099a030b725c73346c524291adef89d2f6fd8dfcda6d07dad811a9314536c2915ed45da34947e83de34e",
|
||||||
|
"a0c65bddde8adef57282b04b11e7bc8aab105b99231b750c021f4a735cb1bcfab87553bba3abb0c3e64a0b6955285185a0bd35fb8cfde557329bebb1f629ee93",
|
||||||
|
"f99d815550558e81eca2f96718aed10d86f3f1cfb675cce06b0eff02f617c5a42c5aa760270f2679da2677c5aeb94f1142277f21c7f79f3c4f0cce4ed8ee62b1",
|
||||||
|
"95391da8fc7b917a2044b3d6f5374e1ca072b41454d572c7356c05fd4bc1e0f40b8bb8b4a9f6bce9be2c4623c399b0dca0dab05cb7281b71a21b0ebcd9e55670",
|
||||||
|
"04b9cd3d20d221c09ac86913d3dc63041989a9a1e694f1e639a3ba7e451840f750c2fc191d56ad61f2e7936bc0ac8e094b60caeed878c18799045402d61ceaf9",
|
||||||
|
"ec0e0ef707e4ed6c0c66f9e089e4954b058030d2dd86398fe84059631f9ee591d9d77375355149178c0cf8f8e7c49ed2a5e4f95488a2247067c208510fadc44c",
|
||||||
|
"9a37cce273b79c09913677510eaf7688e89b3314d3532fd2764c39de022a2945b5710d13517af8ddc0316624e73bec1ce67df15228302036f330ab0cb4d218dd",
|
||||||
|
"4cf9bb8fb3d4de8b38b2f262d3c40f46dfe747e8fc0a414c193d9fcf753106ce47a18f172f12e8a2f1c26726545358e5ee28c9e2213a8787aafbc516d2343152",
|
||||||
|
"64e0c63af9c808fd893137129867fd91939d53f2af04be4fa268006100069b2d69daa5c5d8ed7fddcb2a70eeecdf2b105dd46a1e3b7311728f639ab489326bc9",
|
||||||
|
"5e9c93158d659b2def06b0c3c7565045542662d6eee8a96a89b78ade09fe8b3dcc096d4fe48815d88d8f82620156602af541955e1f6ca30dce14e254c326b88f",
|
||||||
|
"7775dff889458dd11aef417276853e21335eb88e4dec9cfb4e9edb49820088551a2ca60339f12066101169f0dfe84b098fddb148d9da6b3d613df263889ad64b",
|
||||||
|
"f0d2805afbb91f743951351a6d024f9353a23c7ce1fc2b051b3a8b968c233f46f50f806ecb1568ffaa0b60661e334b21dde04f8fa155ac740eeb42e20b60d764",
|
||||||
|
"86a2af316e7d7754201b942e275364ac12ea8962ab5bd8d7fb276dc5fbffc8f9a28cae4e4867df6780d9b72524160927c855da5b6078e0b554aa91e31cb9ca1d",
|
||||||
|
"10bdf0caa0802705e706369baf8a3f79d72c0a03a80675a7bbb00be3a45e516424d1ee88efb56f6d5777545ae6e27765c3a8f5e493fc308915638933a1dfee55",
|
||||||
|
"b01781092b1748459e2e4ec178696627bf4ebafebba774ecf018b79a68aeb84917bf0b84bb79d17b743151144cd66b7b33a4b9e52c76c4e112050ff5385b7f0b",
|
||||||
|
"c6dbc61dec6eaeac81e3d5f755203c8e220551534a0b2fd105a91889945a638550204f44093dd998c076205dffad703a0e5cd3c7f438a7e634cd59fededb539e",
|
||||||
|
"eba51acffb4cea31db4b8d87e9bf7dd48fe97b0253ae67aa580f9ac4a9d941f2bea518ee286818cc9f633f2a3b9fb68e594b48cdd6d515bf1d52ba6c85a203a7",
|
||||||
|
"86221f3ada52037b72224f105d7999231c5e5534d03da9d9c0a12acb68460cd375daf8e24386286f9668f72326dbf99ba094392437d398e95bb8161d717f8991",
|
||||||
|
"5595e05c13a7ec4dc8f41fb70cb50a71bce17c024ff6de7af618d0cc4e9c32d9570d6d3ea45b86525491030c0d8f2b1836d5778c1ce735c17707df364d054347",
|
||||||
|
"ce0f4f6aca89590a37fe034dd74dd5fa65eb1cbd0a41508aaddc09351a3cea6d18cb2189c54b700c009f4cbf0521c7ea01be61c5ae09cb54f27bc1b44d658c82",
|
||||||
|
"7ee80b06a215a3bca970c77cda8761822bc103d44fa4b33f4d07dcb997e36d55298bceae12241b3fa07fa63be5576068da387b8d5859aeab701369848b176d42",
|
||||||
|
"940a84b6a84d109aab208c024c6ce9647676ba0aaa11f86dbb7018f9fd2220a6d901a9027f9abcf935372727cbf09ebd61a2a2eeb87653e8ecad1bab85dc8327",
|
||||||
|
"2020b78264a82d9f4151141adba8d44bf20c5ec062eee9b595a11f9e84901bf148f298e0c9f8777dcdbc7cc4670aac356cc2ad8ccb1629f16f6a76bcefbee760",
|
||||||
|
"d1b897b0e075ba68ab572adf9d9c436663e43eb3d8e62d92fc49c9be214e6f27873fe215a65170e6bea902408a25b49506f47babd07cecf7113ec10c5dd31252",
|
||||||
|
"b14d0c62abfa469a357177e594c10c194243ed2025ab8aa5ad2fa41ad318e0ff48cd5e60bec07b13634a711d2326e488a985f31e31153399e73088efc86a5c55",
|
||||||
|
"4169c5cc808d2697dc2a82430dc23e3cd356dc70a94566810502b8d655b39abf9e7f902fe717e0389219859e1945df1af6ada42e4ccda55a197b7100a30c30a1",
|
||||||
|
"258a4edb113d66c839c8b1c91f15f35ade609f11cd7f8681a4045b9fef7b0b24c82cda06a5f2067b368825e3914e53d6948ede92efd6e8387fa2e537239b5bee",
|
||||||
|
"79d2d8696d30f30fb34657761171a11e6c3f1e64cbe7bebee159cb95bfaf812b4f411e2f26d9c421dc2c284a3342d823ec293849e42d1e46b0a4ac1e3c86abaa",
|
||||||
|
"8b9436010dc5dee992ae38aea97f2cd63b946d94fedd2ec9671dcde3bd4ce9564d555c66c15bb2b900df72edb6b891ebcadfeff63c9ea4036a998be7973981e7",
|
||||||
|
"c8f68e696ed28242bf997f5b3b34959508e42d613810f1e2a435c96ed2ff560c7022f361a9234b9837feee90bf47922ee0fd5f8ddf823718d86d1e16c6090071",
|
||||||
|
"b02d3eee4860d5868b2c39ce39bfe81011290564dd678c85e8783f29302dfc1399ba95b6b53cd9ebbf400cca1db0ab67e19a325f2d115812d25d00978ad1bca4",
|
||||||
|
"7693ea73af3ac4dad21ca0d8da85b3118a7d1c6024cfaf557699868217bc0c2f44a199bc6c0edd519798ba05bd5b1b4484346a47c2cadf6bf30b785cc88b2baf",
|
||||||
|
"a0e5c1c0031c02e48b7f09a5e896ee9aef2f17fc9e18e997d7f6cac7ae316422c2b1e77984e5f3a73cb45deed5d3f84600105e6ee38f2d090c7d0442ea34c46d",
|
||||||
|
"41daa6adcfdb69f1440c37b596440165c15ada596813e2e22f060fcd551f24dee8e04ba6890387886ceec4a7a0d7fc6b44506392ec3822c0d8c1acfc7d5aebe8",
|
||||||
|
"14d4d40d5984d84c5cf7523b7798b254e275a3a8cc0a1bd06ebc0bee726856acc3cbf516ff667cda2058ad5c3412254460a82c92187041363cc77a4dc215e487",
|
||||||
|
"d0e7a1e2b9a447fee83e2277e9ff8010c2f375ae12fa7aaa8ca5a6317868a26a367a0b69fbc1cf32a55d34eb370663016f3d2110230eba754028a56f54acf57c",
|
||||||
|
"e771aa8db5a3e043e8178f39a0857ba04a3f18e4aa05743cf8d222b0b095825350ba422f63382a23d92e4149074e816a36c1cd28284d146267940b31f8818ea2",
|
||||||
|
"feb4fd6f9e87a56bef398b3284d2bda5b5b0e166583a66b61e538457ff0584872c21a32962b9928ffab58de4af2edd4e15d8b35570523207ff4e2a5aa7754caa",
|
||||||
|
"462f17bf005fb1c1b9e671779f665209ec2873e3e411f98dabf240a1d5ec3f95ce6796b6fc23fe171903b502023467dec7273ff74879b92967a2a43a5a183d33",
|
||||||
|
"d3338193b64553dbd38d144bea71c5915bb110e2d88180dbc5db364fd6171df317fc7268831b5aef75e4342b2fad8797ba39eddcef80e6ec08159350b1ad696d",
|
||||||
|
"e1590d585a3d39f7cb599abd479070966409a6846d4377acf4471d065d5db94129cc9be92573b05ed226be1e9b7cb0cabe87918589f80dadd4ef5ef25a93d28e",
|
||||||
|
"f8f3726ac5a26cc80132493a6fedcb0e60760c09cfc84cad178175986819665e76842d7b9fedf76dddebf5d3f56faaad4477587af21606d396ae570d8e719af2",
|
||||||
|
"30186055c07949948183c850e9a756cc09937e247d9d928e869e20bafc3cd9721719d34e04a0899b92c736084550186886efba2e790d8be6ebf040b209c439a4",
|
||||||
|
"f3c4276cb863637712c241c444c5cc1e3554e0fddb174d035819dd83eb700b4ce88df3ab3841ba02085e1a99b4e17310c5341075c0458ba376c95a6818fbb3e2",
|
||||||
|
"0aa007c4dd9d5832393040a1583c930bca7dc5e77ea53add7e2b3f7c8e231368043520d4a3ef53c969b6bbfd025946f632bd7f765d53c21003b8f983f75e2a6a",
|
||||||
|
"08e9464720533b23a04ec24f7ae8c103145f765387d738777d3d343477fd1c58db052142cab754ea674378e18766c53542f71970171cc4f81694246b717d7564",
|
||||||
|
"d37ff7ad297993e7ec21e0f1b4b5ae719cdc83c5db687527f27516cbffa822888a6810ee5c1ca7bfe3321119be1ab7bfa0a502671c8329494df7ad6f522d440f",
|
||||||
|
"dd9042f6e464dcf86b1262f6accfafbd8cfd902ed3ed89abf78ffa482dbdeeb6969842394c9a1168ae3d481a017842f660002d42447c6b22f7b72f21aae021c9",
|
||||||
|
"bd965bf31e87d70327536f2a341cebc4768eca275fa05ef98f7f1b71a0351298de006fba73fe6733ed01d75801b4a928e54231b38e38c562b2e33ea1284992fa",
|
||||||
|
"65676d800617972fbd87e4b9514e1c67402b7a331096d3bfac22f1abb95374abc942f16e9ab0ead33b87c91968a6e509e119ff07787b3ef483e1dcdccf6e3022",
|
||||||
|
"939fa189699c5d2c81ddd1ffc1fa207c970b6a3685bb29ce1d3e99d42f2f7442da53e95a72907314f4588399a3ff5b0a92beb3f6be2694f9f86ecf2952d5b41c",
|
||||||
|
"c516541701863f91005f314108ceece3c643e04fc8c42fd2ff556220e616aaa6a48aeb97a84bad74782e8dff96a1a2fa949339d722edcaa32b57067041df88cc",
|
||||||
|
"987fd6e0d6857c553eaebb3d34970a2c2f6e89a3548f492521722b80a1c21a153892346d2cba6444212d56da9a26e324dccbc0dcde85d4d2ee4399eec5a64e8f",
|
||||||
|
"ae56deb1c2328d9c4017706bce6e99d41349053ba9d336d677c4c27d9fd50ae6aee17e853154e1f4fe7672346da2eaa31eea53fcf24a22804f11d03da6abfc2b",
|
||||||
|
"49d6a608c9bde4491870498572ac31aac3fa40938b38a7818f72383eb040ad39532bc06571e13d767e6945ab77c0bdc3b0284253343f9f6c1244ebf2ff0df866",
|
||||||
|
"da582ad8c5370b4469af862aa6467a2293b2b28bd80ae0e91f425ad3d47249fdf98825cc86f14028c3308c9804c78bfeeeee461444ce243687e1a50522456a1d",
|
||||||
|
"d5266aa3331194aef852eed86d7b5b2633a0af1c735906f2e13279f14931a9fc3b0eac5ce9245273bd1aa92905abe16278ef7efd47694789a7283b77da3c70f8",
|
||||||
|
"2962734c28252186a9a1111c732ad4de4506d4b4480916303eb7991d659ccda07a9911914bc75c418ab7a4541757ad054796e26797feaf36e9f6ad43f14b35a4",
|
||||||
|
"e8b79ec5d06e111bdfafd71e9f5760f00ac8ac5d8bf768f9ff6f08b8f026096b1cc3a4c973333019f1e3553e77da3f98cb9f542e0a90e5f8a940cc58e59844b3",
|
||||||
|
"dfb320c44f9d41d1efdcc015f08dd5539e526e39c87d509ae6812a969e5431bf4fa7d91ffd03b981e0d544cf72d7b1c0374f8801482e6dea2ef903877eba675e",
|
||||||
|
"d88675118fdb55a5fb365ac2af1d217bf526ce1ee9c94b2f0090b2c58a06ca58187d7fe57c7bed9d26fca067b4110eefcd9a0a345de872abe20de368001b0745",
|
||||||
|
"b893f2fc41f7b0dd6e2f6aa2e0370c0cff7df09e3acfcc0e920b6e6fad0ef747c40668417d342b80d2351e8c175f20897a062e9765e6c67b539b6ba8b9170545",
|
||||||
|
"6c67ec5697accd235c59b486d7b70baeedcbd4aa64ebd4eef3c7eac189561a726250aec4d48cadcafbbe2ce3c16ce2d691a8cce06e8879556d4483ed7165c063",
|
||||||
|
"f1aa2b044f8f0c638a3f362e677b5d891d6fd2ab0765f6ee1e4987de057ead357883d9b405b9d609eea1b869d97fb16d9b51017c553f3b93c0a1e0f1296fedcd",
|
||||||
|
"cbaa259572d4aebfc1917acddc582b9f8dfaa928a198ca7acd0f2aa76a134a90252e6298a65b08186a350d5b7626699f8cb721a3ea5921b753ae3a2dce24ba3a",
|
||||||
|
"fa1549c9796cd4d303dcf452c1fbd5744fd9b9b47003d920b92de34839d07ef2a29ded68f6fc9e6c45e071a2e48bd50c5084e96b657dd0404045a1ddefe282ed",
|
||||||
|
"5cf2ac897ab444dcb5c8d87c495dbdb34e1838b6b629427caa51702ad0f9688525f13bec503a3c3a2c80a65e0b5715e8afab00ffa56ec455a49a1ad30aa24fcd",
|
||||||
|
"9aaf80207bace17bb7ab145757d5696bde32406ef22b44292ef65d4519c3bb2ad41a59b62cc3e94b6fa96d32a7faadae28af7d35097219aa3fd8cda31e40c275",
|
||||||
|
"af88b163402c86745cb650c2988fb95211b94b03ef290eed9662034241fd51cf398f8073e369354c43eae1052f9b63b08191caa138aa54fea889cc7024236897",
|
||||||
|
"48fa7d64e1ceee27b9864db5ada4b53d00c9bc7626555813d3cd6730ab3cc06ff342d727905e33171bde6e8476e77fb1720861e94b73a2c538d254746285f430",
|
||||||
|
"0e6fd97a85e904f87bfe85bbeb34f69e1f18105cf4ed4f87aec36c6e8b5f68bd2a6f3dc8a9ecb2b61db4eedb6b2ea10bf9cb0251fb0f8b344abf7f366b6de5ab",
|
||||||
|
"06622da5787176287fdc8fed440bad187d830099c94e6d04c8e9c954cda70c8bb9e1fc4a6d0baa831b9b78ef6648681a4867a11da93ee36e5e6a37d87fc63f6f",
|
||||||
|
"1da6772b58fabf9c61f68d412c82f182c0236d7d575ef0b58dd22458d643cd1dfc93b03871c316d8430d312995d4197f0874c99172ba004a01ee295abac24e46",
|
||||||
|
"3cd2d9320b7b1d5fb9aab951a76023fa667be14a9124e394513918a3f44096ae4904ba0ffc150b63bc7ab1eeb9a6e257e5c8f000a70394a5afd842715de15f29",
|
||||||
|
"04cdc14f7434e0b4be70cb41db4c779a88eaef6accebcb41f2d42fffe7f32a8e281b5c103a27021d0d08362250753cdf70292195a53a48728ceb5844c2d98bab",
|
||||||
|
"9071b7a8a075d0095b8fb3ae5113785735ab98e2b52faf91d5b89e44aac5b5d4ebbf91223b0ff4c71905da55342e64655d6ef8c89a4768c3f93a6dc0366b5bc8",
|
||||||
|
"ebb30240dd96c7bc8d0abe49aa4edcbb4afdc51ff9aaf720d3f9e7fbb0f9c6d6571350501769fc4ebd0b2141247ff400d4fd4be414edf37757bb90a32ac5c65a",
|
||||||
|
"8532c58bf3c8015d9d1cbe00eef1f5082f8f3632fbe9f1ed4f9dfb1fa79e8283066d77c44c4af943d76b300364aecbd0648c8a8939bd204123f4b56260422dec",
|
||||||
|
"fe9846d64f7c7708696f840e2d76cb4408b6595c2f81ec6a28a7f2f20cb88cfe6ac0b9e9b8244f08bd7095c350c1d0842f64fb01bb7f532dfcd47371b0aeeb79",
|
||||||
|
"28f17ea6fb6c42092dc264257e29746321fb5bdaea9873c2a7fa9d8f53818e899e161bc77dfe8090afd82bf2266c5c1bc930a8d1547624439e662ef695f26f24",
|
||||||
|
"ec6b7d7f030d4850acae3cb615c21dd25206d63e84d1db8d957370737ba0e98467ea0ce274c66199901eaec18a08525715f53bfdb0aacb613d342ebdceeddc3b",
|
||||||
|
"b403d3691c03b0d3418df327d5860d34bbfcc4519bfbce36bf33b208385fadb9186bc78a76c489d89fd57e7dc75412d23bcd1dae8470ce9274754bb8585b13c5",
|
||||||
|
"31fc79738b8772b3f55cd8178813b3b52d0db5a419d30ba9495c4b9da0219fac6df8e7c23a811551a62b827f256ecdb8124ac8a6792ccfecc3b3012722e94463",
|
||||||
|
"bb2039ec287091bcc9642fc90049e73732e02e577e2862b32216ae9bedcd730c4c284ef3968c368b7d37584f97bd4b4dc6ef6127acfe2e6ae2509124e66c8af4",
|
||||||
|
"f53d68d13f45edfcb9bd415e2831e938350d5380d3432278fc1c0c381fcb7c65c82dafe051d8c8b0d44e0974a0e59ec7bf7ed0459f86e96f329fc79752510fd3",
|
||||||
|
"8d568c7984f0ecdf7640fbc483b5d8c9f86634f6f43291841b309a350ab9c1137d24066b09da9944bac54d5bb6580d836047aac74ab724b887ebf93d4b32eca9",
|
||||||
|
"c0b65ce5a96ff774c456cac3b5f2c4cd359b4ff53ef93a3da0778be4900d1e8da1601e769e8f1b02d2a2f8c5b9fa10b44f1c186985468feeb008730283a6657d",
|
||||||
|
"4900bba6f5fb103ece8ec96ada13a5c3c85488e05551da6b6b33d988e611ec0fe2e3c2aa48ea6ae8986a3a231b223c5d27cec2eadde91ce07981ee652862d1e4",
|
||||||
|
"c7f5c37c7285f927f76443414d4357ff789647d7a005a5a787e03c346b57f49f21b64fa9cf4b7e45573e23049017567121a9c3d4b2b73ec5e9413577525db45a",
|
||||||
|
"ec7096330736fdb2d64b5653e7475da746c23a4613a82687a28062d3236364284ac01720ffb406cfe265c0df626a188c9e5963ace5d3d5bb363e32c38c2190a6",
|
||||||
|
"82e744c75f4649ec52b80771a77d475a3bc091989556960e276a5f9ead92a03f718742cdcfeaee5cb85c44af198adc43a4a428f5f0c2ddb0be36059f06d7df73",
|
||||||
|
"2834b7a7170f1f5b68559ab78c1050ec21c919740b784a9072f6e5d69f828d70c919c5039fb148e39e2c8a52118378b064ca8d5001cd10a5478387b966715ed6",
|
||||||
|
"16b4ada883f72f853bb7ef253efcab0c3e2161687ad61543a0d2824f91c1f81347d86be709b16996e17f2dd486927b0288ad38d13063c4a9672c39397d3789b6",
|
||||||
|
"78d048f3a69d8b54ae0ed63a573ae350d89f7c6cf1f3688930de899afa037697629b314e5cd303aa62feea72a25bf42b304b6c6bcb27fae21c16d925e1fbdac3",
|
||||||
|
"0f746a48749287ada77a82961f05a4da4abdb7d77b1220f836d09ec814359c0ec0239b8c7b9ff9e02f569d1b301ef67c4612d1de4f730f81c12c40cc063c5caa",
|
||||||
|
"f0fc859d3bd195fbdc2d591e4cdac15179ec0f1dc821c11df1f0c1d26e6260aaa65b79fafacafd7d3ad61e600f250905f5878c87452897647a35b995bcadc3a3",
|
||||||
|
"2620f687e8625f6a412460b42e2cef67634208ce10a0cbd4dff7044a41b7880077e9f8dc3b8d1216d3376a21e015b58fb279b521d83f9388c7382c8505590b9b",
|
||||||
|
"227e3aed8d2cb10b918fcb04f9de3e6d0a57e08476d93759cd7b2ed54a1cbf0239c528fb04bbf288253e601d3bc38b21794afef90b17094a182cac557745e75f",
|
||||||
|
"1a929901b09c25f27d6b35be7b2f1c4745131fdebca7f3e2451926720434e0db6e74fd693ad29b777dc3355c592a361c4873b01133a57c2e3b7075cbdb86f4fc",
|
||||||
|
"5fd7968bc2fe34f220b5e3dc5af9571742d73b7d60819f2888b629072b96a9d8ab2d91b82d0a9aaba61bbd39958132fcc4257023d1eca591b3054e2dc81c8200",
|
||||||
|
"dfcce8cf32870cc6a503eadafc87fd6f78918b9b4d0737db6810be996b5497e7e5cc80e312f61e71ff3e9624436073156403f735f56b0b01845c18f6caf772e6",
|
||||||
|
"02f7ef3a9ce0fff960f67032b296efca3061f4934d690749f2d01c35c81c14f39a67fa350bc8a0359bf1724bffc3bca6d7c7bba4791fd522a3ad353c02ec5aa8",
|
||||||
|
"64be5c6aba65d594844ae78bb022e5bebe127fd6b6ffa5a13703855ab63b624dcd1a363f99203f632ec386f3ea767fc992e8ed9686586aa27555a8599d5b808f",
|
||||||
|
"f78585505c4eaa54a8b5be70a61e735e0ff97af944ddb3001e35d86c4e2199d976104b6ae31750a36a726ed285064f5981b503889fef822fcdc2898dddb7889a",
|
||||||
|
"e4b5566033869572edfd87479a5bb73c80e8759b91232879d96b1dda36c012076ee5a2ed7ae2de63ef8406a06aea82c188031b560beafb583fb3de9e57952a7e",
|
||||||
|
"e1b3e7ed867f6c9484a2a97f7715f25e25294e992e41f6a7c161ffc2adc6daaeb7113102d5e6090287fe6ad94ce5d6b739c6ca240b05c76fb73f25dd024bf935",
|
||||||
|
"85fd085fdc12a080983df07bd7012b0d402a0f4043fcb2775adf0bad174f9b08d1676e476985785c0a5dcc41dbff6d95ef4d66a3fbdc4a74b82ba52da0512b74",
|
||||||
|
"aed8fa764b0fbff821e05233d2f7b0900ec44d826f95e93c343c1bc3ba5a24374b1d616e7e7aba453a0ada5e4fab5382409e0d42ce9c2bc7fb39a99c340c20f0",
|
||||||
|
"7ba3b2e297233522eeb343bd3ebcfd835a04007735e87f0ca300cbee6d416565162171581e4020ff4cf176450f1291ea2285cb9ebffe4c56660627685145051c",
|
||||||
|
"de748bcf89ec88084721e16b85f30adb1a6134d664b5843569babc5bbd1a15ca9b61803c901a4fef32965a1749c9f3a4e243e173939dc5a8dc495c671ab52145",
|
||||||
|
"aaf4d2bdf200a919706d9842dce16c98140d34bc433df320aba9bd429e549aa7a3397652a4d768277786cf993cde2338673ed2e6b66c961fefb82cd20c93338f",
|
||||||
|
"c408218968b788bf864f0997e6bc4c3dba68b276e2125a4843296052ff93bf5767b8cdce7131f0876430c1165fec6c4f47adaa4fd8bcfacef463b5d3d0fa61a0",
|
||||||
|
"76d2d819c92bce55fa8e092ab1bf9b9eab237a25267986cacf2b8ee14d214d730dc9a5aa2d7b596e86a1fd8fa0804c77402d2fcd45083688b218b1cdfa0dcbcb",
|
||||||
|
"72065ee4dd91c2d8509fa1fc28a37c7fc9fa7d5b3f8ad3d0d7a25626b57b1b44788d4caf806290425f9890a3a2a35a905ab4b37acfd0da6e4517b2525c9651e4",
|
||||||
|
"64475dfe7600d7171bea0b394e27c9b00d8e74dd1e416a79473682ad3dfdbb706631558055cfc8a40e07bd015a4540dcdea15883cbbf31412df1de1cd4152b91",
|
||||||
|
"12cd1674a4488a5d7c2b3160d2e2c4b58371bedad793418d6f19c6ee385d70b3e06739369d4df910edb0b0a54cbff43d54544cd37ab3a06cfa0a3ddac8b66c89",
|
||||||
|
"60756966479dedc6dd4bcff8ea7d1d4ce4d4af2e7b097e32e3763518441147cc12b3c0ee6d2ecabf1198cec92e86a3616fba4f4e872f5825330adbb4c1dee444",
|
||||||
|
"a7803bcb71bc1d0f4383dde1e0612e04f872b715ad30815c2249cf34abb8b024915cb2fc9f4e7cc4c8cfd45be2d5a91eab0941c7d270e2da4ca4a9f7ac68663a",
|
||||||
|
"b84ef6a7229a34a750d9a98ee2529871816b87fbe3bc45b45fa5ae82d5141540211165c3c5d7a7476ba5a4aa06d66476f0d9dc49a3f1ee72c3acabd498967414",
|
||||||
|
"fae4b6d8efc3f8c8e64d001dabec3a21f544e82714745251b2b4b393f2f43e0da3d403c64db95a2cb6e23ebb7b9e94cdd5ddac54f07c4a61bd3cb10aa6f93b49",
|
||||||
|
"34f7286605a122369540141ded79b8957255da2d4155abbf5a8dbb89c8eb7ede8eeef1daa46dc29d751d045dc3b1d658bb64b80ff8589eddb3824b13da235a6b",
|
||||||
|
"3b3b48434be27b9eababba43bf6b35f14b30f6a88dc2e750c358470d6b3aa3c18e47db4017fa55106d8252f016371a00f5f8b070b74ba5f23cffc5511c9f09f0",
|
||||||
|
"ba289ebd6562c48c3e10a8ad6ce02e73433d1e93d7c9279d4d60a7e879ee11f441a000f48ed9f7c4ed87a45136d7dccdca482109c78a51062b3ba4044ada2469",
|
||||||
|
"022939e2386c5a37049856c850a2bb10a13dfea4212b4c732a8840a9ffa5faf54875c5448816b2785a007da8a8d2bc7d71a54e4e6571f10b600cbdb25d13ede3",
|
||||||
|
"e6fec19d89ce8717b1a087024670fe026f6c7cbda11caef959bb2d351bf856f8055d1c0ebdaaa9d1b17886fc2c562b5e99642fc064710c0d3488a02b5ed7f6fd",
|
||||||
|
"94c96f02a8f576aca32ba61c2b206f907285d9299b83ac175c209a8d43d53bfe683dd1d83e7549cb906c28f59ab7c46f8751366a28c39dd5fe2693c9019666c8",
|
||||||
|
"31a0cd215ebd2cb61de5b9edc91e6195e31c59a5648d5c9f737e125b2605708f2e325ab3381c8dce1a3e958886f1ecdc60318f882cfe20a24191352e617b0f21",
|
||||||
|
"91ab504a522dce78779f4c6c6ba2e6b6db5565c76d3e7e7c920caf7f757ef9db7c8fcf10e57f03379ea9bf75eb59895d96e149800b6aae01db778bb90afbc989",
|
||||||
|
"d85cabc6bd5b1a01a5afd8c6734740da9fd1c1acc6db29bfc8a2e5b668b028b6b3154bfb8703fa3180251d589ad38040ceb707c4bad1b5343cb426b61eaa49c1",
|
||||||
|
"d62efbec2ca9c1f8bd66ce8b3f6a898cb3f7566ba6568c618ad1feb2b65b76c3ce1dd20f7395372faf28427f61c9278049cf0140df434f5633048c86b81e0399",
|
||||||
|
"7c8fdc6175439e2c3db15bafa7fb06143a6a23bc90f449e79deef73c3d492a671715c193b6fea9f036050b946069856b897e08c00768f5ee5ddcf70b7cd6d0e0",
|
||||||
|
"58602ee7468e6bc9df21bd51b23c005f72d6cb013f0a1b48cbec5eca299299f97f09f54a9a01483eaeb315a6478bad37ba47ca1347c7c8fc9e6695592c91d723",
|
||||||
|
"27f5b79ed256b050993d793496edf4807c1d85a7b0a67c9c4fa99860750b0ae66989670a8ffd7856d7ce411599e58c4d77b232a62bef64d15275be46a68235ff",
|
||||||
|
"3957a976b9f1887bf004a8dca942c92d2b37ea52600f25e0c9bc5707d0279c00c6e85a839b0d2d8eb59c51d94788ebe62474a791cadf52cccf20f5070b6573fc",
|
||||||
|
"eaa2376d55380bf772ecca9cb0aa4668c95c707162fa86d518c8ce0ca9bf7362b9f2a0adc3ff59922df921b94567e81e452f6c1a07fc817cebe99604b3505d38",
|
||||||
|
"c1e2c78b6b2734e2480ec550434cb5d613111adcc21d475545c3b1b7e6ff12444476e5c055132e2229dc0f807044bb919b1a5662dd38a9ee65e243a3911aed1a",
|
||||||
|
"8ab48713389dd0fcf9f965d3ce66b1e559a1f8c58741d67683cd971354f452e62d0207a65e436c5d5d8f8ee71c6abfe50e669004c302b31a7ea8311d4a916051",
|
||||||
|
"24ce0addaa4c65038bd1b1c0f1452a0b128777aabc94a29df2fd6c7e2f85f8ab9ac7eff516b0e0a825c84a24cfe492eaad0a6308e46dd42fe8333ab971bb30ca",
|
||||||
|
"5154f929ee03045b6b0c0004fa778edee1d139893267cc84825ad7b36c63de32798e4a166d24686561354f63b00709a1364b3c241de3febf0754045897467cd4",
|
||||||
|
"e74e907920fd87bd5ad636dd11085e50ee70459c443e1ce5809af2bc2eba39f9e6d7128e0e3712c316da06f4705d78a4838e28121d4344a2c79c5e0db307a677",
|
||||||
|
"bf91a22334bac20f3fd80663b3cd06c4e8802f30e6b59f90d3035cc9798a217ed5a31abbda7fa6842827bdf2a7a1c21f6fcfccbb54c6c52926f32da816269be1",
|
||||||
|
"d9d5c74be5121b0bd742f26bffb8c89f89171f3f934913492b0903c271bbe2b3395ef259669bef43b57f7fcc3027db01823f6baee66e4f9fead4d6726c741fce",
|
||||||
|
"50c8b8cf34cd879f80e2faab3230b0c0e1cc3e9dcadeb1b9d97ab923415dd9a1fe38addd5c11756c67990b256e95ad6d8f9fedce10bf1c90679cde0ecf1be347",
|
||||||
|
"0a386e7cd5dd9b77a035e09fe6fee2c8ce61b5383c87ea43205059c5e4cd4f4408319bb0a82360f6a58e6c9ce3f487c446063bf813bc6ba535e17fc1826cfc91",
|
||||||
|
"1f1459cb6b61cbac5f0efe8fc487538f42548987fcd56221cfa7beb22504769e792c45adfb1d6b3d60d7b749c8a75b0bdf14e8ea721b95dca538ca6e25711209",
|
||||||
|
"e58b3836b7d8fedbb50ca5725c6571e74c0785e97821dab8b6298c10e4c079d4a6cdf22f0fedb55032925c16748115f01a105e77e00cee3d07924dc0d8f90659",
|
||||||
|
"b929cc6505f020158672deda56d0db081a2ee34c00c1100029bdf8ea98034fa4bf3e8655ec697fe36f40553c5bb46801644a627d3342f4fc92b61f03290fb381",
|
||||||
|
"72d353994b49d3e03153929a1e4d4f188ee58ab9e72ee8e512f29bc773913819ce057ddd7002c0433ee0a16114e3d156dd2c4a7e80ee53378b8670f23e33ef56",
|
||||||
|
"c70ef9bfd775d408176737a0736d68517ce1aaad7e81a93c8c1ed967ea214f56c8a377b1763e676615b60f3988241eae6eab9685a5124929d28188f29eab06f7",
|
||||||
|
"c230f0802679cb33822ef8b3b21bf7a9a28942092901d7dac3760300831026cf354c9232df3e084d9903130c601f63c1f4a4a4b8106e468cd443bbe5a734f45f",
|
||||||
|
"6f43094cafb5ebf1f7a4937ec50f56a4c9da303cbb55ac1f27f1f1976cd96beda9464f0e7b9c54620b8a9fba983164b8be3578425a024f5fe199c36356b88972",
|
||||||
|
"3745273f4c38225db2337381871a0c6aafd3af9b018c88aa02025850a5dc3a42a1a3e03e56cbf1b0876d63a441f1d2856a39b8801eb5af325201c415d65e97fe",
|
||||||
|
"c50c44cca3ec3edaae779a7e179450ebdda2f97067c690aa6c5a4ac7c30139bb27c0df4db3220e63cb110d64f37ffe078db72653e2daacf93ae3f0a2d1a7eb2e",
|
||||||
|
"8aef263e385cbc61e19b28914243262af5afe8726af3ce39a79c27028cf3ecd3f8d2dfd9cfc9ad91b58f6f20778fd5f02894a3d91c7d57d1e4b866a7f364b6be",
|
||||||
|
"28696141de6e2d9bcb3235578a66166c1448d3e905a1b482d423be4bc5369bc8c74dae0acc9cc123e1d8ddce9f97917e8c019c552da32d39d2219b9abf0fa8c8",
|
||||||
|
"2fb9eb2085830181903a9dafe3db428ee15be7662224efd643371fb25646aee716e531eca69b2bdc8233f1a8081fa43da1500302975a77f42fa592136710e9dc",
|
||||||
|
"66f9a7143f7a3314a669bf2e24bbb35014261d639f495b6c9c1f104fe8e320aca60d4550d69d52edbd5a3cdeb4014ae65b1d87aa770b69ae5c15f4330b0b0ad8",
|
||||||
|
"f4c4dd1d594c3565e3e25ca43dad82f62abea4835ed4cd811bcd975e46279828d44d4c62c3679f1b7f7b9dd4571d7b49557347b8c5460cbdc1bef690fb2a08c0",
|
||||||
|
"8f1dc9649c3a84551f8f6e91cac68242a43b1f8f328ee92280257387fa7559aa6db12e4aeadc2d26099178749c6864b357f3f83b2fb3efa8d2a8db056bed6bcc",
|
||||||
|
"3139c1a7f97afd1675d460ebbc07f2728aa150df849624511ee04b743ba0a833092f18c12dc91b4dd243f333402f59fe28abdbbbae301e7b659c7a26d5c0f979",
|
||||||
|
"06f94a2996158a819fe34c40de3cf0379fd9fb85b3e363ba3926a0e7d960e3f4c2e0c70c7ce0ccb2a64fc29869f6e7ab12bd4d3f14fce943279027e785fb5c29",
|
||||||
|
"c29c399ef3eee8961e87565c1ce263925fc3d0ce267d13e48dd9e732ee67b0f69fad56401b0f10fcaac119201046cca28c5b14abdea3212ae65562f7f138db3d",
|
||||||
|
"4cec4c9df52eef05c3f6faaa9791bc7445937183224ecc37a1e58d0132d35617531d7e795f52af7b1eb9d147de1292d345fe341823f8e6bc1e5badca5c656108",
|
||||||
|
"898bfbae93b3e18d00697eab7d9704fa36ec339d076131cefdf30edbe8d9cc81c3a80b129659b163a323bab9793d4feed92d54dae966c77529764a09be88db45",
|
||||||
|
"ee9bd0469d3aaf4f14035be48a2c3b84d9b4b1fff1d945e1f1c1d38980a951be197b25fe22c731f20aeacc930ba9c4a1f4762227617ad350fdabb4e80273a0f4",
|
||||||
|
"3d4d3113300581cd96acbf091c3d0f3c310138cd6979e6026cde623e2dd1b24d4a8638bed1073344783ad0649cc6305ccec04beb49f31c633088a99b65130267",
|
||||||
|
"95c0591ad91f921ac7be6d9ce37e0663ed8011c1cfd6d0162a5572e94368bac02024485e6a39854aa46fe38e97d6c6b1947cd272d86b06bb5b2f78b9b68d559d",
|
||||||
|
"227b79ded368153bf46c0a3ca978bfdbef31f3024a5665842468490b0ff748ae04e7832ed4c9f49de9b1706709d623e5c8c15e3caecae8d5e433430ff72f20eb",
|
||||||
|
"5d34f3952f0105eef88ae8b64c6ce95ebfade0e02c69b08762a8712d2e4911ad3f941fc4034dc9b2e479fdbcd279b902faf5d838bb2e0c6495d372b5b7029813",
|
||||||
|
"7f939bf8353abce49e77f14f3750af20b7b03902e1a1e7fb6aaf76d0259cd401a83190f15640e74f3e6c5a90e839c7821f6474757f75c7bf9002084ddc7a62dc",
|
||||||
|
"062b61a2f9a33a71d7d0a06119644c70b0716a504de7e5e1be49bd7b86e7ed6817714f9f0fc313d06129597e9a2235ec8521de36f7290a90ccfc1ffa6d0aee29",
|
||||||
|
"f29e01eeae64311eb7f1c6422f946bf7bea36379523e7b2bbaba7d1d34a22d5ea5f1c5a09d5ce1fe682cced9a4798d1a05b46cd72dff5c1b355440b2a2d476bc",
|
||||||
|
"ec38cd3bbab3ef35d7cb6d5c914298351d8a9dc97fcee051a8a02f58e3ed6184d0b7810a5615411ab1b95209c3c810114fdeb22452084e77f3f847c6dbaafe16",
|
||||||
|
"c2aef5e0ca43e82641565b8cb943aa8ba53550caef793b6532fafad94b816082f0113a3ea2f63608ab40437ecc0f0229cb8fa224dcf1c478a67d9b64162b92d1",
|
||||||
|
"15f534efff7105cd1c254d074e27d5898b89313b7d366dc2d7d87113fa7d53aae13f6dba487ad8103d5e854c91fdb6e1e74b2ef6d1431769c30767dde067a35c",
|
||||||
|
"89acbca0b169897a0a2714c2df8c95b5b79cb69390142b7d6018bb3e3076b099b79a964152a9d912b1b86412b7e372e9cecad7f25d4cbab8a317be36492a67d7",
|
||||||
|
"e3c0739190ed849c9c962fd9dbb55e207e624fcac1eb417691515499eea8d8267b7e8f1287a63633af5011fde8c4ddf55bfdf722edf88831414f2cfaed59cb9a",
|
||||||
|
"8d6cf87c08380d2d1506eee46fd4222d21d8c04e585fbfd08269c98f702833a156326a0724656400ee09351d57b440175e2a5de93cc5f80db6daf83576cf75fa",
|
||||||
|
"da24bede383666d563eeed37f6319baf20d5c75d1635a6ba5ef4cfa1ac95487e96f8c08af600aab87c986ebad49fc70a58b4890b9c876e091016daf49e1d322e",
|
||||||
|
"f9d1d1b1e87ea7ae753a029750cc1cf3d0157d41805e245c5617bb934e732f0ae3180b78e05bfe76c7c3051e3e3ac78b9b50c05142657e1e03215d6ec7bfd0fc",
|
||||||
|
"11b7bc1668032048aa43343de476395e814bbbc223678db951a1b03a021efac948cfbe215f97fe9a72a2f6bc039e3956bfa417c1a9f10d6d7ba5d3d32ff323e5",
|
||||||
|
"b8d9000e4fc2b066edb91afee8e7eb0f24e3a201db8b6793c0608581e628ed0bcc4e5aa6787992a4bcc44e288093e63ee83abd0bc3ec6d0934a674a4da13838a",
|
||||||
|
"ce325e294f9b6719d6b61278276ae06a2564c03bb0b783fafe785bdf89c7d5acd83e78756d301b445699024eaeb77b54d477336ec2a4f332f2b3f88765ddb0c3",
|
||||||
|
"29acc30e9603ae2fccf90bf97e6cc463ebe28c1b2f9b4b765e70537c25c702a29dcbfbf14c99c54345ba2b51f17b77b5f15db92bbad8fa95c471f5d070a137cc",
|
||||||
|
"3379cbaae562a87b4c0425550ffdd6bfe1203f0d666cc7ea095be407a5dfe61ee91441cd5154b3e53b4f5fb31ad4c7a9ad5c7af4ae679aa51a54003a54ca6b2d",
|
||||||
|
"3095a349d245708c7cf550118703d7302c27b60af5d4e67fc978f8a4e60953c7a04f92fcf41aee64321ccb707a895851552b1e37b00bc5e6b72fa5bcef9e3fff",
|
||||||
|
"07262d738b09321f4dbccec4bb26f48cb0f0ed246ce0b31b9a6e7bc683049f1f3e5545f28ce932dd985c5ab0f43bd6de0770560af329065ed2e49d34624c2cbb",
|
||||||
|
"b6405eca8ee3316c87061cc6ec18dba53e6c250c63ba1f3bae9e55dd3498036af08cd272aa24d713c6020d77ab2f3919af1a32f307420618ab97e73953994fb4",
|
||||||
|
"7ee682f63148ee45f6e5315da81e5c6e557c2c34641fc509c7a5701088c38a74756168e2cd8d351e88fd1a451f360a01f5b2580f9b5a2e8cfc138f3dd59a3ffc",
|
||||||
|
"1d263c179d6b268f6fa016f3a4f29e943891125ed8593c81256059f5a7b44af2dcb2030d175c00e62ecaf7ee96682aa07ab20a611024a28532b1c25b86657902",
|
||||||
|
"106d132cbdb4cd2597812846e2bc1bf732fec5f0a5f65dbb39ec4e6dc64ab2ce6d24630d0f15a805c3540025d84afa98e36703c3dbee713e72dde8465bc1be7e",
|
||||||
|
"0e79968226650667a8d862ea8da4891af56a4e3a8b6d1750e394f0dea76d640d85077bcec2cc86886e506751b4f6a5838f7f0b5fef765d9dc90dcdcbaf079f08",
|
||||||
|
"521156a82ab0c4e566e5844d5e31ad9aaf144bbd5a464fdca34dbd5717e8ff711d3ffebbfa085d67fe996a34f6d3e4e60b1396bf4b1610c263bdbb834d560816",
|
||||||
|
"1aba88befc55bc25efbce02db8b9933e46f57661baeabeb21cc2574d2a518a3cba5dc5a38e49713440b25f9c744e75f6b85c9d8f4681f676160f6105357b8406",
|
||||||
|
"5a9949fcb2c473cda968ac1b5d08566dc2d816d960f57e63b898fa701cf8ebd3f59b124d95bfbbedc5f1cf0e17d5eaed0c02c50b69d8a402cabcca4433b51fd4",
|
||||||
|
"b0cead09807c672af2eb2b0f06dde46cf5370e15a4096b1a7d7cbb36ec31c205fbefca00b7a4162fa89fb4fb3eb78d79770c23f44e7206664ce3cd931c291e5d",
|
||||||
|
"bb6664931ec97044e45b2ae420ae1c551a8874bc937d08e969399c3964ebdba8346cdd5d09caafe4c28ba7ec788191ceca65ddd6f95f18583e040d0f30d0364d",
|
||||||
|
"65bc770a5faa3792369803683e844b0be7ee96f29f6d6a35568006bd5590f9a4ef639b7a8061c7b0424b66b60ac34af3119905f33a9d8c3ae18382ca9b689900",
|
||||||
|
"ea9b4dca333336aaf839a45c6eaa48b8cb4c7ddabffea4f643d6357ea6628a480a5b45f2b052c1b07d1fedca918b6f1139d80f74c24510dcbaa4be70eacc1b06",
|
||||||
|
"e6342fb4a780ad975d0e24bce149989b91d360557e87994f6b457b895575cc02d0c15bad3ce7577f4c63927ff13f3e381ff7e72bdbe745324844a9d27e3f1c01",
|
||||||
|
"3e209c9b33e8e461178ab46b1c64b49a07fb745f1c8bc95fbfb94c6b87c69516651b264ef980937fad41238b91ddc011a5dd777c7efd4494b4b6ecd3a9c22ac0",
|
||||||
|
"fd6a3d5b1875d80486d6e69694a56dbb04a99a4d051f15db2689776ba1c4882e6d462a603b7015dc9f4b7450f05394303b8652cfb404a266962c41bae6e18a94",
|
||||||
|
"951e27517e6bad9e4195fc8671dee3e7e9be69cee1422cb9fecfce0dba875f7b310b93ee3a3d558f941f635f668ff832d2c1d033c5e2f0997e4c66f147344e02",
|
||||||
|
"8eba2f874f1ae84041903c7c4253c82292530fc8509550bfdc34c95c7e2889d5650b0ad8cb988e5c4894cb87fbfbb19612ea93ccc4c5cad17158b9763464b492",
|
||||||
|
"16f712eaa1b7c6354719a8e7dbdfaf55e4063a4d277d947550019b38dfb564830911057d50506136e2394c3b28945cc964967d54e3000c2181626cfb9b73efd2",
|
||||||
|
"c39639e7d5c7fb8cdd0fd3e6a52096039437122f21c78f1679cea9d78a734c56ecbeb28654b4f18e342c331f6f7229ec4b4bc281b2d80a6eb50043f31796c88c",
|
||||||
|
"72d081af99f8a173dcc9a0ac4eb3557405639a29084b54a40172912a2f8a395129d5536f0918e902f9e8fa6000995f4168ddc5f893011be6a0dbc9b8a1a3f5bb",
|
||||||
|
"c11aa81e5efd24d5fc27ee586cfd8847fbb0e27601ccece5ecca0198e3c7765393bb74457c7e7a27eb9170350e1fb53857177506be3e762cc0f14d8c3afe9077",
|
||||||
|
"c28f2150b452e6c0c424bcde6f8d72007f9310fed7f2f87de0dbb64f4479d6c1441ba66f44b2accee61609177ed340128b407ecec7c64bbe50d63d22d8627727",
|
||||||
|
"f63d88122877ec30b8c8b00d22e89000a966426112bd44166e2f525b769ccbe9b286d437a0129130dde1a86c43e04bedb594e671d98283afe64ce331de9828fd",
|
||||||
|
"348b0532880b88a6614a8d7408c3f913357fbb60e995c60205be9139e74998aede7f4581e42f6b52698f7fa1219708c14498067fd1e09502de83a77dd281150c",
|
||||||
|
"5133dc8bef725359dff59792d85eaf75b7e1dcd1978b01c35b1b85fcebc63388ad99a17b6346a217dc1a9622ebd122ecf6913c4d31a6b52a695b86af00d741a0",
|
||||||
|
"2753c4c0e98ecad806e88780ec27fccd0f5c1ab547f9e4bf1659d192c23aa2cc971b58b6802580baef8adc3b776ef7086b2545c2987f348ee3719cdef258c403",
|
||||||
|
"b1663573ce4b9d8caefc865012f3e39714b9898a5da6ce17c25a6a47931a9ddb9bbe98adaa553beed436e89578455416c2a52a525cf2862b8d1d49a2531b7391",
|
||||||
|
"64f58bd6bfc856f5e873b2a2956ea0eda0d6db0da39c8c7fc67c9f9feefcff3072cdf9e6ea37f69a44f0c61aa0da3693c2db5b54960c0281a088151db42b11e8",
|
||||||
|
"0764c7be28125d9065c4b98a69d60aede703547c66a12e17e1c618994132f5ef82482c1e3fe3146cc65376cc109f0138ed9a80e49f1f3c7d610d2f2432f20605",
|
||||||
|
"f748784398a2ff03ebeb07e155e66116a839741a336e32da71ec696001f0ad1b25cd48c69cfca7265eca1dd71904a0ce748ac4124f3571076dfa7116a9cf00e9",
|
||||||
|
"3f0dbc0186bceb6b785ba78d2a2a013c910be157bdaffae81bb6663b1a73722f7f1228795f3ecada87cf6ef0078474af73f31eca0cc200ed975b6893f761cb6d",
|
||||||
|
"d4762cd4599876ca75b2b8fe249944dbd27ace741fdab93616cbc6e425460feb51d4e7adcc38180e7fc47c89024a7f56191adb878dfde4ead62223f5a2610efe",
|
||||||
|
"cd36b3d5b4c91b90fcbba79513cfee1907d8645a162afd0cd4cf4192d4a5f4c892183a8eacdb2b6b6a9d9aa8c11ac1b261b380dbee24ca468f1bfd043c58eefe",
|
||||||
|
"98593452281661a53c48a9d8cd790826c1a1ce567738053d0bee4a91a3d5bd92eefdbabebe3204f2031ca5f781bda99ef5d8ae56e5b04a9e1ecd21b0eb05d3e1",
|
||||||
|
"771f57dd2775ccdab55921d3e8e30ccf484d61fe1c1b9c2ae819d0fb2a12fab9be70c4a7a138da84e8280435daade5bbe66af0836a154f817fb17f3397e725a3",
|
||||||
|
"c60897c6f828e21f16fbb5f15b323f87b6c8955eabf1d38061f707f608abdd993fac3070633e286cf8339ce295dd352df4b4b40b2f29da1dd50b3a05d079e6bb",
|
||||||
|
"8210cd2c2d3b135c2cf07fa0d1433cd771f325d075c6469d9c7f1ba0943cd4ab09808cabf4acb9ce5bb88b498929b4b847f681ad2c490d042db2aec94214b06b",
|
||||||
|
"1d4edfffd8fd80f7e4107840fa3aa31e32598491e4af7013c197a65b7f36dd3ac4b478456111cd4309d9243510782fa31b7c4c95fa951520d020eb7e5c36e4ef",
|
||||||
|
"af8e6e91fab46ce4873e1a50a8ef448cc29121f7f74deef34a71ef89cc00d9274bc6c2454bbb3230d8b2ec94c62b1dec85f3593bfa30ea6f7a44d7c09465a253",
|
||||||
|
"29fd384ed4906f2d13aa9fe7af905990938bed807f1832454a372ab412eea1f5625a1fcc9ac8343b7c67c5aba6e0b1cc4644654913692c6b39eb9187ceacd3ec",
|
||||||
|
"a268c7885d9874a51c44dffed8ea53e94f78456e0b2ed99ff5a3924760813826d960a15edbedbb5de5226ba4b074e71b05c55b9756bb79e55c02754c2c7b6c8a",
|
||||||
|
"0cf8545488d56a86817cd7ecb10f7116b7ea530a45b6ea497b6c72c997e09e3d0da8698f46bb006fc977c2cd3d1177463ac9057fdd1662c85d0c126443c10473",
|
||||||
|
"b39614268fdd8781515e2cfebf89b4d5402bab10c226e6344e6b9ae000fb0d6c79cb2f3ec80e80eaeb1980d2f8698916bd2e9f747236655116649cd3ca23a837",
|
||||||
|
"74bef092fc6f1e5dba3663a3fb003b2a5ba257496536d99f62b9d73f8f9eb3ce9ff3eec709eb883655ec9eb896b9128f2afc89cf7d1ab58a72f4a3bf034d2b4a",
|
||||||
|
"3a988d38d75611f3ef38b8774980b33e573b6c57bee0469ba5eed9b44f29945e7347967fba2c162e1c3be7f310f2f75ee2381e7bfd6b3f0baea8d95dfb1dafb1",
|
||||||
|
"58aedfce6f67ddc85a28c992f1c0bd0969f041e66f1ee88020a125cbfcfebcd61709c9c4eba192c15e69f020d462486019fa8dea0cd7a42921a19d2fe546d43d",
|
||||||
|
"9347bd291473e6b4e368437b8e561e065f649a6d8ada479ad09b1999a8f26b91cf6120fd3bfe014e83f23acfa4c0ad7b3712b2c3c0733270663112ccd9285cd9",
|
||||||
|
"b32163e7c5dbb5f51fdc11d2eac875efbbcb7e7699090a7e7ff8a8d50795af5d74d9ff98543ef8cdf89ac13d0485278756e0ef00c817745661e1d59fe38e7537",
|
||||||
|
"1085d78307b1c4b008c57a2e7e5b234658a0a82e4ff1e4aaac72b312fda0fe27d233bc5b10e9cc17fdc7697b540c7d95eb215a19a1a0e20e1abfa126efd568c7",
|
||||||
|
"4e5c734c7dde011d83eac2b7347b373594f92d7091b9ca34cb9c6f39bdf5a8d2f134379e16d822f6522170ccf2ddd55c84b9e6c64fc927ac4cf8dfb2a17701f2",
|
||||||
|
"695d83bd990a1117b3d0ce06cc888027d12a054c2677fd82f0d4fbfc93575523e7991a5e35a3752e9b70ce62992e268a877744cdd435f5f130869c9a2074b338",
|
||||||
|
"a6213743568e3b3158b9184301f3690847554c68457cb40fc9a4b8cfd8d4a118c301a07737aeda0f929c68913c5f51c80394f53bff1c3e83b2e40ca97eba9e15",
|
||||||
|
"d444bfa2362a96df213d070e33fa841f51334e4e76866b8139e8af3bb3398be2dfaddcbc56b9146de9f68118dc5829e74b0c28d7711907b121f9161cb92b69a9",
|
||||||
|
"142709d62e28fcccd0af97fad0f8465b971e82201dc51070faa0372aa43e92484be1c1e73ba10906d5d1853db6a4106e0a7bf9800d373d6dee2d46d62ef2a461",
|
||||||
|
}
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
// Copyright 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build go1.9
|
||||||
|
|
||||||
|
package blake2b
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"hash"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
newHash256 := func() hash.Hash {
|
||||||
|
h, _ := New256(nil)
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
newHash384 := func() hash.Hash {
|
||||||
|
h, _ := New384(nil)
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
newHash512 := func() hash.Hash {
|
||||||
|
h, _ := New512(nil)
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
crypto.RegisterHash(crypto.BLAKE2b_256, newHash256)
|
||||||
|
crypto.RegisterHash(crypto.BLAKE2b_384, newHash384)
|
||||||
|
crypto.RegisterHash(crypto.BLAKE2b_512, newHash512)
|
||||||
|
}
|
||||||
+187
@@ -0,0 +1,187 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package blake2s implements the BLAKE2s hash algorithm defined by RFC 7693
|
||||||
|
// and the extendable output function (XOF) BLAKE2Xs.
|
||||||
|
//
|
||||||
|
// For a detailed specification of BLAKE2s see https://blake2.net/blake2.pdf
|
||||||
|
// and for BLAKE2Xs see https://blake2.net/blake2x.pdf
|
||||||
|
//
|
||||||
|
// If you aren't sure which function you need, use BLAKE2s (Sum256 or New256).
|
||||||
|
// If you need a secret-key MAC (message authentication code), use the New256
|
||||||
|
// function with a non-nil key.
|
||||||
|
//
|
||||||
|
// BLAKE2X is a construction to compute hash values larger than 32 bytes. It
|
||||||
|
// can produce hash values between 0 and 65535 bytes.
|
||||||
|
package blake2s // import "golang.org/x/crypto/blake2s"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"hash"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// The blocksize of BLAKE2s in bytes.
|
||||||
|
BlockSize = 64
|
||||||
|
|
||||||
|
// The hash size of BLAKE2s-256 in bytes.
|
||||||
|
Size = 32
|
||||||
|
|
||||||
|
// The hash size of BLAKE2s-128 in bytes.
|
||||||
|
Size128 = 16
|
||||||
|
)
|
||||||
|
|
||||||
|
var errKeySize = errors.New("blake2s: invalid key size")
|
||||||
|
|
||||||
|
var iv = [8]uint32{
|
||||||
|
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
|
||||||
|
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sum256 returns the BLAKE2s-256 checksum of the data.
|
||||||
|
func Sum256(data []byte) [Size]byte {
|
||||||
|
var sum [Size]byte
|
||||||
|
checkSum(&sum, Size, data)
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
// New256 returns a new hash.Hash computing the BLAKE2s-256 checksum. A non-nil
|
||||||
|
// key turns the hash into a MAC. The key must between zero and 32 bytes long.
|
||||||
|
func New256(key []byte) (hash.Hash, error) { return newDigest(Size, key) }
|
||||||
|
|
||||||
|
// New128 returns a new hash.Hash computing the BLAKE2s-128 checksum given a
|
||||||
|
// non-empty key. Note that a 128-bit digest is too small to be secure as a
|
||||||
|
// cryptographic hash and should only be used as a MAC, thus the key argument
|
||||||
|
// is not optional.
|
||||||
|
func New128(key []byte) (hash.Hash, error) {
|
||||||
|
if len(key) == 0 {
|
||||||
|
return nil, errors.New("blake2s: a key is required for a 128-bit hash")
|
||||||
|
}
|
||||||
|
return newDigest(Size128, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDigest(hashSize int, key []byte) (*digest, error) {
|
||||||
|
if len(key) > Size {
|
||||||
|
return nil, errKeySize
|
||||||
|
}
|
||||||
|
d := &digest{
|
||||||
|
size: hashSize,
|
||||||
|
keyLen: len(key),
|
||||||
|
}
|
||||||
|
copy(d.key[:], key)
|
||||||
|
d.Reset()
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkSum(sum *[Size]byte, hashSize int, data []byte) {
|
||||||
|
var (
|
||||||
|
h [8]uint32
|
||||||
|
c [2]uint32
|
||||||
|
)
|
||||||
|
|
||||||
|
h = iv
|
||||||
|
h[0] ^= uint32(hashSize) | (1 << 16) | (1 << 24)
|
||||||
|
|
||||||
|
if length := len(data); length > BlockSize {
|
||||||
|
n := length &^ (BlockSize - 1)
|
||||||
|
if length == n {
|
||||||
|
n -= BlockSize
|
||||||
|
}
|
||||||
|
hashBlocks(&h, &c, 0, data[:n])
|
||||||
|
data = data[n:]
|
||||||
|
}
|
||||||
|
|
||||||
|
var block [BlockSize]byte
|
||||||
|
offset := copy(block[:], data)
|
||||||
|
remaining := uint32(BlockSize - offset)
|
||||||
|
|
||||||
|
if c[0] < remaining {
|
||||||
|
c[1]--
|
||||||
|
}
|
||||||
|
c[0] -= remaining
|
||||||
|
|
||||||
|
hashBlocks(&h, &c, 0xFFFFFFFF, block[:])
|
||||||
|
|
||||||
|
for i, v := range h {
|
||||||
|
binary.LittleEndian.PutUint32(sum[4*i:], v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type digest struct {
|
||||||
|
h [8]uint32
|
||||||
|
c [2]uint32
|
||||||
|
size int
|
||||||
|
block [BlockSize]byte
|
||||||
|
offset int
|
||||||
|
|
||||||
|
key [BlockSize]byte
|
||||||
|
keyLen int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *digest) BlockSize() int { return BlockSize }
|
||||||
|
|
||||||
|
func (d *digest) Size() int { return d.size }
|
||||||
|
|
||||||
|
func (d *digest) Reset() {
|
||||||
|
d.h = iv
|
||||||
|
d.h[0] ^= uint32(d.size) | (uint32(d.keyLen) << 8) | (1 << 16) | (1 << 24)
|
||||||
|
d.offset, d.c[0], d.c[1] = 0, 0, 0
|
||||||
|
if d.keyLen > 0 {
|
||||||
|
d.block = d.key
|
||||||
|
d.offset = BlockSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *digest) Write(p []byte) (n int, err error) {
|
||||||
|
n = len(p)
|
||||||
|
|
||||||
|
if d.offset > 0 {
|
||||||
|
remaining := BlockSize - d.offset
|
||||||
|
if n <= remaining {
|
||||||
|
d.offset += copy(d.block[d.offset:], p)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
copy(d.block[d.offset:], p[:remaining])
|
||||||
|
hashBlocks(&d.h, &d.c, 0, d.block[:])
|
||||||
|
d.offset = 0
|
||||||
|
p = p[remaining:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if length := len(p); length > BlockSize {
|
||||||
|
nn := length &^ (BlockSize - 1)
|
||||||
|
if length == nn {
|
||||||
|
nn -= BlockSize
|
||||||
|
}
|
||||||
|
hashBlocks(&d.h, &d.c, 0, p[:nn])
|
||||||
|
p = p[nn:]
|
||||||
|
}
|
||||||
|
|
||||||
|
d.offset += copy(d.block[:], p)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *digest) Sum(sum []byte) []byte {
|
||||||
|
var hash [Size]byte
|
||||||
|
d.finalize(&hash)
|
||||||
|
return append(sum, hash[:d.size]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *digest) finalize(hash *[Size]byte) {
|
||||||
|
var block [BlockSize]byte
|
||||||
|
h := d.h
|
||||||
|
c := d.c
|
||||||
|
|
||||||
|
copy(block[:], d.block[:d.offset])
|
||||||
|
remaining := uint32(BlockSize - d.offset)
|
||||||
|
if c[0] < remaining {
|
||||||
|
c[1]--
|
||||||
|
}
|
||||||
|
c[0] -= remaining
|
||||||
|
|
||||||
|
hashBlocks(&h, &c, 0xFFFFFFFF, block[:])
|
||||||
|
for i, v := range h {
|
||||||
|
binary.LittleEndian.PutUint32(hash[4*i:], v)
|
||||||
|
}
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build 386,!gccgo,!appengine
|
||||||
|
|
||||||
|
package blake2s
|
||||||
|
|
||||||
|
var (
|
||||||
|
useSSE4 = false
|
||||||
|
useSSSE3 = supportSSSE3()
|
||||||
|
useSSE2 = supportSSE2()
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func supportSSE2() bool
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func supportSSSE3() bool
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func hashBlocksSSE2(h *[8]uint32, c *[2]uint32, flag uint32, blocks []byte)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func hashBlocksSSSE3(h *[8]uint32, c *[2]uint32, flag uint32, blocks []byte)
|
||||||
|
|
||||||
|
func hashBlocks(h *[8]uint32, c *[2]uint32, flag uint32, blocks []byte) {
|
||||||
|
if useSSSE3 {
|
||||||
|
hashBlocksSSSE3(h, c, flag, blocks)
|
||||||
|
} else if useSSE2 {
|
||||||
|
hashBlocksSSE2(h, c, flag, blocks)
|
||||||
|
} else {
|
||||||
|
hashBlocksGeneric(h, c, flag, blocks)
|
||||||
|
}
|
||||||
|
}
|
||||||
+460
@@ -0,0 +1,460 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build 386,!gccgo,!appengine
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
DATA iv0<>+0x00(SB)/4, $0x6a09e667
|
||||||
|
DATA iv0<>+0x04(SB)/4, $0xbb67ae85
|
||||||
|
DATA iv0<>+0x08(SB)/4, $0x3c6ef372
|
||||||
|
DATA iv0<>+0x0c(SB)/4, $0xa54ff53a
|
||||||
|
GLOBL iv0<>(SB), (NOPTR+RODATA), $16
|
||||||
|
|
||||||
|
DATA iv1<>+0x00(SB)/4, $0x510e527f
|
||||||
|
DATA iv1<>+0x04(SB)/4, $0x9b05688c
|
||||||
|
DATA iv1<>+0x08(SB)/4, $0x1f83d9ab
|
||||||
|
DATA iv1<>+0x0c(SB)/4, $0x5be0cd19
|
||||||
|
GLOBL iv1<>(SB), (NOPTR+RODATA), $16
|
||||||
|
|
||||||
|
DATA rol16<>+0x00(SB)/8, $0x0504070601000302
|
||||||
|
DATA rol16<>+0x08(SB)/8, $0x0D0C0F0E09080B0A
|
||||||
|
GLOBL rol16<>(SB), (NOPTR+RODATA), $16
|
||||||
|
|
||||||
|
DATA rol8<>+0x00(SB)/8, $0x0407060500030201
|
||||||
|
DATA rol8<>+0x08(SB)/8, $0x0C0F0E0D080B0A09
|
||||||
|
GLOBL rol8<>(SB), (NOPTR+RODATA), $16
|
||||||
|
|
||||||
|
DATA counter<>+0x00(SB)/8, $0x40
|
||||||
|
DATA counter<>+0x08(SB)/8, $0x0
|
||||||
|
GLOBL counter<>(SB), (NOPTR+RODATA), $16
|
||||||
|
|
||||||
|
#define ROTL_SSE2(n, t, v) \
|
||||||
|
MOVO v, t; \
|
||||||
|
PSLLL $n, t; \
|
||||||
|
PSRLL $(32-n), v; \
|
||||||
|
PXOR t, v
|
||||||
|
|
||||||
|
#define ROTL_SSSE3(c, v) \
|
||||||
|
PSHUFB c, v
|
||||||
|
|
||||||
|
#define ROUND_SSE2(v0, v1, v2, v3, m0, m1, m2, m3, t) \
|
||||||
|
PADDL m0, v0; \
|
||||||
|
PADDL v1, v0; \
|
||||||
|
PXOR v0, v3; \
|
||||||
|
ROTL_SSE2(16, t, v3); \
|
||||||
|
PADDL v3, v2; \
|
||||||
|
PXOR v2, v1; \
|
||||||
|
ROTL_SSE2(20, t, v1); \
|
||||||
|
PADDL m1, v0; \
|
||||||
|
PADDL v1, v0; \
|
||||||
|
PXOR v0, v3; \
|
||||||
|
ROTL_SSE2(24, t, v3); \
|
||||||
|
PADDL v3, v2; \
|
||||||
|
PXOR v2, v1; \
|
||||||
|
ROTL_SSE2(25, t, v1); \
|
||||||
|
PSHUFL $0x39, v1, v1; \
|
||||||
|
PSHUFL $0x4E, v2, v2; \
|
||||||
|
PSHUFL $0x93, v3, v3; \
|
||||||
|
PADDL m2, v0; \
|
||||||
|
PADDL v1, v0; \
|
||||||
|
PXOR v0, v3; \
|
||||||
|
ROTL_SSE2(16, t, v3); \
|
||||||
|
PADDL v3, v2; \
|
||||||
|
PXOR v2, v1; \
|
||||||
|
ROTL_SSE2(20, t, v1); \
|
||||||
|
PADDL m3, v0; \
|
||||||
|
PADDL v1, v0; \
|
||||||
|
PXOR v0, v3; \
|
||||||
|
ROTL_SSE2(24, t, v3); \
|
||||||
|
PADDL v3, v2; \
|
||||||
|
PXOR v2, v1; \
|
||||||
|
ROTL_SSE2(25, t, v1); \
|
||||||
|
PSHUFL $0x39, v3, v3; \
|
||||||
|
PSHUFL $0x4E, v2, v2; \
|
||||||
|
PSHUFL $0x93, v1, v1
|
||||||
|
|
||||||
|
#define ROUND_SSSE3(v0, v1, v2, v3, m0, m1, m2, m3, t, c16, c8) \
|
||||||
|
PADDL m0, v0; \
|
||||||
|
PADDL v1, v0; \
|
||||||
|
PXOR v0, v3; \
|
||||||
|
ROTL_SSSE3(c16, v3); \
|
||||||
|
PADDL v3, v2; \
|
||||||
|
PXOR v2, v1; \
|
||||||
|
ROTL_SSE2(20, t, v1); \
|
||||||
|
PADDL m1, v0; \
|
||||||
|
PADDL v1, v0; \
|
||||||
|
PXOR v0, v3; \
|
||||||
|
ROTL_SSSE3(c8, v3); \
|
||||||
|
PADDL v3, v2; \
|
||||||
|
PXOR v2, v1; \
|
||||||
|
ROTL_SSE2(25, t, v1); \
|
||||||
|
PSHUFL $0x39, v1, v1; \
|
||||||
|
PSHUFL $0x4E, v2, v2; \
|
||||||
|
PSHUFL $0x93, v3, v3; \
|
||||||
|
PADDL m2, v0; \
|
||||||
|
PADDL v1, v0; \
|
||||||
|
PXOR v0, v3; \
|
||||||
|
ROTL_SSSE3(c16, v3); \
|
||||||
|
PADDL v3, v2; \
|
||||||
|
PXOR v2, v1; \
|
||||||
|
ROTL_SSE2(20, t, v1); \
|
||||||
|
PADDL m3, v0; \
|
||||||
|
PADDL v1, v0; \
|
||||||
|
PXOR v0, v3; \
|
||||||
|
ROTL_SSSE3(c8, v3); \
|
||||||
|
PADDL v3, v2; \
|
||||||
|
PXOR v2, v1; \
|
||||||
|
ROTL_SSE2(25, t, v1); \
|
||||||
|
PSHUFL $0x39, v3, v3; \
|
||||||
|
PSHUFL $0x4E, v2, v2; \
|
||||||
|
PSHUFL $0x93, v1, v1
|
||||||
|
|
||||||
|
#define PRECOMPUTE(dst, off, src, t) \
|
||||||
|
MOVL 0*4(src), t; \
|
||||||
|
MOVL t, 0*4+off+0(dst); \
|
||||||
|
MOVL t, 9*4+off+64(dst); \
|
||||||
|
MOVL t, 5*4+off+128(dst); \
|
||||||
|
MOVL t, 14*4+off+192(dst); \
|
||||||
|
MOVL t, 4*4+off+256(dst); \
|
||||||
|
MOVL t, 2*4+off+320(dst); \
|
||||||
|
MOVL t, 8*4+off+384(dst); \
|
||||||
|
MOVL t, 12*4+off+448(dst); \
|
||||||
|
MOVL t, 3*4+off+512(dst); \
|
||||||
|
MOVL t, 15*4+off+576(dst); \
|
||||||
|
MOVL 1*4(src), t; \
|
||||||
|
MOVL t, 4*4+off+0(dst); \
|
||||||
|
MOVL t, 8*4+off+64(dst); \
|
||||||
|
MOVL t, 14*4+off+128(dst); \
|
||||||
|
MOVL t, 5*4+off+192(dst); \
|
||||||
|
MOVL t, 12*4+off+256(dst); \
|
||||||
|
MOVL t, 11*4+off+320(dst); \
|
||||||
|
MOVL t, 1*4+off+384(dst); \
|
||||||
|
MOVL t, 6*4+off+448(dst); \
|
||||||
|
MOVL t, 10*4+off+512(dst); \
|
||||||
|
MOVL t, 3*4+off+576(dst); \
|
||||||
|
MOVL 2*4(src), t; \
|
||||||
|
MOVL t, 1*4+off+0(dst); \
|
||||||
|
MOVL t, 13*4+off+64(dst); \
|
||||||
|
MOVL t, 6*4+off+128(dst); \
|
||||||
|
MOVL t, 8*4+off+192(dst); \
|
||||||
|
MOVL t, 2*4+off+256(dst); \
|
||||||
|
MOVL t, 0*4+off+320(dst); \
|
||||||
|
MOVL t, 14*4+off+384(dst); \
|
||||||
|
MOVL t, 11*4+off+448(dst); \
|
||||||
|
MOVL t, 12*4+off+512(dst); \
|
||||||
|
MOVL t, 4*4+off+576(dst); \
|
||||||
|
MOVL 3*4(src), t; \
|
||||||
|
MOVL t, 5*4+off+0(dst); \
|
||||||
|
MOVL t, 15*4+off+64(dst); \
|
||||||
|
MOVL t, 9*4+off+128(dst); \
|
||||||
|
MOVL t, 1*4+off+192(dst); \
|
||||||
|
MOVL t, 11*4+off+256(dst); \
|
||||||
|
MOVL t, 7*4+off+320(dst); \
|
||||||
|
MOVL t, 13*4+off+384(dst); \
|
||||||
|
MOVL t, 3*4+off+448(dst); \
|
||||||
|
MOVL t, 6*4+off+512(dst); \
|
||||||
|
MOVL t, 10*4+off+576(dst); \
|
||||||
|
MOVL 4*4(src), t; \
|
||||||
|
MOVL t, 2*4+off+0(dst); \
|
||||||
|
MOVL t, 1*4+off+64(dst); \
|
||||||
|
MOVL t, 15*4+off+128(dst); \
|
||||||
|
MOVL t, 10*4+off+192(dst); \
|
||||||
|
MOVL t, 6*4+off+256(dst); \
|
||||||
|
MOVL t, 8*4+off+320(dst); \
|
||||||
|
MOVL t, 3*4+off+384(dst); \
|
||||||
|
MOVL t, 13*4+off+448(dst); \
|
||||||
|
MOVL t, 14*4+off+512(dst); \
|
||||||
|
MOVL t, 5*4+off+576(dst); \
|
||||||
|
MOVL 5*4(src), t; \
|
||||||
|
MOVL t, 6*4+off+0(dst); \
|
||||||
|
MOVL t, 11*4+off+64(dst); \
|
||||||
|
MOVL t, 2*4+off+128(dst); \
|
||||||
|
MOVL t, 9*4+off+192(dst); \
|
||||||
|
MOVL t, 1*4+off+256(dst); \
|
||||||
|
MOVL t, 13*4+off+320(dst); \
|
||||||
|
MOVL t, 4*4+off+384(dst); \
|
||||||
|
MOVL t, 8*4+off+448(dst); \
|
||||||
|
MOVL t, 15*4+off+512(dst); \
|
||||||
|
MOVL t, 7*4+off+576(dst); \
|
||||||
|
MOVL 6*4(src), t; \
|
||||||
|
MOVL t, 3*4+off+0(dst); \
|
||||||
|
MOVL t, 7*4+off+64(dst); \
|
||||||
|
MOVL t, 13*4+off+128(dst); \
|
||||||
|
MOVL t, 12*4+off+192(dst); \
|
||||||
|
MOVL t, 10*4+off+256(dst); \
|
||||||
|
MOVL t, 1*4+off+320(dst); \
|
||||||
|
MOVL t, 9*4+off+384(dst); \
|
||||||
|
MOVL t, 14*4+off+448(dst); \
|
||||||
|
MOVL t, 0*4+off+512(dst); \
|
||||||
|
MOVL t, 6*4+off+576(dst); \
|
||||||
|
MOVL 7*4(src), t; \
|
||||||
|
MOVL t, 7*4+off+0(dst); \
|
||||||
|
MOVL t, 14*4+off+64(dst); \
|
||||||
|
MOVL t, 10*4+off+128(dst); \
|
||||||
|
MOVL t, 0*4+off+192(dst); \
|
||||||
|
MOVL t, 5*4+off+256(dst); \
|
||||||
|
MOVL t, 9*4+off+320(dst); \
|
||||||
|
MOVL t, 12*4+off+384(dst); \
|
||||||
|
MOVL t, 1*4+off+448(dst); \
|
||||||
|
MOVL t, 13*4+off+512(dst); \
|
||||||
|
MOVL t, 2*4+off+576(dst); \
|
||||||
|
MOVL 8*4(src), t; \
|
||||||
|
MOVL t, 8*4+off+0(dst); \
|
||||||
|
MOVL t, 5*4+off+64(dst); \
|
||||||
|
MOVL t, 4*4+off+128(dst); \
|
||||||
|
MOVL t, 15*4+off+192(dst); \
|
||||||
|
MOVL t, 14*4+off+256(dst); \
|
||||||
|
MOVL t, 3*4+off+320(dst); \
|
||||||
|
MOVL t, 11*4+off+384(dst); \
|
||||||
|
MOVL t, 10*4+off+448(dst); \
|
||||||
|
MOVL t, 7*4+off+512(dst); \
|
||||||
|
MOVL t, 1*4+off+576(dst); \
|
||||||
|
MOVL 9*4(src), t; \
|
||||||
|
MOVL t, 12*4+off+0(dst); \
|
||||||
|
MOVL t, 2*4+off+64(dst); \
|
||||||
|
MOVL t, 11*4+off+128(dst); \
|
||||||
|
MOVL t, 4*4+off+192(dst); \
|
||||||
|
MOVL t, 0*4+off+256(dst); \
|
||||||
|
MOVL t, 15*4+off+320(dst); \
|
||||||
|
MOVL t, 10*4+off+384(dst); \
|
||||||
|
MOVL t, 7*4+off+448(dst); \
|
||||||
|
MOVL t, 5*4+off+512(dst); \
|
||||||
|
MOVL t, 9*4+off+576(dst); \
|
||||||
|
MOVL 10*4(src), t; \
|
||||||
|
MOVL t, 9*4+off+0(dst); \
|
||||||
|
MOVL t, 4*4+off+64(dst); \
|
||||||
|
MOVL t, 8*4+off+128(dst); \
|
||||||
|
MOVL t, 13*4+off+192(dst); \
|
||||||
|
MOVL t, 3*4+off+256(dst); \
|
||||||
|
MOVL t, 5*4+off+320(dst); \
|
||||||
|
MOVL t, 7*4+off+384(dst); \
|
||||||
|
MOVL t, 15*4+off+448(dst); \
|
||||||
|
MOVL t, 11*4+off+512(dst); \
|
||||||
|
MOVL t, 0*4+off+576(dst); \
|
||||||
|
MOVL 11*4(src), t; \
|
||||||
|
MOVL t, 13*4+off+0(dst); \
|
||||||
|
MOVL t, 10*4+off+64(dst); \
|
||||||
|
MOVL t, 0*4+off+128(dst); \
|
||||||
|
MOVL t, 3*4+off+192(dst); \
|
||||||
|
MOVL t, 9*4+off+256(dst); \
|
||||||
|
MOVL t, 6*4+off+320(dst); \
|
||||||
|
MOVL t, 15*4+off+384(dst); \
|
||||||
|
MOVL t, 4*4+off+448(dst); \
|
||||||
|
MOVL t, 2*4+off+512(dst); \
|
||||||
|
MOVL t, 12*4+off+576(dst); \
|
||||||
|
MOVL 12*4(src), t; \
|
||||||
|
MOVL t, 10*4+off+0(dst); \
|
||||||
|
MOVL t, 12*4+off+64(dst); \
|
||||||
|
MOVL t, 1*4+off+128(dst); \
|
||||||
|
MOVL t, 6*4+off+192(dst); \
|
||||||
|
MOVL t, 13*4+off+256(dst); \
|
||||||
|
MOVL t, 4*4+off+320(dst); \
|
||||||
|
MOVL t, 0*4+off+384(dst); \
|
||||||
|
MOVL t, 2*4+off+448(dst); \
|
||||||
|
MOVL t, 8*4+off+512(dst); \
|
||||||
|
MOVL t, 14*4+off+576(dst); \
|
||||||
|
MOVL 13*4(src), t; \
|
||||||
|
MOVL t, 14*4+off+0(dst); \
|
||||||
|
MOVL t, 3*4+off+64(dst); \
|
||||||
|
MOVL t, 7*4+off+128(dst); \
|
||||||
|
MOVL t, 2*4+off+192(dst); \
|
||||||
|
MOVL t, 15*4+off+256(dst); \
|
||||||
|
MOVL t, 12*4+off+320(dst); \
|
||||||
|
MOVL t, 6*4+off+384(dst); \
|
||||||
|
MOVL t, 0*4+off+448(dst); \
|
||||||
|
MOVL t, 9*4+off+512(dst); \
|
||||||
|
MOVL t, 11*4+off+576(dst); \
|
||||||
|
MOVL 14*4(src), t; \
|
||||||
|
MOVL t, 11*4+off+0(dst); \
|
||||||
|
MOVL t, 0*4+off+64(dst); \
|
||||||
|
MOVL t, 12*4+off+128(dst); \
|
||||||
|
MOVL t, 7*4+off+192(dst); \
|
||||||
|
MOVL t, 8*4+off+256(dst); \
|
||||||
|
MOVL t, 14*4+off+320(dst); \
|
||||||
|
MOVL t, 2*4+off+384(dst); \
|
||||||
|
MOVL t, 5*4+off+448(dst); \
|
||||||
|
MOVL t, 1*4+off+512(dst); \
|
||||||
|
MOVL t, 13*4+off+576(dst); \
|
||||||
|
MOVL 15*4(src), t; \
|
||||||
|
MOVL t, 15*4+off+0(dst); \
|
||||||
|
MOVL t, 6*4+off+64(dst); \
|
||||||
|
MOVL t, 3*4+off+128(dst); \
|
||||||
|
MOVL t, 11*4+off+192(dst); \
|
||||||
|
MOVL t, 7*4+off+256(dst); \
|
||||||
|
MOVL t, 10*4+off+320(dst); \
|
||||||
|
MOVL t, 5*4+off+384(dst); \
|
||||||
|
MOVL t, 9*4+off+448(dst); \
|
||||||
|
MOVL t, 4*4+off+512(dst); \
|
||||||
|
MOVL t, 8*4+off+576(dst)
|
||||||
|
|
||||||
|
// func hashBlocksSSE2(h *[8]uint32, c *[2]uint32, flag uint32, blocks []byte)
|
||||||
|
TEXT ·hashBlocksSSE2(SB), 0, $672-24 // frame = 656 + 16 byte alignment
|
||||||
|
MOVL h+0(FP), AX
|
||||||
|
MOVL c+4(FP), BX
|
||||||
|
MOVL flag+8(FP), CX
|
||||||
|
MOVL blocks_base+12(FP), SI
|
||||||
|
MOVL blocks_len+16(FP), DX
|
||||||
|
|
||||||
|
MOVL SP, BP
|
||||||
|
MOVL SP, DI
|
||||||
|
ADDL $15, DI
|
||||||
|
ANDL $~15, DI
|
||||||
|
MOVL DI, SP
|
||||||
|
|
||||||
|
MOVL CX, 8(SP)
|
||||||
|
MOVL 0(BX), CX
|
||||||
|
MOVL CX, 0(SP)
|
||||||
|
MOVL 4(BX), CX
|
||||||
|
MOVL CX, 4(SP)
|
||||||
|
XORL CX, CX
|
||||||
|
MOVL CX, 12(SP)
|
||||||
|
|
||||||
|
MOVOU 0(AX), X0
|
||||||
|
MOVOU 16(AX), X1
|
||||||
|
MOVOU counter<>(SB), X2
|
||||||
|
|
||||||
|
loop:
|
||||||
|
MOVO X0, X4
|
||||||
|
MOVO X1, X5
|
||||||
|
MOVOU iv0<>(SB), X6
|
||||||
|
MOVOU iv1<>(SB), X7
|
||||||
|
|
||||||
|
MOVO 0(SP), X3
|
||||||
|
PADDQ X2, X3
|
||||||
|
PXOR X3, X7
|
||||||
|
MOVO X3, 0(SP)
|
||||||
|
|
||||||
|
PRECOMPUTE(SP, 16, SI, CX)
|
||||||
|
ROUND_SSE2(X4, X5, X6, X7, 16(SP), 32(SP), 48(SP), 64(SP), X3)
|
||||||
|
ROUND_SSE2(X4, X5, X6, X7, 16+64(SP), 32+64(SP), 48+64(SP), 64+64(SP), X3)
|
||||||
|
ROUND_SSE2(X4, X5, X6, X7, 16+128(SP), 32+128(SP), 48+128(SP), 64+128(SP), X3)
|
||||||
|
ROUND_SSE2(X4, X5, X6, X7, 16+192(SP), 32+192(SP), 48+192(SP), 64+192(SP), X3)
|
||||||
|
ROUND_SSE2(X4, X5, X6, X7, 16+256(SP), 32+256(SP), 48+256(SP), 64+256(SP), X3)
|
||||||
|
ROUND_SSE2(X4, X5, X6, X7, 16+320(SP), 32+320(SP), 48+320(SP), 64+320(SP), X3)
|
||||||
|
ROUND_SSE2(X4, X5, X6, X7, 16+384(SP), 32+384(SP), 48+384(SP), 64+384(SP), X3)
|
||||||
|
ROUND_SSE2(X4, X5, X6, X7, 16+448(SP), 32+448(SP), 48+448(SP), 64+448(SP), X3)
|
||||||
|
ROUND_SSE2(X4, X5, X6, X7, 16+512(SP), 32+512(SP), 48+512(SP), 64+512(SP), X3)
|
||||||
|
ROUND_SSE2(X4, X5, X6, X7, 16+576(SP), 32+576(SP), 48+576(SP), 64+576(SP), X3)
|
||||||
|
|
||||||
|
PXOR X4, X0
|
||||||
|
PXOR X5, X1
|
||||||
|
PXOR X6, X0
|
||||||
|
PXOR X7, X1
|
||||||
|
|
||||||
|
LEAL 64(SI), SI
|
||||||
|
SUBL $64, DX
|
||||||
|
JNE loop
|
||||||
|
|
||||||
|
MOVL 0(SP), CX
|
||||||
|
MOVL CX, 0(BX)
|
||||||
|
MOVL 4(SP), CX
|
||||||
|
MOVL CX, 4(BX)
|
||||||
|
|
||||||
|
MOVOU X0, 0(AX)
|
||||||
|
MOVOU X1, 16(AX)
|
||||||
|
|
||||||
|
MOVL BP, SP
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func hashBlocksSSSE3(h *[8]uint32, c *[2]uint32, flag uint32, blocks []byte)
|
||||||
|
TEXT ·hashBlocksSSSE3(SB), 0, $704-24 // frame = 688 + 16 byte alignment
|
||||||
|
MOVL h+0(FP), AX
|
||||||
|
MOVL c+4(FP), BX
|
||||||
|
MOVL flag+8(FP), CX
|
||||||
|
MOVL blocks_base+12(FP), SI
|
||||||
|
MOVL blocks_len+16(FP), DX
|
||||||
|
|
||||||
|
MOVL SP, BP
|
||||||
|
MOVL SP, DI
|
||||||
|
ADDL $15, DI
|
||||||
|
ANDL $~15, DI
|
||||||
|
MOVL DI, SP
|
||||||
|
|
||||||
|
MOVL CX, 8(SP)
|
||||||
|
MOVL 0(BX), CX
|
||||||
|
MOVL CX, 0(SP)
|
||||||
|
MOVL 4(BX), CX
|
||||||
|
MOVL CX, 4(SP)
|
||||||
|
XORL CX, CX
|
||||||
|
MOVL CX, 12(SP)
|
||||||
|
|
||||||
|
MOVOU 0(AX), X0
|
||||||
|
MOVOU 16(AX), X1
|
||||||
|
MOVOU counter<>(SB), X2
|
||||||
|
|
||||||
|
loop:
|
||||||
|
MOVO X0, 656(SP)
|
||||||
|
MOVO X1, 672(SP)
|
||||||
|
MOVO X0, X4
|
||||||
|
MOVO X1, X5
|
||||||
|
MOVOU iv0<>(SB), X6
|
||||||
|
MOVOU iv1<>(SB), X7
|
||||||
|
|
||||||
|
MOVO 0(SP), X3
|
||||||
|
PADDQ X2, X3
|
||||||
|
PXOR X3, X7
|
||||||
|
MOVO X3, 0(SP)
|
||||||
|
|
||||||
|
MOVOU rol16<>(SB), X0
|
||||||
|
MOVOU rol8<>(SB), X1
|
||||||
|
|
||||||
|
PRECOMPUTE(SP, 16, SI, CX)
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, 16(SP), 32(SP), 48(SP), 64(SP), X3, X0, X1)
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, 16+64(SP), 32+64(SP), 48+64(SP), 64+64(SP), X3, X0, X1)
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, 16+128(SP), 32+128(SP), 48+128(SP), 64+128(SP), X3, X0, X1)
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, 16+192(SP), 32+192(SP), 48+192(SP), 64+192(SP), X3, X0, X1)
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, 16+256(SP), 32+256(SP), 48+256(SP), 64+256(SP), X3, X0, X1)
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, 16+320(SP), 32+320(SP), 48+320(SP), 64+320(SP), X3, X0, X1)
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, 16+384(SP), 32+384(SP), 48+384(SP), 64+384(SP), X3, X0, X1)
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, 16+448(SP), 32+448(SP), 48+448(SP), 64+448(SP), X3, X0, X1)
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, 16+512(SP), 32+512(SP), 48+512(SP), 64+512(SP), X3, X0, X1)
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, 16+576(SP), 32+576(SP), 48+576(SP), 64+576(SP), X3, X0, X1)
|
||||||
|
|
||||||
|
MOVO 656(SP), X0
|
||||||
|
MOVO 672(SP), X1
|
||||||
|
PXOR X4, X0
|
||||||
|
PXOR X5, X1
|
||||||
|
PXOR X6, X0
|
||||||
|
PXOR X7, X1
|
||||||
|
|
||||||
|
LEAL 64(SI), SI
|
||||||
|
SUBL $64, DX
|
||||||
|
JNE loop
|
||||||
|
|
||||||
|
MOVL 0(SP), CX
|
||||||
|
MOVL CX, 0(BX)
|
||||||
|
MOVL 4(SP), CX
|
||||||
|
MOVL CX, 4(BX)
|
||||||
|
|
||||||
|
MOVOU X0, 0(AX)
|
||||||
|
MOVOU X1, 16(AX)
|
||||||
|
|
||||||
|
MOVL BP, SP
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func supportSSSE3() bool
|
||||||
|
TEXT ·supportSSSE3(SB), 4, $0-1
|
||||||
|
MOVL $1, AX
|
||||||
|
CPUID
|
||||||
|
MOVL CX, BX
|
||||||
|
ANDL $0x1, BX // supports SSE3
|
||||||
|
JZ FALSE
|
||||||
|
ANDL $0x200, CX // supports SSSE3
|
||||||
|
JZ FALSE
|
||||||
|
MOVB $1, ret+0(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
FALSE:
|
||||||
|
MOVB $0, ret+0(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func supportSSE2() bool
|
||||||
|
TEXT ·supportSSE2(SB), 4, $0-1
|
||||||
|
MOVL $1, AX
|
||||||
|
CPUID
|
||||||
|
SHRL $26, DX
|
||||||
|
ANDL $1, DX // DX != 0 if support SSE2
|
||||||
|
MOVB DX, ret+0(FP)
|
||||||
|
RET
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build amd64,!gccgo,!appengine
|
||||||
|
|
||||||
|
package blake2s
|
||||||
|
|
||||||
|
var (
|
||||||
|
useSSE4 = supportSSE4()
|
||||||
|
useSSSE3 = supportSSSE3()
|
||||||
|
useSSE2 = true // Always available on amd64
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func supportSSSE3() bool
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func supportSSE4() bool
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func hashBlocksSSE2(h *[8]uint32, c *[2]uint32, flag uint32, blocks []byte)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func hashBlocksSSSE3(h *[8]uint32, c *[2]uint32, flag uint32, blocks []byte)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func hashBlocksSSE4(h *[8]uint32, c *[2]uint32, flag uint32, blocks []byte)
|
||||||
|
|
||||||
|
func hashBlocks(h *[8]uint32, c *[2]uint32, flag uint32, blocks []byte) {
|
||||||
|
if useSSE4 {
|
||||||
|
hashBlocksSSE4(h, c, flag, blocks)
|
||||||
|
} else if useSSSE3 {
|
||||||
|
hashBlocksSSSE3(h, c, flag, blocks)
|
||||||
|
} else if useSSE2 {
|
||||||
|
hashBlocksSSE2(h, c, flag, blocks)
|
||||||
|
} else {
|
||||||
|
hashBlocksGeneric(h, c, flag, blocks)
|
||||||
|
}
|
||||||
|
}
|
||||||
+463
@@ -0,0 +1,463 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build amd64,!gccgo,!appengine
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
DATA iv0<>+0x00(SB)/4, $0x6a09e667
|
||||||
|
DATA iv0<>+0x04(SB)/4, $0xbb67ae85
|
||||||
|
DATA iv0<>+0x08(SB)/4, $0x3c6ef372
|
||||||
|
DATA iv0<>+0x0c(SB)/4, $0xa54ff53a
|
||||||
|
GLOBL iv0<>(SB), (NOPTR+RODATA), $16
|
||||||
|
|
||||||
|
DATA iv1<>+0x00(SB)/4, $0x510e527f
|
||||||
|
DATA iv1<>+0x04(SB)/4, $0x9b05688c
|
||||||
|
DATA iv1<>+0x08(SB)/4, $0x1f83d9ab
|
||||||
|
DATA iv1<>+0x0c(SB)/4, $0x5be0cd19
|
||||||
|
GLOBL iv1<>(SB), (NOPTR+RODATA), $16
|
||||||
|
|
||||||
|
DATA rol16<>+0x00(SB)/8, $0x0504070601000302
|
||||||
|
DATA rol16<>+0x08(SB)/8, $0x0D0C0F0E09080B0A
|
||||||
|
GLOBL rol16<>(SB), (NOPTR+RODATA), $16
|
||||||
|
|
||||||
|
DATA rol8<>+0x00(SB)/8, $0x0407060500030201
|
||||||
|
DATA rol8<>+0x08(SB)/8, $0x0C0F0E0D080B0A09
|
||||||
|
GLOBL rol8<>(SB), (NOPTR+RODATA), $16
|
||||||
|
|
||||||
|
DATA counter<>+0x00(SB)/8, $0x40
|
||||||
|
DATA counter<>+0x08(SB)/8, $0x0
|
||||||
|
GLOBL counter<>(SB), (NOPTR+RODATA), $16
|
||||||
|
|
||||||
|
#define ROTL_SSE2(n, t, v) \
|
||||||
|
MOVO v, t; \
|
||||||
|
PSLLL $n, t; \
|
||||||
|
PSRLL $(32-n), v; \
|
||||||
|
PXOR t, v
|
||||||
|
|
||||||
|
#define ROTL_SSSE3(c, v) \
|
||||||
|
PSHUFB c, v
|
||||||
|
|
||||||
|
#define ROUND_SSE2(v0, v1, v2, v3, m0, m1, m2, m3, t) \
|
||||||
|
PADDL m0, v0; \
|
||||||
|
PADDL v1, v0; \
|
||||||
|
PXOR v0, v3; \
|
||||||
|
ROTL_SSE2(16, t, v3); \
|
||||||
|
PADDL v3, v2; \
|
||||||
|
PXOR v2, v1; \
|
||||||
|
ROTL_SSE2(20, t, v1); \
|
||||||
|
PADDL m1, v0; \
|
||||||
|
PADDL v1, v0; \
|
||||||
|
PXOR v0, v3; \
|
||||||
|
ROTL_SSE2(24, t, v3); \
|
||||||
|
PADDL v3, v2; \
|
||||||
|
PXOR v2, v1; \
|
||||||
|
ROTL_SSE2(25, t, v1); \
|
||||||
|
PSHUFL $0x39, v1, v1; \
|
||||||
|
PSHUFL $0x4E, v2, v2; \
|
||||||
|
PSHUFL $0x93, v3, v3; \
|
||||||
|
PADDL m2, v0; \
|
||||||
|
PADDL v1, v0; \
|
||||||
|
PXOR v0, v3; \
|
||||||
|
ROTL_SSE2(16, t, v3); \
|
||||||
|
PADDL v3, v2; \
|
||||||
|
PXOR v2, v1; \
|
||||||
|
ROTL_SSE2(20, t, v1); \
|
||||||
|
PADDL m3, v0; \
|
||||||
|
PADDL v1, v0; \
|
||||||
|
PXOR v0, v3; \
|
||||||
|
ROTL_SSE2(24, t, v3); \
|
||||||
|
PADDL v3, v2; \
|
||||||
|
PXOR v2, v1; \
|
||||||
|
ROTL_SSE2(25, t, v1); \
|
||||||
|
PSHUFL $0x39, v3, v3; \
|
||||||
|
PSHUFL $0x4E, v2, v2; \
|
||||||
|
PSHUFL $0x93, v1, v1
|
||||||
|
|
||||||
|
#define ROUND_SSSE3(v0, v1, v2, v3, m0, m1, m2, m3, t, c16, c8) \
|
||||||
|
PADDL m0, v0; \
|
||||||
|
PADDL v1, v0; \
|
||||||
|
PXOR v0, v3; \
|
||||||
|
ROTL_SSSE3(c16, v3); \
|
||||||
|
PADDL v3, v2; \
|
||||||
|
PXOR v2, v1; \
|
||||||
|
ROTL_SSE2(20, t, v1); \
|
||||||
|
PADDL m1, v0; \
|
||||||
|
PADDL v1, v0; \
|
||||||
|
PXOR v0, v3; \
|
||||||
|
ROTL_SSSE3(c8, v3); \
|
||||||
|
PADDL v3, v2; \
|
||||||
|
PXOR v2, v1; \
|
||||||
|
ROTL_SSE2(25, t, v1); \
|
||||||
|
PSHUFL $0x39, v1, v1; \
|
||||||
|
PSHUFL $0x4E, v2, v2; \
|
||||||
|
PSHUFL $0x93, v3, v3; \
|
||||||
|
PADDL m2, v0; \
|
||||||
|
PADDL v1, v0; \
|
||||||
|
PXOR v0, v3; \
|
||||||
|
ROTL_SSSE3(c16, v3); \
|
||||||
|
PADDL v3, v2; \
|
||||||
|
PXOR v2, v1; \
|
||||||
|
ROTL_SSE2(20, t, v1); \
|
||||||
|
PADDL m3, v0; \
|
||||||
|
PADDL v1, v0; \
|
||||||
|
PXOR v0, v3; \
|
||||||
|
ROTL_SSSE3(c8, v3); \
|
||||||
|
PADDL v3, v2; \
|
||||||
|
PXOR v2, v1; \
|
||||||
|
ROTL_SSE2(25, t, v1); \
|
||||||
|
PSHUFL $0x39, v3, v3; \
|
||||||
|
PSHUFL $0x4E, v2, v2; \
|
||||||
|
PSHUFL $0x93, v1, v1
|
||||||
|
|
||||||
|
|
||||||
|
#define LOAD_MSG_SSE4(m0, m1, m2, m3, src, i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15) \
|
||||||
|
MOVL i0*4(src), m0; \
|
||||||
|
PINSRD $1, i1*4(src), m0; \
|
||||||
|
PINSRD $2, i2*4(src), m0; \
|
||||||
|
PINSRD $3, i3*4(src), m0; \
|
||||||
|
MOVL i4*4(src), m1; \
|
||||||
|
PINSRD $1, i5*4(src), m1; \
|
||||||
|
PINSRD $2, i6*4(src), m1; \
|
||||||
|
PINSRD $3, i7*4(src), m1; \
|
||||||
|
MOVL i8*4(src), m2; \
|
||||||
|
PINSRD $1, i9*4(src), m2; \
|
||||||
|
PINSRD $2, i10*4(src), m2; \
|
||||||
|
PINSRD $3, i11*4(src), m2; \
|
||||||
|
MOVL i12*4(src), m3; \
|
||||||
|
PINSRD $1, i13*4(src), m3; \
|
||||||
|
PINSRD $2, i14*4(src), m3; \
|
||||||
|
PINSRD $3, i15*4(src), m3
|
||||||
|
|
||||||
|
#define PRECOMPUTE_MSG(dst, off, src, R8, R9, R10, R11, R12, R13, R14, R15) \
|
||||||
|
MOVQ 0*4(src), R8; \
|
||||||
|
MOVQ 2*4(src), R9; \
|
||||||
|
MOVQ 4*4(src), R10; \
|
||||||
|
MOVQ 6*4(src), R11; \
|
||||||
|
MOVQ 8*4(src), R12; \
|
||||||
|
MOVQ 10*4(src), R13; \
|
||||||
|
MOVQ 12*4(src), R14; \
|
||||||
|
MOVQ 14*4(src), R15; \
|
||||||
|
\
|
||||||
|
MOVL R8, 0*4+off+0(dst); \
|
||||||
|
MOVL R8, 9*4+off+64(dst); \
|
||||||
|
MOVL R8, 5*4+off+128(dst); \
|
||||||
|
MOVL R8, 14*4+off+192(dst); \
|
||||||
|
MOVL R8, 4*4+off+256(dst); \
|
||||||
|
MOVL R8, 2*4+off+320(dst); \
|
||||||
|
MOVL R8, 8*4+off+384(dst); \
|
||||||
|
MOVL R8, 12*4+off+448(dst); \
|
||||||
|
MOVL R8, 3*4+off+512(dst); \
|
||||||
|
MOVL R8, 15*4+off+576(dst); \
|
||||||
|
SHRQ $32, R8; \
|
||||||
|
MOVL R8, 4*4+off+0(dst); \
|
||||||
|
MOVL R8, 8*4+off+64(dst); \
|
||||||
|
MOVL R8, 14*4+off+128(dst); \
|
||||||
|
MOVL R8, 5*4+off+192(dst); \
|
||||||
|
MOVL R8, 12*4+off+256(dst); \
|
||||||
|
MOVL R8, 11*4+off+320(dst); \
|
||||||
|
MOVL R8, 1*4+off+384(dst); \
|
||||||
|
MOVL R8, 6*4+off+448(dst); \
|
||||||
|
MOVL R8, 10*4+off+512(dst); \
|
||||||
|
MOVL R8, 3*4+off+576(dst); \
|
||||||
|
\
|
||||||
|
MOVL R9, 1*4+off+0(dst); \
|
||||||
|
MOVL R9, 13*4+off+64(dst); \
|
||||||
|
MOVL R9, 6*4+off+128(dst); \
|
||||||
|
MOVL R9, 8*4+off+192(dst); \
|
||||||
|
MOVL R9, 2*4+off+256(dst); \
|
||||||
|
MOVL R9, 0*4+off+320(dst); \
|
||||||
|
MOVL R9, 14*4+off+384(dst); \
|
||||||
|
MOVL R9, 11*4+off+448(dst); \
|
||||||
|
MOVL R9, 12*4+off+512(dst); \
|
||||||
|
MOVL R9, 4*4+off+576(dst); \
|
||||||
|
SHRQ $32, R9; \
|
||||||
|
MOVL R9, 5*4+off+0(dst); \
|
||||||
|
MOVL R9, 15*4+off+64(dst); \
|
||||||
|
MOVL R9, 9*4+off+128(dst); \
|
||||||
|
MOVL R9, 1*4+off+192(dst); \
|
||||||
|
MOVL R9, 11*4+off+256(dst); \
|
||||||
|
MOVL R9, 7*4+off+320(dst); \
|
||||||
|
MOVL R9, 13*4+off+384(dst); \
|
||||||
|
MOVL R9, 3*4+off+448(dst); \
|
||||||
|
MOVL R9, 6*4+off+512(dst); \
|
||||||
|
MOVL R9, 10*4+off+576(dst); \
|
||||||
|
\
|
||||||
|
MOVL R10, 2*4+off+0(dst); \
|
||||||
|
MOVL R10, 1*4+off+64(dst); \
|
||||||
|
MOVL R10, 15*4+off+128(dst); \
|
||||||
|
MOVL R10, 10*4+off+192(dst); \
|
||||||
|
MOVL R10, 6*4+off+256(dst); \
|
||||||
|
MOVL R10, 8*4+off+320(dst); \
|
||||||
|
MOVL R10, 3*4+off+384(dst); \
|
||||||
|
MOVL R10, 13*4+off+448(dst); \
|
||||||
|
MOVL R10, 14*4+off+512(dst); \
|
||||||
|
MOVL R10, 5*4+off+576(dst); \
|
||||||
|
SHRQ $32, R10; \
|
||||||
|
MOVL R10, 6*4+off+0(dst); \
|
||||||
|
MOVL R10, 11*4+off+64(dst); \
|
||||||
|
MOVL R10, 2*4+off+128(dst); \
|
||||||
|
MOVL R10, 9*4+off+192(dst); \
|
||||||
|
MOVL R10, 1*4+off+256(dst); \
|
||||||
|
MOVL R10, 13*4+off+320(dst); \
|
||||||
|
MOVL R10, 4*4+off+384(dst); \
|
||||||
|
MOVL R10, 8*4+off+448(dst); \
|
||||||
|
MOVL R10, 15*4+off+512(dst); \
|
||||||
|
MOVL R10, 7*4+off+576(dst); \
|
||||||
|
\
|
||||||
|
MOVL R11, 3*4+off+0(dst); \
|
||||||
|
MOVL R11, 7*4+off+64(dst); \
|
||||||
|
MOVL R11, 13*4+off+128(dst); \
|
||||||
|
MOVL R11, 12*4+off+192(dst); \
|
||||||
|
MOVL R11, 10*4+off+256(dst); \
|
||||||
|
MOVL R11, 1*4+off+320(dst); \
|
||||||
|
MOVL R11, 9*4+off+384(dst); \
|
||||||
|
MOVL R11, 14*4+off+448(dst); \
|
||||||
|
MOVL R11, 0*4+off+512(dst); \
|
||||||
|
MOVL R11, 6*4+off+576(dst); \
|
||||||
|
SHRQ $32, R11; \
|
||||||
|
MOVL R11, 7*4+off+0(dst); \
|
||||||
|
MOVL R11, 14*4+off+64(dst); \
|
||||||
|
MOVL R11, 10*4+off+128(dst); \
|
||||||
|
MOVL R11, 0*4+off+192(dst); \
|
||||||
|
MOVL R11, 5*4+off+256(dst); \
|
||||||
|
MOVL R11, 9*4+off+320(dst); \
|
||||||
|
MOVL R11, 12*4+off+384(dst); \
|
||||||
|
MOVL R11, 1*4+off+448(dst); \
|
||||||
|
MOVL R11, 13*4+off+512(dst); \
|
||||||
|
MOVL R11, 2*4+off+576(dst); \
|
||||||
|
\
|
||||||
|
MOVL R12, 8*4+off+0(dst); \
|
||||||
|
MOVL R12, 5*4+off+64(dst); \
|
||||||
|
MOVL R12, 4*4+off+128(dst); \
|
||||||
|
MOVL R12, 15*4+off+192(dst); \
|
||||||
|
MOVL R12, 14*4+off+256(dst); \
|
||||||
|
MOVL R12, 3*4+off+320(dst); \
|
||||||
|
MOVL R12, 11*4+off+384(dst); \
|
||||||
|
MOVL R12, 10*4+off+448(dst); \
|
||||||
|
MOVL R12, 7*4+off+512(dst); \
|
||||||
|
MOVL R12, 1*4+off+576(dst); \
|
||||||
|
SHRQ $32, R12; \
|
||||||
|
MOVL R12, 12*4+off+0(dst); \
|
||||||
|
MOVL R12, 2*4+off+64(dst); \
|
||||||
|
MOVL R12, 11*4+off+128(dst); \
|
||||||
|
MOVL R12, 4*4+off+192(dst); \
|
||||||
|
MOVL R12, 0*4+off+256(dst); \
|
||||||
|
MOVL R12, 15*4+off+320(dst); \
|
||||||
|
MOVL R12, 10*4+off+384(dst); \
|
||||||
|
MOVL R12, 7*4+off+448(dst); \
|
||||||
|
MOVL R12, 5*4+off+512(dst); \
|
||||||
|
MOVL R12, 9*4+off+576(dst); \
|
||||||
|
\
|
||||||
|
MOVL R13, 9*4+off+0(dst); \
|
||||||
|
MOVL R13, 4*4+off+64(dst); \
|
||||||
|
MOVL R13, 8*4+off+128(dst); \
|
||||||
|
MOVL R13, 13*4+off+192(dst); \
|
||||||
|
MOVL R13, 3*4+off+256(dst); \
|
||||||
|
MOVL R13, 5*4+off+320(dst); \
|
||||||
|
MOVL R13, 7*4+off+384(dst); \
|
||||||
|
MOVL R13, 15*4+off+448(dst); \
|
||||||
|
MOVL R13, 11*4+off+512(dst); \
|
||||||
|
MOVL R13, 0*4+off+576(dst); \
|
||||||
|
SHRQ $32, R13; \
|
||||||
|
MOVL R13, 13*4+off+0(dst); \
|
||||||
|
MOVL R13, 10*4+off+64(dst); \
|
||||||
|
MOVL R13, 0*4+off+128(dst); \
|
||||||
|
MOVL R13, 3*4+off+192(dst); \
|
||||||
|
MOVL R13, 9*4+off+256(dst); \
|
||||||
|
MOVL R13, 6*4+off+320(dst); \
|
||||||
|
MOVL R13, 15*4+off+384(dst); \
|
||||||
|
MOVL R13, 4*4+off+448(dst); \
|
||||||
|
MOVL R13, 2*4+off+512(dst); \
|
||||||
|
MOVL R13, 12*4+off+576(dst); \
|
||||||
|
\
|
||||||
|
MOVL R14, 10*4+off+0(dst); \
|
||||||
|
MOVL R14, 12*4+off+64(dst); \
|
||||||
|
MOVL R14, 1*4+off+128(dst); \
|
||||||
|
MOVL R14, 6*4+off+192(dst); \
|
||||||
|
MOVL R14, 13*4+off+256(dst); \
|
||||||
|
MOVL R14, 4*4+off+320(dst); \
|
||||||
|
MOVL R14, 0*4+off+384(dst); \
|
||||||
|
MOVL R14, 2*4+off+448(dst); \
|
||||||
|
MOVL R14, 8*4+off+512(dst); \
|
||||||
|
MOVL R14, 14*4+off+576(dst); \
|
||||||
|
SHRQ $32, R14; \
|
||||||
|
MOVL R14, 14*4+off+0(dst); \
|
||||||
|
MOVL R14, 3*4+off+64(dst); \
|
||||||
|
MOVL R14, 7*4+off+128(dst); \
|
||||||
|
MOVL R14, 2*4+off+192(dst); \
|
||||||
|
MOVL R14, 15*4+off+256(dst); \
|
||||||
|
MOVL R14, 12*4+off+320(dst); \
|
||||||
|
MOVL R14, 6*4+off+384(dst); \
|
||||||
|
MOVL R14, 0*4+off+448(dst); \
|
||||||
|
MOVL R14, 9*4+off+512(dst); \
|
||||||
|
MOVL R14, 11*4+off+576(dst); \
|
||||||
|
\
|
||||||
|
MOVL R15, 11*4+off+0(dst); \
|
||||||
|
MOVL R15, 0*4+off+64(dst); \
|
||||||
|
MOVL R15, 12*4+off+128(dst); \
|
||||||
|
MOVL R15, 7*4+off+192(dst); \
|
||||||
|
MOVL R15, 8*4+off+256(dst); \
|
||||||
|
MOVL R15, 14*4+off+320(dst); \
|
||||||
|
MOVL R15, 2*4+off+384(dst); \
|
||||||
|
MOVL R15, 5*4+off+448(dst); \
|
||||||
|
MOVL R15, 1*4+off+512(dst); \
|
||||||
|
MOVL R15, 13*4+off+576(dst); \
|
||||||
|
SHRQ $32, R15; \
|
||||||
|
MOVL R15, 15*4+off+0(dst); \
|
||||||
|
MOVL R15, 6*4+off+64(dst); \
|
||||||
|
MOVL R15, 3*4+off+128(dst); \
|
||||||
|
MOVL R15, 11*4+off+192(dst); \
|
||||||
|
MOVL R15, 7*4+off+256(dst); \
|
||||||
|
MOVL R15, 10*4+off+320(dst); \
|
||||||
|
MOVL R15, 5*4+off+384(dst); \
|
||||||
|
MOVL R15, 9*4+off+448(dst); \
|
||||||
|
MOVL R15, 4*4+off+512(dst); \
|
||||||
|
MOVL R15, 8*4+off+576(dst)
|
||||||
|
|
||||||
|
#define BLAKE2s_SSE2() \
|
||||||
|
PRECOMPUTE_MSG(SP, 16, SI, R8, R9, R10, R11, R12, R13, R14, R15); \
|
||||||
|
ROUND_SSE2(X4, X5, X6, X7, 16(SP), 32(SP), 48(SP), 64(SP), X8); \
|
||||||
|
ROUND_SSE2(X4, X5, X6, X7, 16+64(SP), 32+64(SP), 48+64(SP), 64+64(SP), X8); \
|
||||||
|
ROUND_SSE2(X4, X5, X6, X7, 16+128(SP), 32+128(SP), 48+128(SP), 64+128(SP), X8); \
|
||||||
|
ROUND_SSE2(X4, X5, X6, X7, 16+192(SP), 32+192(SP), 48+192(SP), 64+192(SP), X8); \
|
||||||
|
ROUND_SSE2(X4, X5, X6, X7, 16+256(SP), 32+256(SP), 48+256(SP), 64+256(SP), X8); \
|
||||||
|
ROUND_SSE2(X4, X5, X6, X7, 16+320(SP), 32+320(SP), 48+320(SP), 64+320(SP), X8); \
|
||||||
|
ROUND_SSE2(X4, X5, X6, X7, 16+384(SP), 32+384(SP), 48+384(SP), 64+384(SP), X8); \
|
||||||
|
ROUND_SSE2(X4, X5, X6, X7, 16+448(SP), 32+448(SP), 48+448(SP), 64+448(SP), X8); \
|
||||||
|
ROUND_SSE2(X4, X5, X6, X7, 16+512(SP), 32+512(SP), 48+512(SP), 64+512(SP), X8); \
|
||||||
|
ROUND_SSE2(X4, X5, X6, X7, 16+576(SP), 32+576(SP), 48+576(SP), 64+576(SP), X8)
|
||||||
|
|
||||||
|
#define BLAKE2s_SSSE3() \
|
||||||
|
PRECOMPUTE_MSG(SP, 16, SI, R8, R9, R10, R11, R12, R13, R14, R15); \
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, 16(SP), 32(SP), 48(SP), 64(SP), X8, X13, X14); \
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, 16+64(SP), 32+64(SP), 48+64(SP), 64+64(SP), X8, X13, X14); \
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, 16+128(SP), 32+128(SP), 48+128(SP), 64+128(SP), X8, X13, X14); \
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, 16+192(SP), 32+192(SP), 48+192(SP), 64+192(SP), X8, X13, X14); \
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, 16+256(SP), 32+256(SP), 48+256(SP), 64+256(SP), X8, X13, X14); \
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, 16+320(SP), 32+320(SP), 48+320(SP), 64+320(SP), X8, X13, X14); \
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, 16+384(SP), 32+384(SP), 48+384(SP), 64+384(SP), X8, X13, X14); \
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, 16+448(SP), 32+448(SP), 48+448(SP), 64+448(SP), X8, X13, X14); \
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, 16+512(SP), 32+512(SP), 48+512(SP), 64+512(SP), X8, X13, X14); \
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, 16+576(SP), 32+576(SP), 48+576(SP), 64+576(SP), X8, X13, X14)
|
||||||
|
|
||||||
|
#define BLAKE2s_SSE4() \
|
||||||
|
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 0, 2, 4, 6, 1, 3, 5, 7, 8, 10, 12, 14, 9, 11, 13, 15); \
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14); \
|
||||||
|
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 14, 4, 9, 13, 10, 8, 15, 6, 1, 0, 11, 5, 12, 2, 7, 3); \
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14); \
|
||||||
|
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 11, 12, 5, 15, 8, 0, 2, 13, 10, 3, 7, 9, 14, 6, 1, 4); \
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14); \
|
||||||
|
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 7, 3, 13, 11, 9, 1, 12, 14, 2, 5, 4, 15, 6, 10, 0, 8); \
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14); \
|
||||||
|
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 9, 5, 2, 10, 0, 7, 4, 15, 14, 11, 6, 3, 1, 12, 8, 13); \
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14); \
|
||||||
|
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 2, 6, 0, 8, 12, 10, 11, 3, 4, 7, 15, 1, 13, 5, 14, 9); \
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14); \
|
||||||
|
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 12, 1, 14, 4, 5, 15, 13, 10, 0, 6, 9, 8, 7, 3, 2, 11); \
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14); \
|
||||||
|
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 13, 7, 12, 3, 11, 14, 1, 9, 5, 15, 8, 2, 0, 4, 6, 10); \
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14); \
|
||||||
|
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 6, 14, 11, 0, 15, 9, 3, 8, 12, 13, 1, 10, 2, 7, 4, 5); \
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14); \
|
||||||
|
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 10, 8, 7, 1, 2, 4, 6, 5, 15, 9, 3, 13, 11, 14, 12, 0); \
|
||||||
|
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14)
|
||||||
|
|
||||||
|
#define HASH_BLOCKS(h, c, flag, blocks_base, blocks_len, BLAKE2s_FUNC) \
|
||||||
|
MOVQ h, AX; \
|
||||||
|
MOVQ c, BX; \
|
||||||
|
MOVL flag, CX; \
|
||||||
|
MOVQ blocks_base, SI; \
|
||||||
|
MOVQ blocks_len, DX; \
|
||||||
|
\
|
||||||
|
MOVQ SP, BP; \
|
||||||
|
MOVQ SP, R9; \
|
||||||
|
ADDQ $15, R9; \
|
||||||
|
ANDQ $~15, R9; \
|
||||||
|
MOVQ R9, SP; \
|
||||||
|
\
|
||||||
|
MOVQ 0(BX), R9; \
|
||||||
|
MOVQ R9, 0(SP); \
|
||||||
|
XORQ R9, R9; \
|
||||||
|
MOVQ R9, 8(SP); \
|
||||||
|
MOVL CX, 8(SP); \
|
||||||
|
\
|
||||||
|
MOVOU 0(AX), X0; \
|
||||||
|
MOVOU 16(AX), X1; \
|
||||||
|
MOVOU iv0<>(SB), X2; \
|
||||||
|
MOVOU iv1<>(SB), X3 \
|
||||||
|
\
|
||||||
|
MOVOU counter<>(SB), X12; \
|
||||||
|
MOVOU rol16<>(SB), X13; \
|
||||||
|
MOVOU rol8<>(SB), X14; \
|
||||||
|
MOVO 0(SP), X15; \
|
||||||
|
\
|
||||||
|
loop: \
|
||||||
|
MOVO X0, X4; \
|
||||||
|
MOVO X1, X5; \
|
||||||
|
MOVO X2, X6; \
|
||||||
|
MOVO X3, X7; \
|
||||||
|
\
|
||||||
|
PADDQ X12, X15; \
|
||||||
|
PXOR X15, X7; \
|
||||||
|
\
|
||||||
|
BLAKE2s_FUNC(); \
|
||||||
|
\
|
||||||
|
PXOR X4, X0; \
|
||||||
|
PXOR X5, X1; \
|
||||||
|
PXOR X6, X0; \
|
||||||
|
PXOR X7, X1; \
|
||||||
|
\
|
||||||
|
LEAQ 64(SI), SI; \
|
||||||
|
SUBQ $64, DX; \
|
||||||
|
JNE loop; \
|
||||||
|
\
|
||||||
|
MOVO X15, 0(SP); \
|
||||||
|
MOVQ 0(SP), R9; \
|
||||||
|
MOVQ R9, 0(BX); \
|
||||||
|
\
|
||||||
|
MOVOU X0, 0(AX); \
|
||||||
|
MOVOU X1, 16(AX); \
|
||||||
|
\
|
||||||
|
MOVQ BP, SP
|
||||||
|
|
||||||
|
// func hashBlocksSSE2(h *[8]uint32, c *[2]uint32, flag uint32, blocks []byte)
|
||||||
|
TEXT ·hashBlocksSSE2(SB), 0, $672-48 // frame = 656 + 16 byte alignment
|
||||||
|
HASH_BLOCKS(h+0(FP), c+8(FP), flag+16(FP), blocks_base+24(FP), blocks_len+32(FP), BLAKE2s_SSE2)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func hashBlocksSSSE3(h *[8]uint32, c *[2]uint32, flag uint32, blocks []byte)
|
||||||
|
TEXT ·hashBlocksSSSE3(SB), 0, $672-48 // frame = 656 + 16 byte alignment
|
||||||
|
HASH_BLOCKS(h+0(FP), c+8(FP), flag+16(FP), blocks_base+24(FP), blocks_len+32(FP), BLAKE2s_SSSE3)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func hashBlocksSSE4(h *[8]uint32, c *[2]uint32, flag uint32, blocks []byte)
|
||||||
|
TEXT ·hashBlocksSSE4(SB), 0, $32-48 // frame = 16 + 16 byte alignment
|
||||||
|
HASH_BLOCKS(h+0(FP), c+8(FP), flag+16(FP), blocks_base+24(FP), blocks_len+32(FP), BLAKE2s_SSE4)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func supportSSE4() bool
|
||||||
|
TEXT ·supportSSE4(SB), 4, $0-1
|
||||||
|
MOVL $1, AX
|
||||||
|
CPUID
|
||||||
|
SHRL $19, CX // Bit 19 indicates SSE4.1.
|
||||||
|
ANDL $1, CX
|
||||||
|
MOVB CX, ret+0(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func supportSSSE3() bool
|
||||||
|
TEXT ·supportSSSE3(SB), 4, $0-1
|
||||||
|
MOVL $1, AX
|
||||||
|
CPUID
|
||||||
|
MOVL CX, BX
|
||||||
|
ANDL $0x1, BX // Bit zero indicates SSE3 support.
|
||||||
|
JZ FALSE
|
||||||
|
ANDL $0x200, CX // Bit nine indicates SSSE3 support.
|
||||||
|
JZ FALSE
|
||||||
|
MOVB $1, ret+0(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
FALSE:
|
||||||
|
MOVB $0, ret+0(FP)
|
||||||
|
RET
|
||||||
+174
@@ -0,0 +1,174 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package blake2s
|
||||||
|
|
||||||
|
// the precomputed values for BLAKE2s
|
||||||
|
// there are 10 16-byte arrays - one for each round
|
||||||
|
// the entries are calculated from the sigma constants.
|
||||||
|
var precomputed = [10][16]byte{
|
||||||
|
{0, 2, 4, 6, 1, 3, 5, 7, 8, 10, 12, 14, 9, 11, 13, 15},
|
||||||
|
{14, 4, 9, 13, 10, 8, 15, 6, 1, 0, 11, 5, 12, 2, 7, 3},
|
||||||
|
{11, 12, 5, 15, 8, 0, 2, 13, 10, 3, 7, 9, 14, 6, 1, 4},
|
||||||
|
{7, 3, 13, 11, 9, 1, 12, 14, 2, 5, 4, 15, 6, 10, 0, 8},
|
||||||
|
{9, 5, 2, 10, 0, 7, 4, 15, 14, 11, 6, 3, 1, 12, 8, 13},
|
||||||
|
{2, 6, 0, 8, 12, 10, 11, 3, 4, 7, 15, 1, 13, 5, 14, 9},
|
||||||
|
{12, 1, 14, 4, 5, 15, 13, 10, 0, 6, 9, 8, 7, 3, 2, 11},
|
||||||
|
{13, 7, 12, 3, 11, 14, 1, 9, 5, 15, 8, 2, 0, 4, 6, 10},
|
||||||
|
{6, 14, 11, 0, 15, 9, 3, 8, 12, 13, 1, 10, 2, 7, 4, 5},
|
||||||
|
{10, 8, 7, 1, 2, 4, 6, 5, 15, 9, 3, 13, 11, 14, 12, 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
func hashBlocksGeneric(h *[8]uint32, c *[2]uint32, flag uint32, blocks []byte) {
|
||||||
|
var m [16]uint32
|
||||||
|
c0, c1 := c[0], c[1]
|
||||||
|
|
||||||
|
for i := 0; i < len(blocks); {
|
||||||
|
c0 += BlockSize
|
||||||
|
if c0 < BlockSize {
|
||||||
|
c1++
|
||||||
|
}
|
||||||
|
|
||||||
|
v0, v1, v2, v3, v4, v5, v6, v7 := h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7]
|
||||||
|
v8, v9, v10, v11, v12, v13, v14, v15 := iv[0], iv[1], iv[2], iv[3], iv[4], iv[5], iv[6], iv[7]
|
||||||
|
v12 ^= c0
|
||||||
|
v13 ^= c1
|
||||||
|
v14 ^= flag
|
||||||
|
|
||||||
|
for j := range m {
|
||||||
|
m[j] = uint32(blocks[i]) | uint32(blocks[i+1])<<8 | uint32(blocks[i+2])<<16 | uint32(blocks[i+3])<<24
|
||||||
|
i += 4
|
||||||
|
}
|
||||||
|
|
||||||
|
for k := range precomputed {
|
||||||
|
s := &(precomputed[k])
|
||||||
|
|
||||||
|
v0 += m[s[0]]
|
||||||
|
v0 += v4
|
||||||
|
v12 ^= v0
|
||||||
|
v12 = v12<<(32-16) | v12>>16
|
||||||
|
v8 += v12
|
||||||
|
v4 ^= v8
|
||||||
|
v4 = v4<<(32-12) | v4>>12
|
||||||
|
v1 += m[s[1]]
|
||||||
|
v1 += v5
|
||||||
|
v13 ^= v1
|
||||||
|
v13 = v13<<(32-16) | v13>>16
|
||||||
|
v9 += v13
|
||||||
|
v5 ^= v9
|
||||||
|
v5 = v5<<(32-12) | v5>>12
|
||||||
|
v2 += m[s[2]]
|
||||||
|
v2 += v6
|
||||||
|
v14 ^= v2
|
||||||
|
v14 = v14<<(32-16) | v14>>16
|
||||||
|
v10 += v14
|
||||||
|
v6 ^= v10
|
||||||
|
v6 = v6<<(32-12) | v6>>12
|
||||||
|
v3 += m[s[3]]
|
||||||
|
v3 += v7
|
||||||
|
v15 ^= v3
|
||||||
|
v15 = v15<<(32-16) | v15>>16
|
||||||
|
v11 += v15
|
||||||
|
v7 ^= v11
|
||||||
|
v7 = v7<<(32-12) | v7>>12
|
||||||
|
|
||||||
|
v0 += m[s[4]]
|
||||||
|
v0 += v4
|
||||||
|
v12 ^= v0
|
||||||
|
v12 = v12<<(32-8) | v12>>8
|
||||||
|
v8 += v12
|
||||||
|
v4 ^= v8
|
||||||
|
v4 = v4<<(32-7) | v4>>7
|
||||||
|
v1 += m[s[5]]
|
||||||
|
v1 += v5
|
||||||
|
v13 ^= v1
|
||||||
|
v13 = v13<<(32-8) | v13>>8
|
||||||
|
v9 += v13
|
||||||
|
v5 ^= v9
|
||||||
|
v5 = v5<<(32-7) | v5>>7
|
||||||
|
v2 += m[s[6]]
|
||||||
|
v2 += v6
|
||||||
|
v14 ^= v2
|
||||||
|
v14 = v14<<(32-8) | v14>>8
|
||||||
|
v10 += v14
|
||||||
|
v6 ^= v10
|
||||||
|
v6 = v6<<(32-7) | v6>>7
|
||||||
|
v3 += m[s[7]]
|
||||||
|
v3 += v7
|
||||||
|
v15 ^= v3
|
||||||
|
v15 = v15<<(32-8) | v15>>8
|
||||||
|
v11 += v15
|
||||||
|
v7 ^= v11
|
||||||
|
v7 = v7<<(32-7) | v7>>7
|
||||||
|
|
||||||
|
v0 += m[s[8]]
|
||||||
|
v0 += v5
|
||||||
|
v15 ^= v0
|
||||||
|
v15 = v15<<(32-16) | v15>>16
|
||||||
|
v10 += v15
|
||||||
|
v5 ^= v10
|
||||||
|
v5 = v5<<(32-12) | v5>>12
|
||||||
|
v1 += m[s[9]]
|
||||||
|
v1 += v6
|
||||||
|
v12 ^= v1
|
||||||
|
v12 = v12<<(32-16) | v12>>16
|
||||||
|
v11 += v12
|
||||||
|
v6 ^= v11
|
||||||
|
v6 = v6<<(32-12) | v6>>12
|
||||||
|
v2 += m[s[10]]
|
||||||
|
v2 += v7
|
||||||
|
v13 ^= v2
|
||||||
|
v13 = v13<<(32-16) | v13>>16
|
||||||
|
v8 += v13
|
||||||
|
v7 ^= v8
|
||||||
|
v7 = v7<<(32-12) | v7>>12
|
||||||
|
v3 += m[s[11]]
|
||||||
|
v3 += v4
|
||||||
|
v14 ^= v3
|
||||||
|
v14 = v14<<(32-16) | v14>>16
|
||||||
|
v9 += v14
|
||||||
|
v4 ^= v9
|
||||||
|
v4 = v4<<(32-12) | v4>>12
|
||||||
|
|
||||||
|
v0 += m[s[12]]
|
||||||
|
v0 += v5
|
||||||
|
v15 ^= v0
|
||||||
|
v15 = v15<<(32-8) | v15>>8
|
||||||
|
v10 += v15
|
||||||
|
v5 ^= v10
|
||||||
|
v5 = v5<<(32-7) | v5>>7
|
||||||
|
v1 += m[s[13]]
|
||||||
|
v1 += v6
|
||||||
|
v12 ^= v1
|
||||||
|
v12 = v12<<(32-8) | v12>>8
|
||||||
|
v11 += v12
|
||||||
|
v6 ^= v11
|
||||||
|
v6 = v6<<(32-7) | v6>>7
|
||||||
|
v2 += m[s[14]]
|
||||||
|
v2 += v7
|
||||||
|
v13 ^= v2
|
||||||
|
v13 = v13<<(32-8) | v13>>8
|
||||||
|
v8 += v13
|
||||||
|
v7 ^= v8
|
||||||
|
v7 = v7<<(32-7) | v7>>7
|
||||||
|
v3 += m[s[15]]
|
||||||
|
v3 += v4
|
||||||
|
v14 ^= v3
|
||||||
|
v14 = v14<<(32-8) | v14>>8
|
||||||
|
v9 += v14
|
||||||
|
v4 ^= v9
|
||||||
|
v4 = v4<<(32-7) | v4>>7
|
||||||
|
}
|
||||||
|
|
||||||
|
h[0] ^= v0 ^ v8
|
||||||
|
h[1] ^= v1 ^ v9
|
||||||
|
h[2] ^= v2 ^ v10
|
||||||
|
h[3] ^= v3 ^ v11
|
||||||
|
h[4] ^= v4 ^ v12
|
||||||
|
h[5] ^= v5 ^ v13
|
||||||
|
h[6] ^= v6 ^ v14
|
||||||
|
h[7] ^= v7 ^ v15
|
||||||
|
}
|
||||||
|
c[0], c[1] = c0, c1
|
||||||
|
}
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !amd64,!386 gccgo appengine
|
||||||
|
|
||||||
|
package blake2s
|
||||||
|
|
||||||
|
var (
|
||||||
|
useSSE4 = false
|
||||||
|
useSSSE3 = false
|
||||||
|
useSSE2 = false
|
||||||
|
)
|
||||||
|
|
||||||
|
func hashBlocks(h *[8]uint32, c *[2]uint32, flag uint32, blocks []byte) {
|
||||||
|
hashBlocksGeneric(h, c, flag, blocks)
|
||||||
|
}
|
||||||
+1002
File diff suppressed because it is too large.
Load diff
+178
@@ -0,0 +1,178 @@
|
|||||||
|
// Copyright 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package blake2s
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// XOF defines the interface to hash functions that
|
||||||
|
// support arbitrary-length output.
|
||||||
|
type XOF interface {
|
||||||
|
// Write absorbs more data into the hash's state. It panics if called
|
||||||
|
// after Read.
|
||||||
|
io.Writer
|
||||||
|
|
||||||
|
// Read reads more output from the hash. It returns io.EOF if the limit
|
||||||
|
// has been reached.
|
||||||
|
io.Reader
|
||||||
|
|
||||||
|
// Clone returns a copy of the XOF in its current state.
|
||||||
|
Clone() XOF
|
||||||
|
|
||||||
|
// Reset resets the XOF to its initial state.
|
||||||
|
Reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
// OutputLengthUnknown can be used as the size argument to NewXOF to indicate
|
||||||
|
// the the length of the output is not known in advance.
|
||||||
|
const OutputLengthUnknown = 0
|
||||||
|
|
||||||
|
// magicUnknownOutputLength is a magic value for the output size that indicates
|
||||||
|
// an unknown number of output bytes.
|
||||||
|
const magicUnknownOutputLength = 65535
|
||||||
|
|
||||||
|
// maxOutputLength is the absolute maximum number of bytes to produce when the
|
||||||
|
// number of output bytes is unknown.
|
||||||
|
const maxOutputLength = (1 << 32) * 32
|
||||||
|
|
||||||
|
// NewXOF creates a new variable-output-length hash. The hash either produce a
|
||||||
|
// known number of bytes (1 <= size < 65535), or an unknown number of bytes
|
||||||
|
// (size == OutputLengthUnknown). In the latter case, an absolute limit of
|
||||||
|
// 128GiB applies.
|
||||||
|
//
|
||||||
|
// A non-nil key turns the hash into a MAC. The key must between
|
||||||
|
// zero and 32 bytes long.
|
||||||
|
func NewXOF(size uint16, key []byte) (XOF, error) {
|
||||||
|
if len(key) > Size {
|
||||||
|
return nil, errKeySize
|
||||||
|
}
|
||||||
|
if size == magicUnknownOutputLength {
|
||||||
|
// 2^16-1 indicates an unknown number of bytes and thus isn't a
|
||||||
|
// valid length.
|
||||||
|
return nil, errors.New("blake2s: XOF length too large")
|
||||||
|
}
|
||||||
|
if size == OutputLengthUnknown {
|
||||||
|
size = magicUnknownOutputLength
|
||||||
|
}
|
||||||
|
x := &xof{
|
||||||
|
d: digest{
|
||||||
|
size: Size,
|
||||||
|
keyLen: len(key),
|
||||||
|
},
|
||||||
|
length: size,
|
||||||
|
}
|
||||||
|
copy(x.d.key[:], key)
|
||||||
|
x.Reset()
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type xof struct {
|
||||||
|
d digest
|
||||||
|
length uint16
|
||||||
|
remaining uint64
|
||||||
|
cfg, root, block [Size]byte
|
||||||
|
offset int
|
||||||
|
nodeOffset uint32
|
||||||
|
readMode bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *xof) Write(p []byte) (n int, err error) {
|
||||||
|
if x.readMode {
|
||||||
|
panic("blake2s: write to XOF after read")
|
||||||
|
}
|
||||||
|
return x.d.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *xof) Clone() XOF {
|
||||||
|
clone := *x
|
||||||
|
return &clone
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *xof) Reset() {
|
||||||
|
x.cfg[0] = byte(Size)
|
||||||
|
binary.LittleEndian.PutUint32(x.cfg[4:], uint32(Size)) // leaf length
|
||||||
|
binary.LittleEndian.PutUint16(x.cfg[12:], x.length) // XOF length
|
||||||
|
x.cfg[15] = byte(Size) // inner hash size
|
||||||
|
|
||||||
|
x.d.Reset()
|
||||||
|
x.d.h[3] ^= uint32(x.length)
|
||||||
|
|
||||||
|
x.remaining = uint64(x.length)
|
||||||
|
if x.remaining == magicUnknownOutputLength {
|
||||||
|
x.remaining = maxOutputLength
|
||||||
|
}
|
||||||
|
x.offset, x.nodeOffset = 0, 0
|
||||||
|
x.readMode = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *xof) Read(p []byte) (n int, err error) {
|
||||||
|
if !x.readMode {
|
||||||
|
x.d.finalize(&x.root)
|
||||||
|
x.readMode = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if x.remaining == 0 {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
n = len(p)
|
||||||
|
if uint64(n) > x.remaining {
|
||||||
|
n = int(x.remaining)
|
||||||
|
p = p[:n]
|
||||||
|
}
|
||||||
|
|
||||||
|
if x.offset > 0 {
|
||||||
|
blockRemaining := Size - x.offset
|
||||||
|
if n < blockRemaining {
|
||||||
|
x.offset += copy(p, x.block[x.offset:])
|
||||||
|
x.remaining -= uint64(n)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
copy(p, x.block[x.offset:])
|
||||||
|
p = p[blockRemaining:]
|
||||||
|
x.offset = 0
|
||||||
|
x.remaining -= uint64(blockRemaining)
|
||||||
|
}
|
||||||
|
|
||||||
|
for len(p) >= Size {
|
||||||
|
binary.LittleEndian.PutUint32(x.cfg[8:], x.nodeOffset)
|
||||||
|
x.nodeOffset++
|
||||||
|
|
||||||
|
x.d.initConfig(&x.cfg)
|
||||||
|
x.d.Write(x.root[:])
|
||||||
|
x.d.finalize(&x.block)
|
||||||
|
|
||||||
|
copy(p, x.block[:])
|
||||||
|
p = p[Size:]
|
||||||
|
x.remaining -= uint64(Size)
|
||||||
|
}
|
||||||
|
|
||||||
|
if todo := len(p); todo > 0 {
|
||||||
|
if x.remaining < uint64(Size) {
|
||||||
|
x.cfg[0] = byte(x.remaining)
|
||||||
|
}
|
||||||
|
binary.LittleEndian.PutUint32(x.cfg[8:], x.nodeOffset)
|
||||||
|
x.nodeOffset++
|
||||||
|
|
||||||
|
x.d.initConfig(&x.cfg)
|
||||||
|
x.d.Write(x.root[:])
|
||||||
|
x.d.finalize(&x.block)
|
||||||
|
|
||||||
|
x.offset = copy(p, x.block[:todo])
|
||||||
|
x.remaining -= uint64(todo)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *digest) initConfig(cfg *[Size]byte) {
|
||||||
|
d.offset, d.c[0], d.c[1] = 0, 0, 0
|
||||||
|
for i := range d.h {
|
||||||
|
d.h[i] = iv[i] ^ binary.LittleEndian.Uint32(cfg[i*4:])
|
||||||
|
}
|
||||||
|
}
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
// Copyright 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build go1.9
|
||||||
|
|
||||||
|
package blake2s
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"hash"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
newHash256 := func() hash.Hash {
|
||||||
|
h, _ := New256(nil)
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
crypto.RegisterHash(crypto.BLAKE2s_256, newHash256)
|
||||||
|
}
|
||||||
+159
@@ -0,0 +1,159 @@
|
|||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package blowfish
|
||||||
|
|
||||||
|
// getNextWord returns the next big-endian uint32 value from the byte slice
|
||||||
|
// at the given position in a circular manner, updating the position.
|
||||||
|
func getNextWord(b []byte, pos *int) uint32 {
|
||||||
|
var w uint32
|
||||||
|
j := *pos
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
w = w<<8 | uint32(b[j])
|
||||||
|
j++
|
||||||
|
if j >= len(b) {
|
||||||
|
j = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*pos = j
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpandKey performs a key expansion on the given *Cipher. Specifically, it
|
||||||
|
// performs the Blowfish algorithm's key schedule which sets up the *Cipher's
|
||||||
|
// pi and substitution tables for calls to Encrypt. This is used, primarily,
|
||||||
|
// by the bcrypt package to reuse the Blowfish key schedule during its
|
||||||
|
// set up. It's unlikely that you need to use this directly.
|
||||||
|
func ExpandKey(key []byte, c *Cipher) {
|
||||||
|
j := 0
|
||||||
|
for i := 0; i < 18; i++ {
|
||||||
|
// Using inlined getNextWord for performance.
|
||||||
|
var d uint32
|
||||||
|
for k := 0; k < 4; k++ {
|
||||||
|
d = d<<8 | uint32(key[j])
|
||||||
|
j++
|
||||||
|
if j >= len(key) {
|
||||||
|
j = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.p[i] ^= d
|
||||||
|
}
|
||||||
|
|
||||||
|
var l, r uint32
|
||||||
|
for i := 0; i < 18; i += 2 {
|
||||||
|
l, r = encryptBlock(l, r, c)
|
||||||
|
c.p[i], c.p[i+1] = l, r
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 256; i += 2 {
|
||||||
|
l, r = encryptBlock(l, r, c)
|
||||||
|
c.s0[i], c.s0[i+1] = l, r
|
||||||
|
}
|
||||||
|
for i := 0; i < 256; i += 2 {
|
||||||
|
l, r = encryptBlock(l, r, c)
|
||||||
|
c.s1[i], c.s1[i+1] = l, r
|
||||||
|
}
|
||||||
|
for i := 0; i < 256; i += 2 {
|
||||||
|
l, r = encryptBlock(l, r, c)
|
||||||
|
c.s2[i], c.s2[i+1] = l, r
|
||||||
|
}
|
||||||
|
for i := 0; i < 256; i += 2 {
|
||||||
|
l, r = encryptBlock(l, r, c)
|
||||||
|
c.s3[i], c.s3[i+1] = l, r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is similar to ExpandKey, but folds the salt during the key
|
||||||
|
// schedule. While ExpandKey is essentially expandKeyWithSalt with an all-zero
|
||||||
|
// salt passed in, reusing ExpandKey turns out to be a place of inefficiency
|
||||||
|
// and specializing it here is useful.
|
||||||
|
func expandKeyWithSalt(key []byte, salt []byte, c *Cipher) {
|
||||||
|
j := 0
|
||||||
|
for i := 0; i < 18; i++ {
|
||||||
|
c.p[i] ^= getNextWord(key, &j)
|
||||||
|
}
|
||||||
|
|
||||||
|
j = 0
|
||||||
|
var l, r uint32
|
||||||
|
for i := 0; i < 18; i += 2 {
|
||||||
|
l ^= getNextWord(salt, &j)
|
||||||
|
r ^= getNextWord(salt, &j)
|
||||||
|
l, r = encryptBlock(l, r, c)
|
||||||
|
c.p[i], c.p[i+1] = l, r
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 256; i += 2 {
|
||||||
|
l ^= getNextWord(salt, &j)
|
||||||
|
r ^= getNextWord(salt, &j)
|
||||||
|
l, r = encryptBlock(l, r, c)
|
||||||
|
c.s0[i], c.s0[i+1] = l, r
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 256; i += 2 {
|
||||||
|
l ^= getNextWord(salt, &j)
|
||||||
|
r ^= getNextWord(salt, &j)
|
||||||
|
l, r = encryptBlock(l, r, c)
|
||||||
|
c.s1[i], c.s1[i+1] = l, r
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 256; i += 2 {
|
||||||
|
l ^= getNextWord(salt, &j)
|
||||||
|
r ^= getNextWord(salt, &j)
|
||||||
|
l, r = encryptBlock(l, r, c)
|
||||||
|
c.s2[i], c.s2[i+1] = l, r
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 256; i += 2 {
|
||||||
|
l ^= getNextWord(salt, &j)
|
||||||
|
r ^= getNextWord(salt, &j)
|
||||||
|
l, r = encryptBlock(l, r, c)
|
||||||
|
c.s3[i], c.s3[i+1] = l, r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encryptBlock(l, r uint32, c *Cipher) (uint32, uint32) {
|
||||||
|
xl, xr := l, r
|
||||||
|
xl ^= c.p[0]
|
||||||
|
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[1]
|
||||||
|
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[2]
|
||||||
|
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[3]
|
||||||
|
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[4]
|
||||||
|
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[5]
|
||||||
|
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[6]
|
||||||
|
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[7]
|
||||||
|
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[8]
|
||||||
|
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[9]
|
||||||
|
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[10]
|
||||||
|
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[11]
|
||||||
|
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[12]
|
||||||
|
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[13]
|
||||||
|
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[14]
|
||||||
|
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[15]
|
||||||
|
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[16]
|
||||||
|
xr ^= c.p[17]
|
||||||
|
return xr, xl
|
||||||
|
}
|
||||||
|
|
||||||
|
func decryptBlock(l, r uint32, c *Cipher) (uint32, uint32) {
|
||||||
|
xl, xr := l, r
|
||||||
|
xl ^= c.p[17]
|
||||||
|
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[16]
|
||||||
|
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[15]
|
||||||
|
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[14]
|
||||||
|
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[13]
|
||||||
|
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[12]
|
||||||
|
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[11]
|
||||||
|
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[10]
|
||||||
|
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[9]
|
||||||
|
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[8]
|
||||||
|
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[7]
|
||||||
|
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[6]
|
||||||
|
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[5]
|
||||||
|
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[4]
|
||||||
|
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[3]
|
||||||
|
xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[2]
|
||||||
|
xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[1]
|
||||||
|
xr ^= c.p[0]
|
||||||
|
return xr, xl
|
||||||
|
}
|
||||||
+274
@@ -0,0 +1,274 @@
|
|||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package blowfish
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
type CryptTest struct {
|
||||||
|
key []byte
|
||||||
|
in []byte
|
||||||
|
out []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test vector values are from https://www.schneier.com/code/vectors.txt.
|
||||||
|
var encryptTests = []CryptTest{
|
||||||
|
{
|
||||||
|
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||||
|
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||||
|
[]byte{0x4E, 0xF9, 0x97, 0x45, 0x61, 0x98, 0xDD, 0x78}},
|
||||||
|
{
|
||||||
|
[]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
|
||||||
|
[]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
|
||||||
|
[]byte{0x51, 0x86, 0x6F, 0xD5, 0xB8, 0x5E, 0xCB, 0x8A}},
|
||||||
|
{
|
||||||
|
[]byte{0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||||
|
[]byte{0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
|
||||||
|
[]byte{0x7D, 0x85, 0x6F, 0x9A, 0x61, 0x30, 0x63, 0xF2}},
|
||||||
|
{
|
||||||
|
[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11},
|
||||||
|
[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11},
|
||||||
|
[]byte{0x24, 0x66, 0xDD, 0x87, 0x8B, 0x96, 0x3C, 0x9D}},
|
||||||
|
|
||||||
|
{
|
||||||
|
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
|
||||||
|
[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11},
|
||||||
|
[]byte{0x61, 0xF9, 0xC3, 0x80, 0x22, 0x81, 0xB0, 0x96}},
|
||||||
|
{
|
||||||
|
[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11},
|
||||||
|
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
|
||||||
|
[]byte{0x7D, 0x0C, 0xC6, 0x30, 0xAF, 0xDA, 0x1E, 0xC7}},
|
||||||
|
{
|
||||||
|
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||||
|
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||||
|
[]byte{0x4E, 0xF9, 0x97, 0x45, 0x61, 0x98, 0xDD, 0x78}},
|
||||||
|
{
|
||||||
|
[]byte{0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10},
|
||||||
|
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
|
||||||
|
[]byte{0x0A, 0xCE, 0xAB, 0x0F, 0xC6, 0xA0, 0xA2, 0x8D}},
|
||||||
|
{
|
||||||
|
[]byte{0x7C, 0xA1, 0x10, 0x45, 0x4A, 0x1A, 0x6E, 0x57},
|
||||||
|
[]byte{0x01, 0xA1, 0xD6, 0xD0, 0x39, 0x77, 0x67, 0x42},
|
||||||
|
[]byte{0x59, 0xC6, 0x82, 0x45, 0xEB, 0x05, 0x28, 0x2B}},
|
||||||
|
{
|
||||||
|
[]byte{0x01, 0x31, 0xD9, 0x61, 0x9D, 0xC1, 0x37, 0x6E},
|
||||||
|
[]byte{0x5C, 0xD5, 0x4C, 0xA8, 0x3D, 0xEF, 0x57, 0xDA},
|
||||||
|
[]byte{0xB1, 0xB8, 0xCC, 0x0B, 0x25, 0x0F, 0x09, 0xA0}},
|
||||||
|
{
|
||||||
|
[]byte{0x07, 0xA1, 0x13, 0x3E, 0x4A, 0x0B, 0x26, 0x86},
|
||||||
|
[]byte{0x02, 0x48, 0xD4, 0x38, 0x06, 0xF6, 0x71, 0x72},
|
||||||
|
[]byte{0x17, 0x30, 0xE5, 0x77, 0x8B, 0xEA, 0x1D, 0xA4}},
|
||||||
|
{
|
||||||
|
[]byte{0x38, 0x49, 0x67, 0x4C, 0x26, 0x02, 0x31, 0x9E},
|
||||||
|
[]byte{0x51, 0x45, 0x4B, 0x58, 0x2D, 0xDF, 0x44, 0x0A},
|
||||||
|
[]byte{0xA2, 0x5E, 0x78, 0x56, 0xCF, 0x26, 0x51, 0xEB}},
|
||||||
|
{
|
||||||
|
[]byte{0x04, 0xB9, 0x15, 0xBA, 0x43, 0xFE, 0xB5, 0xB6},
|
||||||
|
[]byte{0x42, 0xFD, 0x44, 0x30, 0x59, 0x57, 0x7F, 0xA2},
|
||||||
|
[]byte{0x35, 0x38, 0x82, 0xB1, 0x09, 0xCE, 0x8F, 0x1A}},
|
||||||
|
{
|
||||||
|
[]byte{0x01, 0x13, 0xB9, 0x70, 0xFD, 0x34, 0xF2, 0xCE},
|
||||||
|
[]byte{0x05, 0x9B, 0x5E, 0x08, 0x51, 0xCF, 0x14, 0x3A},
|
||||||
|
[]byte{0x48, 0xF4, 0xD0, 0x88, 0x4C, 0x37, 0x99, 0x18}},
|
||||||
|
{
|
||||||
|
[]byte{0x01, 0x70, 0xF1, 0x75, 0x46, 0x8F, 0xB5, 0xE6},
|
||||||
|
[]byte{0x07, 0x56, 0xD8, 0xE0, 0x77, 0x47, 0x61, 0xD2},
|
||||||
|
[]byte{0x43, 0x21, 0x93, 0xB7, 0x89, 0x51, 0xFC, 0x98}},
|
||||||
|
{
|
||||||
|
[]byte{0x43, 0x29, 0x7F, 0xAD, 0x38, 0xE3, 0x73, 0xFE},
|
||||||
|
[]byte{0x76, 0x25, 0x14, 0xB8, 0x29, 0xBF, 0x48, 0x6A},
|
||||||
|
[]byte{0x13, 0xF0, 0x41, 0x54, 0xD6, 0x9D, 0x1A, 0xE5}},
|
||||||
|
{
|
||||||
|
[]byte{0x07, 0xA7, 0x13, 0x70, 0x45, 0xDA, 0x2A, 0x16},
|
||||||
|
[]byte{0x3B, 0xDD, 0x11, 0x90, 0x49, 0x37, 0x28, 0x02},
|
||||||
|
[]byte{0x2E, 0xED, 0xDA, 0x93, 0xFF, 0xD3, 0x9C, 0x79}},
|
||||||
|
{
|
||||||
|
[]byte{0x04, 0x68, 0x91, 0x04, 0xC2, 0xFD, 0x3B, 0x2F},
|
||||||
|
[]byte{0x26, 0x95, 0x5F, 0x68, 0x35, 0xAF, 0x60, 0x9A},
|
||||||
|
[]byte{0xD8, 0x87, 0xE0, 0x39, 0x3C, 0x2D, 0xA6, 0xE3}},
|
||||||
|
{
|
||||||
|
[]byte{0x37, 0xD0, 0x6B, 0xB5, 0x16, 0xCB, 0x75, 0x46},
|
||||||
|
[]byte{0x16, 0x4D, 0x5E, 0x40, 0x4F, 0x27, 0x52, 0x32},
|
||||||
|
[]byte{0x5F, 0x99, 0xD0, 0x4F, 0x5B, 0x16, 0x39, 0x69}},
|
||||||
|
{
|
||||||
|
[]byte{0x1F, 0x08, 0x26, 0x0D, 0x1A, 0xC2, 0x46, 0x5E},
|
||||||
|
[]byte{0x6B, 0x05, 0x6E, 0x18, 0x75, 0x9F, 0x5C, 0xCA},
|
||||||
|
[]byte{0x4A, 0x05, 0x7A, 0x3B, 0x24, 0xD3, 0x97, 0x7B}},
|
||||||
|
{
|
||||||
|
[]byte{0x58, 0x40, 0x23, 0x64, 0x1A, 0xBA, 0x61, 0x76},
|
||||||
|
[]byte{0x00, 0x4B, 0xD6, 0xEF, 0x09, 0x17, 0x60, 0x62},
|
||||||
|
[]byte{0x45, 0x20, 0x31, 0xC1, 0xE4, 0xFA, 0xDA, 0x8E}},
|
||||||
|
{
|
||||||
|
[]byte{0x02, 0x58, 0x16, 0x16, 0x46, 0x29, 0xB0, 0x07},
|
||||||
|
[]byte{0x48, 0x0D, 0x39, 0x00, 0x6E, 0xE7, 0x62, 0xF2},
|
||||||
|
[]byte{0x75, 0x55, 0xAE, 0x39, 0xF5, 0x9B, 0x87, 0xBD}},
|
||||||
|
{
|
||||||
|
[]byte{0x49, 0x79, 0x3E, 0xBC, 0x79, 0xB3, 0x25, 0x8F},
|
||||||
|
[]byte{0x43, 0x75, 0x40, 0xC8, 0x69, 0x8F, 0x3C, 0xFA},
|
||||||
|
[]byte{0x53, 0xC5, 0x5F, 0x9C, 0xB4, 0x9F, 0xC0, 0x19}},
|
||||||
|
{
|
||||||
|
[]byte{0x4F, 0xB0, 0x5E, 0x15, 0x15, 0xAB, 0x73, 0xA7},
|
||||||
|
[]byte{0x07, 0x2D, 0x43, 0xA0, 0x77, 0x07, 0x52, 0x92},
|
||||||
|
[]byte{0x7A, 0x8E, 0x7B, 0xFA, 0x93, 0x7E, 0x89, 0xA3}},
|
||||||
|
{
|
||||||
|
[]byte{0x49, 0xE9, 0x5D, 0x6D, 0x4C, 0xA2, 0x29, 0xBF},
|
||||||
|
[]byte{0x02, 0xFE, 0x55, 0x77, 0x81, 0x17, 0xF1, 0x2A},
|
||||||
|
[]byte{0xCF, 0x9C, 0x5D, 0x7A, 0x49, 0x86, 0xAD, 0xB5}},
|
||||||
|
{
|
||||||
|
[]byte{0x01, 0x83, 0x10, 0xDC, 0x40, 0x9B, 0x26, 0xD6},
|
||||||
|
[]byte{0x1D, 0x9D, 0x5C, 0x50, 0x18, 0xF7, 0x28, 0xC2},
|
||||||
|
[]byte{0xD1, 0xAB, 0xB2, 0x90, 0x65, 0x8B, 0xC7, 0x78}},
|
||||||
|
{
|
||||||
|
[]byte{0x1C, 0x58, 0x7F, 0x1C, 0x13, 0x92, 0x4F, 0xEF},
|
||||||
|
[]byte{0x30, 0x55, 0x32, 0x28, 0x6D, 0x6F, 0x29, 0x5A},
|
||||||
|
[]byte{0x55, 0xCB, 0x37, 0x74, 0xD1, 0x3E, 0xF2, 0x01}},
|
||||||
|
{
|
||||||
|
[]byte{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
|
||||||
|
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
|
||||||
|
[]byte{0xFA, 0x34, 0xEC, 0x48, 0x47, 0xB2, 0x68, 0xB2}},
|
||||||
|
{
|
||||||
|
[]byte{0x1F, 0x1F, 0x1F, 0x1F, 0x0E, 0x0E, 0x0E, 0x0E},
|
||||||
|
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
|
||||||
|
[]byte{0xA7, 0x90, 0x79, 0x51, 0x08, 0xEA, 0x3C, 0xAE}},
|
||||||
|
{
|
||||||
|
[]byte{0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1, 0xFE},
|
||||||
|
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
|
||||||
|
[]byte{0xC3, 0x9E, 0x07, 0x2D, 0x9F, 0xAC, 0x63, 0x1D}},
|
||||||
|
{
|
||||||
|
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||||
|
[]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
|
||||||
|
[]byte{0x01, 0x49, 0x33, 0xE0, 0xCD, 0xAF, 0xF6, 0xE4}},
|
||||||
|
{
|
||||||
|
[]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
|
||||||
|
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||||
|
[]byte{0xF2, 0x1E, 0x9A, 0x77, 0xB7, 0x1C, 0x49, 0xBC}},
|
||||||
|
{
|
||||||
|
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
|
||||||
|
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||||
|
[]byte{0x24, 0x59, 0x46, 0x88, 0x57, 0x54, 0x36, 0x9A}},
|
||||||
|
{
|
||||||
|
[]byte{0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10},
|
||||||
|
[]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
|
||||||
|
[]byte{0x6B, 0x5C, 0x5A, 0x9C, 0x5D, 0x9E, 0x0A, 0x5A}},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCipherEncrypt(t *testing.T) {
|
||||||
|
for i, tt := range encryptTests {
|
||||||
|
c, err := NewCipher(tt.key)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("NewCipher(%d bytes) = %s", len(tt.key), err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ct := make([]byte, len(tt.out))
|
||||||
|
c.Encrypt(ct, tt.in)
|
||||||
|
for j, v := range ct {
|
||||||
|
if v != tt.out[j] {
|
||||||
|
t.Errorf("Cipher.Encrypt, test vector #%d: cipher-text[%d] = %#x, expected %#x", i, j, v, tt.out[j])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCipherDecrypt(t *testing.T) {
|
||||||
|
for i, tt := range encryptTests {
|
||||||
|
c, err := NewCipher(tt.key)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("NewCipher(%d bytes) = %s", len(tt.key), err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pt := make([]byte, len(tt.in))
|
||||||
|
c.Decrypt(pt, tt.out)
|
||||||
|
for j, v := range pt {
|
||||||
|
if v != tt.in[j] {
|
||||||
|
t.Errorf("Cipher.Decrypt, test vector #%d: plain-text[%d] = %#x, expected %#x", i, j, v, tt.in[j])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSaltedCipherKeyLength(t *testing.T) {
|
||||||
|
if _, err := NewSaltedCipher(nil, []byte{'a'}); err != KeySizeError(0) {
|
||||||
|
t.Errorf("NewSaltedCipher with short key, gave error %#v, expected %#v", err, KeySizeError(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
// A 57-byte key. One over the typical blowfish restriction.
|
||||||
|
key := []byte("012345678901234567890123456789012345678901234567890123456")
|
||||||
|
if _, err := NewSaltedCipher(key, []byte{'a'}); err != nil {
|
||||||
|
t.Errorf("NewSaltedCipher with long key, gave error %#v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test vectors generated with Blowfish from OpenSSH.
|
||||||
|
var saltedVectors = [][8]byte{
|
||||||
|
{0x0c, 0x82, 0x3b, 0x7b, 0x8d, 0x01, 0x4b, 0x7e},
|
||||||
|
{0xd1, 0xe1, 0x93, 0xf0, 0x70, 0xa6, 0xdb, 0x12},
|
||||||
|
{0xfc, 0x5e, 0xba, 0xde, 0xcb, 0xf8, 0x59, 0xad},
|
||||||
|
{0x8a, 0x0c, 0x76, 0xe7, 0xdd, 0x2c, 0xd3, 0xa8},
|
||||||
|
{0x2c, 0xcb, 0x7b, 0xee, 0xac, 0x7b, 0x7f, 0xf8},
|
||||||
|
{0xbb, 0xf6, 0x30, 0x6f, 0xe1, 0x5d, 0x62, 0xbf},
|
||||||
|
{0x97, 0x1e, 0xc1, 0x3d, 0x3d, 0xe0, 0x11, 0xe9},
|
||||||
|
{0x06, 0xd7, 0x4d, 0xb1, 0x80, 0xa3, 0xb1, 0x38},
|
||||||
|
{0x67, 0xa1, 0xa9, 0x75, 0x0e, 0x5b, 0xc6, 0xb4},
|
||||||
|
{0x51, 0x0f, 0x33, 0x0e, 0x4f, 0x67, 0xd2, 0x0c},
|
||||||
|
{0xf1, 0x73, 0x7e, 0xd8, 0x44, 0xea, 0xdb, 0xe5},
|
||||||
|
{0x14, 0x0e, 0x16, 0xce, 0x7f, 0x4a, 0x9c, 0x7b},
|
||||||
|
{0x4b, 0xfe, 0x43, 0xfd, 0xbf, 0x36, 0x04, 0x47},
|
||||||
|
{0xb1, 0xeb, 0x3e, 0x15, 0x36, 0xa7, 0xbb, 0xe2},
|
||||||
|
{0x6d, 0x0b, 0x41, 0xdd, 0x00, 0x98, 0x0b, 0x19},
|
||||||
|
{0xd3, 0xce, 0x45, 0xce, 0x1d, 0x56, 0xb7, 0xfc},
|
||||||
|
{0xd9, 0xf0, 0xfd, 0xda, 0xc0, 0x23, 0xb7, 0x93},
|
||||||
|
{0x4c, 0x6f, 0xa1, 0xe4, 0x0c, 0xa8, 0xca, 0x57},
|
||||||
|
{0xe6, 0x2f, 0x28, 0xa7, 0x0c, 0x94, 0x0d, 0x08},
|
||||||
|
{0x8f, 0xe3, 0xf0, 0xb6, 0x29, 0xe3, 0x44, 0x03},
|
||||||
|
{0xff, 0x98, 0xdd, 0x04, 0x45, 0xb4, 0x6d, 0x1f},
|
||||||
|
{0x9e, 0x45, 0x4d, 0x18, 0x40, 0x53, 0xdb, 0xef},
|
||||||
|
{0xb7, 0x3b, 0xef, 0x29, 0xbe, 0xa8, 0x13, 0x71},
|
||||||
|
{0x02, 0x54, 0x55, 0x41, 0x8e, 0x04, 0xfc, 0xad},
|
||||||
|
{0x6a, 0x0a, 0xee, 0x7c, 0x10, 0xd9, 0x19, 0xfe},
|
||||||
|
{0x0a, 0x22, 0xd9, 0x41, 0xcc, 0x23, 0x87, 0x13},
|
||||||
|
{0x6e, 0xff, 0x1f, 0xff, 0x36, 0x17, 0x9c, 0xbe},
|
||||||
|
{0x79, 0xad, 0xb7, 0x40, 0xf4, 0x9f, 0x51, 0xa6},
|
||||||
|
{0x97, 0x81, 0x99, 0xa4, 0xde, 0x9e, 0x9f, 0xb6},
|
||||||
|
{0x12, 0x19, 0x7a, 0x28, 0xd0, 0xdc, 0xcc, 0x92},
|
||||||
|
{0x81, 0xda, 0x60, 0x1e, 0x0e, 0xdd, 0x65, 0x56},
|
||||||
|
{0x7d, 0x76, 0x20, 0xb2, 0x73, 0xc9, 0x9e, 0xee},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSaltedCipher(t *testing.T) {
|
||||||
|
var key, salt [32]byte
|
||||||
|
for i := range key {
|
||||||
|
key[i] = byte(i)
|
||||||
|
salt[i] = byte(i + 32)
|
||||||
|
}
|
||||||
|
for i, v := range saltedVectors {
|
||||||
|
c, err := NewSaltedCipher(key[:], salt[:i])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var buf [8]byte
|
||||||
|
c.Encrypt(buf[:], buf[:])
|
||||||
|
if v != buf {
|
||||||
|
t.Errorf("%d: expected %x, got %x", i, v, buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkExpandKeyWithSalt(b *testing.B) {
|
||||||
|
key := make([]byte, 32)
|
||||||
|
salt := make([]byte, 16)
|
||||||
|
c, _ := NewCipher(key)
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
expandKeyWithSalt(key, salt, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkExpandKey(b *testing.B) {
|
||||||
|
key := make([]byte, 32)
|
||||||
|
c, _ := NewCipher(key)
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
ExpandKey(key, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
+91
@@ -0,0 +1,91 @@
|
|||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package blowfish implements Bruce Schneier's Blowfish encryption algorithm.
|
||||||
|
package blowfish // import "golang.org/x/crypto/blowfish"
|
||||||
|
|
||||||
|
// The code is a port of Bruce Schneier's C implementation.
|
||||||
|
// See https://www.schneier.com/blowfish.html.
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
// The Blowfish block size in bytes.
|
||||||
|
const BlockSize = 8
|
||||||
|
|
||||||
|
// A Cipher is an instance of Blowfish encryption using a particular key.
|
||||||
|
type Cipher struct {
|
||||||
|
p [18]uint32
|
||||||
|
s0, s1, s2, s3 [256]uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type KeySizeError int
|
||||||
|
|
||||||
|
func (k KeySizeError) Error() string {
|
||||||
|
return "crypto/blowfish: invalid key size " + strconv.Itoa(int(k))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCipher creates and returns a Cipher.
|
||||||
|
// The key argument should be the Blowfish key, from 1 to 56 bytes.
|
||||||
|
func NewCipher(key []byte) (*Cipher, error) {
|
||||||
|
var result Cipher
|
||||||
|
if k := len(key); k < 1 || k > 56 {
|
||||||
|
return nil, KeySizeError(k)
|
||||||
|
}
|
||||||
|
initCipher(&result)
|
||||||
|
ExpandKey(key, &result)
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSaltedCipher creates a returns a Cipher that folds a salt into its key
|
||||||
|
// schedule. For most purposes, NewCipher, instead of NewSaltedCipher, is
|
||||||
|
// sufficient and desirable. For bcrypt compatibility, the key can be over 56
|
||||||
|
// bytes.
|
||||||
|
func NewSaltedCipher(key, salt []byte) (*Cipher, error) {
|
||||||
|
if len(salt) == 0 {
|
||||||
|
return NewCipher(key)
|
||||||
|
}
|
||||||
|
var result Cipher
|
||||||
|
if k := len(key); k < 1 {
|
||||||
|
return nil, KeySizeError(k)
|
||||||
|
}
|
||||||
|
initCipher(&result)
|
||||||
|
expandKeyWithSalt(key, salt, &result)
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlockSize returns the Blowfish block size, 8 bytes.
|
||||||
|
// It is necessary to satisfy the Block interface in the
|
||||||
|
// package "crypto/cipher".
|
||||||
|
func (c *Cipher) BlockSize() int { return BlockSize }
|
||||||
|
|
||||||
|
// Encrypt encrypts the 8-byte buffer src using the key k
|
||||||
|
// and stores the result in dst.
|
||||||
|
// Note that for amounts of data larger than a block,
|
||||||
|
// it is not safe to just call Encrypt on successive blocks;
|
||||||
|
// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go).
|
||||||
|
func (c *Cipher) Encrypt(dst, src []byte) {
|
||||||
|
l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
|
||||||
|
r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
|
||||||
|
l, r = encryptBlock(l, r, c)
|
||||||
|
dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l)
|
||||||
|
dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt decrypts the 8-byte buffer src using the key k
|
||||||
|
// and stores the result in dst.
|
||||||
|
func (c *Cipher) Decrypt(dst, src []byte) {
|
||||||
|
l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
|
||||||
|
r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
|
||||||
|
l, r = decryptBlock(l, r, c)
|
||||||
|
dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l)
|
||||||
|
dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func initCipher(c *Cipher) {
|
||||||
|
copy(c.p[0:], p[0:])
|
||||||
|
copy(c.s0[0:], s0[0:])
|
||||||
|
copy(c.s1[0:], s1[0:])
|
||||||
|
copy(c.s2[0:], s2[0:])
|
||||||
|
copy(c.s3[0:], s3[0:])
|
||||||
|
}
|
||||||
+199
@@ -0,0 +1,199 @@
|
|||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// The startup permutation array and substitution boxes.
|
||||||
|
// They are the hexadecimal digits of PI; see:
|
||||||
|
// https://www.schneier.com/code/constants.txt.
|
||||||
|
|
||||||
|
package blowfish
|
||||||
|
|
||||||
|
var s0 = [256]uint32{
|
||||||
|
0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96,
|
||||||
|
0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
|
||||||
|
0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658,
|
||||||
|
0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
|
||||||
|
0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e,
|
||||||
|
0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
|
||||||
|
0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6,
|
||||||
|
0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
|
||||||
|
0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c,
|
||||||
|
0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
|
||||||
|
0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1,
|
||||||
|
0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
|
||||||
|
0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a,
|
||||||
|
0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
|
||||||
|
0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176,
|
||||||
|
0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
|
||||||
|
0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706,
|
||||||
|
0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
|
||||||
|
0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b,
|
||||||
|
0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
|
||||||
|
0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c,
|
||||||
|
0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
|
||||||
|
0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a,
|
||||||
|
0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
|
||||||
|
0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760,
|
||||||
|
0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
|
||||||
|
0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8,
|
||||||
|
0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
|
||||||
|
0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33,
|
||||||
|
0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
|
||||||
|
0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0,
|
||||||
|
0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
|
||||||
|
0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777,
|
||||||
|
0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
|
||||||
|
0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705,
|
||||||
|
0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
|
||||||
|
0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e,
|
||||||
|
0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
|
||||||
|
0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9,
|
||||||
|
0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
|
||||||
|
0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f,
|
||||||
|
0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
|
||||||
|
0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
|
||||||
|
}
|
||||||
|
|
||||||
|
var s1 = [256]uint32{
|
||||||
|
0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d,
|
||||||
|
0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
|
||||||
|
0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65,
|
||||||
|
0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
|
||||||
|
0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9,
|
||||||
|
0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
|
||||||
|
0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d,
|
||||||
|
0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
|
||||||
|
0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc,
|
||||||
|
0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
|
||||||
|
0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908,
|
||||||
|
0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
|
||||||
|
0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124,
|
||||||
|
0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
|
||||||
|
0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908,
|
||||||
|
0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
|
||||||
|
0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b,
|
||||||
|
0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
|
||||||
|
0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa,
|
||||||
|
0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
|
||||||
|
0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d,
|
||||||
|
0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
|
||||||
|
0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5,
|
||||||
|
0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
|
||||||
|
0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96,
|
||||||
|
0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
|
||||||
|
0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca,
|
||||||
|
0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
|
||||||
|
0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77,
|
||||||
|
0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
|
||||||
|
0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054,
|
||||||
|
0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
|
||||||
|
0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea,
|
||||||
|
0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
|
||||||
|
0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646,
|
||||||
|
0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
|
||||||
|
0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea,
|
||||||
|
0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
|
||||||
|
0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e,
|
||||||
|
0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
|
||||||
|
0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd,
|
||||||
|
0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
|
||||||
|
0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,
|
||||||
|
}
|
||||||
|
|
||||||
|
var s2 = [256]uint32{
|
||||||
|
0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7,
|
||||||
|
0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
|
||||||
|
0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af,
|
||||||
|
0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
|
||||||
|
0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4,
|
||||||
|
0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
|
||||||
|
0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec,
|
||||||
|
0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
|
||||||
|
0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332,
|
||||||
|
0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
|
||||||
|
0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58,
|
||||||
|
0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
|
||||||
|
0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22,
|
||||||
|
0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
|
||||||
|
0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60,
|
||||||
|
0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
|
||||||
|
0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99,
|
||||||
|
0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
|
||||||
|
0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74,
|
||||||
|
0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
|
||||||
|
0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3,
|
||||||
|
0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
|
||||||
|
0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979,
|
||||||
|
0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
|
||||||
|
0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa,
|
||||||
|
0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
|
||||||
|
0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086,
|
||||||
|
0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
|
||||||
|
0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24,
|
||||||
|
0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
|
||||||
|
0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84,
|
||||||
|
0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
|
||||||
|
0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09,
|
||||||
|
0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
|
||||||
|
0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe,
|
||||||
|
0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
|
||||||
|
0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0,
|
||||||
|
0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
|
||||||
|
0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188,
|
||||||
|
0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
|
||||||
|
0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8,
|
||||||
|
0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
|
||||||
|
0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,
|
||||||
|
}
|
||||||
|
|
||||||
|
var s3 = [256]uint32{
|
||||||
|
0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742,
|
||||||
|
0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
|
||||||
|
0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79,
|
||||||
|
0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
|
||||||
|
0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a,
|
||||||
|
0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
|
||||||
|
0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1,
|
||||||
|
0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
|
||||||
|
0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797,
|
||||||
|
0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
|
||||||
|
0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6,
|
||||||
|
0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
|
||||||
|
0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba,
|
||||||
|
0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
|
||||||
|
0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5,
|
||||||
|
0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
|
||||||
|
0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce,
|
||||||
|
0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
|
||||||
|
0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd,
|
||||||
|
0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
|
||||||
|
0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb,
|
||||||
|
0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
|
||||||
|
0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc,
|
||||||
|
0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
|
||||||
|
0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc,
|
||||||
|
0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
|
||||||
|
0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a,
|
||||||
|
0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
|
||||||
|
0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a,
|
||||||
|
0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
|
||||||
|
0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b,
|
||||||
|
0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
|
||||||
|
0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e,
|
||||||
|
0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
|
||||||
|
0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623,
|
||||||
|
0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
|
||||||
|
0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a,
|
||||||
|
0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
|
||||||
|
0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3,
|
||||||
|
0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
|
||||||
|
0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c,
|
||||||
|
0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
|
||||||
|
0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6,
|
||||||
|
}
|
||||||
|
|
||||||
|
var p = [18]uint32{
|
||||||
|
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0,
|
||||||
|
0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
|
||||||
|
0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b,
|
||||||
|
}
|
||||||
+404
@@ -0,0 +1,404 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package bn256 implements a particular bilinear group at the 128-bit security level.
|
||||||
|
//
|
||||||
|
// Bilinear groups are the basis of many of the new cryptographic protocols
|
||||||
|
// that have been proposed over the past decade. They consist of a triplet of
|
||||||
|
// groups (G₁, G₂ and GT) such that there exists a function e(g₁ˣ,g₂ʸ)=gTˣʸ
|
||||||
|
// (where gₓ is a generator of the respective group). That function is called
|
||||||
|
// a pairing function.
|
||||||
|
//
|
||||||
|
// This package specifically implements the Optimal Ate pairing over a 256-bit
|
||||||
|
// Barreto-Naehrig curve as described in
|
||||||
|
// http://cryptojedi.org/papers/dclxvi-20100714.pdf. Its output is compatible
|
||||||
|
// with the implementation described in that paper.
|
||||||
|
package bn256 // import "golang.org/x/crypto/bn256"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"io"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BUG(agl): this implementation is not constant time.
|
||||||
|
// TODO(agl): keep GF(p²) elements in Mongomery form.
|
||||||
|
|
||||||
|
// G1 is an abstract cyclic group. The zero value is suitable for use as the
|
||||||
|
// output of an operation, but cannot be used as an input.
|
||||||
|
type G1 struct {
|
||||||
|
p *curvePoint
|
||||||
|
}
|
||||||
|
|
||||||
|
// RandomG1 returns x and g₁ˣ where x is a random, non-zero number read from r.
|
||||||
|
func RandomG1(r io.Reader) (*big.Int, *G1, error) {
|
||||||
|
var k *big.Int
|
||||||
|
var err error
|
||||||
|
|
||||||
|
for {
|
||||||
|
k, err = rand.Int(r, Order)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if k.Sign() > 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return k, new(G1).ScalarBaseMult(k), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *G1) String() string {
|
||||||
|
return "bn256.G1" + g.p.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScalarBaseMult sets e to g*k where g is the generator of the group and
|
||||||
|
// then returns e.
|
||||||
|
func (e *G1) ScalarBaseMult(k *big.Int) *G1 {
|
||||||
|
if e.p == nil {
|
||||||
|
e.p = newCurvePoint(nil)
|
||||||
|
}
|
||||||
|
e.p.Mul(curveGen, k, new(bnPool))
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScalarMult sets e to a*k and then returns e.
|
||||||
|
func (e *G1) ScalarMult(a *G1, k *big.Int) *G1 {
|
||||||
|
if e.p == nil {
|
||||||
|
e.p = newCurvePoint(nil)
|
||||||
|
}
|
||||||
|
e.p.Mul(a.p, k, new(bnPool))
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add sets e to a+b and then returns e.
|
||||||
|
// BUG(agl): this function is not complete: a==b fails.
|
||||||
|
func (e *G1) Add(a, b *G1) *G1 {
|
||||||
|
if e.p == nil {
|
||||||
|
e.p = newCurvePoint(nil)
|
||||||
|
}
|
||||||
|
e.p.Add(a.p, b.p, new(bnPool))
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// Neg sets e to -a and then returns e.
|
||||||
|
func (e *G1) Neg(a *G1) *G1 {
|
||||||
|
if e.p == nil {
|
||||||
|
e.p = newCurvePoint(nil)
|
||||||
|
}
|
||||||
|
e.p.Negative(a.p)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal converts n to a byte slice.
|
||||||
|
func (n *G1) Marshal() []byte {
|
||||||
|
n.p.MakeAffine(nil)
|
||||||
|
|
||||||
|
xBytes := new(big.Int).Mod(n.p.x, p).Bytes()
|
||||||
|
yBytes := new(big.Int).Mod(n.p.y, p).Bytes()
|
||||||
|
|
||||||
|
// Each value is a 256-bit number.
|
||||||
|
const numBytes = 256 / 8
|
||||||
|
|
||||||
|
ret := make([]byte, numBytes*2)
|
||||||
|
copy(ret[1*numBytes-len(xBytes):], xBytes)
|
||||||
|
copy(ret[2*numBytes-len(yBytes):], yBytes)
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal sets e to the result of converting the output of Marshal back into
|
||||||
|
// a group element and then returns e.
|
||||||
|
func (e *G1) Unmarshal(m []byte) (*G1, bool) {
|
||||||
|
// Each value is a 256-bit number.
|
||||||
|
const numBytes = 256 / 8
|
||||||
|
|
||||||
|
if len(m) != 2*numBytes {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.p == nil {
|
||||||
|
e.p = newCurvePoint(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
e.p.x.SetBytes(m[0*numBytes : 1*numBytes])
|
||||||
|
e.p.y.SetBytes(m[1*numBytes : 2*numBytes])
|
||||||
|
|
||||||
|
if e.p.x.Sign() == 0 && e.p.y.Sign() == 0 {
|
||||||
|
// This is the point at infinity.
|
||||||
|
e.p.y.SetInt64(1)
|
||||||
|
e.p.z.SetInt64(0)
|
||||||
|
e.p.t.SetInt64(0)
|
||||||
|
} else {
|
||||||
|
e.p.z.SetInt64(1)
|
||||||
|
e.p.t.SetInt64(1)
|
||||||
|
|
||||||
|
if !e.p.IsOnCurve() {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return e, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// G2 is an abstract cyclic group. The zero value is suitable for use as the
|
||||||
|
// output of an operation, but cannot be used as an input.
|
||||||
|
type G2 struct {
|
||||||
|
p *twistPoint
|
||||||
|
}
|
||||||
|
|
||||||
|
// RandomG1 returns x and g₂ˣ where x is a random, non-zero number read from r.
|
||||||
|
func RandomG2(r io.Reader) (*big.Int, *G2, error) {
|
||||||
|
var k *big.Int
|
||||||
|
var err error
|
||||||
|
|
||||||
|
for {
|
||||||
|
k, err = rand.Int(r, Order)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if k.Sign() > 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return k, new(G2).ScalarBaseMult(k), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *G2) String() string {
|
||||||
|
return "bn256.G2" + g.p.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScalarBaseMult sets e to g*k where g is the generator of the group and
|
||||||
|
// then returns out.
|
||||||
|
func (e *G2) ScalarBaseMult(k *big.Int) *G2 {
|
||||||
|
if e.p == nil {
|
||||||
|
e.p = newTwistPoint(nil)
|
||||||
|
}
|
||||||
|
e.p.Mul(twistGen, k, new(bnPool))
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScalarMult sets e to a*k and then returns e.
|
||||||
|
func (e *G2) ScalarMult(a *G2, k *big.Int) *G2 {
|
||||||
|
if e.p == nil {
|
||||||
|
e.p = newTwistPoint(nil)
|
||||||
|
}
|
||||||
|
e.p.Mul(a.p, k, new(bnPool))
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add sets e to a+b and then returns e.
|
||||||
|
// BUG(agl): this function is not complete: a==b fails.
|
||||||
|
func (e *G2) Add(a, b *G2) *G2 {
|
||||||
|
if e.p == nil {
|
||||||
|
e.p = newTwistPoint(nil)
|
||||||
|
}
|
||||||
|
e.p.Add(a.p, b.p, new(bnPool))
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal converts n into a byte slice.
|
||||||
|
func (n *G2) Marshal() []byte {
|
||||||
|
n.p.MakeAffine(nil)
|
||||||
|
|
||||||
|
xxBytes := new(big.Int).Mod(n.p.x.x, p).Bytes()
|
||||||
|
xyBytes := new(big.Int).Mod(n.p.x.y, p).Bytes()
|
||||||
|
yxBytes := new(big.Int).Mod(n.p.y.x, p).Bytes()
|
||||||
|
yyBytes := new(big.Int).Mod(n.p.y.y, p).Bytes()
|
||||||
|
|
||||||
|
// Each value is a 256-bit number.
|
||||||
|
const numBytes = 256 / 8
|
||||||
|
|
||||||
|
ret := make([]byte, numBytes*4)
|
||||||
|
copy(ret[1*numBytes-len(xxBytes):], xxBytes)
|
||||||
|
copy(ret[2*numBytes-len(xyBytes):], xyBytes)
|
||||||
|
copy(ret[3*numBytes-len(yxBytes):], yxBytes)
|
||||||
|
copy(ret[4*numBytes-len(yyBytes):], yyBytes)
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal sets e to the result of converting the output of Marshal back into
|
||||||
|
// a group element and then returns e.
|
||||||
|
func (e *G2) Unmarshal(m []byte) (*G2, bool) {
|
||||||
|
// Each value is a 256-bit number.
|
||||||
|
const numBytes = 256 / 8
|
||||||
|
|
||||||
|
if len(m) != 4*numBytes {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.p == nil {
|
||||||
|
e.p = newTwistPoint(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
e.p.x.x.SetBytes(m[0*numBytes : 1*numBytes])
|
||||||
|
e.p.x.y.SetBytes(m[1*numBytes : 2*numBytes])
|
||||||
|
e.p.y.x.SetBytes(m[2*numBytes : 3*numBytes])
|
||||||
|
e.p.y.y.SetBytes(m[3*numBytes : 4*numBytes])
|
||||||
|
|
||||||
|
if e.p.x.x.Sign() == 0 &&
|
||||||
|
e.p.x.y.Sign() == 0 &&
|
||||||
|
e.p.y.x.Sign() == 0 &&
|
||||||
|
e.p.y.y.Sign() == 0 {
|
||||||
|
// This is the point at infinity.
|
||||||
|
e.p.y.SetOne()
|
||||||
|
e.p.z.SetZero()
|
||||||
|
e.p.t.SetZero()
|
||||||
|
} else {
|
||||||
|
e.p.z.SetOne()
|
||||||
|
e.p.t.SetOne()
|
||||||
|
|
||||||
|
if !e.p.IsOnCurve() {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return e, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// GT is an abstract cyclic group. The zero value is suitable for use as the
|
||||||
|
// output of an operation, but cannot be used as an input.
|
||||||
|
type GT struct {
|
||||||
|
p *gfP12
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GT) String() string {
|
||||||
|
return "bn256.GT" + g.p.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScalarMult sets e to a*k and then returns e.
|
||||||
|
func (e *GT) ScalarMult(a *GT, k *big.Int) *GT {
|
||||||
|
if e.p == nil {
|
||||||
|
e.p = newGFp12(nil)
|
||||||
|
}
|
||||||
|
e.p.Exp(a.p, k, new(bnPool))
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add sets e to a+b and then returns e.
|
||||||
|
func (e *GT) Add(a, b *GT) *GT {
|
||||||
|
if e.p == nil {
|
||||||
|
e.p = newGFp12(nil)
|
||||||
|
}
|
||||||
|
e.p.Mul(a.p, b.p, new(bnPool))
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// Neg sets e to -a and then returns e.
|
||||||
|
func (e *GT) Neg(a *GT) *GT {
|
||||||
|
if e.p == nil {
|
||||||
|
e.p = newGFp12(nil)
|
||||||
|
}
|
||||||
|
e.p.Invert(a.p, new(bnPool))
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal converts n into a byte slice.
|
||||||
|
func (n *GT) Marshal() []byte {
|
||||||
|
n.p.Minimal()
|
||||||
|
|
||||||
|
xxxBytes := n.p.x.x.x.Bytes()
|
||||||
|
xxyBytes := n.p.x.x.y.Bytes()
|
||||||
|
xyxBytes := n.p.x.y.x.Bytes()
|
||||||
|
xyyBytes := n.p.x.y.y.Bytes()
|
||||||
|
xzxBytes := n.p.x.z.x.Bytes()
|
||||||
|
xzyBytes := n.p.x.z.y.Bytes()
|
||||||
|
yxxBytes := n.p.y.x.x.Bytes()
|
||||||
|
yxyBytes := n.p.y.x.y.Bytes()
|
||||||
|
yyxBytes := n.p.y.y.x.Bytes()
|
||||||
|
yyyBytes := n.p.y.y.y.Bytes()
|
||||||
|
yzxBytes := n.p.y.z.x.Bytes()
|
||||||
|
yzyBytes := n.p.y.z.y.Bytes()
|
||||||
|
|
||||||
|
// Each value is a 256-bit number.
|
||||||
|
const numBytes = 256 / 8
|
||||||
|
|
||||||
|
ret := make([]byte, numBytes*12)
|
||||||
|
copy(ret[1*numBytes-len(xxxBytes):], xxxBytes)
|
||||||
|
copy(ret[2*numBytes-len(xxyBytes):], xxyBytes)
|
||||||
|
copy(ret[3*numBytes-len(xyxBytes):], xyxBytes)
|
||||||
|
copy(ret[4*numBytes-len(xyyBytes):], xyyBytes)
|
||||||
|
copy(ret[5*numBytes-len(xzxBytes):], xzxBytes)
|
||||||
|
copy(ret[6*numBytes-len(xzyBytes):], xzyBytes)
|
||||||
|
copy(ret[7*numBytes-len(yxxBytes):], yxxBytes)
|
||||||
|
copy(ret[8*numBytes-len(yxyBytes):], yxyBytes)
|
||||||
|
copy(ret[9*numBytes-len(yyxBytes):], yyxBytes)
|
||||||
|
copy(ret[10*numBytes-len(yyyBytes):], yyyBytes)
|
||||||
|
copy(ret[11*numBytes-len(yzxBytes):], yzxBytes)
|
||||||
|
copy(ret[12*numBytes-len(yzyBytes):], yzyBytes)
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal sets e to the result of converting the output of Marshal back into
|
||||||
|
// a group element and then returns e.
|
||||||
|
func (e *GT) Unmarshal(m []byte) (*GT, bool) {
|
||||||
|
// Each value is a 256-bit number.
|
||||||
|
const numBytes = 256 / 8
|
||||||
|
|
||||||
|
if len(m) != 12*numBytes {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.p == nil {
|
||||||
|
e.p = newGFp12(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
e.p.x.x.x.SetBytes(m[0*numBytes : 1*numBytes])
|
||||||
|
e.p.x.x.y.SetBytes(m[1*numBytes : 2*numBytes])
|
||||||
|
e.p.x.y.x.SetBytes(m[2*numBytes : 3*numBytes])
|
||||||
|
e.p.x.y.y.SetBytes(m[3*numBytes : 4*numBytes])
|
||||||
|
e.p.x.z.x.SetBytes(m[4*numBytes : 5*numBytes])
|
||||||
|
e.p.x.z.y.SetBytes(m[5*numBytes : 6*numBytes])
|
||||||
|
e.p.y.x.x.SetBytes(m[6*numBytes : 7*numBytes])
|
||||||
|
e.p.y.x.y.SetBytes(m[7*numBytes : 8*numBytes])
|
||||||
|
e.p.y.y.x.SetBytes(m[8*numBytes : 9*numBytes])
|
||||||
|
e.p.y.y.y.SetBytes(m[9*numBytes : 10*numBytes])
|
||||||
|
e.p.y.z.x.SetBytes(m[10*numBytes : 11*numBytes])
|
||||||
|
e.p.y.z.y.SetBytes(m[11*numBytes : 12*numBytes])
|
||||||
|
|
||||||
|
return e, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pair calculates an Optimal Ate pairing.
|
||||||
|
func Pair(g1 *G1, g2 *G2) *GT {
|
||||||
|
return >{optimalAte(g2.p, g1.p, new(bnPool))}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bnPool implements a tiny cache of *big.Int objects that's used to reduce the
|
||||||
|
// number of allocations made during processing.
|
||||||
|
type bnPool struct {
|
||||||
|
bns []*big.Int
|
||||||
|
count int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pool *bnPool) Get() *big.Int {
|
||||||
|
if pool == nil {
|
||||||
|
return new(big.Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
pool.count++
|
||||||
|
l := len(pool.bns)
|
||||||
|
if l == 0 {
|
||||||
|
return new(big.Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
bn := pool.bns[l-1]
|
||||||
|
pool.bns = pool.bns[:l-1]
|
||||||
|
return bn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pool *bnPool) Put(bn *big.Int) {
|
||||||
|
if pool == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pool.bns = append(pool.bns, bn)
|
||||||
|
pool.count--
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pool *bnPool) Count() int {
|
||||||
|
return pool.count
|
||||||
|
}
|
||||||
+304
@@ -0,0 +1,304 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package bn256
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGFp2Invert(t *testing.T) {
|
||||||
|
pool := new(bnPool)
|
||||||
|
|
||||||
|
a := newGFp2(pool)
|
||||||
|
a.x.SetString("23423492374", 10)
|
||||||
|
a.y.SetString("12934872398472394827398470", 10)
|
||||||
|
|
||||||
|
inv := newGFp2(pool)
|
||||||
|
inv.Invert(a, pool)
|
||||||
|
|
||||||
|
b := newGFp2(pool).Mul(inv, a, pool)
|
||||||
|
if b.x.Int64() != 0 || b.y.Int64() != 1 {
|
||||||
|
t.Fatalf("bad result for a^-1*a: %s %s", b.x, b.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
a.Put(pool)
|
||||||
|
b.Put(pool)
|
||||||
|
inv.Put(pool)
|
||||||
|
|
||||||
|
if c := pool.Count(); c > 0 {
|
||||||
|
t.Errorf("Pool count non-zero: %d\n", c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isZero(n *big.Int) bool {
|
||||||
|
return new(big.Int).Mod(n, p).Int64() == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func isOne(n *big.Int) bool {
|
||||||
|
return new(big.Int).Mod(n, p).Int64() == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGFp6Invert(t *testing.T) {
|
||||||
|
pool := new(bnPool)
|
||||||
|
|
||||||
|
a := newGFp6(pool)
|
||||||
|
a.x.x.SetString("239487238491", 10)
|
||||||
|
a.x.y.SetString("2356249827341", 10)
|
||||||
|
a.y.x.SetString("082659782", 10)
|
||||||
|
a.y.y.SetString("182703523765", 10)
|
||||||
|
a.z.x.SetString("978236549263", 10)
|
||||||
|
a.z.y.SetString("64893242", 10)
|
||||||
|
|
||||||
|
inv := newGFp6(pool)
|
||||||
|
inv.Invert(a, pool)
|
||||||
|
|
||||||
|
b := newGFp6(pool).Mul(inv, a, pool)
|
||||||
|
if !isZero(b.x.x) ||
|
||||||
|
!isZero(b.x.y) ||
|
||||||
|
!isZero(b.y.x) ||
|
||||||
|
!isZero(b.y.y) ||
|
||||||
|
!isZero(b.z.x) ||
|
||||||
|
!isOne(b.z.y) {
|
||||||
|
t.Fatalf("bad result for a^-1*a: %s", b)
|
||||||
|
}
|
||||||
|
|
||||||
|
a.Put(pool)
|
||||||
|
b.Put(pool)
|
||||||
|
inv.Put(pool)
|
||||||
|
|
||||||
|
if c := pool.Count(); c > 0 {
|
||||||
|
t.Errorf("Pool count non-zero: %d\n", c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGFp12Invert(t *testing.T) {
|
||||||
|
pool := new(bnPool)
|
||||||
|
|
||||||
|
a := newGFp12(pool)
|
||||||
|
a.x.x.x.SetString("239846234862342323958623", 10)
|
||||||
|
a.x.x.y.SetString("2359862352529835623", 10)
|
||||||
|
a.x.y.x.SetString("928836523", 10)
|
||||||
|
a.x.y.y.SetString("9856234", 10)
|
||||||
|
a.x.z.x.SetString("235635286", 10)
|
||||||
|
a.x.z.y.SetString("5628392833", 10)
|
||||||
|
a.y.x.x.SetString("252936598265329856238956532167968", 10)
|
||||||
|
a.y.x.y.SetString("23596239865236954178968", 10)
|
||||||
|
a.y.y.x.SetString("95421692834", 10)
|
||||||
|
a.y.y.y.SetString("236548", 10)
|
||||||
|
a.y.z.x.SetString("924523", 10)
|
||||||
|
a.y.z.y.SetString("12954623", 10)
|
||||||
|
|
||||||
|
inv := newGFp12(pool)
|
||||||
|
inv.Invert(a, pool)
|
||||||
|
|
||||||
|
b := newGFp12(pool).Mul(inv, a, pool)
|
||||||
|
if !isZero(b.x.x.x) ||
|
||||||
|
!isZero(b.x.x.y) ||
|
||||||
|
!isZero(b.x.y.x) ||
|
||||||
|
!isZero(b.x.y.y) ||
|
||||||
|
!isZero(b.x.z.x) ||
|
||||||
|
!isZero(b.x.z.y) ||
|
||||||
|
!isZero(b.y.x.x) ||
|
||||||
|
!isZero(b.y.x.y) ||
|
||||||
|
!isZero(b.y.y.x) ||
|
||||||
|
!isZero(b.y.y.y) ||
|
||||||
|
!isZero(b.y.z.x) ||
|
||||||
|
!isOne(b.y.z.y) {
|
||||||
|
t.Fatalf("bad result for a^-1*a: %s", b)
|
||||||
|
}
|
||||||
|
|
||||||
|
a.Put(pool)
|
||||||
|
b.Put(pool)
|
||||||
|
inv.Put(pool)
|
||||||
|
|
||||||
|
if c := pool.Count(); c > 0 {
|
||||||
|
t.Errorf("Pool count non-zero: %d\n", c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCurveImpl(t *testing.T) {
|
||||||
|
pool := new(bnPool)
|
||||||
|
|
||||||
|
g := &curvePoint{
|
||||||
|
pool.Get().SetInt64(1),
|
||||||
|
pool.Get().SetInt64(-2),
|
||||||
|
pool.Get().SetInt64(1),
|
||||||
|
pool.Get().SetInt64(0),
|
||||||
|
}
|
||||||
|
|
||||||
|
x := pool.Get().SetInt64(32498273234)
|
||||||
|
X := newCurvePoint(pool).Mul(g, x, pool)
|
||||||
|
|
||||||
|
y := pool.Get().SetInt64(98732423523)
|
||||||
|
Y := newCurvePoint(pool).Mul(g, y, pool)
|
||||||
|
|
||||||
|
s1 := newCurvePoint(pool).Mul(X, y, pool).MakeAffine(pool)
|
||||||
|
s2 := newCurvePoint(pool).Mul(Y, x, pool).MakeAffine(pool)
|
||||||
|
|
||||||
|
if s1.x.Cmp(s2.x) != 0 ||
|
||||||
|
s2.x.Cmp(s1.x) != 0 {
|
||||||
|
t.Errorf("DH points don't match: (%s, %s) (%s, %s)", s1.x, s1.y, s2.x, s2.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
pool.Put(x)
|
||||||
|
X.Put(pool)
|
||||||
|
pool.Put(y)
|
||||||
|
Y.Put(pool)
|
||||||
|
s1.Put(pool)
|
||||||
|
s2.Put(pool)
|
||||||
|
g.Put(pool)
|
||||||
|
|
||||||
|
if c := pool.Count(); c > 0 {
|
||||||
|
t.Errorf("Pool count non-zero: %d\n", c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOrderG1(t *testing.T) {
|
||||||
|
g := new(G1).ScalarBaseMult(Order)
|
||||||
|
if !g.p.IsInfinity() {
|
||||||
|
t.Error("G1 has incorrect order")
|
||||||
|
}
|
||||||
|
|
||||||
|
one := new(G1).ScalarBaseMult(new(big.Int).SetInt64(1))
|
||||||
|
g.Add(g, one)
|
||||||
|
g.p.MakeAffine(nil)
|
||||||
|
if g.p.x.Cmp(one.p.x) != 0 || g.p.y.Cmp(one.p.y) != 0 {
|
||||||
|
t.Errorf("1+0 != 1 in G1")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOrderG2(t *testing.T) {
|
||||||
|
g := new(G2).ScalarBaseMult(Order)
|
||||||
|
if !g.p.IsInfinity() {
|
||||||
|
t.Error("G2 has incorrect order")
|
||||||
|
}
|
||||||
|
|
||||||
|
one := new(G2).ScalarBaseMult(new(big.Int).SetInt64(1))
|
||||||
|
g.Add(g, one)
|
||||||
|
g.p.MakeAffine(nil)
|
||||||
|
if g.p.x.x.Cmp(one.p.x.x) != 0 ||
|
||||||
|
g.p.x.y.Cmp(one.p.x.y) != 0 ||
|
||||||
|
g.p.y.x.Cmp(one.p.y.x) != 0 ||
|
||||||
|
g.p.y.y.Cmp(one.p.y.y) != 0 {
|
||||||
|
t.Errorf("1+0 != 1 in G2")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOrderGT(t *testing.T) {
|
||||||
|
gt := Pair(&G1{curveGen}, &G2{twistGen})
|
||||||
|
g := new(GT).ScalarMult(gt, Order)
|
||||||
|
if !g.p.IsOne() {
|
||||||
|
t.Error("GT has incorrect order")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBilinearity(t *testing.T) {
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
a, p1, _ := RandomG1(rand.Reader)
|
||||||
|
b, p2, _ := RandomG2(rand.Reader)
|
||||||
|
e1 := Pair(p1, p2)
|
||||||
|
|
||||||
|
e2 := Pair(&G1{curveGen}, &G2{twistGen})
|
||||||
|
e2.ScalarMult(e2, a)
|
||||||
|
e2.ScalarMult(e2, b)
|
||||||
|
|
||||||
|
minusE2 := new(GT).Neg(e2)
|
||||||
|
e1.Add(e1, minusE2)
|
||||||
|
|
||||||
|
if !e1.p.IsOne() {
|
||||||
|
t.Fatalf("bad pairing result: %s", e1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestG1Marshal(t *testing.T) {
|
||||||
|
g := new(G1).ScalarBaseMult(new(big.Int).SetInt64(1))
|
||||||
|
form := g.Marshal()
|
||||||
|
_, ok := new(G1).Unmarshal(form)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("failed to unmarshal")
|
||||||
|
}
|
||||||
|
|
||||||
|
g.ScalarBaseMult(Order)
|
||||||
|
form = g.Marshal()
|
||||||
|
g2, ok := new(G1).Unmarshal(form)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("failed to unmarshal ∞")
|
||||||
|
}
|
||||||
|
if !g2.p.IsInfinity() {
|
||||||
|
t.Fatalf("∞ unmarshaled incorrectly")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestG2Marshal(t *testing.T) {
|
||||||
|
g := new(G2).ScalarBaseMult(new(big.Int).SetInt64(1))
|
||||||
|
form := g.Marshal()
|
||||||
|
_, ok := new(G2).Unmarshal(form)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("failed to unmarshal")
|
||||||
|
}
|
||||||
|
|
||||||
|
g.ScalarBaseMult(Order)
|
||||||
|
form = g.Marshal()
|
||||||
|
g2, ok := new(G2).Unmarshal(form)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("failed to unmarshal ∞")
|
||||||
|
}
|
||||||
|
if !g2.p.IsInfinity() {
|
||||||
|
t.Fatalf("∞ unmarshaled incorrectly")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestG1Identity(t *testing.T) {
|
||||||
|
g := new(G1).ScalarBaseMult(new(big.Int).SetInt64(0))
|
||||||
|
if !g.p.IsInfinity() {
|
||||||
|
t.Error("failure")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestG2Identity(t *testing.T) {
|
||||||
|
g := new(G2).ScalarBaseMult(new(big.Int).SetInt64(0))
|
||||||
|
if !g.p.IsInfinity() {
|
||||||
|
t.Error("failure")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTripartiteDiffieHellman(t *testing.T) {
|
||||||
|
a, _ := rand.Int(rand.Reader, Order)
|
||||||
|
b, _ := rand.Int(rand.Reader, Order)
|
||||||
|
c, _ := rand.Int(rand.Reader, Order)
|
||||||
|
|
||||||
|
pa, _ := new(G1).Unmarshal(new(G1).ScalarBaseMult(a).Marshal())
|
||||||
|
qa, _ := new(G2).Unmarshal(new(G2).ScalarBaseMult(a).Marshal())
|
||||||
|
pb, _ := new(G1).Unmarshal(new(G1).ScalarBaseMult(b).Marshal())
|
||||||
|
qb, _ := new(G2).Unmarshal(new(G2).ScalarBaseMult(b).Marshal())
|
||||||
|
pc, _ := new(G1).Unmarshal(new(G1).ScalarBaseMult(c).Marshal())
|
||||||
|
qc, _ := new(G2).Unmarshal(new(G2).ScalarBaseMult(c).Marshal())
|
||||||
|
|
||||||
|
k1 := Pair(pb, qc)
|
||||||
|
k1.ScalarMult(k1, a)
|
||||||
|
k1Bytes := k1.Marshal()
|
||||||
|
|
||||||
|
k2 := Pair(pc, qa)
|
||||||
|
k2.ScalarMult(k2, b)
|
||||||
|
k2Bytes := k2.Marshal()
|
||||||
|
|
||||||
|
k3 := Pair(pa, qb)
|
||||||
|
k3.ScalarMult(k3, c)
|
||||||
|
k3Bytes := k3.Marshal()
|
||||||
|
|
||||||
|
if !bytes.Equal(k1Bytes, k2Bytes) || !bytes.Equal(k2Bytes, k3Bytes) {
|
||||||
|
t.Errorf("keys didn't agree")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkPairing(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
Pair(&G1{curveGen}, &G2{twistGen})
|
||||||
|
}
|
||||||
|
}
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package bn256
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
func bigFromBase10(s string) *big.Int {
|
||||||
|
n, _ := new(big.Int).SetString(s, 10)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// u is the BN parameter that determines the prime: 1868033³.
|
||||||
|
var u = bigFromBase10("6518589491078791937")
|
||||||
|
|
||||||
|
// p is a prime over which we form a basic field: 36u⁴+36u³+24u²+6u+1.
|
||||||
|
var p = bigFromBase10("65000549695646603732796438742359905742825358107623003571877145026864184071783")
|
||||||
|
|
||||||
|
// Order is the number of elements in both G₁ and G₂: 36u⁴+36u³+18u²+6u+1.
|
||||||
|
var Order = bigFromBase10("65000549695646603732796438742359905742570406053903786389881062969044166799969")
|
||||||
|
|
||||||
|
// xiToPMinus1Over6 is ξ^((p-1)/6) where ξ = i+3.
|
||||||
|
var xiToPMinus1Over6 = &gfP2{bigFromBase10("8669379979083712429711189836753509758585994370025260553045152614783263110636"), bigFromBase10("19998038925833620163537568958541907098007303196759855091367510456613536016040")}
|
||||||
|
|
||||||
|
// xiToPMinus1Over3 is ξ^((p-1)/3) where ξ = i+3.
|
||||||
|
var xiToPMinus1Over3 = &gfP2{bigFromBase10("26098034838977895781559542626833399156321265654106457577426020397262786167059"), bigFromBase10("15931493369629630809226283458085260090334794394361662678240713231519278691715")}
|
||||||
|
|
||||||
|
// xiToPMinus1Over2 is ξ^((p-1)/2) where ξ = i+3.
|
||||||
|
var xiToPMinus1Over2 = &gfP2{bigFromBase10("50997318142241922852281555961173165965672272825141804376761836765206060036244"), bigFromBase10("38665955945962842195025998234511023902832543644254935982879660597356748036009")}
|
||||||
|
|
||||||
|
// xiToPSquaredMinus1Over3 is ξ^((p²-1)/3) where ξ = i+3.
|
||||||
|
var xiToPSquaredMinus1Over3 = bigFromBase10("65000549695646603727810655408050771481677621702948236658134783353303381437752")
|
||||||
|
|
||||||
|
// xiTo2PSquaredMinus2Over3 is ξ^((2p²-2)/3) where ξ = i+3 (a cubic root of unity, mod p).
|
||||||
|
var xiTo2PSquaredMinus2Over3 = bigFromBase10("4985783334309134261147736404674766913742361673560802634030")
|
||||||
|
|
||||||
|
// xiToPSquaredMinus1Over6 is ξ^((1p²-1)/6) where ξ = i+3 (a cubic root of -1, mod p).
|
||||||
|
var xiToPSquaredMinus1Over6 = bigFromBase10("65000549695646603727810655408050771481677621702948236658134783353303381437753")
|
||||||
|
|
||||||
|
// xiTo2PMinus2Over3 is ξ^((2p-2)/3) where ξ = i+3.
|
||||||
|
var xiTo2PMinus2Over3 = &gfP2{bigFromBase10("19885131339612776214803633203834694332692106372356013117629940868870585019582"), bigFromBase10("21645619881471562101905880913352894726728173167203616652430647841922248593627")}
|
||||||
+278
@@ -0,0 +1,278 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package bn256
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
// curvePoint implements the elliptic curve y²=x³+3. Points are kept in
|
||||||
|
// Jacobian form and t=z² when valid. G₁ is the set of points of this curve on
|
||||||
|
// GF(p).
|
||||||
|
type curvePoint struct {
|
||||||
|
x, y, z, t *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
var curveB = new(big.Int).SetInt64(3)
|
||||||
|
|
||||||
|
// curveGen is the generator of G₁.
|
||||||
|
var curveGen = &curvePoint{
|
||||||
|
new(big.Int).SetInt64(1),
|
||||||
|
new(big.Int).SetInt64(-2),
|
||||||
|
new(big.Int).SetInt64(1),
|
||||||
|
new(big.Int).SetInt64(1),
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCurvePoint(pool *bnPool) *curvePoint {
|
||||||
|
return &curvePoint{
|
||||||
|
pool.Get(),
|
||||||
|
pool.Get(),
|
||||||
|
pool.Get(),
|
||||||
|
pool.Get(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *curvePoint) String() string {
|
||||||
|
c.MakeAffine(new(bnPool))
|
||||||
|
return "(" + c.x.String() + ", " + c.y.String() + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *curvePoint) Put(pool *bnPool) {
|
||||||
|
pool.Put(c.x)
|
||||||
|
pool.Put(c.y)
|
||||||
|
pool.Put(c.z)
|
||||||
|
pool.Put(c.t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *curvePoint) Set(a *curvePoint) {
|
||||||
|
c.x.Set(a.x)
|
||||||
|
c.y.Set(a.y)
|
||||||
|
c.z.Set(a.z)
|
||||||
|
c.t.Set(a.t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsOnCurve returns true iff c is on the curve where c must be in affine form.
|
||||||
|
func (c *curvePoint) IsOnCurve() bool {
|
||||||
|
yy := new(big.Int).Mul(c.y, c.y)
|
||||||
|
xxx := new(big.Int).Mul(c.x, c.x)
|
||||||
|
xxx.Mul(xxx, c.x)
|
||||||
|
yy.Sub(yy, xxx)
|
||||||
|
yy.Sub(yy, curveB)
|
||||||
|
if yy.Sign() < 0 || yy.Cmp(p) >= 0 {
|
||||||
|
yy.Mod(yy, p)
|
||||||
|
}
|
||||||
|
return yy.Sign() == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *curvePoint) SetInfinity() {
|
||||||
|
c.z.SetInt64(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *curvePoint) IsInfinity() bool {
|
||||||
|
return c.z.Sign() == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *curvePoint) Add(a, b *curvePoint, pool *bnPool) {
|
||||||
|
if a.IsInfinity() {
|
||||||
|
c.Set(b)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if b.IsInfinity() {
|
||||||
|
c.Set(a)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// See http://hyperelliptic.org/EFD/g1p/auto-code/shortw/jacobian-0/addition/add-2007-bl.op3
|
||||||
|
|
||||||
|
// Normalize the points by replacing a = [x1:y1:z1] and b = [x2:y2:z2]
|
||||||
|
// by [u1:s1:z1·z2] and [u2:s2:z1·z2]
|
||||||
|
// where u1 = x1·z2², s1 = y1·z2³ and u1 = x2·z1², s2 = y2·z1³
|
||||||
|
z1z1 := pool.Get().Mul(a.z, a.z)
|
||||||
|
z1z1.Mod(z1z1, p)
|
||||||
|
z2z2 := pool.Get().Mul(b.z, b.z)
|
||||||
|
z2z2.Mod(z2z2, p)
|
||||||
|
u1 := pool.Get().Mul(a.x, z2z2)
|
||||||
|
u1.Mod(u1, p)
|
||||||
|
u2 := pool.Get().Mul(b.x, z1z1)
|
||||||
|
u2.Mod(u2, p)
|
||||||
|
|
||||||
|
t := pool.Get().Mul(b.z, z2z2)
|
||||||
|
t.Mod(t, p)
|
||||||
|
s1 := pool.Get().Mul(a.y, t)
|
||||||
|
s1.Mod(s1, p)
|
||||||
|
|
||||||
|
t.Mul(a.z, z1z1)
|
||||||
|
t.Mod(t, p)
|
||||||
|
s2 := pool.Get().Mul(b.y, t)
|
||||||
|
s2.Mod(s2, p)
|
||||||
|
|
||||||
|
// Compute x = (2h)²(s²-u1-u2)
|
||||||
|
// where s = (s2-s1)/(u2-u1) is the slope of the line through
|
||||||
|
// (u1,s1) and (u2,s2). The extra factor 2h = 2(u2-u1) comes from the value of z below.
|
||||||
|
// This is also:
|
||||||
|
// 4(s2-s1)² - 4h²(u1+u2) = 4(s2-s1)² - 4h³ - 4h²(2u1)
|
||||||
|
// = r² - j - 2v
|
||||||
|
// with the notations below.
|
||||||
|
h := pool.Get().Sub(u2, u1)
|
||||||
|
xEqual := h.Sign() == 0
|
||||||
|
|
||||||
|
t.Add(h, h)
|
||||||
|
// i = 4h²
|
||||||
|
i := pool.Get().Mul(t, t)
|
||||||
|
i.Mod(i, p)
|
||||||
|
// j = 4h³
|
||||||
|
j := pool.Get().Mul(h, i)
|
||||||
|
j.Mod(j, p)
|
||||||
|
|
||||||
|
t.Sub(s2, s1)
|
||||||
|
yEqual := t.Sign() == 0
|
||||||
|
if xEqual && yEqual {
|
||||||
|
c.Double(a, pool)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r := pool.Get().Add(t, t)
|
||||||
|
|
||||||
|
v := pool.Get().Mul(u1, i)
|
||||||
|
v.Mod(v, p)
|
||||||
|
|
||||||
|
// t4 = 4(s2-s1)²
|
||||||
|
t4 := pool.Get().Mul(r, r)
|
||||||
|
t4.Mod(t4, p)
|
||||||
|
t.Add(v, v)
|
||||||
|
t6 := pool.Get().Sub(t4, j)
|
||||||
|
c.x.Sub(t6, t)
|
||||||
|
|
||||||
|
// Set y = -(2h)³(s1 + s*(x/4h²-u1))
|
||||||
|
// This is also
|
||||||
|
// y = - 2·s1·j - (s2-s1)(2x - 2i·u1) = r(v-x) - 2·s1·j
|
||||||
|
t.Sub(v, c.x) // t7
|
||||||
|
t4.Mul(s1, j) // t8
|
||||||
|
t4.Mod(t4, p)
|
||||||
|
t6.Add(t4, t4) // t9
|
||||||
|
t4.Mul(r, t) // t10
|
||||||
|
t4.Mod(t4, p)
|
||||||
|
c.y.Sub(t4, t6)
|
||||||
|
|
||||||
|
// Set z = 2(u2-u1)·z1·z2 = 2h·z1·z2
|
||||||
|
t.Add(a.z, b.z) // t11
|
||||||
|
t4.Mul(t, t) // t12
|
||||||
|
t4.Mod(t4, p)
|
||||||
|
t.Sub(t4, z1z1) // t13
|
||||||
|
t4.Sub(t, z2z2) // t14
|
||||||
|
c.z.Mul(t4, h)
|
||||||
|
c.z.Mod(c.z, p)
|
||||||
|
|
||||||
|
pool.Put(z1z1)
|
||||||
|
pool.Put(z2z2)
|
||||||
|
pool.Put(u1)
|
||||||
|
pool.Put(u2)
|
||||||
|
pool.Put(t)
|
||||||
|
pool.Put(s1)
|
||||||
|
pool.Put(s2)
|
||||||
|
pool.Put(h)
|
||||||
|
pool.Put(i)
|
||||||
|
pool.Put(j)
|
||||||
|
pool.Put(r)
|
||||||
|
pool.Put(v)
|
||||||
|
pool.Put(t4)
|
||||||
|
pool.Put(t6)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *curvePoint) Double(a *curvePoint, pool *bnPool) {
|
||||||
|
// See http://hyperelliptic.org/EFD/g1p/auto-code/shortw/jacobian-0/doubling/dbl-2009-l.op3
|
||||||
|
A := pool.Get().Mul(a.x, a.x)
|
||||||
|
A.Mod(A, p)
|
||||||
|
B := pool.Get().Mul(a.y, a.y)
|
||||||
|
B.Mod(B, p)
|
||||||
|
C := pool.Get().Mul(B, B)
|
||||||
|
C.Mod(C, p)
|
||||||
|
|
||||||
|
t := pool.Get().Add(a.x, B)
|
||||||
|
t2 := pool.Get().Mul(t, t)
|
||||||
|
t2.Mod(t2, p)
|
||||||
|
t.Sub(t2, A)
|
||||||
|
t2.Sub(t, C)
|
||||||
|
d := pool.Get().Add(t2, t2)
|
||||||
|
t.Add(A, A)
|
||||||
|
e := pool.Get().Add(t, A)
|
||||||
|
f := pool.Get().Mul(e, e)
|
||||||
|
f.Mod(f, p)
|
||||||
|
|
||||||
|
t.Add(d, d)
|
||||||
|
c.x.Sub(f, t)
|
||||||
|
|
||||||
|
t.Add(C, C)
|
||||||
|
t2.Add(t, t)
|
||||||
|
t.Add(t2, t2)
|
||||||
|
c.y.Sub(d, c.x)
|
||||||
|
t2.Mul(e, c.y)
|
||||||
|
t2.Mod(t2, p)
|
||||||
|
c.y.Sub(t2, t)
|
||||||
|
|
||||||
|
t.Mul(a.y, a.z)
|
||||||
|
t.Mod(t, p)
|
||||||
|
c.z.Add(t, t)
|
||||||
|
|
||||||
|
pool.Put(A)
|
||||||
|
pool.Put(B)
|
||||||
|
pool.Put(C)
|
||||||
|
pool.Put(t)
|
||||||
|
pool.Put(t2)
|
||||||
|
pool.Put(d)
|
||||||
|
pool.Put(e)
|
||||||
|
pool.Put(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *curvePoint) Mul(a *curvePoint, scalar *big.Int, pool *bnPool) *curvePoint {
|
||||||
|
sum := newCurvePoint(pool)
|
||||||
|
sum.SetInfinity()
|
||||||
|
t := newCurvePoint(pool)
|
||||||
|
|
||||||
|
for i := scalar.BitLen(); i >= 0; i-- {
|
||||||
|
t.Double(sum, pool)
|
||||||
|
if scalar.Bit(i) != 0 {
|
||||||
|
sum.Add(t, a, pool)
|
||||||
|
} else {
|
||||||
|
sum.Set(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Set(sum)
|
||||||
|
sum.Put(pool)
|
||||||
|
t.Put(pool)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *curvePoint) MakeAffine(pool *bnPool) *curvePoint {
|
||||||
|
if words := c.z.Bits(); len(words) == 1 && words[0] == 1 {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
zInv := pool.Get().ModInverse(c.z, p)
|
||||||
|
t := pool.Get().Mul(c.y, zInv)
|
||||||
|
t.Mod(t, p)
|
||||||
|
zInv2 := pool.Get().Mul(zInv, zInv)
|
||||||
|
zInv2.Mod(zInv2, p)
|
||||||
|
c.y.Mul(t, zInv2)
|
||||||
|
c.y.Mod(c.y, p)
|
||||||
|
t.Mul(c.x, zInv2)
|
||||||
|
t.Mod(t, p)
|
||||||
|
c.x.Set(t)
|
||||||
|
c.z.SetInt64(1)
|
||||||
|
c.t.SetInt64(1)
|
||||||
|
|
||||||
|
pool.Put(zInv)
|
||||||
|
pool.Put(t)
|
||||||
|
pool.Put(zInv2)
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *curvePoint) Negative(a *curvePoint) {
|
||||||
|
c.x.Set(a.x)
|
||||||
|
c.y.Neg(a.y)
|
||||||
|
c.z.Set(a.z)
|
||||||
|
c.t.SetInt64(0)
|
||||||
|
}
|
||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package bn256
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExamplePair() {
|
||||||
|
// This implements the tripartite Diffie-Hellman algorithm from "A One
|
||||||
|
// Round Protocol for Tripartite Diffie-Hellman", A. Joux.
|
||||||
|
// http://www.springerlink.com/content/cddc57yyva0hburb/fulltext.pdf
|
||||||
|
|
||||||
|
// Each of three parties, a, b and c, generate a private value.
|
||||||
|
a, _ := rand.Int(rand.Reader, Order)
|
||||||
|
b, _ := rand.Int(rand.Reader, Order)
|
||||||
|
c, _ := rand.Int(rand.Reader, Order)
|
||||||
|
|
||||||
|
// Then each party calculates g₁ and g₂ times their private value.
|
||||||
|
pa := new(G1).ScalarBaseMult(a)
|
||||||
|
qa := new(G2).ScalarBaseMult(a)
|
||||||
|
|
||||||
|
pb := new(G1).ScalarBaseMult(b)
|
||||||
|
qb := new(G2).ScalarBaseMult(b)
|
||||||
|
|
||||||
|
pc := new(G1).ScalarBaseMult(c)
|
||||||
|
qc := new(G2).ScalarBaseMult(c)
|
||||||
|
|
||||||
|
// Now each party exchanges its public values with the other two and
|
||||||
|
// all parties can calculate the shared key.
|
||||||
|
k1 := Pair(pb, qc)
|
||||||
|
k1.ScalarMult(k1, a)
|
||||||
|
|
||||||
|
k2 := Pair(pc, qa)
|
||||||
|
k2.ScalarMult(k2, b)
|
||||||
|
|
||||||
|
k3 := Pair(pa, qb)
|
||||||
|
k3.ScalarMult(k3, c)
|
||||||
|
|
||||||
|
// k1, k2 and k3 will all be equal.
|
||||||
|
}
|
||||||
+200
@@ -0,0 +1,200 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package bn256
|
||||||
|
|
||||||
|
// For details of the algorithms used, see "Multiplication and Squaring on
|
||||||
|
// Pairing-Friendly Fields, Devegili et al.
|
||||||
|
// http://eprint.iacr.org/2006/471.pdf.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
// gfP12 implements the field of size p¹² as a quadratic extension of gfP6
|
||||||
|
// where ω²=τ.
|
||||||
|
type gfP12 struct {
|
||||||
|
x, y *gfP6 // value is xω + y
|
||||||
|
}
|
||||||
|
|
||||||
|
func newGFp12(pool *bnPool) *gfP12 {
|
||||||
|
return &gfP12{newGFp6(pool), newGFp6(pool)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP12) String() string {
|
||||||
|
return "(" + e.x.String() + "," + e.y.String() + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP12) Put(pool *bnPool) {
|
||||||
|
e.x.Put(pool)
|
||||||
|
e.y.Put(pool)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP12) Set(a *gfP12) *gfP12 {
|
||||||
|
e.x.Set(a.x)
|
||||||
|
e.y.Set(a.y)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP12) SetZero() *gfP12 {
|
||||||
|
e.x.SetZero()
|
||||||
|
e.y.SetZero()
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP12) SetOne() *gfP12 {
|
||||||
|
e.x.SetZero()
|
||||||
|
e.y.SetOne()
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP12) Minimal() {
|
||||||
|
e.x.Minimal()
|
||||||
|
e.y.Minimal()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP12) IsZero() bool {
|
||||||
|
e.Minimal()
|
||||||
|
return e.x.IsZero() && e.y.IsZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP12) IsOne() bool {
|
||||||
|
e.Minimal()
|
||||||
|
return e.x.IsZero() && e.y.IsOne()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP12) Conjugate(a *gfP12) *gfP12 {
|
||||||
|
e.x.Negative(a.x)
|
||||||
|
e.y.Set(a.y)
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP12) Negative(a *gfP12) *gfP12 {
|
||||||
|
e.x.Negative(a.x)
|
||||||
|
e.y.Negative(a.y)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// Frobenius computes (xω+y)^p = x^p ω·ξ^((p-1)/6) + y^p
|
||||||
|
func (e *gfP12) Frobenius(a *gfP12, pool *bnPool) *gfP12 {
|
||||||
|
e.x.Frobenius(a.x, pool)
|
||||||
|
e.y.Frobenius(a.y, pool)
|
||||||
|
e.x.MulScalar(e.x, xiToPMinus1Over6, pool)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// FrobeniusP2 computes (xω+y)^p² = x^p² ω·ξ^((p²-1)/6) + y^p²
|
||||||
|
func (e *gfP12) FrobeniusP2(a *gfP12, pool *bnPool) *gfP12 {
|
||||||
|
e.x.FrobeniusP2(a.x)
|
||||||
|
e.x.MulGFP(e.x, xiToPSquaredMinus1Over6)
|
||||||
|
e.y.FrobeniusP2(a.y)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP12) Add(a, b *gfP12) *gfP12 {
|
||||||
|
e.x.Add(a.x, b.x)
|
||||||
|
e.y.Add(a.y, b.y)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP12) Sub(a, b *gfP12) *gfP12 {
|
||||||
|
e.x.Sub(a.x, b.x)
|
||||||
|
e.y.Sub(a.y, b.y)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP12) Mul(a, b *gfP12, pool *bnPool) *gfP12 {
|
||||||
|
tx := newGFp6(pool)
|
||||||
|
tx.Mul(a.x, b.y, pool)
|
||||||
|
t := newGFp6(pool)
|
||||||
|
t.Mul(b.x, a.y, pool)
|
||||||
|
tx.Add(tx, t)
|
||||||
|
|
||||||
|
ty := newGFp6(pool)
|
||||||
|
ty.Mul(a.y, b.y, pool)
|
||||||
|
t.Mul(a.x, b.x, pool)
|
||||||
|
t.MulTau(t, pool)
|
||||||
|
e.y.Add(ty, t)
|
||||||
|
e.x.Set(tx)
|
||||||
|
|
||||||
|
tx.Put(pool)
|
||||||
|
ty.Put(pool)
|
||||||
|
t.Put(pool)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP12) MulScalar(a *gfP12, b *gfP6, pool *bnPool) *gfP12 {
|
||||||
|
e.x.Mul(e.x, b, pool)
|
||||||
|
e.y.Mul(e.y, b, pool)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *gfP12) Exp(a *gfP12, power *big.Int, pool *bnPool) *gfP12 {
|
||||||
|
sum := newGFp12(pool)
|
||||||
|
sum.SetOne()
|
||||||
|
t := newGFp12(pool)
|
||||||
|
|
||||||
|
for i := power.BitLen() - 1; i >= 0; i-- {
|
||||||
|
t.Square(sum, pool)
|
||||||
|
if power.Bit(i) != 0 {
|
||||||
|
sum.Mul(t, a, pool)
|
||||||
|
} else {
|
||||||
|
sum.Set(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Set(sum)
|
||||||
|
|
||||||
|
sum.Put(pool)
|
||||||
|
t.Put(pool)
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP12) Square(a *gfP12, pool *bnPool) *gfP12 {
|
||||||
|
// Complex squaring algorithm
|
||||||
|
v0 := newGFp6(pool)
|
||||||
|
v0.Mul(a.x, a.y, pool)
|
||||||
|
|
||||||
|
t := newGFp6(pool)
|
||||||
|
t.MulTau(a.x, pool)
|
||||||
|
t.Add(a.y, t)
|
||||||
|
ty := newGFp6(pool)
|
||||||
|
ty.Add(a.x, a.y)
|
||||||
|
ty.Mul(ty, t, pool)
|
||||||
|
ty.Sub(ty, v0)
|
||||||
|
t.MulTau(v0, pool)
|
||||||
|
ty.Sub(ty, t)
|
||||||
|
|
||||||
|
e.y.Set(ty)
|
||||||
|
e.x.Double(v0)
|
||||||
|
|
||||||
|
v0.Put(pool)
|
||||||
|
t.Put(pool)
|
||||||
|
ty.Put(pool)
|
||||||
|
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP12) Invert(a *gfP12, pool *bnPool) *gfP12 {
|
||||||
|
// See "Implementing cryptographic pairings", M. Scott, section 3.2.
|
||||||
|
// ftp://136.206.11.249/pub/crypto/pairings.pdf
|
||||||
|
t1 := newGFp6(pool)
|
||||||
|
t2 := newGFp6(pool)
|
||||||
|
|
||||||
|
t1.Square(a.x, pool)
|
||||||
|
t2.Square(a.y, pool)
|
||||||
|
t1.MulTau(t1, pool)
|
||||||
|
t1.Sub(t2, t1)
|
||||||
|
t2.Invert(t1, pool)
|
||||||
|
|
||||||
|
e.x.Negative(a.x)
|
||||||
|
e.y.Set(a.y)
|
||||||
|
e.MulScalar(e, t2, pool)
|
||||||
|
|
||||||
|
t1.Put(pool)
|
||||||
|
t2.Put(pool)
|
||||||
|
|
||||||
|
return e
|
||||||
|
}
|
||||||
+219
@@ -0,0 +1,219 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package bn256
|
||||||
|
|
||||||
|
// For details of the algorithms used, see "Multiplication and Squaring on
|
||||||
|
// Pairing-Friendly Fields, Devegili et al.
|
||||||
|
// http://eprint.iacr.org/2006/471.pdf.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
// gfP2 implements a field of size p² as a quadratic extension of the base
|
||||||
|
// field where i²=-1.
|
||||||
|
type gfP2 struct {
|
||||||
|
x, y *big.Int // value is xi+y.
|
||||||
|
}
|
||||||
|
|
||||||
|
func newGFp2(pool *bnPool) *gfP2 {
|
||||||
|
return &gfP2{pool.Get(), pool.Get()}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP2) String() string {
|
||||||
|
x := new(big.Int).Mod(e.x, p)
|
||||||
|
y := new(big.Int).Mod(e.y, p)
|
||||||
|
return "(" + x.String() + "," + y.String() + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP2) Put(pool *bnPool) {
|
||||||
|
pool.Put(e.x)
|
||||||
|
pool.Put(e.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP2) Set(a *gfP2) *gfP2 {
|
||||||
|
e.x.Set(a.x)
|
||||||
|
e.y.Set(a.y)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP2) SetZero() *gfP2 {
|
||||||
|
e.x.SetInt64(0)
|
||||||
|
e.y.SetInt64(0)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP2) SetOne() *gfP2 {
|
||||||
|
e.x.SetInt64(0)
|
||||||
|
e.y.SetInt64(1)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP2) Minimal() {
|
||||||
|
if e.x.Sign() < 0 || e.x.Cmp(p) >= 0 {
|
||||||
|
e.x.Mod(e.x, p)
|
||||||
|
}
|
||||||
|
if e.y.Sign() < 0 || e.y.Cmp(p) >= 0 {
|
||||||
|
e.y.Mod(e.y, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP2) IsZero() bool {
|
||||||
|
return e.x.Sign() == 0 && e.y.Sign() == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP2) IsOne() bool {
|
||||||
|
if e.x.Sign() != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
words := e.y.Bits()
|
||||||
|
return len(words) == 1 && words[0] == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP2) Conjugate(a *gfP2) *gfP2 {
|
||||||
|
e.y.Set(a.y)
|
||||||
|
e.x.Neg(a.x)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP2) Negative(a *gfP2) *gfP2 {
|
||||||
|
e.x.Neg(a.x)
|
||||||
|
e.y.Neg(a.y)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP2) Add(a, b *gfP2) *gfP2 {
|
||||||
|
e.x.Add(a.x, b.x)
|
||||||
|
e.y.Add(a.y, b.y)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP2) Sub(a, b *gfP2) *gfP2 {
|
||||||
|
e.x.Sub(a.x, b.x)
|
||||||
|
e.y.Sub(a.y, b.y)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP2) Double(a *gfP2) *gfP2 {
|
||||||
|
e.x.Lsh(a.x, 1)
|
||||||
|
e.y.Lsh(a.y, 1)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *gfP2) Exp(a *gfP2, power *big.Int, pool *bnPool) *gfP2 {
|
||||||
|
sum := newGFp2(pool)
|
||||||
|
sum.SetOne()
|
||||||
|
t := newGFp2(pool)
|
||||||
|
|
||||||
|
for i := power.BitLen() - 1; i >= 0; i-- {
|
||||||
|
t.Square(sum, pool)
|
||||||
|
if power.Bit(i) != 0 {
|
||||||
|
sum.Mul(t, a, pool)
|
||||||
|
} else {
|
||||||
|
sum.Set(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Set(sum)
|
||||||
|
|
||||||
|
sum.Put(pool)
|
||||||
|
t.Put(pool)
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// See "Multiplication and Squaring in Pairing-Friendly Fields",
|
||||||
|
// http://eprint.iacr.org/2006/471.pdf
|
||||||
|
func (e *gfP2) Mul(a, b *gfP2, pool *bnPool) *gfP2 {
|
||||||
|
tx := pool.Get().Mul(a.x, b.y)
|
||||||
|
t := pool.Get().Mul(b.x, a.y)
|
||||||
|
tx.Add(tx, t)
|
||||||
|
tx.Mod(tx, p)
|
||||||
|
|
||||||
|
ty := pool.Get().Mul(a.y, b.y)
|
||||||
|
t.Mul(a.x, b.x)
|
||||||
|
ty.Sub(ty, t)
|
||||||
|
e.y.Mod(ty, p)
|
||||||
|
e.x.Set(tx)
|
||||||
|
|
||||||
|
pool.Put(tx)
|
||||||
|
pool.Put(ty)
|
||||||
|
pool.Put(t)
|
||||||
|
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP2) MulScalar(a *gfP2, b *big.Int) *gfP2 {
|
||||||
|
e.x.Mul(a.x, b)
|
||||||
|
e.y.Mul(a.y, b)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// MulXi sets e=ξa where ξ=i+3 and then returns e.
|
||||||
|
func (e *gfP2) MulXi(a *gfP2, pool *bnPool) *gfP2 {
|
||||||
|
// (xi+y)(i+3) = (3x+y)i+(3y-x)
|
||||||
|
tx := pool.Get().Lsh(a.x, 1)
|
||||||
|
tx.Add(tx, a.x)
|
||||||
|
tx.Add(tx, a.y)
|
||||||
|
|
||||||
|
ty := pool.Get().Lsh(a.y, 1)
|
||||||
|
ty.Add(ty, a.y)
|
||||||
|
ty.Sub(ty, a.x)
|
||||||
|
|
||||||
|
e.x.Set(tx)
|
||||||
|
e.y.Set(ty)
|
||||||
|
|
||||||
|
pool.Put(tx)
|
||||||
|
pool.Put(ty)
|
||||||
|
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP2) Square(a *gfP2, pool *bnPool) *gfP2 {
|
||||||
|
// Complex squaring algorithm:
|
||||||
|
// (xi+b)² = (x+y)(y-x) + 2*i*x*y
|
||||||
|
t1 := pool.Get().Sub(a.y, a.x)
|
||||||
|
t2 := pool.Get().Add(a.x, a.y)
|
||||||
|
ty := pool.Get().Mul(t1, t2)
|
||||||
|
ty.Mod(ty, p)
|
||||||
|
|
||||||
|
t1.Mul(a.x, a.y)
|
||||||
|
t1.Lsh(t1, 1)
|
||||||
|
|
||||||
|
e.x.Mod(t1, p)
|
||||||
|
e.y.Set(ty)
|
||||||
|
|
||||||
|
pool.Put(t1)
|
||||||
|
pool.Put(t2)
|
||||||
|
pool.Put(ty)
|
||||||
|
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP2) Invert(a *gfP2, pool *bnPool) *gfP2 {
|
||||||
|
// See "Implementing cryptographic pairings", M. Scott, section 3.2.
|
||||||
|
// ftp://136.206.11.249/pub/crypto/pairings.pdf
|
||||||
|
t := pool.Get()
|
||||||
|
t.Mul(a.y, a.y)
|
||||||
|
t2 := pool.Get()
|
||||||
|
t2.Mul(a.x, a.x)
|
||||||
|
t.Add(t, t2)
|
||||||
|
|
||||||
|
inv := pool.Get()
|
||||||
|
inv.ModInverse(t, p)
|
||||||
|
|
||||||
|
e.x.Neg(a.x)
|
||||||
|
e.x.Mul(e.x, inv)
|
||||||
|
e.x.Mod(e.x, p)
|
||||||
|
|
||||||
|
e.y.Mul(a.y, inv)
|
||||||
|
e.y.Mod(e.y, p)
|
||||||
|
|
||||||
|
pool.Put(t)
|
||||||
|
pool.Put(t2)
|
||||||
|
pool.Put(inv)
|
||||||
|
|
||||||
|
return e
|
||||||
|
}
|
||||||
+296
@@ -0,0 +1,296 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package bn256
|
||||||
|
|
||||||
|
// For details of the algorithms used, see "Multiplication and Squaring on
|
||||||
|
// Pairing-Friendly Fields, Devegili et al.
|
||||||
|
// http://eprint.iacr.org/2006/471.pdf.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
// gfP6 implements the field of size p⁶ as a cubic extension of gfP2 where τ³=ξ
|
||||||
|
// and ξ=i+3.
|
||||||
|
type gfP6 struct {
|
||||||
|
x, y, z *gfP2 // value is xτ² + yτ + z
|
||||||
|
}
|
||||||
|
|
||||||
|
func newGFp6(pool *bnPool) *gfP6 {
|
||||||
|
return &gfP6{newGFp2(pool), newGFp2(pool), newGFp2(pool)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP6) String() string {
|
||||||
|
return "(" + e.x.String() + "," + e.y.String() + "," + e.z.String() + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP6) Put(pool *bnPool) {
|
||||||
|
e.x.Put(pool)
|
||||||
|
e.y.Put(pool)
|
||||||
|
e.z.Put(pool)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP6) Set(a *gfP6) *gfP6 {
|
||||||
|
e.x.Set(a.x)
|
||||||
|
e.y.Set(a.y)
|
||||||
|
e.z.Set(a.z)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP6) SetZero() *gfP6 {
|
||||||
|
e.x.SetZero()
|
||||||
|
e.y.SetZero()
|
||||||
|
e.z.SetZero()
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP6) SetOne() *gfP6 {
|
||||||
|
e.x.SetZero()
|
||||||
|
e.y.SetZero()
|
||||||
|
e.z.SetOne()
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP6) Minimal() {
|
||||||
|
e.x.Minimal()
|
||||||
|
e.y.Minimal()
|
||||||
|
e.z.Minimal()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP6) IsZero() bool {
|
||||||
|
return e.x.IsZero() && e.y.IsZero() && e.z.IsZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP6) IsOne() bool {
|
||||||
|
return e.x.IsZero() && e.y.IsZero() && e.z.IsOne()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP6) Negative(a *gfP6) *gfP6 {
|
||||||
|
e.x.Negative(a.x)
|
||||||
|
e.y.Negative(a.y)
|
||||||
|
e.z.Negative(a.z)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP6) Frobenius(a *gfP6, pool *bnPool) *gfP6 {
|
||||||
|
e.x.Conjugate(a.x)
|
||||||
|
e.y.Conjugate(a.y)
|
||||||
|
e.z.Conjugate(a.z)
|
||||||
|
|
||||||
|
e.x.Mul(e.x, xiTo2PMinus2Over3, pool)
|
||||||
|
e.y.Mul(e.y, xiToPMinus1Over3, pool)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// FrobeniusP2 computes (xτ²+yτ+z)^(p²) = xτ^(2p²) + yτ^(p²) + z
|
||||||
|
func (e *gfP6) FrobeniusP2(a *gfP6) *gfP6 {
|
||||||
|
// τ^(2p²) = τ²τ^(2p²-2) = τ²ξ^((2p²-2)/3)
|
||||||
|
e.x.MulScalar(a.x, xiTo2PSquaredMinus2Over3)
|
||||||
|
// τ^(p²) = ττ^(p²-1) = τξ^((p²-1)/3)
|
||||||
|
e.y.MulScalar(a.y, xiToPSquaredMinus1Over3)
|
||||||
|
e.z.Set(a.z)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP6) Add(a, b *gfP6) *gfP6 {
|
||||||
|
e.x.Add(a.x, b.x)
|
||||||
|
e.y.Add(a.y, b.y)
|
||||||
|
e.z.Add(a.z, b.z)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP6) Sub(a, b *gfP6) *gfP6 {
|
||||||
|
e.x.Sub(a.x, b.x)
|
||||||
|
e.y.Sub(a.y, b.y)
|
||||||
|
e.z.Sub(a.z, b.z)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP6) Double(a *gfP6) *gfP6 {
|
||||||
|
e.x.Double(a.x)
|
||||||
|
e.y.Double(a.y)
|
||||||
|
e.z.Double(a.z)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP6) Mul(a, b *gfP6, pool *bnPool) *gfP6 {
|
||||||
|
// "Multiplication and Squaring on Pairing-Friendly Fields"
|
||||||
|
// Section 4, Karatsuba method.
|
||||||
|
// http://eprint.iacr.org/2006/471.pdf
|
||||||
|
|
||||||
|
v0 := newGFp2(pool)
|
||||||
|
v0.Mul(a.z, b.z, pool)
|
||||||
|
v1 := newGFp2(pool)
|
||||||
|
v1.Mul(a.y, b.y, pool)
|
||||||
|
v2 := newGFp2(pool)
|
||||||
|
v2.Mul(a.x, b.x, pool)
|
||||||
|
|
||||||
|
t0 := newGFp2(pool)
|
||||||
|
t0.Add(a.x, a.y)
|
||||||
|
t1 := newGFp2(pool)
|
||||||
|
t1.Add(b.x, b.y)
|
||||||
|
tz := newGFp2(pool)
|
||||||
|
tz.Mul(t0, t1, pool)
|
||||||
|
|
||||||
|
tz.Sub(tz, v1)
|
||||||
|
tz.Sub(tz, v2)
|
||||||
|
tz.MulXi(tz, pool)
|
||||||
|
tz.Add(tz, v0)
|
||||||
|
|
||||||
|
t0.Add(a.y, a.z)
|
||||||
|
t1.Add(b.y, b.z)
|
||||||
|
ty := newGFp2(pool)
|
||||||
|
ty.Mul(t0, t1, pool)
|
||||||
|
ty.Sub(ty, v0)
|
||||||
|
ty.Sub(ty, v1)
|
||||||
|
t0.MulXi(v2, pool)
|
||||||
|
ty.Add(ty, t0)
|
||||||
|
|
||||||
|
t0.Add(a.x, a.z)
|
||||||
|
t1.Add(b.x, b.z)
|
||||||
|
tx := newGFp2(pool)
|
||||||
|
tx.Mul(t0, t1, pool)
|
||||||
|
tx.Sub(tx, v0)
|
||||||
|
tx.Add(tx, v1)
|
||||||
|
tx.Sub(tx, v2)
|
||||||
|
|
||||||
|
e.x.Set(tx)
|
||||||
|
e.y.Set(ty)
|
||||||
|
e.z.Set(tz)
|
||||||
|
|
||||||
|
t0.Put(pool)
|
||||||
|
t1.Put(pool)
|
||||||
|
tx.Put(pool)
|
||||||
|
ty.Put(pool)
|
||||||
|
tz.Put(pool)
|
||||||
|
v0.Put(pool)
|
||||||
|
v1.Put(pool)
|
||||||
|
v2.Put(pool)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP6) MulScalar(a *gfP6, b *gfP2, pool *bnPool) *gfP6 {
|
||||||
|
e.x.Mul(a.x, b, pool)
|
||||||
|
e.y.Mul(a.y, b, pool)
|
||||||
|
e.z.Mul(a.z, b, pool)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP6) MulGFP(a *gfP6, b *big.Int) *gfP6 {
|
||||||
|
e.x.MulScalar(a.x, b)
|
||||||
|
e.y.MulScalar(a.y, b)
|
||||||
|
e.z.MulScalar(a.z, b)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// MulTau computes τ·(aτ²+bτ+c) = bτ²+cτ+aξ
|
||||||
|
func (e *gfP6) MulTau(a *gfP6, pool *bnPool) {
|
||||||
|
tz := newGFp2(pool)
|
||||||
|
tz.MulXi(a.x, pool)
|
||||||
|
ty := newGFp2(pool)
|
||||||
|
ty.Set(a.y)
|
||||||
|
e.y.Set(a.z)
|
||||||
|
e.x.Set(ty)
|
||||||
|
e.z.Set(tz)
|
||||||
|
tz.Put(pool)
|
||||||
|
ty.Put(pool)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP6) Square(a *gfP6, pool *bnPool) *gfP6 {
|
||||||
|
v0 := newGFp2(pool).Square(a.z, pool)
|
||||||
|
v1 := newGFp2(pool).Square(a.y, pool)
|
||||||
|
v2 := newGFp2(pool).Square(a.x, pool)
|
||||||
|
|
||||||
|
c0 := newGFp2(pool).Add(a.x, a.y)
|
||||||
|
c0.Square(c0, pool)
|
||||||
|
c0.Sub(c0, v1)
|
||||||
|
c0.Sub(c0, v2)
|
||||||
|
c0.MulXi(c0, pool)
|
||||||
|
c0.Add(c0, v0)
|
||||||
|
|
||||||
|
c1 := newGFp2(pool).Add(a.y, a.z)
|
||||||
|
c1.Square(c1, pool)
|
||||||
|
c1.Sub(c1, v0)
|
||||||
|
c1.Sub(c1, v1)
|
||||||
|
xiV2 := newGFp2(pool).MulXi(v2, pool)
|
||||||
|
c1.Add(c1, xiV2)
|
||||||
|
|
||||||
|
c2 := newGFp2(pool).Add(a.x, a.z)
|
||||||
|
c2.Square(c2, pool)
|
||||||
|
c2.Sub(c2, v0)
|
||||||
|
c2.Add(c2, v1)
|
||||||
|
c2.Sub(c2, v2)
|
||||||
|
|
||||||
|
e.x.Set(c2)
|
||||||
|
e.y.Set(c1)
|
||||||
|
e.z.Set(c0)
|
||||||
|
|
||||||
|
v0.Put(pool)
|
||||||
|
v1.Put(pool)
|
||||||
|
v2.Put(pool)
|
||||||
|
c0.Put(pool)
|
||||||
|
c1.Put(pool)
|
||||||
|
c2.Put(pool)
|
||||||
|
xiV2.Put(pool)
|
||||||
|
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *gfP6) Invert(a *gfP6, pool *bnPool) *gfP6 {
|
||||||
|
// See "Implementing cryptographic pairings", M. Scott, section 3.2.
|
||||||
|
// ftp://136.206.11.249/pub/crypto/pairings.pdf
|
||||||
|
|
||||||
|
// Here we can give a short explanation of how it works: let j be a cubic root of
|
||||||
|
// unity in GF(p²) so that 1+j+j²=0.
|
||||||
|
// Then (xτ² + yτ + z)(xj²τ² + yjτ + z)(xjτ² + yj²τ + z)
|
||||||
|
// = (xτ² + yτ + z)(Cτ²+Bτ+A)
|
||||||
|
// = (x³ξ²+y³ξ+z³-3ξxyz) = F is an element of the base field (the norm).
|
||||||
|
//
|
||||||
|
// On the other hand (xj²τ² + yjτ + z)(xjτ² + yj²τ + z)
|
||||||
|
// = τ²(y²-ξxz) + τ(ξx²-yz) + (z²-ξxy)
|
||||||
|
//
|
||||||
|
// So that's why A = (z²-ξxy), B = (ξx²-yz), C = (y²-ξxz)
|
||||||
|
t1 := newGFp2(pool)
|
||||||
|
|
||||||
|
A := newGFp2(pool)
|
||||||
|
A.Square(a.z, pool)
|
||||||
|
t1.Mul(a.x, a.y, pool)
|
||||||
|
t1.MulXi(t1, pool)
|
||||||
|
A.Sub(A, t1)
|
||||||
|
|
||||||
|
B := newGFp2(pool)
|
||||||
|
B.Square(a.x, pool)
|
||||||
|
B.MulXi(B, pool)
|
||||||
|
t1.Mul(a.y, a.z, pool)
|
||||||
|
B.Sub(B, t1)
|
||||||
|
|
||||||
|
C := newGFp2(pool)
|
||||||
|
C.Square(a.y, pool)
|
||||||
|
t1.Mul(a.x, a.z, pool)
|
||||||
|
C.Sub(C, t1)
|
||||||
|
|
||||||
|
F := newGFp2(pool)
|
||||||
|
F.Mul(C, a.y, pool)
|
||||||
|
F.MulXi(F, pool)
|
||||||
|
t1.Mul(A, a.z, pool)
|
||||||
|
F.Add(F, t1)
|
||||||
|
t1.Mul(B, a.x, pool)
|
||||||
|
t1.MulXi(t1, pool)
|
||||||
|
F.Add(F, t1)
|
||||||
|
|
||||||
|
F.Invert(F, pool)
|
||||||
|
|
||||||
|
e.x.Mul(C, F, pool)
|
||||||
|
e.y.Mul(B, F, pool)
|
||||||
|
e.z.Mul(A, F, pool)
|
||||||
|
|
||||||
|
t1.Put(pool)
|
||||||
|
A.Put(pool)
|
||||||
|
B.Put(pool)
|
||||||
|
C.Put(pool)
|
||||||
|
F.Put(pool)
|
||||||
|
|
||||||
|
return e
|
||||||
|
}
|
||||||
+395
@@ -0,0 +1,395 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package bn256
|
||||||
|
|
||||||
|
func lineFunctionAdd(r, p *twistPoint, q *curvePoint, r2 *gfP2, pool *bnPool) (a, b, c *gfP2, rOut *twistPoint) {
|
||||||
|
// See the mixed addition algorithm from "Faster Computation of the
|
||||||
|
// Tate Pairing", http://arxiv.org/pdf/0904.0854v3.pdf
|
||||||
|
|
||||||
|
B := newGFp2(pool).Mul(p.x, r.t, pool)
|
||||||
|
|
||||||
|
D := newGFp2(pool).Add(p.y, r.z)
|
||||||
|
D.Square(D, pool)
|
||||||
|
D.Sub(D, r2)
|
||||||
|
D.Sub(D, r.t)
|
||||||
|
D.Mul(D, r.t, pool)
|
||||||
|
|
||||||
|
H := newGFp2(pool).Sub(B, r.x)
|
||||||
|
I := newGFp2(pool).Square(H, pool)
|
||||||
|
|
||||||
|
E := newGFp2(pool).Add(I, I)
|
||||||
|
E.Add(E, E)
|
||||||
|
|
||||||
|
J := newGFp2(pool).Mul(H, E, pool)
|
||||||
|
|
||||||
|
L1 := newGFp2(pool).Sub(D, r.y)
|
||||||
|
L1.Sub(L1, r.y)
|
||||||
|
|
||||||
|
V := newGFp2(pool).Mul(r.x, E, pool)
|
||||||
|
|
||||||
|
rOut = newTwistPoint(pool)
|
||||||
|
rOut.x.Square(L1, pool)
|
||||||
|
rOut.x.Sub(rOut.x, J)
|
||||||
|
rOut.x.Sub(rOut.x, V)
|
||||||
|
rOut.x.Sub(rOut.x, V)
|
||||||
|
|
||||||
|
rOut.z.Add(r.z, H)
|
||||||
|
rOut.z.Square(rOut.z, pool)
|
||||||
|
rOut.z.Sub(rOut.z, r.t)
|
||||||
|
rOut.z.Sub(rOut.z, I)
|
||||||
|
|
||||||
|
t := newGFp2(pool).Sub(V, rOut.x)
|
||||||
|
t.Mul(t, L1, pool)
|
||||||
|
t2 := newGFp2(pool).Mul(r.y, J, pool)
|
||||||
|
t2.Add(t2, t2)
|
||||||
|
rOut.y.Sub(t, t2)
|
||||||
|
|
||||||
|
rOut.t.Square(rOut.z, pool)
|
||||||
|
|
||||||
|
t.Add(p.y, rOut.z)
|
||||||
|
t.Square(t, pool)
|
||||||
|
t.Sub(t, r2)
|
||||||
|
t.Sub(t, rOut.t)
|
||||||
|
|
||||||
|
t2.Mul(L1, p.x, pool)
|
||||||
|
t2.Add(t2, t2)
|
||||||
|
a = newGFp2(pool)
|
||||||
|
a.Sub(t2, t)
|
||||||
|
|
||||||
|
c = newGFp2(pool)
|
||||||
|
c.MulScalar(rOut.z, q.y)
|
||||||
|
c.Add(c, c)
|
||||||
|
|
||||||
|
b = newGFp2(pool)
|
||||||
|
b.SetZero()
|
||||||
|
b.Sub(b, L1)
|
||||||
|
b.MulScalar(b, q.x)
|
||||||
|
b.Add(b, b)
|
||||||
|
|
||||||
|
B.Put(pool)
|
||||||
|
D.Put(pool)
|
||||||
|
H.Put(pool)
|
||||||
|
I.Put(pool)
|
||||||
|
E.Put(pool)
|
||||||
|
J.Put(pool)
|
||||||
|
L1.Put(pool)
|
||||||
|
V.Put(pool)
|
||||||
|
t.Put(pool)
|
||||||
|
t2.Put(pool)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func lineFunctionDouble(r *twistPoint, q *curvePoint, pool *bnPool) (a, b, c *gfP2, rOut *twistPoint) {
|
||||||
|
// See the doubling algorithm for a=0 from "Faster Computation of the
|
||||||
|
// Tate Pairing", http://arxiv.org/pdf/0904.0854v3.pdf
|
||||||
|
|
||||||
|
A := newGFp2(pool).Square(r.x, pool)
|
||||||
|
B := newGFp2(pool).Square(r.y, pool)
|
||||||
|
C := newGFp2(pool).Square(B, pool)
|
||||||
|
|
||||||
|
D := newGFp2(pool).Add(r.x, B)
|
||||||
|
D.Square(D, pool)
|
||||||
|
D.Sub(D, A)
|
||||||
|
D.Sub(D, C)
|
||||||
|
D.Add(D, D)
|
||||||
|
|
||||||
|
E := newGFp2(pool).Add(A, A)
|
||||||
|
E.Add(E, A)
|
||||||
|
|
||||||
|
G := newGFp2(pool).Square(E, pool)
|
||||||
|
|
||||||
|
rOut = newTwistPoint(pool)
|
||||||
|
rOut.x.Sub(G, D)
|
||||||
|
rOut.x.Sub(rOut.x, D)
|
||||||
|
|
||||||
|
rOut.z.Add(r.y, r.z)
|
||||||
|
rOut.z.Square(rOut.z, pool)
|
||||||
|
rOut.z.Sub(rOut.z, B)
|
||||||
|
rOut.z.Sub(rOut.z, r.t)
|
||||||
|
|
||||||
|
rOut.y.Sub(D, rOut.x)
|
||||||
|
rOut.y.Mul(rOut.y, E, pool)
|
||||||
|
t := newGFp2(pool).Add(C, C)
|
||||||
|
t.Add(t, t)
|
||||||
|
t.Add(t, t)
|
||||||
|
rOut.y.Sub(rOut.y, t)
|
||||||
|
|
||||||
|
rOut.t.Square(rOut.z, pool)
|
||||||
|
|
||||||
|
t.Mul(E, r.t, pool)
|
||||||
|
t.Add(t, t)
|
||||||
|
b = newGFp2(pool)
|
||||||
|
b.SetZero()
|
||||||
|
b.Sub(b, t)
|
||||||
|
b.MulScalar(b, q.x)
|
||||||
|
|
||||||
|
a = newGFp2(pool)
|
||||||
|
a.Add(r.x, E)
|
||||||
|
a.Square(a, pool)
|
||||||
|
a.Sub(a, A)
|
||||||
|
a.Sub(a, G)
|
||||||
|
t.Add(B, B)
|
||||||
|
t.Add(t, t)
|
||||||
|
a.Sub(a, t)
|
||||||
|
|
||||||
|
c = newGFp2(pool)
|
||||||
|
c.Mul(rOut.z, r.t, pool)
|
||||||
|
c.Add(c, c)
|
||||||
|
c.MulScalar(c, q.y)
|
||||||
|
|
||||||
|
A.Put(pool)
|
||||||
|
B.Put(pool)
|
||||||
|
C.Put(pool)
|
||||||
|
D.Put(pool)
|
||||||
|
E.Put(pool)
|
||||||
|
G.Put(pool)
|
||||||
|
t.Put(pool)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func mulLine(ret *gfP12, a, b, c *gfP2, pool *bnPool) {
|
||||||
|
a2 := newGFp6(pool)
|
||||||
|
a2.x.SetZero()
|
||||||
|
a2.y.Set(a)
|
||||||
|
a2.z.Set(b)
|
||||||
|
a2.Mul(a2, ret.x, pool)
|
||||||
|
t3 := newGFp6(pool).MulScalar(ret.y, c, pool)
|
||||||
|
|
||||||
|
t := newGFp2(pool)
|
||||||
|
t.Add(b, c)
|
||||||
|
t2 := newGFp6(pool)
|
||||||
|
t2.x.SetZero()
|
||||||
|
t2.y.Set(a)
|
||||||
|
t2.z.Set(t)
|
||||||
|
ret.x.Add(ret.x, ret.y)
|
||||||
|
|
||||||
|
ret.y.Set(t3)
|
||||||
|
|
||||||
|
ret.x.Mul(ret.x, t2, pool)
|
||||||
|
ret.x.Sub(ret.x, a2)
|
||||||
|
ret.x.Sub(ret.x, ret.y)
|
||||||
|
a2.MulTau(a2, pool)
|
||||||
|
ret.y.Add(ret.y, a2)
|
||||||
|
|
||||||
|
a2.Put(pool)
|
||||||
|
t3.Put(pool)
|
||||||
|
t2.Put(pool)
|
||||||
|
t.Put(pool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sixuPlus2NAF is 6u+2 in non-adjacent form.
|
||||||
|
var sixuPlus2NAF = []int8{0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, -1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, -1, 0, 1, 0, 0, 0, 1, 0, -1, 0, 0, 0, -1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 1}
|
||||||
|
|
||||||
|
// miller implements the Miller loop for calculating the Optimal Ate pairing.
|
||||||
|
// See algorithm 1 from http://cryptojedi.org/papers/dclxvi-20100714.pdf
|
||||||
|
func miller(q *twistPoint, p *curvePoint, pool *bnPool) *gfP12 {
|
||||||
|
ret := newGFp12(pool)
|
||||||
|
ret.SetOne()
|
||||||
|
|
||||||
|
aAffine := newTwistPoint(pool)
|
||||||
|
aAffine.Set(q)
|
||||||
|
aAffine.MakeAffine(pool)
|
||||||
|
|
||||||
|
bAffine := newCurvePoint(pool)
|
||||||
|
bAffine.Set(p)
|
||||||
|
bAffine.MakeAffine(pool)
|
||||||
|
|
||||||
|
minusA := newTwistPoint(pool)
|
||||||
|
minusA.Negative(aAffine, pool)
|
||||||
|
|
||||||
|
r := newTwistPoint(pool)
|
||||||
|
r.Set(aAffine)
|
||||||
|
|
||||||
|
r2 := newGFp2(pool)
|
||||||
|
r2.Square(aAffine.y, pool)
|
||||||
|
|
||||||
|
for i := len(sixuPlus2NAF) - 1; i > 0; i-- {
|
||||||
|
a, b, c, newR := lineFunctionDouble(r, bAffine, pool)
|
||||||
|
if i != len(sixuPlus2NAF)-1 {
|
||||||
|
ret.Square(ret, pool)
|
||||||
|
}
|
||||||
|
|
||||||
|
mulLine(ret, a, b, c, pool)
|
||||||
|
a.Put(pool)
|
||||||
|
b.Put(pool)
|
||||||
|
c.Put(pool)
|
||||||
|
r.Put(pool)
|
||||||
|
r = newR
|
||||||
|
|
||||||
|
switch sixuPlus2NAF[i-1] {
|
||||||
|
case 1:
|
||||||
|
a, b, c, newR = lineFunctionAdd(r, aAffine, bAffine, r2, pool)
|
||||||
|
case -1:
|
||||||
|
a, b, c, newR = lineFunctionAdd(r, minusA, bAffine, r2, pool)
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
mulLine(ret, a, b, c, pool)
|
||||||
|
a.Put(pool)
|
||||||
|
b.Put(pool)
|
||||||
|
c.Put(pool)
|
||||||
|
r.Put(pool)
|
||||||
|
r = newR
|
||||||
|
}
|
||||||
|
|
||||||
|
// In order to calculate Q1 we have to convert q from the sextic twist
|
||||||
|
// to the full GF(p^12) group, apply the Frobenius there, and convert
|
||||||
|
// back.
|
||||||
|
//
|
||||||
|
// The twist isomorphism is (x', y') -> (xω², yω³). If we consider just
|
||||||
|
// x for a moment, then after applying the Frobenius, we have x̄ω^(2p)
|
||||||
|
// where x̄ is the conjugate of x. If we are going to apply the inverse
|
||||||
|
// isomorphism we need a value with a single coefficient of ω² so we
|
||||||
|
// rewrite this as x̄ω^(2p-2)ω². ξ⁶ = ω and, due to the construction of
|
||||||
|
// p, 2p-2 is a multiple of six. Therefore we can rewrite as
|
||||||
|
// x̄ξ^((p-1)/3)ω² and applying the inverse isomorphism eliminates the
|
||||||
|
// ω².
|
||||||
|
//
|
||||||
|
// A similar argument can be made for the y value.
|
||||||
|
|
||||||
|
q1 := newTwistPoint(pool)
|
||||||
|
q1.x.Conjugate(aAffine.x)
|
||||||
|
q1.x.Mul(q1.x, xiToPMinus1Over3, pool)
|
||||||
|
q1.y.Conjugate(aAffine.y)
|
||||||
|
q1.y.Mul(q1.y, xiToPMinus1Over2, pool)
|
||||||
|
q1.z.SetOne()
|
||||||
|
q1.t.SetOne()
|
||||||
|
|
||||||
|
// For Q2 we are applying the p² Frobenius. The two conjugations cancel
|
||||||
|
// out and we are left only with the factors from the isomorphism. In
|
||||||
|
// the case of x, we end up with a pure number which is why
|
||||||
|
// xiToPSquaredMinus1Over3 is ∈ GF(p). With y we get a factor of -1. We
|
||||||
|
// ignore this to end up with -Q2.
|
||||||
|
|
||||||
|
minusQ2 := newTwistPoint(pool)
|
||||||
|
minusQ2.x.MulScalar(aAffine.x, xiToPSquaredMinus1Over3)
|
||||||
|
minusQ2.y.Set(aAffine.y)
|
||||||
|
minusQ2.z.SetOne()
|
||||||
|
minusQ2.t.SetOne()
|
||||||
|
|
||||||
|
r2.Square(q1.y, pool)
|
||||||
|
a, b, c, newR := lineFunctionAdd(r, q1, bAffine, r2, pool)
|
||||||
|
mulLine(ret, a, b, c, pool)
|
||||||
|
a.Put(pool)
|
||||||
|
b.Put(pool)
|
||||||
|
c.Put(pool)
|
||||||
|
r.Put(pool)
|
||||||
|
r = newR
|
||||||
|
|
||||||
|
r2.Square(minusQ2.y, pool)
|
||||||
|
a, b, c, newR = lineFunctionAdd(r, minusQ2, bAffine, r2, pool)
|
||||||
|
mulLine(ret, a, b, c, pool)
|
||||||
|
a.Put(pool)
|
||||||
|
b.Put(pool)
|
||||||
|
c.Put(pool)
|
||||||
|
r.Put(pool)
|
||||||
|
r = newR
|
||||||
|
|
||||||
|
aAffine.Put(pool)
|
||||||
|
bAffine.Put(pool)
|
||||||
|
minusA.Put(pool)
|
||||||
|
r.Put(pool)
|
||||||
|
r2.Put(pool)
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// finalExponentiation computes the (p¹²-1)/Order-th power of an element of
|
||||||
|
// GF(p¹²) to obtain an element of GT (steps 13-15 of algorithm 1 from
|
||||||
|
// http://cryptojedi.org/papers/dclxvi-20100714.pdf)
|
||||||
|
func finalExponentiation(in *gfP12, pool *bnPool) *gfP12 {
|
||||||
|
t1 := newGFp12(pool)
|
||||||
|
|
||||||
|
// This is the p^6-Frobenius
|
||||||
|
t1.x.Negative(in.x)
|
||||||
|
t1.y.Set(in.y)
|
||||||
|
|
||||||
|
inv := newGFp12(pool)
|
||||||
|
inv.Invert(in, pool)
|
||||||
|
t1.Mul(t1, inv, pool)
|
||||||
|
|
||||||
|
t2 := newGFp12(pool).FrobeniusP2(t1, pool)
|
||||||
|
t1.Mul(t1, t2, pool)
|
||||||
|
|
||||||
|
fp := newGFp12(pool).Frobenius(t1, pool)
|
||||||
|
fp2 := newGFp12(pool).FrobeniusP2(t1, pool)
|
||||||
|
fp3 := newGFp12(pool).Frobenius(fp2, pool)
|
||||||
|
|
||||||
|
fu, fu2, fu3 := newGFp12(pool), newGFp12(pool), newGFp12(pool)
|
||||||
|
fu.Exp(t1, u, pool)
|
||||||
|
fu2.Exp(fu, u, pool)
|
||||||
|
fu3.Exp(fu2, u, pool)
|
||||||
|
|
||||||
|
y3 := newGFp12(pool).Frobenius(fu, pool)
|
||||||
|
fu2p := newGFp12(pool).Frobenius(fu2, pool)
|
||||||
|
fu3p := newGFp12(pool).Frobenius(fu3, pool)
|
||||||
|
y2 := newGFp12(pool).FrobeniusP2(fu2, pool)
|
||||||
|
|
||||||
|
y0 := newGFp12(pool)
|
||||||
|
y0.Mul(fp, fp2, pool)
|
||||||
|
y0.Mul(y0, fp3, pool)
|
||||||
|
|
||||||
|
y1, y4, y5 := newGFp12(pool), newGFp12(pool), newGFp12(pool)
|
||||||
|
y1.Conjugate(t1)
|
||||||
|
y5.Conjugate(fu2)
|
||||||
|
y3.Conjugate(y3)
|
||||||
|
y4.Mul(fu, fu2p, pool)
|
||||||
|
y4.Conjugate(y4)
|
||||||
|
|
||||||
|
y6 := newGFp12(pool)
|
||||||
|
y6.Mul(fu3, fu3p, pool)
|
||||||
|
y6.Conjugate(y6)
|
||||||
|
|
||||||
|
t0 := newGFp12(pool)
|
||||||
|
t0.Square(y6, pool)
|
||||||
|
t0.Mul(t0, y4, pool)
|
||||||
|
t0.Mul(t0, y5, pool)
|
||||||
|
t1.Mul(y3, y5, pool)
|
||||||
|
t1.Mul(t1, t0, pool)
|
||||||
|
t0.Mul(t0, y2, pool)
|
||||||
|
t1.Square(t1, pool)
|
||||||
|
t1.Mul(t1, t0, pool)
|
||||||
|
t1.Square(t1, pool)
|
||||||
|
t0.Mul(t1, y1, pool)
|
||||||
|
t1.Mul(t1, y0, pool)
|
||||||
|
t0.Square(t0, pool)
|
||||||
|
t0.Mul(t0, t1, pool)
|
||||||
|
|
||||||
|
inv.Put(pool)
|
||||||
|
t1.Put(pool)
|
||||||
|
t2.Put(pool)
|
||||||
|
fp.Put(pool)
|
||||||
|
fp2.Put(pool)
|
||||||
|
fp3.Put(pool)
|
||||||
|
fu.Put(pool)
|
||||||
|
fu2.Put(pool)
|
||||||
|
fu3.Put(pool)
|
||||||
|
fu2p.Put(pool)
|
||||||
|
fu3p.Put(pool)
|
||||||
|
y0.Put(pool)
|
||||||
|
y1.Put(pool)
|
||||||
|
y2.Put(pool)
|
||||||
|
y3.Put(pool)
|
||||||
|
y4.Put(pool)
|
||||||
|
y5.Put(pool)
|
||||||
|
y6.Put(pool)
|
||||||
|
|
||||||
|
return t0
|
||||||
|
}
|
||||||
|
|
||||||
|
func optimalAte(a *twistPoint, b *curvePoint, pool *bnPool) *gfP12 {
|
||||||
|
e := miller(a, b, pool)
|
||||||
|
ret := finalExponentiation(e, pool)
|
||||||
|
e.Put(pool)
|
||||||
|
|
||||||
|
if a.IsInfinity() || b.IsInfinity() {
|
||||||
|
ret.SetOne()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
+249
@@ -0,0 +1,249 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package bn256
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
// twistPoint implements the elliptic curve y²=x³+3/ξ over GF(p²). Points are
|
||||||
|
// kept in Jacobian form and t=z² when valid. The group G₂ is the set of
|
||||||
|
// n-torsion points of this curve over GF(p²) (where n = Order)
|
||||||
|
type twistPoint struct {
|
||||||
|
x, y, z, t *gfP2
|
||||||
|
}
|
||||||
|
|
||||||
|
var twistB = &gfP2{
|
||||||
|
bigFromBase10("6500054969564660373279643874235990574282535810762300357187714502686418407178"),
|
||||||
|
bigFromBase10("45500384786952622612957507119651934019977750675336102500314001518804928850249"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// twistGen is the generator of group G₂.
|
||||||
|
var twistGen = &twistPoint{
|
||||||
|
&gfP2{
|
||||||
|
bigFromBase10("21167961636542580255011770066570541300993051739349375019639421053990175267184"),
|
||||||
|
bigFromBase10("64746500191241794695844075326670126197795977525365406531717464316923369116492"),
|
||||||
|
},
|
||||||
|
&gfP2{
|
||||||
|
bigFromBase10("20666913350058776956210519119118544732556678129809273996262322366050359951122"),
|
||||||
|
bigFromBase10("17778617556404439934652658462602675281523610326338642107814333856843981424549"),
|
||||||
|
},
|
||||||
|
&gfP2{
|
||||||
|
bigFromBase10("0"),
|
||||||
|
bigFromBase10("1"),
|
||||||
|
},
|
||||||
|
&gfP2{
|
||||||
|
bigFromBase10("0"),
|
||||||
|
bigFromBase10("1"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTwistPoint(pool *bnPool) *twistPoint {
|
||||||
|
return &twistPoint{
|
||||||
|
newGFp2(pool),
|
||||||
|
newGFp2(pool),
|
||||||
|
newGFp2(pool),
|
||||||
|
newGFp2(pool),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *twistPoint) String() string {
|
||||||
|
return "(" + c.x.String() + ", " + c.y.String() + ", " + c.z.String() + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *twistPoint) Put(pool *bnPool) {
|
||||||
|
c.x.Put(pool)
|
||||||
|
c.y.Put(pool)
|
||||||
|
c.z.Put(pool)
|
||||||
|
c.t.Put(pool)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *twistPoint) Set(a *twistPoint) {
|
||||||
|
c.x.Set(a.x)
|
||||||
|
c.y.Set(a.y)
|
||||||
|
c.z.Set(a.z)
|
||||||
|
c.t.Set(a.t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsOnCurve returns true iff c is on the curve where c must be in affine form.
|
||||||
|
func (c *twistPoint) IsOnCurve() bool {
|
||||||
|
pool := new(bnPool)
|
||||||
|
yy := newGFp2(pool).Square(c.y, pool)
|
||||||
|
xxx := newGFp2(pool).Square(c.x, pool)
|
||||||
|
xxx.Mul(xxx, c.x, pool)
|
||||||
|
yy.Sub(yy, xxx)
|
||||||
|
yy.Sub(yy, twistB)
|
||||||
|
yy.Minimal()
|
||||||
|
return yy.x.Sign() == 0 && yy.y.Sign() == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *twistPoint) SetInfinity() {
|
||||||
|
c.z.SetZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *twistPoint) IsInfinity() bool {
|
||||||
|
return c.z.IsZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *twistPoint) Add(a, b *twistPoint, pool *bnPool) {
|
||||||
|
// For additional comments, see the same function in curve.go.
|
||||||
|
|
||||||
|
if a.IsInfinity() {
|
||||||
|
c.Set(b)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if b.IsInfinity() {
|
||||||
|
c.Set(a)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// See http://hyperelliptic.org/EFD/g1p/auto-code/shortw/jacobian-0/addition/add-2007-bl.op3
|
||||||
|
z1z1 := newGFp2(pool).Square(a.z, pool)
|
||||||
|
z2z2 := newGFp2(pool).Square(b.z, pool)
|
||||||
|
u1 := newGFp2(pool).Mul(a.x, z2z2, pool)
|
||||||
|
u2 := newGFp2(pool).Mul(b.x, z1z1, pool)
|
||||||
|
|
||||||
|
t := newGFp2(pool).Mul(b.z, z2z2, pool)
|
||||||
|
s1 := newGFp2(pool).Mul(a.y, t, pool)
|
||||||
|
|
||||||
|
t.Mul(a.z, z1z1, pool)
|
||||||
|
s2 := newGFp2(pool).Mul(b.y, t, pool)
|
||||||
|
|
||||||
|
h := newGFp2(pool).Sub(u2, u1)
|
||||||
|
xEqual := h.IsZero()
|
||||||
|
|
||||||
|
t.Add(h, h)
|
||||||
|
i := newGFp2(pool).Square(t, pool)
|
||||||
|
j := newGFp2(pool).Mul(h, i, pool)
|
||||||
|
|
||||||
|
t.Sub(s2, s1)
|
||||||
|
yEqual := t.IsZero()
|
||||||
|
if xEqual && yEqual {
|
||||||
|
c.Double(a, pool)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r := newGFp2(pool).Add(t, t)
|
||||||
|
|
||||||
|
v := newGFp2(pool).Mul(u1, i, pool)
|
||||||
|
|
||||||
|
t4 := newGFp2(pool).Square(r, pool)
|
||||||
|
t.Add(v, v)
|
||||||
|
t6 := newGFp2(pool).Sub(t4, j)
|
||||||
|
c.x.Sub(t6, t)
|
||||||
|
|
||||||
|
t.Sub(v, c.x) // t7
|
||||||
|
t4.Mul(s1, j, pool) // t8
|
||||||
|
t6.Add(t4, t4) // t9
|
||||||
|
t4.Mul(r, t, pool) // t10
|
||||||
|
c.y.Sub(t4, t6)
|
||||||
|
|
||||||
|
t.Add(a.z, b.z) // t11
|
||||||
|
t4.Square(t, pool) // t12
|
||||||
|
t.Sub(t4, z1z1) // t13
|
||||||
|
t4.Sub(t, z2z2) // t14
|
||||||
|
c.z.Mul(t4, h, pool)
|
||||||
|
|
||||||
|
z1z1.Put(pool)
|
||||||
|
z2z2.Put(pool)
|
||||||
|
u1.Put(pool)
|
||||||
|
u2.Put(pool)
|
||||||
|
t.Put(pool)
|
||||||
|
s1.Put(pool)
|
||||||
|
s2.Put(pool)
|
||||||
|
h.Put(pool)
|
||||||
|
i.Put(pool)
|
||||||
|
j.Put(pool)
|
||||||
|
r.Put(pool)
|
||||||
|
v.Put(pool)
|
||||||
|
t4.Put(pool)
|
||||||
|
t6.Put(pool)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *twistPoint) Double(a *twistPoint, pool *bnPool) {
|
||||||
|
// See http://hyperelliptic.org/EFD/g1p/auto-code/shortw/jacobian-0/doubling/dbl-2009-l.op3
|
||||||
|
A := newGFp2(pool).Square(a.x, pool)
|
||||||
|
B := newGFp2(pool).Square(a.y, pool)
|
||||||
|
C := newGFp2(pool).Square(B, pool)
|
||||||
|
|
||||||
|
t := newGFp2(pool).Add(a.x, B)
|
||||||
|
t2 := newGFp2(pool).Square(t, pool)
|
||||||
|
t.Sub(t2, A)
|
||||||
|
t2.Sub(t, C)
|
||||||
|
d := newGFp2(pool).Add(t2, t2)
|
||||||
|
t.Add(A, A)
|
||||||
|
e := newGFp2(pool).Add(t, A)
|
||||||
|
f := newGFp2(pool).Square(e, pool)
|
||||||
|
|
||||||
|
t.Add(d, d)
|
||||||
|
c.x.Sub(f, t)
|
||||||
|
|
||||||
|
t.Add(C, C)
|
||||||
|
t2.Add(t, t)
|
||||||
|
t.Add(t2, t2)
|
||||||
|
c.y.Sub(d, c.x)
|
||||||
|
t2.Mul(e, c.y, pool)
|
||||||
|
c.y.Sub(t2, t)
|
||||||
|
|
||||||
|
t.Mul(a.y, a.z, pool)
|
||||||
|
c.z.Add(t, t)
|
||||||
|
|
||||||
|
A.Put(pool)
|
||||||
|
B.Put(pool)
|
||||||
|
C.Put(pool)
|
||||||
|
t.Put(pool)
|
||||||
|
t2.Put(pool)
|
||||||
|
d.Put(pool)
|
||||||
|
e.Put(pool)
|
||||||
|
f.Put(pool)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *twistPoint) Mul(a *twistPoint, scalar *big.Int, pool *bnPool) *twistPoint {
|
||||||
|
sum := newTwistPoint(pool)
|
||||||
|
sum.SetInfinity()
|
||||||
|
t := newTwistPoint(pool)
|
||||||
|
|
||||||
|
for i := scalar.BitLen(); i >= 0; i-- {
|
||||||
|
t.Double(sum, pool)
|
||||||
|
if scalar.Bit(i) != 0 {
|
||||||
|
sum.Add(t, a, pool)
|
||||||
|
} else {
|
||||||
|
sum.Set(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Set(sum)
|
||||||
|
sum.Put(pool)
|
||||||
|
t.Put(pool)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *twistPoint) MakeAffine(pool *bnPool) *twistPoint {
|
||||||
|
if c.z.IsOne() {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
zInv := newGFp2(pool).Invert(c.z, pool)
|
||||||
|
t := newGFp2(pool).Mul(c.y, zInv, pool)
|
||||||
|
zInv2 := newGFp2(pool).Square(zInv, pool)
|
||||||
|
c.y.Mul(t, zInv2, pool)
|
||||||
|
t.Mul(c.x, zInv2, pool)
|
||||||
|
c.x.Set(t)
|
||||||
|
c.z.SetOne()
|
||||||
|
c.t.SetOne()
|
||||||
|
|
||||||
|
zInv.Put(pool)
|
||||||
|
t.Put(pool)
|
||||||
|
zInv2.Put(pool)
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *twistPoint) Negative(a *twistPoint, pool *bnPool) {
|
||||||
|
c.x.Set(a.x)
|
||||||
|
c.y.SetZero()
|
||||||
|
c.y.Sub(c.y, a.y)
|
||||||
|
c.z.Set(a.z)
|
||||||
|
c.t.SetZero()
|
||||||
|
}
|
||||||
+526
@@ -0,0 +1,526 @@
|
|||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package cast5 implements CAST5, as defined in RFC 2144. CAST5 is a common
|
||||||
|
// OpenPGP cipher.
|
||||||
|
package cast5 // import "golang.org/x/crypto/cast5"
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
const BlockSize = 8
|
||||||
|
const KeySize = 16
|
||||||
|
|
||||||
|
type Cipher struct {
|
||||||
|
masking [16]uint32
|
||||||
|
rotate [16]uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCipher(key []byte) (c *Cipher, err error) {
|
||||||
|
if len(key) != KeySize {
|
||||||
|
return nil, errors.New("CAST5: keys must be 16 bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
c = new(Cipher)
|
||||||
|
c.keySchedule(key)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cipher) BlockSize() int {
|
||||||
|
return BlockSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cipher) Encrypt(dst, src []byte) {
|
||||||
|
l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
|
||||||
|
r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
|
||||||
|
|
||||||
|
l, r = r, l^f1(r, c.masking[0], c.rotate[0])
|
||||||
|
l, r = r, l^f2(r, c.masking[1], c.rotate[1])
|
||||||
|
l, r = r, l^f3(r, c.masking[2], c.rotate[2])
|
||||||
|
l, r = r, l^f1(r, c.masking[3], c.rotate[3])
|
||||||
|
|
||||||
|
l, r = r, l^f2(r, c.masking[4], c.rotate[4])
|
||||||
|
l, r = r, l^f3(r, c.masking[5], c.rotate[5])
|
||||||
|
l, r = r, l^f1(r, c.masking[6], c.rotate[6])
|
||||||
|
l, r = r, l^f2(r, c.masking[7], c.rotate[7])
|
||||||
|
|
||||||
|
l, r = r, l^f3(r, c.masking[8], c.rotate[8])
|
||||||
|
l, r = r, l^f1(r, c.masking[9], c.rotate[9])
|
||||||
|
l, r = r, l^f2(r, c.masking[10], c.rotate[10])
|
||||||
|
l, r = r, l^f3(r, c.masking[11], c.rotate[11])
|
||||||
|
|
||||||
|
l, r = r, l^f1(r, c.masking[12], c.rotate[12])
|
||||||
|
l, r = r, l^f2(r, c.masking[13], c.rotate[13])
|
||||||
|
l, r = r, l^f3(r, c.masking[14], c.rotate[14])
|
||||||
|
l, r = r, l^f1(r, c.masking[15], c.rotate[15])
|
||||||
|
|
||||||
|
dst[0] = uint8(r >> 24)
|
||||||
|
dst[1] = uint8(r >> 16)
|
||||||
|
dst[2] = uint8(r >> 8)
|
||||||
|
dst[3] = uint8(r)
|
||||||
|
dst[4] = uint8(l >> 24)
|
||||||
|
dst[5] = uint8(l >> 16)
|
||||||
|
dst[6] = uint8(l >> 8)
|
||||||
|
dst[7] = uint8(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cipher) Decrypt(dst, src []byte) {
|
||||||
|
l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
|
||||||
|
r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
|
||||||
|
|
||||||
|
l, r = r, l^f1(r, c.masking[15], c.rotate[15])
|
||||||
|
l, r = r, l^f3(r, c.masking[14], c.rotate[14])
|
||||||
|
l, r = r, l^f2(r, c.masking[13], c.rotate[13])
|
||||||
|
l, r = r, l^f1(r, c.masking[12], c.rotate[12])
|
||||||
|
|
||||||
|
l, r = r, l^f3(r, c.masking[11], c.rotate[11])
|
||||||
|
l, r = r, l^f2(r, c.masking[10], c.rotate[10])
|
||||||
|
l, r = r, l^f1(r, c.masking[9], c.rotate[9])
|
||||||
|
l, r = r, l^f3(r, c.masking[8], c.rotate[8])
|
||||||
|
|
||||||
|
l, r = r, l^f2(r, c.masking[7], c.rotate[7])
|
||||||
|
l, r = r, l^f1(r, c.masking[6], c.rotate[6])
|
||||||
|
l, r = r, l^f3(r, c.masking[5], c.rotate[5])
|
||||||
|
l, r = r, l^f2(r, c.masking[4], c.rotate[4])
|
||||||
|
|
||||||
|
l, r = r, l^f1(r, c.masking[3], c.rotate[3])
|
||||||
|
l, r = r, l^f3(r, c.masking[2], c.rotate[2])
|
||||||
|
l, r = r, l^f2(r, c.masking[1], c.rotate[1])
|
||||||
|
l, r = r, l^f1(r, c.masking[0], c.rotate[0])
|
||||||
|
|
||||||
|
dst[0] = uint8(r >> 24)
|
||||||
|
dst[1] = uint8(r >> 16)
|
||||||
|
dst[2] = uint8(r >> 8)
|
||||||
|
dst[3] = uint8(r)
|
||||||
|
dst[4] = uint8(l >> 24)
|
||||||
|
dst[5] = uint8(l >> 16)
|
||||||
|
dst[6] = uint8(l >> 8)
|
||||||
|
dst[7] = uint8(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
type keyScheduleA [4][7]uint8
|
||||||
|
type keyScheduleB [4][5]uint8
|
||||||
|
|
||||||
|
// keyScheduleRound contains the magic values for a round of the key schedule.
|
||||||
|
// The keyScheduleA deals with the lines like:
|
||||||
|
// z0z1z2z3 = x0x1x2x3 ^ S5[xD] ^ S6[xF] ^ S7[xC] ^ S8[xE] ^ S7[x8]
|
||||||
|
// Conceptually, both x and z are in the same array, x first. The first
|
||||||
|
// element describes which word of this array gets written to and the
|
||||||
|
// second, which word gets read. So, for the line above, it's "4, 0", because
|
||||||
|
// it's writing to the first word of z, which, being after x, is word 4, and
|
||||||
|
// reading from the first word of x: word 0.
|
||||||
|
//
|
||||||
|
// Next are the indexes into the S-boxes. Now the array is treated as bytes. So
|
||||||
|
// "xD" is 0xd. The first byte of z is written as "16 + 0", just to be clear
|
||||||
|
// that it's z that we're indexing.
|
||||||
|
//
|
||||||
|
// keyScheduleB deals with lines like:
|
||||||
|
// K1 = S5[z8] ^ S6[z9] ^ S7[z7] ^ S8[z6] ^ S5[z2]
|
||||||
|
// "K1" is ignored because key words are always written in order. So the five
|
||||||
|
// elements are the S-box indexes. They use the same form as in keyScheduleA,
|
||||||
|
// above.
|
||||||
|
|
||||||
|
type keyScheduleRound struct{}
|
||||||
|
type keySchedule []keyScheduleRound
|
||||||
|
|
||||||
|
var schedule = []struct {
|
||||||
|
a keyScheduleA
|
||||||
|
b keyScheduleB
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
keyScheduleA{
|
||||||
|
{4, 0, 0xd, 0xf, 0xc, 0xe, 0x8},
|
||||||
|
{5, 2, 16 + 0, 16 + 2, 16 + 1, 16 + 3, 0xa},
|
||||||
|
{6, 3, 16 + 7, 16 + 6, 16 + 5, 16 + 4, 9},
|
||||||
|
{7, 1, 16 + 0xa, 16 + 9, 16 + 0xb, 16 + 8, 0xb},
|
||||||
|
},
|
||||||
|
keyScheduleB{
|
||||||
|
{16 + 8, 16 + 9, 16 + 7, 16 + 6, 16 + 2},
|
||||||
|
{16 + 0xa, 16 + 0xb, 16 + 5, 16 + 4, 16 + 6},
|
||||||
|
{16 + 0xc, 16 + 0xd, 16 + 3, 16 + 2, 16 + 9},
|
||||||
|
{16 + 0xe, 16 + 0xf, 16 + 1, 16 + 0, 16 + 0xc},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
keyScheduleA{
|
||||||
|
{0, 6, 16 + 5, 16 + 7, 16 + 4, 16 + 6, 16 + 0},
|
||||||
|
{1, 4, 0, 2, 1, 3, 16 + 2},
|
||||||
|
{2, 5, 7, 6, 5, 4, 16 + 1},
|
||||||
|
{3, 7, 0xa, 9, 0xb, 8, 16 + 3},
|
||||||
|
},
|
||||||
|
keyScheduleB{
|
||||||
|
{3, 2, 0xc, 0xd, 8},
|
||||||
|
{1, 0, 0xe, 0xf, 0xd},
|
||||||
|
{7, 6, 8, 9, 3},
|
||||||
|
{5, 4, 0xa, 0xb, 7},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
keyScheduleA{
|
||||||
|
{4, 0, 0xd, 0xf, 0xc, 0xe, 8},
|
||||||
|
{5, 2, 16 + 0, 16 + 2, 16 + 1, 16 + 3, 0xa},
|
||||||
|
{6, 3, 16 + 7, 16 + 6, 16 + 5, 16 + 4, 9},
|
||||||
|
{7, 1, 16 + 0xa, 16 + 9, 16 + 0xb, 16 + 8, 0xb},
|
||||||
|
},
|
||||||
|
keyScheduleB{
|
||||||
|
{16 + 3, 16 + 2, 16 + 0xc, 16 + 0xd, 16 + 9},
|
||||||
|
{16 + 1, 16 + 0, 16 + 0xe, 16 + 0xf, 16 + 0xc},
|
||||||
|
{16 + 7, 16 + 6, 16 + 8, 16 + 9, 16 + 2},
|
||||||
|
{16 + 5, 16 + 4, 16 + 0xa, 16 + 0xb, 16 + 6},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
keyScheduleA{
|
||||||
|
{0, 6, 16 + 5, 16 + 7, 16 + 4, 16 + 6, 16 + 0},
|
||||||
|
{1, 4, 0, 2, 1, 3, 16 + 2},
|
||||||
|
{2, 5, 7, 6, 5, 4, 16 + 1},
|
||||||
|
{3, 7, 0xa, 9, 0xb, 8, 16 + 3},
|
||||||
|
},
|
||||||
|
keyScheduleB{
|
||||||
|
{8, 9, 7, 6, 3},
|
||||||
|
{0xa, 0xb, 5, 4, 7},
|
||||||
|
{0xc, 0xd, 3, 2, 8},
|
||||||
|
{0xe, 0xf, 1, 0, 0xd},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cipher) keySchedule(in []byte) {
|
||||||
|
var t [8]uint32
|
||||||
|
var k [32]uint32
|
||||||
|
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
j := i * 4
|
||||||
|
t[i] = uint32(in[j])<<24 | uint32(in[j+1])<<16 | uint32(in[j+2])<<8 | uint32(in[j+3])
|
||||||
|
}
|
||||||
|
|
||||||
|
x := []byte{6, 7, 4, 5}
|
||||||
|
ki := 0
|
||||||
|
|
||||||
|
for half := 0; half < 2; half++ {
|
||||||
|
for _, round := range schedule {
|
||||||
|
for j := 0; j < 4; j++ {
|
||||||
|
var a [7]uint8
|
||||||
|
copy(a[:], round.a[j][:])
|
||||||
|
w := t[a[1]]
|
||||||
|
w ^= sBox[4][(t[a[2]>>2]>>(24-8*(a[2]&3)))&0xff]
|
||||||
|
w ^= sBox[5][(t[a[3]>>2]>>(24-8*(a[3]&3)))&0xff]
|
||||||
|
w ^= sBox[6][(t[a[4]>>2]>>(24-8*(a[4]&3)))&0xff]
|
||||||
|
w ^= sBox[7][(t[a[5]>>2]>>(24-8*(a[5]&3)))&0xff]
|
||||||
|
w ^= sBox[x[j]][(t[a[6]>>2]>>(24-8*(a[6]&3)))&0xff]
|
||||||
|
t[a[0]] = w
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := 0; j < 4; j++ {
|
||||||
|
var b [5]uint8
|
||||||
|
copy(b[:], round.b[j][:])
|
||||||
|
w := sBox[4][(t[b[0]>>2]>>(24-8*(b[0]&3)))&0xff]
|
||||||
|
w ^= sBox[5][(t[b[1]>>2]>>(24-8*(b[1]&3)))&0xff]
|
||||||
|
w ^= sBox[6][(t[b[2]>>2]>>(24-8*(b[2]&3)))&0xff]
|
||||||
|
w ^= sBox[7][(t[b[3]>>2]>>(24-8*(b[3]&3)))&0xff]
|
||||||
|
w ^= sBox[4+j][(t[b[4]>>2]>>(24-8*(b[4]&3)))&0xff]
|
||||||
|
k[ki] = w
|
||||||
|
ki++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 16; i++ {
|
||||||
|
c.masking[i] = k[i]
|
||||||
|
c.rotate[i] = uint8(k[16+i] & 0x1f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are the three 'f' functions. See RFC 2144, section 2.2.
|
||||||
|
func f1(d, m uint32, r uint8) uint32 {
|
||||||
|
t := m + d
|
||||||
|
I := (t << r) | (t >> (32 - r))
|
||||||
|
return ((sBox[0][I>>24] ^ sBox[1][(I>>16)&0xff]) - sBox[2][(I>>8)&0xff]) + sBox[3][I&0xff]
|
||||||
|
}
|
||||||
|
|
||||||
|
func f2(d, m uint32, r uint8) uint32 {
|
||||||
|
t := m ^ d
|
||||||
|
I := (t << r) | (t >> (32 - r))
|
||||||
|
return ((sBox[0][I>>24] - sBox[1][(I>>16)&0xff]) + sBox[2][(I>>8)&0xff]) ^ sBox[3][I&0xff]
|
||||||
|
}
|
||||||
|
|
||||||
|
func f3(d, m uint32, r uint8) uint32 {
|
||||||
|
t := m - d
|
||||||
|
I := (t << r) | (t >> (32 - r))
|
||||||
|
return ((sBox[0][I>>24] + sBox[1][(I>>16)&0xff]) ^ sBox[2][(I>>8)&0xff]) - sBox[3][I&0xff]
|
||||||
|
}
|
||||||
|
|
||||||
|
var sBox = [8][256]uint32{
|
||||||
|
{
|
||||||
|
0x30fb40d4, 0x9fa0ff0b, 0x6beccd2f, 0x3f258c7a, 0x1e213f2f, 0x9c004dd3, 0x6003e540, 0xcf9fc949,
|
||||||
|
0xbfd4af27, 0x88bbbdb5, 0xe2034090, 0x98d09675, 0x6e63a0e0, 0x15c361d2, 0xc2e7661d, 0x22d4ff8e,
|
||||||
|
0x28683b6f, 0xc07fd059, 0xff2379c8, 0x775f50e2, 0x43c340d3, 0xdf2f8656, 0x887ca41a, 0xa2d2bd2d,
|
||||||
|
0xa1c9e0d6, 0x346c4819, 0x61b76d87, 0x22540f2f, 0x2abe32e1, 0xaa54166b, 0x22568e3a, 0xa2d341d0,
|
||||||
|
0x66db40c8, 0xa784392f, 0x004dff2f, 0x2db9d2de, 0x97943fac, 0x4a97c1d8, 0x527644b7, 0xb5f437a7,
|
||||||
|
0xb82cbaef, 0xd751d159, 0x6ff7f0ed, 0x5a097a1f, 0x827b68d0, 0x90ecf52e, 0x22b0c054, 0xbc8e5935,
|
||||||
|
0x4b6d2f7f, 0x50bb64a2, 0xd2664910, 0xbee5812d, 0xb7332290, 0xe93b159f, 0xb48ee411, 0x4bff345d,
|
||||||
|
0xfd45c240, 0xad31973f, 0xc4f6d02e, 0x55fc8165, 0xd5b1caad, 0xa1ac2dae, 0xa2d4b76d, 0xc19b0c50,
|
||||||
|
0x882240f2, 0x0c6e4f38, 0xa4e4bfd7, 0x4f5ba272, 0x564c1d2f, 0xc59c5319, 0xb949e354, 0xb04669fe,
|
||||||
|
0xb1b6ab8a, 0xc71358dd, 0x6385c545, 0x110f935d, 0x57538ad5, 0x6a390493, 0xe63d37e0, 0x2a54f6b3,
|
||||||
|
0x3a787d5f, 0x6276a0b5, 0x19a6fcdf, 0x7a42206a, 0x29f9d4d5, 0xf61b1891, 0xbb72275e, 0xaa508167,
|
||||||
|
0x38901091, 0xc6b505eb, 0x84c7cb8c, 0x2ad75a0f, 0x874a1427, 0xa2d1936b, 0x2ad286af, 0xaa56d291,
|
||||||
|
0xd7894360, 0x425c750d, 0x93b39e26, 0x187184c9, 0x6c00b32d, 0x73e2bb14, 0xa0bebc3c, 0x54623779,
|
||||||
|
0x64459eab, 0x3f328b82, 0x7718cf82, 0x59a2cea6, 0x04ee002e, 0x89fe78e6, 0x3fab0950, 0x325ff6c2,
|
||||||
|
0x81383f05, 0x6963c5c8, 0x76cb5ad6, 0xd49974c9, 0xca180dcf, 0x380782d5, 0xc7fa5cf6, 0x8ac31511,
|
||||||
|
0x35e79e13, 0x47da91d0, 0xf40f9086, 0xa7e2419e, 0x31366241, 0x051ef495, 0xaa573b04, 0x4a805d8d,
|
||||||
|
0x548300d0, 0x00322a3c, 0xbf64cddf, 0xba57a68e, 0x75c6372b, 0x50afd341, 0xa7c13275, 0x915a0bf5,
|
||||||
|
0x6b54bfab, 0x2b0b1426, 0xab4cc9d7, 0x449ccd82, 0xf7fbf265, 0xab85c5f3, 0x1b55db94, 0xaad4e324,
|
||||||
|
0xcfa4bd3f, 0x2deaa3e2, 0x9e204d02, 0xc8bd25ac, 0xeadf55b3, 0xd5bd9e98, 0xe31231b2, 0x2ad5ad6c,
|
||||||
|
0x954329de, 0xadbe4528, 0xd8710f69, 0xaa51c90f, 0xaa786bf6, 0x22513f1e, 0xaa51a79b, 0x2ad344cc,
|
||||||
|
0x7b5a41f0, 0xd37cfbad, 0x1b069505, 0x41ece491, 0xb4c332e6, 0x032268d4, 0xc9600acc, 0xce387e6d,
|
||||||
|
0xbf6bb16c, 0x6a70fb78, 0x0d03d9c9, 0xd4df39de, 0xe01063da, 0x4736f464, 0x5ad328d8, 0xb347cc96,
|
||||||
|
0x75bb0fc3, 0x98511bfb, 0x4ffbcc35, 0xb58bcf6a, 0xe11f0abc, 0xbfc5fe4a, 0xa70aec10, 0xac39570a,
|
||||||
|
0x3f04442f, 0x6188b153, 0xe0397a2e, 0x5727cb79, 0x9ceb418f, 0x1cacd68d, 0x2ad37c96, 0x0175cb9d,
|
||||||
|
0xc69dff09, 0xc75b65f0, 0xd9db40d8, 0xec0e7779, 0x4744ead4, 0xb11c3274, 0xdd24cb9e, 0x7e1c54bd,
|
||||||
|
0xf01144f9, 0xd2240eb1, 0x9675b3fd, 0xa3ac3755, 0xd47c27af, 0x51c85f4d, 0x56907596, 0xa5bb15e6,
|
||||||
|
0x580304f0, 0xca042cf1, 0x011a37ea, 0x8dbfaadb, 0x35ba3e4a, 0x3526ffa0, 0xc37b4d09, 0xbc306ed9,
|
||||||
|
0x98a52666, 0x5648f725, 0xff5e569d, 0x0ced63d0, 0x7c63b2cf, 0x700b45e1, 0xd5ea50f1, 0x85a92872,
|
||||||
|
0xaf1fbda7, 0xd4234870, 0xa7870bf3, 0x2d3b4d79, 0x42e04198, 0x0cd0ede7, 0x26470db8, 0xf881814c,
|
||||||
|
0x474d6ad7, 0x7c0c5e5c, 0xd1231959, 0x381b7298, 0xf5d2f4db, 0xab838653, 0x6e2f1e23, 0x83719c9e,
|
||||||
|
0xbd91e046, 0x9a56456e, 0xdc39200c, 0x20c8c571, 0x962bda1c, 0xe1e696ff, 0xb141ab08, 0x7cca89b9,
|
||||||
|
0x1a69e783, 0x02cc4843, 0xa2f7c579, 0x429ef47d, 0x427b169c, 0x5ac9f049, 0xdd8f0f00, 0x5c8165bf,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0x1f201094, 0xef0ba75b, 0x69e3cf7e, 0x393f4380, 0xfe61cf7a, 0xeec5207a, 0x55889c94, 0x72fc0651,
|
||||||
|
0xada7ef79, 0x4e1d7235, 0xd55a63ce, 0xde0436ba, 0x99c430ef, 0x5f0c0794, 0x18dcdb7d, 0xa1d6eff3,
|
||||||
|
0xa0b52f7b, 0x59e83605, 0xee15b094, 0xe9ffd909, 0xdc440086, 0xef944459, 0xba83ccb3, 0xe0c3cdfb,
|
||||||
|
0xd1da4181, 0x3b092ab1, 0xf997f1c1, 0xa5e6cf7b, 0x01420ddb, 0xe4e7ef5b, 0x25a1ff41, 0xe180f806,
|
||||||
|
0x1fc41080, 0x179bee7a, 0xd37ac6a9, 0xfe5830a4, 0x98de8b7f, 0x77e83f4e, 0x79929269, 0x24fa9f7b,
|
||||||
|
0xe113c85b, 0xacc40083, 0xd7503525, 0xf7ea615f, 0x62143154, 0x0d554b63, 0x5d681121, 0xc866c359,
|
||||||
|
0x3d63cf73, 0xcee234c0, 0xd4d87e87, 0x5c672b21, 0x071f6181, 0x39f7627f, 0x361e3084, 0xe4eb573b,
|
||||||
|
0x602f64a4, 0xd63acd9c, 0x1bbc4635, 0x9e81032d, 0x2701f50c, 0x99847ab4, 0xa0e3df79, 0xba6cf38c,
|
||||||
|
0x10843094, 0x2537a95e, 0xf46f6ffe, 0xa1ff3b1f, 0x208cfb6a, 0x8f458c74, 0xd9e0a227, 0x4ec73a34,
|
||||||
|
0xfc884f69, 0x3e4de8df, 0xef0e0088, 0x3559648d, 0x8a45388c, 0x1d804366, 0x721d9bfd, 0xa58684bb,
|
||||||
|
0xe8256333, 0x844e8212, 0x128d8098, 0xfed33fb4, 0xce280ae1, 0x27e19ba5, 0xd5a6c252, 0xe49754bd,
|
||||||
|
0xc5d655dd, 0xeb667064, 0x77840b4d, 0xa1b6a801, 0x84db26a9, 0xe0b56714, 0x21f043b7, 0xe5d05860,
|
||||||
|
0x54f03084, 0x066ff472, 0xa31aa153, 0xdadc4755, 0xb5625dbf, 0x68561be6, 0x83ca6b94, 0x2d6ed23b,
|
||||||
|
0xeccf01db, 0xa6d3d0ba, 0xb6803d5c, 0xaf77a709, 0x33b4a34c, 0x397bc8d6, 0x5ee22b95, 0x5f0e5304,
|
||||||
|
0x81ed6f61, 0x20e74364, 0xb45e1378, 0xde18639b, 0x881ca122, 0xb96726d1, 0x8049a7e8, 0x22b7da7b,
|
||||||
|
0x5e552d25, 0x5272d237, 0x79d2951c, 0xc60d894c, 0x488cb402, 0x1ba4fe5b, 0xa4b09f6b, 0x1ca815cf,
|
||||||
|
0xa20c3005, 0x8871df63, 0xb9de2fcb, 0x0cc6c9e9, 0x0beeff53, 0xe3214517, 0xb4542835, 0x9f63293c,
|
||||||
|
0xee41e729, 0x6e1d2d7c, 0x50045286, 0x1e6685f3, 0xf33401c6, 0x30a22c95, 0x31a70850, 0x60930f13,
|
||||||
|
0x73f98417, 0xa1269859, 0xec645c44, 0x52c877a9, 0xcdff33a6, 0xa02b1741, 0x7cbad9a2, 0x2180036f,
|
||||||
|
0x50d99c08, 0xcb3f4861, 0xc26bd765, 0x64a3f6ab, 0x80342676, 0x25a75e7b, 0xe4e6d1fc, 0x20c710e6,
|
||||||
|
0xcdf0b680, 0x17844d3b, 0x31eef84d, 0x7e0824e4, 0x2ccb49eb, 0x846a3bae, 0x8ff77888, 0xee5d60f6,
|
||||||
|
0x7af75673, 0x2fdd5cdb, 0xa11631c1, 0x30f66f43, 0xb3faec54, 0x157fd7fa, 0xef8579cc, 0xd152de58,
|
||||||
|
0xdb2ffd5e, 0x8f32ce19, 0x306af97a, 0x02f03ef8, 0x99319ad5, 0xc242fa0f, 0xa7e3ebb0, 0xc68e4906,
|
||||||
|
0xb8da230c, 0x80823028, 0xdcdef3c8, 0xd35fb171, 0x088a1bc8, 0xbec0c560, 0x61a3c9e8, 0xbca8f54d,
|
||||||
|
0xc72feffa, 0x22822e99, 0x82c570b4, 0xd8d94e89, 0x8b1c34bc, 0x301e16e6, 0x273be979, 0xb0ffeaa6,
|
||||||
|
0x61d9b8c6, 0x00b24869, 0xb7ffce3f, 0x08dc283b, 0x43daf65a, 0xf7e19798, 0x7619b72f, 0x8f1c9ba4,
|
||||||
|
0xdc8637a0, 0x16a7d3b1, 0x9fc393b7, 0xa7136eeb, 0xc6bcc63e, 0x1a513742, 0xef6828bc, 0x520365d6,
|
||||||
|
0x2d6a77ab, 0x3527ed4b, 0x821fd216, 0x095c6e2e, 0xdb92f2fb, 0x5eea29cb, 0x145892f5, 0x91584f7f,
|
||||||
|
0x5483697b, 0x2667a8cc, 0x85196048, 0x8c4bacea, 0x833860d4, 0x0d23e0f9, 0x6c387e8a, 0x0ae6d249,
|
||||||
|
0xb284600c, 0xd835731d, 0xdcb1c647, 0xac4c56ea, 0x3ebd81b3, 0x230eabb0, 0x6438bc87, 0xf0b5b1fa,
|
||||||
|
0x8f5ea2b3, 0xfc184642, 0x0a036b7a, 0x4fb089bd, 0x649da589, 0xa345415e, 0x5c038323, 0x3e5d3bb9,
|
||||||
|
0x43d79572, 0x7e6dd07c, 0x06dfdf1e, 0x6c6cc4ef, 0x7160a539, 0x73bfbe70, 0x83877605, 0x4523ecf1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0x8defc240, 0x25fa5d9f, 0xeb903dbf, 0xe810c907, 0x47607fff, 0x369fe44b, 0x8c1fc644, 0xaececa90,
|
||||||
|
0xbeb1f9bf, 0xeefbcaea, 0xe8cf1950, 0x51df07ae, 0x920e8806, 0xf0ad0548, 0xe13c8d83, 0x927010d5,
|
||||||
|
0x11107d9f, 0x07647db9, 0xb2e3e4d4, 0x3d4f285e, 0xb9afa820, 0xfade82e0, 0xa067268b, 0x8272792e,
|
||||||
|
0x553fb2c0, 0x489ae22b, 0xd4ef9794, 0x125e3fbc, 0x21fffcee, 0x825b1bfd, 0x9255c5ed, 0x1257a240,
|
||||||
|
0x4e1a8302, 0xbae07fff, 0x528246e7, 0x8e57140e, 0x3373f7bf, 0x8c9f8188, 0xa6fc4ee8, 0xc982b5a5,
|
||||||
|
0xa8c01db7, 0x579fc264, 0x67094f31, 0xf2bd3f5f, 0x40fff7c1, 0x1fb78dfc, 0x8e6bd2c1, 0x437be59b,
|
||||||
|
0x99b03dbf, 0xb5dbc64b, 0x638dc0e6, 0x55819d99, 0xa197c81c, 0x4a012d6e, 0xc5884a28, 0xccc36f71,
|
||||||
|
0xb843c213, 0x6c0743f1, 0x8309893c, 0x0feddd5f, 0x2f7fe850, 0xd7c07f7e, 0x02507fbf, 0x5afb9a04,
|
||||||
|
0xa747d2d0, 0x1651192e, 0xaf70bf3e, 0x58c31380, 0x5f98302e, 0x727cc3c4, 0x0a0fb402, 0x0f7fef82,
|
||||||
|
0x8c96fdad, 0x5d2c2aae, 0x8ee99a49, 0x50da88b8, 0x8427f4a0, 0x1eac5790, 0x796fb449, 0x8252dc15,
|
||||||
|
0xefbd7d9b, 0xa672597d, 0xada840d8, 0x45f54504, 0xfa5d7403, 0xe83ec305, 0x4f91751a, 0x925669c2,
|
||||||
|
0x23efe941, 0xa903f12e, 0x60270df2, 0x0276e4b6, 0x94fd6574, 0x927985b2, 0x8276dbcb, 0x02778176,
|
||||||
|
0xf8af918d, 0x4e48f79e, 0x8f616ddf, 0xe29d840e, 0x842f7d83, 0x340ce5c8, 0x96bbb682, 0x93b4b148,
|
||||||
|
0xef303cab, 0x984faf28, 0x779faf9b, 0x92dc560d, 0x224d1e20, 0x8437aa88, 0x7d29dc96, 0x2756d3dc,
|
||||||
|
0x8b907cee, 0xb51fd240, 0xe7c07ce3, 0xe566b4a1, 0xc3e9615e, 0x3cf8209d, 0x6094d1e3, 0xcd9ca341,
|
||||||
|
0x5c76460e, 0x00ea983b, 0xd4d67881, 0xfd47572c, 0xf76cedd9, 0xbda8229c, 0x127dadaa, 0x438a074e,
|
||||||
|
0x1f97c090, 0x081bdb8a, 0x93a07ebe, 0xb938ca15, 0x97b03cff, 0x3dc2c0f8, 0x8d1ab2ec, 0x64380e51,
|
||||||
|
0x68cc7bfb, 0xd90f2788, 0x12490181, 0x5de5ffd4, 0xdd7ef86a, 0x76a2e214, 0xb9a40368, 0x925d958f,
|
||||||
|
0x4b39fffa, 0xba39aee9, 0xa4ffd30b, 0xfaf7933b, 0x6d498623, 0x193cbcfa, 0x27627545, 0x825cf47a,
|
||||||
|
0x61bd8ba0, 0xd11e42d1, 0xcead04f4, 0x127ea392, 0x10428db7, 0x8272a972, 0x9270c4a8, 0x127de50b,
|
||||||
|
0x285ba1c8, 0x3c62f44f, 0x35c0eaa5, 0xe805d231, 0x428929fb, 0xb4fcdf82, 0x4fb66a53, 0x0e7dc15b,
|
||||||
|
0x1f081fab, 0x108618ae, 0xfcfd086d, 0xf9ff2889, 0x694bcc11, 0x236a5cae, 0x12deca4d, 0x2c3f8cc5,
|
||||||
|
0xd2d02dfe, 0xf8ef5896, 0xe4cf52da, 0x95155b67, 0x494a488c, 0xb9b6a80c, 0x5c8f82bc, 0x89d36b45,
|
||||||
|
0x3a609437, 0xec00c9a9, 0x44715253, 0x0a874b49, 0xd773bc40, 0x7c34671c, 0x02717ef6, 0x4feb5536,
|
||||||
|
0xa2d02fff, 0xd2bf60c4, 0xd43f03c0, 0x50b4ef6d, 0x07478cd1, 0x006e1888, 0xa2e53f55, 0xb9e6d4bc,
|
||||||
|
0xa2048016, 0x97573833, 0xd7207d67, 0xde0f8f3d, 0x72f87b33, 0xabcc4f33, 0x7688c55d, 0x7b00a6b0,
|
||||||
|
0x947b0001, 0x570075d2, 0xf9bb88f8, 0x8942019e, 0x4264a5ff, 0x856302e0, 0x72dbd92b, 0xee971b69,
|
||||||
|
0x6ea22fde, 0x5f08ae2b, 0xaf7a616d, 0xe5c98767, 0xcf1febd2, 0x61efc8c2, 0xf1ac2571, 0xcc8239c2,
|
||||||
|
0x67214cb8, 0xb1e583d1, 0xb7dc3e62, 0x7f10bdce, 0xf90a5c38, 0x0ff0443d, 0x606e6dc6, 0x60543a49,
|
||||||
|
0x5727c148, 0x2be98a1d, 0x8ab41738, 0x20e1be24, 0xaf96da0f, 0x68458425, 0x99833be5, 0x600d457d,
|
||||||
|
0x282f9350, 0x8334b362, 0xd91d1120, 0x2b6d8da0, 0x642b1e31, 0x9c305a00, 0x52bce688, 0x1b03588a,
|
||||||
|
0xf7baefd5, 0x4142ed9c, 0xa4315c11, 0x83323ec5, 0xdfef4636, 0xa133c501, 0xe9d3531c, 0xee353783,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0x9db30420, 0x1fb6e9de, 0xa7be7bef, 0xd273a298, 0x4a4f7bdb, 0x64ad8c57, 0x85510443, 0xfa020ed1,
|
||||||
|
0x7e287aff, 0xe60fb663, 0x095f35a1, 0x79ebf120, 0xfd059d43, 0x6497b7b1, 0xf3641f63, 0x241e4adf,
|
||||||
|
0x28147f5f, 0x4fa2b8cd, 0xc9430040, 0x0cc32220, 0xfdd30b30, 0xc0a5374f, 0x1d2d00d9, 0x24147b15,
|
||||||
|
0xee4d111a, 0x0fca5167, 0x71ff904c, 0x2d195ffe, 0x1a05645f, 0x0c13fefe, 0x081b08ca, 0x05170121,
|
||||||
|
0x80530100, 0xe83e5efe, 0xac9af4f8, 0x7fe72701, 0xd2b8ee5f, 0x06df4261, 0xbb9e9b8a, 0x7293ea25,
|
||||||
|
0xce84ffdf, 0xf5718801, 0x3dd64b04, 0xa26f263b, 0x7ed48400, 0x547eebe6, 0x446d4ca0, 0x6cf3d6f5,
|
||||||
|
0x2649abdf, 0xaea0c7f5, 0x36338cc1, 0x503f7e93, 0xd3772061, 0x11b638e1, 0x72500e03, 0xf80eb2bb,
|
||||||
|
0xabe0502e, 0xec8d77de, 0x57971e81, 0xe14f6746, 0xc9335400, 0x6920318f, 0x081dbb99, 0xffc304a5,
|
||||||
|
0x4d351805, 0x7f3d5ce3, 0xa6c866c6, 0x5d5bcca9, 0xdaec6fea, 0x9f926f91, 0x9f46222f, 0x3991467d,
|
||||||
|
0xa5bf6d8e, 0x1143c44f, 0x43958302, 0xd0214eeb, 0x022083b8, 0x3fb6180c, 0x18f8931e, 0x281658e6,
|
||||||
|
0x26486e3e, 0x8bd78a70, 0x7477e4c1, 0xb506e07c, 0xf32d0a25, 0x79098b02, 0xe4eabb81, 0x28123b23,
|
||||||
|
0x69dead38, 0x1574ca16, 0xdf871b62, 0x211c40b7, 0xa51a9ef9, 0x0014377b, 0x041e8ac8, 0x09114003,
|
||||||
|
0xbd59e4d2, 0xe3d156d5, 0x4fe876d5, 0x2f91a340, 0x557be8de, 0x00eae4a7, 0x0ce5c2ec, 0x4db4bba6,
|
||||||
|
0xe756bdff, 0xdd3369ac, 0xec17b035, 0x06572327, 0x99afc8b0, 0x56c8c391, 0x6b65811c, 0x5e146119,
|
||||||
|
0x6e85cb75, 0xbe07c002, 0xc2325577, 0x893ff4ec, 0x5bbfc92d, 0xd0ec3b25, 0xb7801ab7, 0x8d6d3b24,
|
||||||
|
0x20c763ef, 0xc366a5fc, 0x9c382880, 0x0ace3205, 0xaac9548a, 0xeca1d7c7, 0x041afa32, 0x1d16625a,
|
||||||
|
0x6701902c, 0x9b757a54, 0x31d477f7, 0x9126b031, 0x36cc6fdb, 0xc70b8b46, 0xd9e66a48, 0x56e55a79,
|
||||||
|
0x026a4ceb, 0x52437eff, 0x2f8f76b4, 0x0df980a5, 0x8674cde3, 0xedda04eb, 0x17a9be04, 0x2c18f4df,
|
||||||
|
0xb7747f9d, 0xab2af7b4, 0xefc34d20, 0x2e096b7c, 0x1741a254, 0xe5b6a035, 0x213d42f6, 0x2c1c7c26,
|
||||||
|
0x61c2f50f, 0x6552daf9, 0xd2c231f8, 0x25130f69, 0xd8167fa2, 0x0418f2c8, 0x001a96a6, 0x0d1526ab,
|
||||||
|
0x63315c21, 0x5e0a72ec, 0x49bafefd, 0x187908d9, 0x8d0dbd86, 0x311170a7, 0x3e9b640c, 0xcc3e10d7,
|
||||||
|
0xd5cad3b6, 0x0caec388, 0xf73001e1, 0x6c728aff, 0x71eae2a1, 0x1f9af36e, 0xcfcbd12f, 0xc1de8417,
|
||||||
|
0xac07be6b, 0xcb44a1d8, 0x8b9b0f56, 0x013988c3, 0xb1c52fca, 0xb4be31cd, 0xd8782806, 0x12a3a4e2,
|
||||||
|
0x6f7de532, 0x58fd7eb6, 0xd01ee900, 0x24adffc2, 0xf4990fc5, 0x9711aac5, 0x001d7b95, 0x82e5e7d2,
|
||||||
|
0x109873f6, 0x00613096, 0xc32d9521, 0xada121ff, 0x29908415, 0x7fbb977f, 0xaf9eb3db, 0x29c9ed2a,
|
||||||
|
0x5ce2a465, 0xa730f32c, 0xd0aa3fe8, 0x8a5cc091, 0xd49e2ce7, 0x0ce454a9, 0xd60acd86, 0x015f1919,
|
||||||
|
0x77079103, 0xdea03af6, 0x78a8565e, 0xdee356df, 0x21f05cbe, 0x8b75e387, 0xb3c50651, 0xb8a5c3ef,
|
||||||
|
0xd8eeb6d2, 0xe523be77, 0xc2154529, 0x2f69efdf, 0xafe67afb, 0xf470c4b2, 0xf3e0eb5b, 0xd6cc9876,
|
||||||
|
0x39e4460c, 0x1fda8538, 0x1987832f, 0xca007367, 0xa99144f8, 0x296b299e, 0x492fc295, 0x9266beab,
|
||||||
|
0xb5676e69, 0x9bd3ddda, 0xdf7e052f, 0xdb25701c, 0x1b5e51ee, 0xf65324e6, 0x6afce36c, 0x0316cc04,
|
||||||
|
0x8644213e, 0xb7dc59d0, 0x7965291f, 0xccd6fd43, 0x41823979, 0x932bcdf6, 0xb657c34d, 0x4edfd282,
|
||||||
|
0x7ae5290c, 0x3cb9536b, 0x851e20fe, 0x9833557e, 0x13ecf0b0, 0xd3ffb372, 0x3f85c5c1, 0x0aef7ed2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0x7ec90c04, 0x2c6e74b9, 0x9b0e66df, 0xa6337911, 0xb86a7fff, 0x1dd358f5, 0x44dd9d44, 0x1731167f,
|
||||||
|
0x08fbf1fa, 0xe7f511cc, 0xd2051b00, 0x735aba00, 0x2ab722d8, 0x386381cb, 0xacf6243a, 0x69befd7a,
|
||||||
|
0xe6a2e77f, 0xf0c720cd, 0xc4494816, 0xccf5c180, 0x38851640, 0x15b0a848, 0xe68b18cb, 0x4caadeff,
|
||||||
|
0x5f480a01, 0x0412b2aa, 0x259814fc, 0x41d0efe2, 0x4e40b48d, 0x248eb6fb, 0x8dba1cfe, 0x41a99b02,
|
||||||
|
0x1a550a04, 0xba8f65cb, 0x7251f4e7, 0x95a51725, 0xc106ecd7, 0x97a5980a, 0xc539b9aa, 0x4d79fe6a,
|
||||||
|
0xf2f3f763, 0x68af8040, 0xed0c9e56, 0x11b4958b, 0xe1eb5a88, 0x8709e6b0, 0xd7e07156, 0x4e29fea7,
|
||||||
|
0x6366e52d, 0x02d1c000, 0xc4ac8e05, 0x9377f571, 0x0c05372a, 0x578535f2, 0x2261be02, 0xd642a0c9,
|
||||||
|
0xdf13a280, 0x74b55bd2, 0x682199c0, 0xd421e5ec, 0x53fb3ce8, 0xc8adedb3, 0x28a87fc9, 0x3d959981,
|
||||||
|
0x5c1ff900, 0xfe38d399, 0x0c4eff0b, 0x062407ea, 0xaa2f4fb1, 0x4fb96976, 0x90c79505, 0xb0a8a774,
|
||||||
|
0xef55a1ff, 0xe59ca2c2, 0xa6b62d27, 0xe66a4263, 0xdf65001f, 0x0ec50966, 0xdfdd55bc, 0x29de0655,
|
||||||
|
0x911e739a, 0x17af8975, 0x32c7911c, 0x89f89468, 0x0d01e980, 0x524755f4, 0x03b63cc9, 0x0cc844b2,
|
||||||
|
0xbcf3f0aa, 0x87ac36e9, 0xe53a7426, 0x01b3d82b, 0x1a9e7449, 0x64ee2d7e, 0xcddbb1da, 0x01c94910,
|
||||||
|
0xb868bf80, 0x0d26f3fd, 0x9342ede7, 0x04a5c284, 0x636737b6, 0x50f5b616, 0xf24766e3, 0x8eca36c1,
|
||||||
|
0x136e05db, 0xfef18391, 0xfb887a37, 0xd6e7f7d4, 0xc7fb7dc9, 0x3063fcdf, 0xb6f589de, 0xec2941da,
|
||||||
|
0x26e46695, 0xb7566419, 0xf654efc5, 0xd08d58b7, 0x48925401, 0xc1bacb7f, 0xe5ff550f, 0xb6083049,
|
||||||
|
0x5bb5d0e8, 0x87d72e5a, 0xab6a6ee1, 0x223a66ce, 0xc62bf3cd, 0x9e0885f9, 0x68cb3e47, 0x086c010f,
|
||||||
|
0xa21de820, 0xd18b69de, 0xf3f65777, 0xfa02c3f6, 0x407edac3, 0xcbb3d550, 0x1793084d, 0xb0d70eba,
|
||||||
|
0x0ab378d5, 0xd951fb0c, 0xded7da56, 0x4124bbe4, 0x94ca0b56, 0x0f5755d1, 0xe0e1e56e, 0x6184b5be,
|
||||||
|
0x580a249f, 0x94f74bc0, 0xe327888e, 0x9f7b5561, 0xc3dc0280, 0x05687715, 0x646c6bd7, 0x44904db3,
|
||||||
|
0x66b4f0a3, 0xc0f1648a, 0x697ed5af, 0x49e92ff6, 0x309e374f, 0x2cb6356a, 0x85808573, 0x4991f840,
|
||||||
|
0x76f0ae02, 0x083be84d, 0x28421c9a, 0x44489406, 0x736e4cb8, 0xc1092910, 0x8bc95fc6, 0x7d869cf4,
|
||||||
|
0x134f616f, 0x2e77118d, 0xb31b2be1, 0xaa90b472, 0x3ca5d717, 0x7d161bba, 0x9cad9010, 0xaf462ba2,
|
||||||
|
0x9fe459d2, 0x45d34559, 0xd9f2da13, 0xdbc65487, 0xf3e4f94e, 0x176d486f, 0x097c13ea, 0x631da5c7,
|
||||||
|
0x445f7382, 0x175683f4, 0xcdc66a97, 0x70be0288, 0xb3cdcf72, 0x6e5dd2f3, 0x20936079, 0x459b80a5,
|
||||||
|
0xbe60e2db, 0xa9c23101, 0xeba5315c, 0x224e42f2, 0x1c5c1572, 0xf6721b2c, 0x1ad2fff3, 0x8c25404e,
|
||||||
|
0x324ed72f, 0x4067b7fd, 0x0523138e, 0x5ca3bc78, 0xdc0fd66e, 0x75922283, 0x784d6b17, 0x58ebb16e,
|
||||||
|
0x44094f85, 0x3f481d87, 0xfcfeae7b, 0x77b5ff76, 0x8c2302bf, 0xaaf47556, 0x5f46b02a, 0x2b092801,
|
||||||
|
0x3d38f5f7, 0x0ca81f36, 0x52af4a8a, 0x66d5e7c0, 0xdf3b0874, 0x95055110, 0x1b5ad7a8, 0xf61ed5ad,
|
||||||
|
0x6cf6e479, 0x20758184, 0xd0cefa65, 0x88f7be58, 0x4a046826, 0x0ff6f8f3, 0xa09c7f70, 0x5346aba0,
|
||||||
|
0x5ce96c28, 0xe176eda3, 0x6bac307f, 0x376829d2, 0x85360fa9, 0x17e3fe2a, 0x24b79767, 0xf5a96b20,
|
||||||
|
0xd6cd2595, 0x68ff1ebf, 0x7555442c, 0xf19f06be, 0xf9e0659a, 0xeeb9491d, 0x34010718, 0xbb30cab8,
|
||||||
|
0xe822fe15, 0x88570983, 0x750e6249, 0xda627e55, 0x5e76ffa8, 0xb1534546, 0x6d47de08, 0xefe9e7d4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0xf6fa8f9d, 0x2cac6ce1, 0x4ca34867, 0xe2337f7c, 0x95db08e7, 0x016843b4, 0xeced5cbc, 0x325553ac,
|
||||||
|
0xbf9f0960, 0xdfa1e2ed, 0x83f0579d, 0x63ed86b9, 0x1ab6a6b8, 0xde5ebe39, 0xf38ff732, 0x8989b138,
|
||||||
|
0x33f14961, 0xc01937bd, 0xf506c6da, 0xe4625e7e, 0xa308ea99, 0x4e23e33c, 0x79cbd7cc, 0x48a14367,
|
||||||
|
0xa3149619, 0xfec94bd5, 0xa114174a, 0xeaa01866, 0xa084db2d, 0x09a8486f, 0xa888614a, 0x2900af98,
|
||||||
|
0x01665991, 0xe1992863, 0xc8f30c60, 0x2e78ef3c, 0xd0d51932, 0xcf0fec14, 0xf7ca07d2, 0xd0a82072,
|
||||||
|
0xfd41197e, 0x9305a6b0, 0xe86be3da, 0x74bed3cd, 0x372da53c, 0x4c7f4448, 0xdab5d440, 0x6dba0ec3,
|
||||||
|
0x083919a7, 0x9fbaeed9, 0x49dbcfb0, 0x4e670c53, 0x5c3d9c01, 0x64bdb941, 0x2c0e636a, 0xba7dd9cd,
|
||||||
|
0xea6f7388, 0xe70bc762, 0x35f29adb, 0x5c4cdd8d, 0xf0d48d8c, 0xb88153e2, 0x08a19866, 0x1ae2eac8,
|
||||||
|
0x284caf89, 0xaa928223, 0x9334be53, 0x3b3a21bf, 0x16434be3, 0x9aea3906, 0xefe8c36e, 0xf890cdd9,
|
||||||
|
0x80226dae, 0xc340a4a3, 0xdf7e9c09, 0xa694a807, 0x5b7c5ecc, 0x221db3a6, 0x9a69a02f, 0x68818a54,
|
||||||
|
0xceb2296f, 0x53c0843a, 0xfe893655, 0x25bfe68a, 0xb4628abc, 0xcf222ebf, 0x25ac6f48, 0xa9a99387,
|
||||||
|
0x53bddb65, 0xe76ffbe7, 0xe967fd78, 0x0ba93563, 0x8e342bc1, 0xe8a11be9, 0x4980740d, 0xc8087dfc,
|
||||||
|
0x8de4bf99, 0xa11101a0, 0x7fd37975, 0xda5a26c0, 0xe81f994f, 0x9528cd89, 0xfd339fed, 0xb87834bf,
|
||||||
|
0x5f04456d, 0x22258698, 0xc9c4c83b, 0x2dc156be, 0x4f628daa, 0x57f55ec5, 0xe2220abe, 0xd2916ebf,
|
||||||
|
0x4ec75b95, 0x24f2c3c0, 0x42d15d99, 0xcd0d7fa0, 0x7b6e27ff, 0xa8dc8af0, 0x7345c106, 0xf41e232f,
|
||||||
|
0x35162386, 0xe6ea8926, 0x3333b094, 0x157ec6f2, 0x372b74af, 0x692573e4, 0xe9a9d848, 0xf3160289,
|
||||||
|
0x3a62ef1d, 0xa787e238, 0xf3a5f676, 0x74364853, 0x20951063, 0x4576698d, 0xb6fad407, 0x592af950,
|
||||||
|
0x36f73523, 0x4cfb6e87, 0x7da4cec0, 0x6c152daa, 0xcb0396a8, 0xc50dfe5d, 0xfcd707ab, 0x0921c42f,
|
||||||
|
0x89dff0bb, 0x5fe2be78, 0x448f4f33, 0x754613c9, 0x2b05d08d, 0x48b9d585, 0xdc049441, 0xc8098f9b,
|
||||||
|
0x7dede786, 0xc39a3373, 0x42410005, 0x6a091751, 0x0ef3c8a6, 0x890072d6, 0x28207682, 0xa9a9f7be,
|
||||||
|
0xbf32679d, 0xd45b5b75, 0xb353fd00, 0xcbb0e358, 0x830f220a, 0x1f8fb214, 0xd372cf08, 0xcc3c4a13,
|
||||||
|
0x8cf63166, 0x061c87be, 0x88c98f88, 0x6062e397, 0x47cf8e7a, 0xb6c85283, 0x3cc2acfb, 0x3fc06976,
|
||||||
|
0x4e8f0252, 0x64d8314d, 0xda3870e3, 0x1e665459, 0xc10908f0, 0x513021a5, 0x6c5b68b7, 0x822f8aa0,
|
||||||
|
0x3007cd3e, 0x74719eef, 0xdc872681, 0x073340d4, 0x7e432fd9, 0x0c5ec241, 0x8809286c, 0xf592d891,
|
||||||
|
0x08a930f6, 0x957ef305, 0xb7fbffbd, 0xc266e96f, 0x6fe4ac98, 0xb173ecc0, 0xbc60b42a, 0x953498da,
|
||||||
|
0xfba1ae12, 0x2d4bd736, 0x0f25faab, 0xa4f3fceb, 0xe2969123, 0x257f0c3d, 0x9348af49, 0x361400bc,
|
||||||
|
0xe8816f4a, 0x3814f200, 0xa3f94043, 0x9c7a54c2, 0xbc704f57, 0xda41e7f9, 0xc25ad33a, 0x54f4a084,
|
||||||
|
0xb17f5505, 0x59357cbe, 0xedbd15c8, 0x7f97c5ab, 0xba5ac7b5, 0xb6f6deaf, 0x3a479c3a, 0x5302da25,
|
||||||
|
0x653d7e6a, 0x54268d49, 0x51a477ea, 0x5017d55b, 0xd7d25d88, 0x44136c76, 0x0404a8c8, 0xb8e5a121,
|
||||||
|
0xb81a928a, 0x60ed5869, 0x97c55b96, 0xeaec991b, 0x29935913, 0x01fdb7f1, 0x088e8dfa, 0x9ab6f6f5,
|
||||||
|
0x3b4cbf9f, 0x4a5de3ab, 0xe6051d35, 0xa0e1d855, 0xd36b4cf1, 0xf544edeb, 0xb0e93524, 0xbebb8fbd,
|
||||||
|
0xa2d762cf, 0x49c92f54, 0x38b5f331, 0x7128a454, 0x48392905, 0xa65b1db8, 0x851c97bd, 0xd675cf2f,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0x85e04019, 0x332bf567, 0x662dbfff, 0xcfc65693, 0x2a8d7f6f, 0xab9bc912, 0xde6008a1, 0x2028da1f,
|
||||||
|
0x0227bce7, 0x4d642916, 0x18fac300, 0x50f18b82, 0x2cb2cb11, 0xb232e75c, 0x4b3695f2, 0xb28707de,
|
||||||
|
0xa05fbcf6, 0xcd4181e9, 0xe150210c, 0xe24ef1bd, 0xb168c381, 0xfde4e789, 0x5c79b0d8, 0x1e8bfd43,
|
||||||
|
0x4d495001, 0x38be4341, 0x913cee1d, 0x92a79c3f, 0x089766be, 0xbaeeadf4, 0x1286becf, 0xb6eacb19,
|
||||||
|
0x2660c200, 0x7565bde4, 0x64241f7a, 0x8248dca9, 0xc3b3ad66, 0x28136086, 0x0bd8dfa8, 0x356d1cf2,
|
||||||
|
0x107789be, 0xb3b2e9ce, 0x0502aa8f, 0x0bc0351e, 0x166bf52a, 0xeb12ff82, 0xe3486911, 0xd34d7516,
|
||||||
|
0x4e7b3aff, 0x5f43671b, 0x9cf6e037, 0x4981ac83, 0x334266ce, 0x8c9341b7, 0xd0d854c0, 0xcb3a6c88,
|
||||||
|
0x47bc2829, 0x4725ba37, 0xa66ad22b, 0x7ad61f1e, 0x0c5cbafa, 0x4437f107, 0xb6e79962, 0x42d2d816,
|
||||||
|
0x0a961288, 0xe1a5c06e, 0x13749e67, 0x72fc081a, 0xb1d139f7, 0xf9583745, 0xcf19df58, 0xbec3f756,
|
||||||
|
0xc06eba30, 0x07211b24, 0x45c28829, 0xc95e317f, 0xbc8ec511, 0x38bc46e9, 0xc6e6fa14, 0xbae8584a,
|
||||||
|
0xad4ebc46, 0x468f508b, 0x7829435f, 0xf124183b, 0x821dba9f, 0xaff60ff4, 0xea2c4e6d, 0x16e39264,
|
||||||
|
0x92544a8b, 0x009b4fc3, 0xaba68ced, 0x9ac96f78, 0x06a5b79a, 0xb2856e6e, 0x1aec3ca9, 0xbe838688,
|
||||||
|
0x0e0804e9, 0x55f1be56, 0xe7e5363b, 0xb3a1f25d, 0xf7debb85, 0x61fe033c, 0x16746233, 0x3c034c28,
|
||||||
|
0xda6d0c74, 0x79aac56c, 0x3ce4e1ad, 0x51f0c802, 0x98f8f35a, 0x1626a49f, 0xeed82b29, 0x1d382fe3,
|
||||||
|
0x0c4fb99a, 0xbb325778, 0x3ec6d97b, 0x6e77a6a9, 0xcb658b5c, 0xd45230c7, 0x2bd1408b, 0x60c03eb7,
|
||||||
|
0xb9068d78, 0xa33754f4, 0xf430c87d, 0xc8a71302, 0xb96d8c32, 0xebd4e7be, 0xbe8b9d2d, 0x7979fb06,
|
||||||
|
0xe7225308, 0x8b75cf77, 0x11ef8da4, 0xe083c858, 0x8d6b786f, 0x5a6317a6, 0xfa5cf7a0, 0x5dda0033,
|
||||||
|
0xf28ebfb0, 0xf5b9c310, 0xa0eac280, 0x08b9767a, 0xa3d9d2b0, 0x79d34217, 0x021a718d, 0x9ac6336a,
|
||||||
|
0x2711fd60, 0x438050e3, 0x069908a8, 0x3d7fedc4, 0x826d2bef, 0x4eeb8476, 0x488dcf25, 0x36c9d566,
|
||||||
|
0x28e74e41, 0xc2610aca, 0x3d49a9cf, 0xbae3b9df, 0xb65f8de6, 0x92aeaf64, 0x3ac7d5e6, 0x9ea80509,
|
||||||
|
0xf22b017d, 0xa4173f70, 0xdd1e16c3, 0x15e0d7f9, 0x50b1b887, 0x2b9f4fd5, 0x625aba82, 0x6a017962,
|
||||||
|
0x2ec01b9c, 0x15488aa9, 0xd716e740, 0x40055a2c, 0x93d29a22, 0xe32dbf9a, 0x058745b9, 0x3453dc1e,
|
||||||
|
0xd699296e, 0x496cff6f, 0x1c9f4986, 0xdfe2ed07, 0xb87242d1, 0x19de7eae, 0x053e561a, 0x15ad6f8c,
|
||||||
|
0x66626c1c, 0x7154c24c, 0xea082b2a, 0x93eb2939, 0x17dcb0f0, 0x58d4f2ae, 0x9ea294fb, 0x52cf564c,
|
||||||
|
0x9883fe66, 0x2ec40581, 0x763953c3, 0x01d6692e, 0xd3a0c108, 0xa1e7160e, 0xe4f2dfa6, 0x693ed285,
|
||||||
|
0x74904698, 0x4c2b0edd, 0x4f757656, 0x5d393378, 0xa132234f, 0x3d321c5d, 0xc3f5e194, 0x4b269301,
|
||||||
|
0xc79f022f, 0x3c997e7e, 0x5e4f9504, 0x3ffafbbd, 0x76f7ad0e, 0x296693f4, 0x3d1fce6f, 0xc61e45be,
|
||||||
|
0xd3b5ab34, 0xf72bf9b7, 0x1b0434c0, 0x4e72b567, 0x5592a33d, 0xb5229301, 0xcfd2a87f, 0x60aeb767,
|
||||||
|
0x1814386b, 0x30bcc33d, 0x38a0c07d, 0xfd1606f2, 0xc363519b, 0x589dd390, 0x5479f8e6, 0x1cb8d647,
|
||||||
|
0x97fd61a9, 0xea7759f4, 0x2d57539d, 0x569a58cf, 0xe84e63ad, 0x462e1b78, 0x6580f87e, 0xf3817914,
|
||||||
|
0x91da55f4, 0x40a230f3, 0xd1988f35, 0xb6e318d2, 0x3ffa50bc, 0x3d40f021, 0xc3c0bdae, 0x4958c24c,
|
||||||
|
0x518f36b2, 0x84b1d370, 0x0fedce83, 0x878ddada, 0xf2a279c7, 0x94e01be8, 0x90716f4b, 0x954b8aa3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0xe216300d, 0xbbddfffc, 0xa7ebdabd, 0x35648095, 0x7789f8b7, 0xe6c1121b, 0x0e241600, 0x052ce8b5,
|
||||||
|
0x11a9cfb0, 0xe5952f11, 0xece7990a, 0x9386d174, 0x2a42931c, 0x76e38111, 0xb12def3a, 0x37ddddfc,
|
||||||
|
0xde9adeb1, 0x0a0cc32c, 0xbe197029, 0x84a00940, 0xbb243a0f, 0xb4d137cf, 0xb44e79f0, 0x049eedfd,
|
||||||
|
0x0b15a15d, 0x480d3168, 0x8bbbde5a, 0x669ded42, 0xc7ece831, 0x3f8f95e7, 0x72df191b, 0x7580330d,
|
||||||
|
0x94074251, 0x5c7dcdfa, 0xabbe6d63, 0xaa402164, 0xb301d40a, 0x02e7d1ca, 0x53571dae, 0x7a3182a2,
|
||||||
|
0x12a8ddec, 0xfdaa335d, 0x176f43e8, 0x71fb46d4, 0x38129022, 0xce949ad4, 0xb84769ad, 0x965bd862,
|
||||||
|
0x82f3d055, 0x66fb9767, 0x15b80b4e, 0x1d5b47a0, 0x4cfde06f, 0xc28ec4b8, 0x57e8726e, 0x647a78fc,
|
||||||
|
0x99865d44, 0x608bd593, 0x6c200e03, 0x39dc5ff6, 0x5d0b00a3, 0xae63aff2, 0x7e8bd632, 0x70108c0c,
|
||||||
|
0xbbd35049, 0x2998df04, 0x980cf42a, 0x9b6df491, 0x9e7edd53, 0x06918548, 0x58cb7e07, 0x3b74ef2e,
|
||||||
|
0x522fffb1, 0xd24708cc, 0x1c7e27cd, 0xa4eb215b, 0x3cf1d2e2, 0x19b47a38, 0x424f7618, 0x35856039,
|
||||||
|
0x9d17dee7, 0x27eb35e6, 0xc9aff67b, 0x36baf5b8, 0x09c467cd, 0xc18910b1, 0xe11dbf7b, 0x06cd1af8,
|
||||||
|
0x7170c608, 0x2d5e3354, 0xd4de495a, 0x64c6d006, 0xbcc0c62c, 0x3dd00db3, 0x708f8f34, 0x77d51b42,
|
||||||
|
0x264f620f, 0x24b8d2bf, 0x15c1b79e, 0x46a52564, 0xf8d7e54e, 0x3e378160, 0x7895cda5, 0x859c15a5,
|
||||||
|
0xe6459788, 0xc37bc75f, 0xdb07ba0c, 0x0676a3ab, 0x7f229b1e, 0x31842e7b, 0x24259fd7, 0xf8bef472,
|
||||||
|
0x835ffcb8, 0x6df4c1f2, 0x96f5b195, 0xfd0af0fc, 0xb0fe134c, 0xe2506d3d, 0x4f9b12ea, 0xf215f225,
|
||||||
|
0xa223736f, 0x9fb4c428, 0x25d04979, 0x34c713f8, 0xc4618187, 0xea7a6e98, 0x7cd16efc, 0x1436876c,
|
||||||
|
0xf1544107, 0xbedeee14, 0x56e9af27, 0xa04aa441, 0x3cf7c899, 0x92ecbae6, 0xdd67016d, 0x151682eb,
|
||||||
|
0xa842eedf, 0xfdba60b4, 0xf1907b75, 0x20e3030f, 0x24d8c29e, 0xe139673b, 0xefa63fb8, 0x71873054,
|
||||||
|
0xb6f2cf3b, 0x9f326442, 0xcb15a4cc, 0xb01a4504, 0xf1e47d8d, 0x844a1be5, 0xbae7dfdc, 0x42cbda70,
|
||||||
|
0xcd7dae0a, 0x57e85b7a, 0xd53f5af6, 0x20cf4d8c, 0xcea4d428, 0x79d130a4, 0x3486ebfb, 0x33d3cddc,
|
||||||
|
0x77853b53, 0x37effcb5, 0xc5068778, 0xe580b3e6, 0x4e68b8f4, 0xc5c8b37e, 0x0d809ea2, 0x398feb7c,
|
||||||
|
0x132a4f94, 0x43b7950e, 0x2fee7d1c, 0x223613bd, 0xdd06caa2, 0x37df932b, 0xc4248289, 0xacf3ebc3,
|
||||||
|
0x5715f6b7, 0xef3478dd, 0xf267616f, 0xc148cbe4, 0x9052815e, 0x5e410fab, 0xb48a2465, 0x2eda7fa4,
|
||||||
|
0xe87b40e4, 0xe98ea084, 0x5889e9e1, 0xefd390fc, 0xdd07d35b, 0xdb485694, 0x38d7e5b2, 0x57720101,
|
||||||
|
0x730edebc, 0x5b643113, 0x94917e4f, 0x503c2fba, 0x646f1282, 0x7523d24a, 0xe0779695, 0xf9c17a8f,
|
||||||
|
0x7a5b2121, 0xd187b896, 0x29263a4d, 0xba510cdf, 0x81f47c9f, 0xad1163ed, 0xea7b5965, 0x1a00726e,
|
||||||
|
0x11403092, 0x00da6d77, 0x4a0cdd61, 0xad1f4603, 0x605bdfb0, 0x9eedc364, 0x22ebe6a8, 0xcee7d28a,
|
||||||
|
0xa0e736a0, 0x5564a6b9, 0x10853209, 0xc7eb8f37, 0x2de705ca, 0x8951570f, 0xdf09822b, 0xbd691a6c,
|
||||||
|
0xaa12e4f2, 0x87451c0f, 0xe0f6a27a, 0x3ada4819, 0x4cf1764f, 0x0d771c2b, 0x67cdb156, 0x350d8384,
|
||||||
|
0x5938fa0f, 0x42399ef3, 0x36997b07, 0x0e84093d, 0x4aa93e61, 0x8360d87b, 0x1fa98b0c, 0x1149382c,
|
||||||
|
0xe97625a5, 0x0614d1b7, 0x0e25244b, 0x0c768347, 0x589e8d82, 0x0d2059d1, 0xa466bb1e, 0xf8da0a82,
|
||||||
|
0x04f19130, 0xba6e4ec0, 0x99265164, 0x1ee7230d, 0x50b2ad80, 0xeaee6801, 0x8db2a283, 0xea8bf59e,
|
||||||
|
},
|
||||||
|
}
|
||||||
+106
@@ -0,0 +1,106 @@
|
|||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package cast5
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This test vector is taken from RFC 2144, App B.1.
|
||||||
|
// Since the other two test vectors are for reduced-round variants, we can't
|
||||||
|
// use them.
|
||||||
|
var basicTests = []struct {
|
||||||
|
key, plainText, cipherText string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"0123456712345678234567893456789a",
|
||||||
|
"0123456789abcdef",
|
||||||
|
"238b4fe5847e44b2",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBasic(t *testing.T) {
|
||||||
|
for i, test := range basicTests {
|
||||||
|
key, _ := hex.DecodeString(test.key)
|
||||||
|
plainText, _ := hex.DecodeString(test.plainText)
|
||||||
|
expected, _ := hex.DecodeString(test.cipherText)
|
||||||
|
|
||||||
|
c, err := NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("#%d: failed to create Cipher: %s", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var cipherText [BlockSize]byte
|
||||||
|
c.Encrypt(cipherText[:], plainText)
|
||||||
|
if !bytes.Equal(cipherText[:], expected) {
|
||||||
|
t.Errorf("#%d: got:%x want:%x", i, cipherText, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
var plainTextAgain [BlockSize]byte
|
||||||
|
c.Decrypt(plainTextAgain[:], cipherText[:])
|
||||||
|
if !bytes.Equal(plainTextAgain[:], plainText) {
|
||||||
|
t.Errorf("#%d: got:%x want:%x", i, plainTextAgain, plainText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestFull performs the test specified in RFC 2144, App B.2.
|
||||||
|
// However, due to the length of time taken, it's disabled here and a more
|
||||||
|
// limited version is included, below.
|
||||||
|
func TestFull(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
// This is too slow for normal testing
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
a, b := iterate(1000000)
|
||||||
|
|
||||||
|
const expectedA = "eea9d0a249fd3ba6b3436fb89d6dca92"
|
||||||
|
const expectedB = "b2c95eb00c31ad7180ac05b8e83d696e"
|
||||||
|
|
||||||
|
if hex.EncodeToString(a) != expectedA {
|
||||||
|
t.Errorf("a: got:%x want:%s", a, expectedA)
|
||||||
|
}
|
||||||
|
if hex.EncodeToString(b) != expectedB {
|
||||||
|
t.Errorf("b: got:%x want:%s", b, expectedB)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func iterate(iterations int) ([]byte, []byte) {
|
||||||
|
const initValueHex = "0123456712345678234567893456789a"
|
||||||
|
|
||||||
|
initValue, _ := hex.DecodeString(initValueHex)
|
||||||
|
|
||||||
|
var a, b [16]byte
|
||||||
|
copy(a[:], initValue)
|
||||||
|
copy(b[:], initValue)
|
||||||
|
|
||||||
|
for i := 0; i < iterations; i++ {
|
||||||
|
c, _ := NewCipher(b[:])
|
||||||
|
c.Encrypt(a[:8], a[:8])
|
||||||
|
c.Encrypt(a[8:], a[8:])
|
||||||
|
c, _ = NewCipher(a[:])
|
||||||
|
c.Encrypt(b[:8], b[:8])
|
||||||
|
c.Encrypt(b[8:], b[8:])
|
||||||
|
}
|
||||||
|
|
||||||
|
return a[:], b[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLimited(t *testing.T) {
|
||||||
|
a, b := iterate(1000)
|
||||||
|
|
||||||
|
const expectedA = "23f73b14b02a2ad7dfb9f2c35644798d"
|
||||||
|
const expectedB = "e5bf37eff14c456a40b21ce369370a9f"
|
||||||
|
|
||||||
|
if hex.EncodeToString(a) != expectedA {
|
||||||
|
t.Errorf("a: got:%x want:%s", a, expectedA)
|
||||||
|
}
|
||||||
|
if hex.EncodeToString(b) != expectedB {
|
||||||
|
t.Errorf("b: got:%x want:%s", b, expectedB)
|
||||||
|
}
|
||||||
|
}
|
||||||
+83
@@ -0,0 +1,83 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package chacha20poly1305 implements the ChaCha20-Poly1305 AEAD as specified in RFC 7539.
|
||||||
|
package chacha20poly1305 // import "golang.org/x/crypto/chacha20poly1305"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/cipher"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// KeySize is the size of the key used by this AEAD, in bytes.
|
||||||
|
KeySize = 32
|
||||||
|
// NonceSize is the size of the nonce used with this AEAD, in bytes.
|
||||||
|
NonceSize = 12
|
||||||
|
)
|
||||||
|
|
||||||
|
type chacha20poly1305 struct {
|
||||||
|
key [32]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a ChaCha20-Poly1305 AEAD that uses the given, 256-bit key.
|
||||||
|
func New(key []byte) (cipher.AEAD, error) {
|
||||||
|
if len(key) != KeySize {
|
||||||
|
return nil, errors.New("chacha20poly1305: bad key length")
|
||||||
|
}
|
||||||
|
ret := new(chacha20poly1305)
|
||||||
|
copy(ret.key[:], key)
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chacha20poly1305) NonceSize() int {
|
||||||
|
return NonceSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chacha20poly1305) Overhead() int {
|
||||||
|
return 16
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chacha20poly1305) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
|
||||||
|
if len(nonce) != NonceSize {
|
||||||
|
panic("chacha20poly1305: bad nonce length passed to Seal")
|
||||||
|
}
|
||||||
|
|
||||||
|
if uint64(len(plaintext)) > (1<<38)-64 {
|
||||||
|
panic("chacha20poly1305: plaintext too large")
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.seal(dst, nonce, plaintext, additionalData)
|
||||||
|
}
|
||||||
|
|
||||||
|
var errOpen = errors.New("chacha20poly1305: message authentication failed")
|
||||||
|
|
||||||
|
func (c *chacha20poly1305) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
||||||
|
if len(nonce) != NonceSize {
|
||||||
|
panic("chacha20poly1305: bad nonce length passed to Open")
|
||||||
|
}
|
||||||
|
if len(ciphertext) < 16 {
|
||||||
|
return nil, errOpen
|
||||||
|
}
|
||||||
|
if uint64(len(ciphertext)) > (1<<38)-48 {
|
||||||
|
panic("chacha20poly1305: ciphertext too large")
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.open(dst, nonce, ciphertext, additionalData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sliceForAppend takes a slice and a requested number of bytes. It returns a
|
||||||
|
// slice with the contents of the given slice followed by that many bytes and a
|
||||||
|
// second slice that aliases into it and contains only the extra bytes. If the
|
||||||
|
// original slice has sufficient capacity then no allocation is performed.
|
||||||
|
func sliceForAppend(in []byte, n int) (head, tail []byte) {
|
||||||
|
if total := len(in) + n; cap(in) >= total {
|
||||||
|
head = in[:total]
|
||||||
|
} else {
|
||||||
|
head = make([]byte, total)
|
||||||
|
copy(head, in)
|
||||||
|
}
|
||||||
|
tail = head[len(in):]
|
||||||
|
return
|
||||||
|
}
|
||||||
+127
@@ -0,0 +1,127 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build go1.7,amd64,!gccgo,!appengine
|
||||||
|
|
||||||
|
package chacha20poly1305
|
||||||
|
|
||||||
|
import "encoding/binary"
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func chacha20Poly1305Open(dst []byte, key []uint32, src, ad []byte) bool
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func chacha20Poly1305Seal(dst []byte, key []uint32, src, ad []byte)
|
||||||
|
|
||||||
|
// cpuid is implemented in chacha20poly1305_amd64.s.
|
||||||
|
func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32)
|
||||||
|
|
||||||
|
// xgetbv with ecx = 0 is implemented in chacha20poly1305_amd64.s.
|
||||||
|
func xgetbv() (eax, edx uint32)
|
||||||
|
|
||||||
|
var (
|
||||||
|
useASM bool
|
||||||
|
useAVX2 bool
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
detectCPUFeatures()
|
||||||
|
}
|
||||||
|
|
||||||
|
// detectCPUFeatures is used to detect if cpu instructions
|
||||||
|
// used by the functions implemented in assembler in
|
||||||
|
// chacha20poly1305_amd64.s are supported.
|
||||||
|
func detectCPUFeatures() {
|
||||||
|
maxID, _, _, _ := cpuid(0, 0)
|
||||||
|
if maxID < 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, ecx1, _ := cpuid(1, 0)
|
||||||
|
|
||||||
|
haveSSSE3 := isSet(9, ecx1)
|
||||||
|
useASM = haveSSSE3
|
||||||
|
|
||||||
|
haveOSXSAVE := isSet(27, ecx1)
|
||||||
|
|
||||||
|
osSupportsAVX := false
|
||||||
|
// For XGETBV, OSXSAVE bit is required and sufficient.
|
||||||
|
if haveOSXSAVE {
|
||||||
|
eax, _ := xgetbv()
|
||||||
|
// Check if XMM and YMM registers have OS support.
|
||||||
|
osSupportsAVX = isSet(1, eax) && isSet(2, eax)
|
||||||
|
}
|
||||||
|
haveAVX := isSet(28, ecx1) && osSupportsAVX
|
||||||
|
|
||||||
|
if maxID < 7 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ebx7, _, _ := cpuid(7, 0)
|
||||||
|
haveAVX2 := isSet(5, ebx7) && haveAVX
|
||||||
|
haveBMI2 := isSet(8, ebx7)
|
||||||
|
|
||||||
|
useAVX2 = haveAVX2 && haveBMI2
|
||||||
|
}
|
||||||
|
|
||||||
|
// isSet checks if bit at bitpos is set in value.
|
||||||
|
func isSet(bitpos uint, value uint32) bool {
|
||||||
|
return value&(1<<bitpos) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// setupState writes a ChaCha20 input matrix to state. See
|
||||||
|
// https://tools.ietf.org/html/rfc7539#section-2.3.
|
||||||
|
func setupState(state *[16]uint32, key *[32]byte, nonce []byte) {
|
||||||
|
state[0] = 0x61707865
|
||||||
|
state[1] = 0x3320646e
|
||||||
|
state[2] = 0x79622d32
|
||||||
|
state[3] = 0x6b206574
|
||||||
|
|
||||||
|
state[4] = binary.LittleEndian.Uint32(key[:4])
|
||||||
|
state[5] = binary.LittleEndian.Uint32(key[4:8])
|
||||||
|
state[6] = binary.LittleEndian.Uint32(key[8:12])
|
||||||
|
state[7] = binary.LittleEndian.Uint32(key[12:16])
|
||||||
|
state[8] = binary.LittleEndian.Uint32(key[16:20])
|
||||||
|
state[9] = binary.LittleEndian.Uint32(key[20:24])
|
||||||
|
state[10] = binary.LittleEndian.Uint32(key[24:28])
|
||||||
|
state[11] = binary.LittleEndian.Uint32(key[28:32])
|
||||||
|
|
||||||
|
state[12] = 0
|
||||||
|
state[13] = binary.LittleEndian.Uint32(nonce[:4])
|
||||||
|
state[14] = binary.LittleEndian.Uint32(nonce[4:8])
|
||||||
|
state[15] = binary.LittleEndian.Uint32(nonce[8:12])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []byte {
|
||||||
|
if !useASM {
|
||||||
|
return c.sealGeneric(dst, nonce, plaintext, additionalData)
|
||||||
|
}
|
||||||
|
|
||||||
|
var state [16]uint32
|
||||||
|
setupState(&state, &c.key, nonce)
|
||||||
|
|
||||||
|
ret, out := sliceForAppend(dst, len(plaintext)+16)
|
||||||
|
chacha20Poly1305Seal(out[:], state[:], plaintext, additionalData)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
||||||
|
if !useASM {
|
||||||
|
return c.openGeneric(dst, nonce, ciphertext, additionalData)
|
||||||
|
}
|
||||||
|
|
||||||
|
var state [16]uint32
|
||||||
|
setupState(&state, &c.key, nonce)
|
||||||
|
|
||||||
|
ciphertext = ciphertext[:len(ciphertext)-16]
|
||||||
|
ret, out := sliceForAppend(dst, len(ciphertext))
|
||||||
|
if !chacha20Poly1305Open(out, state[:], ciphertext, additionalData) {
|
||||||
|
for i := range out {
|
||||||
|
out[i] = 0
|
||||||
|
}
|
||||||
|
return nil, errOpen
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
+2714
File diff suppressed because it is too large.
Load diff
+70
@@ -0,0 +1,70 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package chacha20poly1305
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/chacha20poly1305/internal/chacha20"
|
||||||
|
"golang.org/x/crypto/poly1305"
|
||||||
|
)
|
||||||
|
|
||||||
|
func roundTo16(n int) int {
|
||||||
|
return 16 * ((n + 15) / 16)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chacha20poly1305) sealGeneric(dst, nonce, plaintext, additionalData []byte) []byte {
|
||||||
|
var counter [16]byte
|
||||||
|
copy(counter[4:], nonce)
|
||||||
|
|
||||||
|
var polyKey [32]byte
|
||||||
|
chacha20.XORKeyStream(polyKey[:], polyKey[:], &counter, &c.key)
|
||||||
|
|
||||||
|
ret, out := sliceForAppend(dst, len(plaintext)+poly1305.TagSize)
|
||||||
|
counter[0] = 1
|
||||||
|
chacha20.XORKeyStream(out, plaintext, &counter, &c.key)
|
||||||
|
|
||||||
|
polyInput := make([]byte, roundTo16(len(additionalData))+roundTo16(len(plaintext))+8+8)
|
||||||
|
copy(polyInput, additionalData)
|
||||||
|
copy(polyInput[roundTo16(len(additionalData)):], out[:len(plaintext)])
|
||||||
|
binary.LittleEndian.PutUint64(polyInput[len(polyInput)-16:], uint64(len(additionalData)))
|
||||||
|
binary.LittleEndian.PutUint64(polyInput[len(polyInput)-8:], uint64(len(plaintext)))
|
||||||
|
|
||||||
|
var tag [poly1305.TagSize]byte
|
||||||
|
poly1305.Sum(&tag, polyInput, &polyKey)
|
||||||
|
copy(out[len(plaintext):], tag[:])
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chacha20poly1305) openGeneric(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
||||||
|
var tag [poly1305.TagSize]byte
|
||||||
|
copy(tag[:], ciphertext[len(ciphertext)-16:])
|
||||||
|
ciphertext = ciphertext[:len(ciphertext)-16]
|
||||||
|
|
||||||
|
var counter [16]byte
|
||||||
|
copy(counter[4:], nonce)
|
||||||
|
|
||||||
|
var polyKey [32]byte
|
||||||
|
chacha20.XORKeyStream(polyKey[:], polyKey[:], &counter, &c.key)
|
||||||
|
|
||||||
|
polyInput := make([]byte, roundTo16(len(additionalData))+roundTo16(len(ciphertext))+8+8)
|
||||||
|
copy(polyInput, additionalData)
|
||||||
|
copy(polyInput[roundTo16(len(additionalData)):], ciphertext)
|
||||||
|
binary.LittleEndian.PutUint64(polyInput[len(polyInput)-16:], uint64(len(additionalData)))
|
||||||
|
binary.LittleEndian.PutUint64(polyInput[len(polyInput)-8:], uint64(len(ciphertext)))
|
||||||
|
|
||||||
|
ret, out := sliceForAppend(dst, len(ciphertext))
|
||||||
|
if !poly1305.Verify(&tag, polyInput, &polyKey) {
|
||||||
|
for i := range out {
|
||||||
|
out[i] = 0
|
||||||
|
}
|
||||||
|
return nil, errOpen
|
||||||
|
}
|
||||||
|
|
||||||
|
counter[0] = 1
|
||||||
|
chacha20.XORKeyStream(out, ciphertext, &counter, &c.key)
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !amd64 !go1.7 gccgo appengine
|
||||||
|
|
||||||
|
package chacha20poly1305
|
||||||
|
|
||||||
|
func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []byte {
|
||||||
|
return c.sealGeneric(dst, nonce, plaintext, additionalData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
||||||
|
return c.openGeneric(dst, nonce, ciphertext, additionalData)
|
||||||
|
}
|
||||||
+182
@@ -0,0 +1,182 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package chacha20poly1305
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
cr "crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
|
mr "math/rand"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestVectors(t *testing.T) {
|
||||||
|
for i, test := range chacha20Poly1305Tests {
|
||||||
|
key, _ := hex.DecodeString(test.key)
|
||||||
|
nonce, _ := hex.DecodeString(test.nonce)
|
||||||
|
ad, _ := hex.DecodeString(test.aad)
|
||||||
|
plaintext, _ := hex.DecodeString(test.plaintext)
|
||||||
|
|
||||||
|
aead, err := New(key)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ct := aead.Seal(nil, nonce, plaintext, ad)
|
||||||
|
if ctHex := hex.EncodeToString(ct); ctHex != test.out {
|
||||||
|
t.Errorf("#%d: got %s, want %s", i, ctHex, test.out)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
plaintext2, err := aead.Open(nil, nonce, ct, ad)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("#%d: Open failed", i)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(plaintext, plaintext2) {
|
||||||
|
t.Errorf("#%d: plaintext's don't match: got %x vs %x", i, plaintext2, plaintext)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ad) > 0 {
|
||||||
|
alterAdIdx := mr.Intn(len(ad))
|
||||||
|
ad[alterAdIdx] ^= 0x80
|
||||||
|
if _, err := aead.Open(nil, nonce, ct, ad); err == nil {
|
||||||
|
t.Errorf("#%d: Open was successful after altering additional data", i)
|
||||||
|
}
|
||||||
|
ad[alterAdIdx] ^= 0x80
|
||||||
|
}
|
||||||
|
|
||||||
|
alterNonceIdx := mr.Intn(aead.NonceSize())
|
||||||
|
nonce[alterNonceIdx] ^= 0x80
|
||||||
|
if _, err := aead.Open(nil, nonce, ct, ad); err == nil {
|
||||||
|
t.Errorf("#%d: Open was successful after altering nonce", i)
|
||||||
|
}
|
||||||
|
nonce[alterNonceIdx] ^= 0x80
|
||||||
|
|
||||||
|
alterCtIdx := mr.Intn(len(ct))
|
||||||
|
ct[alterCtIdx] ^= 0x80
|
||||||
|
if _, err := aead.Open(nil, nonce, ct, ad); err == nil {
|
||||||
|
t.Errorf("#%d: Open was successful after altering ciphertext", i)
|
||||||
|
}
|
||||||
|
ct[alterCtIdx] ^= 0x80
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRandom(t *testing.T) {
|
||||||
|
// Some random tests to verify Open(Seal) == Plaintext
|
||||||
|
for i := 0; i < 256; i++ {
|
||||||
|
var nonce [12]byte
|
||||||
|
var key [32]byte
|
||||||
|
|
||||||
|
al := mr.Intn(128)
|
||||||
|
pl := mr.Intn(16384)
|
||||||
|
ad := make([]byte, al)
|
||||||
|
plaintext := make([]byte, pl)
|
||||||
|
cr.Read(key[:])
|
||||||
|
cr.Read(nonce[:])
|
||||||
|
cr.Read(ad)
|
||||||
|
cr.Read(plaintext)
|
||||||
|
|
||||||
|
aead, err := New(key[:])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ct := aead.Seal(nil, nonce[:], plaintext, ad)
|
||||||
|
|
||||||
|
plaintext2, err := aead.Open(nil, nonce[:], ct, ad)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Random #%d: Open failed", i)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(plaintext, plaintext2) {
|
||||||
|
t.Errorf("Random #%d: plaintext's don't match: got %x vs %x", i, plaintext2, plaintext)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ad) > 0 {
|
||||||
|
alterAdIdx := mr.Intn(len(ad))
|
||||||
|
ad[alterAdIdx] ^= 0x80
|
||||||
|
if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil {
|
||||||
|
t.Errorf("Random #%d: Open was successful after altering additional data", i)
|
||||||
|
}
|
||||||
|
ad[alterAdIdx] ^= 0x80
|
||||||
|
}
|
||||||
|
|
||||||
|
alterNonceIdx := mr.Intn(aead.NonceSize())
|
||||||
|
nonce[alterNonceIdx] ^= 0x80
|
||||||
|
if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil {
|
||||||
|
t.Errorf("Random #%d: Open was successful after altering nonce", i)
|
||||||
|
}
|
||||||
|
nonce[alterNonceIdx] ^= 0x80
|
||||||
|
|
||||||
|
alterCtIdx := mr.Intn(len(ct))
|
||||||
|
ct[alterCtIdx] ^= 0x80
|
||||||
|
if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil {
|
||||||
|
t.Errorf("Random #%d: Open was successful after altering ciphertext", i)
|
||||||
|
}
|
||||||
|
ct[alterCtIdx] ^= 0x80
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func benchamarkChaCha20Poly1305Seal(b *testing.B, buf []byte) {
|
||||||
|
b.SetBytes(int64(len(buf)))
|
||||||
|
|
||||||
|
var key [32]byte
|
||||||
|
var nonce [12]byte
|
||||||
|
var ad [13]byte
|
||||||
|
var out []byte
|
||||||
|
|
||||||
|
aead, _ := New(key[:])
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
out = aead.Seal(out[:0], nonce[:], buf[:], ad[:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func benchamarkChaCha20Poly1305Open(b *testing.B, buf []byte) {
|
||||||
|
b.SetBytes(int64(len(buf)))
|
||||||
|
|
||||||
|
var key [32]byte
|
||||||
|
var nonce [12]byte
|
||||||
|
var ad [13]byte
|
||||||
|
var ct []byte
|
||||||
|
var out []byte
|
||||||
|
|
||||||
|
aead, _ := New(key[:])
|
||||||
|
ct = aead.Seal(ct[:0], nonce[:], buf[:], ad[:])
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
out, _ = aead.Open(out[:0], nonce[:], ct[:], ad[:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkChacha20Poly1305Open_64(b *testing.B) {
|
||||||
|
benchamarkChaCha20Poly1305Open(b, make([]byte, 64))
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkChacha20Poly1305Seal_64(b *testing.B) {
|
||||||
|
benchamarkChaCha20Poly1305Seal(b, make([]byte, 64))
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkChacha20Poly1305Open_1350(b *testing.B) {
|
||||||
|
benchamarkChaCha20Poly1305Open(b, make([]byte, 1350))
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkChacha20Poly1305Seal_1350(b *testing.B) {
|
||||||
|
benchamarkChaCha20Poly1305Seal(b, make([]byte, 1350))
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkChacha20Poly1305Open_8K(b *testing.B) {
|
||||||
|
benchamarkChaCha20Poly1305Open(b, make([]byte, 8*1024))
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkChacha20Poly1305Seal_8K(b *testing.B) {
|
||||||
|
benchamarkChaCha20Poly1305Seal(b, make([]byte, 8*1024))
|
||||||
|
}
|
||||||
+332
@@ -0,0 +1,332 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package chacha20poly1305
|
||||||
|
|
||||||
|
var chacha20Poly1305Tests = []struct {
|
||||||
|
plaintext, aad, key, nonce, out string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393a204966204920636f756c64206f6666657220796f75206f6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73637265656e20776f756c642062652069742e",
|
||||||
|
"50515253c0c1c2c3c4c5c6c7",
|
||||||
|
"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f",
|
||||||
|
"070000004041424344454647",
|
||||||
|
"d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b61161ae10b594f09e26a7e902ecbd0600691",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"1400000cebccee3bf561b292340fec60",
|
||||||
|
"00000000000000001603030010",
|
||||||
|
"a5117e70953568bf750862df9e6f92af81677c3a188e847917a4a915bda7792e",
|
||||||
|
"129039b5572e8a7a8131f76a",
|
||||||
|
"2b487a2941bc07f3cc76d1a531662588ee7c2598e59778c24d5b27559a80d163",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"00000000000000000000000000",
|
||||||
|
"a5117e70953568bf750862df9e6f92af81677c3a188e847917a4a915bda7792e",
|
||||||
|
"129039b5572e8a7a8131f76a",
|
||||||
|
"3f487a25aa70e9c8391763370569c9e83b7650dd1921c8b78869f241f25d2096c910b180930c5b8747fd90959fe8ca2dcadb4fa50fa1439f916b2301e1cc0810d6725775d3ab86721700f96e22709b0a7a8bef32627dd929b2dd3ba15772b669062bb558bc92e6c241a1d60d9f0035e80c335f854815fe1138ab8af653eab3e122135feeec7dfaba1cc24af82a2b7acccdd824899a7e03cc29c25be8a4f56a66673845b93bae1556f09dafc89a0d22af207718e2a6bb022e9d917597295992ea3b750cc0e7a7c3d33b23c5a8aeab45f5bb542f6c9e6c1747ae5a344aff483ba38577ad534b33b3abc7d284776ea33ed488c2a2475648a4fcda561745ea7787ed60f2368deb27c75adce6ff9b6cc6de1f5e72a741e2d59f64751b3ae482d714e0c90e83c671ff98ed611823afb39e6e5019a6ba548a2a72e829c7b7b4a101ac9deb90a25d3e0c50d22e1fc26c7c02296fa13c6d9c14767f68aaf46450a8d0fd5feb60d9d73c6e68623425b4984a79d619dd6bf896459aa77a681ec9c1a97f645e121f47779b051f8948a817f84d1f55da170d5bbbaf2f64e18b97ed3fd822db2819f523314f1e5ac72e8f69bbe6c87c22daddb0e1ac6790f8534071de2f258064b99789bfb165b065b8fe96f9127cd7dca9f7cb0368420f1e802faa3ca23792f2a5b93773dd405e71c320b211b54f7a26626b03c060e1ab87f32ac588abfa056ce090bd7c69913a700c80f325bfe824fa",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"0967de57eefe1aaa999b9b746d88a1a248000d8734e0e938c6aa87",
|
||||||
|
"e4f0a3a4f90a8250f8806aa319053e8d73c62f150e2f239563037e9cc92823ad18c65111d0d462c954cc6c6ed2aafb45702a5a7e597d13bd8091594ab97cf7d1",
|
||||||
|
"f2db28620582e05f00f31c808475ca3df1c20e340bf14828352499466d79295f",
|
||||||
|
"4349e2131d44dc711148dfe3",
|
||||||
|
"bd06cc144fdc0d8b735fa4452eabbf78fd4ad2966ea41a84f68da40ca2da439777bc2ba6c4ec2de0d003eb",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"c4c920fb52a56fe66eaa8aa3fa187c543e3db8e5c8094c4313dc4ed35dfc5821c5791d171e8cfe8d37883031a0ad",
|
||||||
|
"85deea3dc4",
|
||||||
|
"05ff881d1e151bab4ca3db7d44880222733fe62686f71ce1e4610f2ea19599a7",
|
||||||
|
"b34710f65aed442e4a40866b",
|
||||||
|
"b154452fb7e85d175dd0b0db08591565c5587a725cf22386922f5d27a01015aba778975510b38754b2182e24352f019b7ad493e1ed255906715644aec6e0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"c4b337df5e83823900c6c202e93541cf5bc8c677a9aad8b8d87a4d7221e294e595cbc4f34e462d4e0def50f62491c57f598cf60236cfba0f4908816aea154f80e013732e59a07c668fcc5cb35d2232b7ae29b9e4f874f3417c74ab6689fae6690d5a9766fa13cd8adf293d3d4b70f4f999adde9121d1d29d467d04cf77ea398444d0ea3fe4b7c9c3e106002c76f4260fa204a0c3d5",
|
||||||
|
"72611bef65eb664f24ea94f4d5d3d88c9c9c6da29c9a1991c02833c4c9f6993b57b5",
|
||||||
|
"dd0f2d4bb1c9e5ca5aa5f38d69bc8402f7dbb7229857b4a41b3044d481b7655e",
|
||||||
|
"2bbca0910cc47ca0b8517391",
|
||||||
|
"83aa28d6d98901e2981d21d3758ae4db8cce07fe08d82ca6f036a68daa88a7dda56eeb38040c942bdda0fd2d369eec44bd070e2c9314992f68dc16989a6ac0c3912c378cf3254f4bae74a66b075e828df6f855c0d8a827ffed3c03582c12a9112eeb7be43dfe8bd78beb2d1e56678b99a0372531727cb7f2b98d2f917ec10de93fe86267100c20356e80528c5066688c8b7acba76e591449952343f663993d5b642e59eb0f",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"a9775b8e42b63335439cf1c79fe8a3560b3baebfdfc9ef239d70da02cea0947817f00659a63a8ee9d67fb1756854cc738f7a326e432191e1916be35f0b78d72268de7c0e180af7ee8aa864f2fc30658baa97f9edb88ace49f5b2a8002a8023925e9fa076a997643340c8253cf88ac8a221c190d94c5e224110cb423a4b65cca9046c1fad0483e1444c0680449148e7b20a778c56d5ae97e679d920c43eed6d42598cf05d10d1a15cd722a0686a871b74fea7cad45562bacf3bda937ac701bc218dac7e9d7d20f955429abdac21d821207febf4d54daea4898837035038bf71c66cef63e90f5d3e51f7fcfe18d41f38540a2c2958dacde16304e4b33da324030f1366f923c337",
|
||||||
|
"74ba3372d308910b5c9c3885f41252d57556",
|
||||||
|
"9cf77bd06a4ed8fb59349791b98ba40b6019611942f5768e8be2ee88477149e3",
|
||||||
|
"b928935c4c966c60fd6583c0",
|
||||||
|
"ec7fd64fd75b254961a2b7fc942470d8620f439258b871d0d00f58028b5e0bee5e139e8108ac439391465d6658f559b1df57aa21cf826ede1a28bc11af885e13eebfc009870928fae8abfdd943a60c54fca93f0502dc23d29c2fd5340f9bc0e6ef2a18b66ef627af95f796d5bbca50de22c8ec802da9397089b25c6ba5262468e3977b45dc112e51896c70731b0a52d7efec7c93b41995823436bf4b0c477ae79684407c9831b487928b2b8303caca752b3edf1f0598e15831155462706f94ef3fa3a9e5f937f37085afa9b4bbf939d275796a61b78f70597acfd25cd87f967021cd99328fc371b5eb5739869520657b30e4a5b0db7c8715cbe275dee78e719b357d3a9731f9eaba95986479bb2004a77822fc115a3d",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"b3d3128bce6bbf66fd78f1a18352bae56bfcdae18b65c379ee0aeb37ee54fba1270d2df578ec5b75654d16e89fd1cd0acda7ec580dafd2fbbabd32a8112d49383a762db2638928c8d63eb0750f7e7fdd256b35321b072dd5c45f7dd58cc60dc63d3b79a0c4a1689adf180fef968eccbcfa01ee15091ceacd7b67a3082db0ce6aeb470aafe87249c88b58b721e783dde184ccf68de8e05b6347fe6b74ae3adf9a81e9496a5c9332e7ebe908d26ce6b3f0b2a97e9a89d9fdd0d7694585a3241f240d698e69fcc050e7a959ba153f6d06f117848ba05d887134f1b6b994dad9b9e74247513e08a125b1fadfc7394dcd2a6451b504ae3e75e22f2b9bc405747dedb6c43ef4ccdf1a7edaf9451346123eaa63f3af113124f361508e255503a242b96680ae3360c8b13ac1f64d08088bb26b7f617cb0866f11d6fd362b00d86eba3fee68724e302388f119d6f92161ac8ce00d08919377a26974d99575b1032ff0f1976240c785c8b89e9eb2bf005e4be06b5371ffca14683fedfdb49e00e38ff27af1324177faf91599abd5990920797574eb743effdc7decda318ada1419cc8e0bfecf82f9c99792746c2b",
|
||||||
|
"7e8da4f3018f673f8e43bd7a1dee05f8031ec49129c361abbc2a434e9eaf791c3c1d0f3dad767d3bba3ab6d728bbcf2bd994bd03571eae1348f161e6a1da03ddf7121ba4",
|
||||||
|
"7ee32dd501dce849cd492f6e23324c1a4567bfceff9f11d1352bcb8615f1b093",
|
||||||
|
"8998e043d2961afa51ea262a",
|
||||||
|
"ba85e72af18cb5ba85a4a0d6c28b4ac1e5509a3a2fdb0e3255cbc559df5e6a661fc560c756a0264dd99b72c61c51a4b7ad56ca4c8ccb7e8edfc48ff3cceac5d1e8ac5fc87096adc4d0e9a27492857b17604c3a694cfe0e70b22df106c8f3c61f840bcd634964cdb571840e125e381e7dd3a0d97972e965f16f775fa4ce555124318290bf508beb7bd77e633042deb0e863631478fc3dc9122862b3c31264471bcce54e0b74040c8bafd481cf798f332e8940f1134d3027d6f28e771d15e154fc89c6c25fe18a5d312807cc2e623bb1bbb4f0b6ec71d009407eb54bb0759f03682f65d0da8812f84d8e97483f6a8d76a8417efcd9526444abba24288647609791578887ef49780b0b89f51b072cae81c5b5014463da3633dda105b82add0f9c2f065dca46eedd2928be2570493c79a996fa78ea6aec0996497fe2dc444432ade4eaa662ee2255f0f4b92d593288a8e3ffe7a15a10e9d33b0203af23f4c9fd2cfcb6160db63b52810869ff1e65423dbe2c4415884b9f8dec3c968e14cd74f323c89053a96111bc9ce59ec483832c49c53a648e5f0f797f53642ac60170c94b473f1f2e7d8a38e46460b81219b52081263027f74cbf63a75af3a7",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"68d5ba501e87994ef6bc8042d7c5a99693a835a4796ad044f0e536a0790a7ee1e03832fec0cb4cb688cdf85f92a1f526492acac2949a0684803c24f947a3da27db0c259bd87251603f49bfd1eab4f733dec2f5725cfcf6dc381ad57fbdb0a699bccc34943e86f47dcfb34eba6746ed4508e3b764dfad4117c8169785c63d1e8309531747d90cc4a8bf13622759506c613324c512d10629991dc01fe3fe3d6607907e4f698a1312492674707fc4dde0f701a609d2ac336cc9f38badf1c813f9599148c21b5bd4658249d5010db2e205b3880e863441f2fe357dab2645be1f9e5067616bc335d0457ea6468c5828910cb09f92e5e184e316018e3c464c5ce59cc34608867bd8cbfa7e1286d73a17e3ebb675d097f9b3adfa41ea408d46252a096b3290e70a5be1896d6760a87e439334b863ccb11679ab5763ebe4a9110eb37c4043634b9e44d40cab34b42977475e2faa2ae0c0a38b170776fbb0870a63044aa6679545ac6951579d0581144cdf43f60923b6acaecdb325c864acd2c7b01d6e18b2b3c41c041bb9099cce557b114b84350131e3cee4089648b5691065867e7d38314154355d0e3ef9dc9375eddef922df2a06ad0f0e4357c3ac672932e5a66b16e8bf4b45cd893ea91cb397faadb9d9d7bf86e6ceca3e9176a5baa98b6114a149d3ed8ea176cc4a9380e18d2d9b67045aedeb28b729ba2ece74d759d5ebfb1ebee8ac5f5e79aaf1f98b7f2626e62a81d315a98b3e",
|
||||||
|
"63b90dd89066ad7b61cc39497899a8f14399eace1810f5fe3b76d2501f5d8f83169c5ba602082164d45aad4df3553e36ef29050739fa067470d8c58f3554124bf06df1f27612564a6c04976059d69648ff9b50389556ad052e729563c6a7",
|
||||||
|
"7d5c4314a542aff57a454b274a7999dfdc5f878a159c29be27dabdfcf7c06975",
|
||||||
|
"aeb6159fa88bb1ffd51d036d",
|
||||||
|
"7597f7f44191e815a409754db7fea688e0105c987fa065e621823ea6dea617aed613092ad566c487cfa1a93f556615d2a575fb30ac34b11e19cd908d74545906f929dc9e59f6f1e1e6eaaabe182748ef87057ef7820ffcf254c40237d3ea9ff004472db783ed54b5a294a46cf90519bf89367b04fc01ce544c5bcdd3197eb1237923ce2c0c99921ca959c53b54176d292e97f6d9696ded6054711721aebda543e3e077c90e6f216cdc275b86d45603521c5aab24f08fd06833b0743c388382f941e19e0283ac7c4ef22383e1b9b08572882769c1382bab9ad127e7f3e09b5330b82d3e0c7d6f0df46edc93265999eef8e7afa0cb1db77df7accf5bff8631a320d146a5c751a637a80f627b0c9a41b44f09212f38c154226de02f4906ef34139bbeacc3f06739c8540e37334392d38ba1cbf4bc7debe77c09b35d2200216db15ed4389f43bfd8ae9bf76fd8243c3d869546e16b8e44a6cd1edbd2c58ef890b5a84cda889131e5cd9402ca4d8271052c6b4fe3f2dff54fb77bcb575c315b9109f90b14bc8e109919808a581c1809e2a188d29fd34ce639088a6683f641925f5b4b3529baa34e080bb47fb7ad9b43d0d67c9e6ae7cacb50527fa74e56d0c8b20149f5d332d686d48ebbe634c2b5d35fc84c69a5bcc93b93dedcf9fdf19a1fb9b75f6df9692d16f6c3490377a06294499e4b8ebeaa0cfd840bfa05fde21c0b5e94d13063b3f5da7b537caefe89069cfa9de9eb8f06e4d30125de64716f821bcc8279c0c7ea2e",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"89c1ee38b6697d0190c87a2aa756892ee09fca095df1e31aeedbda5750f604d9b8f2116e5b8f70ec57ea16fe419f2d213ef72b9be90eb5d7e98f2e398632123e2524ac80b31c6c0a07820848223569602d94fc16a3b1ed8c411bc6c74ed80573fcb1f3afce60b9d5e2c21d04f78665241b613abe12274a5343101a91e91f04e5d1f7959f574e743a10913e0817a32c320467f0178e3b6ad14b856234a4661a755eaf14b5fd88ef0e192e1631d14263d6a954ed388f5709dadc6c0f81d229f630d80be6d593d5e3ad03f9ded53c41abe595981d24ef27ffcc930e4d653743960f4e7ce4e251c88f55c16d2afdaed5e3446d00685c276728ba757520acb9b6bb0732a0e9836878d829e5022794d70ad8440a40a132a8c9ec1d3f0ccaf8c285fff425e9788d6150b74753dedb2ae8b36ff2f310249bd911b9181d8310e00810d42ef94cbb5a9d72a1f0507c1a382f892b23994fbe7360778b7efa9c5e03ac3231a57fecff1c5fa10caf1d26e84db0137049622ebcc3a64841a0e49fa390d1d43550c1346c20d578cff39fb7404fcab0982dde55f0849d312581d0c811a19d46f25e7a5e7e50d74d43760583c5cf335dfc11b2ec964f1dbbd0ed83e18f2027817ea2dffcf2b64a352c4fb8f11eeb4f1bfc01079251254d2112d103a1f12a2270cc026cbeb8b6f3e505abd62496253f93274625786b73997e449c1f35c742a593441252fcc845e1cef1b8f287dd311a0477407ce3b31661f7b2802c79c2d20d06e45f03aca4e47a959c6c1d7a9d377e1577fbf82a115921c3d94e3d9c204aa204a9a5b04d8a2be3269700a035371f4aaf1a42d92b9bfbee74492b106975b36d1e581d6ce2484f09e04fa91586c85f35e2a10f0d3c0afcb05327c1bc9d7429bbcc4627af8f76b86fc561844c2ae3810c84901ac09a1670ed3d31a9daa5d296",
|
||||||
|
"7219bd21a834d917f93a9b45647ec77102578bc2f2a132dfde6489b9095b4f7b740c9c1c4075333ab0ce7f14",
|
||||||
|
"a7f849b054982cc8a4c8e5e53e181feee79e0233e58882839892134ad582da7c",
|
||||||
|
"4c46854e9e101090b1436f90",
|
||||||
|
"ab2e189baf60886bed88eb751bf3560a8bd3cdb6ee621d8c18b5fb3aa418f350048ecf359a7d542daf7090ec8688c3b0fe85914aa49d83be4ae3396f7bdc48051afae6a97fca7b42c0bf612a42d3c79ef6aadceb57f5cfe8d67f89d49add0ea1ffd423da058297239e72a85fa6cd1d82e243a503b1b0e12d7510a9ee98d7921dae2754d7581e52acb8ab9e7f9df3c73410789115cef6ce7c937a5441ad4edf2b7a8c0c6d152d5a5909c4ce839d59594a6163364038c4c71a1507389717f61e2bda1ea66a83ef477762e7834ebcfaa8f2ee61ced1605ba1380108236e1763bf40af5259da07dd3e3d0fb2801868c2e7c839e318678687cbe33384e2ef5750a0a0e2d2e19e869a4277e32a315ed4de79357f6a12a8a25d5b18291316d9bf40dad2d05d1b523ade76650669c700a1c2965f4e51337aa5d45ec7b4981072779401d6d30ed69034053334bccb18425ac68460becf2aeccc75aacd3d6709f07ee10366ed848c8a54904af4ea71fc2117de133f01e1cc031f2a4d0779b997b82682433ee615202d5dfffba6c916f11a00551d56ffde8c36b303263e14adaf45b6eab0bedf344e5214ce52f071d2f40154d788c6870020791a03d2fd4ec5879d9026241954ed45cfddef4937ea3d0d45647f252be31411237983a1be340fc65ebab9a5620abb0e8d475af4e89e842e895eda0cbd283bb5d0bf20236c62d956de733d60ebceb42fc0c9adbf9b69f8d66551b0aca0e260625ad41cad75d752a234af7caf7902c2c5b62f04b6a8e019a6179d44feeb2ad5859ef1c45371e66f1af1fe0de63997266c290e27f0dd62185c53f81e0a50c296a51ace7c90d9cf0dda8b2d7e72a347f64c44262e2a544d1acc7bb05734dc1783bbc1903279092fe7fe434610aa95fc2ce5fc5ee45858f5e8337d8fcb0a468464becb1cef6b7e5ea48ba383ad8a406df9c581f1cac057d8711fcb",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"2dcfbb59975f217c445f95634d7c0250afe7d8316a70c47dba99ff94167ab74349729ce1d2bd5d161df27a6a6e7cba1e63924fcd03134abdad4952c3c409060d7ca2ee4e5f4c647c3edee7ad5aa1cbbd341a8a372ed4f4db1e469ee250a4efcc46de1aa52a7e22685d0915b7aae075defbff1529d40a04f250a2d4a046c36c8ca18631cb055334625c4919072a8ee5258efb4e6205525455f428f63aeb62c68de9f758ee4b8c50a7d669ae00f89425868f73e894c53ce9b964dff34f42b9dc2bb03519fbc169a397d25197cae5bc50742f3808f474f2add8d1a0281359043e0a395705fbc0a89293fa2a5ddfe6ae5416e65c0a5b4eb83320585b33b26072bc99c9c1948a6a271d64517a433728974d0ff4586a42109d6268f9961a5908d6f2d198875b02ae7866fff3a9361b41842a35dc9477ec32da542b706f8478457649ddfda5dfab1d45aa10efe12c3065566541ebdc2d1db6814826f0cc9e3642e813408df3ebaa3896bb2777e757dc3dbc1d28994a454fcb8d76bc5914f29cfc05dc89f8c734315def58d4d6b0b0136ccd3c05178155e30fcb9f68df9104dc96e0658fa899c0058818da5ec88a723558ae3a6f2f8f523e5af1a73a82ab16198c7ba8341568399d8013fc499e6e7ef61cb8654b48b88aa2a931dc2cdcf245686eed9c8355d620d5e91c1e878a9c7da655e3f29d9b7c3f44ad1c70890eb5f27ca28efff76420cd4e3cebd5c788536ddd365f7ad1dbb91588d58612e43b0460de9260d5f780a245bc8e1a83166df1f3a3506d742c268ab4fc10c6e04bca40295da0ff5420a199dd2fb36045215138c4a2a539ceccc382c8d349a81e13e848708947c4a9e85d861811e75d323896f6da3b2fa807f22bcfc57477e487602cf8e973bc925b1a19732b00d15d38675313a283bbaa75e6793b5af11fe2514bda3abe96cc19b0e58ddbe55e381ec58c31670fec1184d38bbf2d7cde0fcd29e907e780d30130b98e0c9eec44bcb1d0ed18dfda2a64adb523da3102eafe2bd3051353d8148491a290308ed4ec3fa5da5784b481e861360c3b670e256539f96a4c4c4360d0d40260049035f1cfdacb275e7fa847e0df531b466141ac9a3a16e7865947572e4ab732daec23aac6eed1256d796c4d58bf699f20aa4bbae461a16abbe9c1e9",
|
||||||
|
"33791b0d653fb72c2d88519b02bde85a7c51f99cfb4456dfa6f84a61e10b4a14846521",
|
||||||
|
"a0a7b73ca2fc9282a28acc036bd74d7f5cb2a146577a5c29dbc3963fe7ebfd87",
|
||||||
|
"eaa4d916d261676d632455be",
|
||||||
|
"c9a631de470fd04dcbf8ea9f4d8ac37c3988878b6381707ac2c91d3720edbb31576ba90731f433a5e13582aca2b3c76ae75ca8881a463ecfa789910d3a776a9ad4800521c6baa120b2f1afd10f32ef8da63f5b69f5e5fd88ee84bf66b0666b15d05c4050f5358a050b9d5cf1503719f56cd48ceba78f29efe2ae8092e37f5134df526831532f86ccb9339637e2c9e9b9036f83cc058fda23e826a188456e7fd3f4ee20f4e4a3221883fe3232b49db607b90a8956133ab95051c9ec33a908ea7e81a1bfa7bd06c09f0143d07bb23a3feeac7f0d7720269c93e2df19d03605828c8713b84d183c9a50954c12fe3b047511ad15ef03a63355520cbd224d06a34de67a671368e6a8f9feeefe48fc273764a8c69c00314e5d693f159cb5270544f3c4e1760b0529e3303ab308e9a6d03835a3a42aef2df5f7643696f707a574d1dcc676aeecdd9947ebe8c13bcf15d30b2d10d2cd95445a307c1d22d39450615ad38f9302c6eb9dc05764b0503d6a7eaff9feb94834853b47bc25660207be3e7c0e27cb3127b5402cb016396e5ff07ddc3df29861dd68a17f53bf660b23352b739d6da72381b8d19a9fc95da7efb79330a2b360dce4309860af429e3fd10cab235c4acc1d80d9e20d67019375bd161ab65648400f308815afe63cfc717f7d0eea150e687caac25b6603287d44dca4a7cc2f67c3bdd54450bd3170340253b03ba054ec003070eddf9c14fb9dc595e228e4968524900cb5d85af6d1e658a42d744e0e7eb6995023823a8dc33528c6715b2e1aa607782c8e1ddddad72026d657bf122ece8685f6e92236e809139325e4a3c069facf94c10b7896995bba01eb22c7b3a87ea2114a7649d7ed3e83d223e5e785c66a75119beab0968d3eaf0cbcc2d7ede95d024041e6db39a880ce3e19efea32fb89a40a2aae22f407e5fd615e51e48dbd50a8b4ec27ce95e2ba1928bf699d0418705482ed0ed7acc858dfbd690403c74667a88dd5221bb79940c6c4a268379c10343aaefb635982c14f33ad83d47ced9682961540bd4f75804d3d48ba8aa67fb2e3a1db83fbcbe57fec9e4ffb1b575e947f8bd8263c680357960e3a39382974774b5a013f2f8514b3c63c21dbfd314fd5d927d82ba616d76629ac018879f54ff84b5808e94af4fcfe1cf8845b65208ca5510b5b593ce6c109611652cd",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"c335b055b752e083554b5aa2cbb6556cfcace658d5c11b6b000256fd89e9b24c1e62a2d5b582580acdb2ad9869020465aeeabe83acd9eeacdc44aa652d5cb24bbe542073d6787ea32b2b3c942d40f9db2bb75ed7914c836d902dd2be89840948d82abbaea23952cd648e6191ce5b6cf912cad0a3165410a781e3650b676e5340980eee3b484008acce6a3e9dc5aa96d775677b8bbb8b323c6e9747d6069a169ea904d9f145e29d134cdbb0118647e8fbae638669efb9a55d50ed33568749f5304ece2193b0bfa6fc9a570d209ef61b4c59a2b5485b5aa6ab47d902cf23f7ff71c5210476e0aa727a01809b9f76b6ebcf58a018b3fbbe5f42976111ba58112b1d322f9312da068cdb86277bfcde66cb3607e3ea02a1494439aa56f302671f1f994eb3ab28b937043f5f7f3b3de50673ecea5dee8ba633c45089b852f0d772892525344ede6b521dcad15807b65e7ba348d891d47fc498cf4d50223d2794c64db9fa9b9766edb430be0c38746ab317b38ba9870a6d1fdabb70fcf89790bfe449b97fe01f6c94502aa0889f0a3bb6bdc65f44d1cd64ab88d4a7806b373f5080f9cf60183cf4686694f0059e2bbc5cf21ba0c3e8046e70d815f1444c3094cc29632c429f20aa06b49b0b52c6c7aeb8e34f7bcb53e93c2cfe2d704a5d0416876742c90762730d160e1869d5e0178dc366098ebaf2cae6f1f7563b555a52dcc194a5c8f718d50d27ee76fcce8e8991f4921fae85ea9476e1eab1364403120698b7ce8fd0a49cf79213f360a17cf1950f104494fad80adcc3bb1207bf250d57dcdce6ac8082a312959672361363cc227310b66ee8c04aab7b5cb33a81c0915e9c770a1cfaae2e8f44a0c65703927977a22fe58aef2f366b8be9a50da9376b46ae7562a82391386831febf359039ac326891bc58c0f2c34bdb6858859fc3cb4e392df65cbe2ec4f02c8425bcbdd1ee2562ab7d229d406d79a9c6fe4889c996c2f68d1fb5bbe3a5e867caa4249b934afd3ec71fdb088c54b15252f9dc1b909e121dbdc7d8a16cc00836652dd1f877ce363eed11467966f7ccb8f1a8d48146e69e04ad76a51937ad4f9cda209451eeca90dbdbd65441ce20fabfc8ce400fb4de136154b87a8b65c92740e9bb91d78521b261f806a2c6279c85ef6ac5fe1ea3117ff7c9f9832fc2aa6fab660082eb22344c1a3befe0628b6551f62a5014cd6194c42b8d475a50f2c9fb58c97e43ebb29005ed7fe54f0a4aa10074f1154152a9067d364dd7863fa082976a00db55b26b5ba0ea40eff48b90",
|
||||||
|
"f5ff810a41d4b34751e9942970d4c9f26b33f24689a4b1e4449b243490afc485af468ff01a42376b2bcb949b9f5e8d0b917f511a",
|
||||||
|
"a74271c184a82cb074c14b131fd91eb05870cb7c73c9e511ec8140bfe2f34089",
|
||||||
|
"2403fe689e239c2ed261b381",
|
||||||
|
"af9be893d5fd23aab42e6a2e59a8e7cb13d4f543db02af87cb0802bc1af7c717cd0093cc8244994cf21189146922b69927ffd5745e57118bea07a6afe7c21d952c13ab636b3c2e461dc9ffb3ae701175360156338be94b1fa7115799831019455cfaf5114010fe45f8fb9c77ec50fe06f2c5a32423edccb3b2210ee1200a78e1a3130c567542377827586ca8cf0c14c19fa1449a2cce9c039bb441b04e9c0a3f9a743b31c828032174fcdb7c894349aa68f5adf97dfe9294d24e6b5fed95eb994397883f58487bf5c57b0aea5268be7cee9efeab370f89805ebe5373ab2e93658fc078955ccf68b554dd5605005751ee8531c35ca5336a5d0ce273370c0dc9307779b86e96d2d1daf2620d67d43e1fb7800ccf250ca3c02eb74047c1d2a2bc7f29fff8320301694b80d0fd975f834337d00d5f0e4215044d52aa4ca21e6a9d7e03f186d7cdd5c48e3765dc926fb0a46bb0f05c50d9f69c9c507527a60366b7dc251aae1d6bb0d9c73735dcfab959f6fd4382fe2a1f6ad07affb0601bb9040f81b55a48f6a6c5f8ac4a2acc2b0c9a6c439198f7926460695fa11e0b0b017e39de5cf0d5d5f84d972b5eee7b5d1e0343b5485cd84b92ad892e5b23f3e803f5b363f2398c11c15be9f13e59922b0d49902dc8483fb142850b4226da2fb84e9b434a34f6bb67f575a9e57fde3354bc3077a876e260311bb2481bb139aa9af55df5074749fe532d7b8a554218a90cc7e7ac69db280bae5d55a174dfc8d325b9909a8da1016d4e162fe5ba70cf8726cdf291f5e47083d9929cd5e32021cbfd982fd0975f6f9baf4322b553cb3174b11c007559879f308419ff9e4e18eee8d3640cec8aea082b90f69cf3c7676c28af0265c24c91cd58a06513198892ce6ce1ab3ee9ac0a2e937b973a9cac06a039a54f8d994c13d42c59187f677352e5feb32a417aebec4d852b2595e7e67450e06dbd183279e3b63022a3813b37257b085bf8454d6890875a2950d20210a8df4f9da746722f62687e92f0e9efc3e5d526d65ccfbcc042fcac7964dbe147932c73924bdcdf62f9eae58d29e8567ffed90048bcf0566b952e986efeae4c477944af18bd243c3eccf8d88c06d07279adad037450cb8547a8aa0a74223f4851747c803cb21a2dd027e7080aed75038cdcecbc4639d87763cdd41829a1b72cedf0d722b180d0d492a5740ea7607b95f3201df352fb1ab28149124d2df5d5ec106867897b537302c3431402348f94d28eebc701ae1b49d10adedea38f1654fbc48885e59e6e6dfd413c6b5a97d8c35dfb07a6cdefe317bf61cf91",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"4aba5a776ace38b6e2578f0007e770d264e39c49f588ca3547ad2888365e3a811994f8836330394587c8458eb0b6611499fd5d8e8527c3cdd4ec550b4a8f8c632384e786b420cb3be911c999c72aad60270aefad31b27a069ecf11e95e9d4c81213308d554d3103de4d9d6ab04830c2b8dfbd8bead52c44c21d5357f72810193b5096809dc7846c1521c6c569f78812c735aea21acaf6dce84a24df7234e8ad857f3e1346b27f5bd436113e2da950e4deff96e9ba8db692c7db723a105ae795da15b910c8286cac6e7dda8c172b70f61b07dfd58596684d61da8772356f180f74c1103ce97cd947eab3d401df44f7fa4cc7cfc25e280fc002873237e64a375b0b4797f4b4613c9f150090f44588ee8250ae44aec6546ec8dba0f0c1eb281cf66fa4eb141617b32b28441f6ddcfdf02d9c34cc62893b2b64dc2c26b74433adb3e888c7fea07b19c8cf39269c2716b9c35b7625d4a141397d6d5034b193d2657c6b2d6b0ba874c467adeaf3d501ad985d13be21c4ff6b326cbb671e4f4973bba49116a0399b6491394f850e4122969e4644c00b442b3da0d6a4bf25ee22d182b3f822fd83878ebcc713cb183651a67ca66677ea81b58b685a3a8e385d5fbb0147ddfecb558d881c914324c794db443b31bc15c361912bbbcba9e418f99f2a416d190cb29684df27c7f3ff6ccf339800efbdc4514ee00d1a89f12373804db4fd66c1affd467f251e73147b3248033327b0f7790fd7861a51773dd4f78b89e4e24b94df9203f4a077091bb9411eec78dfe3e1dfbb67ea1cdf17e1d6936bbb75b74055495449e9cb52f5749404610cd444fea3f0568e0d35a5ef0c395ab7bf0208044b5c4e2517911a9c351efd31f33220972287253fbccb1eb8f46960a36b68a7a6b4f5cbdc86d668bbf555fde8881e7faa9594da425ff8fb54526bf7cdc4af64899530561c06bed7fc04c5d48cd4542779e901bc48fab79d4d13850ad8247f51b9afa7d5a656ada25b6376d837cb0fa1b4016dfcfc158a39290f43f133b352ed52fab2f951509bacb41284fbdd849d8185fb7e7200f8ab2a07ef2b3b927e18e568dbeeba2c7a66e08cebdc6a6069ebe6656a586652f3905ae2bb867529af6a827b494c97b3a378408f44aaefbe86c613e11e7a44020a9ee4b62569dfc4c462300daec7b1424ff1c1849ca1332367470475c14877cbe76c820cc651c18ab3f18852b93994f93b568dc7f7b0eb5f07ffc4c9384c851fa9071c6f68ddea1ccf627f889c0471c76aff9f52b07ab1b86a7671a2b2f6b25c0ddebb66ac95737bf7e2f493f7665b5265eaa5166556cecfdd3062802724ec24f3978b903d0f0c24e1f0b8d967142bccfed0d354279223f4c28684e9ab611e9ef89a3f25993b5a8b3c0354931780501651236a78b58e7d7814f251b053605f4c0a8e7193b9cc1ee5cf7378e6f3c8fd44ec57bd91e62b09fb1d6bab60cbfabcc6792e6a32ea7918a9ec9180d05a7e1546d5d2d8bbfde2a71b4e427c0a4d28d0b6473ae",
|
||||||
|
"921a401db90935c60edda8624a0590d5c46eff3522e35de2872f6f9394e24126fd8143b68a797c995624fba0298b75eef974",
|
||||||
|
"6a4d35ae03bf277f587da4541dcddf92bbd906dff45d5ff23c0f60ae53b062be",
|
||||||
|
"231b5780fedfb06d724450b3",
|
||||||
|
"ba40968282d98849b19d867f8b564ea5a81d657516099362926bca4cb6e9ae02719d10c8061f53008c727a0eeea5e1e36c9e55c117e9434e213316c96840231a1e356b254a9981d4a6ca3c66cfc61018bcaade1a4486506559e6aa3a86bac980d391d835fd5ded98d10f1394d84bf1bbf2cd3397890d704154802f7864ecc753db782fd3d19213ae65ace4770e1bacf32d61c6730aa5adcab4d7e2e437888c11c29abba4890a17a00f67a53b660becd94092df0598df5ac57326f6860593a519e28bd4a39f6481e1a4748881fd5f0456a3cd9f28d1d1e78dc64030cbd8fdb2c5abdab3f13d6ccccd187e71e989f8c486929efcdbf2a763effa95af62db5cef95e9081b818275c69267022fda4b7fdb8c650b491a785b03d4d0186625962b6326ec3f4e176373da4dc1f83a14815adf82c6bffa7c6967d77528d0249754bb4d17656bc4a89449b16152a4a1aea7eb0054a8892f271138971507d2f3b237ba5b620f444544e4a8c2b1ab4f9168762c27478c9f776c47ee2e9ff05bfa35ed127f0cabe7cc053640bb8aa01f8359b74bf89ef43ca94c48fcd201eae39d1835957eeccd6b3a852f4e1bbfef9a469f42c764481ff8408fe5871afeeae7676b58f4202199aad50a596626dff97c8e60d750cc59da9f595ce12ce9afdce14481cb1e39994de8fe4cce07845110d6703dc59d34734e93e9e57e1c52d61f44143a2d290220a4bad5098d098ee65ea4b6757d8a9bf5485aa3d697a7826d4a285186f5da10eff707566c23c6a15033365bcb498c44487c72d96402d1834753fdbf86770239761f03e0dc8963766441da99c0813e4f1df5a1d018c8799861a396562eb24ce305ca15f4022d83ea3c56b68d9a7ceac4742ec0ce50f4d36273df26005ec2b051fa071b319be2d8a5ed26eb75bc1ea83761b8454db234d15d84d6706cd178981c1f156e6d28f774aee3e9a4fade022e71b52b50aa532b8bc7fe464f22d6eb169c69671875d614e987658820c2f584a4fea3008afdcbb646dba3d69020fbf503f121be3480344db23efdda0d255aa058c3ff66abd3a5fe35db977521608bba7eddae72ae801f4fbb12a1de4133039e046ceb8db87e465e5ede1d79a08c857d59076d7ff858942c31e15cbbdae6fc15c3f9545a0825d6ff8583c0aba8a7d143d27b93f6caefb98c0d83bd8715abcab2a49087f55a9daf9090eacdf45be08ad80b5df5070e1719f68c4cc8f8711083f0f7823a09ec092f22df95fe9e95114fdf82a3f6eed0bfc9c0aa65222609442776154a474dbc9e662cd5dce66846572e52417ee5d7eb59287d07ef60a9537fe1f85c7fa74fe84dea0da235ac7574335e6649b54a6bd33397df4bf4a7976c4ab868aa702766d2bc8d2c82c2d1c2653fc8428b8d1e61852ac185a3a0b416dbcf8eb54c44967ff43c44f2b32c6d4a9dbf2c2f3a587b430aef50f0375cdb4c1b319ac9aca486d9bb321141b065f52f7b6decaf1985531ca7bbc3772a561eb1efb8a6297075920bc432131a5b211bf25e35fa31e12833bc77a9de14c7",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"6c0056937faf1023032df1e2bfacbbc58bb022eba25ffa020d4eb26f0caf0678af5d0b2f0c1b520f4843f107f0adcc7b5dee66ff4d61025bafb4cabb64d133132e3e423a599549a1d83aa8c8e774444462aa44b00b460bbafad5755ea6a872d4e6b40e3f4957e0229288ea79fc2ebe5fd9020fe4481a9f42ef14a196bd136aa3c779e311d0c333624c1ddc484c9aa7259cb609e4d0a826c0bdc7567adac01da23900b30ac4e66c100348584fe200747eb67e6287268947e3509d5d2b5d7bcd977b80a13f660d4f6956a8b938a82db75eab19e5d2a22cb5f3c9131e278eebbe096b5f49d16c983ac240f3fbe821b247cccb2c9e6e59546122677f49f56a07fed56647a6d3e0e09520d49009f54250c10e7c607cd5b4ddf81b5c4110c6490e9baf56418236211856f5a85feaebafacf92c0c7501c052f9dbae3beb7484f90f334f50b68571cedc67763b5161ebfd5a1709cf18c92112a4cf4d8f43d1895204d8a2ba5e14883a7bff75cc6060cabb77d38a909daca2417befd1bfc05a11c432b47f90c807ca4306400f67a0d92218adaca84a584a8bd4395c93f9b6a4bde9583c79204444634a8473b1244cd33cf980e443d82ecfac672b3f60e2e41ecb3c5a445d9e88c0e90c339a31806e6d79ee52bdc6808c73e8b7b24899966664d3c1a9305f31f0483e24e36fa451dc1d3f2eda05af6678971e2bdfb7c1461c9407c5c466f6b5af34d992a37de3809a22ae75275ddba0f4f9cbd4b18c1acd212192e587889a36bd73c860f0abe08bcd8f00f5ecdb95e1d560b586eccf530df0e5f3776d8dae2a01768bf1226b7ceffa7ce4e75879c82dd97db3c64c06d33cebc6b35854618355d80e46fa79c3e9743fce5b974723c421a077e7ec7dba286881dbc1d53d442a1552700fcb33f83f73c69a0a0ebdcf2f5d461649c4d0712c514ded268a31509f83c1ae4ff4a68e676d29727be641aa4487c08d4b90ff78e24c6508d69759751a1a23690ec9f8763621e8b107295b4bb01bd9fcacd8748e24d996fa70ef6f8b0992f4185bec8e920d7643159f9f604fba394b6611bff435998b2f097a9e948430899c8c752a1e83a061983f00f88ebb32da214399167932a1a83c1b47d09f77593b03cf6521520583ea4483e2d33e14ad60584676d1791779b532c085d238df0d3bae735d0078e0eabd63cc90a2e13d023983780afc8f83b1c14437937c16a1b7c41414c48cf4ae49587ad9fa5b16fc949a749e96032248c4667f58e295f999590dae1d99a2cbe3fa45bcf4a1d3f0356d64d40367f64b2c5cca843e5f7dd7b88a85d52328a00622e6c317879607bc036c9006d38652ffe21c83207c00f8348a7d0aaea5aab4c89077df170de6d41052641726eb6925cd85a9ee01a9e636346340e209ea96d17b0eb0921b96662ce9cb430fb6ac348331dd7133875769bbbba99dc49333950e4145a15ddb0789c4d2ccd38878080ca9e57ddc6cd5452790eec45482f8e990392e319609391fce0beba19463a9a00d8f1de9fbf22f23821de7d69fdfbf3019ed61aff79acfc5a6ba663a1e10da2b9ff7149aea43bd6c61a543008402309df0924de72c1cacd2d6120cf422e61fc1de345cc8771934d8be77d9437a09e06a9b2d51c849fd9a200fa714328d34f36b684f33df6968b827df916a599a4bc3367814fec21198e2213ff653cd2a463892966c72ffd42a26b3bb91",
|
||||||
|
"0d55dcd08b54e58916f622f81761ef6a2e19b167ac47d3",
|
||||||
|
"e42e1d6d44138f3d2bf12c951f454686f18d590fd30057405b5a3dc2b317fa97",
|
||||||
|
"1e46a7486c5a03fd6758d938",
|
||||||
|
"fd3c1fac10cc82e49235fd57f5aea0ee7a7bd6d539b138d4b3fb623aee591615c1a61228ef9673113a3a90a3687a12d4c6367d5f7bc67d422fdc4106455084d79c2c42c5e86368dd164bcbce7925bfffe7d96c13a2f49aac8e9d1ada3554e3fdc21aab00455a0f33b0c1fdea91b3588e7ad301bfccf9940027332fbdf966463491f7a33c093e0a13831ea9d2183294f89f414cf7b5876af04fa68d594430194429df74fa5915394427259e832bc545c13400aef6cf16620d48280798a6e49773c9316d79fa1dc758e54cde2e2cdb856092d83f4e9b698385cb976fd6cc2538abe055273a5b34a784182ea5e7d3ac9019a05de5e5afe4308a7ed2d363cd50ed6a52df1c616e4a82f607ced768445d13ae4884f2ae1f9fd8313924e8a1a8a23905c92eb231f638dfa6f4cb27bbb9844e05afbbe2ca4d1a3b3a5b371bf33c9ab6f82a7387d61cf8bf662097624145a983839b0cb9f4bd07556800b4054fb3d0bac94f44bcc9b4ac49c39f5571fac4e02ff09f08b3ed5add4bf8bba934e9feb773c0590b45c45fa036382f3fe9782ad19107d4630321e414b7b442b64f18fdd5219039e5740f34b3ce8925d1afe8a39e35ce8db086060bab63b9720700499f82db19a62897c6d845389461260303f9cf2bc7235a898b4620c2191ef05604a5c8c783d58009533a86b27c12b0772635d34ac53993ccf174c9087073e5e69b26c0c3d9f768507ac4d4e2af847b65e3a6e1b7a6dafb0aefc190871cdae6c60f0b1d6137c351d4cb211870791cf4cb8af2ea446f6401eb9ec8a5bcebccce898d1dfb13454df6b35b81ed6d7637e6e261e004080c60944f3a08e8e5fc7e2e4939e7c2607c8cf07d1d10883ba3ad43e2611826f245df571857ae0a7a867df9659f2082c19f94ce400132e48c7f8de2b102c7f83ba5cd1e785597a0ba0d73bb81bba0c00300d4bcd6ec25fb73105a46122873bfa729c0979d8d314ab7ea52391aabab513dbfd1cf01c2990c0a3612f4511c2bcf0f5a07e659a881a7f99c3f1fc4a46e66904427fe26a4a80a904c047d090c861a075c0ae4e29bfbc18b9620aaa42237f4c6fa76ee7491ee638ab5f1cf0b440759828e1ec519679efc776eb1468999a00f667e87199ad6891e98b95fb682e02517b024a6bb803ed23c944010cb7bad0733eccc12d6ab6030c6e88d510ce92e2f98fdcfaa1e37e41fbfb4e99589c0e8efbefd40473db42b3a73b57b22a2f8c9bdaab16831f1b117dd83a77dd01ee8d0c2e92203adb670f4fd65e618823ad196220d70e014c1aafd8863797c61c16382c2600062683ed3a180c70891717c52da15191b02f25d1715ebf33a5e6037092421989c942082f4b836423cc3e976c9bcda185de36f06265dfc250a27d2de0bc48c73b3bff704f3b386f962522f572108458bdb283c6ab3fd33b3ac13a406268fd5d97e17db9c0f780b4b2a8f761d15a4d8b3a0cd73357ecf4d26a6492ee069f19325823ef50bcb2f73326719a57b67eeef506fe8915a1b1ba1a637592268257b91e9c7c5d33cdd947967efc1952005d82ccef9a3ad7ef8ffbb6b658983d64c51242ba53f8f8963245b87a25aa9324c527e53f8c11d55f30aab598401589acd13f090541b3b057b162190f27910718b02a6b8ddbb8ca6cf40bf0d2848f4b76341bd5e78f476862bcdbe2d1bac84c0566fb45b21388221ecd8483d99fe603646b1a9f38a49230cf4dbe5d7883d73eece01bf",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"04892b94c65685f2eba438322b29bf8439938590d3e0eb10a29e279d356cb439f6dfcdbc3552af21f7e753221012a649a52bda780bc589ae63b04b981dffd113df9fcf14f17e35e865880a769bb1bf40dc99b9e85e4296c1f2e1590fe02b22bfcaf2d4bb7009a4d692ae4c2d5f0b6d3ca526240368bac55b9b1e6a7b498d3b137f0fcfef1873c5aa2111d7811d45bdc26be1c5d49b8a2f36a999b1f226ec06a5fbd59514485abe696c96ea89dba74b4688101a239b495944e30b3609f73caff3114407599ec5c30a5bad933655de7dddef97018ae15acec46504cd5d417c5052c057ac5f1c6f69781cfdae71db2b4fcac35054a4aa22681027356d68b2bdba721466d130d53ba8f23857631382b2de450232e9ad5551bd7c872ae439e79eabfb057d2bdab8d4ccf02b3003ade2e1f3e514dc92692e4fe5b579c9ee6067995b6c168647ce5a13be8543c23326a3260bb7029d2030ec05e565ced3c5366d20a283a6e95201fd108640d2b96676df712de20e4e12fa53f85f22cb24583844fabcebe40eece11e7221f12c88670bf994ed08e2000236f86258c386b0fccbaab8b68ec6a26fe41491d540193c4c12d1391ab3391de9317f41f505f1f1d09ca9862a6f289a533d2b297d4465c956360371ea3c8ed36e0d1563120654e3a2fd69cd6c9267bfcf92e84cd64e162c84199d6e552b42c33857264b5d7a2e007797cde32934a3f8c68b459cd95bc85e7466ccc9910e8dca65b315c32e43c3a5da908904c42cfc8ab74126919ceeef1054bbdae6ca67b02f1ac5f24808b5eee24577e609a3e3935a24b9ebc1a8dad1fc96abe26012928f2d5782755f3763427dda28867d0b1ad830d3c3f17b9ec278346e5a9480ed23ad44a523a4dd86e65a610ee0de1afab64ace7a3b4918fdc14c6b1ce0ec0903994da9bcf18643d7e0a4e6c08200bb394a89b385d2cb829417eeb0f7dab9fa7306a330f82973cf0917b5ca99b585d2ff0e8584e050077467f5245ecfdd5942e4fc72dc26e5ab2ffc61f996167e68168cee9a6d3ea1e1a696060465e35da8c75a1aa380004faffcb0a992c627fbdcb4e97721271802cdaf08d214ec2fbcb389d75709d7a6b9d35662661c8961f93d4a705e7188613f3769114c55400809cadf60d3b6068c8a5ceef078785171b59be1140c6a754ba1de5ced349df63d67d59d3a8ca3c716ffb506772d57e9e3f2caf7fe346c4ad64aa6c37e43b9bbaa8f58e51bfbac31fa6137728f8e5b728025697e5ad5c8301f6ff39eb2ad595d3cb24257adee88a84fbf1ade4d7550cd9ab94bf48e1424ae83184c35c5a5920157d45805c2e0ad129fc7f0ec3c41b9d6fa04cb8918ef379b0783d1cc2863cd80382585fa05320ca4f9fd90353e490b384ed6c166c6f802cd7bd39aa43667246e8da96992db7537d472c709b01114e95febaac5b1a3c77e1e9a18c2d180e63f0d8fa89f6a1ed63e909e4741af5c2a0e47d4d3f8779b7696358f58060f3f461cceeebb390c92779d30bfdedf1b08ed62dcc05a545bd0ea915f42976e81dd8a50cc4689d8d8007508bf53e7da5bd43c3894968cf0677681c6b818353af6bf8ac205139add1310e5d363ccadbfa0eaf735808325e7f9a6aeb1bee3ebb4a27576a88811859c216b6f84371c43d8063a0d87bd326eb6d81c6896ff534ba2c9c14a51d2cfedf33a5c787279bb4a7ff65706b389756a6191d2f791254233ee047d40d64c2dca878a42f903fd4382f39a89a723fe11848fe37b2008be53f7c2d037981d6462a4eea49df1a2e074957afd3c9dfb4d218a309cab395afe301ccf",
|
||||||
|
"67b5eccb1790babc2dab5e0d1ff3871c3024177d45a2ae",
|
||||||
|
"259603e1c3af3fd0ce3257eb627b02e0c0a48ea2f175de3d8c36570a445e5369",
|
||||||
|
"e14de73c4b17581a7e0d0649",
|
||||||
|
"33522e67ef932da5fa8abe628b51f3abd5049951dbc982ea95b7769652d4830c588fa45e3fcff094c8602b9008d7b2f9bf6c1c4a8cfb515401c7c44a7ec42ccb967722a710199e121a41160b1ec581507e9bd2e2e506b10c4b5a8d6977435aa08e27504957cd49e756e1574c4ccbbdde937de35128b7ee3455d2e665c596c2e97c253c94e405f85eb5de84874c099b4a97eb8f492d28f2e4bc64b228dd5984e76ca08376d7f1355ba8e0fa60fca96635075417d8b436278e0fb91e3bfc7d61ca8c7407086933c061b2d318f46f352099e1d317d6c44098539d1d2c1b7894db668e7a82ff991864fae236570cc420a4229883f1e2242d05aa07e175bc6abe11cc643cf1786a4456a2de8c066fb1a70fe387f149ffbe8cca7b110e256fd0c09b1d3bd7381cfa82fa700c8db1e79809ccf75ea52d0b349264557046e8703a191ddaace00ccfc513db5e78810eaac0a99d7bb1a5725e722d4e595216a0e12f3a7aab2e623ea9e1dad06169914bcd51b643016fea7dc3f2743b1e65877f1fd5581bee5ef206d86494a587ec8462a170746fcedb2c9f99090674ee687382711b4610ddac599732453dc063518aa36f5b4129098fb9fddc02eb8f8cfc2fdf0d904ef4d6d06014f977b29d0e9aab4044ce9c662a18b1a8db1ceea97854e90704430fe9b1046b221b27ac79054fcc68c3abd6fab7da66e255ff0cbd0506c852e961e619615c944cd9a05c25abb63742f5da7bd9939feb0f2f2208c8ce82f551a9d4d70e935dad018e3e4e6998e39670221601c3e34716ba75eb4e2fdf53c4d471c444330514986de45cf44d77f793c17e36a271fc65e6bf08943aef4c66547dc310c7a430e3fe7a54898de48f69f282f52bbdc4daabdb325cec7ab66fce1aea4e2fd932dc1a316c821f5220ea437447feae2fa478adade7cd515a27d8c132d0299b3ca1bc8516c9d9e7c65c38c238c69f03e104eb42a29cacc8d79b808ea6fb233a5056201e3697f81a2d49ccd8b8efd1ab0fd407c16a210767d1d3ca798ee53a4bbf1ce5090d321b1a64fc2c5f013c23829f5b0d2737936ca71595a1d02711c8a7b0e74654e5d76376ae26977dd49c68e3c0a7b36e047d44be42d732c31f681bd7b1b4b339f004ecd847960377acd005debfab13d0fb88355025877630aff753a7cfddf6851e8bcc8ec37b8f9149830f47e6b601098b2ba19a4c0808e31e8927b2525cb82bfddc9b4bcba2b46bbe768ee278fb89010243d16f9679f5ba4f13cfe76b5beb16c7b28daf99b0873098115c2233ee3402ac0f6c899a2cfcc83b2ccc06676999ad48017c4ace507080a26501993327ebdcbd1e2eaaaa99f4998b716cd9e36eb26b4573a03fd1d18047198fdf675ef4f979864ac85d230a011c69d8b6c45e9efbdc2a03f195c9731b4cefa60208ba845c0978e73d082bf6d6a513b93dc805a4f5973f4158f60a200167ca88704a15ac5ab1f38ed455a426f7c6a96b6bfea2ebc1ae1247cfe5ff29ee81bdbcb53b03b89568bae9a6f311d2b20e31c2d91bd18fd93a37be266d0de8015d52e325f78356dea0b77cc76f28e0f06e4ec705d1328340013a77b0b6196f44b7712fff4ae0ac7f6afab9456a95012b7c6d387285487476d189977e28f6c9d1a3f736320d61302c2d627d5a7ac8cde4988056b55eeba27efe7e640f94c115762ad5849423ae138c76f15b47bd2a2bde2c492489b7980aaf1c4e32a155f858d7be4fcd0f8a18e7b5d97c5a08d7885d6d56222ef49542c7f80498a14a8eed1c092543aac3439966d5b5d0cb9e602f4fd795c09d652b64f9ab67e38f48c88d18e30a9774f37e9c77b7a94cc7310d",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"4ab8068988d4bbe0bf1e5bc2fe1c668cbe58019c958dd2ec97164aea7f3f41c9f747527f1c0e5fdb2cbb9d2ad704b6955cb731f14403dddb1a28c5996707635e4eb5dd6ac33d46eff8e319cfe7cf6443869534ca9812a5b23a6b4ca172afffc064dc2b28197117115431e03c00447f87d9b45172c6f724006270a1d41fa094847cbfac9630c3a785f488c1f5cc407ca6f4cd18bac43cba26ad5bfaccfb8f50784efc0e7fc0b504b43dc5a90a0525b0faf3c8b4b7046fdeb1cad87ec667ce3eb6cb4c358b01393f3ffee949030ef9fd01c1b2b9c5219777eb6ff5b1d7c3ef8d8e3bc2193dfb597cf942c5fc50befa527fac0b44cda2bbb811b06ae87459750295371cd232754e2bb7132807d1225950ce64949b0650531800bd0074177677acad937ee008cc0bbfdf33c6b0552000238494be8be412a3e5cfa359e619d092c76310a76bdcb22abbe6f16b3b116b5f95001d20e42fc3c9ff6723e580f378475788eec265a1ed2087de8cc2eff72184f73fa5dc6e68a56dcfc85350bccb97135386d5b827c2d9aea065708f5c921454d1b9303f21d5adf19e00415acbd86d1e5e42d78505b033a515a435713649c50702f54623cbf31469f355c3be2e30dd8c72b4127764451d79e952ea1f9bb0269da56dc07060d5d9542a9c1258ccefe53fa3f7b6073cd38026256b45c01b6c5dc0d91e3139f30a8d1da7a076738f5bb23352693a8e3cbbb46226fa22416680013f9e3278913d06aee4a62457357f0a68d173a360af5e1411840e34c574b4c6b352f92ce33632911ad8b6710d357b7607ee19679e777baffb8ae3c0fe9786b2e97fdeccb5105ecfe81441f549bc6b50ab84b749fb33f8f6bddcb6bb733d6d5dbc4b29725b8741439b8239e53fa435ea29ed3324202b1bdd07d1987b0e06d8cb51013dad897ef02401290940ce3f2af72c5d1b4c8836299008c10b16c7e3e119e41ec66d9db6929ee09bdeaeda08a50665c052edf77b7dff3d8815046bf71d5015e3bdb29a4f507aeb2e28c536cdcc9b8d1e89849a0683d78f99dbfa90f94aa5dc08587657a8f042d718080de5d4a973f232f78c387b63c7143fc2a4380c491414a18b6c4a7bae2194b62e798ad7ec7d09e409425f6d0973accb17e4d860f8ec0283584cff076d93bd9b0c4873f9c57cddcebe3c3bc8afe793c6cb6b26c4582847b07446b7e1d9757de6bdf0df826cbc502bf88cf3a773866d3ff293034abc4afa3091b2126a278f50e47f2f66ebebb616e342098ab690f7f5828bf8cc4742c677d378893e9f188e8397bee983a9a0998de2a31798330f8db59a8581e1c847589bc0e2d95ffa68e39226cc15cf6cae5c4f5174e7848375391dfabafec202565ec2383721339f04c5c5d1da953d88f18cda65745ee8e99805e35203a6545a0416923b38c5db3c8aa00d64354bed27d7c78c4b257534bd7a18107ebe64d8c27b6afdb330d8efba79fd1fae480cd51fd3626bf8d79fb651b7c6cf752aa737a5123558420d48fc86451b358d270aacfa6c17f343b7a9956e6f64e4990c1b3f1e5097605edf5ce4247819b19f245e9a90758dd42c36699ba5cd7f3ed99a7df7eb155749f4b42d192c47cacb6b2865fb9ef2cfca283865cd06e40cdf7f89d76a9e2eb393e2e0ac0e2776da929f3f8e3d325d075a966d289c51347bd0bd523a5c81edef63ce9b72f5114c88b08b16edbd73f518096240a5b37421843173be8df4ac7c587a17ca6f2916f7d9a10dc75f81bc778a1eb730d12b51555cc414eab9c066113a7edba9a7f1a18092ae47f12f0368ba211feaf34a3b48a7ff5c91b81cf7c95675a4001c95a19d284fe4197fe8823909a123fcec5e45935da12416be1bdf14918414ad19b54a41052f5b8417ddbd207ee01d6a3e62fd9b0321b1c13d91d6ce15ea7b2ea0c670a5f5cb290ca8e62c26c6499104ab8e9fafb05170ede246bbf7313625d1fc9576f1609ffd08852a2f4b73c04f1f4eeecefe3f3eeb2185a618b6dd3e87d9d3fdcb349cc83c21f26b6c662bbb857aa95378e991640a160a23cce76153c134508c68ec54a5",
|
||||||
|
"0d471079ad3c3432b6de852ec71692d12d9df4f984554d458a9dd1f28a2697976da8111ae4454c9a23d1c8eae75bbc14f8b00e7c065bc290f8938282b91a1a26c22b40a6708c40945d087e45633a595beb67d8f1c29a81",
|
||||||
|
"f3dac58738ce057d3140d68d2b3e651c00ff9dbb2ca0f913be50219dd36f23c6",
|
||||||
|
"bb2d033de71d570ddf824e85",
|
||||||
|
"238c4e6be84bfb151557327095c88f6dc2889bce2d6f0329e0c42a5cd7554ab16c8b5a4db26eab30f519c24766b1085e11d40823053ca77adfe2af387b4dcde12bc38502229510606ff086265f45b1087375dc4a022eb0b641101c74ad566ab6f230133b7aa61861aa8202b67beddc30dda506691a42032357010d45adc7ee633b536a2fefb3b2143837bb46db04f66a6e2bc628d6041b3d306ff78e96205ab66847036efa1fb6e6a387cf8d5a105738be7163df9da0db48e3d8fd6a786f0f887968e180ad6888e110fb3d7919c42a7f8c92491d795c813f30ea645fafcddf877f5035f133f864fd0ba1415b3d698f2349ebe03d9e76610355e7fc23221c5c72b1b2628a40b14badf93288fc4abeaff5306d274f21938650ab236a39496d3f8a6e9086eac058e365d4335b51eafac813f9175bb7bebb75605909ec3fde6515694e119f7b6e96aa1d6d6454c3a7dddeacc83bf0c1f5f6c2a9dd2f460f3e5b074a33b8d7904e6988ae43a22a87f0933f812e45c4c518bf83e606bad4c3c55422ab2207e9d3cfcbc5819049f55e35b9663273d9d3a6f8a897fa38b0dca77eb6c344290cc007b68d913187f2cd480a40262623a4e95d90d5701ac2b9d858d70a27f0672f919c2ded1fb89134ac9a8ba6ac62931c832372abb70e811dc50cce264ece65e87338231f18ac007c5f68f3b1c5904ffbb2e1dc361d53914917770d66afe28c547d8cd5896d892cbdadc34cd6af348c93bdb8b072f38b085361e62ded7a38b4368824c759ec7d2cf4caddb9191e5deedc8b8388bc4ba2c0672321bcda3a7343c9ea71ef03750912f35624d81da5fa8a6ee676c4efd99d0c7258b844ded7b35d8c8233a316b508d79c7c0b3edabad5db9543615179b1c111bfd78b79327ac5b4155336d670baa592d441c810cb1b7c07f3d35473a45b57e780b7d997782aeecfc0363976fb608d6967844ed00b63ba75996054d090aeb605c195b1ff86f9d9ab5892d27632cbb59c06b3ccd69d33ed5dea9398f00b7c6404fcfe2fcb5924e4cb75cbcae0a1b084ea8b15eaa5847431e9ab70e4afe15b4c82239f6165e243e3b76d6c91d23b16edecad8bcb16898641f8e323671452034a8ec9b42b29cec0db210bad0444f1c5bf3505cc41d514d5a270d556f0a34333bd06cd6509ba253a6ba7a6db8f1a60c99f0c3d566a038a72f1271a178cc3ff890b0df1e7438c0c1a12d9873643e2d7bfeb92379545de50834abe2a345faf7ca49beeab87ee516dd8598b71196b8cdb15e7200cb5bd814338babd74c565faaf33d9a8ed4209b417345a1ae611880ea22ab2e894d5d14a28fe3835d3b2718125f0e6daabd85327455646290ceab89e579ed5e1d72a0172e4a6d8da70290b5022c941f3866f96cc4218de5d2622d13af6dab15760a1ec5d10918267f9585284058aba611ba07b1d5711cef505869831699bedc2b190fe1d578814065c91d87a8c8dc9b0d4dae0c80cd241f0bda3a6d5e714c894b7a48b1e5eed4555f103eb03c9db30efcb855df422d7451a6d70f28174c7ebff536dd2cd2891f6c3f264d632ca924c4e0d84b37cf8e06e6f2e29efac6cf008cc27f062441278dbc9f09cf44987e0e9ca088a48437b0b89efb9cf00d3d0c5fb449fd4b64e21dc48cf300c2d80a502cb583219f1881e78e647783d91dd2f3b389a1594eefd8ea07d4786f983d13e33cf7a34e4c9a0ec4b791f1666a4eef4e63bde7a241f49b5cf615888bd8130743bc8a6d502bfc73ab64d1184ead9a611832b7e24483a1a0fc475d9ff6166b86a18a3dc96910ff182cf326456c4461ce8acb3467f801890eaf1ce0b24791da9c650876e718c0bf43c475174f9712dd4a228695e8f8b2b23fc4a06358b4a6a8e1afa87a0280c3e098f218f7a6d6bd716f8c105a7eb799ba0220837fa5a96c8a22a826a6f7ea9d7216a24acbc7b0133210cc17c8190507badb421bc54997ff9340cdc1ee415126ac46a4fec9fee12d40f06300f7e397b228250f36d6f0d2ddad5fe1898ea690e4c7cc3a116a70bfaf6d2dc996753fffae40ba5280b8356b7ab4ffbc914ec74eaa070581fdd1d9e5aa2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"4d81b652fee892d575bd13dad913d976cf0517c819d5183a72eba995b1f27efe743451721ce34791a15a6b7a6e44f13d4a080563dd1d9d4f0946e5ba3863b9ac970a1fb4ed66458ec1b1092ff5fa6c3f0271a2df8e3f2e97851352be760b6a0e1589c202f00791b1b89ae0ae944ced96bd90754bcfa3e355b735132d407d3b5507fd57f705e8a8bd82886b16d459ac91e921dcb8c5bf0d7cf420a9349ee589a5e2e19ce7c944a54ccc1062a0690f3152300d0bf5cd1871c1391bf6d7007f7ce26018ca2a5c6f76287fd8c8e9e7f93b1806460dd35f7f95989a8b6f9a0aeb7c6b0346955fb50b8735e34f1ecb4859e34ea0f022ff6fb797094206a34cf120b7f4664c531c57da513b296f0671c8e9bf68d9e1674998fe52da04f627f516dee97c2b3c988216e9bd3f58c3b021ac70898651f1cfeaef21c4f417ebe92dcad3aaf50f4277262c356584f816a5a5862f2bd720fac10f1b86033371ed603bc00a30cf4da8f579dd5bfdd571a37af7d2a5cef29f9001bb1605ee87f24ec3b259f381a69b771f78d21c4e43bfc83a916e08830d9885c8ae8ab6367c05f92e5eecaf0488262300f83f4e3bff177590857e149216995bc52311fb9f16f4cd74e07c7868a39b699bdbb7d7dace4c6a53ca7ee6e11741a63a52a1d96995a6dd752356dec6f14761ccfe38a6cd8511204f8f0630a747d6e19a77bb030c61e0828436604a28a7acf4a5e49b7269ac93b93b99e9e2e1c0c47b377f7e44e05ec6659526afbdcd5bb172404ce5a9f8786234114c16f20cda6d4359eb873a4a4d9fdf734e9c40aa4db3ea9a98939210f6c62142dd144eb78191116d194bb766ea96da38321ae27fcdcc196560ac75567297984fabe6072c771899906350f74de6d18518eb6898b934b11e945d94ead02b821fd6682602e03e9c70a1ec67eed33874eb24dc83dd1035fba5928f8f62ba1282907aa8935ae72fcb881b3277ee6bebda8fc75d6cd792677c25f70c87b11e094298b2d5f39904be211ff0980e5b83e8ea4a455622d8be9efdb5aa8466c88ea861407d54d98112faa10293af5e16974861dc9f83b45d21b112cc367894c421f5049e49dd205bd7c15e6a70bc810704e2e3a3659800864912527f8be743acdc474a26246a81fc2bdf669b9be7a2a0c986432e1e44b5675607e7e1ee2a8dcb72d8f1964272926e52f909ede0ac8daa32d1d850158db76b959e4d83c9da4e3bb23fd1f5b26463045d6cf13d187fe74a50c09a654d52d0e2f01d66b9f8b4f4aaf4c69fa62a02aa876f9bc4871aacd26a6c6ccfb9bea09cafbd0268b5b65d60aa23ff504d02fad4719698f8b044ca1bb037ea6af58a06a448080dfdbe6a5d698d5db9da5fb4aed04a46c8fa8b93153bca00a5bf8aab64d2b371d072db2ddb688a9442e948f0b99236828dc115a2fddfa2a29e2d4e02ff0173cf734efd4eb687e3f8712be82abe1fac4be0c1eddda090803fbdce41bccfb58c43038991ba1074b281a09bac5eba58a99a1a9678ba26f8f9e3c63ba095f02cd8f3b56aadc5de60477efbf3dcb54b854f651cc72042bf19268554c61b44f2f338a75de56c3c45b3ba40a697f5f21c4557380c777bcc91a151e5676c2a59606200bd476cf98d20b4cdc64bc3b8670810a014871be018bc32fe239e287cfe8a7cbcd1e8b55e08692ccfb4ef871cf797bc0b1fd7ec37931e35b6bc5d32bbe7ae77b9962c179f96436e4a32f566298d2235acf921e38c3f1942fb7674b65e222d17b95a2e58f072c63aa4bba1ce48c303f4bd24d84963f18c5e670015c52342dcdc9c0b348c7dfac721b568effe2bf2f2e816ca3279bbbed823beede8e12fc5bdccd0f1584deb1f6ea1875e9fb350919b675ccde0178bb83a4aa5232bd5e8e9a1b8daf905c6197367a0d106532297ef89f3bc690b48224592c768bd9c50a63d0881370d475081aef052b444744b33fd3fef674a37898fc950f887ed482d2a51ae615ef5b1dfa3a23257e6a6a319a4e2080b2c4094bb09e4b390d1fcbefc4d6c5dab620f8b05b1bd5d976300b007e2b8120ef8a6c9028b7d925c795058c6bdb6711fc5fc2476b9810d1d81bd24637537716edd3b7068b802c531531df710d3682f9865530e1ed51b3b56d860ba4e972bbc74662cdd1e2ea24f81bf469193afc02b14143a32e9556e3f2ecef97c65",
|
||||||
|
"2538d98b64b6aa9258f9141840a5abef66d6037a10356366a3a294719c10d6c148b04cac66f63ebff052d730f8821f5e5822d869573bcffbdd636c7973433abbf38767597da5186df8ef9df071bc4ecade2633366102313e659db8d8e0f293d379fa2df79f456497",
|
||||||
|
"a5049b0aa153e282457555bf6f82b60fc81aa6fd1c2ea3db031478ffb74b5b5d",
|
||||||
|
"350287a6bed5709dfba3d35c",
|
||||||
|
"849670914f5fe318eb01e8849e536374ec11e813acdbbe6a5e82a506f6aef4f916a3a7fb2e41db3adf990175e21f2386d1805af9bbc32a6ac156b13b1a9505958f68599019c4b7297314229c467114754277b10e9f49a4d12837ef24184629c8902ebe2a23f740dc826b01f8963d47100bf617b314835e436104eb207fa9a1079b8feba06d9369b9aa8222d38d87096b73678bc5db9a1add59394530e678b6ec93a80efc6e8320f2909e3e891306d69b016ade0d30cde64c2c903b401f9d01a29b5cb8619dc68ad6c21900b365a6b657f7d9ca4c145fe598a94eeea741e20a9329996b17aba5d7115c93623f2f5d6927068d0f190b49eb885429d771bbbb3980e9293e4d664a71c3cb629d869dc97e58fc3d328331b11df19a38d61e1705ec4c3d779168abe049e9d675337ff658e00d2d610c8f227d1341d1c41f1c01d8b5d83c4b1b30ae4318da9822f46402ee8cd5cfe9f3f22d90a5ec2d0aaa0baa85e10f5295cc6005c5a0887287b0c867a23da1a4c2196f91fe0bd4f0db1ab324c26fe6088d7583f3cd052b7f6fca38e8b21f98fd07fe78b7657da1f586f1fbd3d2b4079e20f21dccc0d269d53a29deb7c7fb63cc291d1d2c50ff163e08ce612310d3bd622f2416e193078ce4e1463f8a3490578af96ca98e665468281f1af9117a2ed23367df19b570885de9d6594f09aaba4090bdd1079720b08d54311793c97bbe14433b031c865b059cb4f75db74779b82c4f83eb4bd829c62eab995027b548063d7cab7d1a6f9642da6cf7181c0ac71594b97fc2c84b1768f81eb287091f63c76623c61e7ba90c922c74d46b9ae5d8094d9752bc1e8020a82601c356a201e0473d540053c707a88f4baad37826152dd245c4cee6b0019583c61e4327fdf6bdcae53584cdba8a503b835bfb5df9d649705fcc1f09376eec96c3da1e105accc1cbc21d90f527041a9beb85f8cbb1ee8db798838bb45374b741618f83b5d0801a3af2f640abdbe74ec3dc15d6711b4c1480aa8d6084defba82ed221ba359c9744705c4feee0955c27ef468cbb816694516f73fb541e0ad4ccf99ec8b67ef090505d1f7c4c3a8ed7e291c820261f12d92bbc6609da6c275349819848c9112826674f243acb9a29ab73f17c8f8af12c7437c11972c824f00db7ad284e51b9b508a925f0664bb259b4443d56463bffc9e5d845c9b9f79b24c1f457088fadd281f48238866e0b92d6253638eb188bbaa8bf6a81d2b1087904974752697cffb00b4ba05e5b7b842a3d2c0a743e4bd691625788fbe9df14600643b1d161bb2916176b6ee40aee38dbb594ec2735d41369ed3a0c6dd9073f1eb51d1b77eb9a967b53670a8ed755f3b2b73a6cb50a9e1ea7549346646dbe4b801c8aa642779d8761b6c2d2e1a9995e758ab92f07c4eb4a23c042171a4b354f434ced5f6d9ccd26cd6c2506e5023dc076ced15566fdabc7364f4a8594cd6ec404e1a9470f52a83052390e4f7789ade9179b069d9f84ca2c7ac9eea51035db817845aded7405bee90cbe92364c8c7cf8a366cbebd7a972438f2a9881395a8610a2cd0c06c46b60cdae5b1f473f4fd6ec48479cf35101656f05485198a470cd36af22838e7ba3e28863cd8ba7bbba7e3c2625c1106a6be44c9e3d9b9938679b26f0713c62c3757a2dc8b2d9eed5e652220a7711cd220bc91a9afd7c940dd8be71616ebb8b2cb0686dfa161c6ef56994a3cafaec5e79bd0a2531fd1c1a42771acb101a38988bcba51ad85bffcd8c67aebec5b37d526b29f7b9d31388e1e7ad7154f8e65516f0d80a30b88c2b868be2541d19ea1d2bcbadd30e2fbb1b4678bfef7f200e0f8309ac0701000c52ebbcd6fa00cb85c8d3ea9c5aceeb3adcf3773cfb3bfc9ac764d031d7c63ab888e9b03eb9fa74554dab4719d426d0875a508c8c86b22cabfeeb70b0f1461db4e5f639d2a2d28a089dbcc48e3f34394ff1acb887b89f75d3236c8143bb9b06273c3878744340ea1858a9f383f8bbdc259250e23a3c3992bf8b7ca7e1a66913547710402bb538a8866772d11cf4214060ed091d403e1c9ca3af75859259f88656a1cfecfdb49d57c193e60a2223627c681a2fbc7390140aeddc19df035a5207adde4f5736bc542bfdc943ae8b094f4a8701618688fadc2284fb423f602c41ad8ee11e5d9fdfa67fb7dc7d4dce7847d4875b3af667168ebb6082f6911c95",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"67f0494a728fbfc84e2f4a043e121ee40f3b12b31616c78e157ed970db28674318b08d8b3f4c538d7b9d91b9b0b09ebfebb07201c6398fdbb8684c9390b3d6a8636333a3b086302b24c2e5d47283935d33065efa3fedd5f755218be5d4618d38c5c1db75470ba06bcd853f3f08d39c3cd9fa3618e70b103c2d9b2101fcaf39c1701436b720d723ed5c622d6535c9a10ec4d727abe237e80fd20911ceb84a90285fc6e07f9d036cfa65995f9b6300a927d7d0d2b907bac9d9c4daa87c2438a583fe85029c886f96ed08f5886bf53292cc0265850a1f4ee3e3288b604dc305d0c28ad35e1242f4ff4ae988b6deba48aabcad2fc6cd7eaab0a63510f3f915c4bb9f9719b1d90db123f639d9d4f3227eafcfad769c2b204dd2555dc54e738909122022c4f92f751d25aef6f9a1187750e825c68450e6d1223c2fe88aa27194b492b6788be6eda80b9b9f053cb77c8d9fa15324f23af5147624fc00c66e947b004bf38b31e1343c7cd341b98abe462a5f994e51d343664968624a2ed0dea9d0299d5c5a7e9097fa63d8b3ed96f917f693654766a9adb01110fa3fe0d8e9b102860d5c049df3fe00ccb2ed62ab05583e6aa0a5134d55245d4f643e274def29d3fc86d79979d599458786a8338b0071f6a01609ee6b2e4bba9289e2df780bb27491890d0b5ea650e62df819b8f98aae99a1b8870ce6d3c7785ca957d5b4094946925751f0fda1d62a9aefe3937a912c1b49b4272f87eea7e397feb84c0702929959e38a568460811e5064b1caf5dee53f920c6e19fb16fc9214b5de1cb770b510533f66d8a0e7f6f04ba8ba41869f8018abee31a6042d3919e217359988eaa9db2a10b3caf7aaba43527484d81304f0bef22165f74e9e1031b545ca3d2f74195984cc237b76ddbec85142a06446902339b1883000264031db85fb19b46f320ef3fe316f750f2d3d6070dec5b66ee8ef20701f20965f5171e44c8a99bcbca7afbbd81e30e74c6d48bc4b0d72baf562da6581fafbe14b6cc597f75e53b305036ede219ec56d0c0d29571a9c110ffeeb747fe56f6030dc26c8d3841b868a1ef56840932dad9f3bd7f75573086571f4d9f0d949510a2577d2f8fbed7e850c73ed4c071bf9a656d09dab43a610b49aeaa57333f67d586d4f50683dceee4942db9549f68eef4c5f8df8a2330857cdf2fc4025f2be7d5f0dcdc74a9cb593de91282787b716d416a3ccb8d6d40fa3c70be4ecfda26a5caf3724fad3d98db16ab6d8f26defc68392923b69664b0c2d56f01a549284b042bbd43c8faec940187f190aec08d06f9a62ab03c9f610f64c0010a0939451d5502511dfd3da1fec5a38f64640c7b6db2961def257eee9a3eff944828e9557deba68bd8e42dc7a9c1570e35537993061fa0f5351fd3cf4ec36386ec4cdc5a2882d5f16703b900c5000efa63888d69982e5ecd3e329c8cf5f003e23ce03c55631246ca15ffcadb0fc9d5634252ccda812ba7bf5e343c44244026512062a68374ed4d8add0855dcc22b30148e0cef0f2886be76bafabadf3ae1205b43c6deb8a41c338114895dd6b49deb329ada31b350e02a1bdad4eb05b61b50f9d22fa2863bd607406f552713e302467ddc78213d584b4933202438d63f99d011b97297f5589f35b7e45ccbd76f02453b7a7668c2b1a1f5d1d63eb805c8881771faaf67433eacfb22f9b6fa58b93f9423a5fcf667aeec39751ae17ad36992556431bca77059a29353598dac12bd3036633d2ccadc18f44123e5bc074f4e5ca380095af062fd83b647015259be929011cfbcdc9bc5d0dcf9b688f0f5d74da95746f447a9e1cb5028ccb2827b45129d04cf6990953a6d8ee0e67fe6bdbd8004f4744cae5607fe7ec4a0f14fe603dcead3367b6870d8e751cf57387d04b881f92cce9772d695f19b36e2db2cf6a807c9ee83225f5c09a11b50e99855921a4eced8e631af7c234aa31615c00ccdd7c6ac5ae8fba6e29cc233765a891864c7d73dae08ed1a3c27cd423d8d4efb550597afee8356c12018f496637daec83575f5e38ed2fdbafabafd38483c239d31cb4d104e93d16eacc6050033a3c86929be4ca8914a538bf540b43d7ce7daaea317bee1ab80504846554879f900d312bf2fbb406a0edc5f4f809cbc68675b0b7f09fd1a8a4d52c0929b3a8b9c1dae4b3d599b976867e6a7e8736450dabf5c49c949544386a71419324ea4ce5c4319899ca510f50d07ace57b013655b0929f79dbf3cd629ad17bdd10109b7c53a4f5f04a16e5471e823c898362df43f57ebdd1627b33fd4cafca6cc065d9140acf0454d5f99be47bc87e0f3b4d4320bbf0f21e7c261bb8d5d615963beeaa46bdbe9b83a8277813ffe6132b23564bef5",
|
||||||
|
"74dfdc364097c39ef91c01b707a522e28edb1c11529d5050ff820234e6c0295aa00591e09d547e9671804d7825705ab44b76c59d1315ed1297ef477db070d85076693013bdafa92e2ff6a654660008b176cd4e8ae23b9c792be3f7db54cf2bca385bddf50a8624397cca8ee3cb96944164e3cb461e68",
|
||||||
|
"b3b5ccd7ef49a27d2c6d13c0ae77a37abec2e27e0b2d3530cdbb7f36792a7d2c",
|
||||||
|
"c0494bb7249f864f69beab46",
|
||||||
|
"ed8d6e964bcde1df68e7f362243073941fd68ac77929c8e480c89f519f748b3dc337b1af6231632c975167a8425b174b42c2c60dfc0ec85a0a212bf5c9aada818a83f9664c8712d96de1036b5e5d8c8298786b753638de3a8da958549f16eb9c723355cdf7b999aac464ec39df7d6c1607e81b88b63043d1c847dab618f1b19336911b4b0145c2a694e61db71e021282006d48e37f10f3b6314dd012a07618228532c28ca84a936e0eff83723d117b2f2db857d14af5bbd5948a0e53018b31e57cc2a81f36aa013a844990753ccb347fe98fab294cbd252a8b8f7246276275d2780511fd3cb7baa2fd1548184f968c422230f7ad73ae9dde91295f79f6b799e7d234dfd6573fee6d6ae748b0a8cd7ed4862ebd957390826f276c2afb01fbb4b64b61a1bfc138508efd630e77580867bdc1e96a48a694cf0db6c2a11f05dd0bc8769e7200bb0749f5798b6f3559de55d0c281eb5df22b731fbbc109da9c68f209b888e61240c4c0ca006d105c0a7f43144021547d3316e5a99f6c429f9ea2f17d77dc68bc9d5125b6260f79bc8b3b8061972e6757d87b6544f21645c0b4debe5224f7c48142c09f35b8e144c0c1e6521f04c170519ff744d61abd59a56d25a26c5ed5972191b25e78e2140f3ce68fe17be9e59a79f6c69619a79b83614c670c7736d19c27fd22515fb5b896a6418cc0b4850e85c07b38b995cffafd9f69763cbbcfa9d1bbea6868244a66a5cc82e815fae09f5775d28437634926d571c2b0d200855e09cbdc67d10f85bd4cc334ded4c83aeea57f8e373a950f135997666b653e8de47a3bc0059525720045996bff500a47baeec97808fe971d7693dfde339e8beca3598fbc053121536c30d0af10f8f5d8e5eeaaaa9586d7abb563fd69e88351f93bcc46520f6d97c1a49ba9f8f6a25cdcfc11b2a722910aabe7435ac8f0dcda9f824fdde80850f21a2d4bcbfd2e9fcbd14dec05c117a9796db49e2f0dc55e74c7f0f615bd049fa7d0bfcf197dcda3ef3de90762e6f6f9f8a8936bd04fcf2a97cf18ecc8f2f118ffbf02b67f252097e4289d02f264161f6f90f79e1e1ef8414b01a9e1a77b88c039ad6eda6df1e28fcfe9370f0d574aa9e857dcebb19eb7ce8af9b19b4481c9fb3e1f0db3b02af483f737ce3ea824b2165e7c0fca8585383d4b0a16eab2c7e3ee5c038f939a97bc8e1c093cc5372ee45d81836c988f3ab3e6ee0e5f9549e4b7bc381a2afac2074cf75ed56b0e757e7966cb253d549fb0902da98294c6dd4de3c2e166b7e45098d2729b1393deb68471d4d3218dea3dfd0183b654ae4092a79357945eea4b28cfd06b40d30d1b4b8f19827895f6f908f0fe511f74ec84cbab2483ca4bdfc6ef50178eabad79b18b58529c9328c13c52c2869858cc20ec36ef7717e1c743d13f9607bbdb0b701d9df6aca7366814e883d23e51ee5b0f20ef70e2c4134ab037d213315fddc89009260981329a1872e541767adbd5ee9501e7df4ef0cdfae9769961f8716ee7dfbab0ec89b3f62e987387d5842e124a69b07245d359052ada50cfd67472d27ce2c4eacb5421b62dd7331da54ebf0989803797f4c8c781d0e2e6477b421c7d5cefc8146aacc0012af3f1f7cd71ce2b1045d86bf48c9a13fe469a1865294e160b4975023d0eb24ed26837afefc250a914f86f8b1f5d67d65e9737e841519148d4dd5dbf2b5a8b073861288ec9793d4b113d71c01727f67d791852fc3946dc912d60fc66bffccf4c45d859eed9f0bfc7f89086df5d5cd830ac919aa7cdb4504018052d67f6a3ca012ed69187cd5fbe91875cfade381bff1e804ba59cd59f0f75cb46dcfba234ab9832c3fb9aa8dde19fc1fb30677ac1793a38d94aefd9ffcd4e777e9e4f6d49e0cdac6c16a36bc2f3ed8e23b80350e3be6d866aaafbc8cbf7c69fe44c2aa80651164803150c23ebe262aa669c77ca94d215895d2ee9c3e325a0bf2c61e419a41e0f7b1ba8ee0508307d49301abccd5b74c054b6c7bd1aa67cffeafee033761d8226d9dbd7214b130a867764062cf4da685deefa23693b8549d5ef5e53df85c19bfb3c43c6bd073e7a836f849587a4747e1a9a3c7194f6d5472d2e3e4c81784a3061fc9bd3b94862c4784974d859134369486f2651f1db94f511c6f59f41da0d75307191602730b88e4e6101fc8d392c87687f3be454dd92fb8ec380715bcd88aadb63717cbce4db91a36821a572c363759d8d0a2ab007e5981b78731dfdea20d900b14f0c5ee6a4a9b532ed2134e6edb4dc267f001cb88dbe43aac4aad453b839d035697df7de98ca7a9ee7601228a79004b89796e9ab971aeb8e62c789bb21f38b77b492c57db402bf6a42ad0cee169e9251d865ea3e5f79b1801ef1e53797aa6c7060d6f9486081",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"04cf92a64cbe135f7fc1d7223b95e41d13f04b482018039f4e7ccacba8aa15ac79a752c5666524e527fb076290ec80a3dccbebfce3ee9b316a65fd130f12bf88b9124d1f7772049e6d0c01fef881a1d44c8dd02f7b6b60e6d15df9e06fb86929cab64842284de09659e19451623525aec2f5dd3e603e24319b1d120bd57b34a0317ce25ac9c2f022a4847306b998b57c8d92baeed0de1f6cfb3177d0acab70de275238f1152813b9ac87bf651f74e1ad079b9bd779ba4374ecba459865b5768d08ae7e1dd691d6821895e8380ac9e5116580e8de3a2c5326e698bf4c4d35d955e45772bae8483d01de2539e8ee1ef9539ee132d80d85fff41dbe406af319c0d7703292587bcf5959f49241e2b03a364e1b682729ed261d0ae45d74d77634afe667413ee210983b042a7ce6dbb61c29d18450fa7176177b5a74f032ea24e1d08b220f6d32a7a836d1241cacda39d6acbd26a62f9dbeaaf7329a291dbf0aed4a2cfcb85ea360947585b1215feaf70ba71eb2d6bb7081b2a21bdcbfdae6ad2513a9dd714d3d06c2c2b7e322a1db2d48f9df1fb44fa066f2bb42b196295ebb3c0898ad55d5b317986afaba0bd5e754cec773821613e908ce2bba6454181f9020b73e758df18c255c87df675cc6bb2b8d2eada44196ac10c26674167f94a79f4be515d8d6a1fd3228dc9a85a355b030845dd4c5f481d5b6e74acc66de730629581b022fbcff61e5dcfb6a7f511aafd577849a6b057021ecbaee53986159c1ba74c3e930c34a159f467f1e9799cd6c1151067c56769e43308c96c8edef8aa7634d909310dba9af2128cdb8c29b24d3ec2a4f43a1ed86d1791c9a670b240e6e719f01827aaa319bd3ff53959a776886a1b7c942a54f141e6bae8576d294e44333e6c5ad90f74863f69bf890126016b318e0f6bd2f0adb9bb861118af5f6cd28dc93d56c8a1dd080b8c810ca29267d410673fe367dd9d1353ae2bf2fd88d57b4202c21aa49f12a01b93acbe260492367bc219d3afb6e6f35502f6529bcbcdddce9fe8632efb034a9eaff8b4a48afb105d04e3fcbbcae010ddd6636992213750b12fb3e01ab72aa957136e0bae591bfb5e0fe819cac82a98ae8df230af399160594540640c6b1d537e7b5f1cc47b08127ae02c35b846de56c4c08773fa18d4436e14b76a7fc4bdee301d0af4880306f2f33328ab79f6f24ec779b2b1928704f09bbc5b0b7108e9a115e4959df79c80eacfb98649a0788867e23b2974b22e654ddab0494bc922ecdf17727d0f0efde9dea7601857d890bfbacbd93f7df794bbc254f50e1e17eaed2f5d5a2e6c58083aff68434730d406fb9fd02b0dd7bfb99a04aea812b6830fe5e05a044ca21c77a174bae8b58eefa11ecfcc1c977bc6218064c9931b5c92f13cfd05799f11e130869c293c1b08dd29c899365014fc8195514b286c97cb6dc4b8633e47751f87fbaba137b6aa04d072ae06c2b2f34448449f60b1272c1efbd4722a2be749a3d2e5450aabef1f7c51bd8324607668a8caf8097c2f358b1b09fd3525d47ec9a7640eb20ffdc17c4f7eff63df75dc7830c471ace3a727feb11533d6e9a2a08106af33069cf482ec63724032e81cab18e12cb5c4c3ddc374e2f75bcc99fc5da09b80a738852a14e8ac552b8471c6ad52e35317b730db2c13c277e06c643e0d0fbea43833de4d2c7a9247ff040e9c56f1ff7ea92049c5341c4d1478a14275a10119d934e8165152b89951bca7ee1399dd8232fdcbf831d8354640e698b68799d060ceb877201b2fb96cec514affeb28721e163e1648164b9e5722271db9b0ee1a7f96819fa1b1590e9daa598d9571ffa3882db9d034056e9b2785a8d13686eba61d7d45cf2e9ecdbc391739ce89297211472be18b21401658c5bf29fc3615924382d802a166d05dafe7876e70a0d081e80c63632da379766928a0555eb5e7a238cfa4da267527c66caf34dd40055f2801b29b3f5604a5bf3d46048bfbec2e24abd2fed2481698a4b5cd71f5d2c12dd473b903c9bdb978eaff7d76fb69951005681ed7b0257054eb3dd6d10097fee51ba7e8d565925e4091cbb78d255c9d3ab4ac0264d172c9bcb0908db1288c9634248f198a1167daa323822058decd83936985f83b08b1e7b942756a7af200af168fb8a091107b4443fd649cdc22106f9b9657c69f19be485c23b2c715b3762c332eccc44f380883357d10019f20612ab6b8f155c2af9e2ec340e5d8f45bf5278ac1fbc9f9f44d2f615d21007d822b244b1c7a0dbc182c7f5912485d6e4d74e90f60a2f964e028c63d49c6aadbf1df170e4914ca514139ba538207b1cf7caaceed4db8423dd1086b2adf15f6c0e50dcf2e12898f53c339a745316904ae03c38b417bcd7f5cd5ea77a4f06e65d56c24f37ebe72d271ac79b6ddd2bb8bd67f0727ead49737aa71af4f620da53769ca3ae878adbaea5a249128074ca3ddbbbaf5a68f9cde2a0e8d69708b0ea7f4c8d2dd4180882bdaacccf2a409a681c551776bd10439fb12b7548342532b371c0e045d8e8c895929464bdd4fe25f0533c66104daaaffed52446094978bcbb389c",
|
||||||
|
"001084c8a5175c0ad43108f9215c35886c82321c800740c6118a3fcd45776a4588ee0e2d056a37d99b40d2bd9c0845088a77240b7e65d18fef105d3488c77910b5873dbbf275948db5",
|
||||||
|
"d614d2e671066e387f7eee07bca9f9313d86e6a4d1371360900d670e11ecf1e7",
|
||||||
|
"674a516f8f59f9067e0485e3",
|
||||||
|
"1ee376e9e3c89b2147bcf75480ff0dec1d0e8cd45ba812f34c84124871d484b4ca87bfc8cf99f85ad452c482933801426e2737a97468809fa36caebebe8eed07a626b3bc3614ef1ceb54f9221ecb16f413f0bd9ed4b3010c40632f05223484af7bf5948c2fb8a3d2ce04c53e3f2682494f3969a0f8eb738cf93c0141799c9e6b68924433f0326991e19626bb19e6fbb5dd46baf39f92e830f9b1ff465a007f031891fb1f1799cc122d3ae7a55624356b5297bd5d948d9ff2e414cd8adf00a53524df43f398938d33c93b2c06bcde2679566c0a7b0177b4a873f35874739d550712d5cfe3d25c19292ba97c01d84224738bb25546e5c252fe5e5f260ca881aaf176a271a6fca2edbb2cf23ae6d4c56c20daadadb8205c2e33881867cd67ae6e59132edccc3601f014b744ff8eb6aef5e09b358607695d3af42ab8fa30e9fdf99ce54427ba9da3699de19f7a8f9be368df47ff0607601a91e7a5fa6e72be50bb32b825427cdeda3972a18a23af290986cde14f5fb9cbddad336f5efcd2d7a0cf3d5b23e54b702352fd5ee52d7e3479441497d56e17d5868574c56cfc421ee47bb00e9c75b84262a1b9e2cbfcccfed9c4c386ef0d2c1be9a7b7556909b5d72a38b7258acdd624de2396c75386e077c34f005f92a2203c82d1072c8998f03b1df22de832ac733977705453b1d72336b8d371cf1ed3923f462ecd22075de5df68c83ab1e6648ede7fd5ee5794a744abcb32af73bcb182cf97d36f37c15535c4107b7c8f2321f9fe0e2b6ccbe74204df3d748c05bc1e0e2c55ae1aee2d4aa4a52e98ca7229d6d06576196ac8e4b14a9ce807075cdc876aaf904c9962741efa8c6caf41e6b87b2ecd6636e2e58f3ecf576e5d8b895162545e618960ff6e336ff17eacd5a1eb335001633fa78c41ed05466d904ef9b81b643a043298c0e291a085e4e67da72e329adfccc407f800709865147db49cbdf4232073b7bc7ad89b3dd901d927ee08ae6497e0f2f9d052ca8d7444d2e2ae2197f930a7b1c8af38d8739ad298464169823684612cb628c484f710cf9c552551b6837b575a43275100bf800b7a3d777adc44d07f67cee5000422b9049dcfbedfccded0f2aa4d189621579b01e3fdaedc4d772dcc593316ca85e7aa248d219dac21c561d318a4936ac0d3bd5c75311486c174e0e2182affdf69bdd6a086534e4a602efba2b9363beeb5346539b45336cbaf479da6b15b226a9ac026482216dedb84ae3443b306820d9f05f78dca7090d727c7481d82c6e5df80e189e24e46f5758e453e542bd91a58eb51a89e07c50afb543c6b998704432e863dc4c0d0236e0672835a7b0b64e14f5ced2904e54da4287597f920bb4d542c35d3b0271cf0eec055656d523d7d2cbd667445d3e8634854f8616b7d7a7f3e14fd32651e9df40e1daedfdff1371f16d5549ed5646adf2d417e4b3a4d145bbe0974ab388c2716861a08296b862e4fd035163281457877eff89dadb160eb2b780414435784804bf4fd36602699d8c2f6a8cbcb509198c38e2df2edaae7bd7c93313ca98a9c2d24419a12ce35b0b3d68c18840e3ff8739d70969927c7db9a6569787bdedf5c99948a9e79b2302a83a71159f4c789b3b3f05f1e574f8a24c899ae3457f8e73f9bd86976fbddd83b1af337eb8da4c0dbac3792921597e18a2fd3a0ac89a270794529d370d36bb6dc7452e754e903781cbf57c8646b92d5d02842e7df229b3d721f9b981f9d61a48f00e53948a5dbc4f739849609d94aba3e3f5f8163d40321576cb8eb8e89953b608a01184d41aafc13f40c47b12240e3ad49413473c26b6843f4514be221c2af632d1a54cba230457f23f00b2608485c381ae03b389ad0a1671fb416de4659cc7f7a9c4b6d9807789c307d061fcf613b96a2d79e5e3e20b863c8b1b75f35c982b40ac8dcb7d2712ef7df94901facef783e8015a9a48574aa6f0cfb0bf6c1a3409028f8d62137c347f5a35ad6a3cd60d71aeb29bae56bb4590f69226fb4e08fab7a9f41e58f4d5784540a70e7a97720c549c8440b089eabd0eb3e4d37a2e54b1160572ce568f4256dd244decec31fec555017ebf488e878945383750eff26a8a1cca73e7d6f52d8cb229d5603360a3bffec23029ee34145c4aade82d486758e0aea9e1b7bf0b4bfbd4fcc96aab66a27fb463b48c6a6c5c5a60253e2fbc5716ef55629277a5f3b89c300e21bf1226241ce0d587fe3f5b11e47f35614169dcfaa375ee1aa589be33a4363765368f5666d155cf72e851d426fa67b982aac4dbbc29356d71deb0715b34e00b9fd8876bbb09ca0701b15615f05cc45e128b3864b26003e6ffe801c4e27402f37b8997e0c29ebc273dc03358cd22fdb68d9cd3b56ff8248a727c2d4ac65acda4d0e0f511bc07ab06cefcf444f1002c151b953d7f7b19695668a86683497c2a2d2e69f19a4997148d2e8d158da859c8f44437d9ce9db92f84a88e89cbffc74c0ef4295088e2543a4f7c6ae9c908bd987bcfd7a074f83ffaf3888bd7f430dc5a5bb70d223c21b1bcd8bff2103408460df864dcc168486f6a66d67ded366c6e10f50bcddada93627cda711764a57ec36035ebc",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ce72c93caa49bb9850774149a87fcf8e23a0c53701554468645554553d54190bc6e247712b02097b794bc421ca94afed34742435ca689d2ebef183fb469c060c7f4d7daa508726c9d2eaeb9c7e9a89b30faee8d9168607d4778acfbd27d5caa623475073ce763ca061273cdfc2c692d1747baa8a01b15f783b2e36620400082747599a16cfd6b630fef310c0b9a2912d1d3bb71eec16972745cd8a49cd927014eb0a2abbe0e1ebded4fb9e8d9e2fbabb6a71da5688717ecd3e08160b9a861f86904a41702b2c4fff28ed8cc61d468187b75bde3fcc5c0c0a642215fea83584387fc5a9aaf2f8a91ae535e0027b618a32bd687289c47e9428a1a92649deab825d702b076223b07c08e55c0b60be95937bfd0504c18398e924420f6e20baf07e2b1b858d3e360a461b66517c24e60f9fe314a4a4973c8dbc7e9d2a9f571a1d8235a21073d81ab9f4800b70a5f17f44d593e8792a2507e6a3a41042fb2a5f7e5f028ed2daa88cce28973ecd88bd125d50fad77b1fde61c38272057d9c65fbfc6789ce41315a105af14e277a0c39d75c34aed7538c39160eab1c8c47818743e8111229426c399c5e88c4d894fdaff0315ec885ea019bf9acb785f3380c37201d494a60b583fc130bc0eb9fbe9b90eff95874e35910dc05c761f8006e2f208b786aeb2eeee841f9a82d9966c82956c181caa4dada81dfa2e2d7a25007c2dc7f2dc7ad1bafef14581cadbee4d614a557df4931b9ca105bade8fdfdefc0d96eeda11c08500b1ca827ca670ba07bb0f85af92914c43a6f71226d6e112d487f1ae99b2239a63ee2cd0849d8a9c488a11f82ca334604a2b7260f25373c6db75656527890f9b772c6bfbb9f687f27099ea9d4d1efd874a6ff83cc36c039ed1690408f20394692ff054d9e6eccc6776b6f4b3c5f24b0052334d159f40b470a9b8799bbc0df4dbfe59a5e536624cad193160ef23abef85df2c9b6e6d4fdf16f848a2a446a77044f1162a278866c491982570cbc16041908cdd0efa2cde011526a3c96d4b39a23c5fcc53d8232869cb4dea871f4ac8afc795aeb1b28cb2d7a3669100a1cab2ee1a7f31e2a25a5c6da836e4b771ad57393305faf582adcd26045e26b618d9943358c615fb206258c8993d700adac7440dcd3ef34fdcb065e10e9c9727662b5abee160aa01d2f2ca6c203a76fb01bb08cee9fc1eb6bc7497bb012ed2774a2d263b9dd03d60c307ccf33233ee33eee702c8e3118f9f86174a97462d0e804a24bbd7f4f938c7f105bb23399967288069e1637b60f2f1883d88ce5a874ea4bc0a7ca0f3b568e4bb1407e4bd6f0d3dc8fe91345f8435d7b1be961c45e4b0f1ef2d92d2d30bb78e1fbf72cd2e7ffae76e8c2bce005195c2003bde46108f37ffacdac28fd67a0de62970b347f0ae3f5f3a5b1d3aacb2fcaceecaf2ff4a2aeef6f5a176cc1b74b234f5658ce603bc353e075278a4056540e43033d37a6eb2615453d8206f5cd294423811283bcd5d79c4afe268a547b98977ed5cf24c0f53a0533bc0b2889356cacb67e2f7353060f9e04362859b1c1f02f96bf5457b58e5ce84a6810d39d7c7f53faaec64db5d6ebb90c1412bdd503ec6bc240c277ce1f5f18876feb24eb6a77e5193e33ce141e8720329add079dc9735f0a35d7d85436f1dba6dcff9147777760b5aa2ec9c8b5e9fb4fc602ec8f754c99ab2372ff5963dbff3fda91865108e606b214cf7acab875197e78060eed52a798751998ce7c73cebc4d5f429f6729a5193d7593072d0921ac8127ba6e796107ee7b9fbcf7128ab35fe9f6fe501fa4695c19fd64460685f287acacf5250efc13899bcf80ad5a340d432a0b9449affda5c8fa090f008e01873aae7d5fbc7972451542c5c29cf9cfdf23db736c8a7112536b1b626caa63f3e4117044cdeab612fff8d8c194d19174f56ce761f6587349c48fab30390f231d209461ee7e18007d10d83ea5aacf199f3b00003259747b1d03274d3c3670595604bb4482d345ffe31d3e88c70da16649a2677bfbdbf618de1d651a53d573aada2eee5c01335ce5519a6d18a70f7ff0b1e66bacc162c49f7f29b9d3fe2c7dd85b6b355c9f9141f02baf08d2be87c36f6d2e1b2e90dfcd100886e306b360df0ecb146a6aa5ac5ad05b63a219ea65885894a386248254348ada17908d776f9b438306ad28b208f80d6b9b265500aead945134b9d388ed5d6205edf07c5d8bbfe0916d0943750150e09c76359d24e3317517ea489fd8a501dd93f159f07d19d00e86d952fbdba2db771910143df346b30a30fba908a1abe5349c3f241958f428dece7ad9a91cb42035c43573b87b26c2ab216cb4c21799f6b3d81acd300ff50edd6fe7868b9ba6c160db3418565ada027b46b63e5d4f3411284fde585ed3673b424ec1cdea678e4a43c262991c3c9b988351d6e0a10af1c959cf21b7a288f2e4d7b3b2c11b400b5e036df71fa993b72ce48d0d8598fe4ef1ce70a970f89b55cf4f07906a479bc84a08bf6ab25221de37afebbc47ea0b38b87be128737d7d43cc84d336cc6ffe1677bd802910a2084751f30398dd0ed09589b2befd2f3b40fbc013318c822fa2faec2323fcc52b43161f47aefc557e92df3050dc5f8b1c5a4b2f8bd7b2ba7aaca79dcfa362fbe7781a2e261683a4a862d5f83e34845a8fcf8a1aa73cd521e87cbeb71f20b20698cc34bee3b8628b1a3784596c",
|
||||||
|
"08b3fbd73d157e79ea9f61665d19867dcb8c1598c1d37b793606936d8aecd992a0d46addeae857d488b83be5d1c1639d4d78350e4cb08782b61bef4107c9d3a79d3d85",
|
||||||
|
"a56f38b4bbe83b3d9e562cdf5ef5b30593f08a1a166676c4fb0d5861654e640b",
|
||||||
|
"8726dc5cae9497936658f603",
|
||||||
|
"88420357d1ad70e7c7bfd55b3cfd4bf06cd4e9b4ed5cba681045199a06985956d35fe86b28b9a4599964930d05d230a23c55a6a152f67082a453fc31f68489df05c553f9ae5cdb3f611445db384d79af865e52440a876fc4153d896b7a2318dbc2a4495ecdbb2e9dc68022326d35289e82aa55197aedc266dd91ba3018c7b474ba22b4e773773f3e9890ea84bc16a6b235e4bb69e785c40c1adc15b0e0ef03aa147b0d14e62341e27398b84a53f72c9199cc1c94cbcad2bd31aa69c96b06d01775b8c0f80278a43f526664bdd430164863c9c9140ad87798a5b8f38dfe90d37f54d1137709d5311136b728e6c799da244294daa4c8b44bfb0acc603a16c088a081129a0d2cff55ce1c4ccb486fa0ecc3098ef2196f47c49f9d253112bd5746fd99df5d2be577617dc2519c0ad04ee49ee1d7be3d50492017108fffc9a414ea227af39fe49fb2c895fcf00d927bf4a2d78c466fd44df4768e6775d39fa5c834b60979ca27ee9f00faf37a090838f56275a894ddadd265a8d2de74265e4d8d286639ce8f01eccd4f551cf6b4429eae3f08902b6ce6ef422cf91ce8946d9403fe8064784895b62a7f5df76ea294132c59da6b9f53d4195c1e9000bec499c14cf8bad460aebb024a76ac50616f0dcda71c0f56dd3239b11764f3ed6ed06c049b2ad673e4beea391dbb854fde1f01b1900858b9809259f3906b34f95a1c6ce8d24fdf0cf7c2ab7bde2202a7f1482baa6e51caaccef9f541c377da620bfbc63955cae0e6644ec8ed6878f704f1dea30d6b50d4291892bad19b0234582d50c6cc0b4165322cff24a9dc2ce1be35be0fdb3bb7abb777ff0b2f4cf16277388af5a89220d59f1f45ee9cc2a0fd7af9aa8e9e8d548fd65be4e47e7f8ef58f7701f93a42e7ff78f70e807fb63513157fcba96ad9731b2e8f80da85ef407d5c368ad16f0657620bfc122ba1b10d7ac2bf46d8133a9c6fec1fe04882f3d5765da8f825e1984a4313f72b67d806ed45c000dd3ddedd524d474b9b5788547d0712e8edb4c6c586d0cdf8f2384f1e093a7f6dffea6e79df9cb9398f5d0b9a7cbd63d489430fbfa397a0d03ef916b7702f33a54ebab84a7055b7ec6179b0ab7722f03e126ed343b1cdf2af3763df7e3a070162535514b01ad86c6cb051859aba1cc4766b12c8cd57b73fdd3c65af6961c45395aa7b885dd59e115db885f644e1c94bfa26b3804f767601c86e2c7dcecd4daa59955e6a40991a4b4701e63fc82b46dc0ccf59af40a8583171375551c868436ede535705f2e6380c5899cddfcaf9e94314794bab98846cd5ba9e9afbdbe1ea7fec5e22e7b2aae59fa598f4d6c0cc6f936a616e11bf01a2acc891cbfa2bc53c511a8a3a3da2e3aa5907d123ab2a4a3c0009fdb5235a3c33718fe4c504e1539abac6370e06150c402b5fc2f8c32608db4ce2eca9d1e4b96371ee195f6cd632f5b972385f9d5d357b87c78cb4e2c27aa9851534de14de923543f5fd9d55e34d6e8b7e1f3f2735df80046de01f79d0321066f9bbd76299c7386d285f7bf4ac15e033e89a040710c90f87aacc09fb8159f93c8b4860247eef079e32d05707e88aac734a2eadaa853f528d9986e0af3435b5c5f44ddfdab9b0c9ab3eea97676e920f80d1794740067f9b229fb018c804e595aa997533a5e967cb79ee58eea18995a90ac08333f1c69600b17ef4f454f540dbfa8b502457761bc4daa876d9053ae1f55001b6916ce559dc6268d01841255990e56614e6f4ee4ce04472dff0657360d75da4e83a71c852a2585110e53137e91bd89d64d99b5614ab2a5691c876f15d9931b092fc6729c0732db5cc40f966fe440ff99d7d05b24a872f552c27fb0cf2af443340b153214b407fb9ca3750d9c157aa75763b0b7600959663889d00f392d6ebc12835bd2f03ad802a21d0228f1d2e9731d0f0051eb2d5369ab790d1134c38e28d2bc2d5d57d6d897244742c176559961a1e40c84ee5c8225c8d72b92352a011e3785c262aac115cafccc2fe1b5e81a677a0220f207ebadd786b93f58e40eb6ade68ddda5b66c5f0f6b4b95cdb8241156110ba3303beb79acbd54423315768bb43b4fe8c4a465e50c4e63bce272c4d731ea4c797e14b2de31ce4264e2479179b906f67af4a23c56e817abafedc2c7a65aa45f0c89fcd0baba60561a8d013e2d5e0bdf9fbcc1346d3edb20e6e9f9c410982e1ac43039ad8fd0ebd453a6788376951fc20374b59946a6803498929d9fdf2e0f5e58c441329a79d1232e957b3a9ed17231c663b4819dcb6b4e33d205edaeb7d7ec466930bd84a064b40aa67fd76f6ca005408062b45b5aed6f8161836c7160a8c8313dc9aa1c6d42c2c16972a1065e41aea9c58db7916e1670cb42a8b54d85498561b4401761506860b19b446655f8988101fb4c45067e30edc3f00df8d88ee34111dd6626d605d993ff207be09704fd8dc242ce514bae77cecd20f10d4a38435a3f5e545882fdc224586a04ca6a162e118d23716240fa67892b78faf98a17916471f7f121fb9f85497a0b34bf5aaa4ee1ed8a4681bec55d1b4973d4368600115bea70f20a37c9e942b87f6cd1e2ab70fd401e703e3c8334c75fc338508e06d6370779578fbe737a75954b4701bfd92028ec32d3d7ae606caaf9f049d9774f70efa707c1c1174d9fcb5b0a0ae2a961c6f58e48ba82c2db14ebbbdc24288e42879f547b855c86dea9a3b9877e4b105515bd78cc43465",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bf7884fab52251e202afd7b5b46aa53f85bca5fb80a009d8016e276579e401385d853312a884f4aa33cc5fe7360426bbc0ccb7416cc0196e2e40d3a825d5e0825a1394029789acca550bb28b10d847d0a4fe1111be2b7fec6b5294902775128288a784203031ea853c9c104c75571d19552e2a1359a900c5fc9455230968a5920f2ab23f5b9cc49739d4e4ae2c01c7812ff295899b954e9729a3bb330b60c51a8a7759e5131d7d4cf261fa1e62c29f91b4341a4fc968e7f30ca2261702eb328d628b7275a9efc29b50bcb9b27e5844328d5e8256c76949d30b6fea0d5a1c9abca80d3251fcf4ec4db0a5ff2ffd43618aa2e3e1694c2a3c579a2665f443ffb1eb0ce33c09d7285687cd55b6ca9918553bfb36a44860e09ffa0604ef4904a034108370195a986fe165c598305eb08599abbb3df31b1d93162397056d9ba5a1ac2812c582aa356310fafb4058abc5f157802e4a9b4bddb16e75b6db105b7dbc838f820539b76949b1648909104efa67ce28b16a738f1be104d2bd142d3ad1b1c953b6020a1f4cbb84d5c49424befbf2e6ac5c593b783a3f4af75477312528fa78dffd82fe493d821e011642bf1135a5be91fef909383953308dcb61b2f35c2ad259acd1a2e953c0ea6a03a97b384e39c94c33d3846c26b4f9f116abe572d5b7cb81886d6adc2d544630fdc1684bfb32972e051b9a2bd0931de63e025813b923944290fe1ebd5264ee4f25569a2088314e8d4ce8b91c7bd602b9d85acc917d60d30d5ef1cbb055b9ff7b0f999b98caea2517d2de334eb436078c90d41e0e34f11b93e3e643389f43b3afdc4f47a7396cbe0b4bf159ff27618cb835aac6699be1fc7ec840b767836a165fb95d06f2cac4fe15b65714ddb8a095ed4a5b57e63d536405931b6c168683763fe07c32aa4130bff787d4d440746a2dbfc584a502d809076b257482abf7f8ead7741c82b54c41acd41581148aeb4149b0c6eeb39ef7ba091c2e8bc72583b2fdf8ce7fad1bc05aefd6db0360c644a9760a9729a88ee4b2ab123d7238c12435b9f3b4660e74c0fd4a9b00aa614453d84fea01f779e5a924f8e79630a8bb6561ae19c7bc8d88b9d823b98285fdd65d4cc05e443944ed5d3cd4f46c7cafd1dd5deaa519772dd24f508bd2d588a832d5689119a2d506ff11dbf37d57a24e35ff38da18af07eaff5775d12dfe795fd3e1f0ec83c5f283d6cd76532519a15a18d93431893b1b88929159bf8fd21f62b30f4e37d540baab0e30ff3349a08d627ac19303fcae8b8e3fe44eceb66d30697c7ea051bf5afdcd8bfc00d49c8d36164ec9194a78a4d8b78826863e93b6a810354861f4a35ec12e5ac102f74e390d9c0227e67acbbe3254e5b892786e3a88a383ea9726485854a319569a678fa70392cee90c9aa83eee8df6800565bb8e083e78a064c0f8b863120efd799ea57d3073663c0d0e7bfb9b717ca1d6372fdf75a77fd9677791cb899fc8033d6d806de1e6aaeef525ea909666316d9d604c1207cbeb6f427c3acc1b02cf59704fc65135703f2a9529bb2c8fec992c4de53e54b029c3f2a5fdbec1008d1a70dce0c11251003ce62af712b9e4abe631902485404e4933f346f1b4467fceb65baf776d0078aae6a2a1f95b85a441b635663c75b485a8a7cb9a5c12192ac874d940e2d9b88cc05a2db9b5b35df769925da508112ab0b8f64a1408633fd0d81810baf2c846b222736bd826c8cf905b2c35633d6013f5565e0a5ec1492e99613f53530799052a0d70023339d1c394fdf9f73a590a2faf68390d2a823bc3e47a173782b03dacbdadaef1e67fb47a7cad71b6067ce5b5e41fc20ea1fed28578e9bdfa99faa657a754488ed3fc084faa7a05b0f6eb66da0a28e9ab26bb319fa4ee993de840948f94dc1d68d926b783a0bd3396a89970b2c2595de8148e87b87c21f664618af4f567115d403715c3d7d2f66d7a90de2c5237893a4c18c20494e3faf94485ed39ecfe972c36acef0d7ee57bf8755924c790ad02dcc5c4e15aa7db53eb6040244c3ebb7874676782e54dfdddc256018ae6af8cc37450a4cef77f21e2e061062ca0c2a514290c960f5993ec1ce9eea6d09d3293118237e079b6015b966361c3032368174d74ae5cce4148ea2b3690fbd3c28ee544c5c5bd7bc618122979d52c9d3d44eab1f2467f338e695ec5f95998bbe77dffac42bc2809d43a324e0f5feb4ca3d5fd951b7dc8a9e6276ee080079b68849b14c7573cd02c76027a856165d1043acf99554c62fe32896d120974ae71f84986bfa0c28fcc399246bef3ab90f8e55f913aabf339dd7ca6f0861a9ef712e77dd28740615479f39a37e746c7df2b267066d1649fafe0459f665f3d5e7124db43ab1ba5ff94989acc7fe0935e0bbacf718b33103a1355d97ab416d8263ab369e6cf0ee563a77f2f265fc3856b7d54dc0887ed439a421c14f733ec1d6da086536f9539d23cb8026218c5e783423b5f4ac24c8d5d8faa7186dd5ea34afe299e6dbed73ffa8f415da706442a48808a9342d6209f65ca11eba76f8ef26db890da76671971f65bce9e6112c8aa92523dd5295d748e28857acff408c161c0513b37b855a8afb0764d118815bb1b68f8f09156641f7eea994ddea20f4062607b9919d041c880b71592402a4d5b92464b239caf431a99dc67787e76b8e1d7337af004bcb88473cd16b3f7640e8aaa59ad4609f060a2cdc71a4b3ed22c1506a7050a63bd8ed68aa58a8109980bb3f2b9f9fba9599d7620b8c25e8aee739095789af83529cfbfce5941d7f14c8ae30583deafdc7c25fc34e75bbed6ce4f6b47e9647c12333ce08c7db77dc94161cfc43f7ea0bba39def8bf8ae61c6fdcc0de6308af963c6d9ef43916d0cd2cedb970d5937c9fdd4b888cc66e99363b5a04ae62a4349f01c3933ada64080741b1781754e4a722303faef69e382cd9823177e4d5ac83e76017124a7b1a41bcdbb9c4209e7b42c",
|
||||||
|
"eaae1c53919e2029c137a80f91704d0871be2c1870d0333d8bcf7f94",
|
||||||
|
"4c434cddb399e52457113cc7e16f046c3f8301f5b6c296979f8a091a9ea557ea",
|
||||||
|
"b633c1a0e1ddf4727b149b3d",
|
||||||
|
"f1de487001a580cee6edadb1ef6b700c861a70c6ef16274447b8c61bb10d2d1efbf104d5f7d7172c6a5cf9c06d886165a2919ee9418e2e8f803d47832dae5ef232ee300d1f973a6298c22d777a1b16264353cc731a7a683cfe31e0abc704460788c555c0c24f281b81d7761235a955c736f17f213a896b40a034609ca8456ec3cf5906d01121b7580ce19d89347b6a59c81add318df487b2442a7a8b5e30df78467abbf46bcd5ee5b994a39ca5bd8846caba6f02f4f1335b73d4e20be0b6ad85966f86d1bb857713ebf947ae936782f1f4929498bbd66bdd5ad6fa252364a5a6b46180e93b54cc321b3cf63cf23d55392475c6b8c8c9dc707924b55544151c7c55ae0bf391f793e52bed70829fcd32b2926600f65be0943d6a9a96547675426b0dca9cc7b0f5dbc9d5439d0281014c6c159d055d6bd89d67828ba7fd2a0570ba82996037f7dcce297fe6518331270f6fd5ee63d406cc5081472bc5f2298a9208dba9398ccf807ce9af982885897715b3c5742456f756d79c70434a9baf7b4b6664c9d9f5696c5256b74099e593f97a2d4a469cb3430d0c3eb06083398cabd58af598945a85c9235a3fdd9ba7686e54d0de9afb594b1bb030be8e6bb839f6b45699dbcd2f771db64b0c62bbf6c8672fb412d60c00b3d87f82ffff6512e8308877573323c5a2d6a216ce3e2ce07c9763835ae59d44d7958fd873e3995b62b1b347e489ce86e023ae27a6cb03ddec27a38fb233499a714acd89232a91d38abce30299f38f437f7a46df647f2be862c1e7bcc1e4263c2147b13ee5b345b7fcb973f3ac71db8bc12309f67ddb62659bd73fbd20664eadcd23a79233386aeec1a6fcc8c592053954ee53826cb9b6bba22400648887311cdfa5414c96d5956fe193a3729be1434d923a3f9849f6c419f77ea05fb72f3c4f75ccec03b7f7aef8c8e55c8c5480ee505ae1a7594e6a911dfbc39dbb0ae8656f5972eb644c64203a920fe0078f3d050cc5666ed9747c23df7853d6913005d0156e741a5ead3bb1b22e5bd802c303a73a961f0b60d0fa698041c22577b44eba5d6071de4b545d9f5de24944c151de6a189bfdc223e0507c74ff929f06a2e7497e8c63073294b4aba110a006a6e9510a9617405d9ee711831e085940006761822672549d1d1c70e50002c2227f6f304b9a7f11dc05751be2dfd297087044d2e20ecfa0c091478d62c1bf5f0aacd25bb0384853762a51144b77d30418b633c4c10a6eda7b2eac46905641da0b685f85349749a91cdbaa4027fc50eb97a7dea9e8cbb5b5f386ace0363803ba579cd16ef80dc40ba1044b4ecd0e81e382635d7855e2341b18e0ca705ff46990282fe25093a248ca04a1fff64ebee25065350ea4b9e5990da4dd2e28688ab08b6d6fcb54d70f6d74fd7e5e05d21c12f5b140839aa966aea9ee094a923ee5ec704b5b709ff009c20ed89a75468c48b505d07c7a5ba1ad54ed610886c9d84468eaa598c71b017578404c909dbca431703e0cb1cfb975a696a1677bc015a75db007eccdcb21b9e5e119c48f148c2cffcf29e245e52156ba5ba0a8b0031570e4cbe7b3ac4646353594f0c4a9424c9d97845c5e1a4b4016df9be8df3013e5269484cf32258849afbdd733189ea11783f0f64d3aba9b4f48818011e868cc03ecaa44ff0ab83ed12981a6df445294ff672f3a16d6e0d19b90007d4646e967e0fb1763b3c879f548e1103a75c94f3a7f72be78555eafc086c1c58d1761aac60b843704f234c55b951a1303a12705f2120f784c2bc1494432a94c835d908f0edd5cbb169afd2d38087ca5bc5e5df9c3bd970dd2da4fb2a00933538148ebf669a20b5beef0402e53dbfc3a0f289b33b41ca27eb2f036a22f0d02e0617bd01e8c74be264515c9b46b9ab6fc67403a35837844580794088a9d3c14ad9309435daa0396f48017be524856ab6c191350529962ead64bab33171a01bb3c144b23bed406cb05102c693ce5df36eb541c47e871acf56f2b47de687eb9b3511ae83d06b1f69fbcef3225c3469c304741437fcd0ff4ae3484c117f51d24b6ae1363beb7d85d9b61e01e3dee901b90f2d3272eedb384ddb4d3b9594b9c0926595e500f8ce2e5cd407bd7a4e2c8e6f4315bf693e8c961ba5b8a6c7f5030c68a6b995e9d3f9eaee9eebc9d679eaf72a5f1cb6b2fc66edc7dffa2370dd778ea7ff446121999afba7bb35ceabf626c6269bc466d65f7f812c663bcb2fd87d3e09ab7d71e727f66d20ec48a5d2bf0aaf0aca05d1546d6e974f90df85c1393e3d45731f71ec7b5cb6cfb4e5c29976ead6944a99df2045056e198b19905362d4e9b765adb65eb089233a8b3777352665489c9456cceed593c6590d9f3cc4024d0bb92e1a0dc619bf8ae65be77456c18f8171e4d2d846073cf5c57ba93adbc0db9799e3d98934aa6899372acfa4d7d2ea32e20164b79c71d7bd33c94f9a781a25cbcafe563462eeacaec0e8d9d6c0199de85558a3a05d1ee3483351915d8a4e65ca0ab129a2386a9e26aff9b912c588babbcf25f8c467145061b9b8fbbff19d8c6ded8527d457be7c926c8f490bbcd627b3002044b7729a52e94147f95772591616f6074047e758597f410b3100f9efafaa4137dedfd0edfa85b0927804f0b4fcea1a174622116222004d42b36c2c73d04781f2f49d080f351e57154a3980005bcfb0ea34288e2fafec5bfd01e1f7901b3efc71ae58bf8df4cd7c045856103b77bd78073f0174aaaef4a3c0e8b5b46dc92db55478f012dc1b7d513e215e735573257f105d2390b5366f49b61809033c13ed4e1ebe19ab89313c947f2585f0788a0c5de90b41ad0dbbfc604a0d414d0e5390a0f3c9616cfce4097e38e05888b8bc6e55e40368bacdba7e5b76f4bd8fe619746155c30b38807a1ad325b00ecc3dbcf23014e79f1c39af7cdd0dc7ea58ce733e6611b7eae069deb047aeadfc21960e614db19d2e7e0905a9873268b9a24f856c28059321a742cd6cb3d1527",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"c89c3cadc094bffd5ba06c600dabe30ea19ad037316fc13b895fe0e14ac8841264c1bf25557e22b01f8e102c3af43adb8e0a12bf79d3fa0232dae37ca3688e07294e2c7ecc4e2eebdd3f17173351f2c15b0480d4d77bd70955ba86f82214004b622cc92f7bf81a5837326f6a83612bdf65abb33c268a457c45cb7467e074b342a17c711c748c74abbee31541444020a9ecd4e5125e2a8ea3f6030bd677be18183a8a34af16a85ad48b7015cfb036789c0a5daf68883d0c7e401754b8d56cd00ff605be0cad19e03989f608392c81d636de859e66c2aae403c138bb96a58ba69b9064a83e7d8877067e7f40aa0016e0df9b7f455d292a60eb621b8107a727a3378c4b7509d3ec10526c50fc6c66dd4b015c915e85bbbf701ddaf2258119c8b9a5132eafe61bbf38870f35f375123f766ed0d4f38b9364a86e56cdef6f95a815a8d7c48ff283c77992fc6c070eab7d7c7b517006e5d4af532a7c429912ebaebac27249b4f5112d870d998e1c450b98c05d08c742dc769506f2d7a004c24ebf84c10838b619653e27ffcc4344d8db0435e4cb77c0410cc734e36738a6b5f72a7600632d19c86b40c737830b0f5f104443dbbb031dc7ca51ab318951e7817b5d81de8a9aa7f5db6e2d5e7a3cbd8a8100653c048204ced3af005d00e7de7b445f5acff901c4d46ff133e92ef073aff1d9ebf55befc32f9ec38c9eaa6a1aefc974bec2758297e474cacea2ba4151ab1a3ca0762c64a5ca273169d29b83c164f77f266c01bd5075871e17426068ed7aa58ef0d1f2959b19c604eb6187acc57e2becea2da93ba23159ba73b9226034c7ee2498e0ba34fa8038e5e2c092a73ebd9329ea3d648d6ebd47e1776941ab3130cfc91089fd0a0a36f0ecf68293343f275d2a64c1b7d27ffeb3f667f4a19824706235fa5f3f04952ff08bb183c0f1aa1d1b0edfd2e05ed093543788f5d0ac6532e15f912163275053b202d772f381900e906fe070cdb00421e78c16b7387be91adb7b3b3ea28b92548d69c780ea578e7ac66eeb931eefb4067bcabdb345a7cd2022085fc494f118215adfa2443630bffc9faa8fbd9943c3140d81c7532895734a9dd20e31c326531d06f5623c252139c4cbc882640c457819c63f6ceed4e03872b246a3766df69373ebf5af1116e8d5e1b15745bd9dbdd663fd4352d1238a43d5d1e74b3edddfb1c9d460daeb49afccfa0712b7a4cf8d07ccd0599ef3e4e1c9b5c814f3a6f3a46fc80449b34df87f47ff91fea3618cab2d5c04cb50e8ad199d752d901b21348ae939d39c86cc1bcecbadcc6f0e581a3bb51e070507b41ea4294b35456c69cf55a2a3f1296f0df73abac3a9c81cc303d1e20ad6e9bef48de83fc22dac2cfc01ce9ff3f70e00ee49bab2f282ceb6859f989075814e690e36a8d16354fd6056cbff49c30e49b1570363498531ff0ad0979a4518e9ae271f57f883abf5e301c0e24a83f09335479698911bca90269a28c0e040a98e67c9e55f4c91542f921511dd980270cd490766da22306b48ca9309aad3b2393b7b1e9ac7afeff64204081f9c0a8f6a5396d02eb9009901ca2c0a75ffbdae3a38ccd5007cc4f6bec8fedd64086cce5c039e8abc9e23bd694fc8de4e858c89bd585ebdd422b492eab26f4ebbdc1d17dfbba19b5ac458c31320a161a52dea638548205a6ad4ec54875ca34238c059177bfab2d5be0a98d12b3932d0661d33ec655446d0283224af8ec7f1c6874add03448fd8029a71d3c5aa06951123c9fd881d435845757df50444e6cacc31a8cf7537a778d1184b96c3512cd474f5d1fd1214555789d24c8d173358e36400b2d937595109729d9f35eecb0963c0da60d2eeb52a778876059fa95d820d5d34e7948d389dffd53d34c4083d27c917879b053cc57dc43c8263e5dfe5f33c19dad0a7126ea6e8abdbacb318d37c305a183596ddb25b1934beff13a4f24fbdcc2064de8e0bc639e672ecfe45692e9f8164365e1691784b4f775ef369aeb135ce15135c20da95064c810592ea33316b9767caaef842f948b9573b2205ec57d3026a2f2244c42991462e233061549cf9bc66a7b4a8a0fc61f73883fd24dad02644004989c4721a0aa03d3b0191d7fa4d3da102e541fe463936c9365ba30681e706ca70cb3c8ad5dcc710de59e7d8a6247aa809bba74ff4dd182a38bb31baa337841302c19ed89d65e87bbed05465f4ce0dfe89b44d7e9266a8ca21d984c41109d813ca76eb67dbd4e39aa437ff98050c968ec1e40c534ab51d6b8ea2309fab08b3757e9edc5972bff316f6f2affbff458ac0299613734b30dfdad20f797d172cf295cbcfee3d8ee25485d40380d3480a9372a1a6e5ecd7c4c6a9d34027ea6c197f37e86e757750c9fc24cc7cf814878b8628326c140930dbb2041bd9ee87f36ebfdbdc34522cfd4e50c9cb48dd52d4647a06d08e0f0069c104849bf30c8e61cb693dffbc69fc0ab9c5d502a227d606a1dcd630ebd799acdb1e47ce2ad52ff53f6cf4fbd5f0058fb5db915702675ea44334d42e0b6ddae78b22b5b5f7e5aa36519e31278e37b64312479b14aef9b8f12d8c1f39faf920851bd53b13bae5490c847b3312b2e956c430f1d8deea91cf171dee5017e7709d0346d81600bd5f0c41da3f548c28aa50589b293685ba059cd7f3edefdb5d8cdea364f4a42153b0632ef0b7ba18610b71fc34a781eead1dc5a00ab47b6840590ba44dafc6a16029cf50e089684194d93dc881beb62edb7ccee6304a4e71a35915f109db92690461b9e4ea21257ffb62477c20feaafc7a78e2aac2301b66893157920ce9fb114ab4f534d61bb3d17dfb4d9ef9f79a736f7c1d32ac3998356aefc876d8c38722787d564e980a1f15056cb3fe634d71d2c98e0475c79cab318b73a863362f85aeacdcfc44e61b5aeb870de9ea5b5abd24e8c19ab05e45e1e9b8894deeb9d29d65ae99aa94b5047f3c1168276cc2e491aba52b5b03703ced28c63a167f0cb3e4bb4d8e4f0292cf3ea4376510fa49a1a5efcc00f23c3cdf6402197b81262e66e17bf4307d87ffbc2b37213b316bddd65aa9d64ce6122c4a1545c5966bf4fc4c6ff17ded787ca9a3b3cadee435bbba8f6590dc4ba30895b84d5b4eb94f4b05be3c",
|
||||||
|
"82abb4ff5457b21f359754f151e456e2c0a185c8363d15918bcee0d6d49f12564ac655",
|
||||||
|
"b63b577e719494032062e3f63682098dcdcfe26cedea2a40893c847a331e4ce9",
|
||||||
|
"9f1d08be539f1244f0f69ad2",
|
||||||
|
"88dcdb0309f8c4a96ad5560f8210eda1f5afb31b85b7a8b15525777748967d4ed77c063f65d64ef19b31044f2adc690f5e457faa1abe2e127b38c626eaa94053c9ae1b6b4d0db1f02c8404b50f58210cc9fcc6fa4ecc615631da631031cd6253b4a13a3e88295ffdc775fd4bdf29655d9780dbe02b0a82aad4c4088e90b51f170909c0f98ff93ca3926067ec94be05841603db4f913b7025a9ee34b8d8bc629ed827a2a9857e0814d36b83cba21e670f8f94ceb4be5757e0b8782895b5d8605868e4f584b5bb6a5f3a94edd9b23fc2b6fa06914aec970c260fc370aa245ca68888c90c43eecb68474c9e45c53a7da055f5bfe39b56769fa56264dc8bf4c1616e30262bd501ff9fc5cd78f73ad89e093feba0393a11c6b2cbca765ba025c40dd0417dfa644fce96db5a0362235ad37a317145e7b5f3c7213c7fb3c393be57a1cb55035f06da1f0bf665653c5fe8a0f3ca67dbcbfc59852694d34819d0978cd09b508d103017168f6848258493be737cc24c2112f2afeabf41038bea1f74bc8656d9910b77d33cc691a0d9b12f7c518ecef93423cb4871949a518d2f06e5427823324275b97110f8f88b0d14788741e617f4b194e679a1627da50376a08d4f23b005c0446b46d4f534ed85e4692e7946ec818437089425ee30e47de995e8774b61003801de67939d9fed7bf0cdaf625798d0d0d04a61a2482217b890168e36f20cf1d6b81f9daf1a49a781567c4363ac2f3ebf0252d5adfbed17f98cc264ed2765aa279b7437410ee8b4cf42932e5055f4884deefd2a979ab1328f97cb750b3b7e4615b9c1c61659c90a5ff6d1c736e785587ec85040fb2c6decf789c2707974bfcbd0c7f699627b31e0762321d55bcc6acf1aabbd44abd7766d397bfbb68c424b311611d9eeb6598ca3126f569f688455da8d5ab86eb01f9c96186858c4b5e447aa2b9ca11aa5453f731beed4e09f95bb7376e200212e2f03551b8b09a19d6910f25898d692bc20bf6ed3ac9a0276db560de5c9e264f4db8fec6577042fbbd4510bb7070086508ac451a1fda26582c259412fbf1bd60cd5e921160c2604fde559b5ed4df52b805010b225f999450adadc6e108b70f169a3d8da6efbe1cce1c4908b004e928e3cdfdd0b4c5f742fd72a11c9585aa3517486201b6d9a98739b77970a88072750d29d005a291546f13b576b4249d71f04a9abf8f653ca206d98f738af2a1203bf0975f0a40138df054ee834ab73a3b1d7036567369a7ae15f808904e08adfc84b34a0e1356009d8a82e51c3e8f2170908179bfe47be8ad819cb12e85b6b76bba7c9b9398dfc00f550e32c171b4d5f2d9676063efee0b0b49660c10260ce052dd00addc3359e35c25dc33066d4b05bec7d93f71e0ad7d5ab83d844c7f33137894327f464260688ea4ce9847046e7dd0bfa48d4e15277a9586b4742daf0c5ecc59aceea6867068b03c20aad38d04a814472287d809a9285cd4dcdbf68f3f4ffb794701f4c265b2dff4aee55c9815938689162e08309df150538e60dccc03d495adcc560fb831444b922a6375845cef5dec56eff2910b5bde5f25f0e550ab5a13205de91d20896fe04a8ecc2c83d1371cf381424f8c43d2a5ced374878405f52bb92f4fa3c15d29ec151508488f9b4e42527921e245a8ee4b5d6ee95797f6ec4374d79acd7b467454a1d7eda05a8ae104534b23c46b27581abed6afc3ca555202dda94fc2b93501fe78867730a84f6f726dfd7364bc240b65d6c3022a04e09c89e36a809fbf244cc5522315110e9e33c8a4e1f1396e3e51fcdd53d9ae94fe7bf6c6ccef0ce02048a11441de3c25aa9787c577501977e486f8dfaa4c81e3183e648311148ce5cf3de56878847a9d14c0645777022c158670377dd9553eb63eb17e19ebb06202be8fd9bc2b24878cc86f9938e5996751ad9ca04b636497199f7f27dfa0f5ba2a01c3a491bec6dc5113d127f6aae38fa07ce7539a0c1817f7f0de0da538f4d85ffa394784a42eb50994e28530e3997e3345db28bafbb836fa463d34146d9f46d8d2b28b3954b9bc7f84046828e9b55e2fd663e562aa95caa97873f48f0a003d2251fb3ddbce0b6072fc17e0d3f99b655b8f41e8e6986ef7526544222e2d402489eabed4c219540605b9f5dd321ad902708601e85bc874c11efedd072aab7e10272c87b08b9457223de9fbc3abc2d1346656a524e9c67d79d4053c4257e886d6b430f5b7f57b2e5e92ae69273c1705a3074d5066def69fadea1af8fa9b3bf4890f9cda4b1833e5ed27f22bc4fe4cf452880c7b53320bc7cb748c0af6e7550ffa84e4714ec18d208131ae9e3edc6cd6fa2c60ab8ebc1ee56eafc01fbfba061e55014b9711eb58fdd01f8936d29dd081565de0b175b02989c5ff374e6f58c3383e9bc00d8a93903e6a221c7475e15aaef77594849af877f3807a76e03bdd54ff0b192bf34385d24d858d6f454810ee48141d73e3acf1aa3d19cd4c723a634cd8e25b4fb604c744e408dfd82961e46e8444f001d0991af24b3b6ec57ba41fb45122afc73ec6b25f501f1abd46181247945729337bf5083e5821968502a5a696043ee696c290095feac000957f968ac61ccb572ab2f37008830ab9a81d02456190af99873450b52df1888c3d8b6b13df65a9bb36a4b6d0538a0f179daebca2bed6f94b4670560fc5471c3770f2d004b6a138b8243068d754fd246e9881242638c6675f1611f237146f6e0f72ff2fba96f479fe0a662a81f40928f5400a0bbfb5ed07a87f457d5febdbdd6f323e2a59f749e6fc8a51d08b023734c762a91cc517401be57ffdf6a52b9174ea153abf2190ae2642955c3c02b4a15d72456c9d2f323de6fabbf56dfa3b566f1aa688c86b662bd34cf2511cc4a30621b6f1f1ac382bc1c4fa4c0d4d5a30ae90a5e54a9fb4afc1475e7c612eeb7f0e09e894c2004cd04126df9359d525d7f090e4b531916207c38c3512341c84218c86fc50061043ba1b89ddfb21cd756b391cb53e8c1cd55352be05efe562669e3986c022e30c79a97bdf087889a392e6da0d72cc7ea208aaf23408df23f3a9ea9bf9a935e49c9994a37a5dd0faf1267d5f7db47cf64ae1d3ec166466b2f882eb21698aa375cb50146c0e660e9bbb38d7bbc1c1c6d8333f7031d6a",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"68ca38fccd585eb14f953358220027046c14ef965478d3d8f206f63fef4fe3571a1b53e774b298c347cc1b69cc426d39575ccfabd5a284c7a87a0698cae9efe40543cb79f5643c3c3057a5fc991235f06f059c44a7200b509a12e864fbd748001a14790f78b54ba80cf0a4a603da9672df32b68652c1d6edd3be51cf969acfb0ae49c026fe0bce0bfc72b1ff4c47712b7a27b2cce888b9bc470b8bdda55a8d53a34d79a25947ad55b95e5406a5c5311fece3ecd46ca590b3b01b9055761da8196b21bbc468681922c66d286c32598b1e3d77f2a91d835ccd9eec231409cb2e74ede9385552517718be9f84f0f9100e368701dfa4843b7222279537306065a54d4edda3a02f1ab9edba3ddeb34dece9d5edc8797103eb942a80cb5ae130ff2e7eddd11f0cecd8f9a615d75963c44238b10ab1230d9db7371d8291feb2912d306efe4f7aea2773903d4be9a00f2bd8c03589e342269a79441c0b42ce9c6fff0a6e4e845876f7e9b342d25351fe2b1233b4f576db90ef1facfa617b96d17aa03fc824973e1c80f15e5344b0516fc28424b7faff47ea1ef4e47f6f7b50e91b8fb14027f05ca7e1bafa266a4b952cd0b9e4cab82bb4d61f99568e14a6772f36296f5d19cb04fa86ff20f04ab61d1a6f01e5282c99fe4c3254da46fb5276317be58e94b1928e3791af27dc6544f6d445dbfc7275fbbea74f98ee4aea647b654909f9fa9c88312d3759099c9d0070e3db6d55506813f8b7abe602964a7dfb9387f58e237dbf50b4185a50b65ac099352dee8695017e4dac644f42aecc3e415333cf76b08fc764a721b45d7b74f6b0a2e43637e5b4849218d3d4c6a01208f345d76af56631590e520d6bcd82627d2446b45b2c68e0be81b3924753a54f47ea27b1e08de2399b34470701c9697eedaf3248db9b28991cdc2c632fd1b376bbda279b6709d5033d1c0a3ee573bdd222ef1afe8a4397a61fc30a4e94bdc55097ecebfef6c00133dc0b72c17e2f93a11eae9fa9f1364f10fa595e8e1623dead10caac068aad3967b9ab2837dabcd8f96a77a25baef16ba84bc93661ed150ffddfbb0369683cd021e8f959c2b531bb1fa95d7a574fe5ff9aae35fb9b4a0a9829c59b932ed8634fc05ca88211da3e30839baadaea8fd9843e0e80d9598a7282500d157ee579cda5d57628e7506617d301c2adec5186708f94f069ed7bdb70cbe88549cefe1673d48c9bbbdc01d2af03945cefe6e25f757750de64cbb9d496a25adf7058f5e32c827fe75e80ba0e834e6a72344dd2aac4228828ed42fd83e4576254af5737dcd9b6c338377d46baccb02d00fdffaac12133ea0e75e791593ef3aded4ae4c9249b8d5cd20aa28cd652b9d750b88111d9b4fbe729e27882206b2f0eb614d7daaf6436816fd80d452ac71c7a7f9e8c595287407c6ab9fe8a242e98da4270b4f1d4ea7243c27f89ed46a567c643f31f967b5f12e518106f3d3e08178078cc714cb6e39079631966a9becd6f02c18e983ceeaa2106ba9043f9985b791027eb5dddceed563106bcdbc48a4ac64bd95e87c708a8cdc33811bcd16c35e193203e70ef2bc7203183fbf60d46bc581f1bdfe62387b3e6c0c4a29130d872c3f8b134e7dcfb080e7e03048c49c0e468dbc44eff4b02e50bc6889cf7600fba58c0ee409ce948aa684baef4956fd8fd4a9c4c49e84e2ff314b7900b179fc66f5fb4affb9ef7a6064354fad8c3d2d50e6f2157576f864a843dda8f547955c4d80a73d4a86b7aaeaecea886927a5ba0e97df740ec7e8b70bb650010df55d4b75f478b07b205b560d45de666d84206c1bffd02ab7b8d1c37f21c47d1711b89d16214d8151a8e75eeeb5c54c39e5a855d578708d314240a064051d8b26c6183ce755be38fe9597dd5b5d198532b1db083a4b856b8dd60bf1db197cf1df852eb6daecffd97287a6cdd4c05307722e0fac798507f75b03e9361d5627ecdb56a3b633938fa61b2673efe6c6e768e4e7055e6c1d55c7113efd3e95151b606bbf169f4296455dccb93da370150c54fc11b3682f092f30381c6ecd218a3d9d39442c8bea61d9a71b174a8b2c56e028689380879cafb7c4bc2691dda0cf6ada039755edf93f851446df9f63267f8b8f030c069fabbe6457d4f63575b5905fb927a5a720d52c351bfbc48f12440a91471697e6b2564b1a2b314fa0e6dff090079637287b635d875f120671561102ad27aa83d9f0cee41bf023bcd703ad670b43ae23bf01713650834cc1e95dd486757f0a4f6fc9337bb95738805ad5e756198579c886eb0ee77e4ba957997dde0eecd84e4c9171c84ad8f0cb23c6a289e037f3a8beeea7965ce34fa47cbd727baa4ac9e6dc3baf17049fd2386674b246aca5ef6b8496f1d17a3175f6fee86299232c7fff682f066cbed895155d475bf9fd4b5571d257534c88c93377b1a600d4c280d42aafda975eb32c740073cffa610b5fd2dda7262a2fff5da7a0f3a875c62949e0c9247827d7a49bd8185bc27967124c34b9725ee961bc8102a029786652c2571be6cf33be63cf867c2b48e5826b31b714a415fe05c27f0862a870d8fb33200719ef4ac8530a4ecf2597b4a7f2e66f078a7505803774889a1cf963083c831f46725a1ec5545d8489e53921d81f80ef99f5e51a2d5992c7769c2a7ec8bd8e0f2fd81de53c7b69b650a2d838b269185c5efd668c470943bd956e3c5e1bba5d3b927b10cee68a75372d4d6fdfa6782c05659281bc9bc56a2123967f4f50cc7ae3379ba21e1617553354b5030b3d3f0092c1824f5d47b97e6b4fedaa90aa2573e1b115ffc72d44fa8209fd8d372c8dc9ee00193b47c2a9a302875da331731713243d02eb5a57d5dc51c35988ffd742ddd75c191f1eb2c2214a1fc47b82db8ea708818262d9583f2b1b98a40b6ff6e94742f25661a51882ef28475aab12d9422b6ac48e341cbd6f38460333b5fa1cfd4d0f43aeb46c21938468fe3f7bc771972246156652d2c58b18c8cecec2dbbc0feb0fae9f6bc022e029111f94e8913c0ad741612a1426b53cff73fbb24fb7b22ab750ba1310ecf339fe12ced6a3fae17b4c429550794a8d68be891b0e30cd28e81de2fb2ecfee58bdf11794951276005eb8a5af21e03c8aaeb595ace652c5ce60a8b98f6897d82698ffbb2e02213e50d9d3f00bb42c8652d22bffb87ec576ef6e206ed6c846fd5136a87f38c9ad899371799f35a6258878418830b04da79fabd80e7290456fe17c0850a4c20e2e657f97f4a53e1a3db65bb5e71bf38eab9f56aa11e6ef71c85b8a28f04c271",
|
||||||
|
"ea196b6a64be4b0799b1c2f72281300c3a2577db44e5d3606b8b02fa8fc46c7aded7e442",
|
||||||
|
"7c86a2c06b7520d60023d18fe73d09c514ed07a91d50d8fd5bff00e7550faed1",
|
||||||
|
"952f492fe00b26028c560fc6",
|
||||||
|
"b3f3294815ce461c8843172efe93f73a8254e58a0e71953e35c15aa89a7bd9dfee967853dcbfba73d3b87fa60449cbcabf13b1206d0cb27d2c3fedcfa695b6d41efda37bb6db35449bd470a23787619ee48f981d3f0b1c8e121725b2289b6d67858a4f9ab41683bdaec8a913ca2cc292a9640efe50fb85a1d1f7b286f45d4448f85b3242f45ab44e3281d759db24dfabbae4259f127d6546ecb914d7e93e2c19230c67fba8a6cba6069023ff7ea3d8a170289c2b4391bb97a7b899228d032b36186dfbb29ae8f0e6c06d753f4c6b21982d49ee682bef50a5c2c8434510c5fa2b9c0349592f33f8d7ad6f7243d42b292aee6d210c61e3f898875b91a17a89148275031b74cb34e628d7b701775dbfcf87c79ab279a73dad14d8eed365eb9f29a007b7d2ccc07ceb8cdcdaece67fa0166e135c9a4b939426882eeca98ab887ed2e4888bbebd5afc9f2da3e9162527262b0fa85903246bc8b80df3060c890ebaa516781a2b2a138b98001287e12a9c68471912dd297bc0beadccdc31a27b7c726baf31510cd355a28e4ef786b30084af66ef135909795aa73814cbbc6552270d5e11d46e9497ba30d6d8cecf343d16e7e3357bc9bbfbc7c1dcaa5fafd8a9b07056129da02e6228886463474c5af1d670bc14cf2868b816cc71578ad807a37477341c8192bfc2e8b1f7bfd58827e041f70384f92bb4c6acc415dde5099a1c2b27b709f9e53d1dab07c87a042ca4af7a2a6ee57b37bf2bb42259d372ecfeaf1dc55ac3a9f211f16fef3b2d5f11dc19fd1f425c14779580b2501ec6e0a84220e7e12baf9e0fee3e8cf499a7fba6721a746f598f04ee8ab4df31fb8fa5ce2d2419d5551155c009f2780cdd225ec2c19f94fb9c8b785ad4574b4da766eabfa696a1994e64a2518d1bcade6390cc683a6e80cf8b163c3e58cfa1134ee743079347f08a89c81478668df32ce9cdd7b853db5cf7af13436f3bbb11bcfa8f6b6d727a1df84f99fb3a5c248b8fd5baf669b68fd9af45298030f3251bf0351fa9b58b0b9fba53ecfd838300790ebd689744c1b7b333fbed76c8fb96fc669ecc6695ff5bf8379dd2a3c270af858cc60894be8922d69fb9707bd2a7825f2eec4a5056e5e91714f4dcfa86974259fcbfd5f20d55923a0a9936fb20e5ae9670e2019336e15f530c0be449fe355a7a02c0938d60720d5b8f4f59d2e4213ad5251c6058312b43d47c44ffc8946a98797f5ace279d3e126da63633c0eff1c412febdd47817aaee466c639e43637c1e179f606780ab490d3f0b3c2d79709f1262305fc87c02f68da2dc32f8c544e7b358c3a5d2c27986a19d13fe736c60a3524e94caa55e853eedeece985d16bfa6c487bed6583436cf82077fcdcf90a05f49db50588f46550f7a0c3a1cfca902d66d25dba8d2c53bb5557cc1d87c8a407898b3c30c4f0852df92d839859c191228d0a47324ea9ec2e0ae84513cbe4ff4aff85e77b8587f1044bcb9775099ebc2f28fbcd1cad58a8ce1f072f2228f559fbfdd8405d86f8262c27c3d95e01016b343c6a4e59dec81b59bb6e3c6109a4cffffa85e9752ed2149b5624417c0dfd1a27bd2630bf59814f15820c43bfa317be59ef6f433c95e8be154a8ae94765bcedadebb717f0d8c24e01e1952bd104ba9620f067554ae0faeb78f13c622c45d97b2b5774a3e30cb07f2cf0e8b19d1266d8a8861f3772305e24ec5c9cb714806c7d705a3bed6385f8be4e12562e17ec3df01afb4ef6f7427c48a1bc0e64fc65eb1c3d3ff2d6687e4c275a019f5ab5c63bbe47e3680fb1802d5835c4d494f0f394de1ae47f81eef005127d0971c4589c456ae6a69855f35635c28b590c1b93f155fabcab59b6c7cd8ea1c4ed1f67093aa782c54329cdcf9bf84a40400de707b894587d6e08cf7fd72fa45b6709a26e97ff5ec1269b8042358f872a79e8c2db1c7ebffac014d6b6f71b0c1c1945ddedaf5b6911668059b61b55eea4737aa307c829309c9ea548fba2bede023849bd61b5a467cd1ab1c61205ce64301e2531e5d58d03c74ecdafe1f5b74627be8716cad0d0a0be60984c9f9dfeae24a6c4949170ce2f589326e0a76c447a578ea3a5e4bd9f18884f18843eb1a78aa2fae06a7569a97551b227c34d429c8e1c8c5417ced93c30dcc607cb32a365d87328aaecb4ce57ab8e74f0d9099e267cfb747a3bca9f76b5f6dfb543bc4b5c06c3646062ec14f511058eb2939601913f8a0f1785249cb72b0bb1c12a9508b23caf490537eec53f614f3e06592eb61f75c1cecfa514cf7b500b0375095d5db74556220131390b77d0db72711c0c7229a5769b1d2b3f5105f3a4370beb1cacbd93ce32f89f1fc833c7949211dd204616c013a3399a22f5325f1a00008f4c8ee7dc5bd7476848721fef843123a6213cb0c0b6ae84233ed01a77a115d06e08990b8e60cfa4f41dbc9505cfae76463278b6c6b5ac7c3b83284caaba4a6a1d739c392528ff5b06bc3b82e98060e3001279a44aabaacc661fb14e7581d1235940cbe067c6b386da09454e0467c785ed0b65d41ff4cf36ba5f63d3ff2b45c11c6c22d3ea8ebbf1d52d770e0ebf2ba0c67c7d3641c145cab474a88119335990137fa82a340c2cc8c453752a3aa801127a47aeefe66d1af1a26ee1cd0e6d935bd548f6ce33a9c204be02ba08f9fa03c685665375db7c0c656ddf3e441ddd96b0d2018beff5086cc63339f26bc8332a5e6a1422bfedb69187a3443c23b630a28b02f8075faf3ff2fbeef6cdf02ba4af47a765003de2254b69f487bb5d038759a33ce6885611198b81b0b6fc5d7a531a7a90dbc3556aa758db1657698cb3698b8207b1c1b589efe5d52790667ac483dde9543953c6392d5eb8afdafa205d325e314f810e9c7722cbf5bb76fd6502733149bf21c60717ff5bc366b85ee9f206bb1f330ea72f61a9766090eabde747b1eb9c046cc8713d5a4f8d4b7dcd7c61f2496c5b467608cd9260382b8f11b04c318a5ebb6411a4c7fa060e08c295c6062ac644bd3d10bcbfcfe2e3748eba66f65d904ff21147faa8475f508f21238d42f62b697249b9fceb905127f7684c8130cb8663f09cd25ea038078e1980237389337d1446c3a77bce41b37b50b9c3a020526e7b7b3bef370cd7af71b225700627060eb65693899d277ed130ec5ed9eee75d4886f31aa93bbf302e0c69c9c4499396b43dceb67c02fafaff8b56698308393a03f60babde883f00de2c66831f024fafaf98b2fcf37a9ce01d4f34e95c9408395716dcf83fe86c7a0f5e3e6741c3b63b6ebe9964f1d5005eeb732ce66402007beb3e6a087053",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"9100c5b2d7c5d5a854bce55e82f94b89a268da7b66357a661dcf75cba10a1b320ae0e4e1a5b989f9766e57f867a3810a0b5b857191ffd7aece4c796f5694a2617486421940cc12b63a6aaea20d2fac188b318a1c3061cafeae436e04d710654b96a864d674768caee03a50ed6afc06f52d90115df1db5c9f1ecaa4f5da094070b1a447251ad3d4fb0e24e87821ee6d4e7e7eac7059080f77d2b36cacbdac1c6e5063946a376865458c4ebdad3c2afcbba8a82b01b03a7882eee42eab904a19e0aead4ae515b02aa2fee74f3a114bf5b9f320baa35b3225491653f4a69e0d864cbbd031d0805b727e42c2b9530dae0c01cfc6a42af8ca730e1d67b4bb743a072f0a38008b937209d534c2284271344340fae76af2b1dd00cf44b48ab8ee92e8f9cae8845e5a8d338f505cd1c19014018bfb6b7dad487e7c8c32064421982c1a63149ec16f2bf4fe7b50cf3ce1e33d6cdea8e98bf067077c9a0ec1bba6edd5090273ca719ebf6f1a0f3e56f021945cff3c468b2dad92a947a06a024758d7505a4a1bcbe9da3a03e97859da99ed36982a7c23572ab60071566b749dc34bee1d9609e87fe32282cc9adba633c9ddcbf359ef4a83a54af5fbb5699978b487954a907dc9739f4b3f3927e66cf0c338e31c272da0cc7795c72dfe60a5b2e73bfd77b8c6ea58122a913910fe29d3360cef5d398f29b024f0dd225183d538bed2b076989aceaac460e3d45e0ca7941897f151261a024b0adf6d5b62429420144497adde6557a3c53b7723471fb760b6a8b1dcc2b327cd939528f5d7bc16ec00ad99df12f082d82bf9fb7318b3d3ce5b84ab1e38d2ebcb6713c03fd0d62bd083c4af96b4316ee02b6953431c261278aabd96e28f81adf7946e3664446135c825e45ed916ccb941350c84523296cadd5360bfe3e16dda75db10da1f710fe796f3456f0911294a4735cf9968656345b9c3049ca47176194c86f36cf702538df699fcffaa254af15b198ac37eed0837b00cd3547e496ecacf6136c6648a535a235059cd75a3bfd0bc49933b379b72e7a8463c268faaf05f0b27256fb179c9d4c923a13ec6600f83aaa2bee13e30c8e676040c06aefc65ba238a29d403f3a8cc164a0bdcaa1a5f54bc1d35fa4efee0c402eccab1e92f6b0cba94e1bd87898a9dd3957a7eafd9d26bf70866450646090833d4b91c032428bdb9097b409305de669a58e44931b7b428bf1a6dc56177cd944b87b04eabd80c64e287a5758c83db26dbc06f0c772335363ea2fb9f19c833644fe3b3fbbbbf5f9d460412d287eef862ae676f258aa45bc8465667601e9ac46e7d77693936c8d67ccde94e54d746b785ad26aa38ca0500105b6870790235e780ac50b9e3198f5fe678ae3a4ff4f1d4a2177edae183daf2de42625845973fc544907e27a90d868f8634c9d529bbaacbd228a5b4ac7fa68ac208e207a022cce4b24a0b5b5791eaddc6b3b3ef6e5dba41855ff531de9bbca0a39ea743c0732772bd32cd15c4b7f28a6ba579d902331a88920fb970aa75114e14b891d42cb947e9eb14feafccf1393796b21099e52b21773adae8e550f93364b1c438dd7d7fc76994c51860b652974d04a7e6ead207610de149f231422595f4e9ced1674d98d0e15ee841143ad8613f804729524e8a5f30d451611676f70a60c5dcc7127497f4d27f35e7ba0e48f98e9022e0deac400e809170970867a1682c7d2f3ef2c632c44568abff76f4f804841ae462c7247147b6e1debe48802674fd55b2ef1be5b4604d5f60c35358c7d773ab3a3ad0ab81868c6044d4e06a48ddbffacddadf813a2ce09aef34f3b60b666245a032f021b87c81fc506166983f25930cff728d399f6dd48ea1c745ad2da7f2cdd9e3ee915f708db0d1f3481018db1c174ea950ed17247bb8ebc065186758e5403bd4d19a445e4a15519326696e4280bcecd1a903f525bbe1e521f94d79df8db4b35f4ef7bd990c0f2c32789a75f95761ca0064bf251fa00b409a58b979e56d2c44bc2302552f118162891bd78272384c739c0c98bbaca3fc46fbb5bfe123eb25df0e27343e38b5a0c2d0774443af91b64b9d4e0649f20290edb84fcedb3bf4ba491bee8754a32716739e5ab64deb6c9888bb9fd2ada1629a59b16934ec5dee3678dcbdcc7fe5e2f3833da9d1281669b1d108837eaae5180396813883de26b957037623825b0675df431fb06b35191c06229f84cc849ccf1b1e079efc2e575331cd77b3297d2908c048b82b7dd14883f3e707bf6ca38f87c19625bec47c11f54988a97205d27ac51a32f19704391af72021b78cc4461386dc3844a1b45596fede3f70e311eba92b1d9ac221d3dc19f3fdd080c2169348f2cc8c9380e12a7ebf69efa37bda4ca6f7e66919b94532ac43022c0518c04d0a8cd99e0cbac88b7a317a1dac5469534b4fbc64080196b44498e149b0a196bb2d6f59392a21c4a4523ec1ff922a52de790e42810fd9355471169d22b734dde4a3361ecd57e271a92132a8b35cfa91d508d45618ad8c6c1ea209405a3d1d3ee1535caeaa3f20546052fc13aff7a584ff79db1726678344098d8563caa2a2abf6fe5aa03d7af49dccf1b17be85600e7cfdbfff54282394b0fbeafda615185574fdff78d59ec2a26dddba1c531a1ac007cabf5be2e2f0a3dedb9174e0a9da5597c9de6d68911fc66ec9d2b1e3fd71ebb83147ab14384ee303d067f47a324a01fc187f54a98f1b0848fdba2ceb3c18936d503e71887d548c4dbc70b7eecac9ead3393f8cb85a84f1484f2e237b36b6d886f54a0f629e8bb05b0c6839c722149a5b541703aeac04e6eb230a5659b12ed0a668d018f75bc94258218c1f5390b9aee4c0b2836cb76a47da649e2425bcf4cc15c4d51d109e5f78cfdb88137c31b2510264e46f1c4eb6e6b3450ad901ff9517b47a24d508844dc85fc5dbcc079e2d09f301691f401ff5f36500cc66f0617eb4dba389d427c7ac778d78438506608f0961f818a2080ea56d0f61c40fc342b49ee63e730df61f757387b9089e1987977b7fa02d87aec2e4be24b8bdf7fb6286d190f9df870944fa910df32f178ab692fa56b071f57366a3981f51800ab416dc4500abcc19e0c6aaeeb9ca063470993ec749a0bcbd07604516b1d51175ebedbaec8986f67a4d9158f75b5f3bcbe86a83220b4fdf12a0242951f94ac7d52882b1b209b82c4749753ea4d46a60bcc4f3eed033bde2d3d20c25cb46fd907f7052217a0a4db143b2efe8875a59441f4d22ef70d0c244b2de6a7e15581e84c860a6326ae3e3aea6d3972e2de0623d2d852c9e65eed318bd3d86d29595575df60d9050e1740f884796b6657718a294adcf2303adf61c6b23933db93885172e82a78f741b8efc6315a2c88ccb6b11692a346cd82a79334e0c610734e61e6378b5e2ecc161d924778bfcf4475805a0823a0d5a54768d9272ee99b7c4a81b3d5dfe1a2f5ff34",
|
||||||
|
"3c77f30bbb698b1571aeb54653fcae2c23dc16be58603f0c361eedd813ec0c4f63005a1e69e533da93e820e6e4ce1308aa29c60289060ebf24fc9738e8a4874ca4e26a0dc79ee75b8607416bd554737f",
|
||||||
|
"0223c0a6052bb3cdc99a284fa169ba76be2df53b677642a606090a9267a60769",
|
||||||
|
"7d3981073f90c6648c5e9c74",
|
||||||
|
"61ec5230306b70113f67b340575b77ef76d521ff75b754d551e4177591a02351ad382b2a4067f2b3af7e8e15431c7133e98be9d8293d17ef40161dbad9a4f1a4f30cdd557bb9a8b03b5f1b277c850e23ecfa0fc2ab1102e4b1d5e836a606883c3d43527fc3aa26955964b144a9a56cafa7b174d72a0635b80e7b4f871ead3838a955a14c4b8c5c3c66fd86a5e4ff10dfaa92105378bbc5f76ad29727e5bc4779ba3e6dc19bf45020f6ce4dfb3400df05cac51577d58eec21b22839b8f055226b204e641783bb3305b4461172f1c1d48eec56fe6f82aae564ac6688d7b0994747d9b23a24418e69f8a4fc548f854f86baacbdec78b7597b138c453349034c8cad2ff272781e0e6799ef2f8addaf18528736aef21ef8c2d213161e36b2c7815fcfc40747626e0165684e46a9a2275c533d548e52a9952a556168195d602ead86f6bd699e97ca59f4cb2050ff148f5bdfec358dc4542ff2f700db9861dfe5ba377ec7fdc0fcb2501e72fe6873c7cc76b95b4f300857f76e6e6e370119f403b556115b19fee7009f4f6675ad2d174f44002e35ddc360f309f20a3a1dbf39d90d7e5fa2106c53afb0bf445e4cede59cb50b8a7a2c0961d00b2c251f2d815309f74a46a424838ee87f1229273ff3b66dfb79e3b1ce11bd60e061e60e3f37bd7ac896b618cd78388590f44b1a276b965a4b95f2e3a7a175b30fb45dc7a71d4b3a1a33e98af30dbb46a217c50046ac21b8bbe9537c02f05a5780c8a5d796bd6424fd9e9f3ed5932069bc050bf4a1898a0ef0ca756aa2e2269b709cc92e0c5192ab49d692143388ede2bde4923c85eae8f59db5c7711dabeb33743c692be6dfebd815456958b5e1384a109f891f433e7b4a1031d4f30478b05766dd97eb964a28f2f7b55aa6c27c7f4ebf4d47ee8709bf99915426b3896412a855798e392e111789213af537cff7a976b4509e0eb6ffbb8e886a3596a242d16d95109b0ff562c624e06636a3611f804f9b2e252afe8a4e5e868b48e9e734f688f2da2012d7fdfe2d3aca75fd74730a85aae90353417fd52b92d28a5098b6af358a096b859859916bcd5a8f779676c6e04ea461fe62872050af92d08cdf1124bde1e889ace3c923457ecfe0a635ec757907a131ad7c2ca3f60e1317880f843c5e63f4ba59ab2882a492dd1e070b070af6f60e18cca29541206a7b267c3f75a5327fd9b8ffc9b36b57b73b36e586541d15c85253e17a2581e8f8a1518f275cc79afcf2b5c88a16e9bf553e757df089b5db90a9dcdc1867b788fe75abb5161dd7ee1cf37d3f0faa793ddb1bbf1eca13f4220ea63af8ef7c0e7144d999ba1c5a983e74d48cef708c1d28d3c0a168ab87d0ef70f381693f0d438ce013ffa2cba65a8cf6b498a7120209564535b7372690329cdbd74eaa76765962720f06aae58338a10064ad80f5a67395db2c31d36b1f5eb777306395f192599d2f737327afdcd9f14b3f24155a3f974915d3302427494fad756703b13afcd1764ef9735e7dbff920f1253cb668e9f40632aea1e0b4620db162138e4a97e6f0729b14be4a7c3256250d5e7423ba1238c704503c51cfc9cb68db7001b2f597a15e77138beea02e11e0bb98a72f2a77b7260e9172fe7e60483114ddd836addd966b69570db5eb26a0cfc4f8a8b80d26357ed51a70165bc0dd11ad7467688025bdb532e7222ea12f23c44d08d111b0ad4acb2f5b3d6b45c387d541ffc84466ed57acacefb1436ef00bcb5b6211dfd0650113ac369b9f3e4891acb2693c377467b1e9c949cc0ea6c4a72ef9292964275ed397cd2b1ed25fe1aa8f47e90cde362392da5e53893eef6e4f61decae1a75e3b726f0596f09c3cba62aa08bea89984b484d5768296a5afa8b0759dceba530a169d22b81979212b3343db35ce4e4766dd251ea6a47f5033cc090d6577efbed441bb4f8944937e812f12ef17ede76df621bd4cfa31567ade18b74583a2b783279150d584ca13c0d4784b70156afdf9be8ae96666b82def888465cd3df349de427d5f5b3572e4f963d33f968e6780e381ca196bc04a6664fe93fdc8558b21b84130dfa2a646950eb2e927885925af46d7a28d1507bcc3c02ba98318bfebe5b9eea1bd47935ad869eb701cbc35a9aef5efad88ff54eb350a34ccef2e159de8e16135b81105bf799fbd86aa11653b5ef93a1ab1c367231d61b42b8bdb4f04d8d05396d53247d51890be9b56c51cb19eec0fd1e6b8cdc98376b6c6b30963ac7ab02656ff94dec0e3a0eb3f3ffb8bebd99d5889df98e6c77093c370373dd5f17871fb334c7eb12c6ca22deb75bdac9eaf24281c965dffe03da9c940e13fb382fb6be332797813710a7cd2e7720f5b9e53fc0d98fcceeea4a8e9f787e670d60bfc4a849f34571e5d09b9e9c28cdf2b2d888eca9bb31ea8b9239bd19dca86880ad3e12b1583acc3a6d1f0a438ce3b5a337487279dc4ead1b214272d455e6a2c8cce4ae3bb29abfdbe77a67ababeaff5dd9c96b17f589cd4615c0209eba5e4b1c7167b4b739ca4b9957185961529d1082226f85068890c94aa1f1c244259ef7b120e40114926a49c4412b67b4caef1ff3ce6f3aea3c6107b830cd34df9f4d73d7d978b6b9d5c481e9d76e83d649e742b098334838fe50d80975fb567642d3b72c461ef3072ebb1d03c0099e97575bae6a12cd2352d9d296351df6965d736d7568c2911394a73d199743526ba54dd62c56c598f4e78495c0172739274c0b8c96755e489765723a24a8704093a94544f6c8764dcd1ce6b4bf2917cfad27d85e4442b4e5bd577ea1a88c2b79d61cc1be01ee9028235b36444483b4e45da1087bf6d45ca540620de5aacc644a0d5c4b807b582c7b058e140eebca539947502bf73c9abc81a0e3a618b39d3a38c4ff7f94767fd7e6b9eb61e629806bc3d183bdade7e369d180dd2f57fef677e22ce41be7224f11723a85a3f1d14d7b72dc98ccb2816b77e625ce3db3e2c5753af8b079e0d63939079a01910ee4699cb405d4d9c60e4ac86a7fda3a4c9c290662afbdb7678c3a84c87ff83470fa8a416511a06d3216a1445699d7ad7e6980491fd596d39762d576b08fcbf0825243c1fc01ec8300780857c429c607113160a8354f6699b368a87983464472a5754fd58943fca6f6779764fbe6cbb510d5280292df02c4a7ed9acec8c95ad67ebcda71d0f519ac18db9b43b28244cd34fe02c5d694df57410eb54c5e1ca0f8501e7776a811d7ee81eb9d8c80b2ca50a012b5eecd5428af965b217e7fdac80be88a01f76d473105b027eb557a523f13c55e1670ff34627667649573e0f19dda41c525a8c96c2866a88bd73e66c786767e1657960f6676d8a22be1c6024158a0f0e4ec761148b5a3d8ea481d8fed94855be82479ba23213190054f937838f0e35e00aa74c89b294c29ea25ad7e96b4b6fa952ea8f1cbe5397b7c86d0b74ccc25e22c88736b045fe86110bffa0679f28a1f27162b51410498cb7",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"0fcff2c29cbb5cc40bfd2ec573ecf368275ade6a00e5730b77dab17e437b46524b3814e7f470acff6ddac4e0c6b748ed112657120bca1d83a4ce01e74a473995804d7c74bd28732a02370ac8ef52b600790d1284d82f077cfe096448509dddd0eb5944a882b7d384efdd4dde3003dea910f12de82035651e3ec9668e66435f519da3fa1f5bcda34aaaf028daf3068304f7b1ec18e65136241a9db281e011d27db5cc9c1099405a4430821e2488a228805314983966ce5d806b0f014c21d4c9d6a066e63aa6407ed6c29cfa4a3e22ca913762ca9d31271d9c371fe858f3b22e931814cdbe544b9416e88f6026b12bb8e88d8285beaaa35be1c24339b5f567480d7b16cbcf6160e549ef4570a0702889feaa0ebc54b11735735b6e2850d5715e5087291fe8890432784aa219bacaa2b874b075c9628cfed5e76dfe38426f9693f6bfb2de49b710c101b2dabb7c7c74f12de9ba8f75b8645d25629568d12bfbc7eaada63364b6f56569cf21e54c95d6797e9008f3496c506ecfe5d6a010d168fb7f0e2ee3c423492df36a133fffe9b87d7ac070c32cc131fba6089cb7d904b25812e03cd6048504f7ef1736ee00ee6b7aaedb3dda9c6fd6437772fa5076aca9888ce55e906a62875979bd477aabb2f4598d32342aa10a6d187c6768f213117a9ff6d830603bb7b9b475002e20b2237a4055ae6af6b8d70e343e76265188a0f07e7820dfb3d898684d99966d4bb9e78b0e95f5044dcc12810a89a75b11474c8fc06c6e734407db91a072ffeb2be6773a7c6c3ec939514b43daf29feb3aeb7afa57e96d9cf0492d90bb2c7be613f2208f5f5f5898b0a3db8a967a75d065efcabdd83759c88086583bb3d422c6c6425525a1adbd515199dbe71350b77940813618b88fe139153974c80d968ed4d9e3f97a91b7cce250a7c963f880dc38011250b9a131f2b76b677f78fd0e4cd6f1465182fd1d644dc42db0bcad8df4ae9f456841765af8e1c1775abf85a69577ece6f9e9035e36c88be784397479e713be4f5434aa4c166bc4702a4916c0c003a6baecaa182372a30af6dc7e6fc4912d13e662bd327829f6e85340fe130001babaee64d211d6761bcc52993c162a692a10cbe7434310392b64792a777a2b31341995072a6b7d4538cfde74e609dd1019a9f75cec0896186c0f42e3896d15be87aac5b11642f74e11d5c2f7de9f07f848ff543507ea4d73fa8f5683fc6b41831606352c482c7a5a013c51e0db59d824582c595f17a6d2113528943194d6b5aadcead62516507f178cd0f76729cf8b81fce4e0138ab224bfdbb8f16f8ea6196b90ef90a63f0fbdcbdfb5320984be8a80a26b932d1db7ecf870dd67fe838069136ff9b9ae087779e82cacf1b06a7b310ce6c439047c26fcec0364ea87e4549a544d540256cb7c3ef7282fa792aad89e919dd89519fe910501f5ef88da43232e917730e742ac2539d454e066feb9058f56dd246fdbb674dcab636585a788b338ffe41f4190447a65985acb9613d02669ad4ad888004c65acb0ca315752e58f51c9ae9259f20cbe8a668a207a5a46e30891bc909108f53db8bf6f0f11549e621d4cf4763e0035c867bfe9e1192fc421c080b25289a78f4167fe517852efdb6f3ccfe67ad01b4337da2c18f35bdc151c5dc76ee66efd27d5fc784e4e6829bea4f8a41ec8bf61ff998d178ce9f4a10551687337d7705eac6cd7fabb3f2379e31c1d01e4dc63e475f0fb01d9efa3de400b5177e2c2d68f2ead89e9ecad62cfc97fd0ad5b3391d0248dd2fd7c75dcbd802d3463ef0af21eb77b07a3286a72f1e9439f457630159abde7983a5c74f7dda12b40913632afedadb691d62003c70a46664fbd976457544cef8ea863858505b1c596e7f745d4a5fb657b1c694226afa9756c40d9c49425b323ce17a8531c5919b24010f715b5f27a300ee37334931ca9ff5c83c3f0a87713768ebccaaa15e35c56f3536ba945e5d954c94c885c68325bc4b51fb55d96c8d424849ece9a812af0747d5b1dc240f71609439f65acd1c17086e025e376eeb79a7255680cd692fc4b0f5768d1985fe8a1a387074f58c8bfdea8e5c11ed379b845ce2052a5b24ef0c1a658923eb87adf5b01e6aa59ae6937564ef97421722c67404cb9e5fe07d5bfad2e52ebe6cccb41ceb1eb2760545fb6a3582bc4ca572b0aa4e4f0a2ecc56299f3b485d980501a4e010576615ad518fd2d43c1f79aed013ed1f1e1bdb74357aaf7dc84772c9ec62da43c8ffe11a7fb3eeabc3584a936c37b28a438dfe78f89de6b0d5597ac1bc55057544e68fb49a6e505db69af122c2a3ad06219b7f2a2955db0ebf55c06baac5e0efac609436dee484857f75a8421945484ad0c7650a1d3008cc85c938208f19002b7994524878d6ddf85c763a65cb72a09c3a059657459f13cb584bfbd754fbf2de904517092be4f1786b2bde26ae8eb2d884592fc9e84395408f8117e47d1ab30d5fca167bbf07e41a33c230d240e3aac53cda9f251e24659da57d721288252fe7ff3653ae3e47b86209e9344accef0009b99f2ec7b3845558f1d77b89fc9b61ebc1b589fffd3261f71b9631e87541e22ed100e694854bed771358f10fe452fba61875a605b8080cc39e3eac13708e32518f28e60464c38b782c7c7800df63b6e7e95ced9154ea54e32900f6998f38eb1e51c112b6949e2eb11a96b1ea0a68c1e3b5af750a99c9fdb2cae44c5a1d37686ef87b158d19343e23daf00dd558cfb91e6f2e18f8e806abb2faf80d082f657717d08ca4e9c0d30d9bc30b612bcb1a3a3a3843231059dec344c6c04ce625b3fe064092e00175fd9d38f8fe54c4088efe30d211412be01460a6d4ad8d0a618b00a21de0a383de30ccd72f119b27a08958729a999e8aadff21829cbe8cfe398d90476e33db4c64981383a9aeab4a27f3bcb29d4b3d3b3a6ebdd71d3ac546b8658e269959630de176819b153cd53d2091efbddd2cf9178ba6ee98e1a3df9a095db0a2b713a0988a22239f5f08cc8f9abc3d67d9267f54dd5dedbf01bd490b0b09adb21d4e5aa7707e36cf77034f01bf8c7988a2e8dd7046bb2f486878436371f1258f3f7026afee6d7f6560be67103ad098edc9665e00118d4879f58bdd677cf2e6bc631d5c517acbb6db8a1debb4fe7492b7daf0b7ec7df056637c23caf926a1a589bef1db29cd81f547afd0fc9e459f46108ffdfcfdee43515a771c439dbde9177ceaf296a8749be0146cdca2b26be8c2ebd6cfd9b5032b1f7a375307f54c2f622711f8cf8684afaaf17c4da3e83666c40d26adc239c8d1a40024bbf560db5787ed404763d4e70ec6635c6a4b82c10f8ff7ad42217613c57648716ba94cb33129f3789dc86f9c8ec2e8e90e6bba0dfba1bb3dc3215188979a09f33346a6647099ed0e624c9ae10f83da0def840bdb25b718e8d86a616ff46b5327b1f99c22937920f5b5bbd6b53fa0b32f24befa4a7603234e6d94be51f00189a20b15c49e8ee58434a15ae9d10b9cf0204bfa7ab1fd9e006b22bebLine truncated
|
||||||
|
"0c5fb7075f5e15a6733737b614bf46871e29417e4b140bae6e10081623f5c52f557c36b4da4b5a4e82920497514b1e6f745fedbf73f86ee10976f82c6cbd5bc13a917514ddd062",
|
||||||
|
"e70954c812cac03e367e99f7b82a6dcc073d2f679f965d524872756ee58654cc",
|
||||||
|
"5f6267f6b3b21423267310e6",
|
||||||
|
"c53868c0fdc14e891ae1bc257fbb13be210a5d9cdbd9d18fe1b474f9a1929dbba3f25222d8fe8c1be3eef22352100064b922fd9642ad128a202b6382ae0a67c8affb0c5bfa1a80e55c1084cc372485243df872d677a80a3ef1ca3589908bca621f6f50133eb762cb9c05775d13db7dd3eb65ffd3eef96e8dd42928facc68390f6bbc50b17e1ef5ea6310d8756dd177be2cceb63a97bcceaa046794915589ca022d90756b02c22e8634c0ed44192abc3b8b1e2814c855ab27aaae3bdd801a73e6209fdd559ceb59a94fd98a66d12a31a643ca2f4b07ed910bc390f77ab89395d5cd1d783d8940dad4447f0452991b209cfcd998b0c814cebd08f9ff15052818bab0bf51c3b72ac1020d3b0974fbdf4ff941b1ab9c01f284fe82f2fd89c0aeb4b9fbb0a74ece08b3debc7b65e7263e2922fd4aba15ae3cba7885d04127c8e06a67f244e7aa4556f8694a5db6653f6e48d6de54f9e4024d25d3236d4f933205b6a358aa1506f832ef7d556c6a1bfe4aabfce51f3b5ac64bf6ab1e665bddb12fe13db9f07a55db3da3886df36ddb89f3a4939b1e9e5b701301570e3d01c0b947f498dcc6af438cc15e6038cb78a78986da0316cab67bca3e28c95e6b7e6b36cae9202cf4a77a0e15d3c3291d267aeee172dd587a944719b9fbe077603b4d39d4302b9a6415aa07af309a5e1cf7a9379552becdb4bc6a0b5c85d2e63bb141c405afc58a8b2b4188b3883a24eedf98dd50fc54725c440ccdb03514a6f37cab49296b6826b6bc7d7ad8cac0a3425eeb6866d94119acdad468cefe162a29e8831c77aa83321e8ae3e20e968cfe51dbf2b63f4e26c61536e6be4f63d61bbd06af38023b15f4fccb8ae0356d924dbf646bff69d1ac0d6e1c7f40b12d6d16e52d1c15958add5708bd38c514e47fe623a67c9ec211cd625b398fa7fd67a23e6e9f65d42dda2bae94524372fbc1a7e0ab3f1c451c126135536e73c573749aa60177dfb68843752b010e2cb9c1afaf51c94a48cf8ac7aab3fb200aaebcedefc6cccb581848da0121af92d9f4be002f0c2beffdfa65c36bec80e7f62d7009b1eb719d24b96e97059e6b50a52662c2c833738849f342391514349305228b29bfa9c7cf2a931558ca8e704c600148a28bd871465b23af499c11784aa45acd051f276d82789c58b14f12619372be4bc3a285f6cee21d65648d18e61752d6e7957736d3385f8ad36702c451c61ed475997d6d9f11c8be5257d8febce329aa701028aa2b5644b8515a95b5e866780e32754ac2e6f2e31b2c04a4ad35cbcbc25b23e9bf49cb1a5d877ca30880741757c29303af8676546760016f1538991b37cf0cd24ad3b1d877e5e1bd083e4b990af6ff5c0b28e530db3f463d21e76c928c8e1ffaa6c045937ea171a9071827a173e231f50e95430ae4895932c88ce048058ce6d0a50ca5c1842506158e98bb2912a61c7991a2256c97cb9050a4bb3ca32594622756291340561e9e584dd2e096263b6ff8eb898ae86f5f24500320d2d0ebb30d84cb4ef876a877dad23a611b39bf0cba5e22f2850e11c298fa23fed40691b83acc87136f8fa540b1dc40d1b0d0bd489ee9dad785c121955a094a2c6bd3353e142c04f7b88b2eb3305fd00d5eddb391b73fa2b16a6357aaa2abf2059ec979bd3ce06d5fff1c325bbe5c833a101615750613047d8155ac0c3a0734cc6aaeae7cb65d7501cb95f9d6d1161d09c961c0681547faf7983ed2efaf4e0fbb87a06169ecff1d0ee540a9223a73f75584441d4669cac09c2dbdb8aa2aed74eb9a2870f2021eb16e5f5c3e79a24d7110af4bece22a1086d27642550cadfa4f0e03f2c032a2745e1c9277a4f67fa4dc74ba056110fed3a63f643567d079c9430b8d5b3bf57a9b3f02d486d870229fee5462043b6bda8d265c745ddc1b8952bf91828d6db2edcfca7051e74df9dd456dca5e04ba469b9ff6a8130aab3903c05659b8f31cf4ba4c22511493a36541ff9d88c708dfb714d52a3c0356543e6efad37530b598bb63c3724772907abe4cad39c896c62daf5b30cd7d37eb36a7be2494353028c76e8d148b018c7bb755c45d2a33f61944071bae8316881e9aa37e4ec2374aac4f8436ed3c7db2092326538f07fc6644e0239899e3335f73c1e3c4602b12d19d7b639d4968974b6b2703ec1add8cd930cbafff4158f68f06aaac83bb4a2e31466e2ddc247ad71c5f4c49af7defd1394e21819cc24c78380caefb2ce87c0d1050680313037def12ca21cf67bb6692d6e4a9e90a9c9a0b7118ac300c6c6f636337aa25bc59cf1d9749dc183803cc0ccd1ff53210352795c6edb49ff1e5e8ebaee7b3eda6e3c0c340fa60594115e37fab60133b8a3b39d2e63db0bc6a03973e236fca801553912f93feafd8b96766049dd2066f3c5ac9222121ee9d36cbcd8f713adc8779949941f8a8dcc92ade62e46e9f1b292d5f7eced14c3bff50a811cb762ced1f103652773ef946e18569eb5892626627e085d4ffb3102c1586ddf88acbaeed903b22d3e7ccd8b8ddcdfddb872403240bc8e0e46a068f55bbddaf90fffb9a914187aac2ceedf21fefa1fe32fc7bdbb9fd76dcda1fca7b39107d308d11a118e47499dc4092ef0cd28d0d9af84440f095b4feb7adcba198894cd89a324c60ed0b996c520d4b33391bbbef1997256af7ba7ec1069244359066af81543ca23105742fee3480f890373d3205236bed566cd22a62bf69f8c0f27b714f84a203bca1605865e2cc2f9211389e0df7a4b3aab9d10826639357efe1f5fe64a1bd6d06d0b5605658c4d2d12e1bec77e70ea393b0a09043dd7d6684bd53f4c883f2f6928d99ba91873d063d43600f9105d503b11d8dc2b05e34b4fcf18e78b2b6c97d3b2c9249a2f6566ddab2a8a67fed6c9f8af2f4ef98dd579f2d4fb572e178489c503df5d5f03bee9920db347a6e734ed72ec7233387f1579c13725599a33a90915ddf03725dce20fd3806abc1029a20732380596057830ed63b6edcaa4d4418871bbfd58de1d1f2800588ed207f2016e11abd1baf1895f6096e2c75cc5916836a9ddc09cab4c28e53fadbd7d3080088131cc270095315b61011b0cea5b4d64b647bbcea54d20be1eec0992c72fc9c9771cae19191cf6a6f1840acec1deff605626d0a0d79ea8fe0af63ea75e80f8141fa8d7ca6f4c99dc7e78aeacc67762ed0134f1a0b053debfb9ccb145800b9818c2deb46f7124e8655f37c3291af107ed75384afcedb44518ca14cdea341c9657ec638531011cb957ed6b3434b736ae8c8199684cc58862638c5f6c07e1cbe8ae68c5582b1697ca9dbdd01e97023138a9173d6b1294cd99514a28102e6912b1c87ef22cdc611133bcc111e95c355a26b20a3d6f0ead66e932c5e1229b0fc17a7d6f78134c69beb362ca75017b1bf1105ac8970fad48acb8313cb3ff10e9d72c4ff11f95c2dab59575525c98653a9c7d31585a3742267c062d6ffc7a4303a3e81a45bf39e1ce2097623bba70f216aa612c64ba06ed6d596ad6abbdde69d56ab45e25eLine truncated
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"95a17355dfa9d378a18ba20e58aa4b8711ea1d6e3c65e0b2d3c6382892c7d02768437d47ed50bf8edc619c340be7bb1cd1d88b0d3d6bbf1031f738c4be09eb264c686d39b92cc7958e63c9994a84b61b5c412999ace8a9dee0e2a29eeb8dc537f63271af5f3844ed9c0d86e6913c02ed7d2b862a132f08f311aa92fc3757342d89a5dce8dd20d5792d5c60be9862ab168d3140a061489472f2266f297da357064833ef2554c49f8120ff40b961ebcfee1d0f8e7e5722f049485f72c502c9cc4afdbb70517f0fd2a00e12596ffe285d1b37eb998e0e89d756e9491ceb13e83610a3a66122b533c2c3461b3244438f5f7a7af8088881dfdf6a29fb563ce38c4c8632ada8e7e06baa2686dc6aca6bc944e5c14d6e432c4dad554803912b8fddb1c18a59a86bc452914b2efc1599c5597f87a6edcad33a7728827bbaad0a975ecc22b7748d7cc71ec7f51adc8fe0350e67dcfb31af35a8d7b72391642e29c2fa4b796ed8f535f6bc2b1198baf1cec858aac38959f83130af55c21383ebd57d364eeb0e442104004c1599060667ce5e1191e76a89199a386e5c4bf147206e7d6e598bb27a90b3c6a54cccacb39a0ac42bf22eb40bc8ec7925376a6c57d8eac6317578ac052b72ab773f572ad961ee05531cb95ee5a6d70add4176351960fb4bd673f7db9f698616a8dd41823f2f87924c40f131e6c83bc40ab1f92312f46ee86765c306cf4a1d77275ef9668d80f9d9c1ea0aa7b2456bbcf764e009584ef1c0b4b4c683fee3fa2641f48ccf7485a8356fb3dd22f848deefadbef8050de9c5c19e8c449c6f3ec2b1324f80a7d428dc44dbb966d40244c3af03bcb410a57ad1430615e07553a22686f1a62dc6cf090aaac3707ec5b44274b7fe28c7a3a298e7a8adc71e016944875bebb421babd2b64809be3454f25b90723e2cec68467ad2d14744b15de8f9c397a505a340e85998e207cd46fa18d76c46f458af4ac3821c0ac6cd68afb72c376c31daad1a2435fc2bf333260c1a82430edaf2499e7455a93b1301eada2e12365ffcd36a1119664d0c996318a3e55bb2c04dfc5eb251f7fd64f9d83f27ea6577d748e1f85248355ed19867857dc3383e01249cc37684b0eb8e891aa663801e4ac8f0331b38686a19f0d19f6e94c7ac95ec395962be0a4e3c8358d2f6d8f13191e164ad29cd1733bde8c31c7d8ab90366e26cc9a06707dcfa60bfe139a112db827778ac348fdfe26892fed61db7e9849a464e3aad561797b6c778e0688bbbeaf3349727b4670a2d0a08f317b0dc9c4b12ea85c0309d57e754d0c7bd5c83985fb82f776c968189908a8ca83b5944767c2efc3c5f898436de54fe8bb17224012a437896d9fa106a749d12aff657266276129ec5ac12fc7a77eb06296d2a2a876d931e479d3ea201cbb4b1b20bd81471eaa33786c624013e1f07577c2171f38f0511c6924078a40c2d55ce392dd2ab0885e29f4c06907a1597c181b933853838970edad7777ed394c491cde27478eafa5b7a36520aa0779261f94b957e83ce058298dcfa07b08ecc425caeb6c599a11103d7631e77daa0d9d3fc6f42703d57f2c624ecddd56b9a27b848de7dd28f8ed656f1e4decc95a8908217e2f2453ae50b5fc1d9352d735ce5bc2b538eaae25501d449d090df793151811443c64f28d19eeaaac4081e10edca4c4148e723ade8f7e7b988b732ba08b3ce4c8a0d655bac4ff66048148135decd7727a49ac59d82ad470b5479c55d3d8399b790ff033d3ef99d770e1eacecdc140480aeca1e2167553cbbdef2090c7592b40681b733b0a0d127beefd49bcbe8904c975a5ab8b1afe56d7ed7667b5cf92f537ad6972b876843364817c20400524097ac9b405e4b35bbba0d12355a0b54bd763b4491b2acd4e8e4fcaaf8fcfd398499d4c4e81ffa93ca07a5ff51a1540f178f43a931e07e1ad56ab5ce57a2f7dc3ccca114dc9ba8a6934e95f4efe9f3f76947909b280ea5fd795bbbc0feb3ad2b704e305cd9d8f37d178961f77355eedc9d7f77c58e1db2f7797eb8682255939293c3ef7dacd2eab46c4cbbdf929aac301a13f59831a88fab173803399d96dc216abb9f079e79bbfab667ca590266891c8a7ea4bc1724573e5c5a67e9f1341b5bffaa538e240f78da7733237999ac86141b2ac0324f17609b71c885630c90befc3b027a5f01e33979165ce2a00968c414838446c2aba76e1d7fe3707c742f68af21d30e23b637accc848f6c8df820a27bb4e94e5090ac6e008fde7cf3fdd5931fa891335ec8d01b5d6f77db57a87dc35d6701adf7ae0bf82dda6511c83ab4d7d3460b221eeb3d6c4aa537924db5559b1c6739040534fc330f5144c78bf99f5f4faa715e85aebac043e2529197a82ca40f65a8149a9447a9e58c61618600b0c5ab221420c0cee114a133a648dbc2eceb2894ffc329376d1eb3ce7039cf30ff6a53038b23c26c38739fdebc7b919956ca2e468d577dea6621a8d66b78075ad26a6e6d8e20c9b694698540d516ea2bd108625e5fd038b5f1e19c5d5993b82bfe16897c375322dbbca81c81cef6ad900f0ffe5ed02714c208a12f5234d78e32ee07af155ad1e1077a0d8938f426d8f326c751f6ee66c8f707e8493cbfc76f9ddf1ea329e094315a91ba9385e16c890823db0f0231c7f939a042665009d5edd8e48102c515341fa6eea33cc00fb5d82380d735b29f2eec3f61428f7b186d43fcee46b2037ad1aa6974d729848cf1a80dc8ddb0580c9c876def06d8f7642cf45263a655ee77f047fcd76171546319622bf71283f3bf0b519e123a85765779c8bb201e99981ed184e642f63aa61f9cc206bf45fa6e514bfc637671d9cdfba2891bb112a3cff438a6372ee0dd3e7d9f352ce52f8b367b7799e1f963bfe50638f0c74b94873fcd3d66fc1e342a8bd36fb8b88f33eefabb78eca4dc9c89e2c57aaa010f2140dc5ea7c86cebe2f8bf42a167d1d546cc80bfa9258c35af6efb1a090c293a4cf588e4bdf5c090ee7fe38fd7b5551e71e5ce2b0b5a50bab95bc4c257edfc94d37579816b4a2249ba05c991bb2ea02d047e480fc8a8ba71f48f344c6d20d140a64ac20184e45b4eea14d0953370c237ef0a47a7a2f22997715dd3ee8ea52f24ffe12674d571b3bf968454ca051701e411499bc43bb55bbd033f9b81d4baa6c49bdd49614efd20d58175af868ca16a9deaf65216abbdc3beed5f30b209e786a5b4c006f3bd27d93e9d78b51a1a2fb7f5160a0bc1b7df70952ea1573888ddde3d9dd5314b0d0a899a733eb48d5e6c7274667e362e4da6b37c480aa4d0d8730e66483fb1453a3aefad69942ac7f09d3c571b6275590938c541336a121bdd20722550236a9a5e4a37c7de628fceffbc260b1e9b6417c4295907937b13609b8585ebb8f076073abdcf19104ed80ffafe1b09997f115d987a552be5689c70fe125ca702d2ae4d807d5690bc2e90b72cabb0b61ad203b34c68df21c16b92bf8def5680b204ce327214c32e4363d5600f96162a6819dda472acc6441858f39Line truncated
|
||||||
|
"5e24f34a8d53b17bd0c2aee5369e3276dbd7e7c2ea0990f1300fbbb00831b76655aab1e2fd625ecd",
|
||||||
|
"c1d796f1e651a1ee825855d80206baff6818cc8c247ee6ce62b7531e6e9ac32f",
|
||||||
|
"240cb25aaae4d085bbb747a5",
|
||||||
|
"319e968ad291ea5d4a057c38f7afa4ddb9c9565962fa1a7b231e397a268ad8e0c5030a2df09dc4f99402ddf2e0d06e753bf55e1b318b3e5ff0108de2328d3b8d53e23e08bf7d84d59fededd60d47bbb52736b0491f82c616eb5f779c496abd6499555035e4513c8613e7204e6bff8d06dfecd9ce38c6b83efd8d0e41f84f7cfc9ae07113237987a4b2eaa87f7e0a310155e282e57858244e9071712fa026cb781e5a4bfe6fa1bc480e534096394459a3d1354e2d9a54aac6926a60b388410fd0b53f7a3a9116292f37406369c22ea674418c4deeead171e00f74f5cabae5d24a0686a4bcd8ba99aea613a23edd0a019a319daa3779c212fbdca9d772fc3fe612cf178c2aca2aeaf6bce2433494027a474eff699bba95fc7dcf79ca1d77b1e097439a9050a5cc78e0b78bf2e7f50f959ea2986a59be3880519cd84d0a673acb0432feb1945c603e70748445c74600ccfec60efcf9e4d02a7df5f967de4b473f63b0b0499ff4ba350ec1182f3a0ac17ef9ae28945fc9bc714c49909a7c1e2f311aa6ad7652e22e1f48bb51cf53814a2125152813752d86c7f9468a991d0ac84b1a2f3969b8081c228b7f5760718036e26a10e211ff04ea323acdaaddf9b06a08c92ed663d0fdf13fa601cda45c416c2d3803dd9b5ca29cba57e59cf4ad93176c65c64507b1995d638541c90b381ff758833a2ad67b0de44c280fdfd82b3c6d4353ae30b33768863cd3169a2032f26e37ddd57e7da1673cfc7375bf6e6792495a2b434155d684f2a6f2b919f944469d47be5aa7da74eed69d871e6f65c3ae08904a9ad042ba39905188f0b9158fd14094bd6a408fba6ef57566d69eccda86bb54cd3ca7381f51bffeaf8bcc1ae8df91d22c359888e21b70f640d6f3726a34e6100ee269124747f0ca05110f63deee07e3628bd6aacf926036ccec02c0b6bd7259db52ea8b7a686b36ba1d0296c85e43e25d72ce46c66a1e646301dafd2f4c502281e6f949011cea69459c026c65bd130d6ef06be17b23a9c9a84746e39d017b144135025ac527c1e653f233770cd68e9f232c3b623ceda836843b3e9ea313cc6a57d28ce71ccfb7265ce73b06bce1447220645e6f66caeb06b55129b97c8dd8db54c94d771504d24cedc86a8ec706a9f7dcbbcd7fc7cf38005b2913b1cfb77370bd23183ac7b5ca5135a2738cc91d05b2b22640469e3daeb6a7b0f14fc6652563663520f7754aba624a35e5d24529a6ee9f5ef0d019d83c04f5a93a38b68cbce0cecd42a11aae305475806326aebb4f673791f50c9f90894add51a0fd7c02807efd8c1bd21fa717a860e224bc9fa3f40975fd8d558e4844a09f8920256528450d77e546604e2ce2d38efadaf39a0ea3ea12156174aa8a20481e6c1190e448564675f9ca60bcef37cacec5aa218122e7bd25b571ff10f54979d62018b779a2a3d5d7d6cd56ae31efef2c844ba50ff9da88eba7a8e0d9fc5388a805ba4ad35eaa4798e395d2fe112083cce2f11cc850d25ca5c6e60a9996cee4789ca99d519daedb62f4fb1e535b742a35d71d7390117e93821ff18948a78c1fcdcb90a5f1211327d7ee0663ef16ff446e0e22d8cb7b2d3d05469b1c02864f4a87e2d9715f60c9e7be841e308d0a5f6c50161a4a0464aebafb88e0d2df8cefcead93c9623106d5518a9852f320235594be10c45bc0cf06c9daa007100ff97959357f9be8e49c870d0a11c884213e266c35e9131439fb3654fd5f1abd1e778ccb02b8c262753a22653a09272a0c33b6b2683c9045e8f967af756b98dc1797ff605c64ac5bda8252e9ebfe0e4d8d7ca754fcca5e3de3c4b63678da095281d76d60fa12ff4ca818825f346b9c4e426cee16db5818d78a527a901cd088bc2983f9b83430b50683018996996717a1738439680b68e3f61cbdcd0f0e1a6b436af8fa05d3ce2228054e319bad1dc6ac970c75313c552fc1136fabc302fcd1d09ef1b9138d18133a772cbd9cb197ff58c6e898f9e83e4e27206f3b15b6bf2778aaf9fb38e0d50152f8dbf5763816132a04b4b2e9639584b3dc8ea6d95ade024f9497944200ab0aeab206ef099859b9240aaa15f737c1e0fe6d015d04f47261ade4928e3c2ca21d1f5ab4a3f571f2ed92ebeeebf2493e6e39f0063ba931e165384ee1b5081f5f8d26ec24716757037f5158d35effbe67009080ad7b0381292a513f312eb28328cf5ff47a6599e36c14277c3eb5053c5aca530ff5954c21c03fb3fd5fc0facdac36dd819b0495fde421411e0440991da0cc4a20d294446115c0b79045037fbfacfeac574da3bf192fec4bf38c27cef71d03787430223b6069ba6d9273ec8679736a832277c657862ca791b559a5054ee8c7c07618083f75480c8aa01cb086c7317315911802e6cefb15bbe20494b14d97e3a885806db775c216dc15949e3b724f7cbb30bd2c46bd5a2fd6132352c2b21cc2b47891dd9794975f70a6fa7a0791ee761ccf4c263f27f64790826c1aa656c39483e029baef0855935e7e6c133a4035a3699925fbde131ca62948879373346af35bd7fa52b8d6c3338f213bbd9c79977c0d710028d1d386df614c5faf4a1f8fe5506a9af7059370893ff6d07d91383baba67a617b5d829e0e2eb20e541ed5c34be7ef0eaf6c6f6f52d7ca01933a2a4e8de46e422dc95161ba8ad354f6bc7c8e4cf8ab5e08607530147fcd7c9481afc621c5a3230a05e2c4db79db9e1e73f43556a8e8f0dff7ffe420282212f23d4c5f6f8d2febe129b9fe5ba7ddf27f72ae898a4eba270b5d2bb3b6b06e38c546ba80a9b2bc46097d0b47db5ae72485ef2c6419e856c33c2d66a861b9d474699e730eb8a8992e3ea9c1ed74316687d5d9fc611189eba2aa31af5ba8e81179866dc016bda977c59c595e40001c8ab3a4a44cec00ff84c6dbd9ad4be30bcc080e69b9398089d6ea464a70f536ace3b447693301c94850606d0de1299770b5f45e6d28f8ab83e3ffe52178522eb91fdaa9e4a696674ba0f52ee18e960b04415782f018d67479081b1bf9b4c9b90de026cbb66bf7d9d12cddccdd9b2c8ee2f010892571c6f0c0feac9555c71bf61f9cd69553cf7fc2be8d058e0c3430e134adb1ba28985fdc4f0cf71bd3cd09f5f82f303cded0de62f98404477bdd0a846c6c51e3e82ebf72f475afc8e6388aec57206018ba2528ede194345cc1ee95cb2023793f692f708aac3c9e8a682af36b078f5d6c7a3ed07475e9fe73b95d1eee048ab898edfee3fac4beda45f03eeb64b2128f6df9453ed77c6010e13c0270c068f704f49e62fb7410be90ffee47584ca2efc5287dae1f63bcc1819e7548eb9f0d8a3182f9ed00da3817255a2ff735876b75cd21cb25e86aa4b2893f9e5089dfac76194563f9a14335dd37ef06a501c89623caaf6feb4afb792092dfed515ba7518e278c341834a9dd17b50a0fc860b62ec621b69408cb3fbf7d4ab88a3e367fda84c82357376fa9b1161b739361c313b99dcbf4122f3870c8175093298cf432174217398928983ab6cea4759f18e7a2Line truncated
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"2158abc2472e1b9c061da2c01d0ad9e996fd687cccca331fe8a2baacd12c06f284b1b5cbdfd067e5ed09a60a137ff4a97c5c26482659680ffb22bbcd4ec1bfd272749e52440537320fdd3c225c30ccd98cf221b34b89c247ab7d14f93ed3ccb0486a028c6f3abe7e17fba1742b6d4db85f6e6baaf82df1a3aa059de8d9699821d39bad42d56cc1ec67626092cfad4a2e1cb5d814e2cab78ccf5474a8bd0dc990a877d37de394694af6cadcc57727f393dccba7bf955f4b65b3c00d71cdd701754ed4f231685b7b5e2557239d7e16305be2d81a773765dcea25ea5bf2c15d670f3159409ab5bbf8da121c779132a8ec1480068cb76b68a19152fd83135aeb228b446225f91d1ed4303a4bc16cf3ad8173b30d2a1e75ccafc8c933db231efeae6260d45c7ef230ae2c7b6f986f1c19e2cf260ded9cd99d64a2d03fc5ee3d73509e47ac1c39dcca655839fec75517a9243eb611da8fae3e317e7df66cbb6abd59b16975eb463f509e784e65cd660ef1a4c5027e54b1bc862f397c9cf4e6594d98c2c2830801d3a679220b46881a372cdf3aaa33eb66b91a9f36b6941c0fe1b4d2a437daa50b811f2d8c65b5a69de185d78bb9c2f172dc90a89324c5a2067974aab14f4fbcd06ee95cd49e03717f88480a410afbb4e68b5c79b0211cb69b90604cdfaf08af1ef10cf28f0f630e97ab18d9b5138d9b9ee9154e0b3104a6c164f2a114fa5032eb5c247a6b87880332a0dce7b36982515297a05dc8a4038a09f52b1def7b4fdad8735443fadc462c7c22132f8b9581de2d213bf5c53f7fce34aaeb24263afefead5341a72f88d3acaae6db367c5c14a97d4f9e438e1e11c3c8fde7ee37e5ece5382e8c68b660146046ef96c24caa6bc9fa0a0c88281e4bf01b32df5218cb3750f9c4b8af24cc106abca62d085198d14ba2ded3cafc1fbb17519a696965a1ba5f65720e893f1ef3fbc5200316b9d4615bb23426ae53e1c5a57b2f0ee0d0c83f353b4ebe7a6cb17531d278478b4ca8e6ffdd0cad30ed73d568a2e44972ac88a7e7d665614316d674e84ebc739b645a9a4166477254ba47bc5c2b05ced88e75bf64da21a7f1f71cd946d84de13ca77b7e0dc2f0617d371ed96323a83bb11dfa16f81bbde913d9c259b10f3aeeb6b56cc4775c25f49343cef667763118932c2e8b47ec745ac537b37746ed65fda2d1c11a2de60ec02adcb79152e8a9e614d8715cc4e6b6891d6a0063576560fa3621146308222432ffdbc351c36c37d844a934088fea92ac54920facf870a62e91ba9299dcb6cbdb918e2d54fb642c3f0d60489c4bda489f6c584b64c8f19359ab25f388dbbe636c4d90c048f5ed87024dcf9f98a9e738163f837a07750d61203254a80d120c795f9c3aa791272f9474fe330da81a45be5ac838613d46c25e781606862912ff88af393040605fd4d55d07e2052227c37ceffcdd2d42a08bbab69140dfa4406853799893daf768af546f915a91b81d0da719ebd45b8b5f1641f15621959689e810217bea18e3996c532ac6e4e2e4f289fddd5e5968bd6fa9aec5ca435c532b6c74a7568c8aeff9dd19bfc2fba3b484a191e2faf9a069a24e2e6d928ac0bdf635644cc1ef3bbacc547a8e4f1d42d4bed3b6b8cc56216fa550dc37da9cf4d1d1591d9348594d14adc7a3fde5e5d1a3b9875c85de7df483cdd0baa86dae793e0796d14fef1f649de6079acbec6b6fa5f2cb2bd0481f5316f00dbe5dbc379bc3cd6d13bd8c775a727ef43e6a5fad1051783b22c05a75d64a8394a73fcb430299b015563c8cb0ae0aa4ec750399855411c076d21aeca8656f3d0cae084fb0a1ffc6f73b52a7ea5d4bd6d24e7057a3811719533105fc967439a32241f2d3e3f299da2deb821748cdee1a1c5e71bfdf88d833bade2f505268f375a9e6488cd8e16705cce91d15b60b2fd269a19148296a7be348aa349a12270fbc0d5748e538afeb0598081a4f1349217ceab3c4141d40f765ea2bfffd530fb9606601469fb131a44939be984c07bac8f26d8c068accfdefb729eeb47cfd6ddc646e22031f53a7698c6501d86cbba05e282d64b2f962a1b08b9064078dd1e3f14006f45f599bc8e600cabe6d855fcbae8c3060859202361d929a241f6c0711ac0d050b67a1d44da19e0b0e236adad1f60a327c9c34b2b9c64cdde5b8e4f664f2fc70599d44a63ee2b14d051c27d71231098ecd3d4086038d63e84547dfaa39db1a92785e38b640ea0345062a1c185b25a72862e7ae6574114eba592d6492087e2580dc5d361c473a614d647e66c0a30de806f4976b69a8b92301e68794ee05b96ee116a5fd5edf5eab43dc1103801eec861383f17c2bab9f2d9126c1802b7aee0c909309ee72679ab644abb9c4caa54add283b5954e6f881781e42f849bce6554c7a5e3becc5d5a209805ccd4a0117272a53807e3978ffb19641a9dffd9034490a9284f658599961daf52f24f6464c2099cc9ed3459d84dbde2ebbdbbeef25c882a9beda03573bdd4c6a0143b14d634a1a021d5f9fa23a7ed0f5598ee57e56672814412b6c7c08b8e709fb98575fe2716100d000a20a7e7200d800e556564c7e6a8da9d609b18ff0bb8a8812e96b834a6b534b0d5dc97f5da17f42f8d58e763f1b201625d1a5158c2f9e9e190921637474ae81d278002f197f7211540088931ca8a941794e56067ef4a497fdc6fa713aa9f20c21f23c3a71ae4cc5aed459ca7c020bf55162fbcf56a066546660c5a009b8ad2aaae9651c97b1e145853a10013d1bf68e7df25dd492c328f823ed982da54557502ebc6cc56d4d0bf2881bf3c536ea53b4dcb0886e73b066969dfec343441b9372d7ff38454c4337d45e2b999415ec48f19cd05f0f80c5a61ec369610784f47a5cf3b2a13ff5d8145303ade7189a300936006846812dec9ff15500f8daf47236e724d72619af3a6cb3e854cb8284d5b8843dfe056beaa45c40a4541a98c7507feb27a605d6e07189c8c5554a492a03ce6701d3d2ec782e2c1c8346b54a963435bdda3a93bbac1d837172cebb9cd18903d25cd6bed404eaf18730a6d1c6da0783b5411770ed34f35fa6c11a4292a34565ff1b23d4200ec5a73e6b7905458088fac19f6aafd35e0e791f28bbb2cb0117ca1c3a9e3c4863e487ce5d8c14dd140e9eb4794d87d75b01f683bca84ebdbf19dafab716421bfac9e95755fd346a0cd31e8520a55c7ca652ff63fb4e20ba67fab41e11f7390bc02363162097802c6a9eb18b430d07ea60064d5b546d15bb68cada79c113848136e797577f1783e9b53574f9427be3a28230fdd69d139205dd6c7e9e7f031fb6eab70d69ce905384c5c77d084360aac590a89b2dbb2d339899b13619b455cf9f0cdc08db6c5b5f3223dc3a663ce42bcc8cc6f947f42cdf8dde15a6926b753177513a52be95b1f0b88d2a1ec90e49959b108fe204bbc29199d7382c42ad5dbaff970cbd2dbeade54bd70415e54daa805d396361f525f38efc2bba3fd818f9d7af0594dcc341c20f18c624fe13ce7e7108e1Line truncated
|
||||||
|
"088fc7ba068f80efd8d4d62813c93c1eba77e9ff400c7781314abc901873ce200295da09245bf8fd2fce254397616151d94b511957c89a881256182ac9e64acb7b25d4a080cc9daf9ac2f231235483fc9fd415f69caf7eaf0597",
|
||||||
|
"78d5f86b071bbf8a185e5e2d54faddd2a9e26983b1e7a74be0f0b979b9f4af31",
|
||||||
|
"d9ce7d249af9496e99c93b36",
|
||||||
|
"ad542824b49fc520f0b7ff8ce2bff8b3d47baacb4a1c95ed56a306483aac551fffba48e8a8f5e4cc536e9266182f6811d070fb9282f5c542cefb4993ccc7044b42cfd6fc71793dc8dd2de23c630f9ceaeddba45efed9d7fca25fcb07d193c000822478b19c2ee9fb31760cfe01475ba8a003db469d1130318a79345a29d054a9f9412dca1edf6d8f1498af5bb6fdbbd3d5f9a244ff176f62742c53779291ef6294df6540d841f4ee8c7c58fc8497ba74d9cf7947add5373427d81ae928305b93dd26cfc65e63b0ed0812ce759511bfbb10aca98f2abdbc9055c4e5ab82637f6a965bb74f592bdf11118b8eb79d50331e76cb4d10c6b4428cd4ec2ef4cb727bdba2b5375f5184d77772d0f9fd3a3c579a4a548b9c2dadc22c805ae959617af49a514b43f47af834313ed2e4d1fcec2c4b9ea87f328fa3d23129a36e6c54bcd08f7e30645de86e98ebb11bcaf99543503eb1e024bc9fd51fe6bd5e6d749033f2452cdf28b3d0f8a304111bdd26dbde641c02fcb15dc21b1a9baac5e86d35b4126ed1cc8a2c3c2a5b94c99fb9b2008daf1a0c090633bf9e31326428c75a50e821b1e72a6504c9d7bcfcaabecd929163d365832e8971f5efebff99ee3f5b95f957e8904d05b410936d8a81c60b4947f8605c58e5b727d491995c76fbe06e556c8ab5cc661a0c09ebc98d61010050f68b31fbe1f9de8f6481b2704204b0164d8433ba4dc1076908c782826e9b555e8d608463581099a466f92bfd6ac9796eacc0ab771a3f11d03806b0f33ec04c69cef6b87d58c11acb5d1374450ce61ba159456b915043c5c17cb03f0ba66d027105bb6fff41e6422f13e2a466f073358bf68149a3b577cfba7ea08b42f83fbc5a2aff17c5ee7dbdac3ff97389f5b8d1f3750e5c9be651209eeb9574127ea81bd7619da16d1cfab85754883543f6474c8c0cc9d5b80e34bf8262d2b4798f9917bcab4b880339397907a5bafe7d149247fd735523df3cbb17ae5e298846ad3bfb7d4f902aa549b7667d3ea945b002e7b209bc83842a7b120d6d27ce80631404371f31d1f61efc5423e1822032a1cbf4fa1a6b6fe79934a202d5add8c6e3595e49be3dd9553a569521c50e9653bc684ef2b73c3526ff7a0843fcac9cc9ecf46e63df5b9328a54c576bd299a366bbdc0f83a9de67b03f1da16244bd6d52e7e4b52c4ed693827735554b05b3a260cd01a41d7c944d0b7b58ae4b0eb052da34bc22b779d7ad46f90f3d4049c097e0adeaf71bbb30ed24b32ff5c7a65177db77492c2571e9cd99f15e613797e319ea7377038d53b28a4cd66a697e5e8f84cf16bd0f0430b34826114b4e1d1ebaaf2939dff7f9f4ce7c0861e51701c42d9cc9e871018b447ccaf4e402e3d63be164dcdf6799314a389ada8bf5e51a35148acf627e51481b9b0e4bec09c9e6d59229721b151fa9adf8323001fcf33afbc9a949643172f39b0d10ef57b37973683fdd9b9eb46e63054fd05ffbef889ff8fc8f251b0ab41fb00757ec1964ef373fceb8f6d148a7f7c89944b3cfc240d091601b23046188ba70a7cdf7b6f96eb93dcd3d24d4aebdc4a29a749bfe3cf5f6e1a025b62982ce188e6b57245d829c9fc1dcaaa5309a8b9557b8824a78eceef6e977721de4065b474ae008642b974001a5565ef5fe4250194e8b861cc45a8691c461817f10b646fb526bf0fe7790bb0db29d1356e8c7a197ec78df8310431d632a032b5490c2a458eb8d4327a9679d7e8ef8739797b0e820e2c567ce3562592e862a1dfcecd50bf77fcfcd00518db65ee0effb9eb3655d5d401a4a47808faa596d17b316f828cbbc14a7e018a0593da9320140a752f3824b5fcb66aa4c3cb94366ee8b821b09e7bea2c04ece15e8a7be1f58463b525e8cfcfc3fdd395ec5b0575094313557e632d0a65e3099e3c653111a5fb4f0eb2aa710229fc055a2bfd8a7147cbecc10823f1244fbb6894af1408ff9047d6483ef83573b5421b9798ee387dc38f166b11de6c33e9785e9b3d9d28bc24c37890e4f8f8ff24cca298b44d6fb1c6aad28cc634a67dd427205285521a172c2a4884ac5b038e261e38faf0086a02aa29195713cea335c47d03d67fa0dec7a8cb21db741519f5f0ba0143f14d71e33d82c75d6a19b3f7a42e6c16d762354daa2670ffa55bd400637de9cddf9e7964a03b4c8956f36bf54d89cf16de23e8c52957b52eb4572a11d1398be72bdb129e2c1abb58c65cc291bb7b0d2dc326c6125a441863a6c92de0f47a355222d58bf10af0d297a86a98b4e933a8f844fc7f1bbc8ba77919dfc50c41219e3db309b92ba056349faa758daf360b8ac05e43fc2069cd46e63fec399cd7764b111467fc65407ac06f5f84a3179930f6215ac5ec906146c19e0d3e162e77a2bca3582128284282b251cdcac03ecc204266ac3a9cfe8d8854008baf89c0ea0096a400d6a0d2f7c681c99462cf0105f7a3dde690ece0438fbb820b9c73c6cdf6208c336831101b904526cf8ac331d879d71615d8b1f750ac7f0ec692d97a5e21e17e194a98c10172b5c4bc1049a8743188ae7c4d70384a7e68c1353aab7882bb91aa383821046ed0ebabb4b2dd126ccb935f48646b299095cdb71ecd5cc402e4635a3f7a3c8a6f54f4076ba028dedb402bcc92f5668dec3d91dda7319f58382017e306237e42480ee2c1f5930564cf16fdf37a3434585336b8e4535bba87311cd47722b9da727250560624a5dde48a2090ee44592d2fc06edda634b600fad9f843c6b2eaa0697b42858afee8191dd2a31e5685bd104188e2ccb057dd0a8d4d1205d7c846f5b8ec0f06bff61c7f47ac4da30e1bc80a4e95af79b14a83e9af2e0f195cb92d14f752a5f12ff90a05765be453075d799694848fcddb07859336ec101c8052bdc273d4abc313cfb351b543fa340dcd01bf32fea59881ddb8f33c6023ccea70532814ce4a2d0c66c846347b86c29dfc34f6fa4db298911d4367c59939020a3d078194e6a3a3c5126c24ed182398468e77fd61a5b1271f5cb2a97868876954c3f7179d6a045f4bd770f681cd82216cd2b1ceeb4e724b3fddeb74481e662fbd7f5dd45bed6d4f89d21b8dd9c1009ad2b0b16954e97993ab8f3fdd9d61f8db102a945591b4552f419971a9e46a792dd8392c8d9502767c82d9b4f69e66071eb579859e9ca070cad5fe3b7fcb77b8474926ea991ce7ad201421f8a79c051b762a066027ab2b9595a1c97ad57f3149f5872ed4d8e99195d47bd3c03bbee590a50a99d8048e912aaeed797977b52f0240a6cf2c865b108456881adbfda60cf701454da17bae879cf098df808f34e50bccaada2d3edeb1aa73cfe3c512d814eb33897b6ff9d67d3d682517cc333c3c2552adc99860b1f0d1076390de9f84fcc9e802581f77e14f5254da01831c70cb8581630dadb44209377d90447a1a21cc8a2d6d897db62d8420afbcc6ed85ce42f3281255bd43e0afd3e86b27d3b957104ef54959282b0e1b381a26f16057246704c7888126055af5a1f494540f01897e8781e1a5c01Line truncated
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"9cd1c25b5bdab9b9080db3e5e05dc749e0783087c310777d89307138613bdffe0ca259677c13208420d4690031314a11a97a986d8b0fea143f5b4da0972c9ea3cef80b4b0b2bcf2bff392c306a764113f0d9807be86a9027c6ddc85d096600d85e0b236937f295362bc1679537a8a9278229a36a9433925a105ab719c0b7f11fc31488fa071d3032de97c81540713dc29ae02c2e13be8823183f3cd9f72ef8ba4280b4499ee47c7c7c4492bcb5cf7e4fafaa7ec26906e58146215a3d4f52f792d3abdb718f57ed0b9b7fc7504e45a0fdf01ebf5924a4da6ac635a715879ea75a4983cbd9dab9e47638acc687f16684e184443aa9e81513ae4abbc4d1596b2ca3eef77cc9b0603fe90c0570fe6cf4dff0381a99212fadcf7968934ac1ff7664ed6ee0b61e41f5074dfb774b676c2b57a445f1c5749e95ed062837c727ae2c151c0ccb3a4dc1429bbcb9e62325117aca566b8fca0924b70f4defd7749d0389b90f55f35d1635f8d2efdef514f06fde46db6e11e492c8f4dfb7cb5454cedd0ddd32013a4836321a25110f3a017f18475a86583e192132f8d8fd4c2dcb2a3aa95c3be3a57216bf9727cfd1284eea6fa870c8e689e91982c116ceeee2f8298b55646efad684b96eab883fd3d629437e9a0b6523f47ea5b59474a4766ccd01c13170bb08f47576a0fdb573d4dfb65279c1b79cb535426bcab60f4022dc42e40db29f15a6148b461241bae62070389932f035e7257752ef2d6130503d72344b24d360cae8ec11fa2dcbe04d3b18e66d081b552e93a71dc0094d1046bf4491e318f2ae00debffa0b8ada58c5f23e33fb598829ec2f46ad3894bd7f530210371a02e51ae0a414eb2eee43f3e08126dbdbae04c7de4b7416df32953234a6694ea84e6889f27c74206ab8144a393a2614e92adcc77550dd54827387b619f004c13f6c4a31e8bf525277669db0a0c3c589eda15063f12eb774a13e2aba2f2f7b6e9bc69f8485f1d6fc5773acf83671812412d28704003e78a17da25bacd1d61a6d9cb9f121abc71d023bcafa713b7c954e4e1c524e5bcaefd86c4a843e209eabbd579cde0263fc059ec6ff10017ba54fc9c2a1171d6b06f5d85079167117c12e6e5d0c71c008765fce756fd0f1141fbad6c1d2f32cd8e80429611a9a78dbc8e738d458f9ddce58ab43c77b34db9befb25cc1a588998e8dc2efa75c6883244fbbf9a7b4d6750c81b8d3fdedaf98dc61f49d067c369409f984b155ec347a3bef73e2a44957b0ca0f84c7fc335fd89453759ad0ac2fd9a5b38afa9fbe74daaee7bc52301302fb2286c21fb922f74d756de84519171fbecaa9b869682d431614ff6845126a4034f10253aa244bf89ab8e0dfd1f7fe8fc1a8472a10746d26896c8ece7ef80eb2e910069435518ccf096caeda63ad692455b04e6525bb8bae27197ca5118a57fb9a5d8fcfae1b9eb7874d91eafafa0e4fab5cb4d0173f7e3e58fae369843a641e98f3ee460e8cfe95d98f7fd38a8d2235e9d6050015833e6d7d21d7015c3b1ff42f0d3a3d9a38d373c8524752e06987c9408cca550f08c38c2a9a8d86d5ac7a04bab44254ed15c7b5670e0747788e11b81adb0d29e3d0b50d6a429340ee0d44a8c286fcaf9bc46403d26b4a4af95b021336103c1ae0f1274b33bb8b21c8cfca8a56c639f18a9df45d083fa7019aaa14d1ba50eb9a4112e574cd70969640602096265a87b1f77c0e00bbb501555f1626196611b4a824991cf10ab2874a12a8e0390267eaf9e3f8f99eadfbf40d111a26772cda1f50743c417eeec9c80171a83a730f246cf31c6691c96185d672a0fde9ccd7091c4b455dc93326913497396e0a4992773caeddcd783e534eb0f34b99bf23a2db6ee738381b5fc94ff603be014c507888ff55557793a8c5439b11dc5a347f35a2666eda81cda4d1c3a78fc4f3df3c7bde91d05524791b67142c446f60c3a4022912ddabdf817ca3280b671beaa496c935661e5adf39c1f4650563c5c807c8f21aa59df926199c4e2404690ea8ffd7dd65f637452ff93995fe9c5ac7a322b9bdc756b7ed6f533b9357a4a1ffa379dd096f144e9e0d87330c238ed3c6b08c8478e23b65518ea1e4e64585e5e9fec2f26dd7400ce4c73ff0eacdc3b07e4f34f6316f5b82fefc66e442ecc92bea8c1d58635d644724a3380e71fbbeef4bf3e57c6240ff603d65447f510eaa3c9ac794fd24f844489b7c560c7814fbc307e03f6a213eca5ea40fddf51d8731b74ec5b472bdf8ba59751065ed2461b02c41ef96622e60c0d26f9dc78c24f94372bef7e47cf09ed565ae3a52d39b02ffddf1953f1ff500f1659db9f1c2b23534702c19ec1cb7c18166fcd33997d53874c7cdb4e6c2b4d82751911913434e48b37a61a0971861187e5decb7f5c1ef6988bc1d6f7fd147a623d8bf361b0d7ece88df6e1ff8d037762d232e22e51d8c6ddaa9dc597b23ff9efbbfd416cc53e5543253732a23aba151cecf73b3ecff21c6a9fd1f24211fc21cde9633aae918ff1c6b72468f1de7e0ecb6539fa353c069fcbe8920dfa8e2fb86782e3062462f7eb2a2c441bfac21ab62744b05c70b6fc3c9f8e3a8a0c5a4263ed256a019861ecb28e20ce78e2d93f1a1def669e9652cb35d105bfdd5ff2313d27ab3eb00d1b628b4c20f42efa23390802af96a8f261ded3678ea0b780e1f4a88d23588a4ebb058adbf9a9c62ce2ce2f8264c874c697482e25f8d5a6daca4f57fd97d23c42d7b71ec150d4ee33931db5f7d63abe7d72dc936bb23a367c798e6a01509644284d52f9ae27d7d1bae597b2cbc26139354dcca0fff6d76c6065d661b66ca5eeb9f8d85810a029cb95b17e5173ef8ab92d475a1d3e21799e874ff04dbc962c668ef4be9f94d85b2a99d97c0db8f6b6d63e00e36c325cfab9aceaf7597113bff0086e8fad36eac7c0b443de6d3a8533789616d4c863df7200ba795a3b8d0a2b9568bb32af95fa604a3e3ea778c3dae159e1b612458584564ffda07b8aba9710134242b2d83d23127b51b9e41584c56f667b71bc01060240f3a2bc7e5d438e7095c1236e0e468079a83a5dbdcf132d258e9ed18f94d3c098867d06d3c09544565677b454be34ce567f1c143e2f3153bdc0353d65090dfd8f7af4633b89a781e01f4634dd7b0323ea1f38184e697bfc39a1299eaa278c39a2709cde0a346fea53a61f211112450b318d137fe68f6c102085aedabd2b045fab912da5c58d8019239f3a44b18f4fe30c5352e2e2bf030334a1dde1dcd23178636f1e38ec9e42102d8c54df0b94b207e804eacab3edddf89fabda6c8e1bd4e17ae31a57716c679ee8bc7de4412fec3934c6f3e8b4c1d1447dbba0fbc775dd3258f789ca53f1593cadc710fef6fd282bb41c0468ede5ad5b914e4758b4148b0d0c04c75ff6208ca3e79d92de8abafa4ec70ea7a4e454f0759337ce575c4954584e2bb8444c34e823d27b025d25fc9becfb4391df9882452bca0373164cd76e9af316df3f5bb7532e22557b485217254d5ab72ce349620f037Line truncated
|
||||||
|
"c22add33457539a957d32dd07ec9110f8cdd2f00ab6ac256b4bc7732f63dd3b867b0ecac262555",
|
||||||
|
"e71f9a3dd457b4064df1d9055889f105af175a2d10dd7b8729da0d0116c2d9fd",
|
||||||
|
"7df9824e774c5f86d83cb5d8",
|
||||||
|
"689683c9e7aa9c48b9fda0cfffea0458ea0c3dedccd21efeb06126f1194780917c9f4f2f44b1daceec3f6b1f75506f4169bdacf12c1f65958784851056fe0b4b42a22aeb043ab35ca73747346ac58c550324c4b849a404c94b8860967b6fc58aff25dad0556f1952c045b91f56ec8eebf6f552c18b2a0641c037e6c6538b289601e1fd5a7bbe7b6e0b224124fec341bf77615183abafb52b3e30082a0abfc2cf224324338c132426011d9f800b382e6b834896ea48a8247f149d92ded7e69c7800096076cd2a729a1fe41c70dafb1f855ffa2ffc27b93e2f5f6827ade7118af60730033675d84de9cde6c260d3d615a945dfe0ed25f33b6cbd2c0e204ee919219d85c7536f4700f06fa61937f8dbbe9bda88db1f4ba8a8d195cd385eec62edd9ce673880800be9aa4430e5c10a5908f6dd349af70f32b32d8db38a7d73821af47b993b622bf168565082d07e88fc48231a440469adeca59263302438ece96d89de11cf8057454d1bfe8e4e36965a4d82618834a0847af39dd8776866d9558a5cff79a1cc9d1e3c22e050677e54ead68b3cf0094daa01330d41bb66708a8bbb8a196fae5c77dc6774629d38905e81d97c5b16d755182f687a8046e55d148419cf9c12139fee50c0533b0f04a805723ce1ea5595fca5b668e58f6b3b396f438308372489b640317cfa3a79392cf6d1afdd8c3359557a83790021a4eb418fa189ad15ba9be0f74182ac76076f102ec171117a3d16ca20b4d200e03e54f1f0ee6308e463a148c0c85aac3ccbe5781cf45b53a313f7c9975a45d1853ed9104a860c08634a8211b87500b5ffa3d8d9d56f22256d485b9b45b24d3873159adb8ae25966cc40f164f342519e88d1ead1e711e1b2bbd4be64c7e83f056f797c2d3a5cf7c5025f92be5637fa7738a1bbba55f761dcd1451ce4b1e85a6628b629a2f7917a86363b01516472c0f8614abe2ad1c9d5501b2a44a68e3eeeb34a64541125bf49138bcd15b7c82dfd40708414b85107d8b982c4f99783a03c707a37787a91a7198063f0e8a2d52dca61755105faaa09c063c7a0849570cba1aa7ddb3600eeba602c7e7c9b90ed00ec731d4d1d8e4bb42f9e9db21616c4aca48dc27b939428834404331288f03c2b5e887103c51748d0257519c3988f6492eb70cabbc2dd8a8a910d737a678d0970ec48bef3b81673bd10b687b37e11d49e7cf90c03c54826ecd833bfd9dbb8174274dd45b139d08371d5d248ee33298193194734c5863adf4bca92bc282bae2f47da5201fc240dd0710a22a8d922faf92c2071a7eede7ee17232d3b6ee5f3ebb1a8b230600b243c860968ab427a5f540912e5e7bfa0271201f288727f2bd5173539d5318e5c1c0a71cba4d9501b91c3bffa7bb61b3713f1751efe94a66e17d2b42da51d13c3df40f4db988dace42a6a1b9d138c4f590b7227990711afbf8f56fa63f2800cc019bbd4a7b3a0983c9b9e5f77562dcad6de96e3b2eb85cd99d28a021a10d6734400a91369236b48ed68528afc68f247d45c79318fc5d634ecb0f3ef8536d8ec2e877adc3308be906c5b96777d0e05970023e5c5dffed12310cc97249e4b95e32451c9acca8394fde699deda57e938bed7167e62e2cb62357f82fbe821ee73b4e09c6e2f512515412c2f27805762a8493e74a3d30bb409e499002a97354381318af28311ce484bdf7c39db53f08f73ca5793945e13fc8c66d503fa95506b37ce134ce2945d75b424ca6367ef4ed47b9cb8ba7de80e773279bf23ac888eb105385ea958b1b49b27c8db6b1e14a5c8ed5d28808a7d0b6bff1a58f24f9c57fd8b8f477a9d1365f89c698b8ba923896181299d474b93e05d3c915b10a69e61910761a6d8644933c593661b0828afeca590ca18e702322d9140d98fcf836c2f7a4f72b59eb529823a52ab05d919c3eee4db2cae1067213c5070450a160fd52fa44bc9bacc5c136701cd7adb1faf484da376477da08f6a4dcaa37af47c7b026c2da9d5fd0b30741357104cb2bc0d3cebd132b5fc7c873ebeceec5492aecab95ab393f35b93b923d2ca071e6bd8522c3ad8598a05e96646504f1620c045aa5734d665acbdda0ef73612be4ca4d95ba069041e042497f7b10445869989ce30f55206a1feb4e64890b7d1f7e9df2e88a352674a52ae4267c06592d425ed1d88101cf94588135892218ac11f3976ab2b47a27f02eb887696c94b13d48b4370eb11222274b5513a0fef905c66d0c1893832ffdb9b333178b65338fd8b81094d8f86f2e4e96a47e72032cd6fd47af87eec295c6e980f595b57f79abeb4654c4039fa03ade732b1e579551898b801ecd6e0fb1c5fd198335834b51673d074a8222640d2a969998f5b878bf897fdcf3426c4e24a7c599e5567643fa79ea5d20e7de581a873ee0181e3632a4e304f9dae09a81f882d4061ec17e588793b160c93a926874d5a8b78727f88de9bc125589a9562db5bb1c01012bbea1b2eeab68877871ce83455db43cc48455effbc71c436aebe362af22c6a319d134f65681c4d0d51f9aa42fb20f48ae3f7065664aeff5d8349624a5d79eb0bef3cbb2a1244ee445f560a6bf7a796b2c950a37dfb85ed5be11e8e305e835c9e077e676aa5ce23edb1f74806278548e3fa35059abc2f032289f9bd76043c8dd1352b6131cf34f66bcd0e7f1d13081f5b08ed0c69136f3b7ad8e05e9fe99a9b73624095f96740c1f40074e5d92ffeccdc0f15502082fdfcfc97a800be511c22b875f2832b2b891cb1aad2a17c7bd0be4427a4549404172f7c14d5e425e14498237c26a7813cd8612d048703cb180f1a6194f688b4644304950b078692faec7a2a5c5bbc482f3a7e8ef2825c4c19032a7a79a2908ca9774c6403e6b15625c485f2dd078902aff769dfee2dca9373704bf63ad981b51f61253910fd48c49ef10e3938f35ca8dd491a8e569baef675df30367b093f1088ebe8f876191dc32055481d074e5e47a4bd728efaea9fee3e83d8556255ffb2fa08194bdc66897d97d1557186d5f873169461494a83368ed8065b9a033fa4c2f07f7c60f945b60479e3c89233d58f674c0c6fa5918150bae0c6de2b65a09ccd490e2ad8571745bc37e70982411af667f3e8e9b9f7f75d863e5fef05c1f0d2acc7c86585a83ee32e0a64a9e67e75b80def5bfeb7cffe6e6822efa7a9cf049689b58336b081c039696e0fd3b2a2a6b0d177c9b3f8fe5cbb1c69ea93c1235b2c5b6934f603127eeafc4ed0728161612acdb2ba894a5ac376c4ef1fa8d49b4722379e5cb39752837395c413dd29a2a88c03849b6fb2221fd85ba6d5a50ba7ee9c09ecc5e6dc66afdaa1b021282cadc68f19529eadab809341187d57cfdfe01d0798ab8a94277b9b868612e575bd98f70de80ebe5f57637c511800373262eb5ac3836b03808ca5d5f732f286a5f18a7b7fb8cd8f60e4debe54731c9c524b84694c5469975443964ed28ccff2f4e8e0cf4c60c1c8a092e986cf12fa90a994e4f26ac89fabe8a0d1e27fdc00f1d3d3fdb73bb76809f93Line truncated
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"3ab6cbeebc18df951d371e0f3cce2697fb367476bd9d50ca9e668c77636eeb9d24b68be0ce6a75eca194fbde6221755d57e9d3148623de24896a9becd98789fd3d14de0c7e53f81fe7f3fd491472a66b5b797fe19c5d0525c7a111a0289a9e65ae7c712ccf694cb75c490070bca7db17205af9bdb7fee27f9ff41fc78ebd2d3d399e690908b5c064ffc0d5bb67b0d2880bcb45c2ca2741691b6131aa1e5ee758fc50610406216905e13ec049ee92d1f95e16bc283dfd91595ec2037d20ead51d3a362140578a4538c80581b79852b0f6686c1ea66aafffc872024592ec1aaf2650d167a75bace024b261db4ab48b401cf85ec2620dc12a7fc37012af8ac1d6db923d82eee962129bc4ede578782594708357d29118fd10dc6d228bf7e461d2769e556488b776237b6309f3dc2e884cb2df1f43f71c53d389765f805ac053d05fa835e75fab0adb0f13ceeb425637f43556372d728a00fb005f7c5a20cf2b7f776066d60b70b11a848005c6d63dba0c93f139067b39017c997dd6b94c0138c3619e9a6d0e4b8792cb8d58a2ca12ae5d03e7637f2065fbb9e2d1722fd3aaf234488ca157d829e9a3b642458054f3dd58da41d7fba6d2b488a327b776d1aaab1a364c710e755ab22b9cf7abf1eb8949c5ca20c070f275f8959cb00c6d5ab7879003f89f795351a4ef4850e033d929f9a349b9133b2e0bd1cabbdd381594bfa697b845100b96b5fade05db12de040b814ec49489f39f5abd5b37f570cbb516636d5b7378f12872d02d4de20b52ed8ca0b12029a4c084621bbb578b870ca2ea79fd5df1ef8664bfb3b1a1bf038e4ba33f6ccde42c5146470c9dd293aa747d2372db1561617920142ac1d32e4f1fd18e8b9e72b7efb8fefc56d08f00450d23b7e8381849b1385ddcf9310a4850dbd6db7a4992690190655760f557a5027b5ceab3743365ac9041a5c14bed1126c4eca00d7e0a0e0e6f666f64bd1466387150ece5835192149237d5dd25e703e9d3a4f652ae04601d6acf8228e4e86055394c3abc9dccd02f04a60c298d101260b408b2620c137f77e2019fc6eaff1b234c56dfe922b0192656254fe3356143e969f64b7609cbedebcc8cb2b68bcdd9d723b9c14669da6cbfffbca2351de51e87db6afde435ead0017682b8014f91d9734a9ab9b374257273e114a8fffac786d53183ba666d8a67e30c1fe45bb1bdcefb5787afcbad213f8e36e78d30ae1305df96bf450349ade655cccbb17d887f79e00728abb449ea427fd2d0af80e3b5607a74a57dbe5264131f2fc49cb74415974b3d43ff872d4106ff11b680f56be06fdf85ec9dd850b1f77f759337b9a9ce04e611036d3f45743e562abe4b959eba7424a712fcf7c3f3773886aef22f7cf6168efa83cd3ff70b9521cae1b6689b2b8c423d883a007bb138025f2a31db2147691bcb365ac242efe40cd09a746cc501ae0289e80205993b07f86538d486803da14b74fb0db6ebf1c2bb8c36275137d654c1be56c65891cd50f705247d85621fd0d61ade8c05cf4ec15b84e8adbcbe017d7d5743d5e91025e0154a5d9bac7c6b8297490e9c195c5d74e046219c042219817a5c56636c7c4382c6a01d721d88f4b4d20250eb5eae5f3ef481dbf8a3f47a1d51d080bd4cc33f12645c8481e57835b77a85a2d83301172782f22026e69a43376ac4f5b78734c9eb914e6c76c6a12d4127cf195ad030825322a279093cbc40a680355d086a27f3fb7560713b019e7c286d96833dc60590e9a709f2e3c632894668e74ed20e42cd83a23ebea3dc3bcc49d14f8697541780fb2072dee6a5672d0d4e7bdf5cbdacdf5fea9e03c6d9cf0faa1e954172acc26dcd344bb3d9b2e0e6015cc55d19713d795bdb7c21b44b305e69c69fdb7261483f9693f36f45d356462f1ba4498de1c2e8bc3e0a70893acef2006dcd73cf15b265a8a5d4ed792a34a846d8f1d3b9b3bb75f1c5e57a00b36c00203973ef4e2654f6cb29e4445318ed99f0de6ca992281e83ed03feedb66aeed6a461c6f2871ae95343cd9797e58430d5639d7ef5c59c78b29f76a055e18e2b85eff177770c60ca4f2d61e612e617e749b4653e7901b62ba02dcbf50e59219349120ac01e6b8a6e98eb54abd16b921a1ff85898f90fc49a3c8f8f4ae9b0dd32c3e7f2e1527c4feb67a496390f28532f20acc71abb8bb4f71b434104f41e36b705289858a4e8430b8cd9449b0198ca2244923cff1df0f63833373c275572de5a9a77b23e5ff54aebce8e86d02651f26ae32e69001e5f3951967579ebe8574682cef8c12dee0b18bc999f8cc0f07e2ad3ac94d3caf30c1c8a8295756aecbbecbbb4ade8a2b8015e52a0eb1290693c6316d036e0c443fc4ec591c32f7e7f1b3933c921d5812233d3c21ee5528822b59ef2ec7eb62f7b04f40cc8238a473ec37a07e54f8907825ccaa1421c2964d2c756be450dedc011e1cdd9045720421b9a4a00e9d3076c2fd10d71ee36d5c0fd2c7e42396b034a4cd0245027449242dfdc42c8af4a34df1b4150097726c9745247b78bb2bad5fe8af94eb13ee1f41dbd36e56d801a4c9c5b9ca5d3c26f4714b6fe9f69b87567426eb6f4ac97e8c9541eafc19fc90d3b24aae0f76c4f3f81063d206ff695d638048c2cb023147a78332939d2f2470d16f1ed0e5d3d4dde438affb2809488b99815e54938fac3b02deceaffde310cf422f9027f364f5e79da5d2b5af1b4138ac9f9d301f396b220829c1f60cd2b54ef24576e5ba6ccd4802900db1bb4eea57de7787eda0e30fa90cc19f099444488699bf7c442c398c2ed989d084c8cadc97325484e337848c34562b3dea6f7670f935ed3d5216c970e04351651c1c31a34e862821bdbcbde202d91fed38965e31cc3b6f1e52288f327bd0a787ecd92b3b6f535d1d000b0f02d41ee01ca54e4e6179ad7fcbd60f0e41dfa5c9cc7ee4f7de3844fb385ffa3b24092b30be697f1fd32c9faef29ead346e42fe2ab1d312901b678b43b7758edb7eaa1c2d038b4cd6a7dc759a6b12cec955bcf4179006a7ab6e22ef15986df107080d340b8870e2304d57caa87a9961c04655d7d66c7f71ca9260e02aced131d6de65d256d6b487141c51bc86eb1e4721742f07d09e799b30da7b5ba94c8d701ae34271ba06f8ce134a7a9a2598d1570cf05edd9ec868cfa2e41b4c20a8bc4b8bfebd45f5a60408f08e931617746d1464bbe1f3844ab3272ede635f771f9af30e483903ee4d0cdecbaff4d31451e7791dc97c92042fb932fe1c82652c1d682a55912e33de3b1299db076cef594458670dc4f911f4a244e2bec757dad4b0052a41235e2f5e60b929682608c16a61287826218a1ac3cf0d8286555d5b0552754685c365d4342f0d9c45065daf6786179da791a86b50a5edd6fb4b21f09d9747136aacf79ecbf52b00fb88b0630ec7f0a6699901ba4eff913a3ab33ac85a71ebb51ed343eac86eebb3e79c16e664078ccda09e77ef8e0919b8cc447116b65ccbd5200fbfe86e9bac5637b33c9bcac9596b57c14ad5dLine truncated
|
||||||
|
"50772c5a0e156ba13a9d86edc0e600021d56f7d31e7e452a74ad53a6775339c7ca6521d87a8c79b42900a1e9e6a1ec03f7e3d615611c3fd5c9927c40e5b508af1a298794b60148df01e9c9e78ab5ea8198c097fadcd6cfa6694be64e00eefe1a1885aece86f6ad87df766e692b58ebc41982bef5",
|
||||||
|
"93a2561a9904a1787a10e2a668cd6a814f2877a7b512698e94796805875c8d1a",
|
||||||
|
"588d9bc1d98210d9700ef488",
|
||||||
|
"165d8c9eabcd5e93e6eff7be122c8c242e1a7f284790c93324f924efabcec4a4ce48262011b7360c2833143d645ff295453853c92f0c48c6dfc2af7ec58d9bec0d13239c7e5593cdb39d49376c6341263df80c0ed2ed79fe9899d0c07de93f6ea95a5dfd307e49bdb5672b158a4df623ee86d54cd1a0fa9a60ce39d1f5f4b6b0ce9daf2a61a907cff3bdd3f29156ac439638e0910d728843ae17ea7368814ad7734732e7c023d4954e1cd5fd19fc9b76e9bb84b61dd4371478917757b14b366b4bfab4eab0d9de746088ad43d8742e2b9e58faff15c2eff084df5f4316111d5dd7d23cc0b1ee1000253f26cd260aa636f03f64a8342e531ca1515b3beecc3ee07a29184988325322d5c09754c278231f92c0d980adc919d4fccf4a1da1d37f1ddb58ca997d6d700946199fa007c43853b6caf5f8049233584087fb23c3952414ac487e452f0c3898486d04e5b008b843122501f9c8a294da9159a04119ad5c8e9f5c211411e34559d3a7bcf2ac10e0174f94f3f2968c80ebdf4498de172884dbdad0acc3a887f9bfe896a6004d54cc424567d53f1198ba33c56aa460edc6af0e437b34322c1144854bafb2434f00703c1992dbad0ceaa0616aec60a380676ca11558cece57a936959d6c2ffe0647eeffd37524fbafa9691f31499701b202d9dc9980e79ea517089eced779aa45b522c9ad193e63ea8b64e8a942f630d44370f23b7e9acfedac51dd9f139f8806b09a8fbbabc76fec3c3721fad5087a6d41f93973af8d787d8bc74a3122d99ea14e2f30a3c90be4b695c8b269784eefafa52d6a79e785eb47a23d72f037ca572b7029d2f37baabce57658119fb02c5b659e3aadfe0052f1cc3c0afc6fe4624533d9700388713945c20c1d175da53738fc73f48fe57fef8305e796b474b6f8d3fc5040042373a13384237d95bb045ce0c20934a964a8372acedfd6e559aa84180a86311a3996cc17bf7f73e5d85d4db2529989e5836edad490aaa5f56d17326825aa20608fd209903335de4b36b79f68b6a52194f6ea8ce42570533df650e65b50c367f69b9f08c32b3ce3e75318106b8b2c6b6d09369c781fbf2aaa35053af215b621f833814ec4778ac683de0dc22c418b077a917a6e405ccbde9f72ed523aa696be1a6f247b096b9235217bcf19b88d43178cce5a7d82335fccb4c079e00280bfd272b9f16ffefa7fea38d09dfb2e4874553b135052595812aed3fa15096abf1eebf9abd598289e0d156974de4c2654c60825d42b662ca7439816d9d3a0255f40a4965504f643f029da535d4b109e8658ec570e99859382ca0ede0b0495d508c63c7f1eff3f648c60e9b773590cc663a751178ba7603a11985ff519056661b9460c1aabc30e83bb0073a927682a06d1b8050c345f7920c1a37546d79587fae2a92c803a986248f90547f0b6c0ad0552d8260d2a0dc3cc76d092ab76b8c12f05dcf141167a6ea300bc23227933396ef6fe9d51a1ba5a754485950f06cfa6964db2d0fd1d4393cc36f0592fca25ac1a6aacda2a32f548ed20287e3d291661848a62d41504e4fcb1cd1785617fa5786712b3005f1a1041733df6cf838ea3ea0b93685889bc6b2857d80a9bc0e7a66f7fb3d805770402f049889311fc112dccc72a25bd127777fd87bf5ab56d39bfe6be2b45a8301c2f324dcc50b27540200d522c24941701f7293b8877ac84cf35638507c7d912a3a94e4384b68c507412df65d0c4ca8ec2da704bd4483eb2e0d13b68c0c2b68c106a55b9710ad0a1436d655a3cf3c419d5e6f027ddf5dcfc896a5b316a7dae9290a7bf81aed539a647c8c98e24e7ed6a4f7f00a11134ca715e5826625c250500f8f16b40de048b095b5dd08268407f58a91c86c36ca5a2bf4f8fc682adf1bf601da24414c74956e1a8fd2888b5260e980c32f6678a4dc4ff73220c22593d23144b84c2ff56920342248876d15ea54fc100c09a81b802dd15f030bda9aa08727ea49e34f0ca8693e0a06d0af06ea7ceddbf0584adfdebeb20510bbac683451d9f84cf0f4e85c34d979e550e07e7f414d6f1011cb3dc28d0df6d4aac113f2d5b04e4486ee2cdcd4157dafcbbd55e8330a7176d1b231d9f47a63da9ee30fec6cc2c5aba3a8c6154f79997af89d972743255355647235ee939f4f305ec655271e0cd562ff6f401b86dd5826c769298445108ad0d9e13c504551f74c507436911331db60ef0ea99dc259b13cfcb0596fa9b3c95cd7fc3b1611e3b012b6719afbcee7548939676dffc372276aecd08e6a14251407cf995266545427d49ae5ab245cd5d534c52542fc71b3973f0b766f3d234c8baaec8b74eaa8ba90abe160b4504769d02e08d7af4e7ecc167780c619cefa58865169b674b2b1e10d82f6560ba0be41a781f4afa46bd722566d941a8e6f87e4a5c03d89685a22a3470354f2922e2915f9d46288a5e8896ed13617dce694a595e379f25fe621dde8ba73d865976950954e5bd07db147a0fb74f87cb06aba49b073942b82fab33a878651df73df2721ef800b658bdc6c359d396f684598e93f38e79639b8736b02dfcc124fb9fc199c35f2fa1d0dc39939c57286e58a7deed7b6c76e02b99a14d9bbf11f65d8eb7fa096fe4baf0f78cb34736499a0ca550f10d7edc8909dc34b039e3abdf1aa67a51d37a2eaf4c07022897d4d8355d3325bcf392d91d02d462488ead90b366e9645b956c3802e4249d34b5b2b2484a1dec15a9477821df6bef5e1626ec5ee9832fc3bd0b63a3c4100d32fac3e9085f0b5ba43123f54beaa7ccbe6ba68231649f35a28acfcbbf97dea2d6cfd96025032b3950ec8437108d0f07baf1bc89e3afbc2cdbb5031d3cd9e20b19018adda466382059229e4c8c54b455eda4280bde43b36afa96e146e408c7104523d5f565d22ef86d4c7cbf9c6e0d0b30e37b37feb9332939c642eacfe19d0dae1259d3267635051ea5f9b518dd74786e45fb8bdf72cbe3753bd50bea2a961b49cc0e2d589e77fd25ebd962463fc728b1d288c38a79a182b124d345872afbcfe792d259e7e5334311244edc75d05f9a12eadb61fd3ff79fe8c097eb01a4ac1f0c339d3be74be3d96b0b6a15e8868d043a0f2007ee8aa51756d78b7a78ad90fd9a26afbcb51fdc20ed7a3947f715c833e363bb87504d8efc9f8b93a993e2e26430f79f3cce203b09093c9b456b1967212eb0db4f7688d4dccd4a523866f75c9d9e7ce07825ae34399c5607a60b771866a647b6d5e1e20795ca906e451f367d8c40ffe79a2cecfe7aa47a402f8d49be9084661c96ebb11f1b48e7e8abd2978ee626f962e98f99db4eb3c6a52aa2bb2e62194120ce1e773b9db784e8c9b5adcfb70e3bd5717293eebf014e9872c5c1bdf3fb296cb88eab5e97a5ac320092033b49f37d840dac23021c19ab2a89190f3c8dde927f6e6b41874bf71ba7747a616682bd5b3f17a1dad40f4993a1b186ce4f44afb4e36af7715450bac62cb1527eb8db1d87bbc4d9c99415d16660e48efd911e02f5777a77e72733af3c3f5315Line truncated
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"3497e8d61062e6f2084ebf72d00e9a47b550591edeee9746f31ea28039a1646d384c4348af293ab778f92a4807c48fbd14e8dbf3d67339c991dc4aca7dae38b5fb7bfeaaa538611d328b653950f4f664dcd257b345917cd66dc6a1ea75d99f70549d1af9d67b1608077b41576f38bb4c0a13ff4fa47b251142c6fbb79f9a27f43841ed0ebc0416c37f571aef8fd63b99e93ae88db50e9ef7d499ae7433d5686b165579d3598f96d9e7b1c876870310703df8fdf2069beadb34984f676eb7d3840c4c5766dcee3fc39f0739260a499647429339482e232362bc72c92a299cae36e9069cc5f4db8893e2c1b9ec0b4f334de26c951090b9724c2b3b7655d8248bc12a27861e020eb1e4cf6ad0dab903279b6fbdabff761d4ba159c1f631e681f210a8782faa86e08e554b5e30046157a0d1144bd08a691c2cc2dd22f3c3a4e5d44c5d03f7e3e385382ee4683345c0d316d41ee75f87038b49e0ad3ca45121789e7e7b95615e1a9a8dfe02c044c2935a97b141f639448182252ebfc980e0411e5fbcb3c01acd5aa7cc5d67101ffa6ab6acacace5f02d67155c26dedc071ffa66dbad26f67a819d46de0556fdffc1b4ab6d60905d8ef873ea1e51c62571c08b4c6db242e733e02e11e5840ee445c290b2232010b118839b37d4615c4521e8928e9ad475cdb4a3de9928ec7e6daf0e20d22e308347b31e7e877fdacda0c25f2e5c33a329e84707816ff4ffdca30dfc753c2cf883df16016795db34359e9363fac60624ae4d2b30bc1f2f99c23d953779c22ffca145fd08dad83c0f76cf727196799544c6c07483e0a41ca2e1b1da5a730956154f531d292b5a39a229ab13bf24a804eb68786e481c8aebfd3bc557afceadc41d00e1472c3b80ce652be1245089283bf1a1a93abd3325bb6eea121db8c0e1d6c0c31decfe9dba63c89b881824b0531651fc500f2f75ca9e5fdcbb179c9ded5d600a495ea704c2709f4a88c4fadcda4cd82a5b089f25a6fe0161159efe03fb5e0d44bdb5487f25e8c9adacc389860f62b06a6a4f8f104d9171622f70652ace736e8b28b70a4d9fd3fa4b9784d1a6e6811150d0a0601d31d17f6041e58a1058f99b80b0a6cd4f79c79a104b6bb731ecc881bc68e1d99ab358faf43d8504957ea0152e46e27dbfaa17d0f58287276e4fa82ab78a03513d5b4c3199d1362e4fd6447d1c26fadbd011abc69332ed0181952b391f2e8a5c89d68e22a7c451f69a9573b6bb6d918c7e3d52116f3f12f1d43d2af46bb450f58bde1732a268293cfd9cf2b90a844588c1979a30d6ac21aaea4b9e5500ef4a8bcd62bd70cae6acc8839f818d23c615e45daf14335c36dd46817c9b816be60c3848caa812b055da33f45bc01721d6fb7e850fb1e1458f27c70bc34876a955aef11f5703cfacde03a039c3b75b99b2d91fc18b00071a28ce25eb169b946b49858aa0885a4c665deca020a3fbba55d4d9175fd91e7901ec9eec0239806e8305f8238e5270f4af5c94d0008f8a5564636cc33c8a3d3e76db2a7915abe798b0dfbb3e322b33e188c7b188573bddbb9e4a7edbd4bb194b9743c4aceeab449f8affddbc2b109eb3d84f3b2f8b18ea2962680437241d82bb6146674ff1abee7baacc38d5dcd688b425c3e3b0dccdda3e36de755afcf7155d3d7cac2e279baad167e2a743b82ff8ddf3db8ecfa9680ddf468339427a4e9fb8ca4ce6f1e790c24e7269912a9989088c65965b0efe68ed44eb26876674261e3e72042f5995f1a7075b3932f4c23a8027d0db35ce4322122f489995bcc0b3fa32b7298c4c1b3354766c866a2fc0ea5690c58c5e08ae7037f70accb3ca7faefc37d78883f2bcd768285dd2571dbcaead813a0b8ae87cc1df868e93500d414c4418d5c80b919f73b9fd46111a02bfc884f9d30ee14fcfc1d55d54256b9572afad4777b8d8172c911472a22e7461f6f85aca063c19d6fdef3351149ee6864e93cdc54ca5dc7837f0ead91f5e3b155795df5dd1f933cee8671ffc05058353995019e5f6f55d2de6470605a5411afcd7fa5aa8f38d77dbf496d7fa9c5a4d35ab661aa15c77ce42bed44763166160ed5bba954e470c293ca301363f5b837406ea8ea746057588c34acf266030864d8c40e2da88ef04c49205fad1607d456767d30eadd884359bce04c12e35487bc1885d9b104c9fd4dea4ceaf054cf46cb3c77a619ffe963acc9bfcfad0447591ccd32cdd1fccb1fe7080ad75cca2e17f695ce0095a774327123f21e2839773506a9f2d896bde87dc5e35512ad733aa408f8a49e9018d1013cc32f550c968a03308cdbc73ab444f0a79a13450d4de906369da4c6a675d7e338f738358dc238be4f047579c8ba7a60448da541cb9e57f22bfcb8c26280a59b77edd0f5a009a3ef1e2958d6d3c3372840dc6a0c6ab1fe86aeb7590137feacbfdc7da57c77595b8572b45c4677836ec86fd8c4ca8ac351397aaa3aa298d752754507e1cc514d41c3f1ae0a692179218141f65bccb9acf6244730c6d00829455d21371972745b3665f930cf2aa9f0abebe6f7b89094aeb4dbdf7bbbe794f134b6284e289c995ef2929fc1bd39b259259950de29e57cdec15c4a7d33ef6e689596a6ce23301d25c2ace77fe699d90c2329da4d0f471bc093563dc735ac2fdb32c6995606a67bc953534939ed1236003c004d3b47590beabf39a1e4d5d1b00898496e9effda68433da17d1ab3a32aefa3681aeac116c5705077552649153ed15e9d704e67d8819579feb02d91db0d3533182ff43ee5648f5cc9a595ded4772d61e77bd9bffd6f29fc1f478dea44c32d5ce3118bc8860b254fb0bb1e85223bf709a7c0b9a52fd3914f1b1f295fd246bcb568388dee43a32df45e3c798068608a102143b5511746903255b98238003eed68776b46bb0e64af6c9118ecf9896709aaaabefbc1f58bf45b45768345b560ae2cdbe4d7da497736da8013c4098addb4258cafe7823bdbdd715250b707b155248d39fc6773639e4de3b201fd3cdfa1526c4149ee7d15bbee680c956fbdea844b1470a287d430c5c7e2d7b51fa756720397bbe214c19df3399a989958732d93979e361f7266e53a59bcef695435db67cd8749d258e7d582726e1bcad1395e68d7848849fb6d74451a53ae6e8989c64701102959f7fedc6a5cf8352e218396f9181f33037ca74886fae6e57460bbcb71cbe4cbb3d3a81e2090434eb1d6d5baeee4ede251952ad88001ce047279cfe435a4afe97847f798d84ad79a11bd44f09222d2f3b7fdcc47ff8a4c61f40c4629a0f603193e0aa2164579a05726e547c9081abcc0087907f8034469f740a020e19623fad42e9cea64068abb3d6ff2f6680da328061c200e1f646816a5083786ae5b71728a0e5cee14d7a942379c389fa9dbc7afe7e7ae075c061df11e4587bc90f92f1b077c091c43a25e7b3e870ad852c2883aba2632063c4ff74a857ef7267816317f823a8bc5dcda311b513be3a40e6bdeb89210bece50a608e624f00c9d063e0c8878884e45527f50a3ab4Line truncated
|
||||||
|
"823d0cd34e7450550da9716c1f456ce0cbc79431483a6214939266581b0e899e4c95719a09c1ef166a618289a6ee6971b6fea3fe380512cb977823b387ac51d341c26d4a835c61eebde37764d2e1d588df7886177e98e3151106c898b3196bf4dbd83f5f",
|
||||||
|
"a4639c22fc7f370d8500a53819102df5e86c541c0ca10e8f6564e50b90c28f34",
|
||||||
|
"34a04df283c45655a52bdd84",
|
||||||
|
"cd8d1b2e5f65ddb3c0da8f12096134da22ad4d541444964077610aafc1f77f8da5ffc75bee807541cb6eb0526e78d57fd88fa9d9608914cf391ae7ccb8eedb0aa711889f9b6192601163b271c90df5d69fef487b6c05a24fc667469cf16cbd5afd58fc830119fc9f61b26dd50a96ed84c96825a615a3aee84ea4c950152323b20884346b25c9e2a6be3a93505ba059fbb114c224bed8f05f54eab76b2c9c23a0fd942eef9696ff67484b542c8347f1b1fd7df7242872b3528c9e45030447b2bc85eaf191963291e4223b75778335e5f1256618ff87bbd68b5a9e5cbd2ca1dc8aff4625c834edf8fb0d879b1f75ba9b85895a6bb4d7569a41bb3be6cdd020065bcc69b44a8fa335d9418ea2d090d8061e042e8e1a6ac03a6d5525079f14274079734ed42c5c9ab9986f0fee6bc9ee6c485e233e9b4d6de70664902529a135a5675ae129353eb2c00b73f226e84fe8c594272d6eceaca28b6da30492c92074250ec80beddb7208f9b5418944305b0864009b3bbb3dfbfb4cc2bba3313f8f7c6c19860f1dc0f5d7aa06e3b551adfc63dddac980a79d72bd2225d54a87a93717291c7b78bdfc5521f7f3239d5564fe9c9559dfefe76b77efc2e75991f31a0134529a6611ab9ef076491f2d2d81ffc5774ba8f8009dd7e5881e09ddf5116fcb5a44e576aef6cea91ebf52c56c742049639392cfb8b280dc2229252e04d8d394ffafa539290acdd8118656e7e1a4f7bfc0bb689448379e8cedff7590a09a3f5a29bf819fd87297b96ca07431a29a07ae126eb9d65e21824c16707db89868e127f17614a536de6ed268b1600a8b02aac2bca54a09b7cccf8e184448df334f95b9f0221187d56da7bd422f09b4d94228098b563df53414a5a86728962a2ea63023d8c3f03847b36db7cd189ccfef3e623b14842b8cccb18b4f80f01b32a4cec48f3009b98ffa25dbad76089c8700e90848da74aeca81d01f4dab2b7e844a3e48bef21f33c92734b821ab382bdf6d0b1048a9866e676b78ac9398678ff626d5c173a15a0a7514b2544405dd54eccaa2791605c87d7117bc9f8c0ad84623a9d3a2b1733304b492d4dec38f7981db9361b03a2837a95fe937976c7f4341a802dbf583366fbe368a3af3f92618046bb55696cf7af1f465a5a57ec5908621f431ffc762f35abe892f772a60a3f75ad8401321f67981e90083fdd1cce40903ce56a629120d6e13c8871523c4d848664331966298c8b31a5bc8174a8c14f61cbe98ae7ee3e90bc832b04318864d19a9b8b6d49a260f42bb120cef9afbe704faecf0f428d917ead9f020f5e9d772bc8f29600f8a7623d8971c1e3c5f1a3b094191e497bd70f85de124137cc4b9fe0617cb73cd44b89aada072625e25976e7aaa5a8fe9d9e3f32db47d1565aaef0e84d256bfce6aedfa1a2dce5a94976a2bb9a0da95941fb7ed444990b0e0e87627e35f3235a998019650a5e5cae804ecab8cf729a5c712f1e7d17486082dd50cbeb2ee1b0be6a7bf08a66ab3cf1fe9f49c7083f5b8ad183f32fb35fb8a41230e4041bcf0e5ef54bc3d21ecc1fceb08d95d745a997e8f2fc3c0f6b1b6c1c02e03ff02ae0d879d13eedd42d9f9949ca7ebb785764162ceb6c6f9944dcb3927b2f4eab23ab566b2b2bcc0c7d77b82579e88203602264064ce98b5b1ed992c1bb13edce579ae7f5e11697b493749f308b33e47512533350df5c07c3dadff656197884f359cdfcb736d29231aea1524b56e06c92f5a98ea663543f67e44003f5b41907a951dd792468c84c5e0e1b46149a5c9751295e153990b78c0cc712889a21b299b0315150dc50aa3b4f7fb0079ddd39d263a754b1dcc595c76ea9fea6c120384afb38d4bd40491c4689b1afc9dd096dd0327c84802bda6bb6b7a8830bc6c06b308ae9665a8666a5551ec954eb72adb827ef38f036c51698a28c92dc1c9e25c267532da2c04c1bf27f5b683ac750c3ef53a8460dc186331549bf82868f9327422c09afe1cd15e161bc41a70cab2f973efcfc8f01a380b86a432e1ae540e09d404d93d22a20dd5f685a52f0acb863dadea236288b1714700f23d1c19e40e219e8ed21f6a393e541abba850ffbbd4030e5f6567b7202fb66d86cc2a0beabd495814f6a50690e8d74cb8b093e4d43261fff80e7a67ca06dfe808899cbef84c09ece01414baac740cbe4c656b17991868e2a136f4785a0de311aeb18cc95ed33fbece22aaed8cc1e47f58cf6c09a6f92c96f37d2d2485b369093506f5e9f8534f8569655277d0399ddd3d33861bd40c71ac53a44d1981cd744d79202322d47a0228356c0e27efa2ff1009cf2a416fb6e8844eb76b8077a4a3961ff193e1c95b222e72688ba48be82ec5da498e58861ea613782ed1ab50a95b5cc236834af98e61528ab18453c20ff978551b81e1bcc0ff4b7092bdd9ab0b946b7324b7361ef05e1f7d7f6a336281b4bb2c671a95a6ab84be6bef1b9c8c3d2536edb8d79b40637e16d7281ec5243016232d7c9fc07ed9dfcf555055d8ae65f12ad150da81f62f2e1e82b3adacf6d623ee4759ad61a09038905bcf1dbbab671dd28fc1d10a0b7eaaef73a5862ab449bd84c8698d061e79fbe52a86739ba945a01353e0f3916667bd7b4356cc65451c7003927f2aa738d98245760550156dda529be741ce3ae1afdea0de35ada26ac241fcb5d518e6ee7f9930baf88bacf8bdaccbecfdb920f3b26285439912a8902ae029b07f28c1dbcfde780cd2bee6c6e5f4520c5c7ff3ab5448ec86cfb270c39586f80041f3764b5dc77dc5ced0695c89671cf90ed34c4067b4bd938b1493c7902dd94be824810a00bbde4915d138fcc7584790bb0b6682fc0799cd415441ac90c1caa008c7fde3ab4a3aae478c64991ebe07e6c4587d3046c9ebb8e125e795f0be9266bcee5a4e4355a2830c5b34e583b0355b34b89c08011db6f6b8371de003074704e8cdda37ce42c7e395b6a37bae3dfbe67bcfd1f125c9a262d56883ddc028773988270aa30c6dd326cbffee589f38286533e1d5c9486011170be591beab5e0ce98837cf91f0a58d69d872e364aa88daf9cfa71bad167129420282d99ed5884a1276dfffb2c4100c74a8b863b063c07937f2e9c12523deac4ea16178863d975e3a5be5efb5ffbea994d07f7ddc5326bed1f5c9415c1d4ee1667e3a581499bb573595158636ad94d84f7c6e4b8efc2b141f2bfab7932a050fd88a8c7b21877cddd488543db5b11138cc808e1248b6e2ef492faa8a32f9d93e3c060b5cec10f03794248f9662ed8c283a8e0eb493824e2750ec75b3b1292d80ce002083a3c64cc487afc31b20f84a778f386b012ef7bef46e638d0f1cd75487ea46e05621d608482637b3e642a9a2c5371bead4386eff968b3e007fc263086d8a930dc76a8431a4e6907ae35c7b3291075d1c723f02e4895714803c0e97d65b04c0f27d01d5d68001bdb3bbd44dfee1eff1754fe8c182cd9bc6ee273beb2a444ca1766f747d86f36cd8cef6eb1dafe0c38b9327a8cac6e83e076099188fLine truncated
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"5622aa8d2f308dd468a7e4959ccc01f0e80d91f79df65b8201eb44911f6abc758c6703bb97908fff377395d33f96c328a4541f414b7ac34c6607dd85729afbfe01feba988e4997c6bd2c99fcc35d2467b143a8fcbe6b49247226a9e4c0a4e3c1a29d5931e6f1f7a31d90a0e0edc4479f08ef9bc65ae4eacd0b93b1cb38948dda31e60b18d702bbf5935bd580201d1f280cbbee679fd834aa6be576a37a037eabe989c3c18c7fb61fda8b9ffaa8bf22b57a101c19e850c454353af7af3d755b26ff1ee78b9d9daa78294972d108958682a5a29c8ef260e2289ad9d7d74f32fd4e51e5d9ee828366abccd97dd56e035713a6f3a1985383c0ed5d98c4accac2fa1ba7d30a295670d5224952f7b7554fcbfb426c9496f054834dec48f9b70af3d2b1c6dcda1c4daf3e9601364e57851952c785e65d753be1c22729bbde33aeb1e4748dbe90da6ecf716f05bfc68ad819515dffafd33a909562b95140ecfff1d0747f8e0459fcd3ca6cd8893262614bb4bf4b639285f327e7ac782898781968ec98f6f0f2f3c4bc5f9c4691ffa7ddb3662816f8ad092095b598bd4d10d6b5fc6fabed619eb11dfd4d638f4c0b6cff7194156a411e8ad6d3229320336ad52fd9811c3a1fcd571d1bbbac67c6186737ac7ca1ed9b2bc46e4e578f81c164b09ae5cdd4059a2c22b5e7ce1dade684e49200867f9bb1430aff9b99805cfd31f7e3fecbe898f70a4eded86b8bbeef7050eff6cf8ba71395a7ae2e270a2b58010e56cdf6efc4003da3d8a82e96979ee68694b6113cc9a6e377d40a810063830eb95005a81405e5b7de8de67424845bab1911bc55da6338513742d237a555465fa54b07ba50ed712e7a57a39fdcfe4af50f064ae969823aa1c40cd86a621ec90769d0c1babd33e8388a8bd76689215b9827a5819127bb32ecc80a562a291f3192eff34cad2635e5b0c0bc174add72e2041864953f1fc72be7d28111fba0438d9036da3d5c0f220ccfde2319bb96fcbfae6055ed7f1c1967ee9a78e93bbb77cbf151084d602a5a2f087d49c3134582c1a5d7af24f4c88be26204cc9dbf4368b19470fef49a5823a2d66c65e9b1e8ab56bf5a7bb3220696840a6222caa58a7b39fb792d95d25038a8bd9d916e853cc5459640f8b8468e3d51f05f1b95e996cee40ffb7ae14cb289094f1b77d5573c1aee7c12a6c3a1e31491422f272cc5f510d4f18ab63d3c3f468c5abd61b2fa7ba0768d46392e2a4dc06c7ce79841dca916cd33cc0a700b50fc660e5d1808d8b87e65feb89428055495823b2dc317d6d9e50aa5ef7ab14076174ed32f56abe7d410e58ca40e92f8a31433d0d74ba7b130b1561f2b075fa11ead744d031f34d82f1a64d428f6cccb0a009be24b42937bf3e99a1ef1fabf0fa7335dab52918382abe756d3de229ee8223aca6d7c5de87047838e387d4e472481a4cfd4365256e13aacb518ce5300f18dcb5e0a28477a6fca08a74756ef6bd8933bacc98d02abc7ae60df7cb3e06d41abcc4bd313c543ddcdea2424d98ffc6dcaa83658aae11f5841ffd4f5df42368a0e815d2146a0fe138b223764b133d17cdb08d485e9f3dd2bf2b220d1f4565b02d7b9231d592130e4436849f49b1a70772244fc0c38da372a8c57fc80ad57828410a5a16ac6d14e093997fdd5b26e4cd4b248e0ea221715ae6e112e1b68b09f795540e31b1231244bc922207b906c4f42b5302dd7474286b653b4d1bb657134bab117d6c349fa0f121c2f8dac9cdcef510c1c28545eae0ab163db6cc84ca182feb858c10153d0136f00a01c9c7d0bed892715dd85c4e73627c3a2ef0f43710dfccacffd1d9f118c9fb1a83b2eb328b8da3e955f027d95294038184f7b895d77532c7570cb86fd6b37a5a66659cf1e330db3930f302838706050c0dcd91d532d49c89d144e9a7f864026ec99f50acc02bd5f11ee88495ee8991ec4723b189f84e03d992fd718b5173ea1b033ab7d3568dc4656648fb54d28d3119b0f293a930a772c394f45ee66838f17b73a94eca27033f9d5c2ae22eb813386905dc024673850a087958eed191d04d05798bcf909eff2deb2a0009d223323b290e3d6f71b2797a2bc2590d54294a5992d629336518514032614a04847c3fad8a7d1cfc2f86765b48cf58acf892f68b691fbece38100e6a71487ef5c4ae934f1ba03b4b26a1967f70ef1c697202e4eb22a3a95ab3b7b524f0241ab4d2adf3ee5e3f2974d0bfe4419ef0ab11039ffc26339570e74d260c4d5a16f22cb4f60b03253487f5e46c47836ce29460728086a615f78d631d89a06790928455889f58adc3d0a3a84ceb2ba9cdb00a403080e6567873b985fd59fd9dec71e375013c12c51cb67d599198f36f58fdaf897e85dfe6f9896cf6d35a84cfdc6834dd9447a2a10e1ffa9fa8edfef1db9e8b4a245b211de49e04b7e88977b4e1ac9285f43526f2452181ee0f80efeb1f6b2533b656519ae45652ccefca81c17714476b497e5d8e9fdf6c9f504c7a7fa7afa36df5f4f8da5b4b973b1618fc8d2d43e866b235e5420551d1659e5bd545fb78a3e17d9cbbc8e842f3fe6be07b892453ffd689d5188f26f9e4c545ba0b3132af12a03bce6914015d026d3d7df661c1e6384bbb50dae24abfa78079a2b1ac41c44c7d82a59183f293f12011e781d3cdca2f791afa5b55a9f2d6139587bfd74bfc54ce91e642847a33b48c1b366fd8f08f520b79ad5113a0273735aee71ceae361a97547fc09b22fbe4e4ae4ae13e52d65e0971341aab368d1e917c8f5f2ac57ac119f981b51b7c99ff2be3e16935b7c73e28fb58d332e6f2c36281228c479c4d6095cf15b14baeb0769191dfc649a70471a25d45d4433797a5b8ba31ff567e60ec4d759d99244d0fb5dfef7c2896809938ddde0d2015a4c5ce5ef6cdb5752da1c2a33e5bc78b6b7c6a5af892f0792c28560a357720da3cee3833bbeda8e98e6a8cccc6535831cfc28bc8557b4181a3978bd90eabb34b99eb7e55d9263e6790ca34561d8c87ec4e12b4a38df524318db00a9b5bbde6f5a8644a818a88e91b521d716fa9f95bf70b109b9905bfca926fd42ecb9114c039790abb0392a41ee4c190536a89ae6194befc2dc4bcf7562bcb84f65c99b69612c0511552f53436b6c489204d3881e1f67e0fba3a061165d2955c2e2e12c440d31556250a8a5cc04ee5e09b1d627c14e08bce1a92df7f6475db92a3ee57e4c16c3ae677c44237122818ad457a29595ab528744707f3ab7ccf3d20bd94047e013e647802a7af14cfc7c11441ea6e9b9f960fe69d03911ad2cf3a8f633e0d647c71dc7e188c92e75353fc953d6a30dd0040c39d4355b71524f1a4872fb1ecab22c8293b54bb22a80e1e3d4c886d2988adec26f041dd0565cfa9edfe5ad9aa7da1d3b8f68fda9e9df9dbe98148120af6ff30e6400deca6dc9593dbf06c856d0d582503e7ffa185f87c6e7ac58184bb80b4a1c0c18d669e23f9791365fe807356a5763ea418c39d94311759b29b14324fb6f3104359ae66532779b825f92bLine truncated
|
||||||
|
"9f522375925222a04f5c95ee14b6386412025903ecad0bc3ab78afe1145136b3a3592835ab4ad6faa66be9",
|
||||||
|
"d1ba82b3ced3e9817642aaacedf482e79bedd0560ef2754215ee792514bbf8e6",
|
||||||
|
"bb21211f342379370f2642d3",
|
||||||
|
"1a6683805d3f478ca1c1512b9846468378f83be27393db63956e151ec408368b47334afe610249182f54c4d0a01b704db2aa90a9755b8feb67ef9301f0715d7d6bdfa5cc4497cef1142a43eeb42f7c413e8f489af30d742a706d05a40a0c4a5991f9e2cc5d9fbca6ad3767682e20c146ac35aef38dfb2a77388b738fa022158d5c802e5f0761096bb45b50815ebf09172759521b5c5d459703ebe9ff669ee4d14a86e5d0650b597f4a082ba0aef366a924ea378b91c3262d99f48189eea19c76c0f644079f8415c11033cf24d30d6c149ab13ca5c29deafdc816e457257361c1af4b915da312d2e6c7fc712faa27be3e67c893f9005a0e2c28369991c1dab22d38961d1abd6d94c4d549cf491aa1f8d522be3ffa6d214825a5fde3c94c4e35c29b8d05b2627eb12c9d94f450a85eec6bc963a279a37c2344ca36eb604c4bd11c2bf2ecc0dc16c2c365bbbcad3541bd54f8d0bdbb3ca4a087b62fc19fcc1c13984eab807d2a6a1386643d90d412d027bcd0a638765498cdbb1f4cc1b91b69bd241eab3645f225ece85a56e5008d6094041f8cca6b9a0ae3b15585de6fe0695d79d348f8619431ece40e736957a7627224fe92bbe30df5124f476d97e36b5b08b3787e8e00f0c10013068eb156f82f3494a35d6edd5f7048d1e91954f1013ede22eca8b4ba41699ee08decedde87139180a567c6d169b672af0f12aa09ce20e9cac4e78b8067d31ba4f63606c00d1d787b868cf7643fbb170f8074667c9f7584d36af80b4e6557724013618c28d0dd40bfe9d4b25761b3c99558af528c2d290d04b09821bd7f992c044dd61dde9395bd0c9ddec6d0bf6e044ddf0b4b2d6753f5acf2e9c904caa4e9f310578527b85e6738803758da646919989f735b09c9a5744e63fed2c3982e59fd29d2baeb9771316bf8d29213a4956b66c78d5654436ffdd82d0d572530fd09507b988d13fd743f35333237681f8abbb301a8ea870159f802a57760659094d0e4902036c5a62c563f1fc86c4238e1ce89f5176ecaea194ca112fbdeefbef4fa7c203678cafd34486fe58b2af04f84a1cb620c6e123bfd96301e0a5e5e5abcc95d28b852d0cee2f51faa73e42f22fc335f50de4c3812ee14038633a195083f3944284c1086c34995832c3cceb7d385b4ce86af10685c16005495121105272d1d739c584a07ec7801c3667bb280987a8aa41f9537e9d1812a5dba5b385a0b71d2e9573c6f3e9ebf0bf7267528946a6aa6f43efce908d32525cdc3b825bb11c7239f1de412704d24c17455b9382fd6a873180f0d5d44dc449320973d5cd0d4e67e83946b6ef47e5fc3dabadd80751f1421404e56b1bce748b7bde63c6975ca81f3eaf52586a55242c9745dee3f7c796d4508e818eaa4fa50490c1a79624561b98d2e1139a328806414c905372356a22393ea0da51c83957029edd8c2dfcf46d9564264d74c1c0497034ec018b1dd4c14acebc34b6d2c1a616937c37b8b4a0ee5dcdf787a0de1173798ab929b72e0fa83a6c9b9a99d8024328d9c236a8f57550a4f83e8071eac76adb55939f85f5b5f514174b670a3e8dc2b54656f6201940a81fe4953d2680ae4ec58635ba74d15efab3e06dca6ac269711ef2d4dd49f731e24a92a3b935ebbb3fe8d001cd4062669ae4baa62c2947033afcfaca227d88a11769f87456d5cd1bb6606891e71d63aff9cd5a7d23263a78768ac2ac54ece1441fd37d096cd27e916e68891137fc3cca427febd1947cfb4d7ccfad75b2ec5e809c132111eadf25a73043d68333139bd2435de9941bbc61c5c509897cfc19a21645019eaaccb6d06371e3d0570c09c7556e41a727e44d9bd672fccd1f89cc7d58761c16df8fb75fb8a1dde2caaf088f02dad91b6489114398740e6798f3ea8c7b0cfd974e160a0106d703d9589ab09aae79108e3212f19cb950ea9c0798a1532bc2a065d5900a12054395c0545b0878ac0b1d461f553dccfc2a22bf254ced88dcb538e3889549960b77ba6237ab1458e158f4f46606372e797ec9d9ecc6534acaa1218e7540eef11030bb9c3e5a7816f3b33a590d970619bdd2dc04d5c6f4ec38b7cb4d525234b836eab57f65dd045e02367eede9049e219b8712b8d6fe178080c5f77b821f1a475259ae571a5578eb3b48863162d45486f71a28ecbcedb35b320e5b6401f9e7870aa5418449bf47502626e1f42abf481b48d5a6819c640bfdb64f873d583fc4e40187940a6c3373ea7b47195270a8657898f55568985018abcea9bce1c155d95b426f91a734b2a14ec2c7ca2011a4d30019fd9b3ef63a804e9c30c3de2651c4213e90285a4ba100b31ee402e8a7f23cf9d4dba003bbf982526bc63be5af102dca34e7d362d6fbf6f56046160d7af33b364f2a86074d1c0fdd54aae89b19480efde2a9caef9de7c0f9491e1cf43a48752cef405a0ff16b0fc67bbe433a3c1b9661406c3726092efdc076febd60c436476f24dab1b0b8f8893986d951ed72282990e8b1526f4dcf539b22c01c6a7eb5577cd540a16a81296ebeeb7ddda72e60fcf2840c5b42c5cba30eaea5402f267d1d04bc80da5ef0dd2bf3c7a2be986507617c9bdbc96c6273a0c9e586a0c48c98b4552113149c6f79557fc8ace0b1a512fec3aa09ef191f95c2163113ac5cdd940f0c2120509bc53c3ea493c54703effb902ef752c830c61e85636ca95429bf16937bf6786b3eae1b277bf08dcd69f521a0078d633beb33c9aa0cb33b238e1021ca67df122a403a3698452740bdcac81d22ccfe4ab5f835d1961708d1faf6d40f115f16c6094ea37a7ff15e0534f62c19a6f4ded0967be337cdbdd2a7c58ba16ba2e4c3686e9d075c6fa7d29b2a0335ab4940d2a95c4500295f4db84ae65e46c54b7300909cc5411c725a31fd962d239aa0e2007c285586b4c778e2ac7afec42cd8409a63d7cd9c677031f43f4aaf04258dcf1270c02a4764177aa66db2d8f860eeb1fd06d0b27587537410bcb641f90aaa7bfc6f12bd143f66e7c933a0f3ce6b5048913e1b2d79eaa6c19e7255d5eabd24d5f12426339541a22d600cdfd1781a1a3894740887840aa82e5a461fc324285b0223ac9b95c3eb88160353f168b3d4ae8a2e87b7715b5fd2671f66e6eaaf9365b3d9e3acd9a749faefba6009783771177aa4dc91f72fed7a5bf6b1b7738b84ac0a07b4a5a3f0a9134a39e1e7e3e2f9a92d5644295f31c5a356092bf07c709b4c34305ebf50e857a4f593dd1cce0439d3fd125c1ede1a48f583bbbe0eec7058345129ef78868a96f8a76ba7fbfd1c5eebf75f3e0eeeb9db87474b96f321b87fffc02433513fb467fb74e2fc8feb498d51530c753e9a173e95e0edc5ba9802641a45db281b2e2d87d409057b4fb1925e834e90fa5619ae3a9237d5b104e7ac67c2bdc31001eedb4ec7064b2f72e0379bf8780f67ec4b195db014a2d130e77b1778efe3dc703f1310a566a6d3b5c9b12b1d4e25815493ed1510a516a31ced3b64ca49a783ad63ea71a57290727fa31386d2fbfe41f12d36a61Line truncated
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"99444e82c6c4c47070b164f298ffdf6955ee5bcb3070b9aa95ce658db4db084d2056cfe61a93568b44ba7ddcba5d450f4ba0da7b119425a6628b3416663c638692326cacc5c237097db5e537122b465dcb21d8dcb5fe831789b72deff3907685c2e23187a56990221e755930a09f8d6cc065487563cb8cec82b9dc754952fa0b342c92d99522fbb39854e338f470a4b4d5ed2a39b8b6253b7001b0b953abc588d757616c7a5d1f12b1024aa572ef5a47dc8480943aa6cfaaa78064fb2b29830280e46efa418d0cf38f57980146f2482276c9b6b16f865b1606bf1131e894336979a163ba2e70adbdc746be0d38062fafcfe5603e6bbb55717b66a263fbd5cc7476302ea4a0dc6167221f745a26a309f5886934f4258965a0ef0803eaddd05e54008df8a0695a078b797be59f1eef95a658c99a7d52001d4108212ce5f18a39f1173291808c980b0513f1a531e03ad7380372b65572d3967af4c25fe54d99d664cb67e557fff05c12e10143c13b1bfa3e8db093ff832a7978ecd85d3971349e3c9b83939b73f0ad55f1f1162d0c106b99c0ff98442911bc15e9194f5b4ded97e9702b84e31b31380c224f392e5fa5c720a45f64cd7020e25a3931b5871e4c708e77f4729225aa9f48f9d876597d3e79219dddee0efdd16836021dbd21692dafe121217347cc128fc5eb051e6843978ae17478ef714957a84c74656ddd931cbeb43e32fb0a448acf2f90ee98d38522b4fa9aa36be4fa13306e799d4c0cb90ac0f73cbc018146d1b0d6bf48aa446a5e3e0502aae9fcbd196b36b6b7426fc10367febf687f05392fdcf878863de2e47be7e625d0e3e3e94e199f055c0fc65f76c41ede43231873ff10eb854dcd6ac9b550ee8533d16f81eb0e86471d4da69311c47255e78ac8e79ab36ce880d6b135279fbb5a712adc5c3862a356af49e9c10d5b16f4e5dedb80914868111e194745b802a0292c7c8564de28ba8e71a44f7eff6573e5434e65d496cde5b5e62cfa9e2e9ac85a164dbff5767983e71dd2661d37d9027a27674ebe3433731a606db88e0880e91ecea8134421962b3f68915c9f6a5e1992c56750f99bc313fb30cb89384c72571a1a6a5e3c01897b691bd70985352217fa8a67f3252a06205bd1a9931d1cea3736559572561fedbf3ac4c8bff9ebd7f3753ee69a69ecbac4be6357db7f4213b697a828edc716ac01da75c1d46098c7d5d6ae6f3f9a2903588c5b340c9d47c234efea21b700cdb8db4279afa2117677e824e627bf0f2b179c864ba823926a57825478395545f130886bdf2a7c55a2647a888c3998b750343d9cdc602e46b7b09a2fe9ef74db1ffc46fe27c254c927ce51b307e96a571da7f3f907223fbed2daedbcc96197e95edde7859f3b4ec6099f791089e368a68a5ba0917ddf4f50b93c0c839ea36cfc8053811f8fcfe6986e5fa9f743119ecd6c3e5fea1dae3ad7eb465a89e9c68569190688a8d56e4143ceea3b11fbd9de67173d5134ec8b0bd7d16560ba2be52345ebacedc01a2e03e8183ef91317d87b2e15cc6301586ed829d438e4ff1d074408b332c8ce60ccb6790ab08c228807509dd4b39f2c227755f6b039f5cd413ad6f46c9ec2cc6a79457529d297b1d9e74ead9bedd9bd652fb31568a8e2a9e2b89e4e57601bc1d960360232cdb30cb502b950ef930d54c2c0692a684cd44b0472995bd2b41dac1553ae47216253d6640d2653a033a862f3118c5b5d60a662d240bda5f4da51092eff514f61a425c5b14b19517ec1b371d240cc30a0739273b34f18a72a69b1586802a7caa6cc8f5817a8a995695d063c9dd26c3d45feb0f84dc8a0773151cf9a537664f942f351599cfbee0558f441f5c7ad320cabe305f9aba570ddf6407749b6db42f9ce94526a8f4170e735b1dcfc5f0e090af10e039db3747aa9b4f1f26acc34639ac8b60557f7753e2c261a29852932901a4093b7f307319cbb228e26eec289898b3f8ee236032163293b8caf64be3f7ffed236f1da688d958a1bbb79dd45026884904bbb936c1ebca7aa6b0c68aa8b667dc1575729e4ecb4ffa82ddced2f4571bf902c52fc4a0ea3f47aaf5c243ac2a1fc19f825fde5d9fc8d06d97a351eebf4ae1846aa62554d57cffdb3f3377695338f8d598d723289ff3962796e8065632e7da9d8dffe2636cd23eac15a60568eefe3e77c561906555268cfc1e9342417b1cdf090cc16c79939b15a9311b0210094087dea22833f74eb0e35d44259ecf327dc84f3f24b8c2bfce7be0d97e00d2be88a150a0d557ff963b4cda60eb99935951d288768b4b2649b717133517f5e3909744417c9c3102c77ddd285976cba2c89e2b4f297665632d7c8652847c4625038a6670169772de0550066ec6c2018f503cce79a333ecc0a0632334df6959d2e3b052fa47c5c84d15ceabdc80bd6be0ea2a5a8d5e374e0e9a613369ca8d4cae3d9f98755560b27b2f6e47b01ba390f5ddeb732c22b12abd225e26ecdb639b08f3237e488430b3b39f0b63aaaef4907cd003a8f2b4c3bfd721d6c3fd3a5f062d72746606a529ba34251ddec4026f40d262e9d527ad84fecf5bb2cc8601c2a38437098aec2335104842ff1c455e5d17c136ece8d461d7a3bd9a60339c22d71059e09b3603c0565c0345684893b56054ec4d3db0bf15546cafb4a03bd7775c3157e7676bb7bdb7baf3100396c563eba1a12952503eb6ccde6b6d0a42d456743c4ddb97f5994fa08c5fa41315080eb6b928090956bfc6252b232f6e0785d233c3adcbb9370b59c35b0dd66005d516befd1fc843df8e68fab19858b91e2aecd1c8a88b0fa3d4c2fed2995ee87e65976b755fbf44ee183f9fa08848bea325807bce0b7b61e03e50b2c7af9b360532a17a8250cf6068fef0198738c82a5e58961c54017e343fcef7076e823d63b4deee472fada7989ca7a213d06a4e3eb2d44b16e5c94b1588321cf6c45a5a792938b058d667e1730f8386dfedc50ea0a959b78f12f2949b34b181f90bec622515227dfb8a5f6e89d2e559c0ba686153b218d2c50b67503018e22914ce9b49d3bdb7cf38172db1ea130baacd640c111614e3db204b3b50641d8978dc14b2afc27a7efa819cac6bafa8166d1c127e2237520d57ad38a80146217a12363cb1f8a720e328cd8f846d379ada43bd4865e4aa633c479bd448d205b2e43befa63486c717af84a733f1dececc127c047850aeeb8ce677612f5966e23d92c1d3c758aaeef82f862c1154fadd6766e1dfc780bb447732a5968c0c78b9af4a9d669338458b57cbb77910a24678092857c0b903152035bab6b1c73f7b667a08cd0d31128888de3ff1fed24866eb60beac19c1b139f77bf0b9332024999a2d56975e691fd7475fd93622119d0d725bb99c1d6ac604d6b6be09d6d29360fff9f84e5318259a67fec08a006d9772b9410ec6abd4cb828b898c625c2fc35c19cb9a6cd3b0073baec7b5af254d21de8e209539f560bc80ea38e33658a68262622cdf35dcd6618b9e272ac3644c91f27d372c6297d8eLine truncated
|
||||||
|
"0ce980442336d0f427db869a6799baa6785b5e030567c588e2a7d2680e96c11b7f415fa27730969e0b1c3973b5f3192d4e773153def6dcc09dae29ac44eac7c42c2666a356fd4262197bd5cf6eeefcbd662d104423ec05c19a2e6ddf1834a3445a09e8b1062a1320a5e8ef13d6ebd03c19e1813ccd86fd68b46a",
|
||||||
|
"1ac8a509db7bf4acb80d8d394a5abf47c273b2093f50f35049e749f3e16cb0fb",
|
||||||
|
"47cc9eea11f9f3f9aafa23bd",
|
||||||
|
"088888333340b3a057b05491fb2402301c8654948aa6d5ee1ec75eb045858c22056fef0873d6675f897126052923a47a30675b266ffb6181cbd29ce2da3720e36a227e4c6e53328d789913c0d9cd149a6e49293996b1be7d6c513b24d876445a950e723ade3efc36907c840b9b8cfdb1503811b4044d931a0009b381fd60a5bf1e73d16348cb57eea672709875fb9d56908dbc729d5d7d322a17a41d0f62c9af9a013ab1e19fb7b6c6e7fa0c0b18bec5e3d3e92546c77e3753193389e5fcdb6a6a1896cba461343e71ef7a156b136b27ae6f45be9368301cfade203e9b53824d70f07de9abfea1968b8ff8489b9804422ba05ac3c3adf23ba0848817fa51febab5e9b5500100310479e710b663f064c1ef101c9a5320367cd8bc6e52081a32f070e7d3fd6f4210cdffdb9fcab1de4af5b06a7c6d191dcc12b25b3053e58952bfd1f723afbf570796946c1df9579ad14ea9c8c30389c1de4d1e845c764fec5eb8faaf4c558c5eb5113018c6a21ef653ac7d7f5b6c7e1a8fd48c6f423e9913436202da176a86731287db7331db055508acc94168888040ee37b3c119c8a0d88360241d68745825fe480324a944d56e7cd0375d4d33a5fe7a3863c2aaa899b2d24f65b70bd804039116fe959c32442c9f0b5470463523eb4336985b71125fe5235cbca0c88a6f92416d038e144de5ff8ef6ca749a9e239f02db505bff8e16fad1cba8b1500445f067a674142b6413e9dc0f432242d8301879bfc11fa86d1ac9992ab12319fea8b703e10a13bfd4b017496222be26b56af3ef67610f904f0ca8a3e7cc249ca8122735a542b289f13922904ff23dd197f8883c7ac77150d7331316ef94e0cf13b6ad95070420513599100b0a6d117640b781c622ed7ef7ead29476b3c835bd9dbda2203930bcee7ac01c3b9c89da405ee436ee652ddcc3e96c7f1a94e200eec9a4a226f3cf7ae5725068916e73b61149497d11dd85157f895669f51978d1bea8fd2afabb18d082365daba2682ef623109988b7d0e27ae57bc14d86603f93b5ac040ae52d8db404ee27e6c34cd4246f40eccf9d3f8637a4615a4006918b01d34709bcbebd02ea72958d54db3e87d69e6d783de2f1841029d6975eb11f9b076c247108797d5368c656f888092b82aa81aa26e164e038b359bd68801c22fc107e4083a9d85fc254b002ece9d4545310b0cb22ec1af04a7ee31d210ede4b605dbdbcb70e4301989422ef46edf63f9c96de9cb3f70638b51df5c0abe79b7af8cd97148f2b7bf394bea0f7bbbf6925f83b901b87a6079f2c3b38a98fe1a86dc7f48bf97553701834f557451df4b41e7db984a34432823585380b45c1b84813d6aa21107cae252923fb4673cf660a541e65610ac0127d238285f53bf329b62169f3e42d5efe268dea62578e97da59a58a1314a1bd46cf7a7cae772814130b51411082e30062fdbda1c9e14d6b2bfff89d0379d32461f3b8e833b105f6a89532ae748b5fb43f283fc86450404e8befb8442b65e338aa0408303a70e9c27a1d923d9f2a06e7c6159c50bf2e3ba5b035420ecbd9d0b5fae478eb1ab72fa714f99d00188bb10e60380fa3a3a318c2d359ea3805c2fa0dde17ee52a504f70d6b466bd38d1dd4196be336a9ab4a9e573d1bc6404018a119f688c1dc2a8ed1433e8a8ebf455ce3808c245f0220f0c12d28c771757763bd111ab829294e2429a6f7a59858dfa1fe0b806e986d40aaff934589fefd75ab91097a979f26bc9352267efb2d82c4738e4e6c451b0d5adc398f546c646b9e6b8fc84e91651a1252d5b805a857c7798d102d1e6f90749252bc53588348ecec0897c79f514442fe3b27608c95d0cba999a7e0fbd7f601689b4dc63ecb9ff553ff12eca3e9b26e3eccbde28770bb6aff7c864ad6be77fc09f81f90df6efd0c4025d0916ab5197ab846dfe6121c462761d9cc87112ebbca197b0a222fd34a15b824b7eda06a56a6ffda760fae5f0b527e2798f01e205a3f47947a4bd190f6abfb1dab2e3a53131af95d593bb57e4f4af506440cf20636d9fccc449d9565bf43dec8b6877337ca5a43900c1dc600c877b290342914e909aad8c5f0755bc25652781535c057ed5ab2ff8ad4322a8edf3fc1b5311dae6361a7395919725f4cd87ce0ccba37c64eb3618f9c5a53644ada569b90cd07184fc048f1b589eb29852909e75e7116ef96a268ea85c2bd257cefdde9222d7eda875a2a3abcd3a02a1fb470ba967b20beb54914b8b0c6ed464ba978088d7f8b30d098966b0bde82a8f1210f5d0c3405c9bc73f703134d0b6ee13326f65fa0b8154f4e30808997d4afbd060285942ca1dededc3410a099881492b5730ab7bdc2a4cfd0068f67766d60b5d4945f121459d2083334ac878d067bef644b9ee427bbbd6c9351d7b019bfc051c05ac301ff3792a1c687546dbf6a07a0cf56717374bfa1191c22b7753f6ae02392f8aac9207d1ad0fcd57c5c8b35817574b7dd90a00cab75f508f8a234eabce6618305f94746cb6a8573389d336bb67e1b0d2b6e9bd3959ef344e1eb245b522c35222813b8c6e82df48987436b5592025e9786ca63b6d1a064223bfacf59ada713c2a3116611393aa8446ea79b3cb21e96d13b659ada2d6524686fd46ec66c1b4d8f5ae7831840c9e3db64d528f83a1cef1e0a586a783f8306cb261ed9c2905493e74d35883fcb39cfc5745c282104cc3ce804999231d13e1bc6f2c022f05999fb57575bbdaf00d7a990e17dd2f8b9dfe66a637b42f58ee49ba60f2dd9718d09d7025b6061b2087bc35f0a8c884f5b67a5e18c2b4e857d3b48b79dc7cab6b72f572d22987566238a7153ed6264578424f1ce091fd05b7f14563fe12c76104d3373367af3ed3aca694a21127b5912c0b7eb1ddf9d4a9f03f660d49f7a7f0fb42797fd112414c3eba2b75a04282dcb9645191fd3dbe376e7f60ab40bb7ca1e991053a1912854a68d7dcf854201d1f2c26c6cfaea32e29d80847e6288274713d2ca973b91dab97884326b280c6f06c65b8fd25d314be29139961051a1d8699467d02b67991baabc9b05629660c243ca3b0477362d5e6bf9eaa33beeb52cf399846c77fcae11a89cbfdb2058e443ddd44fe202a3ba5c2efce937d78b9639781b8b2b99077b433189cf3b0733ed73b59bb194c9a98c5aa0cba6e71d1c5522f193defb9e31fd2cd60f22bedaf7008c2fb0b55a8dd52731dfa2bc69b40f835ae95db040cda6a4a1588a5ba4769edfeb7369c1e9a3b1cda293255b4942881d94d771b7b82460004875e71be64c582f2830c5e80dd6de421a311c5852f4912bea1451b0328d01c7029867cf9af99284cdfc1e1f0aa0d8c19ba9bc035dc270b45724247137da5d3fc4daa09e7014fe1439889968eb23fe124f067825d5f7b304f17a983580e009e0e51630ea0006dbc74a30b512cd9eb4d0b315a0ffdbfb581609ea9661b0007cd234ce43c17c92269a7519bfe99c2ca94b5cd3e7654946e67b37d4270a369266db6804336a446022677a024d44cc02cb04Line truncated
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"0410d1f8bc890649c250a3819766f4496f339a6384e34acdd72b3a87266edd2a7eae223a372883f978277a108d6e59fca1f35f25d7a9f3aed42d35fa9b12241ac04754f76fd8f0e8ff6af88cd851887a45e89f1c9192ca66bfff605b128575d2ccc9ca3ba1ba23a0251b2cfd6db577b29d17ce2ea998946997f5c4a97a397c46024681a400a54425c071232d269adfc3b1adf15b4586c4dd7b8886f5c1023bc348bc674961ac6e221d914f432c2f06dddcf738227dfcfff88485ed45882809d0e57019461c88683919b87c45e78223c37a5be5f758e4f0dc6add22f2062bc2eb9bdc31b8649af17d526ec339f0e6fc6a41e26299c65276302f982235c3e5205ec1521625ec08a23e766577664b73d18d5533261c859c4cb4346feaf7540a56155c6c3a4874dc86ea42fd518d71221ac65541e2dadd2f8e129e7809f2835f07dfcc4128401dae2b5fac7ced1d9e07e3f348c6cd26f55b3893d4418557a18c366dcd5eadea0dd84ab95437d6f23eb9e5877fb2ad740ee507e2268c39c7186f34e5cee2d0dbba1a940f516a018f23e716a399c317a7a81f89cfabc296c432cba900ad79db67936f76e4d97874fc5f8a9ff84eb7a0f6d629c581ec5c451e27ef1ed468f93bfc68b2e0412a543d89dfdd812d9421236a4be9eb374531556c207340886c7b84d42d651557b952e0982f62c5c383e92dced21905174a5a836acdc3f2393e770d6cdc22c39575a42ea406f36889dc9558aeae5dc5f8b84862850b55bf4accccb6a8ef793d641d6b08235f70ad3b0605eab462afad1af80fa003645f4d302b03d81a7d167e9a8187bee0f76b1cfd7006b2d2b55fedad6e8db1d3ecfe031702dc327ff2b0197337d7542f42702cb276de852b3d72d9acff8a7feb8882028a5e340950e523c41cfa184b3d8878effe56742994e60240e58cbfd01541d39fa007a9f0ecccb409c6cc540354ccf35223677cb74e7ef7330bb60420f7d7bf97de6888cb343cd4fb0928fe5df5f1b018592ccfa7aac6dab57cded573b5950b94fd935f32cf332dd85b2b36501de6687612371dbcfdf77279d647ed8bdcf81fda8b7e0c5ab139330d64695d814fc6f761fd141dfb0c8f74e2d7616db3598d8de40b993fbdd272ca37db27b82aedb08bebc4a8e6d0385ab20fbc20c215ad50fab8e93975bcab3ff38667abb0545b3b3f20e325f01b80a32a3cc3ed51703d4b2826849ee22fddd5b544816599dca0d8fc84feed9f7e90caba53b70bc3f457eb1adb89fd0b67d2c0ab53264430c61d2c4a1b19ea99a9b453fc6b5ebf5fb5ab799134769c9b495c479c828bcc49a8f993c3127d5cbc31afb89c0e78fbc323755457ebf0f3344d3ad1cfc59d186e96ac31a9298e655b3d1df74b95f30fb868631053540388a13d597002f689708d35a2365e309bb96db8b1b94ea4c8060c2b165f7f19e72056409159371ac9c44f6bfaad9b9567094d18c29bbc8aa2c8b5b82735d20f55284fe68186004b4a4fb644fd52d9645b277c1dc238a764005c1d2791ef36e71786cd990ccee4571d9a9b1aec757e479cfa645e320bc33268e05af9cf90e0e616ae7f237c637a99fe15b4ea8a3232262d96855fa248920a28ec03f77ce4dd93925db60ec030a7be455ba9d08edbf6bb717b1a13c3ac1deb9821e21505c0a8971d5ea5dd8e4c9cd3a845a336209af191150ba5d9b8c2c450e3a765e8670d7f846b2461f971fdcd1942704f620a40f4204b99f9035bbd543f64b927cbc7a74f32cbb12c3caef955f169a45374e4479430e08d333c4a877baf41a27a0849ca3a157b6651295fa71ac94b6e3d30b5d160965e93d2a81b4d575cefd264399c9e4e17059f4064465b2d92c96ac27e3b221499b5e642d033992c236b905c072faa1e34495f9890bac6228330e4016c061605bbfc478c30e1b8534c49af54785972aca2d144328b0a540e3b3810a73e26acfa22f48652d53ea521875475ffade8ab50b9f08245fad753350f63dc4e898948ac7dcefe520ca47394f8e993a6d13ff68a2f78cf294f235f5f863bad10c4f5bc41c3ba93cf5e076357f0f7fdc136f34b656b1b8ebb3eed1ac429c7d4edbc902f7f4bc24ea9c9b200b9a9fd7adff0c6445ce1d2171fc031e3e9f8b8d6b448053393c8813d91333d4bdc3bc5bb2b8bff876cd29e8b92cf6f7bc727517b6f57ae031f3040b0637dfb40b8c1fbe44cfb6bb9cd0a445fd9b3daa1da2b1c4a82cb4da1fb8d525e0a4d9ec30e9aa75b951214621c58c1f60c9b97e6c6b330497e7dea790a3cd8158a76d898107ff3a5910707ae60c8a46c633b522aee83736d005de60b9abe202435f8bc4577b0eb08b7f2b617bb5a831e95d6488459bbf15919d764b39684d7cb7c9310f343fbfcfbeeb212a90d96c7a26c1026c5cb171ee4ef839785076e5084026077455c73404a2653f333e9bad555cafc1a9613387a02bb1287c380d7478238bec8943208de585bd18b448b6099565cb3ec70ec6672a778fa6af9d1b17b0970439da24c7bfaa74c85ecd8e5852e42391ab2258024ccf91e37f2f0e86df958b197fafd12f4a45f7990375f1665a14f7f5374ff7740f89677ea8660587fb80916b30629a7aa88213bbf80512421a0a37414a2eb549b81cc85072cdd87e4e69d97ecc63f974e60d20de0233101c3d475d777602b12e2f797e9237570085b0e9f48d4dedf233eb1301ed4621f9736946eadf599bfd79157c0b4cc31bc273f5c6f133a4e3679ff6797d3c9b76aff4bd8ad40726c1703c3d8b78f0974b748d0265b0a75928374f91b48c2d2b2c11d8b6e5efddb75009e4db72e562be59efb0bfa06808c89f585a43d4776ef08947a77f277526777f0b52f1e0b5a03aa560fa45c8f30e584b58ac1fc00b104942b7b86a3cdee1abea349dcaea4e058faeffc567e2c3b03e1c5c4ddc675e25aa15de1442bcf5ee972a8c5204ca5794694759c13a2d716839dda61635043bdf1a09e35cb6d93b4df3b7a00871f79cdb4ee69c79041dd14deb7754107b8fef8589d2d240ac1d8eafc52ea847263512651bbede2fccaf6da816b1b892319817bb6af9fc17078ab6cca95f03cf8426249fd4f2bf91921d39b8cee24af07a52bbe54ca7fc4422a310dbf2149b763ac0060fb2c59154d2cb0da1ad4892279b4e0ce7f5f92c189c3ce48e518ff48c4ffa9bf2b02d4792f84534958dc6bd2914ba010aa32d133f6a07bdbb87a237c7acc3ba5cf101efe947147ed4eb3bfdffe5fefa991c0dc8760586218d286944c52d0f221e0101f74826761d01a20af187f9ec1115e9e98bff6fbd7c8816c15d33c07f51c171490997bf269951218ae92b66fa3150d3bd40336abccb717e18b53e8806fff94009910f202a5041b5396d1c339e6d075bad4ab66a0637d81eed1696e4068024001123204b8371f0bcdf0ce07d79f7c917327f7138a75947846fde68665e9c767fbf96bb3308abffe7a8d05512c81e39fa8dab2334f46ab9543921ca97be31076dc7b2a0d05e90b7f7610d1a391b442398ef56cde3b1873Line truncated
|
||||||
|
"113b261414b4b7dfa028668ac8b0cde5734120124991c54f4dd16a87d181efe2bc15f6d0caaeaf6ad615f59ec5c2833904a34b4d34109c82e10609b387f995430e8c13d83ac34310d838af9efa32d7fed6224c0a33",
|
||||||
|
"cd762390b93369f1e207eb15deeaeb0036f5331e82480d180f84a76c3e44550b",
|
||||||
|
"e88c14ef96c7768f5dba9de9",
|
||||||
|
"8d6aaa27892a76fb05a2e96cef9a9b4b7ae0670a12cff95f7b076372456889fbd3b9b4fb5fd98b3bd85b247f15009be2f4e7a0329dd118b6872199b314e159618ede0381dd97db28743461ace1a694c0383d8458150a501d6c45f4b50d5b1bd47e61a51f9ed4929bf2e564f201ed0e6825170027d93e482c1ce268459d2f81cab41f0e7ff281430c16b34a29b5c76630dba72ab9e751bae41122b26121d91f2af271a23e818263f46e05fdd52f319d58330bcabf66637a368c0a8aeeb20cad1916d966e5e0b0de74cc67ebe57e3d1fe01e9743d42a931cb4b98bb762ea43ab937d1e5c42eb08fd56e70e911bdcc1ca4ca0604a329c5364b262ce2de282b4732ea657b89300cc7b7127ba4a2d08c13f581f024fd093ac09c2bc245be60c80e102405597fa8082f4d28cc954a93217edffaba3d2a397bb59ee89c8cc0f33eded78f21183bd1acdce64a923dd609a0620d2911f61e81fb2c8ccad8ad9d81157223253a121ea2bc60d6a3670c563fe06bd75688572b3be83cd31dfeac6b17cf8455267b481219c42034b2252977f32b8e6588fb05166498fa37d17c2b002a655b5711bbc21175348225fdcca041b1f97fae48fb1e222c5bb46b5202191c00666b7e1b2d84aca3edbee7a97dc0f6d1330e929226f8a76c155e973c1ab62c867e1f87be37788754e51825ba31af9f4722b5782ef782fbb70c391a664f252d14e49a805e94790135ff6bd881a687f98b42da96fd34bf240eae4914488af739ec15f13f048a7eb5fa94af14e8b6ac5fae714cbef6268b114813ca2a3920a7a9d5eb506a2ca211758de292047eefdb5a97e18530dcd8410495fc42abed91b1204d9b8ba9d6aed11d2d0fa0d931d46f93f2c1a560ef9f5f7cee1497be770d3cb07c534215cec12c1458bb57aab4d95cf4a15a5e3a3bf8e650206d5cac4af3193d169f1a57638d9a50f6b7c6985d42f7138b9226451670d7359351c2affbca65680557693d03458341198b8e13d0ea6abb7496edea3cd4dee2eb93695e668c7c0901c6809b8ef434e88b85a8b22cab6508b9560fae62900056b7c5c29a8c899bed45a2b5159a1d4929476ef350101317f77f02d48a039cf4cf01c56319cbba16fe908c49ed6f3face88867c0ad3703452baa7b86fe58a00ab8f740b4e8055164b0385dd3fa44502ffbb99cdd843bc3287ea468aafe4cc298a3fc180f284dbf78aa09e0a2f7d8593356eab016ad8dc505420edd376b66598a3d0aaa848fd68c4e07419b8b50e40febe2b6b17ad07726fae1f87e86abd01490a0ce24fb57b533c765504ee0a9ca154187bcf5e6828e3addc7597532643cfd992558d63b1acd00e7aa41b9765094217480c08c43f4f0b3f0127120699b7f2a5ac07c655b6143e467777cdad4bc21d4b57da4d8f9b9a7e4523d8c6fba3614b7f7281e80ff0f9004577adcff1b79fe443c80ca9655ecc102d5df6aab2ff6c3401f344b77666c59ac7d5b92bf4f1e2322f74b75e6ef2bf43ad9e018f164ae76a91451e5221bdf5b65a4fbbaa8dc31e6063b451edbbf4965307f8e65bfae87b15f2453083bea8484017228a9cdc6edab1a28834eed8ce07430f776b916b3bdd2340798955ce9ffcf114c3f6a88bcc4c7b6f2e3842426488c340d00f2c4d2d6fd3b6263dcf7a57f5cea6c77efba7013297bd3320accf033acc0833aaa8e8f95cecba469704214f54a1ed581349878a591f9993371f1daf92e55b2a4faf8f952cf785c687a59b3c258daef1b6d7bf9f904123c7384a859933c3ac31e33edf648a1be4d6264ffade860915bd118f0b9aaec2eb8e16b2015fc25e68caac77a3accea53b9b178f6cf48d15029fac12963b4277df037b7a494cb29b1d9e6d2148531a1f7360519cba5657c080254f130a1cc3ccaadb4298d7ea0223897e63d798b4f4909577cf9b491a82de0275a246bb1211bc4144574c8ef176b382262c0e087975cbef33cc616d32e0131a9efdbe8ad3d9cb5f935d3f4f409852acca22ae2a6e7450e9a426ec3b9183f93b4b7f89d850e1c7053c661936e0cde23e831a261b319b430da45772f0fc0113679d06f025983bbf37ecfba35eeca28de5ff4815a490570491266e92faaf8d0ad4ac8df106faff8fe3c8d050ae9dfc03a01ad177c21d7b653509a80369a668a97eaa532dc9867c32aebaf89ed36586e1ebbe1045347766a354a86ec1e8b2f30c8fdfbb6c5d549e7a84db81b73fb828499c5c4be0d4b2b7ffb197133a0ee18abb5a4e371be0ec0a6535507029316f8decde30833ca47493ffcab781d028edfb91c138609baf1054ad52a5d8ccb98b3ca5b138f253d99bd556afd80f71b39f36e0d96fba4e0cbdb18926894968aa825392f12d98b6497ff85a0e4a91c97f37ba1dcad30fe688b54008b925805104a61dc22b712685202ecdb073fad9b10b5b9ee2ff781f23fd41ecdec87f85b369a304b85bd2af126d08f79d8a9e2bff0b18607a95c4efe35941c5493c94e3f2f3902e79f4cfe84c138b83c7f32d7c5a125b28c6107921e8ac92f1af7da015b46a2f9169369cede770292eee8a5f40d080ea1c267c33cb7d4187093d486dc3911bb2d6cae036cb508e81ca783ab5e95cec751e39f3038003081a252eefa7cd913baf136d4e27076251da9cbf0c7d2586fe02b62ec786790ef08fb3ff3d79bd06868eb1abd9875920e14fccf6dc144e898f578b7295fb5f4e84cbf683722ce3597aafe3195e194736fc317ed03ebbb00d956ce89f7a41a334020e1a88da355d3b47d5bd3965a290f6fbf5dfdc8c8e6347b4eb85151e53a960311582235f3b546ca80a670dcb628fef572dfae0c101bc08c80f78d5630a793bdfe402592c316227f2333b386839a67e6ee8d9396fabc9648ea656a407670efaf80966034958f4a70fe7b920c79dea3d5a0ff05f3ed0516537d51a686efcb258520936fdd415345251c9ac1143a41be295cf12da5d4319e78e1c57ce20507490e5213ca7be92afca8ec8b6a07b33571afe6940daa2afb0dd4dcc1c329474ff8e13d740488e5ced552074fff695a04fc1b70755245895a1e9c387fd9514261dbb0f600ae03f4896e795d1e72f421d8572543243d662f6811eb9402b6a3b8dbb0f32de95bb1ac01b1287663d3b6a3f52339a4f6b27789e15519b2b59f2f4fc8fd33ad1a6e4d02cf0ddf8499f45746da424ee78e72847e3cd3833551b6e6fd6b1aa98c688252b57a1d97660ff006ea1b970a0b8fc7d2e313ffd0b0b85299ded47b60cd2fe9bdd7ebace4b0c1072cdf67231a475045990b35ec761e1dc1dfbd0c402296566eb4b9462979d33c9d652a9295ae70943f38adb212b48bd8ebe82722b1712ab6a3be6060297e2aa54e7d0158e4aba6975237e7c7a1e22b29560b8d262125ff2a6e5c1332acd0f6b5ba15b4a82d3631891a01530321830aa8f2e8ab6b41bc5b5356957a4d0c3bc3eab04df7700305a95d0f9cd18d486c675c963876b25b1a0f78e245deb40dedd14dafdaa9d614fb06eb2538c5411e13be116c76fbd3377ff212eb07cLine truncated
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"9c73ac05648e0c50a3ea3a8eea70841e8e06669c1e7520c5e25e093769c4b005375c0a9cea16ec8e00261ceb96a00924a66fc0c4e4e089c63e93fea857aead8e0ab82af4ce1682cf3c9fbad23fc3f7e632b7aa169834ddd6c7db7e1e892cac93e4d787b2ed0a812aa93bfce8fef3ce30ab794743ad241974ff989288c43e1ba815a25a03acdc2d5517293e161d0c46c8858d0b32b124a6b0bc3838807753288cf6838fa25fbcf876e6368c0342d3cbc860d6fa12faa1c2b7d9fb37504e60dd44e36ce74229dfb80f1545125718dd1f78b31a8aadbb4d6494489ce596fcc2dbdf2ec22157a1d966b61e780d36552daf084739b602861a96ceb67b65b23d40916c02b2c3a38c2a59aaa266e1f8939000dac9b6dc50d1731e87ee833a2cc3cb98c57e5b680a85c1b428289520bb252096efd7723fa8e55d2fd4e16900a435986ab3f3d2bd799471a1bc07c1772ce10d1bb8805a6065b8903999f9393d2ed1a7e1c57a9e3e0e10dfca17a04143814f5f3acfb99a34712a6e0a24a7485279ef343e69d27c77e25b41f9fb833d7cd29cb6a15551d5c77b43d19feb19f2640926a272f81eeadb792bd474ae11f080ada72103f8f7ca733a9b1325b50589be2b2b3023491afec246d336f4e4277592ce9695c68d5f39c8fa4cedaf51776d7ca29ea0ecb89eaefe71e5f3560c68e8dafe7da08cdcd954d626418677b8f3f45b9194474a32f548a4da3bfae6a3e2c0a25f602e3b3a821160c397d77c8bcbd71c5f1e669213af36eeea30d48e12953071f55eac2fe0bd8fa355671fe032f6fc9214632428125a16fc8aea8a9c7fba0d7518b9a4f876349ccb9bbbabcdb2a85fc60b83ee1ddd041967efa4036e5e10e377c9886f40bc0b0b57c7b724795f843f6a072e87e532a04c21445090a360731a2afb896ab795750e5c2c33d58bb714f5be427ca3751df09661402604a09a1eca95a8344d3daa5b99d68e6e6245825704c5d4a73af197d052d7f75778917542261d77735a21cff3f75d6159a3e4b1a7a9854ee376e6b3c8bdaa1f353b957862b2efd50d10a40007026261a546124cef979ad20d8085d53e30f5736b8aebcd3cdaa349ea474af249ac53eef2653ae1fcd5b3095538de9368d307d45df2a19acd44e3b78c2da9d5d9fcc4cb61feac5dd35f66299845bc0018c3d476b6761083baf33a4621e41cfae0e0c642de729fb2d206db6a4b976a635b3fd911b5e9946fddceb6feb2d2f893b2bed590317442037a1d6dc5b5d72910160221cbecb53bc983f1c736c3bfc9757e9e05af1248b28d651f521af67b2a0d7e4bd86a0013338404fabac7b9833c372142e6338a98c0efb7130aae8e34bb0c80937680a7a904aba3be735d41af9462f17b967b13566bcb697579f8a9340429c77baa6e24ae1ac86d8d25ae3cb9112e34a7a948fd141367898c5f33c0635c87de06f603b510cb229df0d0d9a9e107de88b12686c539ed4fc54c8285afde0c8ee502919a125cbcaf4c8c89f56e90d3f641f97c07326956f7b5d87c65b689f39b8b84359ee0f14d2c7ed621ec67f5e2a8ee5faf21c805187edd95e3941ed62fa95a65473a569566d46b87c0d27ca37b6b022a8cca30a4480d392ba15701d1015b3648958cddfb614983211bffc4966ac6c1f691f19bd9fed405a02c06712d62a775f73353f3949c76b6b7757a4ee0410fd6d20071abfe46b09e72b70f9f19b61410ea67037e037934bbefaf09cff018a5c218176d165d1eb5cfd5c46eee7b82fe65ea02e3ed7b18a86ac7b139b7c9df79e1f6e6f85304ad22d97190c7ec12c651fcc835ea434d92ae1444e7cb0dc644efbc2ae70f2f94310805c1d0f2d49643d05e78baa1c54d4fd99137a49efde88dba1374c94208fb4a0ebc1a0090b043610ebc1bb08168ff5bf936ff9834e825eefb9ab73da2b287b06fa2b0ff52f46061b07c1131e4108cde478c767b749b696f3520acd8d3338842d53941282da289dd1e9a0e02aa9be0f127566c9bf2d50a27f6b6ffc9e9880bbfc14ce7eeee70cb0c0ad90fb474efa69b46123638e8405fdef65fa7e0e7b29fa8fe8696edf661f9003a08b4aff85a4a3e6d817655c1d533b834da981b8c37c38abd5977b3ba71b3f57967a471c2eeaf2f6f258431fbb7e92f91814b1db80ea775681f282290db170942bb7b04aa2a331950b74a4b6e337affb4c51c6cd4c4e13ce3095e73e4767c2731f72bdb225ff572163fbd8573378427fda194d165750d487f6bbb63e1378a132fb6ee5115e3c32b2380b096b735bdb4d651853bc7928346fe3ea9df7534f2a4eae1f5ffc4b82ae738db7df0103ba4e68c2a2153bca499bae2439a57778cfc616df16032aa8a19e26597d275d2775b5ea17cb25d204b18028eb25a053e5666ac47c6def151f7d4b68ea62c601d87bfbe04711c24bc34274be6815024d7b7d01e7dae10cea6e485348ab195a83854663cc5826181b688cc9c091dc1e0d491fe51400e20e6f2a51a7d56af258e038bcbc80e2c4ac4b41661bd33229d07b39b59f3aa79d99c1ef41974a33e02a7cacd6fd8f9b99cadd0fd6a031f070bd3a364c64ddda0e9fb94036f374171de0b3f4ee3380780e6d77d50db9d58e670fb4a364827d631226a3491a27602808141ce657ad6e560ad62b088ff086e6f03b8a64bdf7c7d01e7b19289279509a9d6d80e50aef3b05b5561e4556952c46d0b6ab8eae735eccee77e570e1360b7ea38c53ae6b8eb420e4c2663b57827228392db6e79105a47f7d89e06ecfebdd63783101d3bfb5f494785acfdfed41f8166faefdf0b49260222c4080ec2c6e4f949f41784f076ce37fc7a34fa4e547bb44e6b9359b4b95cd67d64e4402ac83973bd50f8adc7c6e4c34019bd8f6d3843bba3d7155890712e0ed5134e00db877398d86b459f312a6272431f01b057446bfb1b8053acf181bac79408c7708f3a0867a64e06d7786849bb874a6bdf8fd6daaa572d5648ae100f4318d6b3a811bb0fb709168e817ed83c0622a7e5b17ebf5cd5ecb21d9ac32ddddb039083144c93cb55a95ad72732132d54bb120639d1620ebd142b58d75835b35cc6367012c93c6772963e9ac852c71c0dda2246ab845469997fc170d8f62334bc5aa4ce23e036967674303ec6f75bd3d17d197d026de69beda70bc59d2ff95a899d28ac7e5e42f4d37233996a8e6d3b0b86b80df49ea8e145b4a6e3e39f3d6c3c6518bac45baf97cde23037709d737b242b8918ca31f90fe59ff2c83e2f347a954d3559a8e4f075c620ad36be20b1e24b3afa156cf3255192171ad0474e4adc9b7f35436325b92945665f038611e5d14bdfe7b7d20c09642323346a717f460dfe7b5062a0098be66febe9f5fccfc747aeaeff81ba08e5dd2b1a489c998ea9970afaf9aa03859073707a686c492fb3f7ddb27897ba5e75e578bd82114b2ba85525a2002927909c970a04035334b64b1169c3a923211e0999db8baa26b6537cdcf57c051c0ca1b317a5b66ad96cb5ebd57994f99ab202348d8ddeb343312f1f26ab2442Line truncated
|
||||||
|
"bf96bbc17abcd1f56a9f22ad164d25ca72f8c996f1a7a66d6effe140336da4f20460b47e1c8573872496343be35a055552ceec437692b0e4919224c4ffc8b603286a8245eff5cc148b004f6e5a54c4ac22b0f09842a07cd332a09732694d3591b8b7d6a7ada2bb38a30aa7fd5e6baa811b9a195d3a96306d",
|
||||||
|
"aa2f714d3a184a9883f4199e8e33fbc9c92b36fff2d59f07a9d0d335d7476e81",
|
||||||
|
"36c79f9f14d431cc8c077439",
|
||||||
|
"873d0617c986dc9d83e9cdfc50b1f916626a9d9e1c595dc7ccd99d1e993d25d89b04a893c89e205952eef8f1733054bbb55fa5e1b07135787d4fcfae226737b50cafa2c11276e8708451be9b4d7f662e98ef6b705c5c4fc64588728eab1dfee22a0a92bae61828a7394977b0ae8a3b6d0126a23583fec025becf0a72a28891391ac1495732a7a4a1d43a63ed8eb37b280b6d886096fbc4f77aadbc5e441e996334d0e10cd7f3dbba9bb7efb147297986509a07735385c681e0543186dc166291edc3b4664f5c8ffb0965c85bc30ff5e7769a69609c69ebb68f35d104bafe3dbd3e2a40e13865f19bca3612e48592aa930eaee29440b4ebc1c0a59f1c54519857c929709b086bfddd6d4a30940b592be48e0067976099efe71f45f956182dbb300e8076e1207baa32d59c1afef7f34171bd66099d2d7f07b39d16d0f8b085185bf2554c6ad66bcd656f07979e8f19575a116f5c4fb9700ec3b46a3254f28afa1ed51348c1af6dba26fd398098a76d7bfa2ff195eebab41330ef290bf75205a2ee570a2fa46bbaa74aa6ba68a0e63e2731dc1974eb44794f3c89ba58cf96f7a070fcca678185711d97cd9d7d8202351ed589e0b05a7a190e60ae4aa109254a7bcf7013f8addd07a64145e21226795ff7c7b1c225f40ed7c3552da8eb18b9bc9bc70c2e7ecb10c8b20c54f04b6e27b5044a7a67b558407eb330f2083444375c022565c45fe817dc00c7d24c23db320d15949b0b64fbbaedd310e73e423fcebe6e1e98a5cd232d97e6466642e5e3b23f06525ac1cdf8688650cd366b1b7ba2a9033e62d836b14bb73717757b76b9673671bd3d3b2a56628f5a309f3b86ad32abac0590c50f7c5a22e0a920d88dc9fbcb3add08b900a2a2fae4178aa100a0e645ab428e0e79bd90baf4af2755e48262b64838a6fbc21226e323c0a1ba5703e30738fc7b5a7df9eabec6199df5ff6ad58f9df5a734ccd6509e53ecb3de1c881732e26e52ab848a0335b04b25f2254aaf8c130c78b0c9a40b60d402673ac7ec7311d0b00c45bd176bc73ad81c2478611804f59e3c145110aacce922e473ef346f8acaabdbb9f313dd3f8d0a937d0c048e5af789e2e09a816146f9ea28170909caf2572a2f6e2d0d511242909de2815e9ec586b2d12183ddbeb7dd70f32424097e2ec28b4ba62cf78f547e2057a4c050cccdf6b582172343742ec8c85e2847efb1595bccf89ece3b3ebba824d2f097b1987ec26c6e5710544739d54a714060fa91b7995cff0161415eaf55758078772c0271d9d282354e47a25b673eb11497a6ed8db82267d65ad47412300ed525af96f943c5336b1de88676dc346e7339230032463d305b0442f934018bdf0242768511d20474c6ecc82fd752c0c0ca5cee1f3e06e679fa5835540f97870d47ccc6bab233290be7a3bbd4a73f1dc7682049bf7b3cbfb6687479c18d246e3c07161df5c889ee95d39cccd989625a8c9e80f951f8b1832f6378e05daa8566477d7fe547e49ae6e822a68de4df9fc4d6500d5219c3d3bd8887bd7f695151ba378da17c2e750399f7482973510a386721c59683a86003edb9f0ce1ea89bd7bb8a25c222df7ebedcc1b56c8ce18f367b2cae720e0591b477f6ffb498c3d7ce59cabb1b01d7cba84d7180b4b2a165d4b889a6ac361720e768f2913aa50b0b5c88e55c35bb4df4fbc4460338809605f1fd445a2bcd97ec1d2f269b5e779a18c8f215bbc5555c745424484ee5436119eb8754f5e9e91f51fe715353596baa1fbb0a690e99691636e6027cbd4b7be752bc278661e2677070ddc12dccc262d3dd47160345de51359ee8dcf2f61044f95dfdaf323881b2bbff68af6572348f786f6e52d1309cff871ad58148307d7eaedc93ef037922b6092ac62171433adc4934884efdee3052ebd60ee115f76f9dbd0eab7c4c0a77b4ce8078209d23d81d957335f331965b556ebd54732327b5aacc899f9ed0edacad9eb98cb845867f249efb0e1a5fa2483227f78decbf7f1f32d060ab0c01eb985d83920b2cc24b5f9a0d5d869e980129d3b78277fb87e5cda61e340a729d86b6617b8828dffc7c37d4c38080ef3515c2784935973dd184e0a8160f84bb78bcd8a5e691760be4a4d41ed6512ee436ce24650c0e17e7d74b5e01cc39b21e21514a84db262d673f24a82cfd5dfe2a162976171c538b24af16429bf8ed5fa8e37f89ec6e7d63ea1d83ac1087cf89e8f43161f225108889e922493d973e36b510074533cb1cb22174d21c4076959e4191a5df880a8b868b95a9cb5151a7ad47375fcd87725660cc0b59c88ceb86984941268493c49b8aa2baa8c531ecf497853ffc3d26b926a379e72188e246d42073041fbca453bd558f328881c8f8d9e099e898a912530c4be499f2b32229c359ea10e0befe6d94cba5ddafe51d164898166e890b22fd1eebd5724451511dce1f8f7431d712a3f1e50fa5f609da686253311af255b84b2106b09b803e94b51729cfa0826869945d46b9606547e7e33fd9961cf15b400d0f5e01d8fd4d92a83ae526934059d4514b9e0005317a70466aa0b6086d5fcfed201d958a0de55fd23f0919ea29b8aa02440031a9fc206b9feef362a73430a4204869354ec81b6fff92eca97e7f1bb12d25228eae466b8137b4806895ce34b57dc14bdcd107fe160776b0e5daab150ba06976eb884eaa574da393af4de355381c7caa4f611a2ee70a0c78df93a4276f55e6281997b4aeb36888a6d9638cc95444047e5202f41f8bdd787f1ff44a648cc7d39f05e49e5d6989fedb194c526780709763da81a780db0d1534a466cce57e11dd3a4c0e273d9873af1040d52a90e20101e1f80ef296d45769d204cd5417a84e022b6b336675d36d9cbdb16b0cbb08f5e240012967c8067c92f97f981cd19d449084400d76adfb7c610abb73bf21e161db04debe6665fca79d71c8cc50adc3ecf0e52d07773478ca97b8e9821a5704dc58acc647a5bc618d2b681f17942c46c266c73ec211ca403a7d47e42e12c775b370cd500d70a4aac7124f5f6d2d4ca78e1c17a96426c326bb60379ceb0c84a86200f3b450e5e9aaa11f45440f5260eee7675a8b9c47fbc58cf18a651a1dc7b39a911442504f12c103054bb50f15381e512dc6e3af7b414b3db26fe767d83a2a53d7181fec8f6b196c7874befd6628b31797ee3c9260c7b7853b137893e36696e2a47277add98462ea9a0edeb7d2d3c0f2805fd7db64c2c7eff353ff2b36f4de862a42779ffd4dbe77b6a79bc9f4ea3e909474ead915fa3fa990bc82b83a670b163e79300b627fb91c4502e96bb9dde00f716ae6ad14dac647c9f7c2e5b2e505708b5fee996b8e9113a8f4f2caaf414061ee72e76b8bf47ec4f781bd7c589adebc2c267448247e30d659998d8037783494a1fdadcc819d7ad7ea2674f75e10639c3d3055046a00814ddda0e463185454a4455d60b9780250183d591c3db6f27373cd2ce4f02f206ae10a8c32d71226e7cb8d5b05909445977164983c0073434d6c0f2bb62bda66Line truncated
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ceb1f819497c0d631a9c9616655f419b5e3470fd3b19cd0e4fa556bd26cd9df57e960ec7121b2a2cb7c0421c1f84b77eb8277bf341490190ee574d1424eb09a281176a933394bfea5502077486bef23ee66e3127b732b7a58a04b9aeefc35170dabb030d4fc3f8a4c5ff194bbd0b89a379baca30ec81d576868f25755276e62c31e93a80ac322571313ebcee494592c3ff5cf3ecdec962645887d9aafdbfd62ea910af5542d4c7731283625bc9f41ec85012b42edb1792339e6cdd9c2bb3cad4c4792a064df17a5f74dcbb3dd0d90620ebba4fc6d1e1f9704dd60c798ad64d4e5077549d68cefdddaab81a7a91209b7ddbea43accb3d1c191328929dffdfeb4f5740ecbf0ee99cb9a1b73333d7ceb0b2b8f35f84307b9d44a42fe1a30ecdf2650dde251bc8c1d46978089c50d64c028f40611370ddb0b481df9624ed63165370f4788bbc396026b268c2023e0f04cd4f66e0bf439074c46f0ae85d6dfeb0ddf22868af61c8d5133097156fa61a3cf5801db5c3ad29871d336f7aa06d2a7d5f52e50eb3aee3c7de7bdc4d21f68a1776a7cc3954f5c071282febc89c1545fc672a0a1bd8eee2b769be048ab58ea12b356d658a6225fb8a55e752f1fc97ed64c2f87f9ae661514f1f56d9d4e47b001ae865a44b8a9fd5df8628d183bfbee781b6661c9cc76debe6c3c5bba840bbc228206673aa05498a8c715b0f3019f6b2d05cce6c233b5809ff1dc4a75d7f69859fcff94ad442d460b32f6fe348659518c16385e49fddee9efab2455732aedcd17dd51b5117efb2ca1e21ae6787437f48a7042d46e11be4dbcd2932ffd70fd154e4eca5fcdc57c6fa79746100b8e1485fe575a5c79089a25eb2d55d89e42eddc81b82c4f7da8bf153ff5353b7349b161911bbe0a14483fff6585d7f3c8b5c04a6dfc99db9548f0c53e25f0b16fa212f0bdd10ad2193ac18eb09972795f42b3bd3f4d98c4868989c4af7a760f1c88ffda59faac73256df1d607644f56a70303d6409c9ad716149bb58f01b4ab8ab475e4af1257d47049aa77adf9ce54fcd22b3d6ec60484da903a6991ff052ca37b01428d5916fd92c17530bb3385a805b0d57476e9f9417a23ab1c12a038b61b3a0898831f9615d10b468c3edc24448d09b8f3e3a2355dc5e069e880929eabcc97344fb6ca5587c5ac1404783848f531f1e915941e7359fedd328f7fd12b3c685f8c1f29d1a6ef7dbae3e5e32cdb251eb43aa2d2ae0cc18b3f40fb006c2778cba387e5852ec4f2d9b8e8ccd5b3e1f4781c974aca940c45d35d30d3b9584c750bd45a80f32f73dcd85c99ae107b92888839c342cdcf88911cb974d611b14b1d85a59e88c502559d6eef3b7f5addf7d307bb25c57aae669767db6d798ca887124e159b0317e09076cfdbe61aa9ddeda189036703b1cd9b1998f88325910a37ef1fc2e227a382ae635e847df8625b99eb6ef0ef10ce7a2a5762ad7d03a7a4e2b767c4df0b477d6e9601dc8e6438184f97193ea7d7a8c22f1b6fac1f0740f1beb8b68db40e0b22940cff2261273aa0be43df561b88184a9377e6a27f27942dd04abb9448b6b6ecb3a60f14dd39b58b8d94e1991cf9d3a071ba42e0e1d71eb211ca466a70fd4724a34639707feefbfd73dd9680d76a214924642a063b38b85cf30eb763fbfe889f34b20fa4a10ba214d938a5a092c6e9b73b13bd664c75b34f746aa360593c0f8dee0f328f0ad4a3e40d498490007e573b8204a1ce7a550deecfb15f18ed5ea6cb5dd95a68adfe4cab37c13b383f8273b1971580016a8df02a3f4f431c9de9e7ebb33244512080fc5852278081b9f4434109c3427441329e8071d19d0fbb74fb6ea73fbfc7c0ac1012d3a0948d94d7ceae9b0112ec43a16cb582f9c53e7eb0ad15e05ceda108fdb3dc9e585a332018d1cb19e4a75d86041308fdd8476c88e4826931601a3a5dce06fc16512f4669f10183d5a8d15bace4649abcac07358089aeb1e9b8fc3776f3239d5442d3be33d532097e13651af7c9a5b465ace9e626889800318447b8876b45dbbe1989e1eecbfb5cdf5067c71a0d7b7fba6555d0edede12f7228d7f9841dc532274f24060b1f52da6fbaa179b81ce962723f43601d248f8f4d5778c1653e038c8d27828836d562968004003810e9aa9318edf3260272b54fca2e012f6c04abe92c2e6152f3c3e973c7e9abe8c3467bdc246f0226d1b7669bd577bb317c571aa8758bfb694fe4dd17ce78f091cf6c6de3cb601a9d177128fce8d42e652b490d90c4f8fa04ddc71cac300d3dff699be3250bfdb2136edb0057af3ebcca77ba5b3ca34531810c5e2d4c5b5b3bc4e71ee9e30cac067b7706c326357fe0ad2a4bd9cd811b4e9d696bd9b4b70579ae246381210f879c769e5f9cc3cf8d70e9c94ab74a55f5d7bf61a17418b6edb6db4147fc40cf98c75de85421b7d192919add48e5334ebce2a06e56b915447fe085b7dcd677659dd55de1f705c389975e56e0338a2ef07ccf5ec3786407e8449d9011641786f1ecd4d3d3da975d61f5a442293e6119ab20686ea8cc7681010421226838a95a157e2de948c536aabadafcd4095dfda48e5613272289a8238dc945e5f1ef30075d5de096131740cdf23da1fb8b9fa009e5b321083cd93bba9271909460c09bbe1e8c54319394ff85c291814e21215816d4791f01424abbe4cc4c792d0d04db1b812f4d24b44caa76de2bc50f4d1d1611862512d87fcebd3c0b2659082b2423bc5360d107ad7b8e8ba7438ae4509105d6b618af25e75c51e272aafaaddf1e5a227f2b2a2c96a8a83dec23223cb428136a30b290181ee20a819cf52f6c03798e7294a89f3b5137693d5a8b7a0ea38d78e43008fc4eeaf6d077ebffd3ef7952620e0af1395c38a289832df391d1710ab5b103a1ffeea8c06684c03a74399cd63797c770e3f0136d8331611502d21fb883136a82f2034358880392fc3d2fc274b799e59b89f8f90d2a5a123d3c21e5bf3540323743858fdb8912c7c6329a3aea241075ae097ebb23c8cd50f4ff46b42486e65bda6beba5f4fe6dbb30f7e61b1bf690c9f00f7513c83274cd21bb71563257a20cc38da2b88c1063bd0849c8243058ee205853342085a8edb7545f0d96a6af936a3d4612b95676665eb02e72e0875100dfa444f039eddde1422ceed8d38e6c3dbba25064f8c6cb5786f9ca67712b7840cfbd40f99b1edadd4bb9a61f48124cf3b49d68bd642404eb1dcf428eeabadfba6810a4032f8ed06b38867a7098c7744d54dcfab8f0ff941ecee69da9916d54097e080cad86dd08bf53833fec4aa4399f7124586223ec70e2c31e8c647be06df9e86a976f37901e9b134e775de2a0fd53d545c5f92236dbf5455859c138b7bb1112427049d29ed4f5dd5c43cffd3113c276d9bba910879e55efe817189fc239a204a9ebe738c0dd161d10d60a51e9dcc8c38861d41ff029ffd841086803320a17ebf5ff14b6cc2ac3dcf0ce2eea9af7ae23597233599c2321dd2b99e06d93f84989e75e30a388f47079c2af5Line truncated
|
||||||
|
"e45eef9561f3acb3672b4f38570256e8cc4d877e2998e72b022e33de8fc20f7320fe0882f2b53559e084923786e8205336a7d15f3fb88a41e7bd20767f2feaa02df2221fa7577988db0bbf61f3dfb429868688c53e130725d0279c505686f083",
|
||||||
|
"475a44cde0cc931edf9a44b0c1e0001766f09ade023dfe6b59a6af800e549b55",
|
||||||
|
"7812a320691ca8442767a51a",
|
||||||
|
"eaa577bd67fe79ce4586f43355c94528e306c1678946e4f7a907d2a8ee7f4281270502522119a8b09b6f05d864921cb515fddf6a1000fc2f67b52d0627998591e2acf5b6faf71c278e5754b2703662ce670dd049da8d6e280c2b84d6a9b29ce28980563c40e03381a49c54608b72faec9b272ef05cfa41957d9eaf3e944b22610c725d8efea90aaac6e782848d368ffc08784d7fe37ea1effbbbb34952def29fc511fb10a1282bb0b6334328e4d00529a44de3259b522553a07d524dc75f431cc9670127c15670c0df419826617cfb5ebdd8788d5f528a9eb1e61324eac5c1746f339aae2e2e2fae598642a389da671482128acf2d69814258d83de98f186468136868b729aa5f0874fef2ff2575a1f87439d64e049e4d0637e9c99ecb7275417af654541306615f30b75a6caaa563e4790dfb28fe9f0e7881ea2d885eefdba99efa7f878925ce7d33e86d888154a1b03189429fe20af8fa3a68d65ced9b690a709031121425cfcd7e1890ed9614f9dc3ecbd0e38c6c84e453e3204978ddc1ef8d7fc6cae28c61a472d8e089e23209f0c36e80c994af771e6505e72ba90e5543f6bad6dcd31fdd468b13533a0254e44797825764ac1f63747d8d6ca019ff16fa732068ee94be382c46b168050ba725379df31a98ab81ec8eb266a3c3f2e1cd95e5f12b3bc79b8b435e4d94098c6184631cec57e9d8913458889223a2a4541f34d2f9df380f34c3e541fc587f0a6cf08c82e99476060eb84709a292f4c7a8551bda3a9eb6735787dbb9d7f1e83937c2e0e49f2cf6e0ab0ad84c40fbafc3c7e61886a8629bea816972fa0afd0f617b6340b1af19e341875e97565c8eb0b25fcf68696ee674d2abdc29396bfd0f282543d2b72a239c6470f76d3b5bff6d1d064e6e2d06f9deef2aae8a259c034373efc820f9a2fdbce36cc27f35dd6386de3b49509d0c305757257f8674d958c580a09e768c0f6ef237416fd53c31511badb2e7cdfee636508482f01899e72052b46b5d844799cf94708520178cfec2b61c8980fa7dfaad8915b0b75ce6eb57ed4a01edcb4a35c1dfcdf8d60f3191bbcdfd522a0e321ea41c2cd87a303522d0f98b82dcbe53232ecbf0e2528de7e1be75569584bf2ec574687fde67ffe9827ebbe78f2e5bc4fb368f3c9b0f588c97f7a139bd82fe86eb605b8e29cee75d07b510da1b24fd62cd2fb366f1621e7dbf268b15937f7f7ea4acf6e615775a32c90733769996dd2c5aebe08ecba73e0bc4781d33971992b2764c1b08aa972859cb61b003406479423254a01ea85a348ef249d408157cc0962d1e24cd9c426e6e6a3784dec6fe935be1f6730b01e8683d97e21d8774b2e2655f85db7149e930a44524d4f86004cd687d8a528b6ceadd890707458cab62809110ee28f61a7277ed79dc41e573fd4a59fabf15393ed4c21bf4d5138ac843e80bbf5e1c39ac2d7f2147f35996eb51a9e835db63faaa196b8aef1823ad72523fbfcb35b5560582a48a25ab770e7528e4b3ef291e6f62f5fac916e2162b3b56304287e46839858daf322b0de083d1691d6bda44d66d085ef0d0ad364eebacdd0a43a4456035e58910d0b2dacce45b1c0beabc784f3620a3e4390c345df6117b86d4fc386523b7ceeaecc21233a2865ec6b63bffba6689fb3323402119db8f0665a4730b2e26ca6411db04f1bcc78ce6272159ed2665a286f1ad7758d6d90090a6fd320e697dafbdfef575077e282b825bd64a4dbcf92d1fc0c6f795154e8466ee4b318f2d44b6f81c52523ab68ff8367e01090c2623e00b4008e784049df873a35c29e0abcfae7acbf27236adba0b913d19a15b4af4996669aba4c656c317084347ca962ac8df15cd2f849f522016eb92de4de62944b917d88200ef9aa2def0d13e5f4ae09d2eb4a2d0800af1d704cb01975f6d59768a2b50e39e78116147fd6dcdfbc08354c1b4033bf6772fa127856a4072556a9f07bd7516d01ef41bcb519005c0a3b2a04400427ec033f1b52fe5fdc1aed8e2521fd0fff663e203defc39d7546281a98a502b8a470af16cc62a6581c9985d7ca516864b799fcc55a803ce80711484f6b81591d2402bb1499c95dfb1dee9846679c22853be87c84b4547138dc4fd46b4e79ad12773a5392540a595954112f0cb1d9be4d4eb3aaa4286b6c01520558d58587d9d7f0df3a0282011ce01c9c17111d10ad61b3675b1826c1ad37fc562bdde951b43f890555d6f74ac4fbdb9abbe8bc1e80bb6d52c13de8960a3ff8f65201265e82981dbe39e0d65cf3f1fb6c56e11f9786210383d0150a5e0cbbdb52ca8b2bc45c12fb572657380df369082685b3de9847d5014beaeef815d63e203cc911061eb53d89a312d187f9f02760bfa71083fb643f5d8c324c410070b7ebde250a185e7359837899bb1568a43fa3418f39c12feb03b148b924bfb98b99352b1fbad3f07ac8e4302f85d1fe9ee4bf7507972670ff8beca105cdeb037f1cc4f944d6ca869d0281653de5ee93a7362420fdba8b01a375ff08fe27873655953ec1c00f53613c6ab8b244e2fc1b6babdca5311428d06f57aa4882dc870165deff75ba877dd2a04d1799f26ebfac97a1be53a83ab77dbc2cd4aa45bd779f61b1283eae1a1866ec8a9c150dd0a4deceb2ddea1bc0f4206cd435600a8f190b999b952337d9eb2bdeb3aba2cb2e7000319056629dc1f00901f0880278509417223a3ea0919fcdcf12bff0771c7cc725bdca292068478ccb2e1f35ae8964e0601789a73e7e7c1769ba53f865910fc3d0085c922d7f7849d27b6e7503d521371351f9d7dfd5afc5df0effdf6ac49617fa228501ad72154a73e07781dc4b07765dbfa721d95cf1dc41e161cbd34fc7883a25e3ba6b03e504b2c3b98c8b12ff629b965c2aefc26d74faff7f784baf09c3fc38c487a9d1f5818261162f97e9dff70cf42eb5dbcd7bebb66d68f26d917ddf2a3efc0db1e3372b170b4cd18da507e44c467943f73648dba74db1053b53f989e481c3054bac22c6342fca2c26d30a859a1312e9c353bf921f68136de2b1589747bc765153927c31ebe749dcdff98b5da84c4b66085451b4c87fe1ba2142f98636bcb268c33f7b8c2b96a6525298814578377aa189dd73d5bb27ec5cd2110d8751c18a3110273df2595d4c3a00809bdeda70d86c4a8169b7010c9cdeabfbc3dd3266518226d0ade9bcc4825f18198c854de329fb8fe456dd3bf35d89bd9d2384f3f3282f6872351a18a2f852bf173ea4426de6d01b3ef4b4685aa82df7dc45b99617a8b8c8a0c65a2237b3eaae8267e1f6c453f485432529d973924a080f6a1cc2cc18f804f53209383ce3601ad9361afc331707be1c88b4370404cb7fe0bc538df04adc5c8d9ced94b4c474b19619a53dca3fddb434cac09ce10c0293fea04e8e1b19fd3ff3d174baa988d91cb604fadc59ac0b61f4f87bfd07eee20f7f3ffd96766dd6f3555cd48da7ecd71d2fef34ab082678bfc4dd007669b3fc7a937a5a46269baa7e4e4e43eff1b2b847ea70b6c6c23905d6fLine truncated
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"228eabb5ad8b4ff13b10d13b27372bc2152dff149859ba47d9c89b741d4a5340d8fff5858a4576c55547007d7e2b3f94583ea8f0976237712bd2e5481c3988f5387e7ac2c3f18718388795b7b2d44b0a13f3faaa55311b800301c9203a511572cf8f349280bbabb9424070f415bbfe28aef8d20329ee842cef4d4c299e619b6ef1cf00718aab2accec9ac00155be2903b6fb07dfe98b0bd8d8580176b99ce4aa6be51cf59046c17ce1817d363fa63af5a241d48bcce064a438651af102ff9c6de4b86374fe24f1dfa66e16e51550dbb791af425d8fa601c70c1bb90e1a557bfe0dde730b0364eba9d2018ee751699ee219e13fa8874070935b29a1767e1d748bfbe796fe4b81a71e823605d39fa4b5b885f4610c34d1a090fa4106785e7a035a629958ad1b00cb9d36d171d575268efa1bef064fc0a6dfbae8e532466035a0c2cef96fe9f93b872f0cf804811e927b39818189412868fb104e2d56ae62f77031f0df1ae91aa11826991ca7b8af22f130a47a72cce36ddc319b32dffd294f2e192e490249ea1a6f8437173ce6392d16dda888a98bf685bc91b89b8ee1eabdfb1806fd61f018d1744fe8b03521de4bff86d4a811ca2ecd5be668e9c752a6c26aacc0cc9dd89d112785c25ca6a0a7a5267b4e37457c04a0626c8a29be30ec28ddacf47a84918bab164d07bdedae62132ab04a6f2c4e108eba9ab878caa4a1a7509521d427ad7f3dfa86fae8345dfb5e0d46ce3a94dec84f7880c7422468ea74fe0b4825b8c762b34d5d9b82ba96e0c7dcae01718ccac0044a87476ff031e3ee3c2c13f5f375a841d243c38cd9a354b6525527de1fe7e36a6e2ad95e5bbc4c97e85f8cdcd5341da777e03451838807d5dd2eb4fd15976783c140e21cfc2eb3e58e40c16374de0aecbe3e3d41c64417a472cba18762080a2348ec3f441bf229a932ea0ca7c816938655d0c81b14dfbf86aa600d0c68172fb0046ef51f601ec89309d43ad1eacd583f9d205bb1ff1a37a97b44b5e35be4945f52897eb2a74645b01a7f82054cda44e9fa9f9af9bad1a235155718713bacd08d354f3fdd95858db0040fb551e9f93ae399d5dc53a67e88bcd5a02d104dfd9d824cdd5fe262ed9266fc47b7e640f2c9d9c7a62c6d24b429fa55560aa254a824a0858482e771144d6d5b05539cf71d75bec3a22be75655e1ababec4dff9472a019f6220067374dd49252282e4945a407084633ef9c88d14833bd95335107d36afdf56a642cb739bf0a61ed53a6915baed78e9d74166ebc492b517c7c594fe6564550bb7108f43012551e65fbafc0a9874e46fb64b5b7aee0082a5d617a43b8bf9473309c6761aebc7f13b72ed460b522a6b0875b67353c705f99d1d9dc899870fcc90c632aba1fa9ced6d7a2368dc4dd3d4b38a5807415e00de6b9ea70525a6c1b67d04521efeeefc6c591fc5256d990a1123522864a029430bb7ea00dd80d283fdd6d61cc5b509221e28f73386803d97a38fb0182fd95b3b91353c6eb60ef2b3d5c8c0ab8dc9cd9be2b4cf69450d00e88cb0f0bc9a4be82b71148a37237ceaf945ab94c365625f58171eb15c1bb244a87335550d813d28f241a3296520046e65aff3291555786d7c871ec8a2d10d4b44429041c3cd6ab60f0def742de3d28393c5aca92b150697ac15504ee66d8a2aa01a6c63d7c719d6d4f94af2ed1d8670e3231a0e481095e425e6231c43ad36e3b7a3478f6a61563f5aa13237beb8a891dbb29013c325f7f91c1b055fb83c436fdf8aef49ec457946e6ab7e955427373fd9c743acfd4b9609569b591ec79c7ea7276de103a35a4a8a05c91f59e04689ba1ddd570b18ed046f785d7e4ff9fce7115ac814fe126f781828877208ddfbb2ebc919e6d1f6eb417f38bfbf22ac9633f75e58e560b85d88d0e4fad9b2e68c9ebf9675819d50c30c8982bbbc2f41e02690390bf0e16979b24e648bf15b18800aaef58c3c465f38cfd1e47bf1266c17b69523b7868d2138cb95c4bce0dd3ceb7c2267b868b6e12888d5a489fc0091b295b56a1c328b54fe1119aaf1e6d7dd52fa450b52fbfc8b84c2200ebe209060b655cad288562786673121691809366af37b76567762d1fc24f1fad3128b43c8d10e9b6954b2efcbe40124fc0a5b670dd6dd544e30263a551825282aa06be3817a8eeacf31ca8b25cba011d60b78d3d2462810764e4acb566ff371005f5481c9d36c991527143af2c44cc8cfc59c920bb4a281f2ed4d494d30ba4d900edf59e23be2f763072255cb6f1e8b24ab1d305fbfb2429cff8bda303617c034e71a17230d0e860420dbcf9fea4ab48557e4d50797179496936ec6c97686fe6d9115809e14069244d251d4bc9c8931e47e06ec051e709ba1df526b55d959b37a6f3408833aaac80cfc9cb99915eb7d83e26998f0da2492b986fe0f5047b2cab6e6d33a117df21e6a8ec7f394a3712885dab176a4d6095e5cf75dbd3f0077e5e74b1ff8b902072380cf172562884de852ff5f07c55856224fb3df8eb44764ab9284944b86ab6f176a863cdd0e7ab5616a14692f6cbf41bc63113b27689fc2fb145736aaf2a5b26d2bef3a2a59ef8bb3f3e4d360a4251d0736482e9ed7e189fc48c0973b6649988228c2ac72b23826a61cfa06b11f13c8555be6e433d87e20113eb74c94f0e51719a7b38c59eba300089d06b9bc2a72017668e5aa3153ca4282718f1762642e7c1be1f865cd9b65c6387c8fe496f1e60d5acbb78c2f71cea1f35dc955b1e7d1cdc9ca339765995d9e05dd729cdf58aa2a1451b633c374e5b6c2af1c8486ee4250a875e80e1f359c15130eb1e2575c0c7badb2af61378527fa24347ebb12c10bbb36e3c94619556b2c641d0ebb691b2706cdd667f55b8fff8fb46e3ac72f3682661a4bac2391075ff5145eb07d69d77437adec2d096c1c89208ab3e7a9ea6a0ff4a5bc1846b3683bd7c6ec4520c3c95861a5856b0191e4221c9819c67273c66729728f6035e79c0dae8842df4c0c27ada1ad18b34efcd55b94ef120762e87e8c5afdec80d5788e83f0d1533cdd7aea8f27f33266e007b274f6d48c59bcfad607e8b298be2b17322be88558c60033452826778f167f318b660607bfb2f285cadb385399636acb8f5350d819511b5e7931c5f8483529d3ab3fdb5ae2dde0ada918f1327c6c0dfbbf5ed3c8afef171910dd0169022b3cad5b08084dd5e8eb8ef1ecb17e48bf69f80e3db0ae1cc7b73d94b89696e3c3443ecb4c7ca12568201744d1858d90ff759f2d264d49edf47772bd0e0990c14dcf8c8a4c2dafa44dc6e92f4c66b03bdc4f68f28ca2d0811a433e184cced99a8e5614ca83c46ec18b47e0c7ae91037ae06c6d6d0f3dee19711c21cddafb5869416d23c5219296acda7774891877f3f8d46155d39f43ed10500ede3afa26943b83b800b54a9752250ec6ae173e920002f365d692a9b3a2f9b27124ac97b8e81b70e8c0bb7022d07ee97e962810962b03fc019695b5399f77aab414327cfc5dedd51e99453179c42ae85a42f8eLine truncated
|
||||||
|
"ade72c2ea29cf829ffe99c2d63840b2eef9b51a9919c02128347d2e88e9f063b86326928cf6252ce4beefbae7206dc61a22d0b33c90d464d551835e3b73c1e3d6e88663deab80c35a607e4180ec079b0ee84e3b7922904e7423acaf976e837",
|
||||||
|
"43348cf32211d7daa300de8a4218543c8e3c7373ad10950765c39760f80b733c",
|
||||||
|
"e4709d225a552e90fb357413",
|
||||||
|
"562050bfb40451f27b1181c389508550a0f46b53d14ca73143da9dae3d3d2b466e9618db39e3219675d2b6eadded7dd9c741d7c9bf3c5619a521189607acbcf6b3964d469d966fa134444aa06d80749c873f0f976e0c5efc5be8d00a2729f03eda6a7b8630575df8b3a19388ff88daf0d00bb3e7c35a525ded90a4511ce815fe6c8904406cf72d7bfa14ca533566f7b54268835285c5402e22a63f98b5d90c86dae0a76d65eacc1ba85b3f5a1499d5f3432dd5455fab9e8bfbd266e99283c2bddf9b556410956b2f061603d1fc91194766f90da841699ba7da3d53ed5abdd8e98034f8fe734446d92b458a731aa4c578552ec1ac5d1baaccc4153a67b48a290602d5f955d61a08436b27cfb0786a80afef76e1266310a42d90feeb3bcc40ae5c4506432dcc92f7e5758ceaf277255401f5c5f4b10df93a249e38edd9effe7bacdf7fecc451d3b2cea77c9bab0403450c41929775b8c0ace46f6928f4d9cf3adf86832d298ea32b236d3201464e2ff506ef01da0e1e389e26e2b3ddc553b369b48d1aa5dd43edd5cab065e276aeff72a4c43206063fc7eea3bcc783ba2221f5b615a7a43a75cecda6bca5aa159e9208bf66af61e2e465c2daee630c4c62077ea6ef0e8b4b4e272d4e93a5f5284f9da463e1a60f815a8a31698ecdc09dff2b62f00e37aea5fd4b07a110cef27e12466c1814d3b10017cb9b8e12f2f38f10cbe31296de2570d5662b16639fcdc05db81e0d48178d055ef873501148d00903ec771400fa4873c5579dc3265028f531538f6dab1e5607a15c8b90cbfa4835107cba6f453bbdc71d08c7e423f58b44be38a9c8a610469f2551ee6177edf639cde35fe8e02f76b7ed106d691a876a4fda3b42d8ace3e0d3d4e026206c5d7d4d56fdda9dcd30fd7b74217fab3c617903f1aeffb8363443ed128af94c391810e327704d6f655e57dece97658d41e074029823850ddf7c5937af41c64465046d8544bba65c691ac69121bd272107f7eef8cfdb6a25da5da16d1033cede09129d51f6abfe63905a6fba9a64d7832fa35825447150595a60163af848eea878fb31a5fb97b1859efbfcc8586eebce8cfe64386461a9b88aa5efc1db43c64dfd5d4a45aa74803fd178f9e16a3f59acfb6e13a564d645cedd73890d0a82fb6dffeef527694a7cf2a89aed9750c3675a67505bff77de8d046087bd39a85c90aedb085e99baf04c7e3bf92e350b332da1b8af85550a00d68904ca426da61add864496d6ff442bb0b848e9aa463bb0c2085cff1a83a47d6f702bd184cfb5c139752754c8978d27b58d364bd88722b9097ee3a6ae28eabb14ca7c31e40461101e92448dbbc63b55cfe56efd078d0058c5e6146c73bcd949c4b3ec9f881b9a5f7b41ca83301261e0c674f2d35d96761baa00ce0675c082bf73dc52dc726a3e605067569a372d2bb47fc8fe1e74f00078ce6f352a6d9d97fd2834670ba3a45aa6751eafc7ed6694e1e07542860c8ea516f296ee901a3ee16b00b40419c74bf6db12c7230325e85a918f412bc2f6469c1a13a5aa77f028e327749efd05b91053f49d9f1edf49aa552c58c68257233a168db60ac55b4086ddaea275b078869cda7b69493c4b371b4e9c8361357a7ac7d3d3bbb464c960addfa8df2b208b21b090d540c440241598212d33273203d484e0930e22469c2a8e866579a4a2b3db8f8344dbf8baa1b97be0c4d976f6aaf14cc09ec52630139b894b2b6f4dad3a205a7b286253f1522b1d6e43bfa37beaf06f831c6f0945cefb2593b9b298da13b0d910582086c5d7e256ed4067bfb476dbe01bcddb437d46ba716d6ace2ff9912c8e460ad33ab3d8f97b7b08dd4ba9e01968d1949ff85b4b9d5b8da291fc0f90ab1eab1d246f67d76092b7a37528ceb388dd76f8a8f0aabb7490f02a2c8bc6498cb26350d859c466dd611bf0ceb81a8b7899c67742c22697ccee21c4963acb003d15c1a2078112bab05595917584e417db3872a0ff0a29138bbca7314449b19827525340370d7e48fdf9f7c6b4a280e78d00775a291081a5e78e7a00ff915015dd5af5f0a45690baba8b1b503bf85f326c23136f4424be4a559aed03fbc81400ac27a33dadb2155d1704950d98043dcd86df1eee78f3f266c4d14deb8126708f74b59aa15e8b497c6a52924a473f999aaf0abd3d148fee8503a1568efec7bfb0bd463402f563e4019cc9c9e1eb498aa54dcb659f43b86df0a34de4e51ec558bbbade3d69511d3fea2baf44f67e85ada7398d7f72ecadcd9e981f82b0743ed74bd33088ba4cbc85b0c99dc5382c599706dd2d51aa9f470c25a98e7e8248dec216a155495630662bf6ba0b7a4baa2cdad30e9ce3e1a65e3c23d69d5f946606ee8504dd70830aa5a8ddd84f10e064695469727d2efeb46186c9d3b7a170057636f05b9ec4c2de7d935fba504a1e7eddf7a5a95226b253b0b9eccec976ca3c57599850db40c27a51ae755c1f30d392467cb74e5c8235861d11d0f8461b0e1d84f5718d64ea92da62f4de184a6499dba473e82b3d197305de0e494f118a263237c7b4c0652327977edb427ccded35552c00a5804b9557ccf2bca2484d9da2c33f6c1bbf2c666ea10b4644a21e3905e5c4eb417ac3572e783428d23dd7222e75c356b99e8183d033034e29e618c90e66ec2f1e9fca47d82c1cffda8ad14c96045159d9437e91ecef41d24cff89009ff57e18c1a422860aa9cd31dd2a85b07422c72a5decc614a9742e62a4988f394421b6918e51c2412d749bb53b1e8fed7b2ef0873ffe14fa77bc366bbd5fa1432be465f5e25266c6c12b55df1f19b1a491acfc5c9019f122c422243d751d8eaa8ff721397915171556e999b34425f7d3ad6f6c3323b8133b4618c65ac16cb5941edc979472734bdccafc73c08939c0b1e306ae3015faa9cfa09ed6560269a1dc54c2c046a12a178144f4381f7b6fd3fd2d28f778d444d9f7a0dae00ea96c6969b78ef326a962d23275f1518f0e6a2469440612f3710b53538fe99a6179471be8c5b2d682ab3e9a5126e41ed6de000cd9e92fec3974e0f4cb2d2245d03d6ee80d6a793b16efa829d75c796f34d4e918250f457703559bb48ff78f0896be1bda403b7f1fd6a319d68478ff70d88238f2b8afc7d20e51757bb9db3bffb35a8040fc0db913c4f03d48619af7fd24cb8986b3e139058be3cc253b3de9b3bb3f8dab7b8818638279b2e6a0c29cfe16fa7250d3c74362ffa07e2977cf562140fe28afba8f61d81f7c73bdd4a2faddb00752bb049d0a57d05c6475c7387e6716ee31974169930c9fd830cef138659cf56f2212de185186c3d683fc6b7fd36e7821f69d0de041a569765066dc4a1934870a7b80f174e8f9e484942e62404a42b21658467873865ef94fc262c231527f39e82dfec91215947b99567daf75c6a28073ee4e67d4307e4b35b46f85433abd9812f35438b34598ff3b6dbd60b60747ad64565391df45ac80b272d0141702ab807fa27c6a6ba2f42c3facfae0c773940cb2943bb1353b412982Line truncated
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"2f6210063cb3071b3d49339185c2cef8357b08ca826d8d1acd852540c16540f1c850f70404fe1f414853d3cd15a1c64a1cce149e3ca1b80926de4ae8438ad90bdad010decf2f201782f3e49794aae1b079f54eb59607bebde508a528927e346d4e444b1d736b34f65e198df2c36fa23c64f1f1fbf8b0b8ddb85d054bdb39b8297d0347f16f7be7cd9474c058e36294485386434b36fb28ee582e393367f15ce5f5a3d6641fbd31b331f10b1554a05da726a0f35c9b1b4af3498426b17582966a266cce452900f85af1046f45a4ccedca6ce02607fb70fa45f420f66aa38cd4c9f8a30e21a3067b940aebdaaeb7c77824a79e2ba20f26e70346dd6de96942b261e5c08288c7fe1cd1e9f680a0bdf8c46497f007a616eea95ccc17463559f8973eb919c68017e25100d9d1a196ca65fb615502076bf0b0c8bcc70ef22006895ebfa2243fba0791bae0625b762cc1718d1673948264454a200c58122d5e9b8b1e3eb05df8b7eeb297510e0d7dcf7f0be5f29f6756e4b177f109891e6825a9866359e35b10d20da7231bb5a0ea34abd0264b377d2fe9f420f27d3e5aa2e8e00541c46052966ef9b989ae5974e2054409507b867f647aa057f7deb19ac6929f0856005aec6e53a5f702fe6be403afed532b73d38fed73e6e551987f182a1e20801e7a6c8ccd1184cf0fefb4139fa166ca15395902ac40e7fed8661602853682a3b0ee307dffb44d0ea3012142a2880cb7c166ba6ea6a16c7e0882808db8023068f060e5ef1432fdb8331ffad6a7078d686d47d613e94291f1c4117e7c13aee4030fcaf223fcefdb300ed606b5dd931e4adbf45dc437eeb5fbff337812e15c15f026071423f6ef5305c559baa2ecd8ecc7cd498b043740ff3673774855d45d45fa64591d5b4970600ec91ab1b6f39d7dc0e709c41e49c355bd3b9d120ffb57095fb127bafa971a086135b917285794e83e9dac5ce76fb1a4aa4fb6b94a0dc3a9beea64b8817ec1e2b37af9dbd18ec30f2b6f6c12df1db6896c6c43b67a066038f0c4f17142b254f62c4dd1fedb950d07047919e397d06d033cb0bab6b61aefa6dee01720926b16beb9e8bc947dca9b8143b565da85d2dec182987838b267de9047f5b0d961c7971aaf54ae2c1e4aad61ff123c84e41a4566b2bd9e64247cf46b72a444d36bdced1a309b464ee5f4afe406eb68eb05ae51b76bf01b906c0ffbdeb440b11f1c9e3a4c3a809a1f7449047b356c663a1ab7f286a70d16141d11f2d151a4f06d422ab97cab539c1f9da09ad20c000c27b8fead5f0cc37329d466fa260aea934c154dc9c0a065df3d057a0f117a1c38321ae59226a8054f7d6b49a3753436c249838b0924f0e861f5627106dd8d3f0fa724a1cecda71d4a1267ed889b234ae4a7d5edcbc5d52cba389dc0152aff24d224c6a0f16dbd3b7f242807bf4b51a3f22690bdeb66eaa59e8766b3b265d784899d247a0ae1b58a06dd91c529e3691b09f9d9f55fc39afd4a00b0fc668880ef25a46a30861fba8cfd4b51262eba4138b41a2d13ddc71128c8c1242e49a51d6f49879fcfa7595ba4a4adcad3670b0b1b26382f03ff402bc70150f54bf513ba3e9a590e41b269e55616af297ebb3499e16cc8e46c0810330a602955553c0f93d668a1181a0bfd7021ad9a9f68ce39493b012da70a3dda149d0369f23f788616e0272efa322b6a54d804f340d32c890e2eb7b538f48f4c9293b584d22d0ae80d321607644271b81a76ac5b49d8e457069b0c3e909b8a222e3fa6016cb1e979e300804742f2005c68acb7b1849c088b3714c9c7af54e9de9390df0041c87924c8fa6b0aec6b6754171e059cba0d27f221f0b9d044a3aed8338dd8745651981e4b0329376f908b86ae9022699d495bbe3a148f7eb73d56eacb2e5e2180f63fcbfa680369f88eefa71f1210bc5b6b7b957f0a1437476a2112998033197673e470dbe7d9d476c97b95db8b5136f6cccc75d6e0ac1e4ace30e34e64fcc4d7e135b2c80e863ed701d3b28c25e982f1b5f8c895a4e6df7216c3c07abf8551a0ba0469c88aa7a08c7b5218a03b9b91f0935985373f65aa56286ad0e7ef2288a926f172b098123c136455b3a0f04590839e16bade7b6434a3cf048abe2612684c03dafd9cec39af508e63f07ea881014697bc24122058b5ef5d3fae835216d055f0cdf1dc06a12c95041d13ac9e15f235d11747f16ffce1cc3b8f508da520e395edd471f3759d8879ba9c2558b1188d822fd4739ed0546b0ce3bb9988db7c1dc8518ebbc62c4440e6e0653f917dcc13aca1864b71dbb67dbe7117474c936414e4f3cfab1f13eb05f3504484ce11977ab21ec523f97ba1b7ecb8fe384b634c30561cdb752fc67a2316bfa7e4d03f5f825d24a556a0460d8cfe0cc54a6f117ac52d553a5d1bb48031732716436675c5c3996b1939b127c6b0338bfaa29c7467cac9a127e455a715c9ce2b0c35a0d2f83a3d1273ee39399e6cc4980e610c752bd51652b96bf9cf34c7fa41fc9b13f5d55007483e4082ddac4675baa7822fd257452411b01de0e5e5da26e17539d64a89dd93c71d15a4c95b1a83039cb2d5f3f7fa04a817e48dfcbfb3de34ecb47f7592123caf27e17982fbfc8597af5b8aa6558f4e6c73db69328e47677afbe6ef8df82c3d1f0db6a108b2279f61822908d7b856432c32ac5ec0f3c53befab2a7ca356b9c2636f646b228b0a830d348be4ece2271814d477d4c73c0fb6e83a338b90ec4ef45cb25f7e3d6a014a9e8d2e8a6f55a383291a57f15667a73ea1daca31c7182523ca85a107efa2518d2f7f179ed4ba21fed479ef2be09669817133b2384bd85b155dfc1c4c9e6dd9ceecf06cc1ab8ebf7f07aeaae7441468b5471aed93f248a84f44c59be33274b11f651de010ab9f8fb24d3a99914e0147951c34280e7dd15ec196f9a4c86e55e7d373c7e31e6672d1b3ac6a45fa6c8c9088c0b8963d89f4ff1feea3e85cf9cf2f6c97128afd845bb131c6f62b3282bbba42745080fd457f1d3322058f1bd4be876bd01269546d1a853310b165926c1fd4e07054deb5d3fbe8f6007711d435994005aba95918c3df4cd390b165fcd139dd418ebbf661b6de57b655698a8a02ca8fad73e8c536c7110957c36e5494a831d536eccb97a2a9ef58fe58e2885aad170720ffcc57c7de601ea1cf723577a30aad8fd544317e33897c8b6c04e5191bec391ab990e197f10038c0726d371677e4a54c28d7ca5c6046e7cc4acde565b91f7f72af6109a0614160d3ae97e9257b8f71a4663b00c681e793cbb478306e97b0e04711eae7722b4845dadf2fff5bbe71ff24acffea2ee67df99bf62a098ddae9d4ebd3bc5dff04a2d9e3d1d83e8f493db3f63c9e24231b1dbe1147c79f21b0730c842f6983330c5c17dd34556d7e932074cfbe98f2dab5b0ebfd778a1e28fe2bac2d942f61a08b787ebfcdeb3d600bb130ca4922a4ffd38ffc4a1a1a7218451e45da4da67ad81ef898ece3d54cef877cb9d09f5dcf72eccbbc06e62f1e2b4d64059bLine truncated
|
||||||
|
"fd5008477b0855f6f2486fd4f74b9fb4f6e19726c6996bc66893183bd76054d5b05c1c2b64722256ba912ab2dcca66d2abfdf972966438fff7513acfb18ea461eac08c4e32aea4ed3fcf9f1c9905ee4402e7b6984bef974340d212f160b6524b76de99a98d3e96cc0d35e8a63ad7ea3cbea1d40a906c4dd03e5fc19e1513e9",
|
||||||
|
"390a5e75c9ff4ad38fb6205ff47f209294337c1f25ff54a3c01eee8e1e220257",
|
||||||
|
"8bf183347ec1ca4bceff3374",
|
||||||
|
"19fa2641519e21293094e9d767ee1237f9e0715dc57172794867c3bbe2cb647f9b28a8d3f85c0ff557b91bad66f5ea16e0107757b0277fdd3ca05bf47c19bcb92a958a57e8c142a51af29bddb20af84377b6db65f77494e0dc4d2634a776b3a5d777319873bc0dacbbd4b9ebccfae849fa7e9769cdf54660ecca0d5cf4fa5190713726d54d02b3a3f21857125b8a808c0ca2f99d11dc430ed5113ee49ff8f00bcc08f0370dd510e8100e1285659a7b2c7457a6049f2af7786c4db1471ce5bd164e11c7a2165e83e03a135ae2b3429f82f677de044a067e99e0bda2d65a7270d629c00e1d528212d3aeb2896e58ee5145a93ed06a9c00705ad5c5988d3a192304c1d17661d45257c5d16799ef70771964435b12e3b2ee9d5b467c3b1992f45b7a59871b40d8daa1c280747ecb3d170257b91df1f549ce6d66455b5b6f60b7c6e95c92a67e20cffe8599ceb183de53f1dedfe19bae836447af8e053ba419660e0912cad064d6125b9e978e8d0d5f28f8a4e43ca3cdf2d4c0e9a11221d8184e9eb6c90761b0beac82d0d22793279aedb1c7db3632adbee323bc3bbde4801152694831abf5676979af26af7dcbadfba1cad1306b635840cbca76c558b37db0803b4c12befa27d16f21506b07ade4a838d6beba1816eb29ed5e3c4f132a752fc747bd9ba879156e87e6c1584e911da9f796e1fa4a055e427272559e4bd6d0f54b8257100f8a55d84c27b702bb1fe2f995425c85fd48b0a0610db5b39f7a5031407a12dae9f508b21b1378f14952d1beb2dea81d016b2d9b7f1a67b814569b69c0e619adea02a8683242d63a11d3317d060e5b4d85df5ad73127541ba5314715d187990735aa81f438f8b94070ec506ba536274d98b766c1694e54367891a602b99e370425b47a70b819277a249fa429c5bbd0530267f987e6022f25030c30f3baeedc0d13c95f3d5e4b2b87465d179a3a23b9f9e76a42ceea55226ce072f9488392f40621289124d786109d2498e74fb37e2ef466fe8bf3016d96e34204c32978775765aa80461cac48518157f86d59f6187bad4ee62fba1ddbe166b29452f4a59af1e057300c353440644a8e40ae8171ea028be2fa315804abf518847c7945e8228b7766cfdb08d3a3116b59aab8e94b6d8c8c9ef442c2dc7f923bc2cd3e5c663baca7dded976bf191fe36da16948c89c385fe71434f4aa5dd15fe0e925d2459e3b068b9d82a9cc8b8f9786bd9f5fef9baaaf2d67027d9bfd58bb2c58ec7c746b747ab62f9242e4b53ed14d6fc75f5280eca0de23717c97a2293826e19cc8eb47f946421516c349dc4ba49225b91e4e868874bdebd373700df1f3792aaa140597e58b88f90e163397dbad3941705b53d754e3e0c9003df836a7fb8d23f40362fcb5f3947a4281b24240be4ee89aa8e917b194f94345eeca224df0adc15f22a617b6427f29410bc48ea3f92216163785723efc36301d23ed52780c6fd7924bcfaa03269b13582b7c7ea9c0e4a451f38a469fbdb585dcb7c81452da77945ebe27eb26ff6e8c7b2decea289aac5af74746dc257c9bea44a0847f02c4f586e1d76f39d5bf952355a0875f177a666d1d354ad86ce5ec0aba2c2b20cab050eaffd31095395132f5af80a2d2d53b77bda49f948bbb37bdf31c8a690476488e14e542ff6841e7fbfc2eb84795696562d079dc1612274b6dff362567084f793f0bc2dd8de23392d05aeeeeac6991c9f74387153a4b7da94790375e336a00c8293bad0fcef2dd1880e7094e2e53f738247c860780ebe308410ca02ae409ae720e841f48c9677acc6e7d4ccd18c219c400f8b7e1257f692e09eaef96802b17a1cb7d93eb81d3bfcbc7af4cdf05b98e22556b3d1a8b56d6d83bb5f5724696f8f329839dbe477483ec3c09fa2e0628faeba1bf285c224bea3f6cdc7bbd768133c6ef1da14f248cc3b819b196588811b073a7291817bd1e89c65760435d8d17cbf9423744a92143e0f956e2977b39c54fdead5a57f3a04a0facca01bbf44d3b1fb9c4fa83ae1046985e3f26aa0a437999004dd8adc04c5111759849f919b93558dbc559173a23b069b59f800096d9fcf077c7640f59170bb9a6fffe64778bac272365d27ea62aa956559e90edd3f6393cc8775597bcf7d91990ab9511973d948324a27261059e93f4b5dd2f70caf12e1a08e0493cb05588618764391f355379578cf94dd33e616136eea997ec11c0d4ff064ff51a767e5558433a2e3a9a74c232d8e187f47b8cca010709eb9fea0dac8f1ea53bf18822e154ecd929c83b0eac366e30fffbd5ba6a46d734f58d26e7f5df538e18b3d827884aa857a680823131bcf30a76f1a555bcabb17b02b53aefad96fe76f7312da69719434c580d3ff1bcdcd594e6375935003d5d732cc577e11ea2abb1d04259f50aed4c3af9866e8c4a52a09809046ee330f05c4403acbc297a9416c5208fadb31ed4eb7a3b01b87bf08c75cf44c2b0df84df30872d021d6567ea649859268e5e1b5b6405e1b41e350a32c1af13722959c17c01b52c42241313b26b25995a1c89a53e248488724d280647226195746901929501df36d1e94815d7fe6c4ca2731f3181293217f71b9d7f59c2474856972013924ae4796db4cbd22d8905a6043c959941ca6b556c53d1688c439036c715d33a47a7dfc2fe40e53424c5093020d2e85e4b04aa4c704ea5bfe5a2384878da38319c59d41d66b6add2a443d9ea11edd8d18fa41004251653857733b388b453943eb33df93dcd5d549757fa2967ef0f9a5105836c48826c47fcccb2d9bc349032b286962136b848632bdcf186a08cbeaa52d195efcfc3a440bac154971d11ff4994f293b14fb8c3214ebe7ab8b3d0f2fe0b03ed7b145fafd7730a173e3cc1847f0cdf2cf629f5ea81a07bef716b1a67dd9e3b7a52fea1aaa7a393f53b5bdb5988df78a57a9dad19a8253316835acab8a6b9a9fb42d97bf29b2443322f46de386fd82bd3453ed68e2370c6eac4497b1bde7b42d569c452f377bd38bd50fa5a6792ef5c9ec6c647001149b86fedb3e2f18d4271e9cc4801aa16ecddb31b6a795fecabc613bfbc8e4f5636d71e74595c841fd11b6a6bc7f169317c1added56b82a71fc36d774bb4d661685363e9da5fd2e1f357006dc5b5bbf8b42ee3f869e75a541586fba558a8f490d641b78c27368b9b4c2db046354e9358ae9140e91cd95ebeffc6c0d2676a3ff4ab10d463bf32bed97023a80a79df191ab9858c43537a03072a17c30b1bd99efbd361590ed6b7d5b0ec4e2326fa35904ab9a48596f44491cbbc0112890f9386ed04dec30126be359a05e99b2b77fa2c8f6b7460a6cd590d71c73b2a1b23312ff89306b6e41c76ddc0a099bfa79498e36ae5cf0c560b8854dff32d2b690ce0ac4aabfa723ac6f2e97ad1083235196b464ad67fdd649aec01695d55c8b4bb198f30630ca635aa5a1915f3718341bcfd8b522f764015fa5479004d28eceea7fe67df7ee24a97a9708d528b89589f1899f13242a0d00f7464c3cdfce2136993Line truncated
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"67c6697351ff4aec29cdbaabf2fbe3467cc254f81be8e78d765a2e63339fc99a66320db73158a35a255d051758e95ed4abb2cdc69bb454110e827441213ddc8770e93ea141e1fc673e017e97eadc6b968f385c2aecb03bfb32af3c54ec18db5c021afe43fbfaaa3afb29d1e6053c7c9475d8be6189f95cbba8990f95b1ebf1b305eff700e9a13ae5ca0bcbd0484764bd1f231ea81c7b64c514735ac55e4b79633b706424119e09dcaad4acf21b10af3b33cde3504847155cbb6f2219ba9b7df50be11a1c7f23f829f8a41b13b5ca4ee8983238e0794d3d34bc5f4e77facb6c05ac86212baa1a55a2be70b5733b045cd33694b3afe2f0e49e4f321549fd824ea90870d4b28a2954489a0abcd50e18a844ac5bf38e4cd72d9b",
|
||||||
|
"0942e506c433afcda3847f2dad",
|
||||||
|
"a5117e70953568bf750862df9e6f92af81677c3a188e847917a4a915bda7792e",
|
||||||
|
"129039b5572e8a7a8131f76a",
|
||||||
|
"588e1356fb8fa32410dad99cf7922aae47b4042502c92f3afe33dc22c1c2e90caf22bc37a254f8dd62a09582c70194f9616982639415178e9fe95740c0f1d497a69b69d4924a7a15290187f9c8acf09cf5b3b3188ecde2d2807207f5bb6a6d3504314b1b47684cf8ba8807eb9a3c497c79ebe1e4c1eca2aa90328563e201425227fca8ee05dcc05fd6c98128626c1e71d2fb3a21860567093db1012dfabe13055c48219d2a301c8a5a49033a811d8d9413bafbb2eefc177226fe578e93c2ef1f309416dc98843bfac387debb1b610b1d2366178ce7212a7312057a3d058357a629f18c78e129e60979a2310455a76207be5611e8b4b840629564020c17f5c9446882e23f610e931246ec434e62de765bf22954cfae02b2ff4b4086fbbd1b6cec23e45481eac5a25d",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"67c6697351ff4aec29cdbaabf2fbe3467cc254f81be8e78d765a2e63339fc99a66320db73158a35a255d051758e95ed4abb2cdc69bb454110e827441213ddc8770e93ea141e1fc673e017e97eadc6b968f385c2aecb03bfb32af3c54ec18db5c021afe43fbfaaa3afb29d1e6053c7c9475d8be6189f95cbba8990f95b1ebf1b305eff700e9a13ae5ca0bcbd0484764bd1f231ea81c7b64c514735ac55e4b79633b706424119e09dcaad4acf21b10af3b33cde3504847155cbb6f2219ba9b7df50be11a1c7f23f829f8a41b13b5ca4ee8983238e0794d3d34bc5f4e77facb6c05ac86212baa1a55a2be70b5733b045cd33694b3afe2f0e49e4f321549fd824ea90870d4b28a2954489a0abcd50e18a844ac5bf38e4cd72d9b0942e506c433afcda3847f2dadd47647de321cec4ac430f62023856cfbb20704f4ec0bb920ba86c33e05f1ecd96733b79950a3e314",
|
||||||
|
"d3d934f75ea0f210a8f6059401",
|
||||||
|
"a5117e70953568bf750862df9e6f92af81677c3a188e847917a4a915bda7792e",
|
||||||
|
"129039b5572e8a7a8131f76a",
|
||||||
|
"588e1356fb8fa32410dad99cf7922aae47b4042502c92f3afe33dc22c1c2e90caf22bc37a254f8dd62a09582c70194f9616982639415178e9fe95740c0f1d497a69b69d4924a7a15290187f9c8acf09cf5b3b3188ecde2d2807207f5bb6a6d3504314b1b47684cf8ba8807eb9a3c497c79ebe1e4c1eca2aa90328563e201425227fca8ee05dcc05fd6c98128626c1e71d2fb3a21860567093db1012dfabe13055c48219d2a301c8a5a49033a811d8d9413bafbb2eefc177226fe578e93c2ef1f309416dc98843bfac387debb1b610b1d2366178ce7212a7312057a3d058357a629f18c78e129e60979a2310455a76207be5611e8b4b840629564020c17f5c9446882e23f610e931246ec434e62de765bf22954cfae02b2ff7c59dfe246e4bb2d6a8afcebdc2beeaabf2a3f43f95a5ea639853f38719875ecdd2bbc0d81bb2a5ed59553b1e76b6365b74f618f685eb7731024bbf6794c3f4c7c5a1cf925",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"67c6697351ff4aec29cdbaabf2fbe3467cc254f81be8e78d765a2e63339fc99a66320db73158a35a255d051758e95ed4abb2cdc69bb454110e827441213ddc8770e93ea141e1fc673e017e97eadc6b968f385c2aecb03bfb32af3c54ec18db5c021afe43fbfaaa3afb29d1e6053c7c9475d8be6189f95cbba8990f95b1ebf1b305eff700e9a13ae5ca0bcbd0484764bd1f231ea81c7b64c514735ac55e4b79633b706424119e09dcaad4acf21b10af3b33cde3504847155cbb6f2219ba9b7df50be11a1c7f23f829f8a41b13b5ca4ee8983238e0794d3d34bc5f4e77facb6c05ac86212baa1a55a2be70b5733b045cd33694b3afe2f0e49e4f321549fd824ea90870d4b28a2954489a0abcd50e18a844ac5bf38e4cd72d9b0942e506c433afcda3847f2dadd47647de321cec4ac430f62023856cfbb20704f4ec0bb920ba86c33e05f1ecd96733b79950a3e314",
|
||||||
|
"d3d934f75ea0f210a8f6059401beb4bc4478fa4969e623d01ada696a7e4c7e5125b34884533a94fb319990325744ee9bbce9e525cf08f5e9e25e5360aad2b2d085fa54d835e8d466826498d9a8877565705a8a3f62802944de7ca5894e5759d351adac869580ec17e485f18c0c66f17cc07cbb",
|
||||||
|
"a5117e70953568bf750862df9e6f92af81677c3a188e847917a4a915bda7792e",
|
||||||
|
"129039b5572e8a7a8131f76a",
|
||||||
|
"588e1356fb8fa32410dad99cf7922aae47b4042502c92f3afe33dc22c1c2e90caf22bc37a254f8dd62a09582c70194f9616982639415178e9fe95740c0f1d497a69b69d4924a7a15290187f9c8acf09cf5b3b3188ecde2d2807207f5bb6a6d3504314b1b47684cf8ba8807eb9a3c497c79ebe1e4c1eca2aa90328563e201425227fca8ee05dcc05fd6c98128626c1e71d2fb3a21860567093db1012dfabe13055c48219d2a301c8a5a49033a811d8d9413bafbb2eefc177226fe578e93c2ef1f309416dc98843bfac387debb1b610b1d2366178ce7212a7312057a3d058357a629f18c78e129e60979a2310455a76207be5611e8b4b840629564020c17f5c9446882e23f610e931246ec434e62de765bf22954cfae02b2ff7c59dfe246e4bb2d6a8afcebdc2beeaabf2a3f43f95a5ea639853f38719875ecdd2bbc0d81bb2a5ed59553b1e76b6365b74f618f68a12d0f1cc99e132db9014100d9668c91",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"67c6697351ff4aec29cdbaabf2fbe3467cc254f81be8e78d765a2e63339fc99a66320db73158a35a255d051758e95ed4abb2cdc69bb454110e827441213ddc8770e93ea141e1fc673e017e97eadc6b968f385c2aecb03bfb32af3c54ec18db5c021afe43fbfaaa3afb29d1e6053c7c9475d8be6189f95cbba8990f95b1ebf1b305eff700e9a13ae5ca0bcbd0484764bd1f231ea81c7b64c514735ac55e4b79633b706424119e09dcaad4acf21b10af3b33cde3504847155cbb6f2219ba9b7df50be11a1c7f23f829f8a41b13b5ca4ee8983238e0794d3d34bc5f4e77facb6c05ac86212baa1a55a2be70b5733b045cd33694b3afe2f0e49e4f321549fd824ea90870d4b28a2954489a0abcd50e18a844ac5bf38e4cd72d9b0942e506c433afcda3847f2dadd47647de321cec4ac430f62023856cfbb20704f4ec0bb920ba86c33e05f1ecd96733b79950a3e314d3d934f75ea0f210a8f6059401beb4bc4478fa4969e623d01ada696a7e4c7e5125b34884533a94fb319990325744ee9b",
|
||||||
|
"bc",
|
||||||
|
"a5117e70953568bf750862df9e6f92af81677c3a188e847917a4a915bda7792e",
|
||||||
|
"129039b5572e8a7a8131f76a",
|
||||||
|
"588e1356fb8fa32410dad99cf7922aae47b4042502c92f3afe33dc22c1c2e90caf22bc37a254f8dd62a09582c70194f9616982639415178e9fe95740c0f1d497a69b69d4924a7a15290187f9c8acf09cf5b3b3188ecde2d2807207f5bb6a6d3504314b1b47684cf8ba8807eb9a3c497c79ebe1e4c1eca2aa90328563e201425227fca8ee05dcc05fd6c98128626c1e71d2fb3a21860567093db1012dfabe13055c48219d2a301c8a5a49033a811d8d9413bafbb2eefc177226fe578e93c2ef1f309416dc98843bfac387debb1b610b1d2366178ce7212a7312057a3d058357a629f18c78e129e60979a2310455a76207be5611e8b4b840629564020c17f5c9446882e23f610e931246ec434e62de765bf22954cfae02b2ff7c59dfe246e4bb2d6a8afcebdc2beeaabf2a3f43f95a5ea639853f38719875ecdd2bbc0d81bb2a5ed59553b1e76b6365b74f618f68d1f05b5662cd6e04de896d3ef5dae4149485a5a2093ff4ec74b20b5e5bf8e61b5c65515938c202beab3eea5a498d2f32d4d00a24b826b6efb16013ef54cbe170",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"67c6697351ff4aec29cdbaabf2fbe3467cc254f81be8e78d765a2e63339fc99a66320db73158a35a255d051758e95ed4abb2cdc69bb454110e827441213ddc8770e93ea141e1fc673e017e97eadc6b968f385c2aecb03bfb32af3c54ec18db5c021afe43fbfaaa3afb29d1e6053c7c9475d8be6189f95cbba8990f95b1ebf1b305eff700e9a13ae5ca0bcbd0484764bd1f231ea81c7b64c514735ac55e4b79633b706424119e09dcaad4acf21b10af3b33cde3504847155cbb6f2219ba9b7df50be11a1c7f23f829f8a41b13b5ca4ee8983238e0794d3d34bc5f4e77facb6c05ac86212baa1a55a2be70b5733b045cd33694b3afe2f0e49e4f321549fd824ea90870d4b28a2954489a0abcd50e18a844ac5bf38e4cd72d9b0942e506c433afcda3847f2dadd47647de321cec4ac430f62023856cfbb20704f4ec0bb920ba86c33e05f1ecd96733b79950a3e314d3d934f75ea0f210a8f6059401beb4bc4478fa4969e623d01ada696a7e4c7e5125b34884533a94fb319990325744ee9bbce9e525cf08f5e9e25e5360aad2b2d085fa54d835e8d466826498d9a8877565705a8a3f62802944de7ca5894e5759d351adac869580ec17e485f18c0c66f17cc0",
|
||||||
|
"7cbb22fce466da610b63af62bc83b4692f3affaf271693ac071fb86d11342d",
|
||||||
|
"a5117e70953568bf750862df9e6f92af81677c3a188e847917a4a915bda7792e",
|
||||||
|
"129039b5572e8a7a8131f76a",
|
||||||
|
"588e1356fb8fa32410dad99cf7922aae47b4042502c92f3afe33dc22c1c2e90caf22bc37a254f8dd62a09582c70194f9616982639415178e9fe95740c0f1d497a69b69d4924a7a15290187f9c8acf09cf5b3b3188ecde2d2807207f5bb6a6d3504314b1b47684cf8ba8807eb9a3c497c79ebe1e4c1eca2aa90328563e201425227fca8ee05dcc05fd6c98128626c1e71d2fb3a21860567093db1012dfabe13055c48219d2a301c8a5a49033a811d8d9413bafbb2eefc177226fe578e93c2ef1f309416dc98843bfac387debb1b610b1d2366178ce7212a7312057a3d058357a629f18c78e129e60979a2310455a76207be5611e8b4b840629564020c17f5c9446882e23f610e931246ec434e62de765bf22954cfae02b2ff7c59dfe246e4bb2d6a8afcebdc2beeaabf2a3f43f95a5ea639853f38719875ecdd2bbc0d81bb2a5ed59553b1e76b6365b74f618f68d1f05b5662cd6e04de896d3ef5dae4149485a5a2093ff4ec74b20b5e5bf8e61b5c65515938c202beab3eea5a498d2f32c38dbb37d04f8272e741da2802c54a9d9aaf8ecf38b36fc9ad0079523f6a4abd5281a22697a3180bc02662a7c13ee23599d18e5c48300dbb831509df4c172f53e524b3c15124a87ac73e5028cde6c94d8d",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"67c6697351ff4aec29cdbaabf2fbe3467cc254f81be8e78d765a2e63339fc99a66320db73158a35a255d051758e95ed4abb2cdc69bb454110e827441213ddc8770e93ea141e1fc673e017e97eadc6b968f385c2aecb03bfb32af3c54ec18db5c021afe43fbfaaa3afb29d1e6053c7c9475d8be6189f95cbba8990f95b1ebf1b305eff700e9a13ae5ca0bcbd0484764bd1f231ea81c7b64c514735ac55e4b79633b706424119e09dcaad4acf21b10af3b33cde3504847155cbb6f2219ba9b7df50be11a1c7f23f829f8a41b13b5ca4ee8983238e0794d3d34bc5f4e77facb6c05ac86212baa1a55a2be70b5733b045cd33694b3afe2f0e49e4f321549fd824ea90870d4b28a2954489a0abcd50e18a844ac5bf38e4cd72d9b0942e506c433afcda3847f2dadd47647de321cec4ac430f62023856cfbb20704f4ec0bb920ba86c33e05f1ecd96733b79950a3e314d3d934f75ea0f210a8f6059401beb4bc4478fa4969e623d01ada696a7e4c7e5125b34884533a94fb319990325744ee9bbce9e525",
|
||||||
|
"",
|
||||||
|
"a5117e70953568bf750862df9e6f92af81677c3a188e847917a4a915bda7792e",
|
||||||
|
"129039b5572e8a7a8131f76a",
|
||||||
|
"588e1356fb8fa32410dad99cf7922aae47b4042502c92f3afe33dc22c1c2e90caf22bc37a254f8dd62a09582c70194f9616982639415178e9fe95740c0f1d497a69b69d4924a7a15290187f9c8acf09cf5b3b3188ecde2d2807207f5bb6a6d3504314b1b47684cf8ba8807eb9a3c497c79ebe1e4c1eca2aa90328563e201425227fca8ee05dcc05fd6c98128626c1e71d2fb3a21860567093db1012dfabe13055c48219d2a301c8a5a49033a811d8d9413bafbb2eefc177226fe578e93c2ef1f309416dc98843bfac387debb1b610b1d2366178ce7212a7312057a3d058357a629f18c78e129e60979a2310455a76207be5611e8b4b840629564020c17f5c9446882e23f610e931246ec434e62de765bf22954cfae02b2ff7c59dfe246e4bb2d6a8afcebdc2beeaabf2a3f43f95a5ea639853f38719875ecdd2bbc0d81bb2a5ed59553b1e76b6365b74f618f68d1f05b5662cd6e04de896d3ef5dae4149485a5a2093ff4ec74b20b5e5bf8e61b5c65515938c202beab3eea5a498d2f32c38dbb370a9bbc3187cc260ddac991f94ce4f0d5",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"0fb826ddb2eb5e708de203d0438be12cf708d635ebdbae56278be09077009586b9bc646ba7c2db35a5de05e86ae71461efea96dac64430edcf117d461113cccacf303576f310ab98efb180599894ba877e50614494923163a3afa9b4c2757f91a6b40799c5b331b464b10dfc45c783c317e408ab76390e19e8b7ceaa2c4d3bd201436bc6f69c7a5a4d8756924ed95665bd5e1034971e4d80d51b2a",
|
||||||
|
"026866d46aa940309fdcabf92a324fbc",
|
||||||
|
"a5117e70953568bf750862df9e6f92af81677c3a188e847917a4a915bda7792e",
|
||||||
|
"129039b5572e8a7a8131f76a",
|
||||||
|
"30f05cf8189bb7b8b4f560e746e228c4cc7e86e8f2fa66e1afe212d1855db51070acd5eb34ce80b2e223957df50fde4c2531d97fc9e573725e7a5e47f0dfc4da1942620320bb2deaf8b17937bae4218d04db8e76f6efe84a117292159507c9f8a09fb2c17921d7762510dbf1dac7b62b1bd7572e3e2cf008d01c445c7fa78833235034281ae180e051451c6a64f22ca9708634bd0d604e4cfcd971b13742efa5b6363e662a875daccb2b00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"c7d4f8790e4c47d4daecbddf5939973521ddbf3b832e564afc66f03b5583c41c58bd956609dc3ae3c8f7c2213059575236168dba44e3044049f47c9e7840bbd0fd5036062d70e9f567ac1797056ee93c8476f6c959fa09a3ee854166c6fc36c34d6cca7adcb36f435f86db65f4c4a1793b974294914b377fd179e697751c5ac289243c65d8aca93732849c27483da083d4e218652d4fe5fec8cb953ee7f00070143dd6ece97f241b03c0424bfee2cfd2c4e738f2361df0ffe8863dcf763d408a7a167763959b7f985bc1e359a4b22c6899645ad0814bcf69d10c38474978d1c48e482723e3a6bb3f689f980c51c474eb28cfbba91a8a12eb964b32dfc303a3524ccb752f71316ed9d007e521cb5a0cf429c79d4351b02ee7fb60c7be636a10af3586dfa7b74d80875466a820c0b514e97cb12cce615ab55cba7c1b1de72bcd1cb1acc368f944ef4eaa986e6a4d8253c9337f9795d94df193c90cb0b0387dcde929905223d441717ed9dfe826613bf094ba872993d41b269e27d74e5f541b497eac9ba180dc12ffb6f1e7dc5223cce6dd541071282b97c6526e15b2c330fb41dc96e25d72f45c28e543053766d11d44252db54e584c14abbb295d7e5a58bf36eea1936095ef897a338eb1995fcedd85fc92d354dfe7ff9a115c186bb4d7a1a27835030d248c87571a38f17906cefe0261d15740b9",
|
||||||
|
"56",
|
||||||
|
"a5117e70953568bf750862df9e6f92af81677c3a188e847917a4a915bda7792e",
|
||||||
|
"129039b5572e8a7a8131f76a",
|
||||||
|
"f89c825ca43cae1ce3fbdee85c505edd1aabefe69a0f9efd740f027aa7dee48a91ad24e69ad061648f0a52b4afb19d7ffccdc21f4b4247dfd89f5f9f998cb3c02b226173fedb6f8770aceef9271e7236fefd19fb3b87d08a5c587ac7918e80aa4b477f22602189811e270d686bc4949137a41d11d95ec96ee9d26c6126f6e923ab37638b34d1538d2e46d6df6216da4f193a3cecb731e632e109ced643056a1673059355d2d1314df35ded8364efed7de490201090a6f2d1751748585f64d26041637ba3723cbc4b60e226f10a19699d223075bc1f27d82e7f560c0db630ea670b3f8a70a8950894af4d1c7b3f674a3fa00d19ee4cc2b6174c1d259a297424bf2c3943a29a16a9830ce11abaa79cd2eb77b53a02b365b1838e7bfd5ae1bd044ffc885c61c6b2186a357e8b8f732b7ab96517969aeb70c7b493bbaca9462a61815a3c6135c748bf9c8487ac0631807aa69243fa09cd3b8efb63f8d4e090ad30b6c2f08bf4e82f191cedfa5cbe2b42268d67ecd105918181e44fc9879efd642d20be84e6f74717e03fb94fcbaa6ed3b307431d2a9384b8a2b3e5825ffce8d99af48f177e43bb4272226d8a5edd37d53807f768feb9e0733b437a1d0f84779ab68a1804e92a5eecca56364f0fa6dca152203b249fdc8fbd950fdc37c1887596308a90ba3a5751c7096bfbd1cb177bb17847b33c4379b43938a67674459cd9a06e3017ccac5b",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"135a28170fe89066da7bcff3a9ccc1b27dfe942a6f47b23835ef746aaea63dc10066d90f4e697528e5451b8e11dd408fdbd4b94a1c6c82515bf7bc099df9cb9d5fa4acad0d22d5f267f18078cec107a995c1f3b12d7603886dbf910ab85ca7180053c50e759b00dc8c81555a425c03d71df6894a6c8cd2d94b64e303c08a1bc1dee1cf537ccf300850856292e1656aff5bf349c87f1ca1ca8085cd400fe901edcad04146a0714ef0f6b083d715edd670e020385f3cda29bc5ff6fc6edffe5ca9ce9def6e0e3d5f04ede2db02cfb2",
|
||||||
|
"73afd2ab0e0e8537cae42dc6530dc4afb6934ca6",
|
||||||
|
"a5117e70953568bf750862df9e6f92af81677c3a188e847917a4a915bda7792e",
|
||||||
|
"129039b5572e8a7a8131f76a",
|
||||||
|
"2c125232a59879aee36cacc4aca5085a4688c4f776667a8fbd86862b5cfb1d57c976688fdd652eafa2b88b1b8e358aa2110ff6ef13cdc1ceca9c9f087c35c38d89d6fbd8de89538070f17916ecb19ca3ef4a1c834f0bdaa1df62aaabef2e117106787056c909e61ecd208357dd5c363f11c5d6cf24992cc873cf69f59360a820fcf290bd90b2cab24c47286acb4e1033962b6d41e562a206a94796a8ab1c6b8bade804ff9bdf5ba6062d2c1f8fe0f4dfc05720bd9a612b92c26789f9f6a7ce43f5e8e3aee99a9cd7d6c11eaa611983c36935b0dda57d898a60a0ab7c4b54",
|
||||||
|
},
|
||||||
|
}
|
||||||
+199
@@ -0,0 +1,199 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package ChaCha20 implements the core ChaCha20 function as specified in https://tools.ietf.org/html/rfc7539#section-2.3.
|
||||||
|
package chacha20
|
||||||
|
|
||||||
|
import "encoding/binary"
|
||||||
|
|
||||||
|
const rounds = 20
|
||||||
|
|
||||||
|
// core applies the ChaCha20 core function to 16-byte input in, 32-byte key k,
|
||||||
|
// and 16-byte constant c, and puts the result into 64-byte array out.
|
||||||
|
func core(out *[64]byte, in *[16]byte, k *[32]byte) {
|
||||||
|
j0 := uint32(0x61707865)
|
||||||
|
j1 := uint32(0x3320646e)
|
||||||
|
j2 := uint32(0x79622d32)
|
||||||
|
j3 := uint32(0x6b206574)
|
||||||
|
j4 := binary.LittleEndian.Uint32(k[0:4])
|
||||||
|
j5 := binary.LittleEndian.Uint32(k[4:8])
|
||||||
|
j6 := binary.LittleEndian.Uint32(k[8:12])
|
||||||
|
j7 := binary.LittleEndian.Uint32(k[12:16])
|
||||||
|
j8 := binary.LittleEndian.Uint32(k[16:20])
|
||||||
|
j9 := binary.LittleEndian.Uint32(k[20:24])
|
||||||
|
j10 := binary.LittleEndian.Uint32(k[24:28])
|
||||||
|
j11 := binary.LittleEndian.Uint32(k[28:32])
|
||||||
|
j12 := binary.LittleEndian.Uint32(in[0:4])
|
||||||
|
j13 := binary.LittleEndian.Uint32(in[4:8])
|
||||||
|
j14 := binary.LittleEndian.Uint32(in[8:12])
|
||||||
|
j15 := binary.LittleEndian.Uint32(in[12:16])
|
||||||
|
|
||||||
|
x0, x1, x2, x3, x4, x5, x6, x7 := j0, j1, j2, j3, j4, j5, j6, j7
|
||||||
|
x8, x9, x10, x11, x12, x13, x14, x15 := j8, j9, j10, j11, j12, j13, j14, j15
|
||||||
|
|
||||||
|
for i := 0; i < rounds; i += 2 {
|
||||||
|
x0 += x4
|
||||||
|
x12 ^= x0
|
||||||
|
x12 = (x12 << 16) | (x12 >> (16))
|
||||||
|
x8 += x12
|
||||||
|
x4 ^= x8
|
||||||
|
x4 = (x4 << 12) | (x4 >> (20))
|
||||||
|
x0 += x4
|
||||||
|
x12 ^= x0
|
||||||
|
x12 = (x12 << 8) | (x12 >> (24))
|
||||||
|
x8 += x12
|
||||||
|
x4 ^= x8
|
||||||
|
x4 = (x4 << 7) | (x4 >> (25))
|
||||||
|
x1 += x5
|
||||||
|
x13 ^= x1
|
||||||
|
x13 = (x13 << 16) | (x13 >> 16)
|
||||||
|
x9 += x13
|
||||||
|
x5 ^= x9
|
||||||
|
x5 = (x5 << 12) | (x5 >> 20)
|
||||||
|
x1 += x5
|
||||||
|
x13 ^= x1
|
||||||
|
x13 = (x13 << 8) | (x13 >> 24)
|
||||||
|
x9 += x13
|
||||||
|
x5 ^= x9
|
||||||
|
x5 = (x5 << 7) | (x5 >> 25)
|
||||||
|
x2 += x6
|
||||||
|
x14 ^= x2
|
||||||
|
x14 = (x14 << 16) | (x14 >> 16)
|
||||||
|
x10 += x14
|
||||||
|
x6 ^= x10
|
||||||
|
x6 = (x6 << 12) | (x6 >> 20)
|
||||||
|
x2 += x6
|
||||||
|
x14 ^= x2
|
||||||
|
x14 = (x14 << 8) | (x14 >> 24)
|
||||||
|
x10 += x14
|
||||||
|
x6 ^= x10
|
||||||
|
x6 = (x6 << 7) | (x6 >> 25)
|
||||||
|
x3 += x7
|
||||||
|
x15 ^= x3
|
||||||
|
x15 = (x15 << 16) | (x15 >> 16)
|
||||||
|
x11 += x15
|
||||||
|
x7 ^= x11
|
||||||
|
x7 = (x7 << 12) | (x7 >> 20)
|
||||||
|
x3 += x7
|
||||||
|
x15 ^= x3
|
||||||
|
x15 = (x15 << 8) | (x15 >> 24)
|
||||||
|
x11 += x15
|
||||||
|
x7 ^= x11
|
||||||
|
x7 = (x7 << 7) | (x7 >> 25)
|
||||||
|
x0 += x5
|
||||||
|
x15 ^= x0
|
||||||
|
x15 = (x15 << 16) | (x15 >> 16)
|
||||||
|
x10 += x15
|
||||||
|
x5 ^= x10
|
||||||
|
x5 = (x5 << 12) | (x5 >> 20)
|
||||||
|
x0 += x5
|
||||||
|
x15 ^= x0
|
||||||
|
x15 = (x15 << 8) | (x15 >> 24)
|
||||||
|
x10 += x15
|
||||||
|
x5 ^= x10
|
||||||
|
x5 = (x5 << 7) | (x5 >> 25)
|
||||||
|
x1 += x6
|
||||||
|
x12 ^= x1
|
||||||
|
x12 = (x12 << 16) | (x12 >> 16)
|
||||||
|
x11 += x12
|
||||||
|
x6 ^= x11
|
||||||
|
x6 = (x6 << 12) | (x6 >> 20)
|
||||||
|
x1 += x6
|
||||||
|
x12 ^= x1
|
||||||
|
x12 = (x12 << 8) | (x12 >> 24)
|
||||||
|
x11 += x12
|
||||||
|
x6 ^= x11
|
||||||
|
x6 = (x6 << 7) | (x6 >> 25)
|
||||||
|
x2 += x7
|
||||||
|
x13 ^= x2
|
||||||
|
x13 = (x13 << 16) | (x13 >> 16)
|
||||||
|
x8 += x13
|
||||||
|
x7 ^= x8
|
||||||
|
x7 = (x7 << 12) | (x7 >> 20)
|
||||||
|
x2 += x7
|
||||||
|
x13 ^= x2
|
||||||
|
x13 = (x13 << 8) | (x13 >> 24)
|
||||||
|
x8 += x13
|
||||||
|
x7 ^= x8
|
||||||
|
x7 = (x7 << 7) | (x7 >> 25)
|
||||||
|
x3 += x4
|
||||||
|
x14 ^= x3
|
||||||
|
x14 = (x14 << 16) | (x14 >> 16)
|
||||||
|
x9 += x14
|
||||||
|
x4 ^= x9
|
||||||
|
x4 = (x4 << 12) | (x4 >> 20)
|
||||||
|
x3 += x4
|
||||||
|
x14 ^= x3
|
||||||
|
x14 = (x14 << 8) | (x14 >> 24)
|
||||||
|
x9 += x14
|
||||||
|
x4 ^= x9
|
||||||
|
x4 = (x4 << 7) | (x4 >> 25)
|
||||||
|
}
|
||||||
|
|
||||||
|
x0 += j0
|
||||||
|
x1 += j1
|
||||||
|
x2 += j2
|
||||||
|
x3 += j3
|
||||||
|
x4 += j4
|
||||||
|
x5 += j5
|
||||||
|
x6 += j6
|
||||||
|
x7 += j7
|
||||||
|
x8 += j8
|
||||||
|
x9 += j9
|
||||||
|
x10 += j10
|
||||||
|
x11 += j11
|
||||||
|
x12 += j12
|
||||||
|
x13 += j13
|
||||||
|
x14 += j14
|
||||||
|
x15 += j15
|
||||||
|
|
||||||
|
binary.LittleEndian.PutUint32(out[0:4], x0)
|
||||||
|
binary.LittleEndian.PutUint32(out[4:8], x1)
|
||||||
|
binary.LittleEndian.PutUint32(out[8:12], x2)
|
||||||
|
binary.LittleEndian.PutUint32(out[12:16], x3)
|
||||||
|
binary.LittleEndian.PutUint32(out[16:20], x4)
|
||||||
|
binary.LittleEndian.PutUint32(out[20:24], x5)
|
||||||
|
binary.LittleEndian.PutUint32(out[24:28], x6)
|
||||||
|
binary.LittleEndian.PutUint32(out[28:32], x7)
|
||||||
|
binary.LittleEndian.PutUint32(out[32:36], x8)
|
||||||
|
binary.LittleEndian.PutUint32(out[36:40], x9)
|
||||||
|
binary.LittleEndian.PutUint32(out[40:44], x10)
|
||||||
|
binary.LittleEndian.PutUint32(out[44:48], x11)
|
||||||
|
binary.LittleEndian.PutUint32(out[48:52], x12)
|
||||||
|
binary.LittleEndian.PutUint32(out[52:56], x13)
|
||||||
|
binary.LittleEndian.PutUint32(out[56:60], x14)
|
||||||
|
binary.LittleEndian.PutUint32(out[60:64], x15)
|
||||||
|
}
|
||||||
|
|
||||||
|
// XORKeyStream crypts bytes from in to out using the given key and counters.
|
||||||
|
// In and out may be the same slice but otherwise should not overlap. Counter
|
||||||
|
// contains the raw ChaCha20 counter bytes (i.e. block counter followed by
|
||||||
|
// nonce).
|
||||||
|
func XORKeyStream(out, in []byte, counter *[16]byte, key *[32]byte) {
|
||||||
|
var block [64]byte
|
||||||
|
var counterCopy [16]byte
|
||||||
|
copy(counterCopy[:], counter[:])
|
||||||
|
|
||||||
|
for len(in) >= 64 {
|
||||||
|
core(&block, &counterCopy, key)
|
||||||
|
for i, x := range block {
|
||||||
|
out[i] = in[i] ^ x
|
||||||
|
}
|
||||||
|
u := uint32(1)
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
u += uint32(counterCopy[i])
|
||||||
|
counterCopy[i] = byte(u)
|
||||||
|
u >>= 8
|
||||||
|
}
|
||||||
|
in = in[64:]
|
||||||
|
out = out[64:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(in) > 0 {
|
||||||
|
core(&block, &counterCopy, key)
|
||||||
|
for i, v := range in {
|
||||||
|
out[i] = v ^ block[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package chacha20
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCore(t *testing.T) {
|
||||||
|
// This is just a smoke test that checks the example from
|
||||||
|
// https://tools.ietf.org/html/rfc7539#section-2.3.2. The
|
||||||
|
// chacha20poly1305 package contains much more extensive tests of this
|
||||||
|
// code.
|
||||||
|
var key [32]byte
|
||||||
|
for i := range key {
|
||||||
|
key[i] = byte(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
var input [16]byte
|
||||||
|
input[0] = 1
|
||||||
|
input[7] = 9
|
||||||
|
input[11] = 0x4a
|
||||||
|
|
||||||
|
var out [64]byte
|
||||||
|
XORKeyStream(out[:], out[:], &input, &key)
|
||||||
|
const expected = "10f1e7e4d13b5915500fdd1fa32071c4c7d1f4c733c068030422aa9ac3d46c4ed2826446079faa0914c2d705d98b02a2b5129cd1de164eb9cbd083e8a2503c4e"
|
||||||
|
if result := hex.EncodeToString(out[:]); result != expected {
|
||||||
|
t.Errorf("wanted %x but got %x", expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
issuerepo: golang/go
|
||||||
+604
@@ -0,0 +1,604 @@
|
|||||||
|
// Copyright 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package cryptobyte
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/asn1"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This file contains ASN.1-related methods for String and Builder.
|
||||||
|
|
||||||
|
// Tag represents an ASN.1 tag number and class (together also referred to as
|
||||||
|
// identifier octets). Methods in this package only support the low-tag-number
|
||||||
|
// form, i.e. a single identifier octet with bits 7-8 encoding the class and
|
||||||
|
// bits 1-6 encoding the tag number.
|
||||||
|
type Tag uint8
|
||||||
|
|
||||||
|
// Contructed returns t with the context-specific class bit set.
|
||||||
|
func (t Tag) ContextSpecific() Tag { return t | 0x80 }
|
||||||
|
|
||||||
|
// Contructed returns t with the constructed class bit set.
|
||||||
|
func (t Tag) Constructed() Tag { return t | 0x20 }
|
||||||
|
|
||||||
|
// Builder
|
||||||
|
|
||||||
|
// AddASN1Int64 appends a DER-encoded ASN.1 INTEGER.
|
||||||
|
func (b *Builder) AddASN1Int64(v int64) {
|
||||||
|
b.addASN1Signed(asn1.TagInteger, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddASN1Enum appends a DER-encoded ASN.1 ENUMERATION.
|
||||||
|
func (b *Builder) AddASN1Enum(v int64) {
|
||||||
|
b.addASN1Signed(asn1.TagEnum, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) addASN1Signed(tag Tag, v int64) {
|
||||||
|
b.AddASN1(tag, func(c *Builder) {
|
||||||
|
length := 1
|
||||||
|
for i := v; i >= 0x80 || i < -0x80; i >>= 8 {
|
||||||
|
length++
|
||||||
|
}
|
||||||
|
|
||||||
|
for ; length > 0; length-- {
|
||||||
|
i := v >> uint((length-1)*8) & 0xff
|
||||||
|
c.AddUint8(uint8(i))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddASN1Uint64 appends a DER-encoded ASN.1 INTEGER.
|
||||||
|
func (b *Builder) AddASN1Uint64(v uint64) {
|
||||||
|
b.AddASN1(asn1.TagInteger, func(c *Builder) {
|
||||||
|
length := 1
|
||||||
|
for i := v; i >= 0x80; i >>= 8 {
|
||||||
|
length++
|
||||||
|
}
|
||||||
|
|
||||||
|
for ; length > 0; length-- {
|
||||||
|
i := v >> uint((length-1)*8) & 0xff
|
||||||
|
c.AddUint8(uint8(i))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddASN1BigInt appends a DER-encoded ASN.1 INTEGER.
|
||||||
|
func (b *Builder) AddASN1BigInt(n *big.Int) {
|
||||||
|
if b.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b.AddASN1(asn1.TagInteger, func(c *Builder) {
|
||||||
|
if n.Sign() < 0 {
|
||||||
|
// A negative number has to be converted to two's-complement form. So we
|
||||||
|
// invert and subtract 1. If the most-significant-bit isn't set then
|
||||||
|
// we'll need to pad the beginning with 0xff in order to keep the number
|
||||||
|
// negative.
|
||||||
|
nMinus1 := new(big.Int).Neg(n)
|
||||||
|
nMinus1.Sub(nMinus1, bigOne)
|
||||||
|
bytes := nMinus1.Bytes()
|
||||||
|
for i := range bytes {
|
||||||
|
bytes[i] ^= 0xff
|
||||||
|
}
|
||||||
|
if bytes[0]&0x80 == 0 {
|
||||||
|
c.add(0xff)
|
||||||
|
}
|
||||||
|
c.add(bytes...)
|
||||||
|
} else if n.Sign() == 0 {
|
||||||
|
c.add(0)
|
||||||
|
} else {
|
||||||
|
bytes := n.Bytes()
|
||||||
|
if bytes[0]&0x80 != 0 {
|
||||||
|
c.add(0)
|
||||||
|
}
|
||||||
|
c.add(bytes...)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddASN1OctetString appends a DER-encoded ASN.1 OCTET STRING.
|
||||||
|
func (b *Builder) AddASN1OctetString(bytes []byte) {
|
||||||
|
b.AddASN1(asn1.TagOctetString, func(c *Builder) {
|
||||||
|
c.AddBytes(bytes)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const generalizedTimeFormatStr = "20060102150405Z0700"
|
||||||
|
|
||||||
|
// AddASN1GeneralizedTime appends a DER-encoded ASN.1 GENERALIZEDTIME.
|
||||||
|
func (b *Builder) AddASN1GeneralizedTime(t time.Time) {
|
||||||
|
if t.Year() < 0 || t.Year() > 9999 {
|
||||||
|
b.err = fmt.Errorf("cryptobyte: cannot represent %v as a GeneralizedTime", t)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b.AddASN1(asn1.TagGeneralizedTime, func(c *Builder) {
|
||||||
|
c.AddBytes([]byte(t.Format(generalizedTimeFormatStr)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddASN1BitString appends a DER-encoded ASN.1 BIT STRING.
|
||||||
|
func (b *Builder) AddASN1BitString(s asn1.BitString) {
|
||||||
|
// TODO(martinkr): Implement.
|
||||||
|
b.MarshalASN1(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalASN1 calls asn1.Marshal on its input and appends the result if
|
||||||
|
// successful or records an error if one occurred.
|
||||||
|
func (b *Builder) MarshalASN1(v interface{}) {
|
||||||
|
// NOTE(martinkr): This is somewhat of a hack to allow propagation of
|
||||||
|
// asn1.Marshal errors into Builder.err. N.B. if you call MarshalASN1 with a
|
||||||
|
// value embedded into a struct, its tag information is lost.
|
||||||
|
if b.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
bytes, err := asn1.Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
b.err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b.AddBytes(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddASN1 appends an ASN.1 object. The object is prefixed with the given tag.
|
||||||
|
// Tags greater than 30 are not supported and result in an error (i.e.
|
||||||
|
// low-tag-number form only). The child builder passed to the
|
||||||
|
// BuilderContinuation can be used to build the content of the ASN.1 object.
|
||||||
|
func (b *Builder) AddASN1(tag Tag, f BuilderContinuation) {
|
||||||
|
if b.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Identifiers with the low five bits set indicate high-tag-number format
|
||||||
|
// (two or more octets), which we don't support.
|
||||||
|
if tag&0x1f == 0x1f {
|
||||||
|
b.err = fmt.Errorf("cryptobyte: high-tag number identifier octects not supported: 0x%x", tag)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b.AddUint8(uint8(tag))
|
||||||
|
b.addLengthPrefixed(1, true, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// String
|
||||||
|
|
||||||
|
var bigIntType = reflect.TypeOf((*big.Int)(nil)).Elem()
|
||||||
|
|
||||||
|
// ReadASN1Integer decodes an ASN.1 INTEGER into out and advances. If out does
|
||||||
|
// not point to an integer or to a big.Int, it panics. It returns true on
|
||||||
|
// success and false on error.
|
||||||
|
func (s *String) ReadASN1Integer(out interface{}) bool {
|
||||||
|
if reflect.TypeOf(out).Kind() != reflect.Ptr {
|
||||||
|
panic("out is not a pointer")
|
||||||
|
}
|
||||||
|
switch reflect.ValueOf(out).Elem().Kind() {
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
var i int64
|
||||||
|
if !s.readASN1Int64(&i) || reflect.ValueOf(out).Elem().OverflowInt(i) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
reflect.ValueOf(out).Elem().SetInt(i)
|
||||||
|
return true
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
var u uint64
|
||||||
|
if !s.readASN1Uint64(&u) || reflect.ValueOf(out).Elem().OverflowUint(u) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
reflect.ValueOf(out).Elem().SetUint(u)
|
||||||
|
return true
|
||||||
|
case reflect.Struct:
|
||||||
|
if reflect.TypeOf(out).Elem() == bigIntType {
|
||||||
|
return s.readASN1BigInt(out.(*big.Int))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic("out does not point to an integer type")
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkASN1Integer(bytes []byte) bool {
|
||||||
|
if len(bytes) == 0 {
|
||||||
|
// An INTEGER is encoded with at least one octet.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(bytes) == 1 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if bytes[0] == 0 && bytes[1]&0x80 == 0 || bytes[0] == 0xff && bytes[1]&0x80 == 0x80 {
|
||||||
|
// Value is not minimally encoded.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var bigOne = big.NewInt(1)
|
||||||
|
|
||||||
|
func (s *String) readASN1BigInt(out *big.Int) bool {
|
||||||
|
var bytes String
|
||||||
|
if !s.ReadASN1(&bytes, asn1.TagInteger) || !checkASN1Integer(bytes) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if bytes[0]&0x80 == 0x80 {
|
||||||
|
// Negative number.
|
||||||
|
neg := make([]byte, len(bytes))
|
||||||
|
for i, b := range bytes {
|
||||||
|
neg[i] = ^b
|
||||||
|
}
|
||||||
|
out.SetBytes(neg)
|
||||||
|
out.Add(out, bigOne)
|
||||||
|
out.Neg(out)
|
||||||
|
} else {
|
||||||
|
out.SetBytes(bytes)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *String) readASN1Int64(out *int64) bool {
|
||||||
|
var bytes String
|
||||||
|
if !s.ReadASN1(&bytes, asn1.TagInteger) || !checkASN1Integer(bytes) || !asn1Signed(out, bytes) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func asn1Signed(out *int64, n []byte) bool {
|
||||||
|
length := len(n)
|
||||||
|
if length > 8 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
*out <<= 8
|
||||||
|
*out |= int64(n[i])
|
||||||
|
}
|
||||||
|
// Shift up and down in order to sign extend the result.
|
||||||
|
*out <<= 64 - uint8(length)*8
|
||||||
|
*out >>= 64 - uint8(length)*8
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *String) readASN1Uint64(out *uint64) bool {
|
||||||
|
var bytes String
|
||||||
|
if !s.ReadASN1(&bytes, asn1.TagInteger) || !checkASN1Integer(bytes) || !asn1Unsigned(out, bytes) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func asn1Unsigned(out *uint64, n []byte) bool {
|
||||||
|
length := len(n)
|
||||||
|
if length > 9 || length == 9 && n[0] != 0 {
|
||||||
|
// Too large for uint64.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if n[0]&0x80 != 0 {
|
||||||
|
// Negative number.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
*out <<= 8
|
||||||
|
*out |= uint64(n[i])
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadASN1Enum decodes an ASN.1 ENUMERATION into out and advances. It returns
|
||||||
|
// true on success and false on error.
|
||||||
|
func (s *String) ReadASN1Enum(out *int) bool {
|
||||||
|
var bytes String
|
||||||
|
var i int64
|
||||||
|
if !s.ReadASN1(&bytes, asn1.TagEnum) || !checkASN1Integer(bytes) || !asn1Signed(&i, bytes) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if int64(int(i)) != i {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
*out = int(i)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *String) readBase128Int(out *int) bool {
|
||||||
|
ret := 0
|
||||||
|
for i := 0; len(*s) > 0; i++ {
|
||||||
|
if i == 4 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
ret <<= 7
|
||||||
|
b := s.read(1)[0]
|
||||||
|
ret |= int(b & 0x7f)
|
||||||
|
if b&0x80 == 0 {
|
||||||
|
*out = ret
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false // truncated
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadASN1ObjectIdentifier decodes an ASN.1 OBJECT IDENTIFIER into out and
|
||||||
|
// advances. It returns true on success and false on error.
|
||||||
|
func (s *String) ReadASN1ObjectIdentifier(out *asn1.ObjectIdentifier) bool {
|
||||||
|
var bytes String
|
||||||
|
if !s.ReadASN1(&bytes, asn1.TagOID) || len(bytes) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// In the worst case, we get two elements from the first byte (which is
|
||||||
|
// encoded differently) and then every varint is a single byte long.
|
||||||
|
components := make([]int, len(bytes)+1)
|
||||||
|
|
||||||
|
// The first varint is 40*value1 + value2:
|
||||||
|
// According to this packing, value1 can take the values 0, 1 and 2 only.
|
||||||
|
// When value1 = 0 or value1 = 1, then value2 is <= 39. When value1 = 2,
|
||||||
|
// then there are no restrictions on value2.
|
||||||
|
var v int
|
||||||
|
if !bytes.readBase128Int(&v) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if v < 80 {
|
||||||
|
components[0] = v / 40
|
||||||
|
components[1] = v % 40
|
||||||
|
} else {
|
||||||
|
components[0] = 2
|
||||||
|
components[1] = v - 80
|
||||||
|
}
|
||||||
|
|
||||||
|
i := 2
|
||||||
|
for ; len(bytes) > 0; i++ {
|
||||||
|
if !bytes.readBase128Int(&v) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
components[i] = v
|
||||||
|
}
|
||||||
|
*out = components[:i]
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadASN1GeneralizedTime decodes an ASN.1 GENERALIZEDTIME into out and
|
||||||
|
// advances. It returns true on success and false on error.
|
||||||
|
func (s *String) ReadASN1GeneralizedTime(out *time.Time) bool {
|
||||||
|
var bytes String
|
||||||
|
if !s.ReadASN1(&bytes, asn1.TagGeneralizedTime) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
t := string(bytes)
|
||||||
|
res, err := time.Parse(generalizedTimeFormatStr, t)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if serialized := res.Format(generalizedTimeFormatStr); serialized != t {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
*out = res
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadASN1BitString decodes an ASN.1 BIT STRING into out and advances. It
|
||||||
|
// returns true on success and false on error.
|
||||||
|
func (s *String) ReadASN1BitString(out *asn1.BitString) bool {
|
||||||
|
var bytes String
|
||||||
|
if !s.ReadASN1(&bytes, asn1.TagBitString) || len(bytes) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
paddingBits := uint8(bytes[0])
|
||||||
|
bytes = bytes[1:]
|
||||||
|
if paddingBits > 7 ||
|
||||||
|
len(bytes) == 0 && paddingBits != 0 ||
|
||||||
|
len(bytes) > 0 && bytes[len(bytes)-1]&(1<<paddingBits-1) != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
out.BitLength = len(bytes)*8 - int(paddingBits)
|
||||||
|
out.Bytes = bytes
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadASN1Bytes reads the contents of a DER-encoded ASN.1 element (not including
|
||||||
|
// tag and length bytes) into out, and advances. The element must match the
|
||||||
|
// given tag. It returns true on success and false on error.
|
||||||
|
func (s *String) ReadASN1Bytes(out *[]byte, tag Tag) bool {
|
||||||
|
return s.ReadASN1((*String)(out), tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadASN1 reads the contents of a DER-encoded ASN.1 element (not including
|
||||||
|
// tag and length bytes) into out, and advances. The element must match the
|
||||||
|
// given tag. It returns true on success and false on error.
|
||||||
|
//
|
||||||
|
// Tags greater than 30 are not supported (i.e. low-tag-number format only).
|
||||||
|
func (s *String) ReadASN1(out *String, tag Tag) bool {
|
||||||
|
var t Tag
|
||||||
|
if !s.ReadAnyASN1(out, &t) || t != tag {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadASN1Element reads the contents of a DER-encoded ASN.1 element (including
|
||||||
|
// tag and length bytes) into out, and advances. The element must match the
|
||||||
|
// given tag. It returns true on success and false on error.
|
||||||
|
//
|
||||||
|
// Tags greater than 30 are not supported (i.e. low-tag-number format only).
|
||||||
|
func (s *String) ReadASN1Element(out *String, tag Tag) bool {
|
||||||
|
var t Tag
|
||||||
|
if !s.ReadAnyASN1Element(out, &t) || t != tag {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadAnyASN1 reads the contents of a DER-encoded ASN.1 element (not including
|
||||||
|
// tag and length bytes) into out, sets outTag to its tag, and advances. It
|
||||||
|
// returns true on success and false on error.
|
||||||
|
//
|
||||||
|
// Tags greater than 30 are not supported (i.e. low-tag-number format only).
|
||||||
|
func (s *String) ReadAnyASN1(out *String, outTag *Tag) bool {
|
||||||
|
return s.readASN1(out, outTag, true /* skip header */)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadAnyASN1Element reads the contents of a DER-encoded ASN.1 element
|
||||||
|
// (including tag and length bytes) into out, sets outTag to is tag, and
|
||||||
|
// advances. It returns true on success and false on error.
|
||||||
|
//
|
||||||
|
// Tags greater than 30 are not supported (i.e. low-tag-number format only).
|
||||||
|
func (s *String) ReadAnyASN1Element(out *String, outTag *Tag) bool {
|
||||||
|
return s.readASN1(out, outTag, false /* include header */)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PeekASN1Tag returns true if the next ASN.1 value on the string starts with
|
||||||
|
// the given tag.
|
||||||
|
func (s String) PeekASN1Tag(tag Tag) bool {
|
||||||
|
if len(s) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return Tag(s[0]) == tag
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadOptionalASN1 attempts to read the contents of a DER-encoded ASN.Element
|
||||||
|
// (not including tag and length bytes) tagged with the given tag into out. It
|
||||||
|
// stores whether an element with the tag was found in outPresent, unless
|
||||||
|
// outPresent is nil. It returns true on success and false on error.
|
||||||
|
func (s *String) ReadOptionalASN1(out *String, outPresent *bool, tag Tag) bool {
|
||||||
|
present := s.PeekASN1Tag(tag)
|
||||||
|
if outPresent != nil {
|
||||||
|
*outPresent = present
|
||||||
|
}
|
||||||
|
if present && !s.ReadASN1(out, tag) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadOptionalASN1Integer attempts to read an optional ASN.1 INTEGER
|
||||||
|
// explicitly tagged with tag into out and advances. If no element with a
|
||||||
|
// matching tag is present, it writes defaultValue into out instead. If out
|
||||||
|
// does not point to an integer or to a big.Int, it panics. It returns true on
|
||||||
|
// success and false on error.
|
||||||
|
func (s *String) ReadOptionalASN1Integer(out interface{}, tag Tag, defaultValue interface{}) bool {
|
||||||
|
if reflect.TypeOf(out).Kind() != reflect.Ptr {
|
||||||
|
panic("out is not a pointer")
|
||||||
|
}
|
||||||
|
var present bool
|
||||||
|
var i String
|
||||||
|
if !s.ReadOptionalASN1(&i, &present, tag) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !present {
|
||||||
|
switch reflect.ValueOf(out).Elem().Kind() {
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||||
|
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
reflect.ValueOf(out).Elem().Set(reflect.ValueOf(defaultValue))
|
||||||
|
case reflect.Struct:
|
||||||
|
if reflect.TypeOf(out).Elem() != bigIntType {
|
||||||
|
panic("invalid integer type")
|
||||||
|
}
|
||||||
|
if reflect.TypeOf(defaultValue).Kind() != reflect.Ptr ||
|
||||||
|
reflect.TypeOf(defaultValue).Elem() != bigIntType {
|
||||||
|
panic("out points to big.Int, but defaultValue does not")
|
||||||
|
}
|
||||||
|
out.(*big.Int).Set(defaultValue.(*big.Int))
|
||||||
|
default:
|
||||||
|
panic("invalid integer type")
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !i.ReadASN1Integer(out) || !i.Empty() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadOptionalASN1OctetString attempts to read an optional ASN.1 OCTET STRING
|
||||||
|
// explicitly tagged with tag into out and advances. If no element with a
|
||||||
|
// matching tag is present, it writes defaultValue into out instead. It returns
|
||||||
|
// true on success and false on error.
|
||||||
|
func (s *String) ReadOptionalASN1OctetString(out *[]byte, outPresent *bool, tag Tag) bool {
|
||||||
|
var present bool
|
||||||
|
var child String
|
||||||
|
if !s.ReadOptionalASN1(&child, &present, tag) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if outPresent != nil {
|
||||||
|
*outPresent = present
|
||||||
|
}
|
||||||
|
if present {
|
||||||
|
var oct String
|
||||||
|
if !child.ReadASN1(&oct, asn1.TagOctetString) || !child.Empty() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
*out = oct
|
||||||
|
} else {
|
||||||
|
*out = nil
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *String) readASN1(out *String, outTag *Tag, skipHeader bool) bool {
|
||||||
|
if len(*s) < 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
tag, lenByte := (*s)[0], (*s)[1]
|
||||||
|
|
||||||
|
if tag&0x1f == 0x1f {
|
||||||
|
// ITU-T X.690 section 8.1.2
|
||||||
|
//
|
||||||
|
// An identifier octet with a tag part of 0x1f indicates a high-tag-number
|
||||||
|
// form identifier with two or more octets. We only support tags less than
|
||||||
|
// 31 (i.e. low-tag-number form, single octet identifier).
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if outTag != nil {
|
||||||
|
*outTag = Tag(tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ITU-T X.690 section 8.1.3
|
||||||
|
//
|
||||||
|
// Bit 8 of the first length byte indicates whether the length is short- or
|
||||||
|
// long-form.
|
||||||
|
var length, headerLen uint32 // length includes headerLen
|
||||||
|
if lenByte&0x80 == 0 {
|
||||||
|
// Short-form length (section 8.1.3.4), encoded in bits 1-7.
|
||||||
|
length = uint32(lenByte) + 2
|
||||||
|
headerLen = 2
|
||||||
|
} else {
|
||||||
|
// Long-form length (section 8.1.3.5). Bits 1-7 encode the number of octets
|
||||||
|
// used to encode the length.
|
||||||
|
lenLen := lenByte & 0x7f
|
||||||
|
var len32 uint32
|
||||||
|
|
||||||
|
if lenLen == 0 || lenLen > 4 || len(*s) < int(2+lenLen) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
lenBytes := String((*s)[2 : 2+lenLen])
|
||||||
|
if !lenBytes.readUnsigned(&len32, int(lenLen)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ITU-T X.690 section 10.1 (DER length forms) requires encoding the length
|
||||||
|
// with the minimum number of octets.
|
||||||
|
if len32 < 128 {
|
||||||
|
// Length should have used short-form encoding.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len32>>((lenLen-1)*8) == 0 {
|
||||||
|
// Leading octet is 0. Length should have been at least one byte shorter.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
headerLen = 2 + uint32(lenLen)
|
||||||
|
if headerLen+len32 < len32 {
|
||||||
|
// Overflow.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
length = headerLen + len32
|
||||||
|
}
|
||||||
|
|
||||||
|
if uint32(int(length)) != length || !s.ReadBytes((*[]byte)(out), int(length)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if skipHeader && !out.Skip(int(headerLen)) {
|
||||||
|
panic("cryptobyte: internal error")
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
+285
@@ -0,0 +1,285 @@
|
|||||||
|
// Copyright 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package cryptobyte
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/asn1"
|
||||||
|
"math/big"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type readASN1Test struct {
|
||||||
|
name string
|
||||||
|
in []byte
|
||||||
|
tag Tag
|
||||||
|
ok bool
|
||||||
|
out interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var readASN1TestData = []readASN1Test{
|
||||||
|
{"valid", []byte{0x30, 2, 1, 2}, 0x30, true, []byte{1, 2}},
|
||||||
|
{"truncated", []byte{0x30, 3, 1, 2}, 0x30, false, nil},
|
||||||
|
{"zero length of length", []byte{0x30, 0x80}, 0x30, false, nil},
|
||||||
|
{"invalid long form length", []byte{0x30, 0x81, 1, 1}, 0x30, false, nil},
|
||||||
|
{"non-minimal length", append([]byte{0x30, 0x82, 0, 0x80}, make([]byte, 0x80)...), 0x30, false, nil},
|
||||||
|
{"invalid tag", []byte{0xa1, 3, 0x4, 1, 1}, 31, false, nil},
|
||||||
|
{"high tag", []byte{0x1f, 0x81, 0x80, 0x01, 2, 1, 2}, 0xff /* actually 0x4001, but tag is uint8 */, false, nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadASN1(t *testing.T) {
|
||||||
|
for _, test := range readASN1TestData {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
var in, out String = test.in, nil
|
||||||
|
ok := in.ReadASN1(&out, test.tag)
|
||||||
|
if ok != test.ok || ok && !bytes.Equal(out, test.out.([]byte)) {
|
||||||
|
t.Errorf("in.ReadASN1() = %v, want %v; out = %v, want %v", ok, test.ok, out, test.out)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadASN1Optional(t *testing.T) {
|
||||||
|
var empty String
|
||||||
|
var present bool
|
||||||
|
ok := empty.ReadOptionalASN1(nil, &present, 0xa0)
|
||||||
|
if !ok || present {
|
||||||
|
t.Errorf("empty.ReadOptionalASN1() = %v, want true; present = %v want false", ok, present)
|
||||||
|
}
|
||||||
|
|
||||||
|
var in, out String = []byte{0xa1, 3, 0x4, 1, 1}, nil
|
||||||
|
ok = in.ReadOptionalASN1(&out, &present, 0xa0)
|
||||||
|
if !ok || present {
|
||||||
|
t.Errorf("in.ReadOptionalASN1() = %v, want true, present = %v, want false", ok, present)
|
||||||
|
}
|
||||||
|
ok = in.ReadOptionalASN1(&out, &present, 0xa1)
|
||||||
|
wantBytes := []byte{4, 1, 1}
|
||||||
|
if !ok || !present || !bytes.Equal(out, wantBytes) {
|
||||||
|
t.Errorf("in.ReadOptionalASN1() = %v, want true; present = %v, want true; out = %v, want = %v", ok, present, out, wantBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var optionalOctetStringTestData = []struct {
|
||||||
|
readASN1Test
|
||||||
|
present bool
|
||||||
|
}{
|
||||||
|
{readASN1Test{"empty", []byte{}, 0xa0, true, []byte{}}, false},
|
||||||
|
{readASN1Test{"invalid", []byte{0xa1, 3, 0x4, 2, 1}, 0xa1, false, []byte{}}, true},
|
||||||
|
{readASN1Test{"missing", []byte{0xa1, 3, 0x4, 1, 1}, 0xa0, true, []byte{}}, false},
|
||||||
|
{readASN1Test{"present", []byte{0xa1, 3, 0x4, 1, 1}, 0xa1, true, []byte{1}}, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadASN1OptionalOctetString(t *testing.T) {
|
||||||
|
for _, test := range optionalOctetStringTestData {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
in := String(test.in)
|
||||||
|
var out []byte
|
||||||
|
var present bool
|
||||||
|
ok := in.ReadOptionalASN1OctetString(&out, &present, test.tag)
|
||||||
|
if ok != test.ok || present != test.present || !bytes.Equal(out, test.out.([]byte)) {
|
||||||
|
t.Errorf("in.ReadOptionalASN1OctetString() = %v, want %v; present = %v want %v; out = %v, want %v", ok, test.ok, present, test.present, out, test.out)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultInt = -1
|
||||||
|
|
||||||
|
var optionalIntTestData = []readASN1Test{
|
||||||
|
{"empty", []byte{}, 0xa0, true, defaultInt},
|
||||||
|
{"invalid", []byte{0xa1, 3, 0x2, 2, 127}, 0xa1, false, 0},
|
||||||
|
{"missing", []byte{0xa1, 3, 0x2, 1, 127}, 0xa0, true, defaultInt},
|
||||||
|
{"present", []byte{0xa1, 3, 0x2, 1, 42}, 0xa1, true, 42},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadASN1OptionalInteger(t *testing.T) {
|
||||||
|
for _, test := range optionalIntTestData {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
in := String(test.in)
|
||||||
|
var out int
|
||||||
|
ok := in.ReadOptionalASN1Integer(&out, test.tag, defaultInt)
|
||||||
|
if ok != test.ok || ok && out != test.out.(int) {
|
||||||
|
t.Errorf("in.ReadOptionalASN1Integer() = %v, want %v; out = %v, want %v", ok, test.ok, out, test.out)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadASN1IntegerSigned(t *testing.T) {
|
||||||
|
testData64 := []struct {
|
||||||
|
in []byte
|
||||||
|
out int64
|
||||||
|
}{
|
||||||
|
{[]byte{2, 3, 128, 0, 0}, -0x800000},
|
||||||
|
{[]byte{2, 2, 255, 0}, -256},
|
||||||
|
{[]byte{2, 2, 255, 127}, -129},
|
||||||
|
{[]byte{2, 1, 128}, -128},
|
||||||
|
{[]byte{2, 1, 255}, -1},
|
||||||
|
{[]byte{2, 1, 0}, 0},
|
||||||
|
{[]byte{2, 1, 1}, 1},
|
||||||
|
{[]byte{2, 1, 2}, 2},
|
||||||
|
{[]byte{2, 1, 127}, 127},
|
||||||
|
{[]byte{2, 2, 0, 128}, 128},
|
||||||
|
{[]byte{2, 2, 1, 0}, 256},
|
||||||
|
{[]byte{2, 4, 0, 128, 0, 0}, 0x800000},
|
||||||
|
}
|
||||||
|
for i, test := range testData64 {
|
||||||
|
in := String(test.in)
|
||||||
|
var out int64
|
||||||
|
ok := in.ReadASN1Integer(&out)
|
||||||
|
if !ok || out != test.out {
|
||||||
|
t.Errorf("#%d: in.ReadASN1Integer() = %v, want true; out = %d, want %d", i, ok, out, test.out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repeat the same cases, reading into a big.Int.
|
||||||
|
t.Run("big.Int", func(t *testing.T) {
|
||||||
|
for i, test := range testData64 {
|
||||||
|
in := String(test.in)
|
||||||
|
var out big.Int
|
||||||
|
ok := in.ReadASN1Integer(&out)
|
||||||
|
if !ok || out.Int64() != test.out {
|
||||||
|
t.Errorf("#%d: in.ReadASN1Integer() = %v, want true; out = %d, want %d", i, ok, out.Int64(), test.out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadASN1IntegerUnsigned(t *testing.T) {
|
||||||
|
testData := []struct {
|
||||||
|
in []byte
|
||||||
|
out uint64
|
||||||
|
}{
|
||||||
|
{[]byte{2, 1, 0}, 0},
|
||||||
|
{[]byte{2, 1, 1}, 1},
|
||||||
|
{[]byte{2, 1, 2}, 2},
|
||||||
|
{[]byte{2, 1, 127}, 127},
|
||||||
|
{[]byte{2, 2, 0, 128}, 128},
|
||||||
|
{[]byte{2, 2, 1, 0}, 256},
|
||||||
|
{[]byte{2, 4, 0, 128, 0, 0}, 0x800000},
|
||||||
|
{[]byte{2, 8, 127, 255, 255, 255, 255, 255, 255, 255}, 0x7fffffffffffffff},
|
||||||
|
{[]byte{2, 9, 0, 128, 0, 0, 0, 0, 0, 0, 0}, 0x8000000000000000},
|
||||||
|
{[]byte{2, 9, 0, 255, 255, 255, 255, 255, 255, 255, 255}, 0xffffffffffffffff},
|
||||||
|
}
|
||||||
|
for i, test := range testData {
|
||||||
|
in := String(test.in)
|
||||||
|
var out uint64
|
||||||
|
ok := in.ReadASN1Integer(&out)
|
||||||
|
if !ok || out != test.out {
|
||||||
|
t.Errorf("#%d: in.ReadASN1Integer() = %v, want true; out = %d, want %d", i, ok, out, test.out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadASN1IntegerInvalid(t *testing.T) {
|
||||||
|
testData := []String{
|
||||||
|
[]byte{3, 1, 0}, // invalid tag
|
||||||
|
// truncated
|
||||||
|
[]byte{2, 1},
|
||||||
|
[]byte{2, 2, 0},
|
||||||
|
// not minimally encoded
|
||||||
|
[]byte{2, 2, 0, 1},
|
||||||
|
[]byte{2, 2, 0xff, 0xff},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range testData {
|
||||||
|
var out int64
|
||||||
|
if test.ReadASN1Integer(&out) {
|
||||||
|
t.Errorf("#%d: in.ReadASN1Integer() = true, want false (out = %d)", i, out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadASN1ObjectIdentifier(t *testing.T) {
|
||||||
|
testData := []struct {
|
||||||
|
in []byte
|
||||||
|
ok bool
|
||||||
|
out []int
|
||||||
|
}{
|
||||||
|
{[]byte{}, false, []int{}},
|
||||||
|
{[]byte{6, 0}, false, []int{}},
|
||||||
|
{[]byte{5, 1, 85}, false, []int{2, 5}},
|
||||||
|
{[]byte{6, 1, 85}, true, []int{2, 5}},
|
||||||
|
{[]byte{6, 2, 85, 0x02}, true, []int{2, 5, 2}},
|
||||||
|
{[]byte{6, 4, 85, 0x02, 0xc0, 0x00}, true, []int{2, 5, 2, 0x2000}},
|
||||||
|
{[]byte{6, 3, 0x81, 0x34, 0x03}, true, []int{2, 100, 3}},
|
||||||
|
{[]byte{6, 7, 85, 0x02, 0xc0, 0x80, 0x80, 0x80, 0x80}, false, []int{}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range testData {
|
||||||
|
in := String(test.in)
|
||||||
|
var out asn1.ObjectIdentifier
|
||||||
|
ok := in.ReadASN1ObjectIdentifier(&out)
|
||||||
|
if ok != test.ok || ok && !out.Equal(test.out) {
|
||||||
|
t.Errorf("#%d: in.ReadASN1ObjectIdentifier() = %v, want %v; out = %v, want %v", i, ok, test.ok, out, test.out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadASN1GeneralizedTime(t *testing.T) {
|
||||||
|
testData := []struct {
|
||||||
|
in string
|
||||||
|
ok bool
|
||||||
|
out time.Time
|
||||||
|
}{
|
||||||
|
{"20100102030405Z", true, time.Date(2010, 01, 02, 03, 04, 05, 0, time.UTC)},
|
||||||
|
{"20100102030405", false, time.Time{}},
|
||||||
|
{"20100102030405+0607", true, time.Date(2010, 01, 02, 03, 04, 05, 0, time.FixedZone("", 6*60*60+7*60))},
|
||||||
|
{"20100102030405-0607", true, time.Date(2010, 01, 02, 03, 04, 05, 0, time.FixedZone("", -6*60*60-7*60))},
|
||||||
|
/* These are invalid times. However, the time package normalises times
|
||||||
|
* and they were accepted in some versions. See #11134. */
|
||||||
|
{"00000100000000Z", false, time.Time{}},
|
||||||
|
{"20101302030405Z", false, time.Time{}},
|
||||||
|
{"20100002030405Z", false, time.Time{}},
|
||||||
|
{"20100100030405Z", false, time.Time{}},
|
||||||
|
{"20100132030405Z", false, time.Time{}},
|
||||||
|
{"20100231030405Z", false, time.Time{}},
|
||||||
|
{"20100102240405Z", false, time.Time{}},
|
||||||
|
{"20100102036005Z", false, time.Time{}},
|
||||||
|
{"20100102030460Z", false, time.Time{}},
|
||||||
|
{"-20100102030410Z", false, time.Time{}},
|
||||||
|
{"2010-0102030410Z", false, time.Time{}},
|
||||||
|
{"2010-0002030410Z", false, time.Time{}},
|
||||||
|
{"201001-02030410Z", false, time.Time{}},
|
||||||
|
{"20100102-030410Z", false, time.Time{}},
|
||||||
|
{"2010010203-0410Z", false, time.Time{}},
|
||||||
|
{"201001020304-10Z", false, time.Time{}},
|
||||||
|
}
|
||||||
|
for i, test := range testData {
|
||||||
|
in := String(append([]byte{asn1.TagGeneralizedTime, byte(len(test.in))}, test.in...))
|
||||||
|
var out time.Time
|
||||||
|
ok := in.ReadASN1GeneralizedTime(&out)
|
||||||
|
if ok != test.ok || ok && !reflect.DeepEqual(out, test.out) {
|
||||||
|
t.Errorf("#%d: in.ReadASN1GeneralizedTime() = %v, want %v; out = %q, want %q", i, ok, test.ok, out, test.out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadASN1BitString(t *testing.T) {
|
||||||
|
testData := []struct {
|
||||||
|
in []byte
|
||||||
|
ok bool
|
||||||
|
out asn1.BitString
|
||||||
|
}{
|
||||||
|
{[]byte{}, false, asn1.BitString{}},
|
||||||
|
{[]byte{0x00}, true, asn1.BitString{}},
|
||||||
|
{[]byte{0x07, 0x00}, true, asn1.BitString{Bytes: []byte{0}, BitLength: 1}},
|
||||||
|
{[]byte{0x07, 0x01}, false, asn1.BitString{}},
|
||||||
|
{[]byte{0x07, 0x40}, false, asn1.BitString{}},
|
||||||
|
{[]byte{0x08, 0x00}, false, asn1.BitString{}},
|
||||||
|
{[]byte{0xff}, false, asn1.BitString{}},
|
||||||
|
{[]byte{0xfe, 0x00}, false, asn1.BitString{}},
|
||||||
|
}
|
||||||
|
for i, test := range testData {
|
||||||
|
in := String(append([]byte{3, byte(len(test.in))}, test.in...))
|
||||||
|
var out asn1.BitString
|
||||||
|
ok := in.ReadASN1BitString(&out)
|
||||||
|
if ok != test.ok || ok && (!bytes.Equal(out.Bytes, test.out.Bytes) || out.BitLength != test.out.BitLength) {
|
||||||
|
t.Errorf("#%d: in.ReadASN1BitString() = %v, want %v; out = %v, want %v", i, ok, test.ok, out, test.out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+255
@@ -0,0 +1,255 @@
|
|||||||
|
// Copyright 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package cryptobyte
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Builder builds byte strings from fixed-length and length-prefixed values.
|
||||||
|
// The zero value is a usable Builder that allocates space as needed.
|
||||||
|
type Builder struct {
|
||||||
|
err error
|
||||||
|
result []byte
|
||||||
|
fixedSize bool
|
||||||
|
child *Builder
|
||||||
|
offset int
|
||||||
|
pendingLenLen int
|
||||||
|
pendingIsASN1 bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBuilder creates a Builder that appends its output to the given buffer.
|
||||||
|
// Like append(), the slice will be reallocated if its capacity is exceeded.
|
||||||
|
// Use Bytes to get the final buffer.
|
||||||
|
func NewBuilder(buffer []byte) *Builder {
|
||||||
|
return &Builder{
|
||||||
|
result: buffer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFixedBuilder creates a Builder that appends its output into the given
|
||||||
|
// buffer. This builder does not reallocate the output buffer. Writes that
|
||||||
|
// would exceed the buffer's capacity are treated as an error.
|
||||||
|
func NewFixedBuilder(buffer []byte) *Builder {
|
||||||
|
return &Builder{
|
||||||
|
result: buffer,
|
||||||
|
fixedSize: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns the bytes written by the builder or an error if one has
|
||||||
|
// occurred during during building.
|
||||||
|
func (b *Builder) Bytes() ([]byte, error) {
|
||||||
|
if b.err != nil {
|
||||||
|
return nil, b.err
|
||||||
|
}
|
||||||
|
return b.result[b.offset:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesOrPanic returns the bytes written by the builder or panics if an error
|
||||||
|
// has occurred during building.
|
||||||
|
func (b *Builder) BytesOrPanic() []byte {
|
||||||
|
if b.err != nil {
|
||||||
|
panic(b.err)
|
||||||
|
}
|
||||||
|
return b.result[b.offset:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddUint8 appends an 8-bit value to the byte string.
|
||||||
|
func (b *Builder) AddUint8(v uint8) {
|
||||||
|
b.add(byte(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddUint16 appends a big-endian, 16-bit value to the byte string.
|
||||||
|
func (b *Builder) AddUint16(v uint16) {
|
||||||
|
b.add(byte(v>>8), byte(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddUint24 appends a big-endian, 24-bit value to the byte string. The highest
|
||||||
|
// byte of the 32-bit input value is silently truncated.
|
||||||
|
func (b *Builder) AddUint24(v uint32) {
|
||||||
|
b.add(byte(v>>16), byte(v>>8), byte(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddUint32 appends a big-endian, 32-bit value to the byte string.
|
||||||
|
func (b *Builder) AddUint32(v uint32) {
|
||||||
|
b.add(byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddBytes appends a sequence of bytes to the byte string.
|
||||||
|
func (b *Builder) AddBytes(v []byte) {
|
||||||
|
b.add(v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuilderContinuation is continuation-passing interface for building
|
||||||
|
// length-prefixed byte sequences. Builder methods for length-prefixed
|
||||||
|
// sequences (AddUint8LengthPrefixed etc.) will invoke the BuilderContinuation
|
||||||
|
// supplied to them. The child builder passed to the continuation can be used
|
||||||
|
// to build the content of the length-prefixed sequence. Example:
|
||||||
|
//
|
||||||
|
// parent := cryptobyte.NewBuilder()
|
||||||
|
// parent.AddUint8LengthPrefixed(func (child *Builder) {
|
||||||
|
// child.AddUint8(42)
|
||||||
|
// child.AddUint8LengthPrefixed(func (grandchild *Builder) {
|
||||||
|
// grandchild.AddUint8(5)
|
||||||
|
// })
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// It is an error to write more bytes to the child than allowed by the reserved
|
||||||
|
// length prefix. After the continuation returns, the child must be considered
|
||||||
|
// invalid, i.e. users must not store any copies or references of the child
|
||||||
|
// that outlive the continuation.
|
||||||
|
type BuilderContinuation func(child *Builder)
|
||||||
|
|
||||||
|
// AddUint8LengthPrefixed adds a 8-bit length-prefixed byte sequence.
|
||||||
|
func (b *Builder) AddUint8LengthPrefixed(f BuilderContinuation) {
|
||||||
|
b.addLengthPrefixed(1, false, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddUint16LengthPrefixed adds a big-endian, 16-bit length-prefixed byte sequence.
|
||||||
|
func (b *Builder) AddUint16LengthPrefixed(f BuilderContinuation) {
|
||||||
|
b.addLengthPrefixed(2, false, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddUint24LengthPrefixed adds a big-endian, 24-bit length-prefixed byte sequence.
|
||||||
|
func (b *Builder) AddUint24LengthPrefixed(f BuilderContinuation) {
|
||||||
|
b.addLengthPrefixed(3, false, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) addLengthPrefixed(lenLen int, isASN1 bool, f BuilderContinuation) {
|
||||||
|
// Subsequent writes can be ignored if the builder has encountered an error.
|
||||||
|
if b.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
offset := len(b.result)
|
||||||
|
b.add(make([]byte, lenLen)...)
|
||||||
|
|
||||||
|
b.child = &Builder{
|
||||||
|
result: b.result,
|
||||||
|
fixedSize: b.fixedSize,
|
||||||
|
offset: offset,
|
||||||
|
pendingLenLen: lenLen,
|
||||||
|
pendingIsASN1: isASN1,
|
||||||
|
}
|
||||||
|
|
||||||
|
f(b.child)
|
||||||
|
b.flushChild()
|
||||||
|
if b.child != nil {
|
||||||
|
panic("cryptobyte: internal error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) flushChild() {
|
||||||
|
if b.child == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b.child.flushChild()
|
||||||
|
child := b.child
|
||||||
|
b.child = nil
|
||||||
|
|
||||||
|
if child.err != nil {
|
||||||
|
b.err = child.err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
length := len(child.result) - child.pendingLenLen - child.offset
|
||||||
|
|
||||||
|
if length < 0 {
|
||||||
|
panic("cryptobyte: internal error") // result unexpectedly shrunk
|
||||||
|
}
|
||||||
|
|
||||||
|
if child.pendingIsASN1 {
|
||||||
|
// For ASN.1, we reserved a single byte for the length. If that turned out
|
||||||
|
// to be incorrect, we have to move the contents along in order to make
|
||||||
|
// space.
|
||||||
|
if child.pendingLenLen != 1 {
|
||||||
|
panic("cryptobyte: internal error")
|
||||||
|
}
|
||||||
|
var lenLen, lenByte uint8
|
||||||
|
if int64(length) > 0xfffffffe {
|
||||||
|
b.err = errors.New("pending ASN.1 child too long")
|
||||||
|
return
|
||||||
|
} else if length > 0xffffff {
|
||||||
|
lenLen = 5
|
||||||
|
lenByte = 0x80 | 4
|
||||||
|
} else if length > 0xffff {
|
||||||
|
lenLen = 4
|
||||||
|
lenByte = 0x80 | 3
|
||||||
|
} else if length > 0xff {
|
||||||
|
lenLen = 3
|
||||||
|
lenByte = 0x80 | 2
|
||||||
|
} else if length > 0x7f {
|
||||||
|
lenLen = 2
|
||||||
|
lenByte = 0x80 | 1
|
||||||
|
} else {
|
||||||
|
lenLen = 1
|
||||||
|
lenByte = uint8(length)
|
||||||
|
length = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert the initial length byte, make space for successive length bytes,
|
||||||
|
// and adjust the offset.
|
||||||
|
child.result[child.offset] = lenByte
|
||||||
|
extraBytes := int(lenLen - 1)
|
||||||
|
if extraBytes != 0 {
|
||||||
|
child.add(make([]byte, extraBytes)...)
|
||||||
|
childStart := child.offset + child.pendingLenLen
|
||||||
|
copy(child.result[childStart+extraBytes:], child.result[childStart:])
|
||||||
|
}
|
||||||
|
child.offset++
|
||||||
|
child.pendingLenLen = extraBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
l := length
|
||||||
|
for i := child.pendingLenLen - 1; i >= 0; i-- {
|
||||||
|
child.result[child.offset+i] = uint8(l)
|
||||||
|
l >>= 8
|
||||||
|
}
|
||||||
|
if l != 0 {
|
||||||
|
b.err = fmt.Errorf("cryptobyte: pending child length %d exceeds %d-byte length prefix", length, child.pendingLenLen)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !b.fixedSize {
|
||||||
|
b.result = child.result // In case child reallocated result.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) add(bytes ...byte) {
|
||||||
|
if b.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if b.child != nil {
|
||||||
|
panic("attempted write while child is pending")
|
||||||
|
}
|
||||||
|
if len(b.result)+len(bytes) < len(bytes) {
|
||||||
|
b.err = errors.New("cryptobyte: length overflow")
|
||||||
|
}
|
||||||
|
if b.fixedSize && len(b.result)+len(bytes) > cap(b.result) {
|
||||||
|
b.err = errors.New("cryptobyte: Builder is exceeding its fixed-size buffer")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b.result = append(b.result, bytes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A MarshalingValue marshals itself into a Builder.
|
||||||
|
type MarshalingValue interface {
|
||||||
|
// Marshal is called by Builder.AddValue. It receives a pointer to a builder
|
||||||
|
// to marshal itself into. It may return an error that occurred during
|
||||||
|
// marshaling, such as unset or invalid values.
|
||||||
|
Marshal(b *Builder) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddValue calls Marshal on v, passing a pointer to the builder to append to.
|
||||||
|
// If Marshal returns an error, it is set on the Builder so that subsequent
|
||||||
|
// appends don't have an effect.
|
||||||
|
func (b *Builder) AddValue(v MarshalingValue) {
|
||||||
|
err := v.Marshal(b)
|
||||||
|
if err != nil {
|
||||||
|
b.err = err
|
||||||
|
}
|
||||||
|
}
|
||||||
+379
@@ -0,0 +1,379 @@
|
|||||||
|
// Copyright 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package cryptobyte
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func builderBytesEq(b *Builder, want ...byte) error {
|
||||||
|
got := b.BytesOrPanic()
|
||||||
|
if !bytes.Equal(got, want) {
|
||||||
|
return fmt.Errorf("Bytes() = %v, want %v", got, want)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBytes(t *testing.T) {
|
||||||
|
var b Builder
|
||||||
|
v := []byte("foobarbaz")
|
||||||
|
b.AddBytes(v[0:3])
|
||||||
|
b.AddBytes(v[3:4])
|
||||||
|
b.AddBytes(v[4:9])
|
||||||
|
if err := builderBytesEq(&b, v...); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
s := String(b.BytesOrPanic())
|
||||||
|
for _, w := range []string{"foo", "bar", "baz"} {
|
||||||
|
var got []byte
|
||||||
|
if !s.ReadBytes(&got, 3) {
|
||||||
|
t.Errorf("ReadBytes() = false, want true (w = %v)", w)
|
||||||
|
}
|
||||||
|
want := []byte(w)
|
||||||
|
if !bytes.Equal(got, want) {
|
||||||
|
t.Errorf("ReadBytes(): got = %v, want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(s) != 0 {
|
||||||
|
t.Errorf("len(s) = %d, want 0", len(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUint8(t *testing.T) {
|
||||||
|
var b Builder
|
||||||
|
b.AddUint8(42)
|
||||||
|
if err := builderBytesEq(&b, 42); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var s String = b.BytesOrPanic()
|
||||||
|
var v uint8
|
||||||
|
if !s.ReadUint8(&v) {
|
||||||
|
t.Error("ReadUint8() = false, want true")
|
||||||
|
}
|
||||||
|
if v != 42 {
|
||||||
|
t.Errorf("v = %d, want 42", v)
|
||||||
|
}
|
||||||
|
if len(s) != 0 {
|
||||||
|
t.Errorf("len(s) = %d, want 0", len(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUint16(t *testing.T) {
|
||||||
|
var b Builder
|
||||||
|
b.AddUint16(65534)
|
||||||
|
if err := builderBytesEq(&b, 255, 254); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
var s String = b.BytesOrPanic()
|
||||||
|
var v uint16
|
||||||
|
if !s.ReadUint16(&v) {
|
||||||
|
t.Error("ReadUint16() == false, want true")
|
||||||
|
}
|
||||||
|
if v != 65534 {
|
||||||
|
t.Errorf("v = %d, want 65534", v)
|
||||||
|
}
|
||||||
|
if len(s) != 0 {
|
||||||
|
t.Errorf("len(s) = %d, want 0", len(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUint24(t *testing.T) {
|
||||||
|
var b Builder
|
||||||
|
b.AddUint24(0xfffefd)
|
||||||
|
if err := builderBytesEq(&b, 255, 254, 253); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var s String = b.BytesOrPanic()
|
||||||
|
var v uint32
|
||||||
|
if !s.ReadUint24(&v) {
|
||||||
|
t.Error("ReadUint8() = false, want true")
|
||||||
|
}
|
||||||
|
if v != 0xfffefd {
|
||||||
|
t.Errorf("v = %d, want fffefd", v)
|
||||||
|
}
|
||||||
|
if len(s) != 0 {
|
||||||
|
t.Errorf("len(s) = %d, want 0", len(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUint24Truncation(t *testing.T) {
|
||||||
|
var b Builder
|
||||||
|
b.AddUint24(0x10111213)
|
||||||
|
if err := builderBytesEq(&b, 0x11, 0x12, 0x13); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUint32(t *testing.T) {
|
||||||
|
var b Builder
|
||||||
|
b.AddUint32(0xfffefdfc)
|
||||||
|
if err := builderBytesEq(&b, 255, 254, 253, 252); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var s String = b.BytesOrPanic()
|
||||||
|
var v uint32
|
||||||
|
if !s.ReadUint32(&v) {
|
||||||
|
t.Error("ReadUint8() = false, want true")
|
||||||
|
}
|
||||||
|
if v != 0xfffefdfc {
|
||||||
|
t.Errorf("v = %x, want fffefdfc", v)
|
||||||
|
}
|
||||||
|
if len(s) != 0 {
|
||||||
|
t.Errorf("len(s) = %d, want 0", len(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUMultiple(t *testing.T) {
|
||||||
|
var b Builder
|
||||||
|
b.AddUint8(23)
|
||||||
|
b.AddUint32(0xfffefdfc)
|
||||||
|
b.AddUint16(42)
|
||||||
|
if err := builderBytesEq(&b, 23, 255, 254, 253, 252, 0, 42); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var s String = b.BytesOrPanic()
|
||||||
|
var (
|
||||||
|
x uint8
|
||||||
|
y uint32
|
||||||
|
z uint16
|
||||||
|
)
|
||||||
|
if !s.ReadUint8(&x) || !s.ReadUint32(&y) || !s.ReadUint16(&z) {
|
||||||
|
t.Error("ReadUint8() = false, want true")
|
||||||
|
}
|
||||||
|
if x != 23 || y != 0xfffefdfc || z != 42 {
|
||||||
|
t.Errorf("x, y, z = %d, %d, %d; want 23, 4294901244, 5", x, y, z)
|
||||||
|
}
|
||||||
|
if len(s) != 0 {
|
||||||
|
t.Errorf("len(s) = %d, want 0", len(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUint8LengthPrefixedSimple(t *testing.T) {
|
||||||
|
var b Builder
|
||||||
|
b.AddUint8LengthPrefixed(func(c *Builder) {
|
||||||
|
c.AddUint8(23)
|
||||||
|
c.AddUint8(42)
|
||||||
|
})
|
||||||
|
if err := builderBytesEq(&b, 2, 23, 42); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var base, child String = b.BytesOrPanic(), nil
|
||||||
|
var x, y uint8
|
||||||
|
if !base.ReadUint8LengthPrefixed(&child) || !child.ReadUint8(&x) ||
|
||||||
|
!child.ReadUint8(&y) {
|
||||||
|
t.Error("parsing failed")
|
||||||
|
}
|
||||||
|
if x != 23 || y != 42 {
|
||||||
|
t.Errorf("want x, y == 23, 42; got %d, %d", x, y)
|
||||||
|
}
|
||||||
|
if len(base) != 0 {
|
||||||
|
t.Errorf("len(base) = %d, want 0", len(base))
|
||||||
|
}
|
||||||
|
if len(child) != 0 {
|
||||||
|
t.Errorf("len(child) = %d, want 0", len(child))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUint8LengthPrefixedMulti(t *testing.T) {
|
||||||
|
var b Builder
|
||||||
|
b.AddUint8LengthPrefixed(func(c *Builder) {
|
||||||
|
c.AddUint8(23)
|
||||||
|
c.AddUint8(42)
|
||||||
|
})
|
||||||
|
b.AddUint8(5)
|
||||||
|
b.AddUint8LengthPrefixed(func(c *Builder) {
|
||||||
|
c.AddUint8(123)
|
||||||
|
c.AddUint8(234)
|
||||||
|
})
|
||||||
|
if err := builderBytesEq(&b, 2, 23, 42, 5, 2, 123, 234); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var s, child String = b.BytesOrPanic(), nil
|
||||||
|
var u, v, w, x, y uint8
|
||||||
|
if !s.ReadUint8LengthPrefixed(&child) || !child.ReadUint8(&u) || !child.ReadUint8(&v) ||
|
||||||
|
!s.ReadUint8(&w) || !s.ReadUint8LengthPrefixed(&child) || !child.ReadUint8(&x) || !child.ReadUint8(&y) {
|
||||||
|
t.Error("parsing failed")
|
||||||
|
}
|
||||||
|
if u != 23 || v != 42 || w != 5 || x != 123 || y != 234 {
|
||||||
|
t.Errorf("u, v, w, x, y = %d, %d, %d, %d, %d; want 23, 42, 5, 123, 234",
|
||||||
|
u, v, w, x, y)
|
||||||
|
}
|
||||||
|
if len(s) != 0 {
|
||||||
|
t.Errorf("len(s) = %d, want 0", len(s))
|
||||||
|
}
|
||||||
|
if len(child) != 0 {
|
||||||
|
t.Errorf("len(child) = %d, want 0", len(child))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUint8LengthPrefixedNested(t *testing.T) {
|
||||||
|
var b Builder
|
||||||
|
b.AddUint8LengthPrefixed(func(c *Builder) {
|
||||||
|
c.AddUint8(5)
|
||||||
|
c.AddUint8LengthPrefixed(func(d *Builder) {
|
||||||
|
d.AddUint8(23)
|
||||||
|
d.AddUint8(42)
|
||||||
|
})
|
||||||
|
c.AddUint8(123)
|
||||||
|
})
|
||||||
|
if err := builderBytesEq(&b, 5, 5, 2, 23, 42, 123); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var base, child1, child2 String = b.BytesOrPanic(), nil, nil
|
||||||
|
var u, v, w, x uint8
|
||||||
|
if !base.ReadUint8LengthPrefixed(&child1) {
|
||||||
|
t.Error("parsing base failed")
|
||||||
|
}
|
||||||
|
if !child1.ReadUint8(&u) || !child1.ReadUint8LengthPrefixed(&child2) || !child1.ReadUint8(&x) {
|
||||||
|
t.Error("parsing child1 failed")
|
||||||
|
}
|
||||||
|
if !child2.ReadUint8(&v) || !child2.ReadUint8(&w) {
|
||||||
|
t.Error("parsing child2 failed")
|
||||||
|
}
|
||||||
|
if u != 5 || v != 23 || w != 42 || x != 123 {
|
||||||
|
t.Errorf("u, v, w, x = %d, %d, %d, %d, want 5, 23, 42, 123",
|
||||||
|
u, v, w, x)
|
||||||
|
}
|
||||||
|
if len(base) != 0 {
|
||||||
|
t.Errorf("len(base) = %d, want 0", len(base))
|
||||||
|
}
|
||||||
|
if len(child1) != 0 {
|
||||||
|
t.Errorf("len(child1) = %d, want 0", len(child1))
|
||||||
|
}
|
||||||
|
if len(base) != 0 {
|
||||||
|
t.Errorf("len(child2) = %d, want 0", len(child2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPreallocatedBuffer(t *testing.T) {
|
||||||
|
var buf [5]byte
|
||||||
|
b := NewBuilder(buf[0:0])
|
||||||
|
b.AddUint8(1)
|
||||||
|
b.AddUint8LengthPrefixed(func(c *Builder) {
|
||||||
|
c.AddUint8(3)
|
||||||
|
c.AddUint8(4)
|
||||||
|
})
|
||||||
|
b.AddUint16(1286) // Outgrow buf by one byte.
|
||||||
|
want := []byte{1, 2, 3, 4, 0}
|
||||||
|
if !bytes.Equal(buf[:], want) {
|
||||||
|
t.Errorf("buf = %v want %v", buf, want)
|
||||||
|
}
|
||||||
|
if err := builderBytesEq(b, 1, 2, 3, 4, 5, 6); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWriteWithPendingChild(t *testing.T) {
|
||||||
|
var b Builder
|
||||||
|
b.AddUint8LengthPrefixed(func(c *Builder) {
|
||||||
|
c.AddUint8LengthPrefixed(func(d *Builder) {
|
||||||
|
defer func() {
|
||||||
|
if recover() == nil {
|
||||||
|
t.Errorf("recover() = nil, want error; c.AddUint8() did not panic")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
c.AddUint8(2) // panics
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if recover() == nil {
|
||||||
|
t.Errorf("recover() = nil, want error; b.AddUint8() did not panic")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
b.AddUint8(2) // panics
|
||||||
|
})
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if recover() == nil {
|
||||||
|
t.Errorf("recover() = nil, want error; b.AddUint8() did not panic")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
b.AddUint8(2) // panics
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ASN.1
|
||||||
|
|
||||||
|
func TestASN1Int64(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
in int64
|
||||||
|
want []byte
|
||||||
|
}{
|
||||||
|
{-0x800000, []byte{2, 3, 128, 0, 0}},
|
||||||
|
{-256, []byte{2, 2, 255, 0}},
|
||||||
|
{-129, []byte{2, 2, 255, 127}},
|
||||||
|
{-128, []byte{2, 1, 128}},
|
||||||
|
{-1, []byte{2, 1, 255}},
|
||||||
|
{0, []byte{2, 1, 0}},
|
||||||
|
{1, []byte{2, 1, 1}},
|
||||||
|
{2, []byte{2, 1, 2}},
|
||||||
|
{127, []byte{2, 1, 127}},
|
||||||
|
{128, []byte{2, 2, 0, 128}},
|
||||||
|
{256, []byte{2, 2, 1, 0}},
|
||||||
|
{0x800000, []byte{2, 4, 0, 128, 0, 0}},
|
||||||
|
}
|
||||||
|
for i, tt := range tests {
|
||||||
|
var b Builder
|
||||||
|
b.AddASN1Int64(tt.in)
|
||||||
|
if err := builderBytesEq(&b, tt.want...); err != nil {
|
||||||
|
t.Errorf("%v, (i = %d; in = %v)", err, i, tt.in)
|
||||||
|
}
|
||||||
|
|
||||||
|
var n int64
|
||||||
|
s := String(b.BytesOrPanic())
|
||||||
|
ok := s.ReadASN1Integer(&n)
|
||||||
|
if !ok || n != tt.in {
|
||||||
|
t.Errorf("s.ReadASN1Integer(&n) = %v, n = %d; want true, n = %d (i = %d)",
|
||||||
|
ok, n, tt.in, i)
|
||||||
|
}
|
||||||
|
if len(s) != 0 {
|
||||||
|
t.Errorf("len(s) = %d, want 0", len(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestASN1Uint64(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
in uint64
|
||||||
|
want []byte
|
||||||
|
}{
|
||||||
|
{0, []byte{2, 1, 0}},
|
||||||
|
{1, []byte{2, 1, 1}},
|
||||||
|
{2, []byte{2, 1, 2}},
|
||||||
|
{127, []byte{2, 1, 127}},
|
||||||
|
{128, []byte{2, 2, 0, 128}},
|
||||||
|
{256, []byte{2, 2, 1, 0}},
|
||||||
|
{0x800000, []byte{2, 4, 0, 128, 0, 0}},
|
||||||
|
{0x7fffffffffffffff, []byte{2, 8, 127, 255, 255, 255, 255, 255, 255, 255}},
|
||||||
|
{0x8000000000000000, []byte{2, 9, 0, 128, 0, 0, 0, 0, 0, 0, 0}},
|
||||||
|
{0xffffffffffffffff, []byte{2, 9, 0, 255, 255, 255, 255, 255, 255, 255, 255}},
|
||||||
|
}
|
||||||
|
for i, tt := range tests {
|
||||||
|
var b Builder
|
||||||
|
b.AddASN1Uint64(tt.in)
|
||||||
|
if err := builderBytesEq(&b, tt.want...); err != nil {
|
||||||
|
t.Errorf("%v, (i = %d; in = %v)", err, i, tt.in)
|
||||||
|
}
|
||||||
|
|
||||||
|
var n uint64
|
||||||
|
s := String(b.BytesOrPanic())
|
||||||
|
ok := s.ReadASN1Integer(&n)
|
||||||
|
if !ok || n != tt.in {
|
||||||
|
t.Errorf("s.ReadASN1Integer(&n) = %v, n = %d; want true, n = %d (i = %d)",
|
||||||
|
ok, n, tt.in, i)
|
||||||
|
}
|
||||||
|
if len(s) != 0 {
|
||||||
|
t.Errorf("len(s) = %d, want 0", len(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loaded 100 of 312 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user